large-model-component 1.0.1 → 1.0.3
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/README.md +1 -1
- package/dist/img/blue_tip.2d17b827.png +0 -0
- package/dist/large-model-component.common.js +8927 -141151
- package/dist/large-model-component.common.js.map +1 -1
- package/dist/large-model-component.css +1 -1
- package/dist/large-model-component.umd.js +8931 -141155
- package/dist/large-model-component.umd.js.map +1 -1
- package/dist/large-model-component.umd.min.js +6 -31
- package/dist/large-model-component.umd.min.js.map +1 -1
- package/docs/comps/README.md +1 -1
- package/package.json +10 -16
- package/packages/footer/footer.vue +346 -0
- package/packages/footer/index.js +2 -0
- package/packages/header/header.vue +516 -0
- package/packages/header/index.js +3 -0
- package/packages/index.js +7 -3
- package/packages/model/index.js +3 -0
- package/packages/{largeModel/index.vue → model/model.vue} +26 -65
- package/packages/upload/index.js +2 -0
- package/packages/upload/src/ajax.js +156 -0
- package/packages/upload/src/index.vue +329 -0
- package/packages/upload/src/upload-dragger.vue +70 -0
- package/packages/upload/src/upload-list.vue +94 -0
- package/packages/upload/src/upload.vue +274 -0
- package/src/App.vue +240 -2
- package/src/assets/blue_tip.png +0 -0
- package/src/assets/gray_tip.png +0 -0
- package/src/assets/logo.png +0 -0
- package/src/components/HelloWorld.vue +59 -0
- package/{packages/largeModel → src/components}/contentFold.vue +13 -0
- package/src/main.js +0 -5
- package/src/utils/request.js +3 -11
- package/tests/unit/example.spec.js +12 -0
- package/vue.config.js +3 -0
- package/.env +0 -2
- package/.env.development +0 -2
- package/.env.production +0 -2
- package/dist/css/107.3716bdaf.css +0 -1
- package/dist/css/644.3716bdaf.css +0 -1
- package/dist/css/848.e455a0b7.css +0 -1
- package/dist/img/ai-chart.167a7713.png +0 -0
- package/dist/img/scrol-bg.f446933a.png +0 -0
- package/dist/img/zhijing-model.6a81c5a7.png +0 -0
- package/dist/large-model-component.common.644.js +0 -73
- package/dist/large-model-component.common.644.js.map +0 -1
- package/dist/large-model-component.umd.107.js +0 -73
- package/dist/large-model-component.umd.107.js.map +0 -1
- package/dist/large-model-component.umd.min.848.js +0 -2
- package/dist/large-model-component.umd.min.848.js.map +0 -1
- package/packages/largeModel/index.js +0 -2
- package/src/assets/css/app.css +0 -3255
- package/src/assets/css/chunk-vendors.css +0 -2071
- package/src/assets/css/github-markdown.css +0 -985
- package/src/router/index.js +0 -20
- package/src/store/index.js +0 -26
- package/src/utils/auth.js +0 -48
- package/src/utils/index.js +0 -111
- package/src/utils/spceialistConfig.js +0 -44
- package/src/utils/tool.js +0 -4
- package/src/utils/validate.js +0 -20
- /package/{packages/largeModel → src/utils}/pubsub.js +0 -0
- /package/{packages/largeModel → src/utils}/wsconnecter.js +0 -0
|
@@ -161,20 +161,17 @@
|
|
|
161
161
|
|
|
162
162
|
<div class="input-tools">
|
|
163
163
|
<div class="left-tools">
|
|
164
|
-
<
|
|
165
|
-
|
|
166
|
-
<
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
/>
|
|
176
|
-
</div>
|
|
177
|
-
</el-tooltip>
|
|
164
|
+
<div class="upload-trigger">
|
|
165
|
+
<i class="el-icon-paperclip tool-icon" :class="{ 'tool-icon-disabled': loading }"></i>
|
|
166
|
+
<input
|
|
167
|
+
type="file"
|
|
168
|
+
id="fileInput"
|
|
169
|
+
@change="handleFileChange"
|
|
170
|
+
accept=".pdf,.doc,.docx,.txt,.pptx,.ppt,.xls,.xlsx,.png,.jpg,.jpeg,.gif,.webp,image/*"
|
|
171
|
+
:disabled="loading"
|
|
172
|
+
multiple
|
|
173
|
+
/>
|
|
174
|
+
</div>
|
|
178
175
|
</div>
|
|
179
176
|
<el-button
|
|
180
177
|
type="primary"
|
|
@@ -206,15 +203,11 @@ import {
|
|
|
206
203
|
conversationPage,
|
|
207
204
|
deleteHistory,
|
|
208
205
|
historyDetail,
|
|
209
|
-
loginCas,
|
|
210
206
|
quickAccess as getQuickAccessApi,
|
|
211
207
|
suggestive,
|
|
212
208
|
votePort
|
|
213
209
|
} from "@/api/user";
|
|
214
|
-
import ContentFold from
|
|
215
|
-
|
|
216
|
-
const TOKEN_KEY = 'Industrial-Access-Token';
|
|
217
|
-
const CAS_LOGIN_URL = 'https://dticts.paas.casicloud.com/cas/login?service=';
|
|
210
|
+
import ContentFold from "@/components/contentFold";
|
|
218
211
|
|
|
219
212
|
const ALLOWED_EXTENSIONS = ['pdf', 'doc', 'docx', 'txt', 'pptx', 'ppt', 'xls', 'xlsx', 'jpg', 'jpeg', 'png', 'gif', 'webp'];
|
|
220
213
|
const MAX_FILE_SIZE = 10 * 1024 * 1024; // 10M
|
|
@@ -225,7 +218,7 @@ const DEFAULT_ICONS = [
|
|
|
225
218
|
require("@/assets/img/quick_finder_icon4.png")
|
|
226
219
|
];
|
|
227
220
|
export default {
|
|
228
|
-
name: '
|
|
221
|
+
name: 'LLMAssistantDialog',
|
|
229
222
|
components: {ContentFold},
|
|
230
223
|
data() {
|
|
231
224
|
return {
|
|
@@ -262,8 +255,7 @@ export default {
|
|
|
262
255
|
return this.pageParams.hasMore && !this.isSearching;
|
|
263
256
|
}
|
|
264
257
|
},
|
|
265
|
-
|
|
266
|
-
await this.handleCasCallback();
|
|
258
|
+
mounted() {
|
|
267
259
|
this.$nextTick(() => {
|
|
268
260
|
this.initScrollListener();
|
|
269
261
|
});
|
|
@@ -276,44 +268,6 @@ export default {
|
|
|
276
268
|
}
|
|
277
269
|
},
|
|
278
270
|
methods: {
|
|
279
|
-
// 处理 CAS 回调:如果 URL 带有 ticket,则用它换取 token
|
|
280
|
-
async handleCasCallback() {
|
|
281
|
-
const urlParams = new URLSearchParams(window.location.search);
|
|
282
|
-
const ticket = urlParams.get('ticket');
|
|
283
|
-
if (!ticket) return;
|
|
284
|
-
|
|
285
|
-
try {
|
|
286
|
-
// serviceUrl 是去掉 ticket 参数后的当前页面地址
|
|
287
|
-
const serviceUrl = window.location.origin + window.location.pathname;
|
|
288
|
-
const res = await loginCas({ ticket, serviceUrl });
|
|
289
|
-
const token = res.data?.data?.token;
|
|
290
|
-
if (token) {
|
|
291
|
-
localStorage.setItem(TOKEN_KEY, token);
|
|
292
|
-
// ticket 换取 token 成功后自动打开弹窗
|
|
293
|
-
this.openDialog();
|
|
294
|
-
}
|
|
295
|
-
} catch (e) {
|
|
296
|
-
console.error('CAS 登录失败:', e);
|
|
297
|
-
} finally {
|
|
298
|
-
// 清除 URL 中的 ticket 参数,避免刷新重复请求
|
|
299
|
-
const cleanUrl = window.location.origin + window.location.pathname;
|
|
300
|
-
window.history.replaceState({}, '', cleanUrl);
|
|
301
|
-
}
|
|
302
|
-
},
|
|
303
|
-
|
|
304
|
-
// 打开组件前先做 CAS 认证
|
|
305
|
-
open() {
|
|
306
|
-
window.location.href = CAS_LOGIN_URL + encodeURIComponent(location.href);
|
|
307
|
-
},
|
|
308
|
-
|
|
309
|
-
// CAS 认证完成后真正打开弹窗(由 handleCasCallback 成功后调用,或 token 已存在时直接调用)
|
|
310
|
-
openDialog() {
|
|
311
|
-
this.dialogVisible = true;
|
|
312
|
-
this.$nextTick(() => {
|
|
313
|
-
this.initData();
|
|
314
|
-
this.initScrollListener();
|
|
315
|
-
});
|
|
316
|
-
},
|
|
317
271
|
async stopGeneration() {
|
|
318
272
|
if (this.abortController && !this.abortController.signal.aborted) {
|
|
319
273
|
console.log('=== 触发停止生成 ===');
|
|
@@ -369,6 +323,13 @@ export default {
|
|
|
369
323
|
container.removeEventListener('scroll', this.handleHistoryScroll);
|
|
370
324
|
}
|
|
371
325
|
},
|
|
326
|
+
open() {
|
|
327
|
+
this.dialogVisible = true;
|
|
328
|
+
this.$nextTick(() => {
|
|
329
|
+
this.initData();
|
|
330
|
+
this.initScrollListener();
|
|
331
|
+
});
|
|
332
|
+
},
|
|
372
333
|
async initData() {
|
|
373
334
|
await this.getQuickAccess();
|
|
374
335
|
await this.changeFaq();
|
|
@@ -552,7 +513,7 @@ export default {
|
|
|
552
513
|
|
|
553
514
|
const parseFileList = this.files
|
|
554
515
|
.filter(f => f.type === 'document' && f.status === 'completed')
|
|
555
|
-
.map(f => ({
|
|
516
|
+
.map(f => ({fileId: f.id, fileName: f.name, markdown: f.parsedContent}));
|
|
556
517
|
const imageFiles = this.files
|
|
557
518
|
.filter(f => f.type === 'image' && f.status === 'completed')
|
|
558
519
|
.map(f => f.rawFile);
|
|
@@ -607,10 +568,10 @@ export default {
|
|
|
607
568
|
const decoder = new TextDecoder('utf-8');
|
|
608
569
|
|
|
609
570
|
while (true) {
|
|
610
|
-
const {
|
|
571
|
+
const {done, value} = await reader.read();
|
|
611
572
|
if (done) break;
|
|
612
573
|
|
|
613
|
-
const chunk = decoder.decode(value, {
|
|
574
|
+
const chunk = decoder.decode(value, {stream: true});
|
|
614
575
|
if (chunk) {
|
|
615
576
|
this.appendAnswer(chunk);
|
|
616
577
|
}
|
|
@@ -622,7 +583,7 @@ export default {
|
|
|
622
583
|
} catch (error) {
|
|
623
584
|
if (error.name === 'AbortError') {
|
|
624
585
|
console.log('请求已中止');
|
|
625
|
-
}else{
|
|
586
|
+
} else {
|
|
626
587
|
console.error('请求失败:', error);
|
|
627
588
|
this.loading = false;
|
|
628
589
|
const lastItem = this.conversation.dialogue[this.conversation.dialogue.length - 1];
|
|
@@ -632,7 +593,7 @@ export default {
|
|
|
632
593
|
lastItem.isStreaming = false;
|
|
633
594
|
}
|
|
634
595
|
}
|
|
635
|
-
}finally {
|
|
596
|
+
} finally {
|
|
636
597
|
this.loading = false;
|
|
637
598
|
this.abortController = null;
|
|
638
599
|
this.isCancelled = false;
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
function getError(action, option, xhr) {
|
|
2
|
+
let msg;
|
|
3
|
+
if (xhr.response) {
|
|
4
|
+
msg = `${xhr.response.error || xhr.response}`;
|
|
5
|
+
} else if (xhr.responseText) {
|
|
6
|
+
msg = `${xhr.responseText}`;
|
|
7
|
+
} else {
|
|
8
|
+
msg = `fail to post ${action} ${xhr.status}`;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
const err = new Error(msg);
|
|
12
|
+
err.status = xhr.status;
|
|
13
|
+
err.method = 'post';
|
|
14
|
+
err.url = action;
|
|
15
|
+
return err;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
function getBody(xhr) {
|
|
19
|
+
const text = xhr.responseText || xhr.response;
|
|
20
|
+
if (!text) {
|
|
21
|
+
return text;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
try {
|
|
25
|
+
return JSON.parse(text);
|
|
26
|
+
} catch (e) {
|
|
27
|
+
return text;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export default function upload(option) {
|
|
32
|
+
if (typeof XMLHttpRequest === 'undefined') {
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
const action = option.action; // 文件上传上传路径
|
|
36
|
+
const percentage = []; // 文件上传进度的数组,单项就是一个分片的进度
|
|
37
|
+
//将Blob转为上传时需要的FormData格式
|
|
38
|
+
const formDataList = option.chunkList.map((item, index) => {
|
|
39
|
+
const formData = new FormData();
|
|
40
|
+
// 额外加入组件外外面传入的data数据
|
|
41
|
+
const md5 = option.chunkHashList[index];
|
|
42
|
+
const chunkOption = {
|
|
43
|
+
// chunkSize: item.size, //当前分片大小
|
|
44
|
+
// chunkTotal: Math.ceil(option.file.size / option.chunkSize), // 所有切片数量
|
|
45
|
+
// chunkIndex: index, // 当前切片下标
|
|
46
|
+
// chunkHash: md5, // 当前切片hash
|
|
47
|
+
// fileName: option.file.name, // 文件名
|
|
48
|
+
// fileHash: option.fileHash, // 整个文件hash
|
|
49
|
+
// fileSize: option.file.size, // 总文件大小
|
|
50
|
+
partName: index, // 当前切片下标
|
|
51
|
+
fileName: option.file.name, //文件名
|
|
52
|
+
totalParts: Math.ceil(option.file.size / option.chunkSize), //分片总数
|
|
53
|
+
fileMd5: option.fileHash, //整个文件的hash
|
|
54
|
+
fileSize: option.file.size, //总文件大小
|
|
55
|
+
};
|
|
56
|
+
Object.keys(chunkOption).forEach((key) => {
|
|
57
|
+
formData.append(key, chunkOption[key]);
|
|
58
|
+
});
|
|
59
|
+
if (option.data) {
|
|
60
|
+
Object.keys(option.data).forEach((key) => {
|
|
61
|
+
formData.append(key, option.data[key]);
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
formData.append(option.filename, item); // 文件的Blob
|
|
65
|
+
return formData;
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
// 更新上传进度条的方法
|
|
69
|
+
const updataPercentage = (e) => {
|
|
70
|
+
let loaded = 0; // 当前已经上传文件的总大小
|
|
71
|
+
percentage.forEach((item) => {
|
|
72
|
+
loaded += item;
|
|
73
|
+
});
|
|
74
|
+
e.percent = (loaded / option.file.size) * 100;
|
|
75
|
+
option.onProgress(e);
|
|
76
|
+
};
|
|
77
|
+
const xhrList = []; // 所有的xhr请求
|
|
78
|
+
function sendRequest(formDataList, limit) {
|
|
79
|
+
let counter = 0; //上传成功的数量
|
|
80
|
+
let index = 0; //当前上传文件的下标
|
|
81
|
+
let isStop = false;
|
|
82
|
+
const len = formDataList.length;
|
|
83
|
+
const start = async () => {
|
|
84
|
+
if (isStop) {
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
87
|
+
const item = formDataList.shift();
|
|
88
|
+
if (item) {
|
|
89
|
+
const chunkIndex = index++;
|
|
90
|
+
const xhr = new XMLHttpRequest();
|
|
91
|
+
// 分片上传失败回调
|
|
92
|
+
xhr.onerror = function error() {
|
|
93
|
+
isStop = true;
|
|
94
|
+
option.onerror(getError(action, option, xhr));
|
|
95
|
+
};
|
|
96
|
+
// 分片上传成功回调
|
|
97
|
+
xhr.onload = function onload() {
|
|
98
|
+
if (xhr.status < 200 || xhr.status >= 300) {
|
|
99
|
+
isStop = true;
|
|
100
|
+
option.onerror(getError(action, option, xhr));
|
|
101
|
+
}
|
|
102
|
+
// 最后一个上传完成
|
|
103
|
+
if (counter === len - 1) {
|
|
104
|
+
const result = xhrList.map((item) => getBody(item));
|
|
105
|
+
option.onSuccess(result);
|
|
106
|
+
} else {
|
|
107
|
+
counter++;
|
|
108
|
+
start();
|
|
109
|
+
}
|
|
110
|
+
};
|
|
111
|
+
// 上传中的时候更新进度条
|
|
112
|
+
if (xhr.upload) {
|
|
113
|
+
xhr.upload.onprogress = function progress(e) {
|
|
114
|
+
if (e.total > 0) {
|
|
115
|
+
e.percent = (e.loaded / e.total) * 100;
|
|
116
|
+
}
|
|
117
|
+
percentage[chunkIndex] = e.loaded;
|
|
118
|
+
updataPercentage(e);
|
|
119
|
+
};
|
|
120
|
+
}
|
|
121
|
+
if (action.slice(-1) == '/') {
|
|
122
|
+
xhr.open('post', action + chunkIndex, true);
|
|
123
|
+
} else {
|
|
124
|
+
xhr.open('post', action + '/' + chunkIndex, true);
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
if (option.withCredentials && 'withCredentials' in xhr) {
|
|
128
|
+
xhr.withCredentials = true;
|
|
129
|
+
}
|
|
130
|
+
const headers = option.headers || {};
|
|
131
|
+
// 添加请求头
|
|
132
|
+
for (const item in headers) {
|
|
133
|
+
if (Object.prototype.hasOwnProperty.call(headers, item) && headers[item] !== null) {
|
|
134
|
+
xhr.setRequestHeader(item, headers[item]);
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
// 文件开始上传,并把xhr对象存入xhrList中
|
|
138
|
+
xhr.send(item);
|
|
139
|
+
xhrList.push(xhr);
|
|
140
|
+
}
|
|
141
|
+
};
|
|
142
|
+
while (limit > 0) {
|
|
143
|
+
setTimeout(() => {
|
|
144
|
+
start();
|
|
145
|
+
}, Math.random() * 1000);
|
|
146
|
+
limit -= 1;
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
try {
|
|
151
|
+
sendRequest(formDataList, option.thread);
|
|
152
|
+
return xhrList;
|
|
153
|
+
} catch (error) {
|
|
154
|
+
option.onError(error);
|
|
155
|
+
}
|
|
156
|
+
}
|
|
@@ -0,0 +1,329 @@
|
|
|
1
|
+
<script>
|
|
2
|
+
import UploadList from './upload-list';
|
|
3
|
+
import Upload from './upload';
|
|
4
|
+
|
|
5
|
+
function noop() {}
|
|
6
|
+
|
|
7
|
+
export default {
|
|
8
|
+
name: 'iipUpload',
|
|
9
|
+
|
|
10
|
+
components: {
|
|
11
|
+
UploadList,
|
|
12
|
+
Upload,
|
|
13
|
+
},
|
|
14
|
+
|
|
15
|
+
provide() {
|
|
16
|
+
return {
|
|
17
|
+
uploader: this,
|
|
18
|
+
};
|
|
19
|
+
},
|
|
20
|
+
|
|
21
|
+
inject: {
|
|
22
|
+
elForm: {
|
|
23
|
+
default: '',
|
|
24
|
+
},
|
|
25
|
+
},
|
|
26
|
+
|
|
27
|
+
props: {
|
|
28
|
+
chunkSize: {
|
|
29
|
+
type: Number,
|
|
30
|
+
default: 10 * 1024 * 1024,
|
|
31
|
+
},
|
|
32
|
+
thread: {
|
|
33
|
+
type: Number,
|
|
34
|
+
default: 3,
|
|
35
|
+
},
|
|
36
|
+
action: {
|
|
37
|
+
type: String,
|
|
38
|
+
required: true,
|
|
39
|
+
},
|
|
40
|
+
headers: {
|
|
41
|
+
type: Object,
|
|
42
|
+
default() {
|
|
43
|
+
return {};
|
|
44
|
+
},
|
|
45
|
+
},
|
|
46
|
+
data: Object,
|
|
47
|
+
multiple: Boolean,
|
|
48
|
+
name: {
|
|
49
|
+
type: String,
|
|
50
|
+
default: 'file',
|
|
51
|
+
},
|
|
52
|
+
drag: Boolean,
|
|
53
|
+
dragger: Boolean,
|
|
54
|
+
withCredentials: Boolean,
|
|
55
|
+
showFileList: {
|
|
56
|
+
type: Boolean,
|
|
57
|
+
default: true,
|
|
58
|
+
},
|
|
59
|
+
accept: String,
|
|
60
|
+
type: {
|
|
61
|
+
type: String,
|
|
62
|
+
default: 'select',
|
|
63
|
+
},
|
|
64
|
+
beforeUpload: Function,
|
|
65
|
+
beforeRemove: Function,
|
|
66
|
+
onRemove: {
|
|
67
|
+
type: Function,
|
|
68
|
+
default: noop,
|
|
69
|
+
},
|
|
70
|
+
onChange: {
|
|
71
|
+
type: Function,
|
|
72
|
+
default: noop,
|
|
73
|
+
},
|
|
74
|
+
onPreview: {
|
|
75
|
+
type: Function,
|
|
76
|
+
},
|
|
77
|
+
onSuccess: {
|
|
78
|
+
type: Function,
|
|
79
|
+
default: noop,
|
|
80
|
+
},
|
|
81
|
+
onProgress: {
|
|
82
|
+
type: Function,
|
|
83
|
+
default: noop,
|
|
84
|
+
},
|
|
85
|
+
onError: {
|
|
86
|
+
type: Function,
|
|
87
|
+
default: noop,
|
|
88
|
+
},
|
|
89
|
+
fileList: {
|
|
90
|
+
type: Array,
|
|
91
|
+
default() {
|
|
92
|
+
return [];
|
|
93
|
+
},
|
|
94
|
+
},
|
|
95
|
+
autoUpload: {
|
|
96
|
+
type: Boolean,
|
|
97
|
+
default: true,
|
|
98
|
+
},
|
|
99
|
+
listType: {
|
|
100
|
+
type: String,
|
|
101
|
+
default: 'text', // text,picture,picture-card
|
|
102
|
+
},
|
|
103
|
+
httpRequest: Function,
|
|
104
|
+
disabled: Boolean,
|
|
105
|
+
limit: Number,
|
|
106
|
+
onExceed: {
|
|
107
|
+
type: Function,
|
|
108
|
+
default: noop,
|
|
109
|
+
},
|
|
110
|
+
},
|
|
111
|
+
|
|
112
|
+
data() {
|
|
113
|
+
return {
|
|
114
|
+
uploadFiles: [],
|
|
115
|
+
dragOver: false,
|
|
116
|
+
draging: false,
|
|
117
|
+
tempIndex: 1,
|
|
118
|
+
};
|
|
119
|
+
},
|
|
120
|
+
|
|
121
|
+
computed: {
|
|
122
|
+
uploadDisabled() {
|
|
123
|
+
return this.disabled || (this.elForm || {}).disabled;
|
|
124
|
+
},
|
|
125
|
+
},
|
|
126
|
+
|
|
127
|
+
watch: {
|
|
128
|
+
listType(type) {
|
|
129
|
+
if (type === 'picture-card' || type === 'picture') {
|
|
130
|
+
this.uploadFiles = this.uploadFiles.map((file) => {
|
|
131
|
+
if (!file.url && file.raw) {
|
|
132
|
+
try {
|
|
133
|
+
file.url = URL.createObjectURL(file.raw);
|
|
134
|
+
} catch (err) {
|
|
135
|
+
console.error('[Element Error][Upload]', err);
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
return file;
|
|
139
|
+
});
|
|
140
|
+
}
|
|
141
|
+
},
|
|
142
|
+
fileList: {
|
|
143
|
+
immediate: true,
|
|
144
|
+
handler(fileList) {
|
|
145
|
+
this.uploadFiles = fileList.map((item) => {
|
|
146
|
+
item.uid = item.uid || Date.now() + this.tempIndex++;
|
|
147
|
+
item.status = item.status || 'success';
|
|
148
|
+
return item;
|
|
149
|
+
});
|
|
150
|
+
},
|
|
151
|
+
},
|
|
152
|
+
},
|
|
153
|
+
|
|
154
|
+
methods: {
|
|
155
|
+
handleStart(rawFile) {
|
|
156
|
+
rawFile.uid = Date.now() + this.tempIndex++;
|
|
157
|
+
let file = {
|
|
158
|
+
status: 'ready',
|
|
159
|
+
name: rawFile.name,
|
|
160
|
+
size: rawFile.size,
|
|
161
|
+
percentage: 0,
|
|
162
|
+
uid: rawFile.uid,
|
|
163
|
+
raw: rawFile,
|
|
164
|
+
};
|
|
165
|
+
|
|
166
|
+
if (this.listType === 'picture-card' || this.listType === 'picture') {
|
|
167
|
+
try {
|
|
168
|
+
file.url = URL.createObjectURL(rawFile);
|
|
169
|
+
} catch (err) {
|
|
170
|
+
console.error('[Element Error][Upload]', err);
|
|
171
|
+
return;
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
this.uploadFiles.push(file);
|
|
176
|
+
this.onChange(file, this.uploadFiles);
|
|
177
|
+
},
|
|
178
|
+
handleProgress(ev, rawFile) {
|
|
179
|
+
const file = this.getFile(rawFile);
|
|
180
|
+
this.onProgress(ev, file, this.uploadFiles);
|
|
181
|
+
file.status = 'uploading';
|
|
182
|
+
file.percentage = ev.percent || 0;
|
|
183
|
+
},
|
|
184
|
+
handleSuccess(res, rawFile) {
|
|
185
|
+
const file = this.getFile(rawFile);
|
|
186
|
+
|
|
187
|
+
if (file) {
|
|
188
|
+
file.status = 'success';
|
|
189
|
+
file.response = res;
|
|
190
|
+
|
|
191
|
+
this.onSuccess(res, file, this.uploadFiles);
|
|
192
|
+
this.onChange(file, this.uploadFiles);
|
|
193
|
+
}
|
|
194
|
+
},
|
|
195
|
+
handleError(err, rawFile) {
|
|
196
|
+
const file = this.getFile(rawFile);
|
|
197
|
+
const fileList = this.uploadFiles;
|
|
198
|
+
|
|
199
|
+
file.status = 'fail';
|
|
200
|
+
|
|
201
|
+
fileList.splice(fileList.indexOf(file), 1);
|
|
202
|
+
|
|
203
|
+
this.onError(err, file, this.uploadFiles);
|
|
204
|
+
this.onChange(file, this.uploadFiles);
|
|
205
|
+
},
|
|
206
|
+
handleRemove(file, raw) {
|
|
207
|
+
if (raw) {
|
|
208
|
+
file = this.getFile(raw);
|
|
209
|
+
}
|
|
210
|
+
let doRemove = () => {
|
|
211
|
+
this.abort(file);
|
|
212
|
+
let fileList = this.uploadFiles;
|
|
213
|
+
fileList.splice(fileList.indexOf(file), 1);
|
|
214
|
+
this.onRemove(file, fileList);
|
|
215
|
+
};
|
|
216
|
+
|
|
217
|
+
if (!this.beforeRemove) {
|
|
218
|
+
doRemove();
|
|
219
|
+
} else if (typeof this.beforeRemove === 'function') {
|
|
220
|
+
const before = this.beforeRemove(file, this.uploadFiles);
|
|
221
|
+
if (before && before.then) {
|
|
222
|
+
before.then(() => {
|
|
223
|
+
doRemove();
|
|
224
|
+
}, noop);
|
|
225
|
+
} else if (before !== false) {
|
|
226
|
+
doRemove();
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
},
|
|
230
|
+
getFile(rawFile) {
|
|
231
|
+
let fileList = this.uploadFiles;
|
|
232
|
+
let target;
|
|
233
|
+
fileList.every((item) => {
|
|
234
|
+
target = rawFile.uid === item.uid ? item : null;
|
|
235
|
+
return !target;
|
|
236
|
+
});
|
|
237
|
+
return target;
|
|
238
|
+
},
|
|
239
|
+
abort(file) {
|
|
240
|
+
this.$refs['upload-inner'].abort(file);
|
|
241
|
+
},
|
|
242
|
+
clearFiles() {
|
|
243
|
+
this.uploadFiles = [];
|
|
244
|
+
},
|
|
245
|
+
submit() {
|
|
246
|
+
this.uploadFiles
|
|
247
|
+
.filter((file) => file.status === 'ready')
|
|
248
|
+
.forEach((file) => {
|
|
249
|
+
this.$refs['upload-inner'].upload(file.raw);
|
|
250
|
+
});
|
|
251
|
+
},
|
|
252
|
+
},
|
|
253
|
+
|
|
254
|
+
beforeDestroy() {
|
|
255
|
+
this.uploadFiles.forEach((file) => {
|
|
256
|
+
if (file.url && file.url.indexOf('blob:') === 0) {
|
|
257
|
+
URL.revokeObjectURL(file.url);
|
|
258
|
+
}
|
|
259
|
+
});
|
|
260
|
+
},
|
|
261
|
+
|
|
262
|
+
render(h) {
|
|
263
|
+
let uploadList;
|
|
264
|
+
|
|
265
|
+
if (this.showFileList) {
|
|
266
|
+
uploadList = (
|
|
267
|
+
<UploadList
|
|
268
|
+
disabled={this.uploadDisabled}
|
|
269
|
+
listType={this.listType}
|
|
270
|
+
files={this.uploadFiles}
|
|
271
|
+
on-remove={this.handleRemove}
|
|
272
|
+
handlePreview={this.onPreview}
|
|
273
|
+
>
|
|
274
|
+
{(props) => {
|
|
275
|
+
if (this.$scopedSlots.file) {
|
|
276
|
+
return this.$scopedSlots.file({
|
|
277
|
+
file: props.file,
|
|
278
|
+
});
|
|
279
|
+
}
|
|
280
|
+
}}
|
|
281
|
+
</UploadList>
|
|
282
|
+
);
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
const uploadData = {
|
|
286
|
+
props: {
|
|
287
|
+
type: this.type,
|
|
288
|
+
drag: this.drag,
|
|
289
|
+
action: this.action,
|
|
290
|
+
multiple: this.multiple,
|
|
291
|
+
'before-upload': this.beforeUpload,
|
|
292
|
+
'with-credentials': this.withCredentials,
|
|
293
|
+
headers: this.headers,
|
|
294
|
+
name: this.name,
|
|
295
|
+
data: this.data,
|
|
296
|
+
accept: this.accept,
|
|
297
|
+
fileList: this.uploadFiles,
|
|
298
|
+
autoUpload: this.autoUpload,
|
|
299
|
+
listType: this.listType,
|
|
300
|
+
disabled: this.uploadDisabled,
|
|
301
|
+
limit: this.limit,
|
|
302
|
+
chunkSize: this.chunkSize,
|
|
303
|
+
thread: this.thread,
|
|
304
|
+
'on-exceed': this.onExceed,
|
|
305
|
+
'on-start': this.handleStart,
|
|
306
|
+
'on-progress': this.handleProgress,
|
|
307
|
+
'on-success': this.handleSuccess,
|
|
308
|
+
'on-error': this.handleError,
|
|
309
|
+
'on-preview': this.onPreview,
|
|
310
|
+
'on-remove': this.handleRemove,
|
|
311
|
+
'http-request': this.httpRequest,
|
|
312
|
+
},
|
|
313
|
+
ref: 'upload-inner',
|
|
314
|
+
};
|
|
315
|
+
|
|
316
|
+
const trigger = this.$slots.trigger || this.$slots.default;
|
|
317
|
+
const uploadComponent = <upload {...uploadData}>{trigger}</upload>;
|
|
318
|
+
|
|
319
|
+
return (
|
|
320
|
+
<div>
|
|
321
|
+
{this.listType === 'picture-card' ? uploadList : ''}
|
|
322
|
+
{this.$slots.trigger ? [uploadComponent, this.$slots.default] : uploadComponent}
|
|
323
|
+
{this.$slots.tip}
|
|
324
|
+
{this.listType !== 'picture-card' ? uploadList : ''}
|
|
325
|
+
</div>
|
|
326
|
+
);
|
|
327
|
+
},
|
|
328
|
+
};
|
|
329
|
+
</script>
|