eoss-ui 0.8.23 → 0.8.24
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/lib/button-group.js +228 -231
- package/lib/button.js +166 -153
- package/lib/calendar.js +2 -2
- package/lib/calogin.js +160 -146
- package/lib/card.js +2 -2
- package/lib/cascader.js +2 -2
- package/lib/checkbox-group.js +162 -148
- package/lib/clients.js +2 -2
- package/lib/config/api.js +5 -0
- package/lib/data-table-form.js +162 -148
- package/lib/data-table.js +165 -151
- package/lib/date-picker.js +159 -146
- package/lib/dialog.js +166 -153
- package/lib/enable-drag.js +2 -2
- package/lib/enterprise.js +2 -2
- package/lib/eoss-ui.common.js +704 -233
- package/lib/error-page.js +2 -2
- package/lib/flow-group.js +159 -146
- package/lib/flow-list.js +270 -257
- package/lib/flow.js +286 -272
- package/lib/form.js +161 -148
- package/lib/handle-user.js +160 -147
- package/lib/handler.js +160 -147
- package/lib/icon.js +160 -147
- package/lib/icons.js +2 -2
- package/lib/index.js +1 -1
- package/lib/input-number.js +159 -146
- package/lib/input.js +159 -146
- package/lib/label.js +2 -2
- package/lib/layout.js +2 -2
- package/lib/login.js +174 -160
- package/lib/main.js +235 -221
- package/lib/menu.js +2 -2
- package/lib/nav.js +159 -146
- package/lib/notify.js +157 -148
- package/lib/page.js +159 -146
- package/lib/pagination.js +159 -146
- package/lib/player.js +164 -151
- package/lib/qr-code.js +161 -148
- package/lib/radio-group.js +161 -148
- package/lib/retrial-auth.js +163 -150
- package/lib/select-ganged.js +161 -148
- package/lib/select.js +161 -148
- package/lib/selector-panel.js +178 -164
- package/lib/selector.js +161 -148
- package/lib/sizer.js +168 -155
- package/lib/steps.js +159 -146
- package/lib/switch.js +159 -146
- package/lib/table-form.js +159 -146
- package/lib/tabs-panel.js +2 -2
- package/lib/tabs.js +159 -146
- package/lib/theme-chalk/index.css +1 -1
- package/lib/theme-chalk/login.css +1 -1
- package/lib/theme-chalk/tree.css +1 -1
- package/lib/tips.js +160 -147
- package/lib/toolbar.js +2 -2
- package/lib/tree-group.js +159 -146
- package/lib/tree.js +160 -147
- package/lib/upload.js +698 -223
- package/lib/utils/util.js +5 -1
- package/lib/wujie.js +159 -146
- package/lib/wxlogin.js +159 -146
- package/package.json +162 -161
- package/packages/.DS_Store +0 -0
- package/packages/button-group/src/main.vue +346 -346
- package/packages/calogin/.DS_Store +0 -0
- package/packages/calogin/src/main.vue +412 -412
- package/packages/clients/src/main.vue +151 -151
- package/packages/date-picker/.DS_Store +0 -0
- package/packages/date-picker/src/.DS_Store +0 -0
- package/packages/dialog/.DS_Store +0 -0
- package/packages/flow/.DS_Store +0 -0
- package/packages/flow/src/.DS_Store +0 -0
- package/packages/flow/src/component/CommonOpinions.vue +376 -376
- package/packages/flow/src/component/FileList.vue +97 -97
- package/packages/flow/src/component/SendMsg.vue +242 -242
- package/packages/flow/src/component/SortFlow.vue +110 -110
- package/packages/flow/src/form.vue +123 -123
- package/packages/flow/src/table.vue +58 -58
- package/packages/flow-list/.DS_Store +0 -0
- package/packages/flow-list/src/main.vue +2337 -2337
- package/packages/form/.DS_Store +0 -0
- package/packages/form/src/table.vue +1512 -1512
- package/packages/icon/.DS_Store +0 -0
- package/packages/icon/src/main.vue +104 -104
- package/packages/login/.DS_Store +0 -0
- package/packages/login/src/resetPassword.vue +557 -557
- package/packages/main/.DS_Store +0 -0
- package/packages/main/src/.DS_Store +0 -0
- package/packages/main/src/public/online.vue +89 -89
- package/packages/main/src/public/search.vue +464 -464
- package/packages/main/src/public/settings.vue +273 -273
- package/packages/main/src/simplicity/apps.vue +388 -388
- package/packages/main/src/simplicity/avatar.vue +82 -82
- package/packages/main/src/simplicity/handler.vue +158 -158
- package/packages/main/src/simplicity/menu-list.vue +135 -135
- package/packages/main/src/simplicity/message.vue +293 -293
- package/packages/main/src/simplicity/notice.vue +222 -222
- package/packages/main/src/simplicity/sub-menu.vue +276 -276
- package/packages/main/src/simplicity/user.vue +259 -259
- package/packages/main/src/simplicityTop/apps.vue +388 -388
- package/packages/main/src/simplicityTop/avatar.vue +82 -82
- package/packages/main/src/simplicityTop/handler.vue +215 -215
- package/packages/main/src/simplicityTop/lists.vue +84 -84
- package/packages/main/src/simplicityTop/menu-list.vue +135 -135
- package/packages/main/src/simplicityTop/message.vue +293 -293
- package/packages/main/src/simplicityTop/notice.vue +222 -222
- package/packages/main/src/simplicityTop/router-page.vue +45 -45
- package/packages/main/src/simplicityTop/sub-menu.vue +274 -274
- package/packages/main/src/simplicityTop/user.vue +259 -259
- package/packages/menu/.DS_Store +0 -0
- package/packages/nav/src/main.vue +351 -351
- package/packages/player/src/main.vue +1 -1
- package/packages/select/.DS_Store +0 -0
- package/packages/selector/.DS_Store +0 -0
- package/packages/selector/src/main.vue +761 -761
- package/packages/selector-panel/.DS_Store +0 -0
- package/packages/selector-panel/src/main.vue +1036 -1036
- package/packages/selector-panel/src/selection.vue +174 -174
- package/packages/switch/src/main.vue +170 -170
- package/packages/theme-chalk/lib/index.css +1 -1
- package/packages/theme-chalk/lib/login.css +1 -1
- package/packages/theme-chalk/lib/tree.css +1 -1
- package/packages/theme-chalk/src/.DS_Store +0 -0
- package/packages/theme-chalk/src/data-table.scss +297 -297
- package/packages/theme-chalk/src/flow-list.scss +55 -55
- package/packages/theme-chalk/src/form.scss +501 -501
- package/packages/theme-chalk/src/handler.scss +148 -148
- package/packages/theme-chalk/src/icon.scss +3452 -3452
- package/packages/theme-chalk/src/login.scss +1006 -1006
- package/packages/theme-chalk/src/main.scss +664 -664
- package/packages/theme-chalk/src/menu.scss +224 -224
- package/packages/theme-chalk/src/selector.scss +114 -114
- package/packages/theme-chalk/src/simplicity-top.scss +1845 -1845
- package/packages/theme-chalk/src/simplicity.scss +1403 -1403
- package/packages/theme-chalk/src/tree.scss +167 -165
- package/packages/theme-chalk/src/upload.scss +172 -172
- package/packages/tips/src/main.vue +141 -141
- package/packages/upload/.DS_Store +0 -0
- package/packages/upload/src/main.vue +482 -14
- package/packages/wujie/src/main.vue +146 -146
- package/src/.DS_Store +0 -0
- package/src/config/api.js +5 -0
- package/src/index.js +163 -163
- package/src/utils/.DS_Store +0 -0
- package/src/utils/rules.js +18 -18
- package/src/utils/util.js +6 -1
|
@@ -10,6 +10,8 @@
|
|
|
10
10
|
:height="boxHeight"
|
|
11
11
|
:multiple="portrait ? false : multiple"
|
|
12
12
|
:action="uploadUrl"
|
|
13
|
+
:http-request="chunkHttpRequest"
|
|
14
|
+
:handles="chunkHandles"
|
|
13
15
|
:show-file-list="showList"
|
|
14
16
|
:file-list="lists"
|
|
15
17
|
:result-file="resultFile"
|
|
@@ -98,10 +100,8 @@
|
|
|
98
100
|
:type="selectType"
|
|
99
101
|
:size="btnSize"
|
|
100
102
|
:disabled="isDisabled"
|
|
101
|
-
>{{
|
|
102
|
-
|
|
103
|
-
}}</el-button
|
|
104
|
-
>
|
|
103
|
+
>{{ text ? text : autoUpload ? '点击上传' : '选择文件' }}
|
|
104
|
+
</el-button>
|
|
105
105
|
<el-button
|
|
106
106
|
class="es-upload-button"
|
|
107
107
|
v-if="!autoUpload"
|
|
@@ -178,23 +178,29 @@
|
|
|
178
178
|
</template>
|
|
179
179
|
<script>
|
|
180
180
|
import {
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
181
|
+
chunkCheckFile,
|
|
182
|
+
chunkMergeChunk,
|
|
183
|
+
chunkUploadChunk,
|
|
184
|
+
delAdjunct,
|
|
185
|
+
downloadByAdjunctId,
|
|
184
186
|
getAdjunctFileInfos,
|
|
185
|
-
|
|
186
|
-
|
|
187
|
+
getAdjunctProperties,
|
|
188
|
+
getDochubBucket,
|
|
187
189
|
previewAdjunct,
|
|
188
190
|
previewAdjunct2,
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
191
|
+
previewAdjunctOffice,
|
|
192
|
+
uploadDownloads,
|
|
193
|
+
uploadOnlyOne,
|
|
194
|
+
uploads,
|
|
195
|
+
uploadSort
|
|
192
196
|
} from 'eoss-ui/src/config/api.js';
|
|
193
197
|
import { debounce } from 'throttle-debounce';
|
|
194
198
|
import util from 'eoss-ui/src/utils/util.js';
|
|
195
199
|
import picture from './picture.js';
|
|
196
200
|
import store from 'eoss-ui/src/utils/store';
|
|
197
201
|
import { Base64 } from 'js-base64';
|
|
202
|
+
import SparkMD5 from 'spark-md5';
|
|
203
|
+
|
|
198
204
|
export default {
|
|
199
205
|
name: 'EsUpload',
|
|
200
206
|
components: {
|
|
@@ -446,7 +452,9 @@ export default {
|
|
|
446
452
|
boxHeight: this.listHeight,
|
|
447
453
|
previewAdjunct: previewAdjunct,
|
|
448
454
|
kkfileview: null,
|
|
449
|
-
dochubConfig: {}
|
|
455
|
+
dochubConfig: {},
|
|
456
|
+
// 文档中台桶配置下发的分片上传配置(baseBucketConfig),null 表示未下发 → 回退组件 props
|
|
457
|
+
bucketChunkConfig: null
|
|
450
458
|
};
|
|
451
459
|
},
|
|
452
460
|
computed: {
|
|
@@ -635,6 +643,80 @@ export default {
|
|
|
635
643
|
}
|
|
636
644
|
}
|
|
637
645
|
return this.photo;
|
|
646
|
+
},
|
|
647
|
+
// 是否走分片上传:完全由后台桶配置(/v2/chunk/config)的开关决定,且具备桶编码(code)与业务id(ownId)
|
|
648
|
+
isChunk() {
|
|
649
|
+
const cfg = this.bucketChunkConfig;
|
|
650
|
+
return !!(cfg && cfg.enableChunkUpload) && !!this.code && !!this.ownId;
|
|
651
|
+
},
|
|
652
|
+
// 生效的分片大小(字节):桶配置 > 默认 5MB
|
|
653
|
+
_chunkSize() {
|
|
654
|
+
const cfg = this.bucketChunkConfig;
|
|
655
|
+
return (
|
|
656
|
+
(cfg && Number(cfg.chunkSize) > 0 && Number(cfg.chunkSize)) ||
|
|
657
|
+
5 * 1024 * 1024
|
|
658
|
+
);
|
|
659
|
+
},
|
|
660
|
+
// 生效的分片并发数:桶配置 > 默认 3
|
|
661
|
+
_chunkConcurrent() {
|
|
662
|
+
const cfg = this.bucketChunkConfig;
|
|
663
|
+
return (
|
|
664
|
+
(cfg &&
|
|
665
|
+
Number(cfg.chunkConcurrent) > 0 &&
|
|
666
|
+
Number(cfg.chunkConcurrent)) ||
|
|
667
|
+
3
|
|
668
|
+
);
|
|
669
|
+
},
|
|
670
|
+
// 分片三接口地址:优先 dochubConfig 下发,否则回退 api.js 常量(与本组件 dochubConfig.x || 常量 的写法一致)
|
|
671
|
+
chunkUrls() {
|
|
672
|
+
const cfg = this.dochubConfig || {};
|
|
673
|
+
return {
|
|
674
|
+
check: cfg.chunkCheckUrl || chunkCheckFile,
|
|
675
|
+
upload: cfg.chunkUploadUrl || chunkUploadChunk,
|
|
676
|
+
merge: cfg.chunkMergeUrl || chunkMergeChunk
|
|
677
|
+
};
|
|
678
|
+
},
|
|
679
|
+
// 传给 el-upload 的自定义上传实现;非分片模式返回 undefined 以走默认上传
|
|
680
|
+
chunkHttpRequest() {
|
|
681
|
+
return this.isChunk ? this.chunkUpload : undefined;
|
|
682
|
+
},
|
|
683
|
+
// 操作列按钮:非分片模式回退用户经 $attrs 传入的 handles(或 undefined → el-upload 默认),
|
|
684
|
+
// 分片模式注入"暂停/继续"并保留预览/下载/删除(emit 复用既有事件链)。仅分片模式改变渲染,不影响存量业务。
|
|
685
|
+
chunkHandles() {
|
|
686
|
+
if (!this.isChunk) return this.$attrs.handles;
|
|
687
|
+
const done = (f) => f.status === 'success' || f.status == 0;
|
|
688
|
+
return [
|
|
689
|
+
{
|
|
690
|
+
icon: 'el-icon-video-pause',
|
|
691
|
+
title: '暂停',
|
|
692
|
+
rules: (f) => this.isChunkUploading(f) && !f.chunkPaused,
|
|
693
|
+
event: (f) => this.pauseUpload(f)
|
|
694
|
+
},
|
|
695
|
+
{
|
|
696
|
+
icon: 'el-icon-video-play',
|
|
697
|
+
title: '继续',
|
|
698
|
+
rules: (f) => this.isChunkUploading(f) && !!f.chunkPaused,
|
|
699
|
+
event: (f) => this.resumeUpload(f)
|
|
700
|
+
},
|
|
701
|
+
{
|
|
702
|
+
icon: 'el-icon-document-copy',
|
|
703
|
+
title: '预览',
|
|
704
|
+
rules: (f) => !!this.preview && done(f),
|
|
705
|
+
emit: 'preview'
|
|
706
|
+
},
|
|
707
|
+
{
|
|
708
|
+
icon: 'el-icon-download',
|
|
709
|
+
title: '下载',
|
|
710
|
+
rules: (f) => this.isDownload && done(f),
|
|
711
|
+
emit: 'download'
|
|
712
|
+
},
|
|
713
|
+
{
|
|
714
|
+
icon: 'el-icon-delete',
|
|
715
|
+
title: '删除',
|
|
716
|
+
rules: (f) => this.isRemove,
|
|
717
|
+
emit: 'remove'
|
|
718
|
+
}
|
|
719
|
+
];
|
|
638
720
|
}
|
|
639
721
|
},
|
|
640
722
|
watch: {
|
|
@@ -701,6 +783,8 @@ export default {
|
|
|
701
783
|
});
|
|
702
784
|
},
|
|
703
785
|
created() {
|
|
786
|
+
// 分片上传的运行态(按 file.uid 索引,非响应式,仅用于流程控制)
|
|
787
|
+
this._chunkState = {};
|
|
704
788
|
const dochubConfig = sessionStorage.getItem('dochubConfig');
|
|
705
789
|
if (dochubConfig) {
|
|
706
790
|
this.dochubConfig = JSON.parse(dochubConfig);
|
|
@@ -737,6 +821,11 @@ export default {
|
|
|
737
821
|
this.fileTotalSize = config.totalSize;
|
|
738
822
|
if (config.dochubConfig) {
|
|
739
823
|
this.dochubConfig = config.dochubConfig;
|
|
824
|
+
if (config.bucketChunkConfig !== undefined) {
|
|
825
|
+
this.bucketChunkConfig = config.bucketChunkConfig;
|
|
826
|
+
} else {
|
|
827
|
+
this.getBucketChunkConfig();
|
|
828
|
+
}
|
|
740
829
|
}
|
|
741
830
|
|
|
742
831
|
let url =
|
|
@@ -772,6 +861,8 @@ export default {
|
|
|
772
861
|
'dochubConfig',
|
|
773
862
|
JSON.stringify(res.results.dochubConfig)
|
|
774
863
|
);
|
|
864
|
+
// dochubConfig 有数据 → 走文档中台,再拉取该桶的桶配置(分片上传开关/参数)
|
|
865
|
+
this.getBucketChunkConfig();
|
|
775
866
|
}
|
|
776
867
|
let url = this.portrait
|
|
777
868
|
? this.dochubConfig.reuploadDocumentUrl || uploadOnlyOne
|
|
@@ -803,6 +894,42 @@ export default {
|
|
|
803
894
|
}
|
|
804
895
|
}
|
|
805
896
|
},
|
|
897
|
+
// 根据桶编码调中台获取桶配置接口(/v2/getBucket),取 baseBucketConfig 中的分片上传开关与参数。
|
|
898
|
+
getBucketChunkConfig() {
|
|
899
|
+
if (!this.code) return;
|
|
900
|
+
let url = getDochubBucket;
|
|
901
|
+
util
|
|
902
|
+
.ajax({
|
|
903
|
+
method: this.method,
|
|
904
|
+
url: url,
|
|
905
|
+
data: { bucketCode: this.code },
|
|
906
|
+
params: { bucketCode: this.code }
|
|
907
|
+
})
|
|
908
|
+
.then((res) => {
|
|
909
|
+
if (res.rCode === 0 && res.results && res.results.baseBucketConfig) {
|
|
910
|
+
const cfg = res.results.baseBucketConfig;
|
|
911
|
+
this.bucketChunkConfig = {
|
|
912
|
+
enableChunkUpload: cfg.enableChunkUpload,
|
|
913
|
+
chunkSize: cfg.chunkSize,
|
|
914
|
+
chunkConcurrent: cfg.chunkConcurrent
|
|
915
|
+
};
|
|
916
|
+
} else {
|
|
917
|
+
this.bucketChunkConfig = null;
|
|
918
|
+
}
|
|
919
|
+
// 回写本地缓存,避免同 code 的其他实例重复请求
|
|
920
|
+
let cached = store.get(this.code);
|
|
921
|
+
if (cached) {
|
|
922
|
+
store.set(this.code, {
|
|
923
|
+
...cached,
|
|
924
|
+
bucketChunkConfig: this.bucketChunkConfig
|
|
925
|
+
});
|
|
926
|
+
}
|
|
927
|
+
})
|
|
928
|
+
.catch(() => {
|
|
929
|
+
// 桶配置获取失败不阻断上传,维持 props 行为
|
|
930
|
+
this.bucketChunkConfig = null;
|
|
931
|
+
});
|
|
932
|
+
},
|
|
806
933
|
getFileLists() {
|
|
807
934
|
if (
|
|
808
935
|
!this.show ||
|
|
@@ -1226,7 +1353,12 @@ export default {
|
|
|
1226
1353
|
if (this.onSort) {
|
|
1227
1354
|
this.onSort(files);
|
|
1228
1355
|
} else {
|
|
1229
|
-
if (
|
|
1356
|
+
if (
|
|
1357
|
+
!this.ownId ||
|
|
1358
|
+
!this.code ||
|
|
1359
|
+
(this.fileList && this.fileList.length > 0)
|
|
1360
|
+
)
|
|
1361
|
+
return;
|
|
1230
1362
|
let ids = files.map((item) => {
|
|
1231
1363
|
if (item.status === 'success' || item.status == 0) {
|
|
1232
1364
|
return item.adjunctId || item.response.adjunctId;
|
|
@@ -1318,6 +1450,7 @@ export default {
|
|
|
1318
1450
|
this.onChange && this.onChange(file, fileList);
|
|
1319
1451
|
},
|
|
1320
1452
|
handleRemove(file, fileList) {
|
|
1453
|
+
this.cleanupChunk(file);
|
|
1321
1454
|
this.$emit('input', fileList.length ? fileList : '');
|
|
1322
1455
|
this.$emit('remove', file, fileList);
|
|
1323
1456
|
this.$emit('change', fileList);
|
|
@@ -1339,6 +1472,341 @@ export default {
|
|
|
1339
1472
|
}
|
|
1340
1473
|
this.$emit('error', err, file, fileList);
|
|
1341
1474
|
this.onError && this.onError(err, file, fileList);
|
|
1475
|
+
},
|
|
1476
|
+
// el-upload 的自定义上传实现:建立可暂停的运行态并返回受控 Promise。
|
|
1477
|
+
// 暂停时不 resolve/reject(promise 维持 pending,文件保持"上传中"而非失败),继续时再次进入流程。
|
|
1478
|
+
chunkUpload(options) {
|
|
1479
|
+
const file = options.file;
|
|
1480
|
+
const uid = file.uid;
|
|
1481
|
+
const state = {
|
|
1482
|
+
paused: false,
|
|
1483
|
+
controller: null,
|
|
1484
|
+
fileMd5: '',
|
|
1485
|
+
totalChunks: Math.max(1, Math.ceil(file.size / this._chunkSize)),
|
|
1486
|
+
options: options
|
|
1487
|
+
};
|
|
1488
|
+
this._chunkState[uid] = state;
|
|
1489
|
+
return new Promise((resolve, reject) => {
|
|
1490
|
+
state.resolve = resolve;
|
|
1491
|
+
state.reject = reject;
|
|
1492
|
+
this.runChunkFlow(uid);
|
|
1493
|
+
});
|
|
1494
|
+
},
|
|
1495
|
+
// 分片主流程(可被"继续"重新进入):MD5(缓存) → 秒传/断点续传检测 → 并发上传缺失分片 → 合并入库。
|
|
1496
|
+
runChunkFlow(uid) {
|
|
1497
|
+
const state = this._chunkState[uid];
|
|
1498
|
+
if (!state) return;
|
|
1499
|
+
const options = state.options;
|
|
1500
|
+
const file = options.file;
|
|
1501
|
+
const onProgress = options.onProgress || function () {};
|
|
1502
|
+
const baseParams = () => ({
|
|
1503
|
+
...this.param,
|
|
1504
|
+
fileMd5: state.fileMd5,
|
|
1505
|
+
fileName: file.name,
|
|
1506
|
+
fileSize: file.size,
|
|
1507
|
+
totalChunks: state.totalChunks,
|
|
1508
|
+
bucketCode: this.code,
|
|
1509
|
+
businessId: this.ownId
|
|
1510
|
+
});
|
|
1511
|
+
const isAbort = (e) =>
|
|
1512
|
+
!!e &&
|
|
1513
|
+
(e.__aborted === true ||
|
|
1514
|
+
e.name === 'CanceledError' ||
|
|
1515
|
+
e.name === 'AbortError' ||
|
|
1516
|
+
(typeof e.message === 'string' && /cancel|abort/i.test(e.message)));
|
|
1517
|
+
|
|
1518
|
+
Promise.resolve()
|
|
1519
|
+
.then(() => state.fileMd5 || this.computeFileMd5(file, onProgress))
|
|
1520
|
+
.then((md5) => {
|
|
1521
|
+
state.fileMd5 = md5;
|
|
1522
|
+
if (state.paused) return null;
|
|
1523
|
+
return util.ajax({
|
|
1524
|
+
method: 'post',
|
|
1525
|
+
url: this.chunkUrls.check,
|
|
1526
|
+
data: baseParams(),
|
|
1527
|
+
params: baseParams(),
|
|
1528
|
+
headers: options.headers
|
|
1529
|
+
});
|
|
1530
|
+
})
|
|
1531
|
+
.then((checkRes) => {
|
|
1532
|
+
if (!checkRes) return; // 暂停于 MD5/检测阶段,维持 pending
|
|
1533
|
+
if (checkRes.rCode !== 0) return Promise.reject(checkRes);
|
|
1534
|
+
const info = checkRes.results || {};
|
|
1535
|
+
// 秒传命中:该业务桶下已存在同 MD5 文件,直接视为成功
|
|
1536
|
+
if (info.instant) {
|
|
1537
|
+
onProgress({ percent: 100 });
|
|
1538
|
+
this.normalizeChunkDoc(info, file.name);
|
|
1539
|
+
this.finishChunk(uid, {
|
|
1540
|
+
rCode: 0,
|
|
1541
|
+
msg: checkRes.msg || '秒传成功',
|
|
1542
|
+
results: info
|
|
1543
|
+
});
|
|
1544
|
+
// 秒传文件已存在于服务端,刷新列表以展示其完整信息(含可下载/预览的 id)
|
|
1545
|
+
this.requestFiles && this.getFiles();
|
|
1546
|
+
return;
|
|
1547
|
+
}
|
|
1548
|
+
// 断点续传:跳过服务端已存在的分片
|
|
1549
|
+
const uploaded = new Set(info.uploadedChunks || []);
|
|
1550
|
+
const pending = [];
|
|
1551
|
+
for (let i = 0; i < state.totalChunks; i++) {
|
|
1552
|
+
if (!uploaded.has(i)) pending.push(i);
|
|
1553
|
+
}
|
|
1554
|
+
let finished = state.totalChunks - pending.length;
|
|
1555
|
+
// 上传阶段占 5~95%(MD5 校验已占 0~5%),保证进度单调不回退
|
|
1556
|
+
onProgress({
|
|
1557
|
+
percent: Math.min(
|
|
1558
|
+
95,
|
|
1559
|
+
5 + Math.round((finished / state.totalChunks) * 90)
|
|
1560
|
+
)
|
|
1561
|
+
});
|
|
1562
|
+
state.controller =
|
|
1563
|
+
typeof AbortController !== 'undefined'
|
|
1564
|
+
? new AbortController()
|
|
1565
|
+
: null;
|
|
1566
|
+
const signal = state.controller ? state.controller.signal : undefined;
|
|
1567
|
+
return this.runChunkQueue(pending, this._chunkConcurrent, (index) => {
|
|
1568
|
+
if (state.paused) return Promise.reject({ __aborted: true });
|
|
1569
|
+
return this.uploadOneChunk(
|
|
1570
|
+
file,
|
|
1571
|
+
index,
|
|
1572
|
+
state.totalChunks,
|
|
1573
|
+
state.fileMd5,
|
|
1574
|
+
options.headers,
|
|
1575
|
+
signal
|
|
1576
|
+
).then(() => {
|
|
1577
|
+
finished++;
|
|
1578
|
+
onProgress({
|
|
1579
|
+
percent: Math.min(
|
|
1580
|
+
95,
|
|
1581
|
+
5 + Math.round((finished / state.totalChunks) * 90)
|
|
1582
|
+
)
|
|
1583
|
+
});
|
|
1584
|
+
});
|
|
1585
|
+
}).then(() => {
|
|
1586
|
+
if (state.paused) return; // 暂停于上传阶段,维持 pending
|
|
1587
|
+
return util
|
|
1588
|
+
.ajax({
|
|
1589
|
+
method: 'post',
|
|
1590
|
+
url: this.chunkUrls.merge,
|
|
1591
|
+
data: baseParams(),
|
|
1592
|
+
params: baseParams(),
|
|
1593
|
+
headers: options.headers
|
|
1594
|
+
})
|
|
1595
|
+
.then((mergeRes) => {
|
|
1596
|
+
if (mergeRes.rCode !== 0) return Promise.reject(mergeRes);
|
|
1597
|
+
onProgress({ percent: 100 });
|
|
1598
|
+
this.normalizeChunkDoc(mergeRes.results, file.name);
|
|
1599
|
+
this.finishChunk(uid, mergeRes);
|
|
1600
|
+
// 刷新列表:让刚入库的文件以与其他行一致的结构展示(时间/大小/上传人等)
|
|
1601
|
+
this.requestFiles && this.getFiles();
|
|
1602
|
+
});
|
|
1603
|
+
});
|
|
1604
|
+
})
|
|
1605
|
+
.catch((e) => {
|
|
1606
|
+
// 暂停导致的中止:保持 pending 等待"继续";其余才算失败
|
|
1607
|
+
if (state.paused || isAbort(e)) return;
|
|
1608
|
+
this.failChunk(uid, e);
|
|
1609
|
+
});
|
|
1610
|
+
},
|
|
1611
|
+
// 暂停:置暂停标记并中止在传分片;promise 维持 pending,文件保持"上传中"
|
|
1612
|
+
pauseUpload(file) {
|
|
1613
|
+
const state = file && this._chunkState[file.uid];
|
|
1614
|
+
if (!state) return;
|
|
1615
|
+
state.paused = true;
|
|
1616
|
+
if (state.controller) {
|
|
1617
|
+
try {
|
|
1618
|
+
state.controller.abort();
|
|
1619
|
+
} catch (e) {
|
|
1620
|
+
/* ignore */
|
|
1621
|
+
}
|
|
1622
|
+
}
|
|
1623
|
+
this.$set(file, 'chunkPaused', true);
|
|
1624
|
+
this.$emit('chunk-pause', file);
|
|
1625
|
+
},
|
|
1626
|
+
// 继续:清暂停标记,重新进入流程(checkFile 会返回已传分片,自动续传)
|
|
1627
|
+
resumeUpload(file) {
|
|
1628
|
+
const state = file && this._chunkState[file.uid];
|
|
1629
|
+
if (!state || !state.paused) return; // 仅暂停态可续传,防止重复触发并发流程
|
|
1630
|
+
state.paused = false;
|
|
1631
|
+
this.$set(file, 'chunkPaused', false);
|
|
1632
|
+
this.$emit('chunk-resume', file);
|
|
1633
|
+
this.runChunkFlow(file.uid);
|
|
1634
|
+
},
|
|
1635
|
+
// 该文件是否处于分片上传运行态(用于操作列按钮显隐)
|
|
1636
|
+
isChunkUploading(file) {
|
|
1637
|
+
return !!(file && this._chunkState && this._chunkState[file.uid]);
|
|
1638
|
+
},
|
|
1639
|
+
// 归一化合并响应:dochub Document 用 docTagId 标识、且无 suffix 字段,
|
|
1640
|
+
// 而组件下载读 adjunctId、预览读 suffix/originalName,这里统一映射补齐,避免预览取 suffix 报错。
|
|
1641
|
+
normalizeChunkDoc(results, fileName) {
|
|
1642
|
+
if (!results || typeof results !== 'object') return results;
|
|
1643
|
+
const id =
|
|
1644
|
+
results.adjunctId ||
|
|
1645
|
+
results.docTagId ||
|
|
1646
|
+
results.documentId ||
|
|
1647
|
+
results.id;
|
|
1648
|
+
if (id) {
|
|
1649
|
+
if (!results.adjunctId) results.adjunctId = id;
|
|
1650
|
+
if (!results.documentId) results.documentId = id;
|
|
1651
|
+
}
|
|
1652
|
+
// 文件名:优先用上传时的原始名,其次后端 documentFile.docName
|
|
1653
|
+
const name =
|
|
1654
|
+
fileName ||
|
|
1655
|
+
(results.documentFile && results.documentFile.docName) ||
|
|
1656
|
+
results.originalName ||
|
|
1657
|
+
results.name ||
|
|
1658
|
+
'';
|
|
1659
|
+
if (!results.originalName && name) results.originalName = name;
|
|
1660
|
+
// 后缀:预览按后缀判断类型,缺省从文件名推导;始终保证为字符串
|
|
1661
|
+
if (results.suffix === undefined || results.suffix === null) {
|
|
1662
|
+
results.suffix =
|
|
1663
|
+
name && name.lastIndexOf('.') > -1
|
|
1664
|
+
? name.slice(name.lastIndexOf('.') + 1)
|
|
1665
|
+
: '';
|
|
1666
|
+
}
|
|
1667
|
+
// 展示字段:dochub 的元数据在 documentFile 下且字段名不同,映射成 showInfo 默认读取的扁平字段
|
|
1668
|
+
const df = results.documentFile;
|
|
1669
|
+
if (df && typeof df === 'object') {
|
|
1670
|
+
if (!results.uploadTime) {
|
|
1671
|
+
results.uploadTime = df.createTime || df.lastModifyTime || '';
|
|
1672
|
+
}
|
|
1673
|
+
if (!results.userName) {
|
|
1674
|
+
results.userName = df.createUserName || df.lastModifyUserName || '';
|
|
1675
|
+
}
|
|
1676
|
+
if (!results.fileSize && (df.docSize || df.docSize === 0)) {
|
|
1677
|
+
results.fileSize = this.formatFileSize(df.docSize);
|
|
1678
|
+
}
|
|
1679
|
+
}
|
|
1680
|
+
return results;
|
|
1681
|
+
},
|
|
1682
|
+
// 字节数格式化为可读大小(与列表展示风格一致:1 位小数、无空格)
|
|
1683
|
+
formatFileSize(bytes) {
|
|
1684
|
+
const n = Number(bytes);
|
|
1685
|
+
if (!n || n <= 0) return '0B';
|
|
1686
|
+
if (n >= 1073741824) return (n / 1073741824).toFixed(1) + 'GB';
|
|
1687
|
+
if (n >= 1048576) return (n / 1048576).toFixed(1) + 'MB';
|
|
1688
|
+
if (n >= 1024) return (n / 1024).toFixed(1) + 'KB';
|
|
1689
|
+
return n + 'B';
|
|
1690
|
+
},
|
|
1691
|
+
// 完成:兑现 promise 并清理运行态
|
|
1692
|
+
finishChunk(uid, response) {
|
|
1693
|
+
const state = this._chunkState[uid];
|
|
1694
|
+
if (!state) return;
|
|
1695
|
+
state.resolve && state.resolve(response);
|
|
1696
|
+
delete this._chunkState[uid];
|
|
1697
|
+
},
|
|
1698
|
+
// 失败:拒绝 promise 并清理运行态
|
|
1699
|
+
failChunk(uid, err) {
|
|
1700
|
+
const state = this._chunkState[uid];
|
|
1701
|
+
if (!state) return;
|
|
1702
|
+
state.reject && state.reject(err);
|
|
1703
|
+
delete this._chunkState[uid];
|
|
1704
|
+
},
|
|
1705
|
+
// 移除文件时清理分片运行态(中止在传分片,孤立 pending promise 不再触发回调)
|
|
1706
|
+
cleanupChunk(file) {
|
|
1707
|
+
const uid = file && file.uid;
|
|
1708
|
+
const state = uid && this._chunkState && this._chunkState[uid];
|
|
1709
|
+
if (!state) return;
|
|
1710
|
+
state.paused = true;
|
|
1711
|
+
state.resolve = null;
|
|
1712
|
+
state.reject = null;
|
|
1713
|
+
if (state.controller) {
|
|
1714
|
+
try {
|
|
1715
|
+
state.controller.abort();
|
|
1716
|
+
} catch (e) {
|
|
1717
|
+
/* ignore */
|
|
1718
|
+
}
|
|
1719
|
+
}
|
|
1720
|
+
delete this._chunkState[uid];
|
|
1721
|
+
},
|
|
1722
|
+
// 上传单个分片(multipart/form-data);signal 用于暂停时中止
|
|
1723
|
+
uploadOneChunk(file, index, totalChunks, fileMd5, headers, signal) {
|
|
1724
|
+
const start = index * this._chunkSize;
|
|
1725
|
+
const end = Math.min(start + this._chunkSize, file.size);
|
|
1726
|
+
const formData = new FormData();
|
|
1727
|
+
formData.append('file', file.slice(start, end), file.name);
|
|
1728
|
+
formData.append('fileMd5', fileMd5);
|
|
1729
|
+
formData.append('fileName', file.name);
|
|
1730
|
+
formData.append('fileSize', file.size);
|
|
1731
|
+
formData.append('chunkIndex', index);
|
|
1732
|
+
formData.append('totalChunks', totalChunks);
|
|
1733
|
+
formData.append('bucketCode', this.code);
|
|
1734
|
+
formData.append('businessId', this.ownId);
|
|
1735
|
+
Object.keys(this.param || {}).forEach((key) => {
|
|
1736
|
+
formData.append(key, this.param[key]);
|
|
1737
|
+
});
|
|
1738
|
+
return util
|
|
1739
|
+
.ajax({
|
|
1740
|
+
method: 'post',
|
|
1741
|
+
url: this.chunkUrls.upload,
|
|
1742
|
+
data: formData,
|
|
1743
|
+
format: false,
|
|
1744
|
+
headers: headers,
|
|
1745
|
+
signal: signal
|
|
1746
|
+
})
|
|
1747
|
+
.then((res) => {
|
|
1748
|
+
if (res.rCode !== 0) return Promise.reject(res);
|
|
1749
|
+
return res;
|
|
1750
|
+
});
|
|
1751
|
+
},
|
|
1752
|
+
// 分块读取文件并增量计算 MD5(避免大文件一次性读入内存)
|
|
1753
|
+
computeFileMd5(file, onProgress) {
|
|
1754
|
+
return new Promise((resolve, reject) => {
|
|
1755
|
+
const size = this._chunkSize;
|
|
1756
|
+
const chunks = Math.max(1, Math.ceil(file.size / size));
|
|
1757
|
+
const spark = new SparkMD5.ArrayBuffer();
|
|
1758
|
+
const reader = new FileReader();
|
|
1759
|
+
let current = 0;
|
|
1760
|
+
const loadNext = () => {
|
|
1761
|
+
const start = current * size;
|
|
1762
|
+
reader.readAsArrayBuffer(
|
|
1763
|
+
file.slice(start, Math.min(start + size, file.size))
|
|
1764
|
+
);
|
|
1765
|
+
};
|
|
1766
|
+
reader.onload = (e) => {
|
|
1767
|
+
spark.append(e.target.result);
|
|
1768
|
+
current++;
|
|
1769
|
+
// 校验阶段占进度 0~5%,剩余留给上传与合并
|
|
1770
|
+
onProgress &&
|
|
1771
|
+
onProgress({ percent: Math.round((current / chunks) * 5) });
|
|
1772
|
+
if (current < chunks) {
|
|
1773
|
+
loadNext();
|
|
1774
|
+
} else {
|
|
1775
|
+
// 取标准 32 位 MD5 的中间 16 位,与后端 hutool digestHex16(即 doc_md5)口径一致,
|
|
1776
|
+
// 否则秒传按 md5 比对永远不命中
|
|
1777
|
+
resolve(spark.end().substring(8, 24));
|
|
1778
|
+
}
|
|
1779
|
+
};
|
|
1780
|
+
reader.onerror = () => reject({ msg: '文件读取失败,无法计算MD5' });
|
|
1781
|
+
loadNext();
|
|
1782
|
+
});
|
|
1783
|
+
},
|
|
1784
|
+
// 限制并发的分片任务队列
|
|
1785
|
+
runChunkQueue(indexes, concurrency, worker) {
|
|
1786
|
+
return new Promise((resolve, reject) => {
|
|
1787
|
+
if (!indexes.length) return resolve();
|
|
1788
|
+
let cursor = 0;
|
|
1789
|
+
let active = 0;
|
|
1790
|
+
let failed = false;
|
|
1791
|
+
const schedule = () => {
|
|
1792
|
+
if (failed) return;
|
|
1793
|
+
if (cursor >= indexes.length && active === 0) return resolve();
|
|
1794
|
+
while (active < concurrency && cursor < indexes.length) {
|
|
1795
|
+
const index = indexes[cursor++];
|
|
1796
|
+
active++;
|
|
1797
|
+
worker(index)
|
|
1798
|
+
.then(() => {
|
|
1799
|
+
active--;
|
|
1800
|
+
schedule();
|
|
1801
|
+
})
|
|
1802
|
+
.catch((err) => {
|
|
1803
|
+
failed = true;
|
|
1804
|
+
reject(err);
|
|
1805
|
+
});
|
|
1806
|
+
}
|
|
1807
|
+
};
|
|
1808
|
+
schedule();
|
|
1809
|
+
});
|
|
1342
1810
|
}
|
|
1343
1811
|
}
|
|
1344
1812
|
};
|