@sugarat/easypicker2-client 2.4.1
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/.env +6 -0
- package/.env.production +3 -0
- package/.env.test +4 -0
- package/.eslintignore +0 -0
- package/.eslintrc.json +57 -0
- package/.github/workflows/main.yml +61 -0
- package/.prettierrc.js +9 -0
- package/LICENSE +21 -0
- package/README.md +86 -0
- package/auto-imports.d.ts +6 -0
- package/components.d.ts +56 -0
- package/docker/ep_backup/easypicker2.sql +214 -0
- package/docker/ep_backup/mongodb/easypicker2/action.bson +0 -0
- package/docker/ep_backup/mongodb/easypicker2/action.metadata.json +1 -0
- package/docker/ep_backup/mongodb/easypicker2/log.bson +0 -0
- package/docker/ep_backup/mongodb/easypicker2/log.metadata.json +1 -0
- package/docker/ep_backup/user-config.json +176 -0
- package/docs/.env +1 -0
- package/docs/.env.production +2 -0
- package/docs/.vitepress/config.ts +204 -0
- package/docs/.vitepress/theme/bg.png +0 -0
- package/docs/.vitepress/theme/index.scss +41 -0
- package/docs/.vitepress/theme/index.ts +5 -0
- package/docs/author.md +24 -0
- package/docs/auto-imports.d.ts +6 -0
- package/docs/components.d.ts +17 -0
- package/docs/deploy/design/api.md +3 -0
- package/docs/deploy/design/db.md +3 -0
- package/docs/deploy/design/index.md +3 -0
- package/docs/deploy/design/shell.md +9 -0
- package/docs/deploy/faq.md +86 -0
- package/docs/deploy/index.md +9 -0
- package/docs/deploy/local.md +275 -0
- package/docs/deploy/online-new.md +610 -0
- package/docs/deploy/online.md +683 -0
- package/docs/deploy/qiniu.md +183 -0
- package/docs/index.md +40 -0
- package/docs/introduction/about/code.md +26 -0
- package/docs/introduction/about/index.md +33 -0
- package/docs/introduction/feature/index.md +3 -0
- package/docs/plan/log.md +333 -0
- package/docs/plan/todo.md +127 -0
- package/docs/plan/wish.md +29 -0
- package/docs/praise/index.md +45 -0
- package/docs/public/favicon.ico +0 -0
- package/docs/public/logo.png +0 -0
- package/docs/public/robots.txt +2 -0
- package/docs/src/apis/ajax.ts +66 -0
- package/docs/src/apis/index.ts +1 -0
- package/docs/src/apis/modules/wish.ts +20 -0
- package/docs/src/components/Avatar.vue +60 -0
- package/docs/src/components/Home.vue +85 -0
- package/docs/src/components/Picture.vue +13 -0
- package/docs/src/components/Praise.vue +52 -0
- package/docs/src/components/WishBtn.vue +98 -0
- package/docs/src/components/WishPanel.vue +170 -0
- package/docs/src/components/callme/index.vue +72 -0
- package/docs/vite.config.ts +42 -0
- package/index.html +127 -0
- package/package.json +52 -0
- package/public/favicon.ico +0 -0
- package/public/logo.png +0 -0
- package/scripts/deploy/docs.mjs +24 -0
- package/scripts/deploy/prod.mjs +24 -0
- package/scripts/deploy/test.mjs +26 -0
- package/src/@types/ajax.d.ts +5 -0
- package/src/@types/api.d.ts +305 -0
- package/src/@types/lib.d.ts +26 -0
- package/src/@types/page.d.ts +18 -0
- package/src/App.vue +36 -0
- package/src/apis/ajax.ts +70 -0
- package/src/apis/index.ts +20 -0
- package/src/apis/modules/action.ts +17 -0
- package/src/apis/modules/category.ts +20 -0
- package/src/apis/modules/config.ts +19 -0
- package/src/apis/modules/file.ts +150 -0
- package/src/apis/modules/people.ts +81 -0
- package/src/apis/modules/public.ts +49 -0
- package/src/apis/modules/super/overview.ts +56 -0
- package/src/apis/modules/super/user.ts +62 -0
- package/src/apis/modules/task.ts +67 -0
- package/src/apis/modules/user.ts +56 -0
- package/src/apis/modules/wish.ts +31 -0
- package/src/assets/i/EasyPicker.png +0 -0
- package/src/assets/logo.png +0 -0
- package/src/assets/styles/app.css +69 -0
- package/src/components/HomeFooter/index.vue +134 -0
- package/src/components/HomeHeader/index.vue +156 -0
- package/src/components/InfosForm/index.vue +73 -0
- package/src/components/MessageList/index.vue +155 -0
- package/src/components/MessagePanel/index.vue +42 -0
- package/src/components/Praise/index.vue +102 -0
- package/src/components/QrCode.vue +44 -0
- package/src/components/linkDialog.vue +104 -0
- package/src/components/loginPanel.vue +92 -0
- package/src/constants/index.ts +83 -0
- package/src/env.d.ts +8 -0
- package/src/main.ts +19 -0
- package/src/pages/404/index.vue +59 -0
- package/src/pages/about/index.vue +152 -0
- package/src/pages/callme/index.vue +155 -0
- package/src/pages/dashboard/config/index.vue +264 -0
- package/src/pages/dashboard/files/index.vue +1152 -0
- package/src/pages/dashboard/index.vue +335 -0
- package/src/pages/dashboard/manage/config/index.vue +97 -0
- package/src/pages/dashboard/manage/index.vue +105 -0
- package/src/pages/dashboard/manage/overview/index.vue +488 -0
- package/src/pages/dashboard/manage/user/index.vue +679 -0
- package/src/pages/dashboard/manage/wish/index.vue +257 -0
- package/src/pages/dashboard/tasks/components/CategoryPanel.vue +208 -0
- package/src/pages/dashboard/tasks/components/CreateTask.vue +93 -0
- package/src/pages/dashboard/tasks/components/TaskInfo.vue +129 -0
- package/src/pages/dashboard/tasks/components/infoPanel/ddl.vue +96 -0
- package/src/pages/dashboard/tasks/components/infoPanel/file.vue +175 -0
- package/src/pages/dashboard/tasks/components/infoPanel/info.vue +477 -0
- package/src/pages/dashboard/tasks/components/infoPanel/people.vue +567 -0
- package/src/pages/dashboard/tasks/components/infoPanel/template.vue +146 -0
- package/src/pages/dashboard/tasks/components/infoPanel/tip.vue +55 -0
- package/src/pages/dashboard/tasks/components/infoPanel/tipInfo.vue +196 -0
- package/src/pages/dashboard/tasks/index.vue +302 -0
- package/src/pages/dashboard/tasks/public.ts +32 -0
- package/src/pages/disabled/index.vue +47 -0
- package/src/pages/feedback/index.vue +5 -0
- package/src/pages/home/index.vue +72 -0
- package/src/pages/login/index.vue +270 -0
- package/src/pages/register/index.vue +211 -0
- package/src/pages/reset/index.vue +186 -0
- package/src/pages/task/index.vue +897 -0
- package/src/pages/wish/index.vue +152 -0
- package/src/router/Interceptor/index.ts +112 -0
- package/src/router/index.ts +13 -0
- package/src/router/routes/index.ts +197 -0
- package/src/shims-vue.d.ts +6 -0
- package/src/store/index.ts +17 -0
- package/src/store/modules/category.ts +44 -0
- package/src/store/modules/public.ts +27 -0
- package/src/store/modules/task.ts +55 -0
- package/src/store/modules/user.ts +57 -0
- package/src/utils/elementUI.ts +8 -0
- package/src/utils/networkUtil.ts +236 -0
- package/src/utils/other.ts +25 -0
- package/src/utils/regExp.ts +11 -0
- package/src/utils/stringUtil.ts +242 -0
- package/tsconfig.json +24 -0
- package/vite.config.ts +55 -0
|
@@ -0,0 +1,236 @@
|
|
|
1
|
+
import { base64 } from './stringUtil'
|
|
2
|
+
|
|
3
|
+
/* eslint-disable no-restricted-syntax */
|
|
4
|
+
type SuccessCallback = (data: any) => void
|
|
5
|
+
export function jsonp(
|
|
6
|
+
url: string,
|
|
7
|
+
jsonpCallback: string,
|
|
8
|
+
success: SuccessCallback
|
|
9
|
+
) {
|
|
10
|
+
const $script = document.createElement('script')
|
|
11
|
+
$script.src = `${url}&callback=${jsonpCallback}`
|
|
12
|
+
$script.async = true
|
|
13
|
+
$script.type = 'text/javascript'
|
|
14
|
+
;(<any>window)[jsonpCallback] = function callback(data: any) {
|
|
15
|
+
if (success) {
|
|
16
|
+
success(data)
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
document.body.appendChild($script)
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
interface UploadFileOptions {
|
|
23
|
+
success?: any
|
|
24
|
+
error?: any
|
|
25
|
+
process?: any
|
|
26
|
+
method?: string
|
|
27
|
+
}
|
|
28
|
+
export function uploadFile(
|
|
29
|
+
file: File,
|
|
30
|
+
url: string,
|
|
31
|
+
options?: UploadFileOptions
|
|
32
|
+
) {
|
|
33
|
+
const form = new FormData()
|
|
34
|
+
// ajax对象
|
|
35
|
+
const xhr = new XMLHttpRequest()
|
|
36
|
+
// 添加文件到表单中
|
|
37
|
+
form.append('file', file)
|
|
38
|
+
// 设置请求方式 路径 是否异步
|
|
39
|
+
xhr.open(options?.method || 'post', url, true)
|
|
40
|
+
// 设置请求头参数的方式,如果没有可忽略此行代码
|
|
41
|
+
xhr.setRequestHeader('token', localStorage.getItem('token') as string)
|
|
42
|
+
if (options?.success) {
|
|
43
|
+
// 上传完成
|
|
44
|
+
xhr.onload = (e) => {
|
|
45
|
+
const target = e?.currentTarget as any
|
|
46
|
+
if (target.response) {
|
|
47
|
+
options.success(JSON.parse(target.response))
|
|
48
|
+
return
|
|
49
|
+
}
|
|
50
|
+
options.success()
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
if (options?.error) {
|
|
54
|
+
// 上传出错
|
|
55
|
+
xhr.onerror = options.error
|
|
56
|
+
}
|
|
57
|
+
if (options?.process) {
|
|
58
|
+
// 上传中
|
|
59
|
+
xhr.onprogress = (e) => {
|
|
60
|
+
const { total, loaded, lengthComputable } = e
|
|
61
|
+
if (lengthComputable) {
|
|
62
|
+
options.process((loaded / total).toFixed(2))
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// 发送请求
|
|
68
|
+
xhr.send(form)
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
export interface tableItem {
|
|
72
|
+
value: string
|
|
73
|
+
col?: number
|
|
74
|
+
row?: number
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* 导出表格数据为xls
|
|
78
|
+
* @param headers 头部
|
|
79
|
+
* @param body 身体部分数据
|
|
80
|
+
*/
|
|
81
|
+
export function tableToExcel(
|
|
82
|
+
headers: (string | tableItem)[],
|
|
83
|
+
body: any[],
|
|
84
|
+
filename = 'res.xlsx'
|
|
85
|
+
) {
|
|
86
|
+
// 列标题
|
|
87
|
+
let str = `<tr>${headers
|
|
88
|
+
.map((v) => {
|
|
89
|
+
if (v instanceof Object) {
|
|
90
|
+
const { value, col = 1, row = 1 } = v
|
|
91
|
+
return `<th colspan="${col}" rowspan="${row}">${value}</th>`
|
|
92
|
+
}
|
|
93
|
+
return `<th>${v}</th>`
|
|
94
|
+
})
|
|
95
|
+
.join('')}</tr>`
|
|
96
|
+
// 循环遍历,每行加入tr标签,每个单元格加td标签
|
|
97
|
+
for (const row of body) {
|
|
98
|
+
str += '<tr>'
|
|
99
|
+
for (const cell of row) {
|
|
100
|
+
if (cell instanceof Object) {
|
|
101
|
+
const { value, col = 1, row = 1 } = cell
|
|
102
|
+
str += `<td colspan="${col}" rowspan="${row}">${value}</td>`
|
|
103
|
+
}
|
|
104
|
+
// 增加\t为了不让表格显示科学计数法或者其他格式
|
|
105
|
+
str += `<td>${`${cell}\t`}</td>`
|
|
106
|
+
}
|
|
107
|
+
str += '</tr>'
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
// Worksheet名
|
|
111
|
+
// const worksheet = 'sheet1'
|
|
112
|
+
// const uri = 'data:application/vnd.ms-excel;base64,'
|
|
113
|
+
|
|
114
|
+
// // 下载的表格模板数据
|
|
115
|
+
// const template =
|
|
116
|
+
// '<html xmlns:o="urn:schemas-microsoft-com:office:office" \n' +
|
|
117
|
+
// ' xmlns:x="urn:schemas-microsoft-com:office:excel" \n' +
|
|
118
|
+
// ' xmlns="http://www.w3.org/TR/REC-html40">\n' +
|
|
119
|
+
// ' <head><!--[if gte mso 9]><xml><x:ExcelWorkbook><x:ExcelWorksheets><x:ExcelWorksheet>\n' +
|
|
120
|
+
// ` <x:Name>${worksheet}</x:Name>\n` +
|
|
121
|
+
// ' <x:WorksheetOptions><x:DisplayGridlines/></x:WorksheetOptions></x:ExcelWorksheet>\n' +
|
|
122
|
+
// ' </x:ExcelWorksheets></x:ExcelWorkbook></xml><![endif]-->\n' +
|
|
123
|
+
// ` </head><body><table>${str}</table></body></html>\n`
|
|
124
|
+
// 下载模板
|
|
125
|
+
// const tempA = document.createElement('a')
|
|
126
|
+
// tempA.href = uri + base64(template)
|
|
127
|
+
// tempA.download = filename
|
|
128
|
+
// document.body.appendChild(tempA)
|
|
129
|
+
// tempA.click()
|
|
130
|
+
// document.body.removeChild(tempA)
|
|
131
|
+
const tableElement = document.createElement('table')
|
|
132
|
+
tableElement.innerHTML = str
|
|
133
|
+
const wb = XLSX.utils.table_to_book(tableElement)
|
|
134
|
+
XLSX.writeFile(wb, filename)
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* 七牛云上传
|
|
139
|
+
*/
|
|
140
|
+
export function qiniuUpload(
|
|
141
|
+
token: string,
|
|
142
|
+
file: File,
|
|
143
|
+
key: string,
|
|
144
|
+
options?: UploadFileOptions
|
|
145
|
+
) {
|
|
146
|
+
const observable = qiniu.upload(file, key, token)
|
|
147
|
+
const { success, error, process } = options || {}
|
|
148
|
+
const subscription = observable.subscribe({
|
|
149
|
+
next(res) {
|
|
150
|
+
const {
|
|
151
|
+
total: { percent }
|
|
152
|
+
} = res
|
|
153
|
+
if (process) {
|
|
154
|
+
process(percent.toFixed(2), res, subscription)
|
|
155
|
+
}
|
|
156
|
+
},
|
|
157
|
+
error(err) {
|
|
158
|
+
if (error) {
|
|
159
|
+
error(err, subscription)
|
|
160
|
+
}
|
|
161
|
+
},
|
|
162
|
+
complete(res) {
|
|
163
|
+
if (success) {
|
|
164
|
+
success(res, subscription)
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
})
|
|
168
|
+
// subscription.close() // 取消上传
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
export function downLoadByUrl(url: string, filename = `${Date.now()}`) {
|
|
172
|
+
const a = document.createElement('a')
|
|
173
|
+
a.href = url
|
|
174
|
+
a.target = '_blank'
|
|
175
|
+
a.download = filename
|
|
176
|
+
a.click()
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
/**
|
|
180
|
+
* 解决图片被预览的问题
|
|
181
|
+
* @param url
|
|
182
|
+
* @param filename
|
|
183
|
+
*/
|
|
184
|
+
export function downLoadByXhr(
|
|
185
|
+
url: string,
|
|
186
|
+
filename = `${Date.now()}`,
|
|
187
|
+
options?: {
|
|
188
|
+
progress: (loaded: number, total: number) => void
|
|
189
|
+
success: (res) => void
|
|
190
|
+
}
|
|
191
|
+
) {
|
|
192
|
+
const { progress, success } = options || {}
|
|
193
|
+
const xhr = new XMLHttpRequest()
|
|
194
|
+
xhr.open('GET', url)
|
|
195
|
+
// 设置返回数据的类型为blob
|
|
196
|
+
xhr.responseType = 'blob'
|
|
197
|
+
|
|
198
|
+
// 增加的代码
|
|
199
|
+
xhr.onprogress = function (e) {
|
|
200
|
+
const { total, loaded } = e
|
|
201
|
+
if (typeof progress === 'function') {
|
|
202
|
+
progress(loaded, total)
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
// 资源完成下载
|
|
207
|
+
xhr.onload = function () {
|
|
208
|
+
let name = filename
|
|
209
|
+
// 获取响应的blob对象
|
|
210
|
+
const blob = xhr.response
|
|
211
|
+
const a = document.createElement('a')
|
|
212
|
+
// 设置下载的文件名字
|
|
213
|
+
name = name || blob.name || 'download'
|
|
214
|
+
a.download = name
|
|
215
|
+
|
|
216
|
+
// 解决安全问题,新页面的window.opener 指向前一个页面的window对象
|
|
217
|
+
// 使用noopener使 window.opener 获取的值为null
|
|
218
|
+
a.rel = 'noopener'
|
|
219
|
+
|
|
220
|
+
// 创建一个DOMString指向这个blob
|
|
221
|
+
// 简单理解就是为这个blob对象生成一个可访问的链接
|
|
222
|
+
a.href = URL.createObjectURL(blob)
|
|
223
|
+
|
|
224
|
+
// 40s后移除这个临时链接
|
|
225
|
+
setTimeout(() => {
|
|
226
|
+
URL.revokeObjectURL(a.href)
|
|
227
|
+
}, 4e4) // 40s
|
|
228
|
+
// 触发a标签,执行下载
|
|
229
|
+
setTimeout(() => {
|
|
230
|
+
a.dispatchEvent(new MouseEvent('click'))
|
|
231
|
+
success(xhr)
|
|
232
|
+
}, 0)
|
|
233
|
+
}
|
|
234
|
+
// 发送请求
|
|
235
|
+
xhr.send()
|
|
236
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/* eslint-disable prefer-rest-params */
|
|
2
|
+
/* eslint-disable @typescript-eslint/no-this-alias */
|
|
3
|
+
export function debounce(func, wait = 1000, immediate = false) {
|
|
4
|
+
let timeout
|
|
5
|
+
let count = 0
|
|
6
|
+
return function () {
|
|
7
|
+
count += 1
|
|
8
|
+
const context = this
|
|
9
|
+
const args = arguments
|
|
10
|
+
const later = function () {
|
|
11
|
+
timeout = null
|
|
12
|
+
if (count > 0) {
|
|
13
|
+
func.apply(context, args)
|
|
14
|
+
count = 0
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
const callNow = immediate && !timeout
|
|
18
|
+
clearTimeout(timeout)
|
|
19
|
+
timeout = setTimeout(later, wait)
|
|
20
|
+
if (callNow) {
|
|
21
|
+
func.apply(context, args)
|
|
22
|
+
count = 0
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
}
|
|
@@ -0,0 +1,242 @@
|
|
|
1
|
+
import { ElMessage } from 'element-plus'
|
|
2
|
+
import SparkMD5 from 'spark-md5'
|
|
3
|
+
import copy from 'clipboard-copy'
|
|
4
|
+
import { jsonp } from './networkUtil'
|
|
5
|
+
/**
|
|
6
|
+
* 将结果写入的剪贴板
|
|
7
|
+
* @param {String} text
|
|
8
|
+
*/
|
|
9
|
+
export function copyRes(text: string, msg = '结果已成功复制到剪贴板') {
|
|
10
|
+
// 自定义
|
|
11
|
+
// const input = document.createElement('input')
|
|
12
|
+
// document.body.appendChild(input)
|
|
13
|
+
// input.setAttribute('value', text)
|
|
14
|
+
// input.select()
|
|
15
|
+
// if (document.execCommand('copy')) {
|
|
16
|
+
// document.execCommand('copy')
|
|
17
|
+
// }
|
|
18
|
+
// document.body.removeChild(input)
|
|
19
|
+
|
|
20
|
+
// 第三方
|
|
21
|
+
copy(text)
|
|
22
|
+
.then(() => {
|
|
23
|
+
if (msg) {
|
|
24
|
+
ElMessage.success(msg)
|
|
25
|
+
}
|
|
26
|
+
})
|
|
27
|
+
.catch((err) => {
|
|
28
|
+
// TODO:错误上报API接入
|
|
29
|
+
console.error(err)
|
|
30
|
+
ElMessage.warning('不支持自动复制,请手动选择复制')
|
|
31
|
+
})
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* 生成短地址
|
|
36
|
+
* @param url
|
|
37
|
+
*/
|
|
38
|
+
export function getShortUrl(text: string): Promise<string> {
|
|
39
|
+
return new Promise((resolve, reject) => {
|
|
40
|
+
jsonp(
|
|
41
|
+
`https://api.suowo.cn/api.htm?format=jsonp&url=${encodeURIComponent(
|
|
42
|
+
text
|
|
43
|
+
)}&key=5ec8a001be96bd79a37f19b8@bf33c7483d0c6900bb7bc90a0e6dfdf0&expireDate=2030-03-31&domain=0`,
|
|
44
|
+
'shortLink',
|
|
45
|
+
(res) => {
|
|
46
|
+
const { url, err } = res
|
|
47
|
+
if (err) {
|
|
48
|
+
reject(err)
|
|
49
|
+
}
|
|
50
|
+
resolve(url)
|
|
51
|
+
}
|
|
52
|
+
)
|
|
53
|
+
})
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export function base64(s: string) {
|
|
57
|
+
return window.btoa(unescape(encodeURIComponent(s)))
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
export function formatDate(d: Date, fmt = 'yyyy-MM-dd hh:mm:ss') {
|
|
61
|
+
if (!(d instanceof Date)) {
|
|
62
|
+
d = new Date(d)
|
|
63
|
+
}
|
|
64
|
+
const o: any = {
|
|
65
|
+
'M+': d.getMonth() + 1, // 月份
|
|
66
|
+
'd+': d.getDate(), // 日
|
|
67
|
+
'h+': d.getHours(), // 小时
|
|
68
|
+
'm+': d.getMinutes(), // 分
|
|
69
|
+
's+': d.getSeconds(), // 秒
|
|
70
|
+
'q+': Math.floor((d.getMonth() + 3) / 3), // 季度
|
|
71
|
+
S: d.getMilliseconds() // 毫秒
|
|
72
|
+
}
|
|
73
|
+
if (/(y+)/.test(fmt)) {
|
|
74
|
+
fmt = fmt.replace(
|
|
75
|
+
RegExp.$1,
|
|
76
|
+
`${d.getFullYear()}`.substr(4 - RegExp.$1.length)
|
|
77
|
+
)
|
|
78
|
+
}
|
|
79
|
+
// eslint-disable-next-line no-restricted-syntax
|
|
80
|
+
for (const k in o) {
|
|
81
|
+
if (new RegExp(`(${k})`).test(fmt))
|
|
82
|
+
fmt = fmt.replace(
|
|
83
|
+
RegExp.$1,
|
|
84
|
+
RegExp.$1.length === 1 ? o[k] : `00${o[k]}`.substr(`${o[k]}`.length)
|
|
85
|
+
)
|
|
86
|
+
}
|
|
87
|
+
return fmt
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
export function getFileSuffix(str: string) {
|
|
91
|
+
const startIndex = str.lastIndexOf('.')
|
|
92
|
+
return startIndex >= 0 ? str.slice(startIndex) : ''
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
export function getFileMd5Hash(file: File) {
|
|
96
|
+
return new Promise((resolve, reject) => {
|
|
97
|
+
const blobSlice = File.prototype.slice
|
|
98
|
+
const chunkSize = 2097152 // Read in chunks of 2MB
|
|
99
|
+
const chunks = Math.ceil(file.size / chunkSize)
|
|
100
|
+
let currentChunk = 0
|
|
101
|
+
const spark = new SparkMD5.ArrayBuffer()
|
|
102
|
+
const fileReader = new FileReader()
|
|
103
|
+
|
|
104
|
+
function loadNext() {
|
|
105
|
+
const start = currentChunk * chunkSize
|
|
106
|
+
const end = start + chunkSize >= file.size ? file.size : start + chunkSize
|
|
107
|
+
|
|
108
|
+
fileReader.readAsArrayBuffer(blobSlice.call(file, start, end))
|
|
109
|
+
}
|
|
110
|
+
fileReader.onload = function (e) {
|
|
111
|
+
// console.log('read chunk nr', currentChunk + 1, 'of', chunks)
|
|
112
|
+
spark.append(e?.target?.result) // Append array buffer
|
|
113
|
+
currentChunk += 1
|
|
114
|
+
|
|
115
|
+
if (currentChunk < chunks) {
|
|
116
|
+
loadNext()
|
|
117
|
+
} else {
|
|
118
|
+
// console.log('finished loading')
|
|
119
|
+
const hashResult = spark.end()
|
|
120
|
+
// console.info('computed hash', hashResult) // Compute hash
|
|
121
|
+
resolve(hashResult)
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
fileReader.onerror = function () {
|
|
126
|
+
reject(new Error('oops, something went wrong.'))
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
loadNext()
|
|
130
|
+
})
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
export function formatSize(
|
|
134
|
+
size: number,
|
|
135
|
+
pointLength?: number,
|
|
136
|
+
units?: string[]
|
|
137
|
+
) {
|
|
138
|
+
let unit
|
|
139
|
+
units = units || ['B', 'K', 'M', 'G', 'TB']
|
|
140
|
+
// eslint-disable-next-line no-cond-assign
|
|
141
|
+
while ((unit = units.shift()) && size > 1024) {
|
|
142
|
+
size /= 1024
|
|
143
|
+
}
|
|
144
|
+
return (
|
|
145
|
+
(unit === 'B'
|
|
146
|
+
? size
|
|
147
|
+
: size.toFixed(pointLength === undefined ? 2 : pointLength)) + unit
|
|
148
|
+
)
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
/**
|
|
152
|
+
* 浏览器支持预览的类型
|
|
153
|
+
*/
|
|
154
|
+
function getSupportPreviewType() {
|
|
155
|
+
const types = [
|
|
156
|
+
'image/jpeg',
|
|
157
|
+
'image/png',
|
|
158
|
+
'image/gif',
|
|
159
|
+
'image/bmp',
|
|
160
|
+
'image/webp',
|
|
161
|
+
'image/svg+xml',
|
|
162
|
+
'application/pdf',
|
|
163
|
+
'text/plain',
|
|
164
|
+
'video/mp4'
|
|
165
|
+
]
|
|
166
|
+
const supportTypes = []
|
|
167
|
+
types.forEach((type) => {
|
|
168
|
+
if (
|
|
169
|
+
typeof FileReader !== 'undefined' &&
|
|
170
|
+
typeof FileReader.prototype.readAsDataURL !== 'undefined'
|
|
171
|
+
) {
|
|
172
|
+
const fileReader = new FileReader()
|
|
173
|
+
if (fileReader.readAsDataURL) {
|
|
174
|
+
try {
|
|
175
|
+
fileReader.readAsDataURL(new Blob([new ArrayBuffer(1)], { type }))
|
|
176
|
+
supportTypes.push(type)
|
|
177
|
+
} catch (e) {
|
|
178
|
+
// console.log(e)
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
})
|
|
183
|
+
return supportTypes
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
export function isSupportPreview(type: string) {
|
|
187
|
+
const supportTypes = getSupportPreviewType()
|
|
188
|
+
return supportTypes.includes(type)
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
export function parseInfo(info: string): InfoItem[] {
|
|
192
|
+
return JSON.parse(info).map((v: string | InfoItem) => {
|
|
193
|
+
// 兼容旧表单数据
|
|
194
|
+
if (typeof v === 'string') {
|
|
195
|
+
return { type: 'input', text: v, value: '' }
|
|
196
|
+
}
|
|
197
|
+
// 兼容旧数据展示
|
|
198
|
+
if (!v.type) {
|
|
199
|
+
v.type = 'input'
|
|
200
|
+
}
|
|
201
|
+
v.value = v.value || ''
|
|
202
|
+
return v
|
|
203
|
+
})
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
/**
|
|
207
|
+
* 文件名合法化
|
|
208
|
+
*/
|
|
209
|
+
export function normalizeFileName(name: string) {
|
|
210
|
+
return name.replace(/[\\/:*?"<>|]/g, '-')
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
export const getDefaultFormat = () => {
|
|
214
|
+
return {
|
|
215
|
+
size: 0,
|
|
216
|
+
sizeUnit: 'MB',
|
|
217
|
+
status: false,
|
|
218
|
+
format: [],
|
|
219
|
+
limit: 10,
|
|
220
|
+
splitChar: '-'
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
export function parseFileFormat(format: string) {
|
|
224
|
+
const formatData = getDefaultFormat()
|
|
225
|
+
try {
|
|
226
|
+
const v = JSON.parse(format)
|
|
227
|
+
Object.keys(v).forEach((key) => {
|
|
228
|
+
formatData[key] = v[key] || formatData[key]
|
|
229
|
+
})
|
|
230
|
+
} catch (_) {
|
|
231
|
+
return formatData
|
|
232
|
+
}
|
|
233
|
+
return formatData
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
export function getTipImageKey(
|
|
237
|
+
key: string,
|
|
238
|
+
name: string,
|
|
239
|
+
uid?: number | string
|
|
240
|
+
) {
|
|
241
|
+
return `easypicker2/tip/${key}/${uid || Date.now()}/${name}`
|
|
242
|
+
}
|
package/tsconfig.json
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"target": "esnext",
|
|
4
|
+
"module": "esnext",
|
|
5
|
+
"moduleResolution": "node",
|
|
6
|
+
"strict": false,
|
|
7
|
+
"jsx": "preserve",
|
|
8
|
+
"sourceMap": true,
|
|
9
|
+
"resolveJsonModule": true,
|
|
10
|
+
"esModuleInterop": true,
|
|
11
|
+
"allowJs": true,
|
|
12
|
+
"lib": ["esnext", "dom"],
|
|
13
|
+
"types": ["vite/client","node","element-plus/global"],
|
|
14
|
+
"baseUrl": "./",
|
|
15
|
+
"paths": {
|
|
16
|
+
"@/*": [
|
|
17
|
+
"src/*"
|
|
18
|
+
],
|
|
19
|
+
"@components/":["src/components/*"]
|
|
20
|
+
}
|
|
21
|
+
},
|
|
22
|
+
"include": ["src/**/*.ts", "src/**/*.d.ts", "src/**/*.tsx", "src/**/*.vue", "docs/**/*.ts", "docs/**/*.d.ts"],
|
|
23
|
+
"exclude": ["vite.config.ts"]
|
|
24
|
+
}
|
package/vite.config.ts
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { defineConfig } from 'vite'
|
|
2
|
+
import vue from '@vitejs/plugin-vue'
|
|
3
|
+
import path from 'path'
|
|
4
|
+
import AutoImport from 'unplugin-auto-import/vite'
|
|
5
|
+
import Components from 'unplugin-vue-components/vite'
|
|
6
|
+
import ElementPlus from 'unplugin-element-plus/vite'
|
|
7
|
+
import { ElementPlusResolver } from 'unplugin-vue-components/resolvers'
|
|
8
|
+
import legacy from '@vitejs/plugin-legacy'
|
|
9
|
+
|
|
10
|
+
// https://vitejs.dev/config/
|
|
11
|
+
export default defineConfig({
|
|
12
|
+
plugins: [
|
|
13
|
+
legacy({
|
|
14
|
+
targets: ['defaults', 'not IE 11']
|
|
15
|
+
}),
|
|
16
|
+
vue(),
|
|
17
|
+
AutoImport({
|
|
18
|
+
resolvers: [ElementPlusResolver()]
|
|
19
|
+
}),
|
|
20
|
+
Components({
|
|
21
|
+
resolvers: [ElementPlusResolver()]
|
|
22
|
+
})
|
|
23
|
+
// ElementPlus({
|
|
24
|
+
// defaultLocale: 'zh-cn'
|
|
25
|
+
// })
|
|
26
|
+
],
|
|
27
|
+
optimizeDeps: {
|
|
28
|
+
include: ['vue', 'vue-router', 'vuex', 'axios', 'vue-json-viewer']
|
|
29
|
+
},
|
|
30
|
+
build: {
|
|
31
|
+
target: 'modules', // 默认值
|
|
32
|
+
sourcemap: true
|
|
33
|
+
},
|
|
34
|
+
server: {
|
|
35
|
+
host: '0.0.0.0',
|
|
36
|
+
proxy: {
|
|
37
|
+
'/api/': {
|
|
38
|
+
target: 'http://localhost:3000',
|
|
39
|
+
changeOrigin: true,
|
|
40
|
+
rewrite: (p) => p.replace(/^\/api/, '')
|
|
41
|
+
},
|
|
42
|
+
'/api-test/': {
|
|
43
|
+
target: 'https://ep.dev.sugarat.top',
|
|
44
|
+
changeOrigin: true,
|
|
45
|
+
rewrite: (p) => p.replace(/^\/api-test/, 'api/')
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
},
|
|
49
|
+
resolve: {
|
|
50
|
+
alias: {
|
|
51
|
+
'@': path.resolve(__dirname, './src'),
|
|
52
|
+
'@components': path.resolve(__dirname, './src/components')
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
})
|