pimelon-ui 0.0.98
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/license.md +0 -0
- package/package.json +75 -0
- package/readme.md +0 -0
- package/src/components/Alert.vue +57 -0
- package/src/components/Autocomplete.vue +176 -0
- package/src/components/Avatar.vue +62 -0
- package/src/components/Badge.vue +42 -0
- package/src/components/Button.vue +155 -0
- package/src/components/Card.vue +37 -0
- package/src/components/DatePicker.vue +252 -0
- package/src/components/Dialog.vue +216 -0
- package/src/components/Dropdown.vue +145 -0
- package/src/components/ErrorMessage.vue +24 -0
- package/src/components/FeatherIcon.vue +59 -0
- package/src/components/FileUploader.vue +220 -0
- package/src/components/GreenCheckIcon.vue +16 -0
- package/src/components/Input.vue +214 -0
- package/src/components/Link.vue +28 -0
- package/src/components/ListItem.vue +28 -0
- package/src/components/LoadingIndicator.vue +27 -0
- package/src/components/LoadingText.vue +21 -0
- package/src/components/Popover.vue +253 -0
- package/src/components/Resource.vue +21 -0
- package/src/components/TextEditor/FontColor.vue +108 -0
- package/src/components/TextEditor/InsertImage.vue +74 -0
- package/src/components/TextEditor/InsertLink.vue +67 -0
- package/src/components/TextEditor/InsertVideo.vue +94 -0
- package/src/components/TextEditor/MentionList.vue +95 -0
- package/src/components/TextEditor/Menu.vue +99 -0
- package/src/components/TextEditor/TextEditor.vue +275 -0
- package/src/components/TextEditor/TextEditorBubbleMenu.vue +69 -0
- package/src/components/TextEditor/TextEditorFixedMenu.vue +72 -0
- package/src/components/TextEditor/TextEditorFloatingMenu.vue +55 -0
- package/src/components/TextEditor/commands.js +272 -0
- package/src/components/TextEditor/icons/align-center.vue +14 -0
- package/src/components/TextEditor/icons/align-justify.vue +14 -0
- package/src/components/TextEditor/icons/align-left.vue +14 -0
- package/src/components/TextEditor/icons/align-right.vue +14 -0
- package/src/components/TextEditor/icons/arrow-go-back-line.vue +14 -0
- package/src/components/TextEditor/icons/arrow-go-forward-line.vue +14 -0
- package/src/components/TextEditor/icons/bold.vue +14 -0
- package/src/components/TextEditor/icons/code-view.vue +14 -0
- package/src/components/TextEditor/icons/double-quotes-r.vue +14 -0
- package/src/components/TextEditor/icons/font-color.vue +14 -0
- package/src/components/TextEditor/icons/format-clear.vue +14 -0
- package/src/components/TextEditor/icons/h-1.vue +14 -0
- package/src/components/TextEditor/icons/h-2.vue +14 -0
- package/src/components/TextEditor/icons/h-3.vue +14 -0
- package/src/components/TextEditor/icons/h-4.vue +14 -0
- package/src/components/TextEditor/icons/h-5.vue +14 -0
- package/src/components/TextEditor/icons/h-6.vue +14 -0
- package/src/components/TextEditor/icons/image-add-line.vue +14 -0
- package/src/components/TextEditor/icons/italic.vue +14 -0
- package/src/components/TextEditor/icons/link.vue +14 -0
- package/src/components/TextEditor/icons/list-ordered.vue +14 -0
- package/src/components/TextEditor/icons/list-unordered.vue +14 -0
- package/src/components/TextEditor/icons/readme.md +1 -0
- package/src/components/TextEditor/icons/separator.vue +14 -0
- package/src/components/TextEditor/icons/strikethrough.vue +14 -0
- package/src/components/TextEditor/icons/table-2.vue +14 -0
- package/src/components/TextEditor/icons/text.vue +11 -0
- package/src/components/TextEditor/icons/underline.vue +14 -0
- package/src/components/TextEditor/icons/video-add-line.vue +14 -0
- package/src/components/TextEditor/image-extension.js +152 -0
- package/src/components/TextEditor/index.js +6 -0
- package/src/components/TextEditor/mention.js +72 -0
- package/src/components/TextEditor/utils.js +11 -0
- package/src/components/TextEditor/video-extension.js +60 -0
- package/src/components/Toast.vue +83 -0
- package/src/components/Tooltip.vue +36 -0
- package/src/components/toast.js +98 -0
- package/src/directives/onOutsideClick.js +34 -0
- package/src/directives/visibility.js +24 -0
- package/src/index.js +56 -0
- package/src/resources/documentResource.js +194 -0
- package/src/resources/index.js +4 -0
- package/src/resources/listResource.js +312 -0
- package/src/resources/local.js +16 -0
- package/src/resources/plugin.js +105 -0
- package/src/resources/resources.js +215 -0
- package/src/style.css +15 -0
- package/src/utils/call.js +98 -0
- package/src/utils/config.js +9 -0
- package/src/utils/debounce.js +15 -0
- package/src/utils/file-to-base64.js +9 -0
- package/src/utils/markdown.js +29 -0
- package/src/utils/melonRequest.js +105 -0
- package/src/utils/pageMeta.js +52 -0
- package/src/utils/plugin.js +24 -0
- package/src/utils/request.js +49 -0
- package/src/utils/socketio.js +11 -0
- package/src/utils/tailwind.config.js +117 -0
- package/src/utils/vite-dev-server.js +14 -0
|
@@ -0,0 +1,215 @@
|
|
|
1
|
+
import { reactive } from 'vue'
|
|
2
|
+
import { debounce, request } from '../index'
|
|
3
|
+
import { getLocal, saveLocal } from './local'
|
|
4
|
+
import { getConfig } from '../utils/config'
|
|
5
|
+
|
|
6
|
+
let cached = {}
|
|
7
|
+
|
|
8
|
+
export function createResource(options, vm) {
|
|
9
|
+
let cacheKey = null
|
|
10
|
+
if (options.cache) {
|
|
11
|
+
cacheKey = getCacheKey(options.cache)
|
|
12
|
+
let cachedResource = cached[cacheKey]
|
|
13
|
+
|
|
14
|
+
if (cachedResource) {
|
|
15
|
+
if (cachedResource.auto) {
|
|
16
|
+
cachedResource.reload()
|
|
17
|
+
}
|
|
18
|
+
return cachedResource
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
if (typeof options == 'string') {
|
|
23
|
+
options = {
|
|
24
|
+
url: options,
|
|
25
|
+
auto: true,
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
let fetchFunction = options.debounce
|
|
30
|
+
? debounce(fetch, options.debounce)
|
|
31
|
+
: fetch
|
|
32
|
+
|
|
33
|
+
let out = reactive({
|
|
34
|
+
method: options.method,
|
|
35
|
+
url: options.url,
|
|
36
|
+
data: options.initialData || null,
|
|
37
|
+
previousData: null,
|
|
38
|
+
loading: false,
|
|
39
|
+
fetched: false,
|
|
40
|
+
error: null,
|
|
41
|
+
promise: null,
|
|
42
|
+
auto: options.auto,
|
|
43
|
+
params: null,
|
|
44
|
+
fetch: fetchFunction,
|
|
45
|
+
reload: fetchFunction,
|
|
46
|
+
submit: fetchFunction,
|
|
47
|
+
reset,
|
|
48
|
+
update,
|
|
49
|
+
setData,
|
|
50
|
+
})
|
|
51
|
+
|
|
52
|
+
async function fetch(params, tempOptions = {}) {
|
|
53
|
+
let resourceFetcher =
|
|
54
|
+
options.resourceFetcher || getConfig('resourceFetcher') || request
|
|
55
|
+
|
|
56
|
+
if (params instanceof Event) {
|
|
57
|
+
params = null
|
|
58
|
+
}
|
|
59
|
+
params = params || out.params
|
|
60
|
+
if (options.makeParams) {
|
|
61
|
+
params = options.makeParams.call(vm, params)
|
|
62
|
+
}
|
|
63
|
+
out.params = params
|
|
64
|
+
out.previousData = out.data ? JSON.parse(JSON.stringify(out.data)) : null
|
|
65
|
+
out.loading = true
|
|
66
|
+
out.error = null
|
|
67
|
+
|
|
68
|
+
if (options.onFetch) {
|
|
69
|
+
options.onFetch.call(vm, out.params)
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
let beforeSubmitFunctions = [options.beforeSubmit, tempOptions.beforeSubmit]
|
|
73
|
+
for (let fn of beforeSubmitFunctions) {
|
|
74
|
+
if (fn) {
|
|
75
|
+
fn.call(vm, out.params)
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
let validateFunction = tempOptions.validate || options.validate
|
|
80
|
+
let errorFunctions = [options.onError, tempOptions.onError]
|
|
81
|
+
let successFunctions = [options.onSuccess, tempOptions.onSuccess]
|
|
82
|
+
let dataFunctions = [options.onData, tempOptions.onData]
|
|
83
|
+
|
|
84
|
+
if (validateFunction) {
|
|
85
|
+
let invalidMessage
|
|
86
|
+
try {
|
|
87
|
+
invalidMessage = await validateFunction.call(vm, out.params)
|
|
88
|
+
if (invalidMessage && typeof invalidMessage == 'string') {
|
|
89
|
+
let error = new Error(invalidMessage)
|
|
90
|
+
handleError(error, errorFunctions)
|
|
91
|
+
return
|
|
92
|
+
}
|
|
93
|
+
} catch (error) {
|
|
94
|
+
handleError(error, errorFunctions)
|
|
95
|
+
return
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
try {
|
|
100
|
+
out.promise = resourceFetcher({
|
|
101
|
+
...options,
|
|
102
|
+
params: params || options.params,
|
|
103
|
+
})
|
|
104
|
+
let data = await out.promise
|
|
105
|
+
saveLocal(cacheKey, data)
|
|
106
|
+
out.data = transform(data)
|
|
107
|
+
out.fetched = true
|
|
108
|
+
for (let fn of successFunctions) {
|
|
109
|
+
if (fn) {
|
|
110
|
+
fn.call(vm, data)
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
for (let fn of dataFunctions) {
|
|
114
|
+
if (fn) {
|
|
115
|
+
fn.call(vm, data)
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
} catch (error) {
|
|
119
|
+
handleError(error, errorFunctions)
|
|
120
|
+
}
|
|
121
|
+
out.loading = false
|
|
122
|
+
return out.data
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
function update({ method, url, params, auto }) {
|
|
126
|
+
if (method && method !== options.method) {
|
|
127
|
+
out.method = method
|
|
128
|
+
}
|
|
129
|
+
if (url && url !== options.url) {
|
|
130
|
+
out.url = url
|
|
131
|
+
}
|
|
132
|
+
if (params && params !== options.params) {
|
|
133
|
+
out.params = params
|
|
134
|
+
}
|
|
135
|
+
if (auto !== undefined && auto !== out.auto) {
|
|
136
|
+
out.auto = auto
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
function reset() {
|
|
141
|
+
out.data = options.initialData || null
|
|
142
|
+
out.previousData = null
|
|
143
|
+
out.loading = false
|
|
144
|
+
out.fetched = false
|
|
145
|
+
out.error = null
|
|
146
|
+
out.params = null
|
|
147
|
+
out.auto = options.auto
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
function handleError(error, errorFunctions) {
|
|
151
|
+
out.loading = false
|
|
152
|
+
if (out.previousData) {
|
|
153
|
+
out.data = out.previousData
|
|
154
|
+
}
|
|
155
|
+
out.error = error
|
|
156
|
+
for (let fn of errorFunctions) {
|
|
157
|
+
if (fn) {
|
|
158
|
+
fn.call(vm, error)
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
throw error
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
// usage:
|
|
165
|
+
// setData(newData) or
|
|
166
|
+
// setData(data => data.filter(d => !d.deleted))
|
|
167
|
+
function setData(data) {
|
|
168
|
+
if (typeof data === 'function') {
|
|
169
|
+
data = data.call(vm, out.data)
|
|
170
|
+
}
|
|
171
|
+
out.data = transform(data)
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
function transform(data) {
|
|
175
|
+
if (options.transform) {
|
|
176
|
+
let returnValue = options.transform.call(vm, data)
|
|
177
|
+
if (returnValue != null) {
|
|
178
|
+
return returnValue
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
return data
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
if (cacheKey && !cached[cacheKey]) {
|
|
185
|
+
cached[cacheKey] = out
|
|
186
|
+
// offline
|
|
187
|
+
getLocal(cacheKey).then((data) => {
|
|
188
|
+
if ((out.loading || !out.fetched) && data) {
|
|
189
|
+
setData(data)
|
|
190
|
+
options.onData?.call(vm, data)
|
|
191
|
+
}
|
|
192
|
+
})
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
if (options.auto) {
|
|
196
|
+
out.fetch()
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
return out
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
export function getCacheKey(cacheKey) {
|
|
203
|
+
if (!cacheKey) {
|
|
204
|
+
return null
|
|
205
|
+
}
|
|
206
|
+
if (typeof cacheKey === 'string') {
|
|
207
|
+
cacheKey = [cacheKey]
|
|
208
|
+
}
|
|
209
|
+
return JSON.stringify(cacheKey)
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
export function getCachedResource(cacheKey) {
|
|
213
|
+
cacheKey = getCacheKey(cacheKey)
|
|
214
|
+
return cached[cacheKey] || null
|
|
215
|
+
}
|
package/src/style.css
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
@tailwind base;
|
|
2
|
+
@tailwind components;
|
|
3
|
+
@tailwind utilities;
|
|
4
|
+
|
|
5
|
+
@layer components {
|
|
6
|
+
.form-input,
|
|
7
|
+
.form-textarea,
|
|
8
|
+
.form-select {
|
|
9
|
+
@apply rounded-md border-0 bg-gray-100 py-1 text-base leading-5 placeholder-gray-700 focus:bg-gray-200 focus:shadow-none focus:ring-0;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
.form-checkbox {
|
|
13
|
+
@apply rounded-md bg-gray-100 text-blue-500 focus:ring-0 focus-visible:ring-1;
|
|
14
|
+
}
|
|
15
|
+
}
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
export default async function call(method, args, options = {}) {
|
|
2
|
+
if (!args) {
|
|
3
|
+
args = {}
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
let headers = Object.assign(
|
|
7
|
+
{
|
|
8
|
+
Accept: 'application/json',
|
|
9
|
+
'Content-Type': 'application/json; charset=utf-8',
|
|
10
|
+
'X-Melon-Site-Name': window.location.hostname,
|
|
11
|
+
},
|
|
12
|
+
options.headers || {}
|
|
13
|
+
)
|
|
14
|
+
|
|
15
|
+
if (window.csrf_token && window.csrf_token !== '{{ csrf_token }}') {
|
|
16
|
+
headers['X-Melon-CSRF-Token'] = window.csrf_token
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
let path = method.startsWith('/') ? method : `/api/method/${method}`
|
|
20
|
+
const res = await fetch(path, {
|
|
21
|
+
method: 'POST',
|
|
22
|
+
headers,
|
|
23
|
+
body: JSON.stringify(args),
|
|
24
|
+
})
|
|
25
|
+
|
|
26
|
+
if (res.ok) {
|
|
27
|
+
const data = await res.json()
|
|
28
|
+
if (data.docs || method === 'login') {
|
|
29
|
+
return data
|
|
30
|
+
}
|
|
31
|
+
if (data.exc) {
|
|
32
|
+
try {
|
|
33
|
+
console.groupCollapsed(method)
|
|
34
|
+
console.log(`method: ${method}`)
|
|
35
|
+
console.log(`params:`, args)
|
|
36
|
+
let warning = JSON.parse(data.exc)
|
|
37
|
+
for (let text of warning) {
|
|
38
|
+
console.log(text)
|
|
39
|
+
}
|
|
40
|
+
console.groupEnd()
|
|
41
|
+
} catch (e) {
|
|
42
|
+
console.warn('Error printing debug messages', e)
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
return data.message
|
|
46
|
+
} else {
|
|
47
|
+
let response = await res.text()
|
|
48
|
+
let error, exception
|
|
49
|
+
try {
|
|
50
|
+
error = JSON.parse(response)
|
|
51
|
+
// eslint-disable-next-line no-empty
|
|
52
|
+
} catch (e) {}
|
|
53
|
+
let errorParts = [
|
|
54
|
+
[method, error.exc_type, error._error_message].filter(Boolean).join(' '),
|
|
55
|
+
]
|
|
56
|
+
if (error.exc) {
|
|
57
|
+
exception = error.exc
|
|
58
|
+
try {
|
|
59
|
+
exception = JSON.parse(exception)[0]
|
|
60
|
+
console.log(exception)
|
|
61
|
+
// eslint-disable-next-line no-empty
|
|
62
|
+
} catch (e) {}
|
|
63
|
+
}
|
|
64
|
+
let e = new Error(errorParts.join('\n'))
|
|
65
|
+
e.exc_type = error.exc_type
|
|
66
|
+
e.exc = exception
|
|
67
|
+
e.status = res.status
|
|
68
|
+
e.messages = error._server_messages
|
|
69
|
+
? JSON.parse(error._server_messages)
|
|
70
|
+
: []
|
|
71
|
+
e.messages = e.messages.concat(error.message)
|
|
72
|
+
e.messages = e.messages.map((m) => {
|
|
73
|
+
try {
|
|
74
|
+
return JSON.parse(m).message
|
|
75
|
+
} catch (error) {
|
|
76
|
+
return m
|
|
77
|
+
}
|
|
78
|
+
})
|
|
79
|
+
e.messages = e.messages.filter(Boolean)
|
|
80
|
+
if (!e.messages.length) {
|
|
81
|
+
e.messages = error._error_message
|
|
82
|
+
? [error._error_message]
|
|
83
|
+
: ['Internal Server Error']
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
if (options.onError) {
|
|
87
|
+
options.onError({ response: res, status: res.status, error: e })
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
throw e
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
export function createCall(options) {
|
|
95
|
+
return function customCall(method, args) {
|
|
96
|
+
return call(method, args, options)
|
|
97
|
+
}
|
|
98
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
export default function debounce(func, wait, immediate) {
|
|
2
|
+
var timeout
|
|
3
|
+
return function () {
|
|
4
|
+
var context = this,
|
|
5
|
+
args = arguments
|
|
6
|
+
var later = function () {
|
|
7
|
+
timeout = null
|
|
8
|
+
if (!immediate) func.apply(context, args)
|
|
9
|
+
}
|
|
10
|
+
var callNow = immediate && !timeout
|
|
11
|
+
clearTimeout(timeout)
|
|
12
|
+
timeout = setTimeout(later, wait)
|
|
13
|
+
if (callNow) func.apply(context, args)
|
|
14
|
+
}
|
|
15
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import showdown from 'showdown'
|
|
2
|
+
|
|
3
|
+
export function markdownToHTML(text) {
|
|
4
|
+
const converter = new showdown.Converter()
|
|
5
|
+
return converter.makeHtml(text)
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export function htmlToMarkdown(text) {
|
|
9
|
+
const converter = new showdown.Converter()
|
|
10
|
+
return converter.makeMarkdown(text)
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export function detectMarkdown(text) {
|
|
14
|
+
const lines = text.split('\n')
|
|
15
|
+
const markdown = lines.filter(
|
|
16
|
+
(line) =>
|
|
17
|
+
line.startsWith('![') ||
|
|
18
|
+
line.startsWith('#') ||
|
|
19
|
+
line.startsWith('> ') ||
|
|
20
|
+
line.startsWith('*') ||
|
|
21
|
+
line.startsWith('- ') ||
|
|
22
|
+
line.startsWith('1. ') ||
|
|
23
|
+
line.startsWith('```') ||
|
|
24
|
+
line.startsWith('`') ||
|
|
25
|
+
line.startsWith('[') ||
|
|
26
|
+
line.startsWith('---')
|
|
27
|
+
)
|
|
28
|
+
return markdown.length > 0
|
|
29
|
+
}
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
import { request } from './request'
|
|
2
|
+
|
|
3
|
+
export function melonRequest(options) {
|
|
4
|
+
return request({
|
|
5
|
+
...options,
|
|
6
|
+
transformRequest: (options = {}) => {
|
|
7
|
+
if (!options.url) {
|
|
8
|
+
throw new Error('[melonRequest] options.url is required')
|
|
9
|
+
}
|
|
10
|
+
let headers = Object.assign(
|
|
11
|
+
{
|
|
12
|
+
Accept: 'application/json',
|
|
13
|
+
'Content-Type': 'application/json; charset=utf-8',
|
|
14
|
+
'X-Melon-Site-Name': window.location.hostname,
|
|
15
|
+
},
|
|
16
|
+
options.headers || {}
|
|
17
|
+
)
|
|
18
|
+
if (window.csrf_token && window.csrf_token !== '{{ csrf_token }}') {
|
|
19
|
+
headers['X-Melon-CSRF-Token'] = window.csrf_token
|
|
20
|
+
}
|
|
21
|
+
if (!options.url.startsWith('/') && !options.url.startsWith('http')) {
|
|
22
|
+
options.url = '/api/method/' + options.url
|
|
23
|
+
}
|
|
24
|
+
return {
|
|
25
|
+
...options,
|
|
26
|
+
method: options.method || 'POST',
|
|
27
|
+
headers,
|
|
28
|
+
}
|
|
29
|
+
},
|
|
30
|
+
transformResponse: async (response, options) => {
|
|
31
|
+
let url = options.url
|
|
32
|
+
if (response.ok) {
|
|
33
|
+
const data = await response.json()
|
|
34
|
+
if (data.docs || url === 'login') {
|
|
35
|
+
return data
|
|
36
|
+
}
|
|
37
|
+
if (data.exc) {
|
|
38
|
+
try {
|
|
39
|
+
console.groupCollapsed(url)
|
|
40
|
+
console.log(options)
|
|
41
|
+
let warning = JSON.parse(data.exc)
|
|
42
|
+
for (let text of warning) {
|
|
43
|
+
console.log(text)
|
|
44
|
+
}
|
|
45
|
+
console.groupEnd()
|
|
46
|
+
} catch (e) {
|
|
47
|
+
console.warn('Error printing debug messages', e)
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
return data.message
|
|
51
|
+
} else {
|
|
52
|
+
let errorResponse = await response.text()
|
|
53
|
+
let error, exception
|
|
54
|
+
try {
|
|
55
|
+
error = JSON.parse(errorResponse)
|
|
56
|
+
// eslint-disable-next-line no-empty
|
|
57
|
+
} catch (e) {}
|
|
58
|
+
let errorParts = [
|
|
59
|
+
[options.url, error.exc_type, error._error_message]
|
|
60
|
+
.filter(Boolean)
|
|
61
|
+
.join(' '),
|
|
62
|
+
]
|
|
63
|
+
if (error.exc) {
|
|
64
|
+
exception = error.exc
|
|
65
|
+
try {
|
|
66
|
+
exception = JSON.parse(exception)[0]
|
|
67
|
+
console.log(exception)
|
|
68
|
+
// eslint-disable-next-line no-empty
|
|
69
|
+
} catch (e) {}
|
|
70
|
+
}
|
|
71
|
+
let e = new Error(errorParts.join('\n'))
|
|
72
|
+
e.exc_type = error.exc_type
|
|
73
|
+
e.exc = exception
|
|
74
|
+
e.status = errorResponse.status
|
|
75
|
+
e.messages = error._server_messages
|
|
76
|
+
? JSON.parse(error._server_messages)
|
|
77
|
+
: []
|
|
78
|
+
e.messages = e.messages.concat(error.message)
|
|
79
|
+
e.messages = e.messages.map((m) => {
|
|
80
|
+
try {
|
|
81
|
+
return JSON.parse(m).message
|
|
82
|
+
} catch (error) {
|
|
83
|
+
return m
|
|
84
|
+
}
|
|
85
|
+
})
|
|
86
|
+
e.messages = e.messages.filter(Boolean)
|
|
87
|
+
if (!e.messages.length) {
|
|
88
|
+
e.messages = error._error_message
|
|
89
|
+
? [error._error_message]
|
|
90
|
+
: ['Internal Server Error']
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
if (options.onError) {
|
|
94
|
+
options.onError({
|
|
95
|
+
response: errorResponse,
|
|
96
|
+
status: errorResponse.status,
|
|
97
|
+
error: e,
|
|
98
|
+
})
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
throw e
|
|
102
|
+
}
|
|
103
|
+
},
|
|
104
|
+
})
|
|
105
|
+
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { watch } from 'vue'
|
|
2
|
+
|
|
3
|
+
export default {
|
|
4
|
+
install(app) {
|
|
5
|
+
app.mixin(createMixin())
|
|
6
|
+
},
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
function createMixin() {
|
|
10
|
+
let faviconRef = document.querySelector('link[rel="icon"]')
|
|
11
|
+
let defaultFavIcon = faviconRef.href
|
|
12
|
+
|
|
13
|
+
return {
|
|
14
|
+
created() {
|
|
15
|
+
if (this.$options.pageMeta) {
|
|
16
|
+
watch(
|
|
17
|
+
() => {
|
|
18
|
+
try {
|
|
19
|
+
return this.$options.pageMeta.call(this)
|
|
20
|
+
} catch (error) {
|
|
21
|
+
let debugInfo = `${this.$options.name} (${
|
|
22
|
+
this.$options.__file || ''
|
|
23
|
+
})`
|
|
24
|
+
if (process.env.NODE_ENV === 'development') {
|
|
25
|
+
console.warn('Failed to parse pageMeta in', debugInfo)
|
|
26
|
+
}
|
|
27
|
+
return null
|
|
28
|
+
}
|
|
29
|
+
},
|
|
30
|
+
(pageMeta) => {
|
|
31
|
+
if (!pageMeta) return
|
|
32
|
+
if (pageMeta.title) {
|
|
33
|
+
document.title = pageMeta.title
|
|
34
|
+
}
|
|
35
|
+
if (pageMeta.emoji) {
|
|
36
|
+
let href = `data:image/svg+xml,<svg xmlns=%22http://www.w3.org/2000/svg%22 viewBox=%220 0 100 100%22><text y=%22.9em%22 font-size=%2290%22>${pageMeta.emoji}</text></svg>`
|
|
37
|
+
faviconRef.href = href
|
|
38
|
+
} else if (pageMeta.icon) {
|
|
39
|
+
faviconRef.href = pageMeta.icon
|
|
40
|
+
} else {
|
|
41
|
+
faviconRef.href = defaultFavIcon
|
|
42
|
+
}
|
|
43
|
+
},
|
|
44
|
+
{
|
|
45
|
+
immediate: true,
|
|
46
|
+
deep: true,
|
|
47
|
+
}
|
|
48
|
+
)
|
|
49
|
+
}
|
|
50
|
+
},
|
|
51
|
+
}
|
|
52
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import resourcesPlugin from '../resources/plugin'
|
|
2
|
+
import call from './call'
|
|
3
|
+
import initSocket from './socketio'
|
|
4
|
+
|
|
5
|
+
let defaultOptions = {
|
|
6
|
+
resources: true,
|
|
7
|
+
call: true,
|
|
8
|
+
socketio: true,
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export default {
|
|
12
|
+
install(app, options = {}) {
|
|
13
|
+
options = Object.assign({}, defaultOptions, options)
|
|
14
|
+
options.resources && app.use(resourcesPlugin, options.resources)
|
|
15
|
+
|
|
16
|
+
if (options.call) {
|
|
17
|
+
let callFunction = typeof options.call == 'function' ? options.call : call
|
|
18
|
+
app.config.globalProperties.$call = callFunction
|
|
19
|
+
}
|
|
20
|
+
if (options.socketio) {
|
|
21
|
+
app.config.globalProperties.$socket = initSocket(options.socketio)
|
|
22
|
+
}
|
|
23
|
+
},
|
|
24
|
+
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
export function request(_options) {
|
|
2
|
+
let options = Object.assign({}, _options)
|
|
3
|
+
if (!options.url) {
|
|
4
|
+
throw new Error('[request] options.url is required')
|
|
5
|
+
}
|
|
6
|
+
if (options.transformRequest) {
|
|
7
|
+
options = options.transformRequest(_options)
|
|
8
|
+
}
|
|
9
|
+
if (!options.responseType) {
|
|
10
|
+
options.responseType = 'json'
|
|
11
|
+
}
|
|
12
|
+
if (!options.method) {
|
|
13
|
+
options.method = 'GET'
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
let url = options.url
|
|
17
|
+
let body
|
|
18
|
+
if (options.params) {
|
|
19
|
+
if (options.method === 'GET') {
|
|
20
|
+
let params = new URLSearchParams()
|
|
21
|
+
for (let key in options.params) {
|
|
22
|
+
params.append(key, options.params[key])
|
|
23
|
+
}
|
|
24
|
+
url = options.url + '?' + params.toString()
|
|
25
|
+
} else {
|
|
26
|
+
body = JSON.stringify(options.params)
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
return fetch(url, {
|
|
31
|
+
method: options.method || 'GET',
|
|
32
|
+
headers: options.headers,
|
|
33
|
+
body,
|
|
34
|
+
}).then((response) => {
|
|
35
|
+
if (options.transformResponse) {
|
|
36
|
+
return options.transformResponse(response, options)
|
|
37
|
+
}
|
|
38
|
+
if (response.status >= 200 && response.status < 300) {
|
|
39
|
+
if (options.responseType === 'json') {
|
|
40
|
+
return response.json()
|
|
41
|
+
}
|
|
42
|
+
return response
|
|
43
|
+
} else {
|
|
44
|
+
let error = new Error(response.statusText)
|
|
45
|
+
error.response = response
|
|
46
|
+
throw error
|
|
47
|
+
}
|
|
48
|
+
})
|
|
49
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { io } from 'socket.io-client'
|
|
2
|
+
|
|
3
|
+
export default function initSocket(options = {}) {
|
|
4
|
+
let host = window.location.hostname
|
|
5
|
+
let socketio_port = options.port || 9000
|
|
6
|
+
let port = window.location.port ? `:${socketio_port}` : ''
|
|
7
|
+
let protocol = port ? 'http' : 'https'
|
|
8
|
+
let url = `${protocol}://${host}${port}`
|
|
9
|
+
let socket = io(url, { withCredentials: true })
|
|
10
|
+
return socket
|
|
11
|
+
}
|