@netang/quasar 0.2.20 → 0.2.21
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/.editorconfig +12 -12
- package/_docs/docs/components/field-table.md +58 -58
- package/_docs/docs/components/field-tree.md +21 -21
- package/_docs/docs/components/table.md +24 -24
- package/_docs/docs/utils/table.md +196 -196
- package/components/column-title/index.vue +38 -38
- package/components/data/index.vue +20 -20
- package/components/dialog/img-viewer/index.vue +697 -697
- package/components/dragger/index.vue +203 -203
- package/components/drawer/index.vue +303 -303
- package/components/editor-code/index.vue +325 -325
- package/components/empty/index.vue +82 -82
- package/components/field-date/index.vue +850 -850
- package/components/field-date/methods.js +100 -100
- package/components/field-table/index.vue +1482 -1482
- package/components/field-text/index.vue +165 -165
- package/components/field-tree/index.vue +754 -754
- package/components/img/index.vue +279 -279
- package/components/list-menu/index.vue +149 -149
- package/components/list-menu-item/index.vue +79 -79
- package/components/mixed-table/index.vue +532 -532
- package/components/mixed-table-splitter/index.vue +377 -377
- package/components/power-page/index.vue +96 -96
- package/components/price/index.vue +188 -188
- package/components/private/components/index.js +11 -11
- package/components/private/components/move-to-tree/index.vue +154 -154
- package/components/private/edit-power-data/index.vue +846 -846
- package/components/private/table-visible-columns-button/index.vue +114 -114
- package/components/render/index.vue +123 -123
- package/components/search/index.vue +231 -231
- package/components/search-item/index.vue +210 -210
- package/components/select/index.vue +177 -177
- package/components/splitter/index.vue +422 -422
- package/components/table/index.vue +513 -513
- package/components/table-column-fixed/index.vue +110 -110
- package/components/table-pagination/index.vue +192 -192
- package/components/table-summary/index.vue +107 -107
- package/components/thumbnail/index.vue +72 -72
- package/components/toolbar/index.vue +146 -146
- package/components/tree/index.vue +1728 -1728
- package/components/uploader/index.vue +195 -195
- package/components/uploader-query/index.vue +945 -945
- package/components/value-format/index.vue +274 -274
- package/configs/area3.js +1 -1
- package/docs/404.html +33 -33
- package/docs/assets/404.html-60b35caa.js +1 -1
- package/docs/assets/404.html-d1e63d77.js +1 -1
- package/docs/assets/alert.html-b2a2a72f.js +5 -5
- package/docs/assets/alert.html-ba46d137.js +1 -1
- package/docs/assets/app-9f30aa4b.js +6 -6
- package/docs/assets/area.html-01b9b58d.js +42 -42
- package/docs/assets/area.html-9a4fce6a.js +1 -1
- package/docs/assets/arr.html-145d27e7.js +1 -1
- package/docs/assets/arr.html-674e65ab.js +11 -11
- package/docs/assets/auth.html-579fa830.js +1 -1
- package/docs/assets/auth.html-8544ed95.js +8 -8
- package/docs/assets/bus.html-c71254aa.js +1 -1
- package/docs/assets/bus.html-dc7d3d19.js +6 -6
- package/docs/assets/column-title.html-c735cb5a.js +3 -3
- package/docs/assets/column-title.html-e9316762.js +1 -1
- package/docs/assets/confirm.html-ddfdc27f.js +10 -10
- package/docs/assets/confirm.html-ef3e2bef.js +1 -1
- package/docs/assets/copy.html-d20345b6.js +1 -1
- package/docs/assets/copy.html-ef8c8571.js +13 -13
- package/docs/assets/data.html-6432175d.js +30 -30
- package/docs/assets/data.html-a3b05d5b.js +1 -1
- package/docs/assets/dialog.html-1f698e5a.js +1 -1
- package/docs/assets/dialog.html-62902b83.js +68 -68
- package/docs/assets/dialog.html-baea77c9.js +1 -1
- package/docs/assets/dialog.html-bb082fc4.js +1 -1
- package/docs/assets/dict.html-1311da3d.js +23 -23
- package/docs/assets/dict.html-b96fbf0c.js +1 -1
- package/docs/assets/dictOptions.html-7c4f40a5.js +1 -1
- package/docs/assets/dictOptions.html-fb99d175.js +5 -5
- package/docs/assets/dragger.html-668d3efa.js +1 -1
- package/docs/assets/dragger.html-749d585a.js +1 -1
- package/docs/assets/editor-code.html-6ab26ea9.js +1 -1
- package/docs/assets/editor-code.html-d196205d.js +1 -1
- package/docs/assets/empty.html-1c139131.js +1 -1
- package/docs/assets/empty.html-1e9c441d.js +1 -1
- package/docs/assets/field-date.html-069fdb13.js +1 -1
- package/docs/assets/field-date.html-ad204aa9.js +1 -1
- package/docs/assets/field-table.html-ce480f03.js +1 -1
- package/docs/assets/field-table.html-d9236160.js +1 -1
- package/docs/assets/field-text.html-7277c62f.js +1 -1
- package/docs/assets/field-text.html-ccb4cecf.js +1 -1
- package/docs/assets/field-tree.html-519bfb45.js +1 -1
- package/docs/assets/field-tree.html-fdc748d6.js +1 -1
- package/docs/assets/form.html-2b562c37.js +2 -2
- package/docs/assets/form.html-75104cd5.js +1 -1
- package/docs/assets/framework-204010b2.js +5 -5
- package/docs/assets/getData.html-990e3787.js +1 -1
- package/docs/assets/getData.html-bb72025f.js +34 -34
- package/docs/assets/getFile.html-42368004.js +1 -1
- package/docs/assets/getFile.html-99abd054.js +3 -3
- package/docs/assets/getImage.html-3429c5a1.js +1 -1
- package/docs/assets/getImage.html-4d886d83.js +3 -3
- package/docs/assets/getTime.html-7435f922.js +1 -1
- package/docs/assets/getTime.html-b37f49eb.js +20 -20
- package/docs/assets/img.html-7d1da657.js +1 -1
- package/docs/assets/img.html-fbea1105.js +1 -1
- package/docs/assets/index.html-1695dd7c.js +1 -1
- package/docs/assets/index.html-65a4aa67.js +1 -1
- package/docs/assets/index.html-7b98d5bd.js +1 -1
- package/docs/assets/index.html-c01f2648.js +1 -1
- package/docs/assets/input-number.html-0b250d2a.js +1 -1
- package/docs/assets/input-number.html-a8eb0378.js +1 -1
- package/docs/assets/list-menu-item.html-7f1b4611.js +1 -1
- package/docs/assets/list-menu-item.html-84ed5ab8.js +1 -1
- package/docs/assets/list-menu.html-28b4163f.js +1 -1
- package/docs/assets/list-menu.html-cb6ba95b.js +1 -1
- package/docs/assets/loading.html-dae9e39d.js +6 -6
- package/docs/assets/loading.html-dc74c9e6.js +1 -1
- package/docs/assets/notify.html-e6c4c514.js +1 -1
- package/docs/assets/notify.html-f2c4d914.js +8 -8
- package/docs/assets/power-page.html-32e02f82.js +1 -1
- package/docs/assets/power-page.html-485e77da.js +1 -1
- package/docs/assets/power.html-d258cc19.js +93 -93
- package/docs/assets/power.html-e490bd32.js +1 -1
- package/docs/assets/previewImage.html-6a6b4245.js +1 -1
- package/docs/assets/previewImage.html-c5b7e945.js +2 -2
- package/docs/assets/price.html-1882c548.js +19 -19
- package/docs/assets/price.html-94d3f5be.js +1 -1
- package/docs/assets/price.html-d213df0f.js +1 -1
- package/docs/assets/price.html-deaf880f.js +1 -1
- package/docs/assets/render.html-8efcbdd4.js +1 -1
- package/docs/assets/render.html-df228e38.js +1 -1
- package/docs/assets/rule.html-2cd57fc2.js +13 -13
- package/docs/assets/rule.html-61662001.js +1 -1
- package/docs/assets/ruleValid.html-04fe2552.js +1 -1
- package/docs/assets/ruleValid.html-e0a776af.js +14 -14
- package/docs/assets/search-0782d0d1.svg +1 -1
- package/docs/assets/search-item.html-3f75394c.js +1 -1
- package/docs/assets/search-item.html-4e942ecd.js +1 -1
- package/docs/assets/search.html-2807043e.js +1 -1
- package/docs/assets/search.html-c24f8806.js +1 -1
- package/docs/assets/select.html-00d0607c.js +1 -1
- package/docs/assets/select.html-de7731f5.js +1 -1
- package/docs/assets/splitter.html-56f51a70.js +1 -1
- package/docs/assets/splitter.html-f5c836d7.js +1 -1
- package/docs/assets/style-161e43ab.css +1 -1
- package/docs/assets/symbols.html-a6aea4bf.js +1 -1
- package/docs/assets/symbols.html-b1f65bad.js +21 -21
- package/docs/assets/table-column-fixed.html-3a69e7b2.js +1 -1
- package/docs/assets/table-column-fixed.html-e763c38b.js +1 -1
- package/docs/assets/table-pagination.html-236934d3.js +1 -1
- package/docs/assets/table-pagination.html-c37ee2ac.js +1 -1
- package/docs/assets/table-splitter.html-07eab15c.js +1 -1
- package/docs/assets/table-splitter.html-7670ee65.js +1 -1
- package/docs/assets/table-summary.html-04db434f.js +1 -1
- package/docs/assets/table-summary.html-943c65a0.js +1 -1
- package/docs/assets/table.html-36253ad7.js +1 -1
- package/docs/assets/table.html-7f9c5d1b.js +38 -38
- package/docs/assets/table.html-93d53dc8.js +1 -1
- package/docs/assets/table.html-ac99b9cb.js +1 -1
- package/docs/assets/thumbnail.html-bab1976b.js +1 -1
- package/docs/assets/thumbnail.html-eb64e5e8.js +1 -1
- package/docs/assets/timestamp.html-4e54f79b.js +13 -13
- package/docs/assets/timestamp.html-d0e1b88a.js +1 -1
- package/docs/assets/toast.html-58ecbe21.js +1 -1
- package/docs/assets/toast.html-c9b9d36b.js +6 -6
- package/docs/assets/toolbar.html-83d9f97c.js +1 -1
- package/docs/assets/toolbar.html-ff7b8c92.js +1 -1
- package/docs/assets/tree.html-d07cbe79.js +23 -23
- package/docs/assets/tree.html-ea04193e.js +1 -1
- package/docs/assets/uploader-query.html-05590718.js +1 -1
- package/docs/assets/uploader-query.html-3175bac5.js +1 -1
- package/docs/assets/uploader.html-36da4394.js +2 -2
- package/docs/assets/uploader.html-6b5f3079.js +1 -1
- package/docs/assets/uploader.html-b9340b57.js +1 -1
- package/docs/assets/uploader.html-bc1c22e3.js +1 -1
- package/docs/assets/value-format.html-8ae3d47d.js +1 -1
- package/docs/assets/value-format.html-afa99b3d.js +1 -1
- package/docs/components/column-title.html +35 -35
- package/docs/components/data.html +62 -62
- package/docs/components/dialog.html +33 -33
- package/docs/components/dragger.html +33 -33
- package/docs/components/editor-code.html +33 -33
- package/docs/components/empty.html +33 -33
- package/docs/components/field-date.html +33 -33
- package/docs/components/field-table.html +33 -33
- package/docs/components/field-text.html +33 -33
- package/docs/components/field-tree.html +33 -33
- package/docs/components/img.html +33 -33
- package/docs/components/input-number.html +33 -33
- package/docs/components/list-menu-item.html +33 -33
- package/docs/components/list-menu.html +33 -33
- package/docs/components/power-page.html +33 -33
- package/docs/components/price.html +33 -33
- package/docs/components/render.html +33 -33
- package/docs/components/search-item.html +33 -33
- package/docs/components/search.html +33 -33
- package/docs/components/select.html +33 -33
- package/docs/components/splitter.html +33 -33
- package/docs/components/table-column-fixed.html +33 -33
- package/docs/components/table-pagination.html +33 -33
- package/docs/components/table-splitter.html +33 -33
- package/docs/components/table-summary.html +33 -33
- package/docs/components/table.html +33 -33
- package/docs/components/thumbnail.html +33 -33
- package/docs/components/toolbar.html +33 -33
- package/docs/components/uploader-query.html +33 -33
- package/docs/components/uploader.html +33 -33
- package/docs/components/value-format.html +33 -33
- package/docs/css/index.css +3 -3
- package/docs/index.html +33 -33
- package/docs/utils/alert.html +37 -37
- package/docs/utils/area.html +74 -74
- package/docs/utils/arr.html +43 -43
- package/docs/utils/auth.html +40 -40
- package/docs/utils/bus.html +38 -38
- package/docs/utils/confirm.html +42 -42
- package/docs/utils/copy.html +45 -45
- package/docs/utils/dialog.html +100 -100
- package/docs/utils/dict.html +55 -55
- package/docs/utils/dictOptions.html +37 -37
- package/docs/utils/form.html +34 -34
- package/docs/utils/getData.html +66 -66
- package/docs/utils/getFile.html +35 -35
- package/docs/utils/getImage.html +35 -35
- package/docs/utils/getTime.html +52 -52
- package/docs/utils/index.html +33 -33
- package/docs/utils/loading.html +38 -38
- package/docs/utils/notify.html +40 -40
- package/docs/utils/power.html +125 -125
- package/docs/utils/previewImage.html +34 -34
- package/docs/utils/price.html +51 -51
- package/docs/utils/rule.html +45 -45
- package/docs/utils/ruleValid.html +46 -46
- package/docs/utils/symbols.html +53 -53
- package/docs/utils/table.html +70 -70
- package/docs/utils/timestamp.html +45 -45
- package/docs/utils/toast.html +38 -38
- package/docs/utils/tree.html +55 -55
- package/docs/utils/uploader.html +34 -34
- package/package.json +25 -25
- package/sass/common.scss +184 -184
- package/sass/index.scss +12 -12
- package/sass/line.scss +39 -39
- package/sass/quasar/btn.scss +46 -46
- package/sass/quasar/common.scss +3 -3
- package/sass/quasar/drawer.scss +6 -6
- package/sass/quasar/field.scss +259 -259
- package/sass/quasar/loading.scss +6 -6
- package/sass/quasar/table.scss +168 -168
- package/sass/quasar/toolbar.scss +22 -22
- package/sass/variables.scss +140 -140
- package/store/index.js +29 -29
- package/utils/$auth.js +127 -127
- package/utils/$form.js +72 -72
- package/utils/$power.js +1486 -1486
- package/utils/$render.js +75 -75
- package/utils/$rule.js +13 -13
- package/utils/$ruleValid.js +10 -10
- package/utils/$search.js +416 -416
- package/utils/$table.js +1275 -1275
- package/utils/alert.js +12 -12
- package/utils/area.js +400 -400
- package/utils/arr.js +51 -51
- package/utils/bus.js +6 -6
- package/utils/config.js +64 -64
- package/utils/confirm.js +11 -11
- package/utils/copy.js +30 -30
- package/utils/dialog.js +36 -36
- package/utils/dict.js +21 -21
- package/utils/dictOptions.js +28 -28
- package/utils/getData.js +88 -88
- package/utils/getFile.js +67 -67
- package/utils/getImage.js +276 -276
- package/utils/getTime.js +113 -113
- package/utils/index.js +67 -67
- package/utils/loading.js +15 -15
- package/utils/notify.js +13 -13
- package/utils/play.js +40 -40
- package/utils/previewImage.js +14 -14
- package/utils/price.js +18 -18
- package/utils/symbols.js +18 -18
- package/utils/timestamp.js +18 -18
- package/utils/toast.js +13 -13
- package/utils/uploader.js +2114 -2114
- package/utils/useAuth.js +30 -30
- package/utils/useFileUrl.js +26 -26
- package/utils/useRouter.js +47 -47
- package/utils/useSearch.js +499 -499
package/utils/uploader.js
CHANGED
|
@@ -1,2114 +1,2114 @@
|
|
|
1
|
-
import { ref, isRef, } from 'vue'
|
|
2
|
-
import { useQuasar } from 'quasar'
|
|
3
|
-
import SparkMD5 from 'spark-md5'
|
|
4
|
-
|
|
5
|
-
import $n_has from 'lodash/has'
|
|
6
|
-
import $n_get from 'lodash/get'
|
|
7
|
-
import $n_toLower from 'lodash/toLower'
|
|
8
|
-
import $n_findIndex from 'lodash/findIndex'
|
|
9
|
-
import $n_uniq from 'lodash/uniq'
|
|
10
|
-
import $n_find from 'lodash/find'
|
|
11
|
-
import $n_isFunction from 'lodash/isFunction'
|
|
12
|
-
|
|
13
|
-
import $n_storage from '@netang/utils/storage'
|
|
14
|
-
import $n_isValidArray from '@netang/utils/isValidArray'
|
|
15
|
-
import $n_isValidObject from '@netang/utils/isValidObject'
|
|
16
|
-
import $n_isValidString from '@netang/utils/isValidString'
|
|
17
|
-
import $n_forEach from '@netang/utils/forEach'
|
|
18
|
-
import $n_indexOf from '@netang/utils/indexOf'
|
|
19
|
-
import $n_json from '@netang/utils/json'
|
|
20
|
-
import $n_join from '@netang/utils/join'
|
|
21
|
-
import $n_split from '@netang/utils/split'
|
|
22
|
-
import $n_trimString from '@netang/utils/trimString'
|
|
23
|
-
import $n_run from '@netang/utils/run'
|
|
24
|
-
import $n_isValidValue from '@netang/utils/isValidValue'
|
|
25
|
-
import $n_http from '@netang/utils/http'
|
|
26
|
-
import $n_getThrowMessage from '@netang/utils/getThrowMessage'
|
|
27
|
-
import $n_runAsync from '@netang/utils/runAsync'
|
|
28
|
-
import $n_numberDeep from '@netang/utils/numberDeep'
|
|
29
|
-
|
|
30
|
-
import $n_$ruleValid from './$ruleValid'
|
|
31
|
-
import $n_toast from './toast'
|
|
32
|
-
import $n_confirm from './confirm'
|
|
33
|
-
import $n_alert from './alert'
|
|
34
|
-
import $n_previewImage from './previewImage'
|
|
35
|
-
import $n_getImage from './getImage'
|
|
36
|
-
import $n_getFile from './getFile'
|
|
37
|
-
import $n_config from './config'
|
|
38
|
-
import $n_timestamp from './timestamp'
|
|
39
|
-
import $n_play from './play'
|
|
40
|
-
|
|
41
|
-
import copy from './copy'
|
|
42
|
-
import { configs } from './config'
|
|
43
|
-
|
|
44
|
-
/**
|
|
45
|
-
* 文件类型映射
|
|
46
|
-
*/
|
|
47
|
-
export const FilE_TYPE = {
|
|
48
|
-
file: 1,
|
|
49
|
-
image: 2,
|
|
50
|
-
video: 3,
|
|
51
|
-
audio: 4,
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
/**
|
|
55
|
-
* 文件名称映射
|
|
56
|
-
*/
|
|
57
|
-
export const FilE_NAME = {
|
|
58
|
-
1: '文件',
|
|
59
|
-
2: '图片',
|
|
60
|
-
3: '视频',
|
|
61
|
-
4: '音频',
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
/**
|
|
65
|
-
* 上传状态
|
|
66
|
-
*/
|
|
67
|
-
export const UPLOAD_STATUS = {
|
|
68
|
-
// 等待上传中
|
|
69
|
-
waiting: 1,
|
|
70
|
-
// 检查 hash 中
|
|
71
|
-
hashChecking: 2,
|
|
72
|
-
// 检查 hash 完成
|
|
73
|
-
hashChecked: 3,
|
|
74
|
-
// 检查是否存在服务器中
|
|
75
|
-
existChecking: 4,
|
|
76
|
-
// 检查是否存在服务器完成
|
|
77
|
-
existChecked: 5,
|
|
78
|
-
// 上传中
|
|
79
|
-
uploading: 6,
|
|
80
|
-
// 上传完成
|
|
81
|
-
success: 7,
|
|
82
|
-
// 上传失败
|
|
83
|
-
fail: 8,
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
// 文件数量
|
|
87
|
-
let _fileNum = 0
|
|
88
|
-
|
|
89
|
-
/**
|
|
90
|
-
* 将 base64 转换为 File
|
|
91
|
-
*/
|
|
92
|
-
function dataUrlToFile(dataUrl) {
|
|
93
|
-
const arr = dataUrl.split(',')
|
|
94
|
-
const mime = arr[0].match(/:(.*?);/)[1]
|
|
95
|
-
const bstr = window.atob(arr[1])
|
|
96
|
-
let n = bstr.length
|
|
97
|
-
const arrayBuffer = new Uint8Array(n)
|
|
98
|
-
while (n--) {
|
|
99
|
-
arrayBuffer[n] = bstr.charCodeAt(n)
|
|
100
|
-
}
|
|
101
|
-
const blob = new Blob([arrayBuffer], { type: mime })
|
|
102
|
-
|
|
103
|
-
return {
|
|
104
|
-
arrayBuffer,
|
|
105
|
-
file: new File([blob], String($n_timestamp()), { type: blob.type }),
|
|
106
|
-
}
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
/**
|
|
110
|
-
* 创建上传器
|
|
111
|
-
*/
|
|
112
|
-
function create(options) {
|
|
113
|
-
|
|
114
|
-
// ==========【数据】=========================================================================================
|
|
115
|
-
|
|
116
|
-
// quasar 对象
|
|
117
|
-
const $q = useQuasar()
|
|
118
|
-
|
|
119
|
-
const {
|
|
120
|
-
// 上传器类型
|
|
121
|
-
uploaderType,
|
|
122
|
-
// 上传文件输入框节点
|
|
123
|
-
fileRef,
|
|
124
|
-
// 更新值方法(初始化上传列表时不更新值)
|
|
125
|
-
onUpdateModelValue,
|
|
126
|
-
// 更新方法
|
|
127
|
-
onUpdate,
|
|
128
|
-
|
|
129
|
-
} = Object.assign({
|
|
130
|
-
// 上传器类型
|
|
131
|
-
uploaderType: null,
|
|
132
|
-
// 更新值方法
|
|
133
|
-
onUpdateModelValue: null,
|
|
134
|
-
// 更新方法
|
|
135
|
-
onUpdate: null,
|
|
136
|
-
}, options)
|
|
137
|
-
|
|
138
|
-
const optionsProps = $n_get(options, 'props')
|
|
139
|
-
|
|
140
|
-
// 声明属性
|
|
141
|
-
const props = Object.assign({
|
|
142
|
-
// 值
|
|
143
|
-
modelValue: '',
|
|
144
|
-
// 上传文件类型, 可选值 file image video audio
|
|
145
|
-
type: 'image',
|
|
146
|
-
// 上传文件数量(0:不限)
|
|
147
|
-
count: 0,
|
|
148
|
-
// 单个文件的最大大小(单位: MB)
|
|
149
|
-
maxSize: 0,
|
|
150
|
-
// 单个文件的限制后缀
|
|
151
|
-
exts: [],
|
|
152
|
-
// true: 值格式为数组, 如 ['xxxxxx', 'xxxxxx', 'xxxxxx']
|
|
153
|
-
// false: 值格式为字符串, 如 xxxxxx,xxxxxx,xxxxxx
|
|
154
|
-
valueArray: false,
|
|
155
|
-
// 是否去重
|
|
156
|
-
unique: false,
|
|
157
|
-
// 是否初始加载文件信息(仅图片有效, 其他类型自动会加载文件信息)
|
|
158
|
-
loadInfo: false,
|
|
159
|
-
// 单文件上传提示
|
|
160
|
-
confirm: false,
|
|
161
|
-
}, optionsProps)
|
|
162
|
-
|
|
163
|
-
// options 中是否存在 props.modelValue
|
|
164
|
-
const hasPropsModelValue = $n_has(optionsProps, 'modelValue')
|
|
165
|
-
|
|
166
|
-
// 上传文件列表
|
|
167
|
-
const uploadFileLists = $n_has(options, 'uploadFileLists') && isRef(options.uploadFileLists) ? options.uploadFileLists : ref([])
|
|
168
|
-
|
|
169
|
-
// 上传网络外链回调
|
|
170
|
-
let uploadNetCallback
|
|
171
|
-
|
|
172
|
-
/**
|
|
173
|
-
* 上传配置
|
|
174
|
-
*/
|
|
175
|
-
const configUpload = getUpload(null, uploaderType)
|
|
176
|
-
|
|
177
|
-
const configLimit = Object.assign(
|
|
178
|
-
{
|
|
179
|
-
maxSize: 100,
|
|
180
|
-
exts: [],
|
|
181
|
-
},
|
|
182
|
-
$n_config('uploader.limit.' + props.type, {})
|
|
183
|
-
)
|
|
184
|
-
|
|
185
|
-
// 如果有单个文件的最大大小
|
|
186
|
-
if (props.maxSize) {
|
|
187
|
-
configLimit.maxSize = props.maxSize
|
|
188
|
-
}
|
|
189
|
-
|
|
190
|
-
// 如果有单个文件的限制后缀
|
|
191
|
-
if ($n_isValidArray(props.exts)) {
|
|
192
|
-
configLimit.exts = props.exts
|
|
193
|
-
}
|
|
194
|
-
|
|
195
|
-
// ==========【计算属性】=========================================================================================
|
|
196
|
-
|
|
197
|
-
/**
|
|
198
|
-
* 上传文件后缀名
|
|
199
|
-
*/
|
|
200
|
-
// const accept = computed(function () {
|
|
201
|
-
//
|
|
202
|
-
// })
|
|
203
|
-
|
|
204
|
-
// ==========【监听数据】==============================================================================================
|
|
205
|
-
|
|
206
|
-
/**
|
|
207
|
-
* 监听上传文件列表
|
|
208
|
-
*/
|
|
209
|
-
// if (props.watchModelValue && hasPropsModelValue) {
|
|
210
|
-
// watch(()=>options.props.modelValue, function() {
|
|
211
|
-
// // 初始化上传列表
|
|
212
|
-
// initUploadFileLists()
|
|
213
|
-
// .finally()
|
|
214
|
-
// })
|
|
215
|
-
// }
|
|
216
|
-
|
|
217
|
-
// ==========【方法】=================================================================================================
|
|
218
|
-
|
|
219
|
-
/**
|
|
220
|
-
* 获取值
|
|
221
|
-
*/
|
|
222
|
-
function getValue() {
|
|
223
|
-
|
|
224
|
-
const hashs = []
|
|
225
|
-
const files = []
|
|
226
|
-
|
|
227
|
-
for (const fileItem of uploadFileLists.value) {
|
|
228
|
-
if (fileItem.status === UPLOAD_STATUS.success) {
|
|
229
|
-
hashs.push(fileItem.hash)
|
|
230
|
-
files.push(fileItem)
|
|
231
|
-
}
|
|
232
|
-
}
|
|
233
|
-
|
|
234
|
-
const hashsString = $n_join(hashs, ',')
|
|
235
|
-
|
|
236
|
-
return {
|
|
237
|
-
value: props.valueArray ? hashs : hashsString,
|
|
238
|
-
hashsString,
|
|
239
|
-
hashs,
|
|
240
|
-
files,
|
|
241
|
-
query: uploadFileLists,
|
|
242
|
-
}
|
|
243
|
-
}
|
|
244
|
-
|
|
245
|
-
/**
|
|
246
|
-
* 更新值
|
|
247
|
-
*/
|
|
248
|
-
function updateValue() {
|
|
249
|
-
|
|
250
|
-
// 获取值
|
|
251
|
-
const result = getValue()
|
|
252
|
-
|
|
253
|
-
// 更新值
|
|
254
|
-
$n_run(onUpdateModelValue)(result)
|
|
255
|
-
|
|
256
|
-
// 更新
|
|
257
|
-
$n_run(onUpdate)(result)
|
|
258
|
-
|
|
259
|
-
// 上传网络外链回调
|
|
260
|
-
$n_run(uploadNetCallback)()
|
|
261
|
-
}
|
|
262
|
-
|
|
263
|
-
/**
|
|
264
|
-
* 更新
|
|
265
|
-
*/
|
|
266
|
-
function update() {
|
|
267
|
-
// 更新
|
|
268
|
-
$n_run(onUpdate)(getValue())
|
|
269
|
-
}
|
|
270
|
-
|
|
271
|
-
/**
|
|
272
|
-
* 初始化上传列表
|
|
273
|
-
*/
|
|
274
|
-
async function initUploadFileLists() {
|
|
275
|
-
|
|
276
|
-
const modelValue = hasPropsModelValue ? options.props.modelValue : props.modelValue
|
|
277
|
-
|
|
278
|
-
// 值数组
|
|
279
|
-
const hashs = []
|
|
280
|
-
|
|
281
|
-
// hash all
|
|
282
|
-
const hashAll = {}
|
|
283
|
-
|
|
284
|
-
// 获取值数组
|
|
285
|
-
const lists = props.valueArray ? modelValue : $n_split(modelValue, ',')
|
|
286
|
-
|
|
287
|
-
if (
|
|
288
|
-
// 如果只能上传一个
|
|
289
|
-
props.count === 1
|
|
290
|
-
// 如果为空
|
|
291
|
-
&& ! $n_isValidArray(lists)
|
|
292
|
-
) {
|
|
293
|
-
// 更新上传文件列表
|
|
294
|
-
uploadFileLists.value = []
|
|
295
|
-
return
|
|
296
|
-
}
|
|
297
|
-
|
|
298
|
-
// 新上传文件列表
|
|
299
|
-
const newUploadFileLists = []
|
|
300
|
-
|
|
301
|
-
// 新列表
|
|
302
|
-
const newLists = []
|
|
303
|
-
|
|
304
|
-
// 是否更新
|
|
305
|
-
let isUpdate = false
|
|
306
|
-
|
|
307
|
-
// 合并当前上传列表中未上传成功的文件
|
|
308
|
-
for (const fileItem of uploadFileLists.value) {
|
|
309
|
-
if (fileItem.status !== UPLOAD_STATUS.success) {
|
|
310
|
-
newUploadFileLists.push(fileItem)
|
|
311
|
-
hashAll[fileItem.hash] = fileItem
|
|
312
|
-
}
|
|
313
|
-
}
|
|
314
|
-
|
|
315
|
-
$n_forEach(lists, function(hash) {
|
|
316
|
-
if ($n_isValidString(hash)) {
|
|
317
|
-
|
|
318
|
-
const hasItem = $n_find(uploadFileLists.value, { hash })
|
|
319
|
-
|
|
320
|
-
// 如果在当前上传文件列表中已存在
|
|
321
|
-
if (hasItem) {
|
|
322
|
-
hashAll[hash] = hasItem
|
|
323
|
-
|
|
324
|
-
} else if (! $n_has(hashAll, 'hash')) {
|
|
325
|
-
|
|
326
|
-
// 如果是外链
|
|
327
|
-
if (/^http(s)?:\/\//i.test(hash)) {
|
|
328
|
-
hashs.push(hash)
|
|
329
|
-
hashAll[hash] = {
|
|
330
|
-
hash,
|
|
331
|
-
__img: hash,
|
|
332
|
-
isNet: true,
|
|
333
|
-
isNetUploaded: false,
|
|
334
|
-
}
|
|
335
|
-
|
|
336
|
-
// 否则为 hash 文件
|
|
337
|
-
} else {
|
|
338
|
-
hashs.push(hash)
|
|
339
|
-
hashAll[hash] = {
|
|
340
|
-
hash,
|
|
341
|
-
isNet: false,
|
|
342
|
-
}
|
|
343
|
-
}
|
|
344
|
-
}
|
|
345
|
-
|
|
346
|
-
// 新列表
|
|
347
|
-
newLists.push(hash)
|
|
348
|
-
}
|
|
349
|
-
})
|
|
350
|
-
|
|
351
|
-
// 如果类型不是图片 || 初始加载文件信息, 则请求文件信息
|
|
352
|
-
if (
|
|
353
|
-
(props.type !== 'image' || props.loadInfo)
|
|
354
|
-
&& hashs.length
|
|
355
|
-
) {
|
|
356
|
-
// 请求 - 获取文件
|
|
357
|
-
const { status, data: resExisted } = await $n_http({
|
|
358
|
-
url: $n_config('apiFileUrl') + 'get_file',
|
|
359
|
-
data: {
|
|
360
|
-
hashs: $n_uniq(hashs),
|
|
361
|
-
},
|
|
362
|
-
// 关闭错误
|
|
363
|
-
warn: false,
|
|
364
|
-
// 关闭防抖(可以重复请求)
|
|
365
|
-
debounce: false,
|
|
366
|
-
})
|
|
367
|
-
if (status) {
|
|
368
|
-
|
|
369
|
-
$n_forEach(resExisted, function (existedItem) {
|
|
370
|
-
|
|
371
|
-
// 创建原始单个文件
|
|
372
|
-
const fileItem = createRawFileItem()
|
|
373
|
-
|
|
374
|
-
// 设置已存在文件
|
|
375
|
-
setExistedFileItem(fileItem, existedItem)
|
|
376
|
-
|
|
377
|
-
// 添加至 hash all
|
|
378
|
-
hashAll[fileItem.hash] = Object.assign(fileItem, {
|
|
379
|
-
key: fileItem.hash,
|
|
380
|
-
})
|
|
381
|
-
})
|
|
382
|
-
|
|
383
|
-
// 需要更新
|
|
384
|
-
isUpdate = true
|
|
385
|
-
}
|
|
386
|
-
}
|
|
387
|
-
|
|
388
|
-
for (const hash of newLists) {
|
|
389
|
-
|
|
390
|
-
let hasItem = hashAll[hash]
|
|
391
|
-
|
|
392
|
-
// 如果该 hash 已存在
|
|
393
|
-
if ($n_has(hasItem, 'id')) {
|
|
394
|
-
|
|
395
|
-
// 如果新列表中存在 该 id
|
|
396
|
-
if ($n_findIndex(newUploadFileLists, { id: hasItem.id }) > -1) {
|
|
397
|
-
|
|
398
|
-
// 更新 id
|
|
399
|
-
hasItem = Object.assign({}, hasItem, {
|
|
400
|
-
id: ++_fileNum
|
|
401
|
-
})
|
|
402
|
-
}
|
|
403
|
-
|
|
404
|
-
// 否则该 hash 不存在
|
|
405
|
-
} else {
|
|
406
|
-
hasItem = Object.assign(createRawFileItem(), hasItem, {
|
|
407
|
-
// 文件唯一 key
|
|
408
|
-
key: hash,
|
|
409
|
-
// hash
|
|
410
|
-
hash,
|
|
411
|
-
// 状态
|
|
412
|
-
status: UPLOAD_STATUS.success,
|
|
413
|
-
// 进度
|
|
414
|
-
progress: 100,
|
|
415
|
-
// 信息
|
|
416
|
-
msg: '',
|
|
417
|
-
})
|
|
418
|
-
hashAll[hash] = hasItem
|
|
419
|
-
}
|
|
420
|
-
|
|
421
|
-
// 添加至新列表中
|
|
422
|
-
newUploadFileLists.push(hasItem)
|
|
423
|
-
}
|
|
424
|
-
|
|
425
|
-
// 更新上传文件列表
|
|
426
|
-
uploadFileLists.value = newUploadFileLists
|
|
427
|
-
|
|
428
|
-
if (isUpdate) {
|
|
429
|
-
// 更新
|
|
430
|
-
update()
|
|
431
|
-
}
|
|
432
|
-
}
|
|
433
|
-
|
|
434
|
-
/**
|
|
435
|
-
* 初始化上传网络外链列表
|
|
436
|
-
*/
|
|
437
|
-
function initUploadNetLists(callback) {
|
|
438
|
-
|
|
439
|
-
// 如果提交时禁止上传网络外链文件
|
|
440
|
-
if ($n_get(optionsProps, 'submitUploadNet') !== true) {
|
|
441
|
-
return
|
|
442
|
-
}
|
|
443
|
-
|
|
444
|
-
uploadNetCallback = callback
|
|
445
|
-
|
|
446
|
-
for (const fileItem of uploadFileLists.value) {
|
|
447
|
-
if (
|
|
448
|
-
fileItem.isNet
|
|
449
|
-
&& fileItem.status === UPLOAD_STATUS.success
|
|
450
|
-
) {
|
|
451
|
-
// 将文件状态修改为: 等待上传中
|
|
452
|
-
fileItem.status = UPLOAD_STATUS.waiting
|
|
453
|
-
}
|
|
454
|
-
}
|
|
455
|
-
}
|
|
456
|
-
|
|
457
|
-
/**
|
|
458
|
-
* 上传网络外链文件
|
|
459
|
-
*/
|
|
460
|
-
async function uploadNet(submitUploadNet = false) {
|
|
461
|
-
|
|
462
|
-
// 如果提交时禁止上传网络外链文件
|
|
463
|
-
if (submitUploadNet === true || $n_get(optionsProps, 'submitUploadNet') === true) {
|
|
464
|
-
|
|
465
|
-
const promises = []
|
|
466
|
-
|
|
467
|
-
for (const fileItem of uploadFileLists.value) {
|
|
468
|
-
if (
|
|
469
|
-
fileItem.isNet
|
|
470
|
-
&& fileItem.status === UPLOAD_STATUS.waiting
|
|
471
|
-
) {
|
|
472
|
-
// 设置网络图片 file
|
|
473
|
-
promises.push(setNetFile(fileItem))
|
|
474
|
-
}
|
|
475
|
-
}
|
|
476
|
-
|
|
477
|
-
if (! promises.length) {
|
|
478
|
-
return
|
|
479
|
-
}
|
|
480
|
-
await Promise.all(promises)
|
|
481
|
-
|
|
482
|
-
// 检查待上传文件在服务器上是否存在
|
|
483
|
-
// --------------------------------------------------
|
|
484
|
-
if (! await checkWaitUploadFileExists()) {
|
|
485
|
-
return
|
|
486
|
-
}
|
|
487
|
-
|
|
488
|
-
// 上传
|
|
489
|
-
await upload()
|
|
490
|
-
}
|
|
491
|
-
}
|
|
492
|
-
|
|
493
|
-
/**
|
|
494
|
-
* 获取上传网络外链进度
|
|
495
|
-
*/
|
|
496
|
-
// function getUploadNetProgress() {
|
|
497
|
-
//
|
|
498
|
-
// let total = 0
|
|
499
|
-
// let loaded = 0
|
|
500
|
-
//
|
|
501
|
-
// // 如果提交时允许上传网络外链文件
|
|
502
|
-
// if ($n_get(optionsProps, 'submitUploadNet') === true) {
|
|
503
|
-
//
|
|
504
|
-
// for (const fileItem of uploadFileLists.value) {
|
|
505
|
-
// if (fileItem.isNet && fileItem.status <= UPLOAD_STATUS.success) {
|
|
506
|
-
// total++
|
|
507
|
-
// if (fileItem.isNetUploaded) {
|
|
508
|
-
// loaded++
|
|
509
|
-
// }
|
|
510
|
-
// }
|
|
511
|
-
// }
|
|
512
|
-
//
|
|
513
|
-
// // if (total && loaded < total) {
|
|
514
|
-
// // return {
|
|
515
|
-
// // loaded,
|
|
516
|
-
// // total,
|
|
517
|
-
// // // progress: Math.round(loaded * 100 / total),
|
|
518
|
-
// // }
|
|
519
|
-
// // }
|
|
520
|
-
// }
|
|
521
|
-
//
|
|
522
|
-
// return {
|
|
523
|
-
// loaded,
|
|
524
|
-
// total,
|
|
525
|
-
// // progress: 100,
|
|
526
|
-
// }
|
|
527
|
-
// }
|
|
528
|
-
|
|
529
|
-
/**
|
|
530
|
-
* 设置网络图片 file
|
|
531
|
-
*/
|
|
532
|
-
async function setNetFile(fileItem) {
|
|
533
|
-
|
|
534
|
-
// 设置文件状态
|
|
535
|
-
fileItem.status = UPLOAD_STATUS.hashChecking
|
|
536
|
-
// 设置文件检查进度
|
|
537
|
-
fileItem.progress = 0
|
|
538
|
-
|
|
539
|
-
try {
|
|
540
|
-
const r = await fetch(fileItem.__img, {
|
|
541
|
-
method: 'GET',
|
|
542
|
-
})
|
|
543
|
-
const arrayBuffer = await r.arrayBuffer()
|
|
544
|
-
const blob = new Blob([arrayBuffer], { type: r.headers.get('Content-Type') })
|
|
545
|
-
|
|
546
|
-
// -------- axios
|
|
547
|
-
// const r = await axios({
|
|
548
|
-
// method: 'GET',
|
|
549
|
-
// url: fileItem.__img,
|
|
550
|
-
// responseType: 'arraybuffer'
|
|
551
|
-
// })
|
|
552
|
-
// console.log(r)
|
|
553
|
-
// const arrayBuffer = r.data
|
|
554
|
-
// const blob = new Blob([arrayBuffer], { type: r.headers['content-type'] })
|
|
555
|
-
// -------- axios
|
|
556
|
-
|
|
557
|
-
// 如果有类型
|
|
558
|
-
if (blob.type) {
|
|
559
|
-
|
|
560
|
-
// 后缀名
|
|
561
|
-
let ext = ''
|
|
562
|
-
|
|
563
|
-
// 如果为图片
|
|
564
|
-
if (
|
|
565
|
-
props.type === 'image'
|
|
566
|
-
|| $n_indexOf(blob.type, 'image/') > -1
|
|
567
|
-
) {
|
|
568
|
-
switch (blob.type) {
|
|
569
|
-
case 'image/png':
|
|
570
|
-
ext = 'png'
|
|
571
|
-
break
|
|
572
|
-
case 'image/gif':
|
|
573
|
-
ext = 'gif'
|
|
574
|
-
break
|
|
575
|
-
default:
|
|
576
|
-
ext = 'jpg'
|
|
577
|
-
break
|
|
578
|
-
}
|
|
579
|
-
|
|
580
|
-
// 如果为视频
|
|
581
|
-
} else if (props.type === 'video') {
|
|
582
|
-
ext = 'mp4'
|
|
583
|
-
|
|
584
|
-
// 如果为音频
|
|
585
|
-
} else if (props.type === 'audio') {
|
|
586
|
-
ext = 'mp3'
|
|
587
|
-
|
|
588
|
-
// 否则为文件
|
|
589
|
-
} else {
|
|
590
|
-
const arr = $n_split(props.type, '/')
|
|
591
|
-
if (arr.length > 0) {
|
|
592
|
-
ext = arr[1]
|
|
593
|
-
}
|
|
594
|
-
}
|
|
595
|
-
|
|
596
|
-
// 如果有后缀名
|
|
597
|
-
if (ext) {
|
|
598
|
-
// 设置文件
|
|
599
|
-
fileItem.file = new File([blob], String($n_timestamp()), { type: blob.type })
|
|
600
|
-
// 设置后缀名
|
|
601
|
-
fileItem.ext = ext
|
|
602
|
-
|
|
603
|
-
const {
|
|
604
|
-
title,
|
|
605
|
-
size,
|
|
606
|
-
} = fileItem.file
|
|
607
|
-
|
|
608
|
-
// 文件大小
|
|
609
|
-
fileItem.size = size
|
|
610
|
-
|
|
611
|
-
// 检查文件错误
|
|
612
|
-
const errMsg = checkFileError(fileItem)
|
|
613
|
-
if (errMsg) {
|
|
614
|
-
// 设置文件上传失败
|
|
615
|
-
setFileFail(fileItem, errMsg)
|
|
616
|
-
return
|
|
617
|
-
}
|
|
618
|
-
|
|
619
|
-
// 初始化 SparkMD5
|
|
620
|
-
const spark = new SparkMD5.ArrayBuffer()
|
|
621
|
-
spark.append(arrayBuffer)
|
|
622
|
-
|
|
623
|
-
// 获取文件 hash
|
|
624
|
-
const hash = spark.end(false)
|
|
625
|
-
if (hash) {
|
|
626
|
-
// 设置文件 hash
|
|
627
|
-
fileItem.hash = await formatFileItemHash(hash)
|
|
628
|
-
// 标题
|
|
629
|
-
fileItem.title = title
|
|
630
|
-
// 设置文件状态
|
|
631
|
-
fileItem.status = UPLOAD_STATUS.hashChecked
|
|
632
|
-
// 设置文件检查进度
|
|
633
|
-
fileItem.progress = 100
|
|
634
|
-
|
|
635
|
-
// 设置单个文件信息
|
|
636
|
-
await setFileItemInfo(fileItem, setFileFail)
|
|
637
|
-
return
|
|
638
|
-
}
|
|
639
|
-
}
|
|
640
|
-
}
|
|
641
|
-
|
|
642
|
-
} catch (e) {}
|
|
643
|
-
|
|
644
|
-
// 设置文件上传失败
|
|
645
|
-
setFileFail(fileItem)
|
|
646
|
-
}
|
|
647
|
-
|
|
648
|
-
/**
|
|
649
|
-
* 检查是否正在上传文件
|
|
650
|
-
*/
|
|
651
|
-
function checkUploading() {
|
|
652
|
-
for (const fileItem of uploadFileLists.value) {
|
|
653
|
-
if (fileItem.status < UPLOAD_STATUS.success) {
|
|
654
|
-
return true
|
|
655
|
-
}
|
|
656
|
-
}
|
|
657
|
-
return false
|
|
658
|
-
}
|
|
659
|
-
|
|
660
|
-
/**
|
|
661
|
-
* 选择文件上传
|
|
662
|
-
*/
|
|
663
|
-
function chooseUpload() {
|
|
664
|
-
// 点击文件输入框
|
|
665
|
-
fileRef.value.click()
|
|
666
|
-
}
|
|
667
|
-
|
|
668
|
-
/**
|
|
669
|
-
* 选择网络外链上传
|
|
670
|
-
*/
|
|
671
|
-
function chooseUploadNet() {
|
|
672
|
-
$q.dialog({
|
|
673
|
-
title: `添加网络${FilE_NAME[FilE_TYPE[props.type]]}`,
|
|
674
|
-
style: 'min-width:600px;',
|
|
675
|
-
// message: `添加网络${FilE_NAME[FilE_TYPE[props.type]]}`,
|
|
676
|
-
prompt: {
|
|
677
|
-
model: '',
|
|
678
|
-
isValid: $n_$ruleValid('required'),
|
|
679
|
-
type: 'textarea',
|
|
680
|
-
placeholder: '多个链接,使用中英文逗号、空格、换行隔开',
|
|
681
|
-
outlined: true,
|
|
682
|
-
},
|
|
683
|
-
cancel: true,
|
|
684
|
-
persistent: true,
|
|
685
|
-
})
|
|
686
|
-
.onOk(async function(value) {
|
|
687
|
-
|
|
688
|
-
let files = $n_isValidString(value)
|
|
689
|
-
? $n_uniq($n_split(value.replace(/\n|,|,/g, ','), ','))
|
|
690
|
-
.map(e => $n_trimString(e))
|
|
691
|
-
.filter(
|
|
692
|
-
val => val && /^(http(s)?:\/\/)/i.test(val)
|
|
693
|
-
)
|
|
694
|
-
: []
|
|
695
|
-
|
|
696
|
-
if (! files.length) {
|
|
697
|
-
// 轻提示
|
|
698
|
-
$n_toast({
|
|
699
|
-
message: '请输入正确的链接'
|
|
700
|
-
})
|
|
701
|
-
return
|
|
702
|
-
}
|
|
703
|
-
|
|
704
|
-
// 格式化上传网络链接
|
|
705
|
-
// --------------------------------------------------
|
|
706
|
-
const {
|
|
707
|
-
formatUploadNet,
|
|
708
|
-
} = configs.uploader
|
|
709
|
-
if ($n_isFunction(formatUploadNet)) {
|
|
710
|
-
files = formatUploadNet(files, props.type === 'image')
|
|
711
|
-
}
|
|
712
|
-
// --------------------------------------------------
|
|
713
|
-
|
|
714
|
-
if (! $n_isValidArray(files)) {
|
|
715
|
-
// 轻提示
|
|
716
|
-
$n_toast({
|
|
717
|
-
message: '请输入正确的链接'
|
|
718
|
-
})
|
|
719
|
-
return
|
|
720
|
-
}
|
|
721
|
-
|
|
722
|
-
// 添加上传网络外链至文件列表
|
|
723
|
-
function addFileNetItem(hash) {
|
|
724
|
-
uploadFileLists.value.push(Object.assign(createRawFileItem(), {
|
|
725
|
-
// id
|
|
726
|
-
id: ++_fileNum,
|
|
727
|
-
// 文件唯一 key
|
|
728
|
-
key: hash,
|
|
729
|
-
// hash
|
|
730
|
-
hash,
|
|
731
|
-
// 将文件状态修改为: 等待上传中
|
|
732
|
-
status: UPLOAD_STATUS.waiting,
|
|
733
|
-
// 进度
|
|
734
|
-
progress: 100,
|
|
735
|
-
// 信息
|
|
736
|
-
msg: '',
|
|
737
|
-
|
|
738
|
-
__img: hash,
|
|
739
|
-
isNet: true,
|
|
740
|
-
isNetUploaded: false,
|
|
741
|
-
}))
|
|
742
|
-
}
|
|
743
|
-
|
|
744
|
-
// 遍历选择的文件列表
|
|
745
|
-
for (const file of files) {
|
|
746
|
-
|
|
747
|
-
// 如果只能上传一个
|
|
748
|
-
if (props.count === 1) {
|
|
749
|
-
|
|
750
|
-
// 如果有上传文件列表
|
|
751
|
-
if (uploadFileLists.value.length) {
|
|
752
|
-
|
|
753
|
-
// 如果开启单文件上传提示
|
|
754
|
-
if (props.confirm) {
|
|
755
|
-
|
|
756
|
-
// 确认框
|
|
757
|
-
$n_confirm({
|
|
758
|
-
message: '最多只能上传1个文件,确认上传并替换吗?',
|
|
759
|
-
})
|
|
760
|
-
// 点击确认执行
|
|
761
|
-
.onOk(function () {
|
|
762
|
-
|
|
763
|
-
// 删除所有文件
|
|
764
|
-
deleteAll()
|
|
765
|
-
|
|
766
|
-
// 添加上传网络外链至文件列表
|
|
767
|
-
addFileNetItem(file)
|
|
768
|
-
|
|
769
|
-
// 上传网络外链文件
|
|
770
|
-
uploadNet(true)
|
|
771
|
-
.finally()
|
|
772
|
-
})
|
|
773
|
-
return
|
|
774
|
-
}
|
|
775
|
-
|
|
776
|
-
// 删除所有文件
|
|
777
|
-
deleteAll()
|
|
778
|
-
}
|
|
779
|
-
|
|
780
|
-
} else if (
|
|
781
|
-
// 如果有上传数量限制
|
|
782
|
-
props.count > 1
|
|
783
|
-
// 上传文件列表数量 === 上传数量限制
|
|
784
|
-
&& uploadFileLists.value.length >= props.count
|
|
785
|
-
) {
|
|
786
|
-
// 轻提示
|
|
787
|
-
$n_toast({
|
|
788
|
-
message: `最多只能上传${props.count}个文件,请先删除后再上传`,
|
|
789
|
-
})
|
|
790
|
-
return
|
|
791
|
-
}
|
|
792
|
-
|
|
793
|
-
// 添加上传网络外链至文件列表
|
|
794
|
-
addFileNetItem(file)
|
|
795
|
-
}
|
|
796
|
-
|
|
797
|
-
// 上传网络外链文件
|
|
798
|
-
uploadNet(true)
|
|
799
|
-
.finally()
|
|
800
|
-
})
|
|
801
|
-
}
|
|
802
|
-
|
|
803
|
-
/**
|
|
804
|
-
* 文件输入框更新
|
|
805
|
-
*/
|
|
806
|
-
async function fileChange(e) {
|
|
807
|
-
|
|
808
|
-
try {
|
|
809
|
-
// 获取上传文件
|
|
810
|
-
const files = Array.from(e.target.files)
|
|
811
|
-
|
|
812
|
-
// 清空上传文件输入框内容
|
|
813
|
-
fileRef.value.value = ''
|
|
814
|
-
|
|
815
|
-
// 如果没有选择文件
|
|
816
|
-
if (! files.length) {
|
|
817
|
-
// 则无任何操作
|
|
818
|
-
return
|
|
819
|
-
}
|
|
820
|
-
|
|
821
|
-
// 遍历选择的文件列表
|
|
822
|
-
for (const file of files) {
|
|
823
|
-
|
|
824
|
-
// 创建单个文件
|
|
825
|
-
const fileItem = await createFileItem(file)
|
|
826
|
-
if (fileItem !== false) {
|
|
827
|
-
|
|
828
|
-
// 如果只能上传一个
|
|
829
|
-
if (props.count === 1) {
|
|
830
|
-
|
|
831
|
-
// 如果有上传文件列表
|
|
832
|
-
if (uploadFileLists.value.length) {
|
|
833
|
-
|
|
834
|
-
// 如果开启单文件上传提示
|
|
835
|
-
if (props.confirm) {
|
|
836
|
-
|
|
837
|
-
// 确认框
|
|
838
|
-
$n_confirm({
|
|
839
|
-
message: '最多只能上传1个文件,确认上传并替换吗?',
|
|
840
|
-
})
|
|
841
|
-
// 点击确认执行
|
|
842
|
-
.onOk(function () {
|
|
843
|
-
|
|
844
|
-
// 删除所有文件
|
|
845
|
-
deleteAll()
|
|
846
|
-
|
|
847
|
-
// 添加至上传文件列表
|
|
848
|
-
uploadFileLists.value.push(fileItem)
|
|
849
|
-
|
|
850
|
-
// 开始上传
|
|
851
|
-
startUpload()
|
|
852
|
-
.finally()
|
|
853
|
-
})
|
|
854
|
-
return
|
|
855
|
-
}
|
|
856
|
-
|
|
857
|
-
// 删除所有文件
|
|
858
|
-
deleteAll()
|
|
859
|
-
}
|
|
860
|
-
|
|
861
|
-
} else if (
|
|
862
|
-
// 如果有上传数量限制
|
|
863
|
-
props.count > 1
|
|
864
|
-
// 上传文件列表数量 === 上传数量限制
|
|
865
|
-
&& uploadFileLists.value.length >= props.count
|
|
866
|
-
) {
|
|
867
|
-
// 轻提示
|
|
868
|
-
$n_toast({
|
|
869
|
-
message: `最多只能上传${props.count}个文件,请先删除后再上传`,
|
|
870
|
-
})
|
|
871
|
-
return
|
|
872
|
-
}
|
|
873
|
-
|
|
874
|
-
// 添加至上传文件列表
|
|
875
|
-
uploadFileLists.value.push(fileItem)
|
|
876
|
-
}
|
|
877
|
-
}
|
|
878
|
-
|
|
879
|
-
// 开始上传
|
|
880
|
-
startUpload()
|
|
881
|
-
.finally()
|
|
882
|
-
|
|
883
|
-
} catch (e) {
|
|
884
|
-
|
|
885
|
-
// 错误提示
|
|
886
|
-
$n_alert({
|
|
887
|
-
message: $n_getThrowMessage(e),
|
|
888
|
-
})
|
|
889
|
-
}
|
|
890
|
-
|
|
891
|
-
}
|
|
892
|
-
|
|
893
|
-
/**
|
|
894
|
-
* 开始上传
|
|
895
|
-
*/
|
|
896
|
-
async function startUpload() {
|
|
897
|
-
|
|
898
|
-
// 【设置待上传文件的 hash】
|
|
899
|
-
// --------------------------------------------------
|
|
900
|
-
const promises = []
|
|
901
|
-
for (const fileItem of uploadFileLists.value) {
|
|
902
|
-
// 如果是等待上传的文件
|
|
903
|
-
if (fileItem.status === UPLOAD_STATUS.waiting) {
|
|
904
|
-
|
|
905
|
-
// 检查文件错误
|
|
906
|
-
const errMsg = checkFileError(fileItem)
|
|
907
|
-
if (errMsg) {
|
|
908
|
-
// 设置文件上传失败
|
|
909
|
-
setFileFail(fileItem, errMsg)
|
|
910
|
-
|
|
911
|
-
} else {
|
|
912
|
-
// 设置文件 hash
|
|
913
|
-
promises.push(setFileHash(fileItem))
|
|
914
|
-
}
|
|
915
|
-
}
|
|
916
|
-
}
|
|
917
|
-
|
|
918
|
-
if (promises.length) {
|
|
919
|
-
await Promise.all(promises)
|
|
920
|
-
}
|
|
921
|
-
|
|
922
|
-
// 检查待上传文件在服务器上是否存在
|
|
923
|
-
// --------------------------------------------------
|
|
924
|
-
if (! await checkWaitUploadFileExists()) {
|
|
925
|
-
return
|
|
926
|
-
}
|
|
927
|
-
|
|
928
|
-
// 上传
|
|
929
|
-
await upload()
|
|
930
|
-
}
|
|
931
|
-
|
|
932
|
-
/**
|
|
933
|
-
* 上传
|
|
934
|
-
*/
|
|
935
|
-
async function upload() {
|
|
936
|
-
try {
|
|
937
|
-
// 待上传文件列表
|
|
938
|
-
const waitUploadFileLists = []
|
|
939
|
-
for (const fileItem of uploadFileLists.value) {
|
|
940
|
-
// 检查存在服务器完成
|
|
941
|
-
if (fileItem.status === UPLOAD_STATUS.existChecked) {
|
|
942
|
-
waitUploadFileLists.push(fileItem)
|
|
943
|
-
}
|
|
944
|
-
}
|
|
945
|
-
if (! waitUploadFileLists.length) {
|
|
946
|
-
return
|
|
947
|
-
}
|
|
948
|
-
|
|
949
|
-
// 上传至服务器
|
|
950
|
-
await uploadServer({
|
|
951
|
-
fileType: FilE_TYPE[props.type],
|
|
952
|
-
waitUploadFileLists,
|
|
953
|
-
setFileSuccess,
|
|
954
|
-
setFileFail,
|
|
955
|
-
})
|
|
956
|
-
|
|
957
|
-
} catch (e) {
|
|
958
|
-
// 错误提示
|
|
959
|
-
$n_alert({
|
|
960
|
-
message: $n_getThrowMessage(e),
|
|
961
|
-
})
|
|
962
|
-
}
|
|
963
|
-
}
|
|
964
|
-
|
|
965
|
-
/**
|
|
966
|
-
* 设置文件上传成功
|
|
967
|
-
*/
|
|
968
|
-
const setFileSuccess = (fileItem) => {
|
|
969
|
-
|
|
970
|
-
// 设置文件状态
|
|
971
|
-
fileItem.status = UPLOAD_STATUS.success
|
|
972
|
-
// 设置文件信息
|
|
973
|
-
fileItem.msg = ''
|
|
974
|
-
// 设置文件检查进度
|
|
975
|
-
fileItem.progress = 0
|
|
976
|
-
// 是否网络文件已上传
|
|
977
|
-
if (fileItem.isNet) {
|
|
978
|
-
fileItem.isNetUploaded = true
|
|
979
|
-
}
|
|
980
|
-
|
|
981
|
-
// // 单个文件上传结束回调
|
|
982
|
-
// uploadQueryCallback(fileItem)
|
|
983
|
-
//
|
|
984
|
-
// // 上传更新回调
|
|
985
|
-
// uploadChange(getResultData())
|
|
986
|
-
|
|
987
|
-
// 更新值
|
|
988
|
-
updateValue()
|
|
989
|
-
}
|
|
990
|
-
|
|
991
|
-
/**
|
|
992
|
-
* 设置文件上传失败
|
|
993
|
-
*/
|
|
994
|
-
function setFileFail(fileItem, errMsg) {
|
|
995
|
-
|
|
996
|
-
// 设置文件状态
|
|
997
|
-
fileItem.status = UPLOAD_STATUS.fail
|
|
998
|
-
// 设置文件信息
|
|
999
|
-
fileItem.msg = errMsg || `无效${FilE_NAME[fileItem.type]}`
|
|
1000
|
-
// 设置文件检查进度
|
|
1001
|
-
fileItem.progress = 0
|
|
1002
|
-
|
|
1003
|
-
// // 单个文件上传结束回调
|
|
1004
|
-
// uploadQueryCallback(fileItem)
|
|
1005
|
-
|
|
1006
|
-
// 上传网络外链回调
|
|
1007
|
-
$n_run(uploadNetCallback)()
|
|
1008
|
-
}
|
|
1009
|
-
|
|
1010
|
-
/**
|
|
1011
|
-
* 设置文件 hash
|
|
1012
|
-
*/
|
|
1013
|
-
function setFileHash(fileItem) {
|
|
1014
|
-
return new Promise(function (resolve) {
|
|
1015
|
-
|
|
1016
|
-
// 设置文件状态
|
|
1017
|
-
fileItem.status = UPLOAD_STATUS.hashChecking
|
|
1018
|
-
// 设置文件检查进度
|
|
1019
|
-
fileItem.progress = 0
|
|
1020
|
-
|
|
1021
|
-
const {
|
|
1022
|
-
file,
|
|
1023
|
-
} = fileItem
|
|
1024
|
-
|
|
1025
|
-
// blob 切片
|
|
1026
|
-
const blobSlice = File.prototype.slice || File.prototype.mozSlice || File.prototype.webkitSlice
|
|
1027
|
-
// 以 2MB 的分片读取
|
|
1028
|
-
const chunkSize = 2097152
|
|
1029
|
-
// 总分片数量
|
|
1030
|
-
const chunks = Math.ceil(file.size / chunkSize)
|
|
1031
|
-
// 初始化 SparkMD5
|
|
1032
|
-
const spark = new SparkMD5.ArrayBuffer()
|
|
1033
|
-
// 当前分片数
|
|
1034
|
-
let currentChunk = 0
|
|
1035
|
-
// 创建文件读取器
|
|
1036
|
-
const fileReader = new FileReader()
|
|
1037
|
-
|
|
1038
|
-
// 文件加载
|
|
1039
|
-
fileReader.onload = async function(e) {
|
|
1040
|
-
|
|
1041
|
-
// 追加 array buffer
|
|
1042
|
-
spark.append(e.target.result)
|
|
1043
|
-
|
|
1044
|
-
// 当前分片数++
|
|
1045
|
-
currentChunk++
|
|
1046
|
-
|
|
1047
|
-
// 如果分块数量 < 总分片数量
|
|
1048
|
-
if (currentChunk < chunks) {
|
|
1049
|
-
|
|
1050
|
-
// 设置文件检查进度
|
|
1051
|
-
fileItem.progress = Math.floor((currentChunk / chunks) * 100)
|
|
1052
|
-
|
|
1053
|
-
// 读取下一个分片
|
|
1054
|
-
loadNext()
|
|
1055
|
-
|
|
1056
|
-
} else {
|
|
1057
|
-
|
|
1058
|
-
// 获取文件 hash
|
|
1059
|
-
const hash = spark.end(false)
|
|
1060
|
-
if (! hash) {
|
|
1061
|
-
// 设置文件上传失败
|
|
1062
|
-
setFileFail(fileItem)
|
|
1063
|
-
// 完成回调
|
|
1064
|
-
resolve()
|
|
1065
|
-
return
|
|
1066
|
-
}
|
|
1067
|
-
|
|
1068
|
-
if (
|
|
1069
|
-
// 如果开启去重
|
|
1070
|
-
props.unique
|
|
1071
|
-
// 如果该文件 hash 在上传文件列表中
|
|
1072
|
-
&& $n_findIndex(uploadFileLists.value, { hash }) > -1
|
|
1073
|
-
) {
|
|
1074
|
-
// 轻提示
|
|
1075
|
-
$n_toast({
|
|
1076
|
-
message: '该文件已存在,不可重复上传',
|
|
1077
|
-
})
|
|
1078
|
-
|
|
1079
|
-
// 设置文件上传失败
|
|
1080
|
-
setFileFail(fileItem, '已存在')
|
|
1081
|
-
|
|
1082
|
-
// 删除单个文件
|
|
1083
|
-
deleteFileItem(fileItem)
|
|
1084
|
-
|
|
1085
|
-
} else {
|
|
1086
|
-
// 设置文件 hash
|
|
1087
|
-
fileItem.hash = await formatFileItemHash(hash)
|
|
1088
|
-
// 设置文件状态
|
|
1089
|
-
fileItem.status = UPLOAD_STATUS.hashChecked
|
|
1090
|
-
// 设置文件检查进度
|
|
1091
|
-
fileItem.progress = 100
|
|
1092
|
-
}
|
|
1093
|
-
|
|
1094
|
-
// 设置单个文件信息
|
|
1095
|
-
await setFileItemInfo(fileItem, setFileFail)
|
|
1096
|
-
|
|
1097
|
-
resolve()
|
|
1098
|
-
}
|
|
1099
|
-
}
|
|
1100
|
-
|
|
1101
|
-
// 文件加载错误
|
|
1102
|
-
fileReader.onerror = function() {
|
|
1103
|
-
// 设置文件上传失败
|
|
1104
|
-
setFileFail(fileItem)
|
|
1105
|
-
// 完成回调
|
|
1106
|
-
resolve()
|
|
1107
|
-
}
|
|
1108
|
-
|
|
1109
|
-
/**
|
|
1110
|
-
* 读取下一个分片
|
|
1111
|
-
*/
|
|
1112
|
-
function loadNext() {
|
|
1113
|
-
const start = currentChunk * chunkSize
|
|
1114
|
-
const end = ((start + chunkSize) >= file.size) ? file.size : start + chunkSize
|
|
1115
|
-
fileReader.readAsArrayBuffer(blobSlice.call(file, start, end))
|
|
1116
|
-
}
|
|
1117
|
-
|
|
1118
|
-
// 读取下一个分片
|
|
1119
|
-
loadNext()
|
|
1120
|
-
})
|
|
1121
|
-
}
|
|
1122
|
-
|
|
1123
|
-
/**
|
|
1124
|
-
* 检查文件错误
|
|
1125
|
-
*/
|
|
1126
|
-
function checkFileError({ type, size, ext }) {
|
|
1127
|
-
|
|
1128
|
-
// 需要上传的文件类型
|
|
1129
|
-
const fileType = FilE_TYPE[props.type]
|
|
1130
|
-
|
|
1131
|
-
// 如果 类型为文件, 则不受限制 || 当前上传的类型允许
|
|
1132
|
-
if (props.type === 'file' || type === fileType) {
|
|
1133
|
-
|
|
1134
|
-
const {
|
|
1135
|
-
maxSize,
|
|
1136
|
-
exts,
|
|
1137
|
-
} = configLimit
|
|
1138
|
-
|
|
1139
|
-
// 检查文件后缀名
|
|
1140
|
-
if (
|
|
1141
|
-
// 如果没有后缀名, 则无效
|
|
1142
|
-
! ext
|
|
1143
|
-
// 如果后缀名不在允许范围内, 则无效
|
|
1144
|
-
|| (
|
|
1145
|
-
$n_isValidArray(exts)
|
|
1146
|
-
&& exts.indexOf(ext) === -1
|
|
1147
|
-
)
|
|
1148
|
-
) {
|
|
1149
|
-
return '后缀名不允许'
|
|
1150
|
-
}
|
|
1151
|
-
|
|
1152
|
-
// 检查文件大小
|
|
1153
|
-
if (size > 0) {
|
|
1154
|
-
if (
|
|
1155
|
-
maxSize > 0
|
|
1156
|
-
&& size > (maxSize * 1048576)
|
|
1157
|
-
) {
|
|
1158
|
-
return `${FilE_NAME[fileType]}大于${maxSize}M`
|
|
1159
|
-
}
|
|
1160
|
-
return
|
|
1161
|
-
}
|
|
1162
|
-
}
|
|
1163
|
-
|
|
1164
|
-
return `无效${FilE_NAME[fileType]}`
|
|
1165
|
-
}
|
|
1166
|
-
|
|
1167
|
-
/**
|
|
1168
|
-
* 检查待上传文件在服务器上是否存在
|
|
1169
|
-
*/
|
|
1170
|
-
async function checkWaitUploadFileExists() {
|
|
1171
|
-
|
|
1172
|
-
// 需检查的 hashs
|
|
1173
|
-
const checkHashs = []
|
|
1174
|
-
// 需检查的文件列表
|
|
1175
|
-
const checkFileLists = []
|
|
1176
|
-
|
|
1177
|
-
for (const fileItem of uploadFileLists.value) {
|
|
1178
|
-
if (fileItem.status === UPLOAD_STATUS.hashChecked) {
|
|
1179
|
-
// 设置文件状态
|
|
1180
|
-
fileItem.status = UPLOAD_STATUS.existChecking
|
|
1181
|
-
// 添加检查 hash 数组
|
|
1182
|
-
checkHashs.push(fileItem.hash)
|
|
1183
|
-
// 添加检查文件列表
|
|
1184
|
-
checkFileLists.push(fileItem)
|
|
1185
|
-
}
|
|
1186
|
-
}
|
|
1187
|
-
|
|
1188
|
-
// 如果没有需要检查的 hash
|
|
1189
|
-
if (! checkHashs.length) {
|
|
1190
|
-
// 返回失败
|
|
1191
|
-
return false
|
|
1192
|
-
}
|
|
1193
|
-
|
|
1194
|
-
// 请求 - 检查文件是否存在 hash
|
|
1195
|
-
const { status, data: resExisted } = await $n_http({
|
|
1196
|
-
url: $n_config('apiFileUrl') + 'check_exist',
|
|
1197
|
-
data: {
|
|
1198
|
-
hashs: $n_uniq(checkHashs),
|
|
1199
|
-
},
|
|
1200
|
-
// 关闭错误
|
|
1201
|
-
warn: false,
|
|
1202
|
-
// 关闭防抖(可以重复请求)
|
|
1203
|
-
debounce: false,
|
|
1204
|
-
})
|
|
1205
|
-
|
|
1206
|
-
// 如果请求失败
|
|
1207
|
-
if (! status) {
|
|
1208
|
-
// 设置文件上传失败
|
|
1209
|
-
for (const fileItem of checkFileLists) {
|
|
1210
|
-
setFileFail(fileItem, resExisted.msg)
|
|
1211
|
-
}
|
|
1212
|
-
// 返回失败
|
|
1213
|
-
return false
|
|
1214
|
-
}
|
|
1215
|
-
|
|
1216
|
-
// 设置文件状态
|
|
1217
|
-
for (const fileItem of checkFileLists) {
|
|
1218
|
-
fileItem.status = UPLOAD_STATUS.existChecked
|
|
1219
|
-
}
|
|
1220
|
-
|
|
1221
|
-
// 如果有存在的文件列表
|
|
1222
|
-
if ($n_isValidArray(resExisted)) {
|
|
1223
|
-
|
|
1224
|
-
// 已存在文件数量
|
|
1225
|
-
let existedNum = 0
|
|
1226
|
-
// 不存在文件数量
|
|
1227
|
-
let noExistNum = 0
|
|
1228
|
-
|
|
1229
|
-
for (const fileItem of checkFileLists) {
|
|
1230
|
-
|
|
1231
|
-
// 如果文件已存在(已经上传过了, 就是检查是否秒传文件)
|
|
1232
|
-
const existedItem = $n_find(resExisted, { hash: fileItem.hash })
|
|
1233
|
-
if (existedItem) {
|
|
1234
|
-
|
|
1235
|
-
// 如果 类型为文件, 则不受限制 || 文件类型正确
|
|
1236
|
-
if (props.type === 'file' || fileItem.type === $n_get(existedItem, 'type')) {
|
|
1237
|
-
|
|
1238
|
-
// 设置已存在文件
|
|
1239
|
-
setExistedFileItem(fileItem, existedItem)
|
|
1240
|
-
|
|
1241
|
-
// 设置文件为非网络外链
|
|
1242
|
-
fileItem.isNet = false
|
|
1243
|
-
|
|
1244
|
-
// 单个文件上传结束回调
|
|
1245
|
-
// uploadQueryCallback(fileItem)
|
|
1246
|
-
|
|
1247
|
-
// 已存在文件数量++
|
|
1248
|
-
existedNum++
|
|
1249
|
-
|
|
1250
|
-
// 否则有不存在文件
|
|
1251
|
-
} else {
|
|
1252
|
-
// 不存在文件数量++
|
|
1253
|
-
noExistNum++
|
|
1254
|
-
}
|
|
1255
|
-
|
|
1256
|
-
// 否则有不存在文件
|
|
1257
|
-
} else {
|
|
1258
|
-
// 不存在文件数量++
|
|
1259
|
-
noExistNum++
|
|
1260
|
-
}
|
|
1261
|
-
}
|
|
1262
|
-
|
|
1263
|
-
// 如果有已存在的文件
|
|
1264
|
-
if (existedNum) {
|
|
1265
|
-
|
|
1266
|
-
// 更新值
|
|
1267
|
-
updateValue()
|
|
1268
|
-
|
|
1269
|
-
// 上传更新回调
|
|
1270
|
-
// uploadChange(getResultData())
|
|
1271
|
-
}
|
|
1272
|
-
|
|
1273
|
-
return noExistNum > 0
|
|
1274
|
-
}
|
|
1275
|
-
|
|
1276
|
-
return true
|
|
1277
|
-
}
|
|
1278
|
-
|
|
1279
|
-
/**
|
|
1280
|
-
* 创建原始单个文件
|
|
1281
|
-
*/
|
|
1282
|
-
function createRawFileItem() {
|
|
1283
|
-
return {
|
|
1284
|
-
// id
|
|
1285
|
-
id: ++_fileNum,
|
|
1286
|
-
// 文件唯一 key
|
|
1287
|
-
// key,
|
|
1288
|
-
// 文件名
|
|
1289
|
-
title: '',
|
|
1290
|
-
// 文件原始数据
|
|
1291
|
-
// file,
|
|
1292
|
-
// 类型(1:文件,2:图片,3:视频,4:音频)
|
|
1293
|
-
type: FilE_TYPE[props.type],
|
|
1294
|
-
// 后缀
|
|
1295
|
-
ext: '',
|
|
1296
|
-
// 大小
|
|
1297
|
-
size: 0,
|
|
1298
|
-
// hash
|
|
1299
|
-
hash: '',
|
|
1300
|
-
// 信息
|
|
1301
|
-
json: {},
|
|
1302
|
-
// 状态: waiting checking checked uploading success fail
|
|
1303
|
-
status: UPLOAD_STATUS.waiting,
|
|
1304
|
-
// 进度
|
|
1305
|
-
progress: 0,
|
|
1306
|
-
// 信息
|
|
1307
|
-
msg: '待上传',
|
|
1308
|
-
// 是否为网络文件
|
|
1309
|
-
isNet: false,
|
|
1310
|
-
// 是否网络文件已上传
|
|
1311
|
-
isNetUploaded: false,
|
|
1312
|
-
// 中断上传
|
|
1313
|
-
abort() {},
|
|
1314
|
-
}
|
|
1315
|
-
}
|
|
1316
|
-
|
|
1317
|
-
/**
|
|
1318
|
-
* 创建单个文件
|
|
1319
|
-
*/
|
|
1320
|
-
async function createFileItem(file) {
|
|
1321
|
-
|
|
1322
|
-
// 单个文件示例
|
|
1323
|
-
// name: "123.jpg"
|
|
1324
|
-
// size: 101206
|
|
1325
|
-
// type: "image/jpeg"
|
|
1326
|
-
// lastModified: 1629099994411
|
|
1327
|
-
// lastModifiedDate: Mon Aug 16 2021 15:46:34 GMT+0800 (中国标准时间) {}
|
|
1328
|
-
// webkitRelativePath: ""
|
|
1329
|
-
|
|
1330
|
-
const {
|
|
1331
|
-
name,
|
|
1332
|
-
size,
|
|
1333
|
-
lastModified,
|
|
1334
|
-
webkitRelativePath,
|
|
1335
|
-
} = file
|
|
1336
|
-
|
|
1337
|
-
// 文件唯一 key
|
|
1338
|
-
const key = webkitRelativePath + lastModified + name + size
|
|
1339
|
-
|
|
1340
|
-
if (
|
|
1341
|
-
// 如果开启去重
|
|
1342
|
-
props.unique
|
|
1343
|
-
// 如果该文件 key 在上传文件列表中
|
|
1344
|
-
&& $n_findIndex(uploadFileLists.value, { key }) > -1
|
|
1345
|
-
) {
|
|
1346
|
-
// 轻提示
|
|
1347
|
-
$n_toast({
|
|
1348
|
-
message: '该文件已存在,不可重复上传',
|
|
1349
|
-
})
|
|
1350
|
-
|
|
1351
|
-
// 则返回 false
|
|
1352
|
-
return false
|
|
1353
|
-
}
|
|
1354
|
-
|
|
1355
|
-
// 文件名
|
|
1356
|
-
let title = name
|
|
1357
|
-
// 文件后缀名
|
|
1358
|
-
let ext = ''
|
|
1359
|
-
|
|
1360
|
-
const index = name.lastIndexOf('.')
|
|
1361
|
-
if (index > -1) {
|
|
1362
|
-
title = name.substring(0, index)
|
|
1363
|
-
ext = $n_toLower(name.substring(index + 1))
|
|
1364
|
-
}
|
|
1365
|
-
|
|
1366
|
-
// 创建单个文件
|
|
1367
|
-
const fileItem = Object.assign(createRawFileItem(), {
|
|
1368
|
-
// 文件唯一 key
|
|
1369
|
-
key,
|
|
1370
|
-
// 文件原始数据
|
|
1371
|
-
file,
|
|
1372
|
-
// 文件名
|
|
1373
|
-
title,
|
|
1374
|
-
// 后缀
|
|
1375
|
-
ext,
|
|
1376
|
-
// 大小
|
|
1377
|
-
size,
|
|
1378
|
-
})
|
|
1379
|
-
|
|
1380
|
-
// 如果上传类型为图片
|
|
1381
|
-
if (
|
|
1382
|
-
props.type === 'image'
|
|
1383
|
-
&& file.type.toLowerCase().startsWith('image')
|
|
1384
|
-
) {
|
|
1385
|
-
// 设置单个文件信息
|
|
1386
|
-
const res = await setFileItemInfo(fileItem, setFileFail)
|
|
1387
|
-
if (! res) {
|
|
1388
|
-
return false
|
|
1389
|
-
}
|
|
1390
|
-
}
|
|
1391
|
-
|
|
1392
|
-
return fileItem
|
|
1393
|
-
}
|
|
1394
|
-
|
|
1395
|
-
/**
|
|
1396
|
-
* 设置已存在文件
|
|
1397
|
-
*/
|
|
1398
|
-
function setExistedFileItem(fileItem, existedItem) {
|
|
1399
|
-
|
|
1400
|
-
// 返回数据示例
|
|
1401
|
-
// ------------------------------
|
|
1402
|
-
// title: 1
|
|
1403
|
-
// type: 2
|
|
1404
|
-
// hash: "799e6bb77f638e192b9079e6a55bc2de"
|
|
1405
|
-
// ext: "jpg"
|
|
1406
|
-
// size: 306037
|
|
1407
|
-
// json: "{\"w\":1920,\"h\":1200,\"o\":1}"
|
|
1408
|
-
// status: 1
|
|
1409
|
-
|
|
1410
|
-
const {
|
|
1411
|
-
// 标题
|
|
1412
|
-
title,
|
|
1413
|
-
// 类型(1:文件,2:图片,3:视频,4:音频)
|
|
1414
|
-
type,
|
|
1415
|
-
// hash
|
|
1416
|
-
hash,
|
|
1417
|
-
// 后缀
|
|
1418
|
-
ext,
|
|
1419
|
-
// 大小
|
|
1420
|
-
size,
|
|
1421
|
-
// 信息
|
|
1422
|
-
json,
|
|
1423
|
-
} = existedItem
|
|
1424
|
-
|
|
1425
|
-
const fileJson = $n_json.parse(json)
|
|
1426
|
-
|
|
1427
|
-
// 设置文件
|
|
1428
|
-
Object.assign(fileItem, {
|
|
1429
|
-
// 标题
|
|
1430
|
-
title,
|
|
1431
|
-
// 类型(1:文件,2:图片,3:视频,4:音频)
|
|
1432
|
-
type,
|
|
1433
|
-
// hash
|
|
1434
|
-
hash,
|
|
1435
|
-
// 后缀
|
|
1436
|
-
ext,
|
|
1437
|
-
// 大小
|
|
1438
|
-
size,
|
|
1439
|
-
// 信息
|
|
1440
|
-
json: $n_isValidObject(fileJson) ? fileJson : {},
|
|
1441
|
-
// 状态
|
|
1442
|
-
status: UPLOAD_STATUS.success,
|
|
1443
|
-
// 进度
|
|
1444
|
-
progress: 100,
|
|
1445
|
-
// 信息
|
|
1446
|
-
msg: '',
|
|
1447
|
-
})
|
|
1448
|
-
}
|
|
1449
|
-
|
|
1450
|
-
/**
|
|
1451
|
-
* 删除所有文件
|
|
1452
|
-
*/
|
|
1453
|
-
function deleteAll() {
|
|
1454
|
-
|
|
1455
|
-
// 如果有上传文件列表
|
|
1456
|
-
if (uploadFileLists.value.length) {
|
|
1457
|
-
|
|
1458
|
-
for (const fileItem of uploadFileLists.value) {
|
|
1459
|
-
// 退出上传
|
|
1460
|
-
fileItem.abort()
|
|
1461
|
-
}
|
|
1462
|
-
|
|
1463
|
-
// 清空上传文件列表
|
|
1464
|
-
uploadFileLists.value = []
|
|
1465
|
-
}
|
|
1466
|
-
}
|
|
1467
|
-
|
|
1468
|
-
/**
|
|
1469
|
-
* 删除单个文件
|
|
1470
|
-
*/
|
|
1471
|
-
function deleteFileItem(fileItem) {
|
|
1472
|
-
const index = $n_findIndex(uploadFileLists.value, { id: fileItem.id })
|
|
1473
|
-
if (index > -1) {
|
|
1474
|
-
|
|
1475
|
-
const {
|
|
1476
|
-
status,
|
|
1477
|
-
} = fileItem
|
|
1478
|
-
|
|
1479
|
-
// 退出上传
|
|
1480
|
-
fileItem.abort()
|
|
1481
|
-
|
|
1482
|
-
// 从上传文件列表中删除
|
|
1483
|
-
uploadFileLists.value.splice(index, 1)
|
|
1484
|
-
|
|
1485
|
-
// 如果该文件已上传成功
|
|
1486
|
-
if (status === UPLOAD_STATUS.success) {
|
|
1487
|
-
// 更新值
|
|
1488
|
-
updateValue()
|
|
1489
|
-
}
|
|
1490
|
-
}
|
|
1491
|
-
}
|
|
1492
|
-
|
|
1493
|
-
/**
|
|
1494
|
-
* 预览图片
|
|
1495
|
-
*/
|
|
1496
|
-
function previewImage(fileItem) {
|
|
1497
|
-
// 预览图片
|
|
1498
|
-
if (fileItem.type === FilE_TYPE.image) {
|
|
1499
|
-
$n_previewImage({
|
|
1500
|
-
images: [
|
|
1501
|
-
$n_has(fileItem, '__img') ? fileItem.__img : fileItem.hash,
|
|
1502
|
-
],
|
|
1503
|
-
})
|
|
1504
|
-
}
|
|
1505
|
-
}
|
|
1506
|
-
|
|
1507
|
-
/**
|
|
1508
|
-
* 修改文件名
|
|
1509
|
-
*/
|
|
1510
|
-
async function editFileTitle(newTitle, fileItem) {
|
|
1511
|
-
|
|
1512
|
-
if ($n_isValidValue(newTitle)) {
|
|
1513
|
-
|
|
1514
|
-
newTitle = $n_trimString(newTitle)
|
|
1515
|
-
|
|
1516
|
-
const {
|
|
1517
|
-
hash,
|
|
1518
|
-
title,
|
|
1519
|
-
} = fileItem
|
|
1520
|
-
|
|
1521
|
-
if (title === newTitle) {
|
|
1522
|
-
return
|
|
1523
|
-
}
|
|
1524
|
-
|
|
1525
|
-
// 先设置新文件名
|
|
1526
|
-
fileItem.title = newTitle
|
|
1527
|
-
|
|
1528
|
-
// 请求 - 修改文件名
|
|
1529
|
-
const { status } = await $n_http({
|
|
1530
|
-
url: $n_config('apiFileUrl') + 'edit_file_title',
|
|
1531
|
-
data: {
|
|
1532
|
-
hash,
|
|
1533
|
-
title: newTitle,
|
|
1534
|
-
},
|
|
1535
|
-
})
|
|
1536
|
-
|
|
1537
|
-
// 如果修改失败
|
|
1538
|
-
if (! status) {
|
|
1539
|
-
// 还原文件名
|
|
1540
|
-
fileItem.title = title
|
|
1541
|
-
return
|
|
1542
|
-
}
|
|
1543
|
-
|
|
1544
|
-
// 轻提示
|
|
1545
|
-
$n_toast({
|
|
1546
|
-
type: 'positive',
|
|
1547
|
-
message: '修改成功',
|
|
1548
|
-
})
|
|
1549
|
-
}
|
|
1550
|
-
}
|
|
1551
|
-
|
|
1552
|
-
/**
|
|
1553
|
-
* 复制地址
|
|
1554
|
-
*/
|
|
1555
|
-
function copyUrl({ type, hash }) {
|
|
1556
|
-
|
|
1557
|
-
const _url = type === FilE_TYPE.image ?
|
|
1558
|
-
// 如果是图片
|
|
1559
|
-
$n_getImage(hash)
|
|
1560
|
-
// 否则是文件
|
|
1561
|
-
: $n_getFile(hash)
|
|
1562
|
-
|
|
1563
|
-
if ($n_isValidString(_url)) {
|
|
1564
|
-
copy(_url, '复制地址成功')
|
|
1565
|
-
}
|
|
1566
|
-
}
|
|
1567
|
-
|
|
1568
|
-
/**
|
|
1569
|
-
* ==============================【私有函数】==============================
|
|
1570
|
-
*/
|
|
1571
|
-
|
|
1572
|
-
/**
|
|
1573
|
-
* 格式化单个文件 hash
|
|
1574
|
-
*/
|
|
1575
|
-
async function formatFileItemHash(hash) {
|
|
1576
|
-
|
|
1577
|
-
const {
|
|
1578
|
-
hasMinioSuffix,
|
|
1579
|
-
formatUploadFileHash,
|
|
1580
|
-
} = configs.uploader
|
|
1581
|
-
|
|
1582
|
-
if (hasMinioSuffix && configUpload.type === 'minio') {
|
|
1583
|
-
hash += '_'
|
|
1584
|
-
}
|
|
1585
|
-
|
|
1586
|
-
// 如果有格式化上传文件 hash
|
|
1587
|
-
if ($n_isFunction(formatUploadFileHash)) {
|
|
1588
|
-
hash = await $n_runAsync(formatUploadFileHash)(hash)
|
|
1589
|
-
}
|
|
1590
|
-
|
|
1591
|
-
return hash
|
|
1592
|
-
}
|
|
1593
|
-
|
|
1594
|
-
/**
|
|
1595
|
-
* 获取图片信息
|
|
1596
|
-
*/
|
|
1597
|
-
function getImageInfo(fileItem) {
|
|
1598
|
-
return new Promise(function (resolve) {
|
|
1599
|
-
const img = new Image()
|
|
1600
|
-
img.src = window.URL.createObjectURL(fileItem.file)
|
|
1601
|
-
fileItem.__img = img.src
|
|
1602
|
-
img.onload = function() {
|
|
1603
|
-
fileItem.json = {
|
|
1604
|
-
w: this.naturalWidth,
|
|
1605
|
-
h: this.naturalHeight,
|
|
1606
|
-
}
|
|
1607
|
-
resolve(true)
|
|
1608
|
-
}
|
|
1609
|
-
img.onerror = function() {
|
|
1610
|
-
resolve(false)
|
|
1611
|
-
}
|
|
1612
|
-
})
|
|
1613
|
-
}
|
|
1614
|
-
|
|
1615
|
-
/**
|
|
1616
|
-
* 获取媒体信息
|
|
1617
|
-
*/
|
|
1618
|
-
function getMediaInfo(fileItem, type) {
|
|
1619
|
-
return new Promise(function (resolve) {
|
|
1620
|
-
const dom = document.createElement(type)
|
|
1621
|
-
dom.src = URL.createObjectURL(fileItem.file)
|
|
1622
|
-
dom.onerror = function() {
|
|
1623
|
-
resolve(false)
|
|
1624
|
-
}
|
|
1625
|
-
|
|
1626
|
-
// 如果为视频
|
|
1627
|
-
if (type === 'video') {
|
|
1628
|
-
dom.currentTime = 3
|
|
1629
|
-
dom.oncanplay = async function() {
|
|
1630
|
-
|
|
1631
|
-
fileItem.json = {
|
|
1632
|
-
w: this.videoWidth ? this.videoWidth : this.width,
|
|
1633
|
-
h: this.videoHeight ? this.videoHeight : this.height,
|
|
1634
|
-
d: this.duration,
|
|
1635
|
-
}
|
|
1636
|
-
|
|
1637
|
-
// 获取视频缩略图 hash
|
|
1638
|
-
const p = await getVideoThumbHash(dom, fileItem.json)
|
|
1639
|
-
if (p) {
|
|
1640
|
-
fileItem.json.p = p
|
|
1641
|
-
}
|
|
1642
|
-
|
|
1643
|
-
resolve(true)
|
|
1644
|
-
}
|
|
1645
|
-
|
|
1646
|
-
// 否则为音频
|
|
1647
|
-
} else {
|
|
1648
|
-
dom.onloadedmetadata = function() {
|
|
1649
|
-
fileItem.json = {
|
|
1650
|
-
d: this.duration,
|
|
1651
|
-
}
|
|
1652
|
-
resolve(true)
|
|
1653
|
-
}
|
|
1654
|
-
}
|
|
1655
|
-
})
|
|
1656
|
-
}
|
|
1657
|
-
|
|
1658
|
-
/**
|
|
1659
|
-
* 获取视频缩略图 hash
|
|
1660
|
-
*/
|
|
1661
|
-
function getVideoThumbHash(dom, { w, h }) {
|
|
1662
|
-
return new Promise(async function (resolve) {
|
|
1663
|
-
|
|
1664
|
-
const canvas = document.createElement('canvas')
|
|
1665
|
-
const ctx = canvas.getContext('2d')
|
|
1666
|
-
canvas.setAttribute('width', w)
|
|
1667
|
-
canvas.setAttribute('height', h)
|
|
1668
|
-
ctx.drawImage(dom, 0, 0, w, h)
|
|
1669
|
-
|
|
1670
|
-
const {
|
|
1671
|
-
arrayBuffer,
|
|
1672
|
-
file,
|
|
1673
|
-
} = dataUrlToFile(canvas.toDataURL('image/jpeg', 0.8))
|
|
1674
|
-
|
|
1675
|
-
// 获取缩略图 hash
|
|
1676
|
-
const spark = new SparkMD5.ArrayBuffer()
|
|
1677
|
-
spark.append(arrayBuffer)
|
|
1678
|
-
const hash = spark.end(false)
|
|
1679
|
-
if (! hash) {
|
|
1680
|
-
resolve(false)
|
|
1681
|
-
return
|
|
1682
|
-
}
|
|
1683
|
-
|
|
1684
|
-
// 创建原始单个文件
|
|
1685
|
-
const fileItem = Object.assign(createRawFileItem(), {
|
|
1686
|
-
// 设置文件 file
|
|
1687
|
-
file,
|
|
1688
|
-
// 设置文件类型
|
|
1689
|
-
type: FilE_TYPE.image,
|
|
1690
|
-
// 设置文件后缀
|
|
1691
|
-
ext: 'jpg',
|
|
1692
|
-
// 设置文件大小
|
|
1693
|
-
size: file.size,
|
|
1694
|
-
// 设置文件 hash
|
|
1695
|
-
hash: await formatFileItemHash(hash),
|
|
1696
|
-
// 设置文件 json
|
|
1697
|
-
json: {
|
|
1698
|
-
w,
|
|
1699
|
-
h,
|
|
1700
|
-
},
|
|
1701
|
-
})
|
|
1702
|
-
|
|
1703
|
-
// 上传至服务器
|
|
1704
|
-
await uploadServer({
|
|
1705
|
-
fileType: FilE_TYPE.image,
|
|
1706
|
-
waitUploadFileLists: [ fileItem ],
|
|
1707
|
-
setFileSuccess(fileItem) {
|
|
1708
|
-
resolve(fileItem.hash)
|
|
1709
|
-
},
|
|
1710
|
-
setFileFail() {
|
|
1711
|
-
resolve(false)
|
|
1712
|
-
},
|
|
1713
|
-
})
|
|
1714
|
-
})
|
|
1715
|
-
}
|
|
1716
|
-
|
|
1717
|
-
/**
|
|
1718
|
-
* 设置单个文件信息
|
|
1719
|
-
*/
|
|
1720
|
-
function setFileItemInfo(fileItem, setFileFail) {
|
|
1721
|
-
return new Promise(async function (resolve) {
|
|
1722
|
-
|
|
1723
|
-
// 如果已经设置过了
|
|
1724
|
-
if (
|
|
1725
|
-
$n_has(fileItem.json, 'w')
|
|
1726
|
-
|| $n_has(fileItem.json, 'd')
|
|
1727
|
-
) {
|
|
1728
|
-
resolve(true)
|
|
1729
|
-
return
|
|
1730
|
-
}
|
|
1731
|
-
|
|
1732
|
-
let res
|
|
1733
|
-
|
|
1734
|
-
// 如果为图片
|
|
1735
|
-
// --------------------------------------------------
|
|
1736
|
-
if (fileItem.type === FilE_TYPE.image) {
|
|
1737
|
-
res = await getImageInfo(fileItem)
|
|
1738
|
-
|
|
1739
|
-
// 如果为视频
|
|
1740
|
-
// --------------------------------------------------
|
|
1741
|
-
} else if (fileItem.type === FilE_TYPE.video) {
|
|
1742
|
-
res = await getMediaInfo(fileItem, 'video')
|
|
1743
|
-
|
|
1744
|
-
// 如果为音频
|
|
1745
|
-
// --------------------------------------------------
|
|
1746
|
-
} else if (fileItem.type === FilE_TYPE.audio) {
|
|
1747
|
-
res = await getMediaInfo(fileItem, 'audio')
|
|
1748
|
-
|
|
1749
|
-
// 否则为文件
|
|
1750
|
-
} else {
|
|
1751
|
-
// 先判断是否为图片
|
|
1752
|
-
res = await getImageInfo(fileItem)
|
|
1753
|
-
if (! res) {
|
|
1754
|
-
// 再判断是否为视频
|
|
1755
|
-
res = await getMediaInfo(fileItem, 'video')
|
|
1756
|
-
if (! res) {
|
|
1757
|
-
// 最后再判断是否为音频
|
|
1758
|
-
res = await getMediaInfo(fileItem, 'video')
|
|
1759
|
-
}
|
|
1760
|
-
}
|
|
1761
|
-
}
|
|
1762
|
-
// --------------------------------------------------
|
|
1763
|
-
|
|
1764
|
-
if (res) {
|
|
1765
|
-
resolve(true)
|
|
1766
|
-
} else {
|
|
1767
|
-
// 设置文件上传失败
|
|
1768
|
-
setFileFail(fileItem)
|
|
1769
|
-
resolve(false)
|
|
1770
|
-
}
|
|
1771
|
-
})
|
|
1772
|
-
}
|
|
1773
|
-
|
|
1774
|
-
/**
|
|
1775
|
-
* 获取上传参数
|
|
1776
|
-
*/
|
|
1777
|
-
async function getUploadParams(type, bucket = 'public') {
|
|
1778
|
-
|
|
1779
|
-
// 缓存名
|
|
1780
|
-
const cacheName = `upload_params_${type}_${bucket}`
|
|
1781
|
-
|
|
1782
|
-
// 获取缓存
|
|
1783
|
-
const cache = $n_storage.get(cacheName)
|
|
1784
|
-
if (cache !== null) {
|
|
1785
|
-
return cache
|
|
1786
|
-
}
|
|
1787
|
-
|
|
1788
|
-
// 请求数据
|
|
1789
|
-
const { status, data } = await $n_http({
|
|
1790
|
-
url: $n_config('apiFileUrl') + 'get_upload_params',
|
|
1791
|
-
data: {
|
|
1792
|
-
// 类型
|
|
1793
|
-
type,
|
|
1794
|
-
// 空间名称
|
|
1795
|
-
bucket,
|
|
1796
|
-
},
|
|
1797
|
-
})
|
|
1798
|
-
|
|
1799
|
-
// 如果成功
|
|
1800
|
-
if (! status || ! $n_isValidObject(data)) {
|
|
1801
|
-
return false
|
|
1802
|
-
}
|
|
1803
|
-
|
|
1804
|
-
// 【生产模式】
|
|
1805
|
-
// --------------------------------------------------
|
|
1806
|
-
// #ifdef IS_PRO
|
|
1807
|
-
// 保存缓存(6 小时)
|
|
1808
|
-
$n_storage.set(cacheName, data, 21600000)
|
|
1809
|
-
// #endif
|
|
1810
|
-
// --------------------------------------------------
|
|
1811
|
-
|
|
1812
|
-
return data
|
|
1813
|
-
}
|
|
1814
|
-
|
|
1815
|
-
/**
|
|
1816
|
-
* 删除上传参数缓存
|
|
1817
|
-
*/
|
|
1818
|
-
function deleteUploadParams(type, bucket = 'public') {
|
|
1819
|
-
$n_storage.delete(`upload_params_${type}_${bucket}`)
|
|
1820
|
-
}
|
|
1821
|
-
|
|
1822
|
-
/**
|
|
1823
|
-
* 上传至服务器
|
|
1824
|
-
*/
|
|
1825
|
-
async function uploadServer(options) {
|
|
1826
|
-
|
|
1827
|
-
const {
|
|
1828
|
-
fileType,
|
|
1829
|
-
waitUploadFileLists,
|
|
1830
|
-
setFileSuccess,
|
|
1831
|
-
setFileFail,
|
|
1832
|
-
warn,
|
|
1833
|
-
} = Object.assign({
|
|
1834
|
-
warn: true,
|
|
1835
|
-
}, options)
|
|
1836
|
-
|
|
1837
|
-
// 获取上传参数
|
|
1838
|
-
const uploadParams = await getUploadParams(configUpload.type)
|
|
1839
|
-
if (uploadParams === false) {
|
|
1840
|
-
for (const fileItem of waitUploadFileLists) {
|
|
1841
|
-
setFileFail(fileItem, '上传失败')
|
|
1842
|
-
}
|
|
1843
|
-
if (warn) {
|
|
1844
|
-
$n_toast({
|
|
1845
|
-
message: `获取[${configUpload.type}]上传参数失败`,
|
|
1846
|
-
})
|
|
1847
|
-
}
|
|
1848
|
-
return false
|
|
1849
|
-
}
|
|
1850
|
-
|
|
1851
|
-
// 是否上传 minio 备份
|
|
1852
|
-
// --------------------------------------------------
|
|
1853
|
-
let backupParams = null
|
|
1854
|
-
let backupConfig = null
|
|
1855
|
-
if (configUpload.type !== 'minio') {
|
|
1856
|
-
backupConfig = getUpload(null, 'minio')
|
|
1857
|
-
// 如果同步
|
|
1858
|
-
if (backupConfig.sync === true) {
|
|
1859
|
-
backupParams = await getUploadParams(backupConfig.type)
|
|
1860
|
-
if (backupParams === false) {
|
|
1861
|
-
for (const fileItem of waitUploadFileLists) {
|
|
1862
|
-
setFileFail(fileItem, '上传失败')
|
|
1863
|
-
}
|
|
1864
|
-
if (warn) {
|
|
1865
|
-
$n_toast({
|
|
1866
|
-
message: `获取[${backupConfig.type}]上传参数失败`,
|
|
1867
|
-
})
|
|
1868
|
-
}
|
|
1869
|
-
return false
|
|
1870
|
-
}
|
|
1871
|
-
}
|
|
1872
|
-
}
|
|
1873
|
-
// --------------------------------------------------
|
|
1874
|
-
|
|
1875
|
-
// 批量上传
|
|
1876
|
-
for (const fileItem of waitUploadFileLists) {
|
|
1877
|
-
// 上传单个文件
|
|
1878
|
-
await uploadFileItem(fileItem)
|
|
1879
|
-
}
|
|
1880
|
-
|
|
1881
|
-
/**
|
|
1882
|
-
* 上传单个文件
|
|
1883
|
-
*/
|
|
1884
|
-
async function uploadFileItem(fileItem) {
|
|
1885
|
-
|
|
1886
|
-
// 设置文件状态
|
|
1887
|
-
fileItem.status = UPLOAD_STATUS.uploading
|
|
1888
|
-
|
|
1889
|
-
const {
|
|
1890
|
-
// 上传请求
|
|
1891
|
-
onUploadHttp,
|
|
1892
|
-
} = configs.uploader
|
|
1893
|
-
|
|
1894
|
-
// 上传文件
|
|
1895
|
-
const upload = async function(configUpload, uploadParams, startPercent, halfPercent) {
|
|
1896
|
-
|
|
1897
|
-
const {
|
|
1898
|
-
// 上传地址
|
|
1899
|
-
url,
|
|
1900
|
-
// 文件名
|
|
1901
|
-
fileName,
|
|
1902
|
-
// 键值名
|
|
1903
|
-
keyName,
|
|
1904
|
-
// 上传数据
|
|
1905
|
-
data,
|
|
1906
|
-
} = uploadParams
|
|
1907
|
-
|
|
1908
|
-
// 请求数据
|
|
1909
|
-
const httpData = Object.assign({}, data)
|
|
1910
|
-
// 文件
|
|
1911
|
-
httpData[fileName] = fileItem.file
|
|
1912
|
-
// 自定义文件 key
|
|
1913
|
-
httpData[keyName] = fileItem.hash
|
|
1914
|
-
|
|
1915
|
-
let opts = {
|
|
1916
|
-
// 上传地址
|
|
1917
|
-
url,
|
|
1918
|
-
// 数据
|
|
1919
|
-
data: httpData,
|
|
1920
|
-
// 关闭错误提醒
|
|
1921
|
-
warn: false,
|
|
1922
|
-
// 关闭检查结果 code
|
|
1923
|
-
checkCode: false,
|
|
1924
|
-
// 不包含头部鉴权认证
|
|
1925
|
-
token: false,
|
|
1926
|
-
// 开启上传
|
|
1927
|
-
upload: true,
|
|
1928
|
-
// 取消请求
|
|
1929
|
-
onCancel(cancel) {
|
|
1930
|
-
// 设置中断上传
|
|
1931
|
-
fileItem.abort = function(msg) {
|
|
1932
|
-
cancel($n_isValidString(msg) ? msg : '已取消')
|
|
1933
|
-
}
|
|
1934
|
-
},
|
|
1935
|
-
// 监听上传进度
|
|
1936
|
-
onUploadProgress(percent) {
|
|
1937
|
-
// 设置上传进度
|
|
1938
|
-
fileItem.progress = Math.round(startPercent + (halfPercent ? percent / 2 : percent))
|
|
1939
|
-
},
|
|
1940
|
-
}
|
|
1941
|
-
|
|
1942
|
-
// 上传请求
|
|
1943
|
-
if ($n_isFunction(onUploadHttp)) {
|
|
1944
|
-
const res = onUploadHttp(opts, fileItem)
|
|
1945
|
-
if (res) {
|
|
1946
|
-
opts = res
|
|
1947
|
-
}
|
|
1948
|
-
}
|
|
1949
|
-
|
|
1950
|
-
const { status, data: res } = await $n_http(opts)
|
|
1951
|
-
|
|
1952
|
-
// 如果请求失败
|
|
1953
|
-
if (! status) {
|
|
1954
|
-
// 设置文件上传失败
|
|
1955
|
-
setFileFail(fileItem, res.msg)
|
|
1956
|
-
// 删除上传参数缓存
|
|
1957
|
-
deleteUploadParams(configUpload.type)
|
|
1958
|
-
return false
|
|
1959
|
-
}
|
|
1960
|
-
|
|
1961
|
-
return res
|
|
1962
|
-
}
|
|
1963
|
-
|
|
1964
|
-
const resUpload = await upload(configUpload, uploadParams, 0, !! backupParams)
|
|
1965
|
-
if (resUpload === false) {
|
|
1966
|
-
return false
|
|
1967
|
-
}
|
|
1968
|
-
|
|
1969
|
-
// 是否已备份
|
|
1970
|
-
let is_backup = 0
|
|
1971
|
-
|
|
1972
|
-
// 上传至备份服务器
|
|
1973
|
-
// --------------------------------------------------
|
|
1974
|
-
if (backupParams) {
|
|
1975
|
-
const res = await upload(backupConfig, backupParams, 50, true)
|
|
1976
|
-
if (res === false) {
|
|
1977
|
-
return false
|
|
1978
|
-
}
|
|
1979
|
-
is_backup = 1
|
|
1980
|
-
}
|
|
1981
|
-
// --------------------------------------------------
|
|
1982
|
-
|
|
1983
|
-
const {
|
|
1984
|
-
title,
|
|
1985
|
-
type,
|
|
1986
|
-
hash,
|
|
1987
|
-
ext,
|
|
1988
|
-
size,
|
|
1989
|
-
json,
|
|
1990
|
-
} = fileItem
|
|
1991
|
-
|
|
1992
|
-
// 请求数据
|
|
1993
|
-
const data = {
|
|
1994
|
-
// 类型
|
|
1995
|
-
type: configUpload.type,
|
|
1996
|
-
// 需上传的文件类型
|
|
1997
|
-
file_type: fileType,
|
|
1998
|
-
// 文件
|
|
1999
|
-
file: {
|
|
2000
|
-
// 标题
|
|
2001
|
-
title: title || '',
|
|
2002
|
-
// 类型
|
|
2003
|
-
type,
|
|
2004
|
-
// hash
|
|
2005
|
-
hash,
|
|
2006
|
-
// 后缀
|
|
2007
|
-
ext,
|
|
2008
|
-
// 文件大小
|
|
2009
|
-
size,
|
|
2010
|
-
// 文件 json
|
|
2011
|
-
json,
|
|
2012
|
-
// 是否已备份
|
|
2013
|
-
is_backup,
|
|
2014
|
-
},
|
|
2015
|
-
// 结果
|
|
2016
|
-
result: $n_isValidObject(resUpload) ? resUpload : {},
|
|
2017
|
-
}
|
|
2018
|
-
|
|
2019
|
-
// 请求 - 上传文件至 cdn
|
|
2020
|
-
const { status: statusCallback, data: resCallback } = await $n_http({
|
|
2021
|
-
url: $n_config('apiFileUrl') + 'upload_callback',
|
|
2022
|
-
data,
|
|
2023
|
-
// 关闭错误提示
|
|
2024
|
-
warn: false,
|
|
2025
|
-
})
|
|
2026
|
-
|
|
2027
|
-
// 请求失败
|
|
2028
|
-
if (! statusCallback || ! $n_isValidObject(resCallback)) {
|
|
2029
|
-
// 设置文件上传失败
|
|
2030
|
-
setFileFail(fileItem, resCallback.msg || '上传失败')
|
|
2031
|
-
return false
|
|
2032
|
-
}
|
|
2033
|
-
|
|
2034
|
-
// 格式化 json
|
|
2035
|
-
const _json = $n_json.parse(resCallback.json)
|
|
2036
|
-
Object.assign(fileItem, resCallback, {
|
|
2037
|
-
json: $n_isValidObject(_json) ? $n_numberDeep(_json) : {},
|
|
2038
|
-
})
|
|
2039
|
-
|
|
2040
|
-
// 设置文件上传成功
|
|
2041
|
-
setFileSuccess(fileItem)
|
|
2042
|
-
|
|
2043
|
-
return true
|
|
2044
|
-
}
|
|
2045
|
-
|
|
2046
|
-
return true
|
|
2047
|
-
}
|
|
2048
|
-
|
|
2049
|
-
return {
|
|
2050
|
-
// 上传文件列表
|
|
2051
|
-
query: uploadFileLists,
|
|
2052
|
-
// 更新值
|
|
2053
|
-
updateValue,
|
|
2054
|
-
// 初始化上传列表
|
|
2055
|
-
initUploadFileLists,
|
|
2056
|
-
|
|
2057
|
-
// 初始化上传网络外链列表
|
|
2058
|
-
initUploadNetLists,
|
|
2059
|
-
// 上传网络外链文件
|
|
2060
|
-
uploadNet,
|
|
2061
|
-
// 获取上传网络外链进度
|
|
2062
|
-
// getUploadNetProgress,
|
|
2063
|
-
|
|
2064
|
-
// 检查是否正在上传文件
|
|
2065
|
-
checkUploading,
|
|
2066
|
-
// 选择文件上传
|
|
2067
|
-
chooseUpload,
|
|
2068
|
-
// 选择网络外链上传
|
|
2069
|
-
chooseUploadNet,
|
|
2070
|
-
// 文件输入框更新
|
|
2071
|
-
fileChange,
|
|
2072
|
-
// 删除所有文件
|
|
2073
|
-
deleteAll,
|
|
2074
|
-
// 删除单个文件
|
|
2075
|
-
deleteFileItem,
|
|
2076
|
-
// 预览图片
|
|
2077
|
-
previewImage,
|
|
2078
|
-
// 修改文件名
|
|
2079
|
-
editFileTitle,
|
|
2080
|
-
// 播放视频/音频
|
|
2081
|
-
play({ hash, __img }) {
|
|
2082
|
-
$n_play(__img ? __img : hash)
|
|
2083
|
-
},
|
|
2084
|
-
// 复制地址
|
|
2085
|
-
copyUrl,
|
|
2086
|
-
}
|
|
2087
|
-
}
|
|
2088
|
-
|
|
2089
|
-
/**
|
|
2090
|
-
* 获取上传方式
|
|
2091
|
-
*/
|
|
2092
|
-
export function getUpload(userConfig = null, defaultUpload = '') {
|
|
2093
|
-
const uploadConfig = $n_get((userConfig ? userConfig : configs.userConfig), 'uploader.upload', {})
|
|
2094
|
-
const type = $n_isValidString(defaultUpload) ? defaultUpload : uploadConfig.default
|
|
2095
|
-
return Object.assign(
|
|
2096
|
-
{},
|
|
2097
|
-
$n_get(uploadConfig, type, {}),
|
|
2098
|
-
{
|
|
2099
|
-
type,
|
|
2100
|
-
}
|
|
2101
|
-
)
|
|
2102
|
-
}
|
|
2103
|
-
|
|
2104
|
-
/**
|
|
2105
|
-
* 上传器
|
|
2106
|
-
*/
|
|
2107
|
-
const uploader = {
|
|
2108
|
-
// 创建对话框
|
|
2109
|
-
create,
|
|
2110
|
-
// 获取上传方式
|
|
2111
|
-
getUpload,
|
|
2112
|
-
}
|
|
2113
|
-
|
|
2114
|
-
export default uploader
|
|
1
|
+
import { ref, isRef, } from 'vue'
|
|
2
|
+
import { useQuasar } from 'quasar'
|
|
3
|
+
import SparkMD5 from 'spark-md5'
|
|
4
|
+
|
|
5
|
+
import $n_has from 'lodash/has'
|
|
6
|
+
import $n_get from 'lodash/get'
|
|
7
|
+
import $n_toLower from 'lodash/toLower'
|
|
8
|
+
import $n_findIndex from 'lodash/findIndex'
|
|
9
|
+
import $n_uniq from 'lodash/uniq'
|
|
10
|
+
import $n_find from 'lodash/find'
|
|
11
|
+
import $n_isFunction from 'lodash/isFunction'
|
|
12
|
+
|
|
13
|
+
import $n_storage from '@netang/utils/storage'
|
|
14
|
+
import $n_isValidArray from '@netang/utils/isValidArray'
|
|
15
|
+
import $n_isValidObject from '@netang/utils/isValidObject'
|
|
16
|
+
import $n_isValidString from '@netang/utils/isValidString'
|
|
17
|
+
import $n_forEach from '@netang/utils/forEach'
|
|
18
|
+
import $n_indexOf from '@netang/utils/indexOf'
|
|
19
|
+
import $n_json from '@netang/utils/json'
|
|
20
|
+
import $n_join from '@netang/utils/join'
|
|
21
|
+
import $n_split from '@netang/utils/split'
|
|
22
|
+
import $n_trimString from '@netang/utils/trimString'
|
|
23
|
+
import $n_run from '@netang/utils/run'
|
|
24
|
+
import $n_isValidValue from '@netang/utils/isValidValue'
|
|
25
|
+
import $n_http from '@netang/utils/http'
|
|
26
|
+
import $n_getThrowMessage from '@netang/utils/getThrowMessage'
|
|
27
|
+
import $n_runAsync from '@netang/utils/runAsync'
|
|
28
|
+
import $n_numberDeep from '@netang/utils/numberDeep'
|
|
29
|
+
|
|
30
|
+
import $n_$ruleValid from './$ruleValid'
|
|
31
|
+
import $n_toast from './toast'
|
|
32
|
+
import $n_confirm from './confirm'
|
|
33
|
+
import $n_alert from './alert'
|
|
34
|
+
import $n_previewImage from './previewImage'
|
|
35
|
+
import $n_getImage from './getImage'
|
|
36
|
+
import $n_getFile from './getFile'
|
|
37
|
+
import $n_config from './config'
|
|
38
|
+
import $n_timestamp from './timestamp'
|
|
39
|
+
import $n_play from './play'
|
|
40
|
+
|
|
41
|
+
import copy from './copy'
|
|
42
|
+
import { configs } from './config'
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* 文件类型映射
|
|
46
|
+
*/
|
|
47
|
+
export const FilE_TYPE = {
|
|
48
|
+
file: 1,
|
|
49
|
+
image: 2,
|
|
50
|
+
video: 3,
|
|
51
|
+
audio: 4,
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* 文件名称映射
|
|
56
|
+
*/
|
|
57
|
+
export const FilE_NAME = {
|
|
58
|
+
1: '文件',
|
|
59
|
+
2: '图片',
|
|
60
|
+
3: '视频',
|
|
61
|
+
4: '音频',
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* 上传状态
|
|
66
|
+
*/
|
|
67
|
+
export const UPLOAD_STATUS = {
|
|
68
|
+
// 等待上传中
|
|
69
|
+
waiting: 1,
|
|
70
|
+
// 检查 hash 中
|
|
71
|
+
hashChecking: 2,
|
|
72
|
+
// 检查 hash 完成
|
|
73
|
+
hashChecked: 3,
|
|
74
|
+
// 检查是否存在服务器中
|
|
75
|
+
existChecking: 4,
|
|
76
|
+
// 检查是否存在服务器完成
|
|
77
|
+
existChecked: 5,
|
|
78
|
+
// 上传中
|
|
79
|
+
uploading: 6,
|
|
80
|
+
// 上传完成
|
|
81
|
+
success: 7,
|
|
82
|
+
// 上传失败
|
|
83
|
+
fail: 8,
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
// 文件数量
|
|
87
|
+
let _fileNum = 0
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* 将 base64 转换为 File
|
|
91
|
+
*/
|
|
92
|
+
function dataUrlToFile(dataUrl) {
|
|
93
|
+
const arr = dataUrl.split(',')
|
|
94
|
+
const mime = arr[0].match(/:(.*?);/)[1]
|
|
95
|
+
const bstr = window.atob(arr[1])
|
|
96
|
+
let n = bstr.length
|
|
97
|
+
const arrayBuffer = new Uint8Array(n)
|
|
98
|
+
while (n--) {
|
|
99
|
+
arrayBuffer[n] = bstr.charCodeAt(n)
|
|
100
|
+
}
|
|
101
|
+
const blob = new Blob([arrayBuffer], { type: mime })
|
|
102
|
+
|
|
103
|
+
return {
|
|
104
|
+
arrayBuffer,
|
|
105
|
+
file: new File([blob], String($n_timestamp()), { type: blob.type }),
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* 创建上传器
|
|
111
|
+
*/
|
|
112
|
+
function create(options) {
|
|
113
|
+
|
|
114
|
+
// ==========【数据】=========================================================================================
|
|
115
|
+
|
|
116
|
+
// quasar 对象
|
|
117
|
+
const $q = useQuasar()
|
|
118
|
+
|
|
119
|
+
const {
|
|
120
|
+
// 上传器类型
|
|
121
|
+
uploaderType,
|
|
122
|
+
// 上传文件输入框节点
|
|
123
|
+
fileRef,
|
|
124
|
+
// 更新值方法(初始化上传列表时不更新值)
|
|
125
|
+
onUpdateModelValue,
|
|
126
|
+
// 更新方法
|
|
127
|
+
onUpdate,
|
|
128
|
+
|
|
129
|
+
} = Object.assign({
|
|
130
|
+
// 上传器类型
|
|
131
|
+
uploaderType: null,
|
|
132
|
+
// 更新值方法
|
|
133
|
+
onUpdateModelValue: null,
|
|
134
|
+
// 更新方法
|
|
135
|
+
onUpdate: null,
|
|
136
|
+
}, options)
|
|
137
|
+
|
|
138
|
+
const optionsProps = $n_get(options, 'props')
|
|
139
|
+
|
|
140
|
+
// 声明属性
|
|
141
|
+
const props = Object.assign({
|
|
142
|
+
// 值
|
|
143
|
+
modelValue: '',
|
|
144
|
+
// 上传文件类型, 可选值 file image video audio
|
|
145
|
+
type: 'image',
|
|
146
|
+
// 上传文件数量(0:不限)
|
|
147
|
+
count: 0,
|
|
148
|
+
// 单个文件的最大大小(单位: MB)
|
|
149
|
+
maxSize: 0,
|
|
150
|
+
// 单个文件的限制后缀
|
|
151
|
+
exts: [],
|
|
152
|
+
// true: 值格式为数组, 如 ['xxxxxx', 'xxxxxx', 'xxxxxx']
|
|
153
|
+
// false: 值格式为字符串, 如 xxxxxx,xxxxxx,xxxxxx
|
|
154
|
+
valueArray: false,
|
|
155
|
+
// 是否去重
|
|
156
|
+
unique: false,
|
|
157
|
+
// 是否初始加载文件信息(仅图片有效, 其他类型自动会加载文件信息)
|
|
158
|
+
loadInfo: false,
|
|
159
|
+
// 单文件上传提示
|
|
160
|
+
confirm: false,
|
|
161
|
+
}, optionsProps)
|
|
162
|
+
|
|
163
|
+
// options 中是否存在 props.modelValue
|
|
164
|
+
const hasPropsModelValue = $n_has(optionsProps, 'modelValue')
|
|
165
|
+
|
|
166
|
+
// 上传文件列表
|
|
167
|
+
const uploadFileLists = $n_has(options, 'uploadFileLists') && isRef(options.uploadFileLists) ? options.uploadFileLists : ref([])
|
|
168
|
+
|
|
169
|
+
// 上传网络外链回调
|
|
170
|
+
let uploadNetCallback
|
|
171
|
+
|
|
172
|
+
/**
|
|
173
|
+
* 上传配置
|
|
174
|
+
*/
|
|
175
|
+
const configUpload = getUpload(null, uploaderType)
|
|
176
|
+
|
|
177
|
+
const configLimit = Object.assign(
|
|
178
|
+
{
|
|
179
|
+
maxSize: 100,
|
|
180
|
+
exts: [],
|
|
181
|
+
},
|
|
182
|
+
$n_config('uploader.limit.' + props.type, {})
|
|
183
|
+
)
|
|
184
|
+
|
|
185
|
+
// 如果有单个文件的最大大小
|
|
186
|
+
if (props.maxSize) {
|
|
187
|
+
configLimit.maxSize = props.maxSize
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
// 如果有单个文件的限制后缀
|
|
191
|
+
if ($n_isValidArray(props.exts)) {
|
|
192
|
+
configLimit.exts = props.exts
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
// ==========【计算属性】=========================================================================================
|
|
196
|
+
|
|
197
|
+
/**
|
|
198
|
+
* 上传文件后缀名
|
|
199
|
+
*/
|
|
200
|
+
// const accept = computed(function () {
|
|
201
|
+
//
|
|
202
|
+
// })
|
|
203
|
+
|
|
204
|
+
// ==========【监听数据】==============================================================================================
|
|
205
|
+
|
|
206
|
+
/**
|
|
207
|
+
* 监听上传文件列表
|
|
208
|
+
*/
|
|
209
|
+
// if (props.watchModelValue && hasPropsModelValue) {
|
|
210
|
+
// watch(()=>options.props.modelValue, function() {
|
|
211
|
+
// // 初始化上传列表
|
|
212
|
+
// initUploadFileLists()
|
|
213
|
+
// .finally()
|
|
214
|
+
// })
|
|
215
|
+
// }
|
|
216
|
+
|
|
217
|
+
// ==========【方法】=================================================================================================
|
|
218
|
+
|
|
219
|
+
/**
|
|
220
|
+
* 获取值
|
|
221
|
+
*/
|
|
222
|
+
function getValue() {
|
|
223
|
+
|
|
224
|
+
const hashs = []
|
|
225
|
+
const files = []
|
|
226
|
+
|
|
227
|
+
for (const fileItem of uploadFileLists.value) {
|
|
228
|
+
if (fileItem.status === UPLOAD_STATUS.success) {
|
|
229
|
+
hashs.push(fileItem.hash)
|
|
230
|
+
files.push(fileItem)
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
const hashsString = $n_join(hashs, ',')
|
|
235
|
+
|
|
236
|
+
return {
|
|
237
|
+
value: props.valueArray ? hashs : hashsString,
|
|
238
|
+
hashsString,
|
|
239
|
+
hashs,
|
|
240
|
+
files,
|
|
241
|
+
query: uploadFileLists,
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
/**
|
|
246
|
+
* 更新值
|
|
247
|
+
*/
|
|
248
|
+
function updateValue() {
|
|
249
|
+
|
|
250
|
+
// 获取值
|
|
251
|
+
const result = getValue()
|
|
252
|
+
|
|
253
|
+
// 更新值
|
|
254
|
+
$n_run(onUpdateModelValue)(result)
|
|
255
|
+
|
|
256
|
+
// 更新
|
|
257
|
+
$n_run(onUpdate)(result)
|
|
258
|
+
|
|
259
|
+
// 上传网络外链回调
|
|
260
|
+
$n_run(uploadNetCallback)()
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
/**
|
|
264
|
+
* 更新
|
|
265
|
+
*/
|
|
266
|
+
function update() {
|
|
267
|
+
// 更新
|
|
268
|
+
$n_run(onUpdate)(getValue())
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
/**
|
|
272
|
+
* 初始化上传列表
|
|
273
|
+
*/
|
|
274
|
+
async function initUploadFileLists() {
|
|
275
|
+
|
|
276
|
+
const modelValue = hasPropsModelValue ? options.props.modelValue : props.modelValue
|
|
277
|
+
|
|
278
|
+
// 值数组
|
|
279
|
+
const hashs = []
|
|
280
|
+
|
|
281
|
+
// hash all
|
|
282
|
+
const hashAll = {}
|
|
283
|
+
|
|
284
|
+
// 获取值数组
|
|
285
|
+
const lists = props.valueArray ? modelValue : $n_split(modelValue, ',')
|
|
286
|
+
|
|
287
|
+
if (
|
|
288
|
+
// 如果只能上传一个
|
|
289
|
+
props.count === 1
|
|
290
|
+
// 如果为空
|
|
291
|
+
&& ! $n_isValidArray(lists)
|
|
292
|
+
) {
|
|
293
|
+
// 更新上传文件列表
|
|
294
|
+
uploadFileLists.value = []
|
|
295
|
+
return
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
// 新上传文件列表
|
|
299
|
+
const newUploadFileLists = []
|
|
300
|
+
|
|
301
|
+
// 新列表
|
|
302
|
+
const newLists = []
|
|
303
|
+
|
|
304
|
+
// 是否更新
|
|
305
|
+
let isUpdate = false
|
|
306
|
+
|
|
307
|
+
// 合并当前上传列表中未上传成功的文件
|
|
308
|
+
for (const fileItem of uploadFileLists.value) {
|
|
309
|
+
if (fileItem.status !== UPLOAD_STATUS.success) {
|
|
310
|
+
newUploadFileLists.push(fileItem)
|
|
311
|
+
hashAll[fileItem.hash] = fileItem
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
$n_forEach(lists, function(hash) {
|
|
316
|
+
if ($n_isValidString(hash)) {
|
|
317
|
+
|
|
318
|
+
const hasItem = $n_find(uploadFileLists.value, { hash })
|
|
319
|
+
|
|
320
|
+
// 如果在当前上传文件列表中已存在
|
|
321
|
+
if (hasItem) {
|
|
322
|
+
hashAll[hash] = hasItem
|
|
323
|
+
|
|
324
|
+
} else if (! $n_has(hashAll, 'hash')) {
|
|
325
|
+
|
|
326
|
+
// 如果是外链
|
|
327
|
+
if (/^http(s)?:\/\//i.test(hash)) {
|
|
328
|
+
hashs.push(hash)
|
|
329
|
+
hashAll[hash] = {
|
|
330
|
+
hash,
|
|
331
|
+
__img: hash,
|
|
332
|
+
isNet: true,
|
|
333
|
+
isNetUploaded: false,
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
// 否则为 hash 文件
|
|
337
|
+
} else {
|
|
338
|
+
hashs.push(hash)
|
|
339
|
+
hashAll[hash] = {
|
|
340
|
+
hash,
|
|
341
|
+
isNet: false,
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
// 新列表
|
|
347
|
+
newLists.push(hash)
|
|
348
|
+
}
|
|
349
|
+
})
|
|
350
|
+
|
|
351
|
+
// 如果类型不是图片 || 初始加载文件信息, 则请求文件信息
|
|
352
|
+
if (
|
|
353
|
+
(props.type !== 'image' || props.loadInfo)
|
|
354
|
+
&& hashs.length
|
|
355
|
+
) {
|
|
356
|
+
// 请求 - 获取文件
|
|
357
|
+
const { status, data: resExisted } = await $n_http({
|
|
358
|
+
url: $n_config('apiFileUrl') + 'get_file',
|
|
359
|
+
data: {
|
|
360
|
+
hashs: $n_uniq(hashs),
|
|
361
|
+
},
|
|
362
|
+
// 关闭错误
|
|
363
|
+
warn: false,
|
|
364
|
+
// 关闭防抖(可以重复请求)
|
|
365
|
+
debounce: false,
|
|
366
|
+
})
|
|
367
|
+
if (status) {
|
|
368
|
+
|
|
369
|
+
$n_forEach(resExisted, function (existedItem) {
|
|
370
|
+
|
|
371
|
+
// 创建原始单个文件
|
|
372
|
+
const fileItem = createRawFileItem()
|
|
373
|
+
|
|
374
|
+
// 设置已存在文件
|
|
375
|
+
setExistedFileItem(fileItem, existedItem)
|
|
376
|
+
|
|
377
|
+
// 添加至 hash all
|
|
378
|
+
hashAll[fileItem.hash] = Object.assign(fileItem, {
|
|
379
|
+
key: fileItem.hash,
|
|
380
|
+
})
|
|
381
|
+
})
|
|
382
|
+
|
|
383
|
+
// 需要更新
|
|
384
|
+
isUpdate = true
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
for (const hash of newLists) {
|
|
389
|
+
|
|
390
|
+
let hasItem = hashAll[hash]
|
|
391
|
+
|
|
392
|
+
// 如果该 hash 已存在
|
|
393
|
+
if ($n_has(hasItem, 'id')) {
|
|
394
|
+
|
|
395
|
+
// 如果新列表中存在 该 id
|
|
396
|
+
if ($n_findIndex(newUploadFileLists, { id: hasItem.id }) > -1) {
|
|
397
|
+
|
|
398
|
+
// 更新 id
|
|
399
|
+
hasItem = Object.assign({}, hasItem, {
|
|
400
|
+
id: ++_fileNum
|
|
401
|
+
})
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
// 否则该 hash 不存在
|
|
405
|
+
} else {
|
|
406
|
+
hasItem = Object.assign(createRawFileItem(), hasItem, {
|
|
407
|
+
// 文件唯一 key
|
|
408
|
+
key: hash,
|
|
409
|
+
// hash
|
|
410
|
+
hash,
|
|
411
|
+
// 状态
|
|
412
|
+
status: UPLOAD_STATUS.success,
|
|
413
|
+
// 进度
|
|
414
|
+
progress: 100,
|
|
415
|
+
// 信息
|
|
416
|
+
msg: '',
|
|
417
|
+
})
|
|
418
|
+
hashAll[hash] = hasItem
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
// 添加至新列表中
|
|
422
|
+
newUploadFileLists.push(hasItem)
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
// 更新上传文件列表
|
|
426
|
+
uploadFileLists.value = newUploadFileLists
|
|
427
|
+
|
|
428
|
+
if (isUpdate) {
|
|
429
|
+
// 更新
|
|
430
|
+
update()
|
|
431
|
+
}
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
/**
|
|
435
|
+
* 初始化上传网络外链列表
|
|
436
|
+
*/
|
|
437
|
+
function initUploadNetLists(callback) {
|
|
438
|
+
|
|
439
|
+
// 如果提交时禁止上传网络外链文件
|
|
440
|
+
if ($n_get(optionsProps, 'submitUploadNet') !== true) {
|
|
441
|
+
return
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
uploadNetCallback = callback
|
|
445
|
+
|
|
446
|
+
for (const fileItem of uploadFileLists.value) {
|
|
447
|
+
if (
|
|
448
|
+
fileItem.isNet
|
|
449
|
+
&& fileItem.status === UPLOAD_STATUS.success
|
|
450
|
+
) {
|
|
451
|
+
// 将文件状态修改为: 等待上传中
|
|
452
|
+
fileItem.status = UPLOAD_STATUS.waiting
|
|
453
|
+
}
|
|
454
|
+
}
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
/**
|
|
458
|
+
* 上传网络外链文件
|
|
459
|
+
*/
|
|
460
|
+
async function uploadNet(submitUploadNet = false) {
|
|
461
|
+
|
|
462
|
+
// 如果提交时禁止上传网络外链文件
|
|
463
|
+
if (submitUploadNet === true || $n_get(optionsProps, 'submitUploadNet') === true) {
|
|
464
|
+
|
|
465
|
+
const promises = []
|
|
466
|
+
|
|
467
|
+
for (const fileItem of uploadFileLists.value) {
|
|
468
|
+
if (
|
|
469
|
+
fileItem.isNet
|
|
470
|
+
&& fileItem.status === UPLOAD_STATUS.waiting
|
|
471
|
+
) {
|
|
472
|
+
// 设置网络图片 file
|
|
473
|
+
promises.push(setNetFile(fileItem))
|
|
474
|
+
}
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
if (! promises.length) {
|
|
478
|
+
return
|
|
479
|
+
}
|
|
480
|
+
await Promise.all(promises)
|
|
481
|
+
|
|
482
|
+
// 检查待上传文件在服务器上是否存在
|
|
483
|
+
// --------------------------------------------------
|
|
484
|
+
if (! await checkWaitUploadFileExists()) {
|
|
485
|
+
return
|
|
486
|
+
}
|
|
487
|
+
|
|
488
|
+
// 上传
|
|
489
|
+
await upload()
|
|
490
|
+
}
|
|
491
|
+
}
|
|
492
|
+
|
|
493
|
+
/**
|
|
494
|
+
* 获取上传网络外链进度
|
|
495
|
+
*/
|
|
496
|
+
// function getUploadNetProgress() {
|
|
497
|
+
//
|
|
498
|
+
// let total = 0
|
|
499
|
+
// let loaded = 0
|
|
500
|
+
//
|
|
501
|
+
// // 如果提交时允许上传网络外链文件
|
|
502
|
+
// if ($n_get(optionsProps, 'submitUploadNet') === true) {
|
|
503
|
+
//
|
|
504
|
+
// for (const fileItem of uploadFileLists.value) {
|
|
505
|
+
// if (fileItem.isNet && fileItem.status <= UPLOAD_STATUS.success) {
|
|
506
|
+
// total++
|
|
507
|
+
// if (fileItem.isNetUploaded) {
|
|
508
|
+
// loaded++
|
|
509
|
+
// }
|
|
510
|
+
// }
|
|
511
|
+
// }
|
|
512
|
+
//
|
|
513
|
+
// // if (total && loaded < total) {
|
|
514
|
+
// // return {
|
|
515
|
+
// // loaded,
|
|
516
|
+
// // total,
|
|
517
|
+
// // // progress: Math.round(loaded * 100 / total),
|
|
518
|
+
// // }
|
|
519
|
+
// // }
|
|
520
|
+
// }
|
|
521
|
+
//
|
|
522
|
+
// return {
|
|
523
|
+
// loaded,
|
|
524
|
+
// total,
|
|
525
|
+
// // progress: 100,
|
|
526
|
+
// }
|
|
527
|
+
// }
|
|
528
|
+
|
|
529
|
+
/**
|
|
530
|
+
* 设置网络图片 file
|
|
531
|
+
*/
|
|
532
|
+
async function setNetFile(fileItem) {
|
|
533
|
+
|
|
534
|
+
// 设置文件状态
|
|
535
|
+
fileItem.status = UPLOAD_STATUS.hashChecking
|
|
536
|
+
// 设置文件检查进度
|
|
537
|
+
fileItem.progress = 0
|
|
538
|
+
|
|
539
|
+
try {
|
|
540
|
+
const r = await fetch(fileItem.__img, {
|
|
541
|
+
method: 'GET',
|
|
542
|
+
})
|
|
543
|
+
const arrayBuffer = await r.arrayBuffer()
|
|
544
|
+
const blob = new Blob([arrayBuffer], { type: r.headers.get('Content-Type') })
|
|
545
|
+
|
|
546
|
+
// -------- axios
|
|
547
|
+
// const r = await axios({
|
|
548
|
+
// method: 'GET',
|
|
549
|
+
// url: fileItem.__img,
|
|
550
|
+
// responseType: 'arraybuffer'
|
|
551
|
+
// })
|
|
552
|
+
// console.log(r)
|
|
553
|
+
// const arrayBuffer = r.data
|
|
554
|
+
// const blob = new Blob([arrayBuffer], { type: r.headers['content-type'] })
|
|
555
|
+
// -------- axios
|
|
556
|
+
|
|
557
|
+
// 如果有类型
|
|
558
|
+
if (blob.type) {
|
|
559
|
+
|
|
560
|
+
// 后缀名
|
|
561
|
+
let ext = ''
|
|
562
|
+
|
|
563
|
+
// 如果为图片
|
|
564
|
+
if (
|
|
565
|
+
props.type === 'image'
|
|
566
|
+
|| $n_indexOf(blob.type, 'image/') > -1
|
|
567
|
+
) {
|
|
568
|
+
switch (blob.type) {
|
|
569
|
+
case 'image/png':
|
|
570
|
+
ext = 'png'
|
|
571
|
+
break
|
|
572
|
+
case 'image/gif':
|
|
573
|
+
ext = 'gif'
|
|
574
|
+
break
|
|
575
|
+
default:
|
|
576
|
+
ext = 'jpg'
|
|
577
|
+
break
|
|
578
|
+
}
|
|
579
|
+
|
|
580
|
+
// 如果为视频
|
|
581
|
+
} else if (props.type === 'video') {
|
|
582
|
+
ext = 'mp4'
|
|
583
|
+
|
|
584
|
+
// 如果为音频
|
|
585
|
+
} else if (props.type === 'audio') {
|
|
586
|
+
ext = 'mp3'
|
|
587
|
+
|
|
588
|
+
// 否则为文件
|
|
589
|
+
} else {
|
|
590
|
+
const arr = $n_split(props.type, '/')
|
|
591
|
+
if (arr.length > 0) {
|
|
592
|
+
ext = arr[1]
|
|
593
|
+
}
|
|
594
|
+
}
|
|
595
|
+
|
|
596
|
+
// 如果有后缀名
|
|
597
|
+
if (ext) {
|
|
598
|
+
// 设置文件
|
|
599
|
+
fileItem.file = new File([blob], String($n_timestamp()), { type: blob.type })
|
|
600
|
+
// 设置后缀名
|
|
601
|
+
fileItem.ext = ext
|
|
602
|
+
|
|
603
|
+
const {
|
|
604
|
+
title,
|
|
605
|
+
size,
|
|
606
|
+
} = fileItem.file
|
|
607
|
+
|
|
608
|
+
// 文件大小
|
|
609
|
+
fileItem.size = size
|
|
610
|
+
|
|
611
|
+
// 检查文件错误
|
|
612
|
+
const errMsg = checkFileError(fileItem)
|
|
613
|
+
if (errMsg) {
|
|
614
|
+
// 设置文件上传失败
|
|
615
|
+
setFileFail(fileItem, errMsg)
|
|
616
|
+
return
|
|
617
|
+
}
|
|
618
|
+
|
|
619
|
+
// 初始化 SparkMD5
|
|
620
|
+
const spark = new SparkMD5.ArrayBuffer()
|
|
621
|
+
spark.append(arrayBuffer)
|
|
622
|
+
|
|
623
|
+
// 获取文件 hash
|
|
624
|
+
const hash = spark.end(false)
|
|
625
|
+
if (hash) {
|
|
626
|
+
// 设置文件 hash
|
|
627
|
+
fileItem.hash = await formatFileItemHash(hash)
|
|
628
|
+
// 标题
|
|
629
|
+
fileItem.title = title
|
|
630
|
+
// 设置文件状态
|
|
631
|
+
fileItem.status = UPLOAD_STATUS.hashChecked
|
|
632
|
+
// 设置文件检查进度
|
|
633
|
+
fileItem.progress = 100
|
|
634
|
+
|
|
635
|
+
// 设置单个文件信息
|
|
636
|
+
await setFileItemInfo(fileItem, setFileFail)
|
|
637
|
+
return
|
|
638
|
+
}
|
|
639
|
+
}
|
|
640
|
+
}
|
|
641
|
+
|
|
642
|
+
} catch (e) {}
|
|
643
|
+
|
|
644
|
+
// 设置文件上传失败
|
|
645
|
+
setFileFail(fileItem)
|
|
646
|
+
}
|
|
647
|
+
|
|
648
|
+
/**
|
|
649
|
+
* 检查是否正在上传文件
|
|
650
|
+
*/
|
|
651
|
+
function checkUploading() {
|
|
652
|
+
for (const fileItem of uploadFileLists.value) {
|
|
653
|
+
if (fileItem.status < UPLOAD_STATUS.success) {
|
|
654
|
+
return true
|
|
655
|
+
}
|
|
656
|
+
}
|
|
657
|
+
return false
|
|
658
|
+
}
|
|
659
|
+
|
|
660
|
+
/**
|
|
661
|
+
* 选择文件上传
|
|
662
|
+
*/
|
|
663
|
+
function chooseUpload() {
|
|
664
|
+
// 点击文件输入框
|
|
665
|
+
fileRef.value.click()
|
|
666
|
+
}
|
|
667
|
+
|
|
668
|
+
/**
|
|
669
|
+
* 选择网络外链上传
|
|
670
|
+
*/
|
|
671
|
+
function chooseUploadNet() {
|
|
672
|
+
$q.dialog({
|
|
673
|
+
title: `添加网络${FilE_NAME[FilE_TYPE[props.type]]}`,
|
|
674
|
+
style: 'min-width:600px;',
|
|
675
|
+
// message: `添加网络${FilE_NAME[FilE_TYPE[props.type]]}`,
|
|
676
|
+
prompt: {
|
|
677
|
+
model: '',
|
|
678
|
+
isValid: $n_$ruleValid('required'),
|
|
679
|
+
type: 'textarea',
|
|
680
|
+
placeholder: '多个链接,使用中英文逗号、空格、换行隔开',
|
|
681
|
+
outlined: true,
|
|
682
|
+
},
|
|
683
|
+
cancel: true,
|
|
684
|
+
persistent: true,
|
|
685
|
+
})
|
|
686
|
+
.onOk(async function(value) {
|
|
687
|
+
|
|
688
|
+
let files = $n_isValidString(value)
|
|
689
|
+
? $n_uniq($n_split(value.replace(/\n|,|,/g, ','), ','))
|
|
690
|
+
.map(e => $n_trimString(e))
|
|
691
|
+
.filter(
|
|
692
|
+
val => val && /^(http(s)?:\/\/)/i.test(val)
|
|
693
|
+
)
|
|
694
|
+
: []
|
|
695
|
+
|
|
696
|
+
if (! files.length) {
|
|
697
|
+
// 轻提示
|
|
698
|
+
$n_toast({
|
|
699
|
+
message: '请输入正确的链接'
|
|
700
|
+
})
|
|
701
|
+
return
|
|
702
|
+
}
|
|
703
|
+
|
|
704
|
+
// 格式化上传网络链接
|
|
705
|
+
// --------------------------------------------------
|
|
706
|
+
const {
|
|
707
|
+
formatUploadNet,
|
|
708
|
+
} = configs.uploader
|
|
709
|
+
if ($n_isFunction(formatUploadNet)) {
|
|
710
|
+
files = formatUploadNet(files, props.type === 'image')
|
|
711
|
+
}
|
|
712
|
+
// --------------------------------------------------
|
|
713
|
+
|
|
714
|
+
if (! $n_isValidArray(files)) {
|
|
715
|
+
// 轻提示
|
|
716
|
+
$n_toast({
|
|
717
|
+
message: '请输入正确的链接'
|
|
718
|
+
})
|
|
719
|
+
return
|
|
720
|
+
}
|
|
721
|
+
|
|
722
|
+
// 添加上传网络外链至文件列表
|
|
723
|
+
function addFileNetItem(hash) {
|
|
724
|
+
uploadFileLists.value.push(Object.assign(createRawFileItem(), {
|
|
725
|
+
// id
|
|
726
|
+
id: ++_fileNum,
|
|
727
|
+
// 文件唯一 key
|
|
728
|
+
key: hash,
|
|
729
|
+
// hash
|
|
730
|
+
hash,
|
|
731
|
+
// 将文件状态修改为: 等待上传中
|
|
732
|
+
status: UPLOAD_STATUS.waiting,
|
|
733
|
+
// 进度
|
|
734
|
+
progress: 100,
|
|
735
|
+
// 信息
|
|
736
|
+
msg: '',
|
|
737
|
+
|
|
738
|
+
__img: hash,
|
|
739
|
+
isNet: true,
|
|
740
|
+
isNetUploaded: false,
|
|
741
|
+
}))
|
|
742
|
+
}
|
|
743
|
+
|
|
744
|
+
// 遍历选择的文件列表
|
|
745
|
+
for (const file of files) {
|
|
746
|
+
|
|
747
|
+
// 如果只能上传一个
|
|
748
|
+
if (props.count === 1) {
|
|
749
|
+
|
|
750
|
+
// 如果有上传文件列表
|
|
751
|
+
if (uploadFileLists.value.length) {
|
|
752
|
+
|
|
753
|
+
// 如果开启单文件上传提示
|
|
754
|
+
if (props.confirm) {
|
|
755
|
+
|
|
756
|
+
// 确认框
|
|
757
|
+
$n_confirm({
|
|
758
|
+
message: '最多只能上传1个文件,确认上传并替换吗?',
|
|
759
|
+
})
|
|
760
|
+
// 点击确认执行
|
|
761
|
+
.onOk(function () {
|
|
762
|
+
|
|
763
|
+
// 删除所有文件
|
|
764
|
+
deleteAll()
|
|
765
|
+
|
|
766
|
+
// 添加上传网络外链至文件列表
|
|
767
|
+
addFileNetItem(file)
|
|
768
|
+
|
|
769
|
+
// 上传网络外链文件
|
|
770
|
+
uploadNet(true)
|
|
771
|
+
.finally()
|
|
772
|
+
})
|
|
773
|
+
return
|
|
774
|
+
}
|
|
775
|
+
|
|
776
|
+
// 删除所有文件
|
|
777
|
+
deleteAll()
|
|
778
|
+
}
|
|
779
|
+
|
|
780
|
+
} else if (
|
|
781
|
+
// 如果有上传数量限制
|
|
782
|
+
props.count > 1
|
|
783
|
+
// 上传文件列表数量 === 上传数量限制
|
|
784
|
+
&& uploadFileLists.value.length >= props.count
|
|
785
|
+
) {
|
|
786
|
+
// 轻提示
|
|
787
|
+
$n_toast({
|
|
788
|
+
message: `最多只能上传${props.count}个文件,请先删除后再上传`,
|
|
789
|
+
})
|
|
790
|
+
return
|
|
791
|
+
}
|
|
792
|
+
|
|
793
|
+
// 添加上传网络外链至文件列表
|
|
794
|
+
addFileNetItem(file)
|
|
795
|
+
}
|
|
796
|
+
|
|
797
|
+
// 上传网络外链文件
|
|
798
|
+
uploadNet(true)
|
|
799
|
+
.finally()
|
|
800
|
+
})
|
|
801
|
+
}
|
|
802
|
+
|
|
803
|
+
/**
|
|
804
|
+
* 文件输入框更新
|
|
805
|
+
*/
|
|
806
|
+
async function fileChange(e) {
|
|
807
|
+
|
|
808
|
+
try {
|
|
809
|
+
// 获取上传文件
|
|
810
|
+
const files = Array.from(e.target.files)
|
|
811
|
+
|
|
812
|
+
// 清空上传文件输入框内容
|
|
813
|
+
fileRef.value.value = ''
|
|
814
|
+
|
|
815
|
+
// 如果没有选择文件
|
|
816
|
+
if (! files.length) {
|
|
817
|
+
// 则无任何操作
|
|
818
|
+
return
|
|
819
|
+
}
|
|
820
|
+
|
|
821
|
+
// 遍历选择的文件列表
|
|
822
|
+
for (const file of files) {
|
|
823
|
+
|
|
824
|
+
// 创建单个文件
|
|
825
|
+
const fileItem = await createFileItem(file)
|
|
826
|
+
if (fileItem !== false) {
|
|
827
|
+
|
|
828
|
+
// 如果只能上传一个
|
|
829
|
+
if (props.count === 1) {
|
|
830
|
+
|
|
831
|
+
// 如果有上传文件列表
|
|
832
|
+
if (uploadFileLists.value.length) {
|
|
833
|
+
|
|
834
|
+
// 如果开启单文件上传提示
|
|
835
|
+
if (props.confirm) {
|
|
836
|
+
|
|
837
|
+
// 确认框
|
|
838
|
+
$n_confirm({
|
|
839
|
+
message: '最多只能上传1个文件,确认上传并替换吗?',
|
|
840
|
+
})
|
|
841
|
+
// 点击确认执行
|
|
842
|
+
.onOk(function () {
|
|
843
|
+
|
|
844
|
+
// 删除所有文件
|
|
845
|
+
deleteAll()
|
|
846
|
+
|
|
847
|
+
// 添加至上传文件列表
|
|
848
|
+
uploadFileLists.value.push(fileItem)
|
|
849
|
+
|
|
850
|
+
// 开始上传
|
|
851
|
+
startUpload()
|
|
852
|
+
.finally()
|
|
853
|
+
})
|
|
854
|
+
return
|
|
855
|
+
}
|
|
856
|
+
|
|
857
|
+
// 删除所有文件
|
|
858
|
+
deleteAll()
|
|
859
|
+
}
|
|
860
|
+
|
|
861
|
+
} else if (
|
|
862
|
+
// 如果有上传数量限制
|
|
863
|
+
props.count > 1
|
|
864
|
+
// 上传文件列表数量 === 上传数量限制
|
|
865
|
+
&& uploadFileLists.value.length >= props.count
|
|
866
|
+
) {
|
|
867
|
+
// 轻提示
|
|
868
|
+
$n_toast({
|
|
869
|
+
message: `最多只能上传${props.count}个文件,请先删除后再上传`,
|
|
870
|
+
})
|
|
871
|
+
return
|
|
872
|
+
}
|
|
873
|
+
|
|
874
|
+
// 添加至上传文件列表
|
|
875
|
+
uploadFileLists.value.push(fileItem)
|
|
876
|
+
}
|
|
877
|
+
}
|
|
878
|
+
|
|
879
|
+
// 开始上传
|
|
880
|
+
startUpload()
|
|
881
|
+
.finally()
|
|
882
|
+
|
|
883
|
+
} catch (e) {
|
|
884
|
+
|
|
885
|
+
// 错误提示
|
|
886
|
+
$n_alert({
|
|
887
|
+
message: $n_getThrowMessage(e),
|
|
888
|
+
})
|
|
889
|
+
}
|
|
890
|
+
|
|
891
|
+
}
|
|
892
|
+
|
|
893
|
+
/**
|
|
894
|
+
* 开始上传
|
|
895
|
+
*/
|
|
896
|
+
async function startUpload() {
|
|
897
|
+
|
|
898
|
+
// 【设置待上传文件的 hash】
|
|
899
|
+
// --------------------------------------------------
|
|
900
|
+
const promises = []
|
|
901
|
+
for (const fileItem of uploadFileLists.value) {
|
|
902
|
+
// 如果是等待上传的文件
|
|
903
|
+
if (fileItem.status === UPLOAD_STATUS.waiting) {
|
|
904
|
+
|
|
905
|
+
// 检查文件错误
|
|
906
|
+
const errMsg = checkFileError(fileItem)
|
|
907
|
+
if (errMsg) {
|
|
908
|
+
// 设置文件上传失败
|
|
909
|
+
setFileFail(fileItem, errMsg)
|
|
910
|
+
|
|
911
|
+
} else {
|
|
912
|
+
// 设置文件 hash
|
|
913
|
+
promises.push(setFileHash(fileItem))
|
|
914
|
+
}
|
|
915
|
+
}
|
|
916
|
+
}
|
|
917
|
+
|
|
918
|
+
if (promises.length) {
|
|
919
|
+
await Promise.all(promises)
|
|
920
|
+
}
|
|
921
|
+
|
|
922
|
+
// 检查待上传文件在服务器上是否存在
|
|
923
|
+
// --------------------------------------------------
|
|
924
|
+
if (! await checkWaitUploadFileExists()) {
|
|
925
|
+
return
|
|
926
|
+
}
|
|
927
|
+
|
|
928
|
+
// 上传
|
|
929
|
+
await upload()
|
|
930
|
+
}
|
|
931
|
+
|
|
932
|
+
/**
|
|
933
|
+
* 上传
|
|
934
|
+
*/
|
|
935
|
+
async function upload() {
|
|
936
|
+
try {
|
|
937
|
+
// 待上传文件列表
|
|
938
|
+
const waitUploadFileLists = []
|
|
939
|
+
for (const fileItem of uploadFileLists.value) {
|
|
940
|
+
// 检查存在服务器完成
|
|
941
|
+
if (fileItem.status === UPLOAD_STATUS.existChecked) {
|
|
942
|
+
waitUploadFileLists.push(fileItem)
|
|
943
|
+
}
|
|
944
|
+
}
|
|
945
|
+
if (! waitUploadFileLists.length) {
|
|
946
|
+
return
|
|
947
|
+
}
|
|
948
|
+
|
|
949
|
+
// 上传至服务器
|
|
950
|
+
await uploadServer({
|
|
951
|
+
fileType: FilE_TYPE[props.type],
|
|
952
|
+
waitUploadFileLists,
|
|
953
|
+
setFileSuccess,
|
|
954
|
+
setFileFail,
|
|
955
|
+
})
|
|
956
|
+
|
|
957
|
+
} catch (e) {
|
|
958
|
+
// 错误提示
|
|
959
|
+
$n_alert({
|
|
960
|
+
message: $n_getThrowMessage(e),
|
|
961
|
+
})
|
|
962
|
+
}
|
|
963
|
+
}
|
|
964
|
+
|
|
965
|
+
/**
|
|
966
|
+
* 设置文件上传成功
|
|
967
|
+
*/
|
|
968
|
+
const setFileSuccess = (fileItem) => {
|
|
969
|
+
|
|
970
|
+
// 设置文件状态
|
|
971
|
+
fileItem.status = UPLOAD_STATUS.success
|
|
972
|
+
// 设置文件信息
|
|
973
|
+
fileItem.msg = ''
|
|
974
|
+
// 设置文件检查进度
|
|
975
|
+
fileItem.progress = 0
|
|
976
|
+
// 是否网络文件已上传
|
|
977
|
+
if (fileItem.isNet) {
|
|
978
|
+
fileItem.isNetUploaded = true
|
|
979
|
+
}
|
|
980
|
+
|
|
981
|
+
// // 单个文件上传结束回调
|
|
982
|
+
// uploadQueryCallback(fileItem)
|
|
983
|
+
//
|
|
984
|
+
// // 上传更新回调
|
|
985
|
+
// uploadChange(getResultData())
|
|
986
|
+
|
|
987
|
+
// 更新值
|
|
988
|
+
updateValue()
|
|
989
|
+
}
|
|
990
|
+
|
|
991
|
+
/**
|
|
992
|
+
* 设置文件上传失败
|
|
993
|
+
*/
|
|
994
|
+
function setFileFail(fileItem, errMsg) {
|
|
995
|
+
|
|
996
|
+
// 设置文件状态
|
|
997
|
+
fileItem.status = UPLOAD_STATUS.fail
|
|
998
|
+
// 设置文件信息
|
|
999
|
+
fileItem.msg = errMsg || `无效${FilE_NAME[fileItem.type]}`
|
|
1000
|
+
// 设置文件检查进度
|
|
1001
|
+
fileItem.progress = 0
|
|
1002
|
+
|
|
1003
|
+
// // 单个文件上传结束回调
|
|
1004
|
+
// uploadQueryCallback(fileItem)
|
|
1005
|
+
|
|
1006
|
+
// 上传网络外链回调
|
|
1007
|
+
$n_run(uploadNetCallback)()
|
|
1008
|
+
}
|
|
1009
|
+
|
|
1010
|
+
/**
|
|
1011
|
+
* 设置文件 hash
|
|
1012
|
+
*/
|
|
1013
|
+
function setFileHash(fileItem) {
|
|
1014
|
+
return new Promise(function (resolve) {
|
|
1015
|
+
|
|
1016
|
+
// 设置文件状态
|
|
1017
|
+
fileItem.status = UPLOAD_STATUS.hashChecking
|
|
1018
|
+
// 设置文件检查进度
|
|
1019
|
+
fileItem.progress = 0
|
|
1020
|
+
|
|
1021
|
+
const {
|
|
1022
|
+
file,
|
|
1023
|
+
} = fileItem
|
|
1024
|
+
|
|
1025
|
+
// blob 切片
|
|
1026
|
+
const blobSlice = File.prototype.slice || File.prototype.mozSlice || File.prototype.webkitSlice
|
|
1027
|
+
// 以 2MB 的分片读取
|
|
1028
|
+
const chunkSize = 2097152
|
|
1029
|
+
// 总分片数量
|
|
1030
|
+
const chunks = Math.ceil(file.size / chunkSize)
|
|
1031
|
+
// 初始化 SparkMD5
|
|
1032
|
+
const spark = new SparkMD5.ArrayBuffer()
|
|
1033
|
+
// 当前分片数
|
|
1034
|
+
let currentChunk = 0
|
|
1035
|
+
// 创建文件读取器
|
|
1036
|
+
const fileReader = new FileReader()
|
|
1037
|
+
|
|
1038
|
+
// 文件加载
|
|
1039
|
+
fileReader.onload = async function(e) {
|
|
1040
|
+
|
|
1041
|
+
// 追加 array buffer
|
|
1042
|
+
spark.append(e.target.result)
|
|
1043
|
+
|
|
1044
|
+
// 当前分片数++
|
|
1045
|
+
currentChunk++
|
|
1046
|
+
|
|
1047
|
+
// 如果分块数量 < 总分片数量
|
|
1048
|
+
if (currentChunk < chunks) {
|
|
1049
|
+
|
|
1050
|
+
// 设置文件检查进度
|
|
1051
|
+
fileItem.progress = Math.floor((currentChunk / chunks) * 100)
|
|
1052
|
+
|
|
1053
|
+
// 读取下一个分片
|
|
1054
|
+
loadNext()
|
|
1055
|
+
|
|
1056
|
+
} else {
|
|
1057
|
+
|
|
1058
|
+
// 获取文件 hash
|
|
1059
|
+
const hash = spark.end(false)
|
|
1060
|
+
if (! hash) {
|
|
1061
|
+
// 设置文件上传失败
|
|
1062
|
+
setFileFail(fileItem)
|
|
1063
|
+
// 完成回调
|
|
1064
|
+
resolve()
|
|
1065
|
+
return
|
|
1066
|
+
}
|
|
1067
|
+
|
|
1068
|
+
if (
|
|
1069
|
+
// 如果开启去重
|
|
1070
|
+
props.unique
|
|
1071
|
+
// 如果该文件 hash 在上传文件列表中
|
|
1072
|
+
&& $n_findIndex(uploadFileLists.value, { hash }) > -1
|
|
1073
|
+
) {
|
|
1074
|
+
// 轻提示
|
|
1075
|
+
$n_toast({
|
|
1076
|
+
message: '该文件已存在,不可重复上传',
|
|
1077
|
+
})
|
|
1078
|
+
|
|
1079
|
+
// 设置文件上传失败
|
|
1080
|
+
setFileFail(fileItem, '已存在')
|
|
1081
|
+
|
|
1082
|
+
// 删除单个文件
|
|
1083
|
+
deleteFileItem(fileItem)
|
|
1084
|
+
|
|
1085
|
+
} else {
|
|
1086
|
+
// 设置文件 hash
|
|
1087
|
+
fileItem.hash = await formatFileItemHash(hash)
|
|
1088
|
+
// 设置文件状态
|
|
1089
|
+
fileItem.status = UPLOAD_STATUS.hashChecked
|
|
1090
|
+
// 设置文件检查进度
|
|
1091
|
+
fileItem.progress = 100
|
|
1092
|
+
}
|
|
1093
|
+
|
|
1094
|
+
// 设置单个文件信息
|
|
1095
|
+
await setFileItemInfo(fileItem, setFileFail)
|
|
1096
|
+
|
|
1097
|
+
resolve()
|
|
1098
|
+
}
|
|
1099
|
+
}
|
|
1100
|
+
|
|
1101
|
+
// 文件加载错误
|
|
1102
|
+
fileReader.onerror = function() {
|
|
1103
|
+
// 设置文件上传失败
|
|
1104
|
+
setFileFail(fileItem)
|
|
1105
|
+
// 完成回调
|
|
1106
|
+
resolve()
|
|
1107
|
+
}
|
|
1108
|
+
|
|
1109
|
+
/**
|
|
1110
|
+
* 读取下一个分片
|
|
1111
|
+
*/
|
|
1112
|
+
function loadNext() {
|
|
1113
|
+
const start = currentChunk * chunkSize
|
|
1114
|
+
const end = ((start + chunkSize) >= file.size) ? file.size : start + chunkSize
|
|
1115
|
+
fileReader.readAsArrayBuffer(blobSlice.call(file, start, end))
|
|
1116
|
+
}
|
|
1117
|
+
|
|
1118
|
+
// 读取下一个分片
|
|
1119
|
+
loadNext()
|
|
1120
|
+
})
|
|
1121
|
+
}
|
|
1122
|
+
|
|
1123
|
+
/**
|
|
1124
|
+
* 检查文件错误
|
|
1125
|
+
*/
|
|
1126
|
+
function checkFileError({ type, size, ext }) {
|
|
1127
|
+
|
|
1128
|
+
// 需要上传的文件类型
|
|
1129
|
+
const fileType = FilE_TYPE[props.type]
|
|
1130
|
+
|
|
1131
|
+
// 如果 类型为文件, 则不受限制 || 当前上传的类型允许
|
|
1132
|
+
if (props.type === 'file' || type === fileType) {
|
|
1133
|
+
|
|
1134
|
+
const {
|
|
1135
|
+
maxSize,
|
|
1136
|
+
exts,
|
|
1137
|
+
} = configLimit
|
|
1138
|
+
|
|
1139
|
+
// 检查文件后缀名
|
|
1140
|
+
if (
|
|
1141
|
+
// 如果没有后缀名, 则无效
|
|
1142
|
+
! ext
|
|
1143
|
+
// 如果后缀名不在允许范围内, 则无效
|
|
1144
|
+
|| (
|
|
1145
|
+
$n_isValidArray(exts)
|
|
1146
|
+
&& exts.indexOf(ext) === -1
|
|
1147
|
+
)
|
|
1148
|
+
) {
|
|
1149
|
+
return '后缀名不允许'
|
|
1150
|
+
}
|
|
1151
|
+
|
|
1152
|
+
// 检查文件大小
|
|
1153
|
+
if (size > 0) {
|
|
1154
|
+
if (
|
|
1155
|
+
maxSize > 0
|
|
1156
|
+
&& size > (maxSize * 1048576)
|
|
1157
|
+
) {
|
|
1158
|
+
return `${FilE_NAME[fileType]}大于${maxSize}M`
|
|
1159
|
+
}
|
|
1160
|
+
return
|
|
1161
|
+
}
|
|
1162
|
+
}
|
|
1163
|
+
|
|
1164
|
+
return `无效${FilE_NAME[fileType]}`
|
|
1165
|
+
}
|
|
1166
|
+
|
|
1167
|
+
/**
|
|
1168
|
+
* 检查待上传文件在服务器上是否存在
|
|
1169
|
+
*/
|
|
1170
|
+
async function checkWaitUploadFileExists() {
|
|
1171
|
+
|
|
1172
|
+
// 需检查的 hashs
|
|
1173
|
+
const checkHashs = []
|
|
1174
|
+
// 需检查的文件列表
|
|
1175
|
+
const checkFileLists = []
|
|
1176
|
+
|
|
1177
|
+
for (const fileItem of uploadFileLists.value) {
|
|
1178
|
+
if (fileItem.status === UPLOAD_STATUS.hashChecked) {
|
|
1179
|
+
// 设置文件状态
|
|
1180
|
+
fileItem.status = UPLOAD_STATUS.existChecking
|
|
1181
|
+
// 添加检查 hash 数组
|
|
1182
|
+
checkHashs.push(fileItem.hash)
|
|
1183
|
+
// 添加检查文件列表
|
|
1184
|
+
checkFileLists.push(fileItem)
|
|
1185
|
+
}
|
|
1186
|
+
}
|
|
1187
|
+
|
|
1188
|
+
// 如果没有需要检查的 hash
|
|
1189
|
+
if (! checkHashs.length) {
|
|
1190
|
+
// 返回失败
|
|
1191
|
+
return false
|
|
1192
|
+
}
|
|
1193
|
+
|
|
1194
|
+
// 请求 - 检查文件是否存在 hash
|
|
1195
|
+
const { status, data: resExisted } = await $n_http({
|
|
1196
|
+
url: $n_config('apiFileUrl') + 'check_exist',
|
|
1197
|
+
data: {
|
|
1198
|
+
hashs: $n_uniq(checkHashs),
|
|
1199
|
+
},
|
|
1200
|
+
// 关闭错误
|
|
1201
|
+
warn: false,
|
|
1202
|
+
// 关闭防抖(可以重复请求)
|
|
1203
|
+
debounce: false,
|
|
1204
|
+
})
|
|
1205
|
+
|
|
1206
|
+
// 如果请求失败
|
|
1207
|
+
if (! status) {
|
|
1208
|
+
// 设置文件上传失败
|
|
1209
|
+
for (const fileItem of checkFileLists) {
|
|
1210
|
+
setFileFail(fileItem, resExisted.msg)
|
|
1211
|
+
}
|
|
1212
|
+
// 返回失败
|
|
1213
|
+
return false
|
|
1214
|
+
}
|
|
1215
|
+
|
|
1216
|
+
// 设置文件状态
|
|
1217
|
+
for (const fileItem of checkFileLists) {
|
|
1218
|
+
fileItem.status = UPLOAD_STATUS.existChecked
|
|
1219
|
+
}
|
|
1220
|
+
|
|
1221
|
+
// 如果有存在的文件列表
|
|
1222
|
+
if ($n_isValidArray(resExisted)) {
|
|
1223
|
+
|
|
1224
|
+
// 已存在文件数量
|
|
1225
|
+
let existedNum = 0
|
|
1226
|
+
// 不存在文件数量
|
|
1227
|
+
let noExistNum = 0
|
|
1228
|
+
|
|
1229
|
+
for (const fileItem of checkFileLists) {
|
|
1230
|
+
|
|
1231
|
+
// 如果文件已存在(已经上传过了, 就是检查是否秒传文件)
|
|
1232
|
+
const existedItem = $n_find(resExisted, { hash: fileItem.hash })
|
|
1233
|
+
if (existedItem) {
|
|
1234
|
+
|
|
1235
|
+
// 如果 类型为文件, 则不受限制 || 文件类型正确
|
|
1236
|
+
if (props.type === 'file' || fileItem.type === $n_get(existedItem, 'type')) {
|
|
1237
|
+
|
|
1238
|
+
// 设置已存在文件
|
|
1239
|
+
setExistedFileItem(fileItem, existedItem)
|
|
1240
|
+
|
|
1241
|
+
// 设置文件为非网络外链
|
|
1242
|
+
fileItem.isNet = false
|
|
1243
|
+
|
|
1244
|
+
// 单个文件上传结束回调
|
|
1245
|
+
// uploadQueryCallback(fileItem)
|
|
1246
|
+
|
|
1247
|
+
// 已存在文件数量++
|
|
1248
|
+
existedNum++
|
|
1249
|
+
|
|
1250
|
+
// 否则有不存在文件
|
|
1251
|
+
} else {
|
|
1252
|
+
// 不存在文件数量++
|
|
1253
|
+
noExistNum++
|
|
1254
|
+
}
|
|
1255
|
+
|
|
1256
|
+
// 否则有不存在文件
|
|
1257
|
+
} else {
|
|
1258
|
+
// 不存在文件数量++
|
|
1259
|
+
noExistNum++
|
|
1260
|
+
}
|
|
1261
|
+
}
|
|
1262
|
+
|
|
1263
|
+
// 如果有已存在的文件
|
|
1264
|
+
if (existedNum) {
|
|
1265
|
+
|
|
1266
|
+
// 更新值
|
|
1267
|
+
updateValue()
|
|
1268
|
+
|
|
1269
|
+
// 上传更新回调
|
|
1270
|
+
// uploadChange(getResultData())
|
|
1271
|
+
}
|
|
1272
|
+
|
|
1273
|
+
return noExistNum > 0
|
|
1274
|
+
}
|
|
1275
|
+
|
|
1276
|
+
return true
|
|
1277
|
+
}
|
|
1278
|
+
|
|
1279
|
+
/**
|
|
1280
|
+
* 创建原始单个文件
|
|
1281
|
+
*/
|
|
1282
|
+
function createRawFileItem() {
|
|
1283
|
+
return {
|
|
1284
|
+
// id
|
|
1285
|
+
id: ++_fileNum,
|
|
1286
|
+
// 文件唯一 key
|
|
1287
|
+
// key,
|
|
1288
|
+
// 文件名
|
|
1289
|
+
title: '',
|
|
1290
|
+
// 文件原始数据
|
|
1291
|
+
// file,
|
|
1292
|
+
// 类型(1:文件,2:图片,3:视频,4:音频)
|
|
1293
|
+
type: FilE_TYPE[props.type],
|
|
1294
|
+
// 后缀
|
|
1295
|
+
ext: '',
|
|
1296
|
+
// 大小
|
|
1297
|
+
size: 0,
|
|
1298
|
+
// hash
|
|
1299
|
+
hash: '',
|
|
1300
|
+
// 信息
|
|
1301
|
+
json: {},
|
|
1302
|
+
// 状态: waiting checking checked uploading success fail
|
|
1303
|
+
status: UPLOAD_STATUS.waiting,
|
|
1304
|
+
// 进度
|
|
1305
|
+
progress: 0,
|
|
1306
|
+
// 信息
|
|
1307
|
+
msg: '待上传',
|
|
1308
|
+
// 是否为网络文件
|
|
1309
|
+
isNet: false,
|
|
1310
|
+
// 是否网络文件已上传
|
|
1311
|
+
isNetUploaded: false,
|
|
1312
|
+
// 中断上传
|
|
1313
|
+
abort() {},
|
|
1314
|
+
}
|
|
1315
|
+
}
|
|
1316
|
+
|
|
1317
|
+
/**
|
|
1318
|
+
* 创建单个文件
|
|
1319
|
+
*/
|
|
1320
|
+
async function createFileItem(file) {
|
|
1321
|
+
|
|
1322
|
+
// 单个文件示例
|
|
1323
|
+
// name: "123.jpg"
|
|
1324
|
+
// size: 101206
|
|
1325
|
+
// type: "image/jpeg"
|
|
1326
|
+
// lastModified: 1629099994411
|
|
1327
|
+
// lastModifiedDate: Mon Aug 16 2021 15:46:34 GMT+0800 (中国标准时间) {}
|
|
1328
|
+
// webkitRelativePath: ""
|
|
1329
|
+
|
|
1330
|
+
const {
|
|
1331
|
+
name,
|
|
1332
|
+
size,
|
|
1333
|
+
lastModified,
|
|
1334
|
+
webkitRelativePath,
|
|
1335
|
+
} = file
|
|
1336
|
+
|
|
1337
|
+
// 文件唯一 key
|
|
1338
|
+
const key = webkitRelativePath + lastModified + name + size
|
|
1339
|
+
|
|
1340
|
+
if (
|
|
1341
|
+
// 如果开启去重
|
|
1342
|
+
props.unique
|
|
1343
|
+
// 如果该文件 key 在上传文件列表中
|
|
1344
|
+
&& $n_findIndex(uploadFileLists.value, { key }) > -1
|
|
1345
|
+
) {
|
|
1346
|
+
// 轻提示
|
|
1347
|
+
$n_toast({
|
|
1348
|
+
message: '该文件已存在,不可重复上传',
|
|
1349
|
+
})
|
|
1350
|
+
|
|
1351
|
+
// 则返回 false
|
|
1352
|
+
return false
|
|
1353
|
+
}
|
|
1354
|
+
|
|
1355
|
+
// 文件名
|
|
1356
|
+
let title = name
|
|
1357
|
+
// 文件后缀名
|
|
1358
|
+
let ext = ''
|
|
1359
|
+
|
|
1360
|
+
const index = name.lastIndexOf('.')
|
|
1361
|
+
if (index > -1) {
|
|
1362
|
+
title = name.substring(0, index)
|
|
1363
|
+
ext = $n_toLower(name.substring(index + 1))
|
|
1364
|
+
}
|
|
1365
|
+
|
|
1366
|
+
// 创建单个文件
|
|
1367
|
+
const fileItem = Object.assign(createRawFileItem(), {
|
|
1368
|
+
// 文件唯一 key
|
|
1369
|
+
key,
|
|
1370
|
+
// 文件原始数据
|
|
1371
|
+
file,
|
|
1372
|
+
// 文件名
|
|
1373
|
+
title,
|
|
1374
|
+
// 后缀
|
|
1375
|
+
ext,
|
|
1376
|
+
// 大小
|
|
1377
|
+
size,
|
|
1378
|
+
})
|
|
1379
|
+
|
|
1380
|
+
// 如果上传类型为图片
|
|
1381
|
+
if (
|
|
1382
|
+
props.type === 'image'
|
|
1383
|
+
&& file.type.toLowerCase().startsWith('image')
|
|
1384
|
+
) {
|
|
1385
|
+
// 设置单个文件信息
|
|
1386
|
+
const res = await setFileItemInfo(fileItem, setFileFail)
|
|
1387
|
+
if (! res) {
|
|
1388
|
+
return false
|
|
1389
|
+
}
|
|
1390
|
+
}
|
|
1391
|
+
|
|
1392
|
+
return fileItem
|
|
1393
|
+
}
|
|
1394
|
+
|
|
1395
|
+
/**
|
|
1396
|
+
* 设置已存在文件
|
|
1397
|
+
*/
|
|
1398
|
+
function setExistedFileItem(fileItem, existedItem) {
|
|
1399
|
+
|
|
1400
|
+
// 返回数据示例
|
|
1401
|
+
// ------------------------------
|
|
1402
|
+
// title: 1
|
|
1403
|
+
// type: 2
|
|
1404
|
+
// hash: "799e6bb77f638e192b9079e6a55bc2de"
|
|
1405
|
+
// ext: "jpg"
|
|
1406
|
+
// size: 306037
|
|
1407
|
+
// json: "{\"w\":1920,\"h\":1200,\"o\":1}"
|
|
1408
|
+
// status: 1
|
|
1409
|
+
|
|
1410
|
+
const {
|
|
1411
|
+
// 标题
|
|
1412
|
+
title,
|
|
1413
|
+
// 类型(1:文件,2:图片,3:视频,4:音频)
|
|
1414
|
+
type,
|
|
1415
|
+
// hash
|
|
1416
|
+
hash,
|
|
1417
|
+
// 后缀
|
|
1418
|
+
ext,
|
|
1419
|
+
// 大小
|
|
1420
|
+
size,
|
|
1421
|
+
// 信息
|
|
1422
|
+
json,
|
|
1423
|
+
} = existedItem
|
|
1424
|
+
|
|
1425
|
+
const fileJson = $n_json.parse(json)
|
|
1426
|
+
|
|
1427
|
+
// 设置文件
|
|
1428
|
+
Object.assign(fileItem, {
|
|
1429
|
+
// 标题
|
|
1430
|
+
title,
|
|
1431
|
+
// 类型(1:文件,2:图片,3:视频,4:音频)
|
|
1432
|
+
type,
|
|
1433
|
+
// hash
|
|
1434
|
+
hash,
|
|
1435
|
+
// 后缀
|
|
1436
|
+
ext,
|
|
1437
|
+
// 大小
|
|
1438
|
+
size,
|
|
1439
|
+
// 信息
|
|
1440
|
+
json: $n_isValidObject(fileJson) ? fileJson : {},
|
|
1441
|
+
// 状态
|
|
1442
|
+
status: UPLOAD_STATUS.success,
|
|
1443
|
+
// 进度
|
|
1444
|
+
progress: 100,
|
|
1445
|
+
// 信息
|
|
1446
|
+
msg: '',
|
|
1447
|
+
})
|
|
1448
|
+
}
|
|
1449
|
+
|
|
1450
|
+
/**
|
|
1451
|
+
* 删除所有文件
|
|
1452
|
+
*/
|
|
1453
|
+
function deleteAll() {
|
|
1454
|
+
|
|
1455
|
+
// 如果有上传文件列表
|
|
1456
|
+
if (uploadFileLists.value.length) {
|
|
1457
|
+
|
|
1458
|
+
for (const fileItem of uploadFileLists.value) {
|
|
1459
|
+
// 退出上传
|
|
1460
|
+
fileItem.abort()
|
|
1461
|
+
}
|
|
1462
|
+
|
|
1463
|
+
// 清空上传文件列表
|
|
1464
|
+
uploadFileLists.value = []
|
|
1465
|
+
}
|
|
1466
|
+
}
|
|
1467
|
+
|
|
1468
|
+
/**
|
|
1469
|
+
* 删除单个文件
|
|
1470
|
+
*/
|
|
1471
|
+
function deleteFileItem(fileItem) {
|
|
1472
|
+
const index = $n_findIndex(uploadFileLists.value, { id: fileItem.id })
|
|
1473
|
+
if (index > -1) {
|
|
1474
|
+
|
|
1475
|
+
const {
|
|
1476
|
+
status,
|
|
1477
|
+
} = fileItem
|
|
1478
|
+
|
|
1479
|
+
// 退出上传
|
|
1480
|
+
fileItem.abort()
|
|
1481
|
+
|
|
1482
|
+
// 从上传文件列表中删除
|
|
1483
|
+
uploadFileLists.value.splice(index, 1)
|
|
1484
|
+
|
|
1485
|
+
// 如果该文件已上传成功
|
|
1486
|
+
if (status === UPLOAD_STATUS.success) {
|
|
1487
|
+
// 更新值
|
|
1488
|
+
updateValue()
|
|
1489
|
+
}
|
|
1490
|
+
}
|
|
1491
|
+
}
|
|
1492
|
+
|
|
1493
|
+
/**
|
|
1494
|
+
* 预览图片
|
|
1495
|
+
*/
|
|
1496
|
+
function previewImage(fileItem) {
|
|
1497
|
+
// 预览图片
|
|
1498
|
+
if (fileItem.type === FilE_TYPE.image) {
|
|
1499
|
+
$n_previewImage({
|
|
1500
|
+
images: [
|
|
1501
|
+
$n_has(fileItem, '__img') ? fileItem.__img : fileItem.hash,
|
|
1502
|
+
],
|
|
1503
|
+
})
|
|
1504
|
+
}
|
|
1505
|
+
}
|
|
1506
|
+
|
|
1507
|
+
/**
|
|
1508
|
+
* 修改文件名
|
|
1509
|
+
*/
|
|
1510
|
+
async function editFileTitle(newTitle, fileItem) {
|
|
1511
|
+
|
|
1512
|
+
if ($n_isValidValue(newTitle)) {
|
|
1513
|
+
|
|
1514
|
+
newTitle = $n_trimString(newTitle)
|
|
1515
|
+
|
|
1516
|
+
const {
|
|
1517
|
+
hash,
|
|
1518
|
+
title,
|
|
1519
|
+
} = fileItem
|
|
1520
|
+
|
|
1521
|
+
if (title === newTitle) {
|
|
1522
|
+
return
|
|
1523
|
+
}
|
|
1524
|
+
|
|
1525
|
+
// 先设置新文件名
|
|
1526
|
+
fileItem.title = newTitle
|
|
1527
|
+
|
|
1528
|
+
// 请求 - 修改文件名
|
|
1529
|
+
const { status } = await $n_http({
|
|
1530
|
+
url: $n_config('apiFileUrl') + 'edit_file_title',
|
|
1531
|
+
data: {
|
|
1532
|
+
hash,
|
|
1533
|
+
title: newTitle,
|
|
1534
|
+
},
|
|
1535
|
+
})
|
|
1536
|
+
|
|
1537
|
+
// 如果修改失败
|
|
1538
|
+
if (! status) {
|
|
1539
|
+
// 还原文件名
|
|
1540
|
+
fileItem.title = title
|
|
1541
|
+
return
|
|
1542
|
+
}
|
|
1543
|
+
|
|
1544
|
+
// 轻提示
|
|
1545
|
+
$n_toast({
|
|
1546
|
+
type: 'positive',
|
|
1547
|
+
message: '修改成功',
|
|
1548
|
+
})
|
|
1549
|
+
}
|
|
1550
|
+
}
|
|
1551
|
+
|
|
1552
|
+
/**
|
|
1553
|
+
* 复制地址
|
|
1554
|
+
*/
|
|
1555
|
+
function copyUrl({ type, hash }) {
|
|
1556
|
+
|
|
1557
|
+
const _url = type === FilE_TYPE.image ?
|
|
1558
|
+
// 如果是图片
|
|
1559
|
+
$n_getImage(hash)
|
|
1560
|
+
// 否则是文件
|
|
1561
|
+
: $n_getFile(hash)
|
|
1562
|
+
|
|
1563
|
+
if ($n_isValidString(_url)) {
|
|
1564
|
+
copy(_url, '复制地址成功')
|
|
1565
|
+
}
|
|
1566
|
+
}
|
|
1567
|
+
|
|
1568
|
+
/**
|
|
1569
|
+
* ==============================【私有函数】==============================
|
|
1570
|
+
*/
|
|
1571
|
+
|
|
1572
|
+
/**
|
|
1573
|
+
* 格式化单个文件 hash
|
|
1574
|
+
*/
|
|
1575
|
+
async function formatFileItemHash(hash) {
|
|
1576
|
+
|
|
1577
|
+
const {
|
|
1578
|
+
hasMinioSuffix,
|
|
1579
|
+
formatUploadFileHash,
|
|
1580
|
+
} = configs.uploader
|
|
1581
|
+
|
|
1582
|
+
if (hasMinioSuffix && configUpload.type === 'minio') {
|
|
1583
|
+
hash += '_'
|
|
1584
|
+
}
|
|
1585
|
+
|
|
1586
|
+
// 如果有格式化上传文件 hash
|
|
1587
|
+
if ($n_isFunction(formatUploadFileHash)) {
|
|
1588
|
+
hash = await $n_runAsync(formatUploadFileHash)(hash)
|
|
1589
|
+
}
|
|
1590
|
+
|
|
1591
|
+
return hash
|
|
1592
|
+
}
|
|
1593
|
+
|
|
1594
|
+
/**
|
|
1595
|
+
* 获取图片信息
|
|
1596
|
+
*/
|
|
1597
|
+
function getImageInfo(fileItem) {
|
|
1598
|
+
return new Promise(function (resolve) {
|
|
1599
|
+
const img = new Image()
|
|
1600
|
+
img.src = window.URL.createObjectURL(fileItem.file)
|
|
1601
|
+
fileItem.__img = img.src
|
|
1602
|
+
img.onload = function() {
|
|
1603
|
+
fileItem.json = {
|
|
1604
|
+
w: this.naturalWidth,
|
|
1605
|
+
h: this.naturalHeight,
|
|
1606
|
+
}
|
|
1607
|
+
resolve(true)
|
|
1608
|
+
}
|
|
1609
|
+
img.onerror = function() {
|
|
1610
|
+
resolve(false)
|
|
1611
|
+
}
|
|
1612
|
+
})
|
|
1613
|
+
}
|
|
1614
|
+
|
|
1615
|
+
/**
|
|
1616
|
+
* 获取媒体信息
|
|
1617
|
+
*/
|
|
1618
|
+
function getMediaInfo(fileItem, type) {
|
|
1619
|
+
return new Promise(function (resolve) {
|
|
1620
|
+
const dom = document.createElement(type)
|
|
1621
|
+
dom.src = URL.createObjectURL(fileItem.file)
|
|
1622
|
+
dom.onerror = function() {
|
|
1623
|
+
resolve(false)
|
|
1624
|
+
}
|
|
1625
|
+
|
|
1626
|
+
// 如果为视频
|
|
1627
|
+
if (type === 'video') {
|
|
1628
|
+
dom.currentTime = 3
|
|
1629
|
+
dom.oncanplay = async function() {
|
|
1630
|
+
|
|
1631
|
+
fileItem.json = {
|
|
1632
|
+
w: this.videoWidth ? this.videoWidth : this.width,
|
|
1633
|
+
h: this.videoHeight ? this.videoHeight : this.height,
|
|
1634
|
+
d: this.duration,
|
|
1635
|
+
}
|
|
1636
|
+
|
|
1637
|
+
// 获取视频缩略图 hash
|
|
1638
|
+
const p = await getVideoThumbHash(dom, fileItem.json)
|
|
1639
|
+
if (p) {
|
|
1640
|
+
fileItem.json.p = p
|
|
1641
|
+
}
|
|
1642
|
+
|
|
1643
|
+
resolve(true)
|
|
1644
|
+
}
|
|
1645
|
+
|
|
1646
|
+
// 否则为音频
|
|
1647
|
+
} else {
|
|
1648
|
+
dom.onloadedmetadata = function() {
|
|
1649
|
+
fileItem.json = {
|
|
1650
|
+
d: this.duration,
|
|
1651
|
+
}
|
|
1652
|
+
resolve(true)
|
|
1653
|
+
}
|
|
1654
|
+
}
|
|
1655
|
+
})
|
|
1656
|
+
}
|
|
1657
|
+
|
|
1658
|
+
/**
|
|
1659
|
+
* 获取视频缩略图 hash
|
|
1660
|
+
*/
|
|
1661
|
+
function getVideoThumbHash(dom, { w, h }) {
|
|
1662
|
+
return new Promise(async function (resolve) {
|
|
1663
|
+
|
|
1664
|
+
const canvas = document.createElement('canvas')
|
|
1665
|
+
const ctx = canvas.getContext('2d')
|
|
1666
|
+
canvas.setAttribute('width', w)
|
|
1667
|
+
canvas.setAttribute('height', h)
|
|
1668
|
+
ctx.drawImage(dom, 0, 0, w, h)
|
|
1669
|
+
|
|
1670
|
+
const {
|
|
1671
|
+
arrayBuffer,
|
|
1672
|
+
file,
|
|
1673
|
+
} = dataUrlToFile(canvas.toDataURL('image/jpeg', 0.8))
|
|
1674
|
+
|
|
1675
|
+
// 获取缩略图 hash
|
|
1676
|
+
const spark = new SparkMD5.ArrayBuffer()
|
|
1677
|
+
spark.append(arrayBuffer)
|
|
1678
|
+
const hash = spark.end(false)
|
|
1679
|
+
if (! hash) {
|
|
1680
|
+
resolve(false)
|
|
1681
|
+
return
|
|
1682
|
+
}
|
|
1683
|
+
|
|
1684
|
+
// 创建原始单个文件
|
|
1685
|
+
const fileItem = Object.assign(createRawFileItem(), {
|
|
1686
|
+
// 设置文件 file
|
|
1687
|
+
file,
|
|
1688
|
+
// 设置文件类型
|
|
1689
|
+
type: FilE_TYPE.image,
|
|
1690
|
+
// 设置文件后缀
|
|
1691
|
+
ext: 'jpg',
|
|
1692
|
+
// 设置文件大小
|
|
1693
|
+
size: file.size,
|
|
1694
|
+
// 设置文件 hash
|
|
1695
|
+
hash: await formatFileItemHash(hash),
|
|
1696
|
+
// 设置文件 json
|
|
1697
|
+
json: {
|
|
1698
|
+
w,
|
|
1699
|
+
h,
|
|
1700
|
+
},
|
|
1701
|
+
})
|
|
1702
|
+
|
|
1703
|
+
// 上传至服务器
|
|
1704
|
+
await uploadServer({
|
|
1705
|
+
fileType: FilE_TYPE.image,
|
|
1706
|
+
waitUploadFileLists: [ fileItem ],
|
|
1707
|
+
setFileSuccess(fileItem) {
|
|
1708
|
+
resolve(fileItem.hash)
|
|
1709
|
+
},
|
|
1710
|
+
setFileFail() {
|
|
1711
|
+
resolve(false)
|
|
1712
|
+
},
|
|
1713
|
+
})
|
|
1714
|
+
})
|
|
1715
|
+
}
|
|
1716
|
+
|
|
1717
|
+
/**
|
|
1718
|
+
* 设置单个文件信息
|
|
1719
|
+
*/
|
|
1720
|
+
function setFileItemInfo(fileItem, setFileFail) {
|
|
1721
|
+
return new Promise(async function (resolve) {
|
|
1722
|
+
|
|
1723
|
+
// 如果已经设置过了
|
|
1724
|
+
if (
|
|
1725
|
+
$n_has(fileItem.json, 'w')
|
|
1726
|
+
|| $n_has(fileItem.json, 'd')
|
|
1727
|
+
) {
|
|
1728
|
+
resolve(true)
|
|
1729
|
+
return
|
|
1730
|
+
}
|
|
1731
|
+
|
|
1732
|
+
let res
|
|
1733
|
+
|
|
1734
|
+
// 如果为图片
|
|
1735
|
+
// --------------------------------------------------
|
|
1736
|
+
if (fileItem.type === FilE_TYPE.image) {
|
|
1737
|
+
res = await getImageInfo(fileItem)
|
|
1738
|
+
|
|
1739
|
+
// 如果为视频
|
|
1740
|
+
// --------------------------------------------------
|
|
1741
|
+
} else if (fileItem.type === FilE_TYPE.video) {
|
|
1742
|
+
res = await getMediaInfo(fileItem, 'video')
|
|
1743
|
+
|
|
1744
|
+
// 如果为音频
|
|
1745
|
+
// --------------------------------------------------
|
|
1746
|
+
} else if (fileItem.type === FilE_TYPE.audio) {
|
|
1747
|
+
res = await getMediaInfo(fileItem, 'audio')
|
|
1748
|
+
|
|
1749
|
+
// 否则为文件
|
|
1750
|
+
} else {
|
|
1751
|
+
// 先判断是否为图片
|
|
1752
|
+
res = await getImageInfo(fileItem)
|
|
1753
|
+
if (! res) {
|
|
1754
|
+
// 再判断是否为视频
|
|
1755
|
+
res = await getMediaInfo(fileItem, 'video')
|
|
1756
|
+
if (! res) {
|
|
1757
|
+
// 最后再判断是否为音频
|
|
1758
|
+
res = await getMediaInfo(fileItem, 'video')
|
|
1759
|
+
}
|
|
1760
|
+
}
|
|
1761
|
+
}
|
|
1762
|
+
// --------------------------------------------------
|
|
1763
|
+
|
|
1764
|
+
if (res) {
|
|
1765
|
+
resolve(true)
|
|
1766
|
+
} else {
|
|
1767
|
+
// 设置文件上传失败
|
|
1768
|
+
setFileFail(fileItem)
|
|
1769
|
+
resolve(false)
|
|
1770
|
+
}
|
|
1771
|
+
})
|
|
1772
|
+
}
|
|
1773
|
+
|
|
1774
|
+
/**
|
|
1775
|
+
* 获取上传参数
|
|
1776
|
+
*/
|
|
1777
|
+
async function getUploadParams(type, bucket = 'public') {
|
|
1778
|
+
|
|
1779
|
+
// 缓存名
|
|
1780
|
+
const cacheName = `upload_params_${type}_${bucket}`
|
|
1781
|
+
|
|
1782
|
+
// 获取缓存
|
|
1783
|
+
const cache = $n_storage.get(cacheName)
|
|
1784
|
+
if (cache !== null) {
|
|
1785
|
+
return cache
|
|
1786
|
+
}
|
|
1787
|
+
|
|
1788
|
+
// 请求数据
|
|
1789
|
+
const { status, data } = await $n_http({
|
|
1790
|
+
url: $n_config('apiFileUrl') + 'get_upload_params',
|
|
1791
|
+
data: {
|
|
1792
|
+
// 类型
|
|
1793
|
+
type,
|
|
1794
|
+
// 空间名称
|
|
1795
|
+
bucket,
|
|
1796
|
+
},
|
|
1797
|
+
})
|
|
1798
|
+
|
|
1799
|
+
// 如果成功
|
|
1800
|
+
if (! status || ! $n_isValidObject(data)) {
|
|
1801
|
+
return false
|
|
1802
|
+
}
|
|
1803
|
+
|
|
1804
|
+
// 【生产模式】
|
|
1805
|
+
// --------------------------------------------------
|
|
1806
|
+
// #ifdef IS_PRO
|
|
1807
|
+
// 保存缓存(6 小时)
|
|
1808
|
+
$n_storage.set(cacheName, data, 21600000)
|
|
1809
|
+
// #endif
|
|
1810
|
+
// --------------------------------------------------
|
|
1811
|
+
|
|
1812
|
+
return data
|
|
1813
|
+
}
|
|
1814
|
+
|
|
1815
|
+
/**
|
|
1816
|
+
* 删除上传参数缓存
|
|
1817
|
+
*/
|
|
1818
|
+
function deleteUploadParams(type, bucket = 'public') {
|
|
1819
|
+
$n_storage.delete(`upload_params_${type}_${bucket}`)
|
|
1820
|
+
}
|
|
1821
|
+
|
|
1822
|
+
/**
|
|
1823
|
+
* 上传至服务器
|
|
1824
|
+
*/
|
|
1825
|
+
async function uploadServer(options) {
|
|
1826
|
+
|
|
1827
|
+
const {
|
|
1828
|
+
fileType,
|
|
1829
|
+
waitUploadFileLists,
|
|
1830
|
+
setFileSuccess,
|
|
1831
|
+
setFileFail,
|
|
1832
|
+
warn,
|
|
1833
|
+
} = Object.assign({
|
|
1834
|
+
warn: true,
|
|
1835
|
+
}, options)
|
|
1836
|
+
|
|
1837
|
+
// 获取上传参数
|
|
1838
|
+
const uploadParams = await getUploadParams(configUpload.type)
|
|
1839
|
+
if (uploadParams === false) {
|
|
1840
|
+
for (const fileItem of waitUploadFileLists) {
|
|
1841
|
+
setFileFail(fileItem, '上传失败')
|
|
1842
|
+
}
|
|
1843
|
+
if (warn) {
|
|
1844
|
+
$n_toast({
|
|
1845
|
+
message: `获取[${configUpload.type}]上传参数失败`,
|
|
1846
|
+
})
|
|
1847
|
+
}
|
|
1848
|
+
return false
|
|
1849
|
+
}
|
|
1850
|
+
|
|
1851
|
+
// 是否上传 minio 备份
|
|
1852
|
+
// --------------------------------------------------
|
|
1853
|
+
let backupParams = null
|
|
1854
|
+
let backupConfig = null
|
|
1855
|
+
if (configUpload.type !== 'minio') {
|
|
1856
|
+
backupConfig = getUpload(null, 'minio')
|
|
1857
|
+
// 如果同步
|
|
1858
|
+
if (backupConfig.sync === true) {
|
|
1859
|
+
backupParams = await getUploadParams(backupConfig.type)
|
|
1860
|
+
if (backupParams === false) {
|
|
1861
|
+
for (const fileItem of waitUploadFileLists) {
|
|
1862
|
+
setFileFail(fileItem, '上传失败')
|
|
1863
|
+
}
|
|
1864
|
+
if (warn) {
|
|
1865
|
+
$n_toast({
|
|
1866
|
+
message: `获取[${backupConfig.type}]上传参数失败`,
|
|
1867
|
+
})
|
|
1868
|
+
}
|
|
1869
|
+
return false
|
|
1870
|
+
}
|
|
1871
|
+
}
|
|
1872
|
+
}
|
|
1873
|
+
// --------------------------------------------------
|
|
1874
|
+
|
|
1875
|
+
// 批量上传
|
|
1876
|
+
for (const fileItem of waitUploadFileLists) {
|
|
1877
|
+
// 上传单个文件
|
|
1878
|
+
await uploadFileItem(fileItem)
|
|
1879
|
+
}
|
|
1880
|
+
|
|
1881
|
+
/**
|
|
1882
|
+
* 上传单个文件
|
|
1883
|
+
*/
|
|
1884
|
+
async function uploadFileItem(fileItem) {
|
|
1885
|
+
|
|
1886
|
+
// 设置文件状态
|
|
1887
|
+
fileItem.status = UPLOAD_STATUS.uploading
|
|
1888
|
+
|
|
1889
|
+
const {
|
|
1890
|
+
// 上传请求
|
|
1891
|
+
onUploadHttp,
|
|
1892
|
+
} = configs.uploader
|
|
1893
|
+
|
|
1894
|
+
// 上传文件
|
|
1895
|
+
const upload = async function(configUpload, uploadParams, startPercent, halfPercent) {
|
|
1896
|
+
|
|
1897
|
+
const {
|
|
1898
|
+
// 上传地址
|
|
1899
|
+
url,
|
|
1900
|
+
// 文件名
|
|
1901
|
+
fileName,
|
|
1902
|
+
// 键值名
|
|
1903
|
+
keyName,
|
|
1904
|
+
// 上传数据
|
|
1905
|
+
data,
|
|
1906
|
+
} = uploadParams
|
|
1907
|
+
|
|
1908
|
+
// 请求数据
|
|
1909
|
+
const httpData = Object.assign({}, data)
|
|
1910
|
+
// 文件
|
|
1911
|
+
httpData[fileName] = fileItem.file
|
|
1912
|
+
// 自定义文件 key
|
|
1913
|
+
httpData[keyName] = fileItem.hash
|
|
1914
|
+
|
|
1915
|
+
let opts = {
|
|
1916
|
+
// 上传地址
|
|
1917
|
+
url,
|
|
1918
|
+
// 数据
|
|
1919
|
+
data: httpData,
|
|
1920
|
+
// 关闭错误提醒
|
|
1921
|
+
warn: false,
|
|
1922
|
+
// 关闭检查结果 code
|
|
1923
|
+
checkCode: false,
|
|
1924
|
+
// 不包含头部鉴权认证
|
|
1925
|
+
token: false,
|
|
1926
|
+
// 开启上传
|
|
1927
|
+
upload: true,
|
|
1928
|
+
// 取消请求
|
|
1929
|
+
onCancel(cancel) {
|
|
1930
|
+
// 设置中断上传
|
|
1931
|
+
fileItem.abort = function(msg) {
|
|
1932
|
+
cancel($n_isValidString(msg) ? msg : '已取消')
|
|
1933
|
+
}
|
|
1934
|
+
},
|
|
1935
|
+
// 监听上传进度
|
|
1936
|
+
onUploadProgress(percent) {
|
|
1937
|
+
// 设置上传进度
|
|
1938
|
+
fileItem.progress = Math.round(startPercent + (halfPercent ? percent / 2 : percent))
|
|
1939
|
+
},
|
|
1940
|
+
}
|
|
1941
|
+
|
|
1942
|
+
// 上传请求
|
|
1943
|
+
if ($n_isFunction(onUploadHttp)) {
|
|
1944
|
+
const res = onUploadHttp(opts, fileItem)
|
|
1945
|
+
if (res) {
|
|
1946
|
+
opts = res
|
|
1947
|
+
}
|
|
1948
|
+
}
|
|
1949
|
+
|
|
1950
|
+
const { status, data: res } = await $n_http(opts)
|
|
1951
|
+
|
|
1952
|
+
// 如果请求失败
|
|
1953
|
+
if (! status) {
|
|
1954
|
+
// 设置文件上传失败
|
|
1955
|
+
setFileFail(fileItem, res.msg)
|
|
1956
|
+
// 删除上传参数缓存
|
|
1957
|
+
deleteUploadParams(configUpload.type)
|
|
1958
|
+
return false
|
|
1959
|
+
}
|
|
1960
|
+
|
|
1961
|
+
return res
|
|
1962
|
+
}
|
|
1963
|
+
|
|
1964
|
+
const resUpload = await upload(configUpload, uploadParams, 0, !! backupParams)
|
|
1965
|
+
if (resUpload === false) {
|
|
1966
|
+
return false
|
|
1967
|
+
}
|
|
1968
|
+
|
|
1969
|
+
// 是否已备份
|
|
1970
|
+
let is_backup = 0
|
|
1971
|
+
|
|
1972
|
+
// 上传至备份服务器
|
|
1973
|
+
// --------------------------------------------------
|
|
1974
|
+
if (backupParams) {
|
|
1975
|
+
const res = await upload(backupConfig, backupParams, 50, true)
|
|
1976
|
+
if (res === false) {
|
|
1977
|
+
return false
|
|
1978
|
+
}
|
|
1979
|
+
is_backup = 1
|
|
1980
|
+
}
|
|
1981
|
+
// --------------------------------------------------
|
|
1982
|
+
|
|
1983
|
+
const {
|
|
1984
|
+
title,
|
|
1985
|
+
type,
|
|
1986
|
+
hash,
|
|
1987
|
+
ext,
|
|
1988
|
+
size,
|
|
1989
|
+
json,
|
|
1990
|
+
} = fileItem
|
|
1991
|
+
|
|
1992
|
+
// 请求数据
|
|
1993
|
+
const data = {
|
|
1994
|
+
// 类型
|
|
1995
|
+
type: configUpload.type,
|
|
1996
|
+
// 需上传的文件类型
|
|
1997
|
+
file_type: fileType,
|
|
1998
|
+
// 文件
|
|
1999
|
+
file: {
|
|
2000
|
+
// 标题
|
|
2001
|
+
title: title || '',
|
|
2002
|
+
// 类型
|
|
2003
|
+
type,
|
|
2004
|
+
// hash
|
|
2005
|
+
hash,
|
|
2006
|
+
// 后缀
|
|
2007
|
+
ext,
|
|
2008
|
+
// 文件大小
|
|
2009
|
+
size,
|
|
2010
|
+
// 文件 json
|
|
2011
|
+
json,
|
|
2012
|
+
// 是否已备份
|
|
2013
|
+
is_backup,
|
|
2014
|
+
},
|
|
2015
|
+
// 结果
|
|
2016
|
+
result: $n_isValidObject(resUpload) ? resUpload : {},
|
|
2017
|
+
}
|
|
2018
|
+
|
|
2019
|
+
// 请求 - 上传文件至 cdn
|
|
2020
|
+
const { status: statusCallback, data: resCallback } = await $n_http({
|
|
2021
|
+
url: $n_config('apiFileUrl') + 'upload_callback',
|
|
2022
|
+
data,
|
|
2023
|
+
// 关闭错误提示
|
|
2024
|
+
warn: false,
|
|
2025
|
+
})
|
|
2026
|
+
|
|
2027
|
+
// 请求失败
|
|
2028
|
+
if (! statusCallback || ! $n_isValidObject(resCallback)) {
|
|
2029
|
+
// 设置文件上传失败
|
|
2030
|
+
setFileFail(fileItem, resCallback.msg || '上传失败')
|
|
2031
|
+
return false
|
|
2032
|
+
}
|
|
2033
|
+
|
|
2034
|
+
// 格式化 json
|
|
2035
|
+
const _json = $n_json.parse(resCallback.json)
|
|
2036
|
+
Object.assign(fileItem, resCallback, {
|
|
2037
|
+
json: $n_isValidObject(_json) ? $n_numberDeep(_json) : {},
|
|
2038
|
+
})
|
|
2039
|
+
|
|
2040
|
+
// 设置文件上传成功
|
|
2041
|
+
setFileSuccess(fileItem)
|
|
2042
|
+
|
|
2043
|
+
return true
|
|
2044
|
+
}
|
|
2045
|
+
|
|
2046
|
+
return true
|
|
2047
|
+
}
|
|
2048
|
+
|
|
2049
|
+
return {
|
|
2050
|
+
// 上传文件列表
|
|
2051
|
+
query: uploadFileLists,
|
|
2052
|
+
// 更新值
|
|
2053
|
+
updateValue,
|
|
2054
|
+
// 初始化上传列表
|
|
2055
|
+
initUploadFileLists,
|
|
2056
|
+
|
|
2057
|
+
// 初始化上传网络外链列表
|
|
2058
|
+
initUploadNetLists,
|
|
2059
|
+
// 上传网络外链文件
|
|
2060
|
+
uploadNet,
|
|
2061
|
+
// 获取上传网络外链进度
|
|
2062
|
+
// getUploadNetProgress,
|
|
2063
|
+
|
|
2064
|
+
// 检查是否正在上传文件
|
|
2065
|
+
checkUploading,
|
|
2066
|
+
// 选择文件上传
|
|
2067
|
+
chooseUpload,
|
|
2068
|
+
// 选择网络外链上传
|
|
2069
|
+
chooseUploadNet,
|
|
2070
|
+
// 文件输入框更新
|
|
2071
|
+
fileChange,
|
|
2072
|
+
// 删除所有文件
|
|
2073
|
+
deleteAll,
|
|
2074
|
+
// 删除单个文件
|
|
2075
|
+
deleteFileItem,
|
|
2076
|
+
// 预览图片
|
|
2077
|
+
previewImage,
|
|
2078
|
+
// 修改文件名
|
|
2079
|
+
editFileTitle,
|
|
2080
|
+
// 播放视频/音频
|
|
2081
|
+
play({ hash, __img }) {
|
|
2082
|
+
$n_play(__img ? __img : hash)
|
|
2083
|
+
},
|
|
2084
|
+
// 复制地址
|
|
2085
|
+
copyUrl,
|
|
2086
|
+
}
|
|
2087
|
+
}
|
|
2088
|
+
|
|
2089
|
+
/**
|
|
2090
|
+
* 获取上传方式
|
|
2091
|
+
*/
|
|
2092
|
+
export function getUpload(userConfig = null, defaultUpload = '') {
|
|
2093
|
+
const uploadConfig = $n_get((userConfig ? userConfig : configs.userConfig), 'uploader.upload', {})
|
|
2094
|
+
const type = $n_isValidString(defaultUpload) ? defaultUpload : uploadConfig.default
|
|
2095
|
+
return Object.assign(
|
|
2096
|
+
{},
|
|
2097
|
+
$n_get(uploadConfig, type, {}),
|
|
2098
|
+
{
|
|
2099
|
+
type,
|
|
2100
|
+
}
|
|
2101
|
+
)
|
|
2102
|
+
}
|
|
2103
|
+
|
|
2104
|
+
/**
|
|
2105
|
+
* 上传器
|
|
2106
|
+
*/
|
|
2107
|
+
const uploader = {
|
|
2108
|
+
// 创建对话框
|
|
2109
|
+
create,
|
|
2110
|
+
// 获取上传方式
|
|
2111
|
+
getUpload,
|
|
2112
|
+
}
|
|
2113
|
+
|
|
2114
|
+
export default uploader
|