@skyfox2000/webui 1.4.9 → 1.4.11
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/assets/modules/{baseLayout-DIny49tw.js → baseLayout-DSVT_hCt.js} +3 -3
- package/lib/assets/modules/{file-upload-DHUBZlX4.js → file-upload-T2kmeyGd.js} +59 -45
- package/lib/assets/modules/{index-CEffEFtI.js → index-C6BAsERS.js} +1 -1
- package/lib/assets/modules/{index-DyRjXb_O.js → index-Cwr2EDEI.js} +2 -2
- package/lib/assets/modules/{index-BIAdOoSj.js → index-DkMjvF98.js} +10 -10
- package/lib/assets/modules/{menuTabs-CCrFWIOl.js → menuTabs-D99nhxm_.js} +2 -2
- package/lib/assets/modules/{toolIcon-BGZR_aUI.js → toolIcon-DDp0EFRE.js} +1 -1
- package/lib/assets/modules/{uploadList-CMXuGRWT.js → upload-template-BK8iQQYz.js} +273 -263
- package/lib/assets/modules/uploadList-B8p45yS2.js +423 -0
- package/lib/es/AceEditor/index.js +3 -3
- package/lib/es/BasicLayout/index.js +2 -2
- package/lib/es/Error403/index.js +1 -1
- package/lib/es/Error404/index.js +1 -1
- package/lib/es/ExcelForm/index.js +16 -15
- package/lib/es/MenuLayout/index.js +2 -2
- package/lib/es/TemplateFile/index.js +62 -90
- package/lib/es/UploadForm/index.js +4 -4
- package/lib/index.d.ts +1 -0
- package/lib/utils/file-upload.d.ts +2 -1
- package/lib/utils/upload-template.d.ts +4 -0
- package/lib/webui.css +1 -1
- package/lib/webui.es.js +52 -50
- package/package.json +1 -1
- package/src/components/content/dialog/excelForm.vue +5 -0
- package/src/components/content/dialog/templateFile.vue +27 -79
- package/src/components/content/table/index.vue +1 -0
- package/src/components/form/upload/uploadList.vue +122 -40
- package/src/index.ts +2 -0
- package/src/utils/file-upload.ts +31 -1
- package/src/utils/upload-template.ts +15 -0
- package/lib/assets/modules/uploadList-l4q5o65m.js +0 -400
|
@@ -2,10 +2,10 @@
|
|
|
2
2
|
import { watch, ref, onMounted, PropType } from 'vue';
|
|
3
3
|
import { Button } from '../../common';
|
|
4
4
|
import { Modal, Space } from 'ant-design-vue';
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
5
|
+
import { MinioFile, UploadFile, UploadStatus } from '@/index';
|
|
6
|
+
import { httpPost, IUrlInfo, ResStatus } from '@skyfox2000/fapi';
|
|
7
7
|
import UploadList from '../../form/upload/uploadList.vue';
|
|
8
|
-
import {
|
|
8
|
+
import { uploadTempOpener } from '../../../utils/upload-template';
|
|
9
9
|
|
|
10
10
|
const props = defineProps({
|
|
11
11
|
/**
|
|
@@ -33,10 +33,10 @@ const props = defineProps({
|
|
|
33
33
|
/**
|
|
34
34
|
* 弹窗控制器
|
|
35
35
|
*/
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
},
|
|
36
|
+
// open: {
|
|
37
|
+
// type: Boolean as PropType<boolean>,
|
|
38
|
+
// required: true,
|
|
39
|
+
// },
|
|
40
40
|
/**
|
|
41
41
|
* 文件列表地址
|
|
42
42
|
*/
|
|
@@ -81,59 +81,44 @@ const props = defineProps({
|
|
|
81
81
|
required: false,
|
|
82
82
|
},
|
|
83
83
|
});
|
|
84
|
+
|
|
85
|
+
const api = props.api ?? "FILE_API";
|
|
84
86
|
/**
|
|
85
87
|
* minio桶名称template
|
|
86
88
|
*
|
|
87
89
|
* 子目录templateType
|
|
88
90
|
*/
|
|
89
91
|
const fileListUrl = ref(props.fileListUrl || {
|
|
90
|
-
api:
|
|
92
|
+
api: api,
|
|
91
93
|
authorize: true,
|
|
92
94
|
url: '/api/TemplateFileSrv/list',
|
|
93
95
|
});
|
|
94
96
|
const uploadUrl = ref(props.uploadUrl || {
|
|
95
|
-
api:
|
|
97
|
+
api: api,
|
|
96
98
|
authorize: true,
|
|
97
99
|
url: '/api/TemplateFileSrv/upload',
|
|
98
100
|
});
|
|
99
101
|
const downloadUrl = ref(props.downloadUrl || {
|
|
100
|
-
api:
|
|
102
|
+
api: api,
|
|
101
103
|
authorize: true,
|
|
102
104
|
url: '/api/TemplateFileSrv/download',
|
|
103
105
|
});
|
|
104
106
|
const deleteUrl = ref(props.deleteUrl || {
|
|
105
|
-
api:
|
|
107
|
+
api: api,
|
|
106
108
|
authorize: true,
|
|
107
109
|
url: '/api/TemplateFileSrv/delete',
|
|
108
110
|
});
|
|
109
111
|
|
|
110
|
-
const uploadFormCtrl = props.uploadForm;
|
|
111
112
|
const open = ref<boolean>(false);
|
|
112
113
|
|
|
113
114
|
const fileList = ref<UploadFile[]>([]);
|
|
114
|
-
const emit = defineEmits<{
|
|
115
|
-
|
|
116
|
-
* 显示预处理
|
|
117
|
-
*/
|
|
118
|
-
'before:file-list': [EditorControl<AnyData>, UploadFile[]];
|
|
119
|
-
/**
|
|
120
|
-
* 上传前预处理
|
|
121
|
-
*/
|
|
122
|
-
'before:upload': [UploadFile[]];
|
|
123
|
-
/**
|
|
124
|
-
* 上传结束,处理上传后的文件
|
|
125
|
-
*/
|
|
126
|
-
'after:upload': [boolean, UploadFile[]];
|
|
127
|
-
/**
|
|
128
|
-
* 上传进度
|
|
129
|
-
*/
|
|
130
|
-
'on:progress': [UploadFile];
|
|
131
|
-
}>();
|
|
115
|
+
// const emit = defineEmits<{
|
|
116
|
+
// }>();
|
|
132
117
|
|
|
133
118
|
watch(
|
|
134
|
-
() =>
|
|
119
|
+
() => uploadTempOpener.value,
|
|
135
120
|
() => {
|
|
136
|
-
open.value =
|
|
121
|
+
open.value = uploadTempOpener.value;
|
|
137
122
|
},
|
|
138
123
|
);
|
|
139
124
|
|
|
@@ -142,14 +127,10 @@ watch(
|
|
|
142
127
|
* 加载现有模板文件列表
|
|
143
128
|
*/
|
|
144
129
|
const loadFileList = async () => {
|
|
145
|
-
const res
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
Query: {
|
|
150
|
-
PrefixPath: props.templateType,
|
|
151
|
-
}
|
|
152
|
-
},
|
|
130
|
+
const res = await httpPost<MinioFile[]>(fileListUrl.value, {
|
|
131
|
+
Query: {
|
|
132
|
+
PrefixPath: props.templateType,
|
|
133
|
+
}
|
|
153
134
|
});
|
|
154
135
|
if (res?.status === ResStatus.SUCCESS && res.data) {
|
|
155
136
|
const files = res.data as MinioFile[];
|
|
@@ -166,66 +147,33 @@ const loadFileList = async () => {
|
|
|
166
147
|
}
|
|
167
148
|
return uploadFile;
|
|
168
149
|
})
|
|
169
|
-
emit('before:file-list', uploadFormCtrl, list);
|
|
150
|
+
// emit('before:file-list', uploadFormCtrl, list);
|
|
170
151
|
fileList.value.push(...list);
|
|
171
152
|
}
|
|
172
153
|
};
|
|
173
154
|
|
|
174
|
-
const dialogSave = async () => {
|
|
175
|
-
emit('before:upload', fileList.value);
|
|
176
|
-
const uploader = new AsyncUploader(uploadUrl.value, 3);
|
|
177
|
-
await uploader.doUpload(
|
|
178
|
-
fileList.value,
|
|
179
|
-
uploadFormCtrl.isFormLoading,
|
|
180
|
-
true,
|
|
181
|
-
async (result, files) => {
|
|
182
|
-
emit('after:upload', result, files);
|
|
183
|
-
if (result) {
|
|
184
|
-
uploadFormCtrl.visible.value = false;
|
|
185
|
-
}
|
|
186
|
-
},
|
|
187
|
-
(file) => {
|
|
188
|
-
emit('on:progress', file);
|
|
189
|
-
},
|
|
190
|
-
);
|
|
191
|
-
};
|
|
192
|
-
|
|
193
155
|
onMounted(() => {
|
|
194
|
-
if (!fileListUrl.value.api) {
|
|
195
|
-
fileListUrl.value.api = uploadFormCtrl.page.api;
|
|
196
|
-
}
|
|
197
|
-
if (!uploadUrl.value.api) {
|
|
198
|
-
uploadUrl.value.api = uploadFormCtrl.page.api;
|
|
199
|
-
}
|
|
200
|
-
if (!downloadUrl.value.api) {
|
|
201
|
-
downloadUrl.value.api = uploadFormCtrl.page.api;
|
|
202
|
-
}
|
|
203
|
-
if (!deleteUrl.value.api) {
|
|
204
|
-
deleteUrl.value.api = uploadFormCtrl.page.api;
|
|
205
|
-
}
|
|
206
156
|
loadFileList();
|
|
207
157
|
|
|
208
|
-
open.value =
|
|
158
|
+
open.value = uploadTempOpener.value;
|
|
209
159
|
});
|
|
210
160
|
|
|
211
161
|
const dialogClose = () => {
|
|
212
|
-
|
|
162
|
+
open.value = false;
|
|
163
|
+
uploadTempOpener.value = false;
|
|
213
164
|
};
|
|
214
165
|
</script>
|
|
215
166
|
<template>
|
|
216
167
|
<Modal title="模板文件管理" v-model:open="open"
|
|
217
168
|
:wrapClassName="'modal mx-auto ' + ($attrs.width ? 'w-[' + $attrs.width + ']' : 'w-[500px]')"
|
|
218
169
|
@close="dialogClose">
|
|
219
|
-
|
|
170
|
+
<!-- 改为选择后自动上传 -->
|
|
171
|
+
<UploadList v-model:file-list="fileList" auto-upload :parent-path="props.templateType" :upload-url="uploadUrl"
|
|
220
172
|
:download-url="downloadUrl" :delete-url="deleteUrl" :max-count="maxCount" :file-ext="fileExt"
|
|
221
173
|
:show-delete="showDelete" />
|
|
222
174
|
<template #footer>
|
|
223
175
|
<Space>
|
|
224
176
|
<Button @click="dialogClose">关闭</Button>
|
|
225
|
-
<Button @click="dialogSave" type="primary" :loading="uploadFormCtrl.isFormSaving.value"
|
|
226
|
-
:disabled="fileList.length === 0">
|
|
227
|
-
上传保存
|
|
228
|
-
</Button>
|
|
229
177
|
</Space>
|
|
230
178
|
</template>
|
|
231
179
|
</Modal>
|
|
@@ -7,6 +7,7 @@ import { Upload, Progress, Tag, Popconfirm } from 'ant-design-vue';
|
|
|
7
7
|
import { UploadFile, UploadStatus, donwloadFromMinio, path, Switch, previewFromMinio } from '@/index';
|
|
8
8
|
import { useInputFactory } from '@/utils/form-validate';
|
|
9
9
|
import { ApiResponse, httpPost, IUrlInfo, ResStatus } from '@skyfox2000/fapi';
|
|
10
|
+
import { fastUpload } from '@/utils/file-upload';
|
|
10
11
|
|
|
11
12
|
export interface UploadListProps {
|
|
12
13
|
/**
|
|
@@ -97,13 +98,19 @@ const props = withDefaults(defineProps<UploadListProps>(), {
|
|
|
97
98
|
const inputFactory = useInputFactory();
|
|
98
99
|
const { errInfo } = inputFactory;
|
|
99
100
|
|
|
100
|
-
|
|
101
|
+
|
|
102
|
+
// Upload 组件的文件列表更新
|
|
103
|
+
const displayList = ref<UploadFile[]>(props.fileList);
|
|
104
|
+
// Upload 组件的内部文件列表
|
|
105
|
+
const uploadList = ref<UploadFile[]>([]);
|
|
106
|
+
|
|
101
107
|
const fileUploader = ref();
|
|
102
108
|
const emit = defineEmits(['update:file-list']);
|
|
103
|
-
const confirmOpen = ref(false);
|
|
104
109
|
const acceptString = computed(() => (props.fileExt?.length ? props.fileExt.map((ext) => `.${ext}`).join(',') : ''));
|
|
105
110
|
|
|
106
|
-
|
|
111
|
+
// 统一的文件验证函数
|
|
112
|
+
const validateFile = (file: File): boolean => {
|
|
113
|
+
// 文件类型验证
|
|
107
114
|
if (props.fileExt && props.fileExt.length > 0) {
|
|
108
115
|
const extension = file.name.split('.').pop()?.toLowerCase() || '';
|
|
109
116
|
if (!props.fileExt.includes(extension)) {
|
|
@@ -112,43 +119,114 @@ const beforeUpload: UploadProps['beforeUpload'] = (file) => {
|
|
|
112
119
|
}
|
|
113
120
|
}
|
|
114
121
|
|
|
122
|
+
// 文件大小验证
|
|
115
123
|
if (file.size / 1024 / 1024 > props.maxFileSize) {
|
|
116
124
|
message.error(`文件大小超过 ${props.maxFileSize}MB 限制`);
|
|
117
125
|
return false;
|
|
118
126
|
}
|
|
119
127
|
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
128
|
+
return true;
|
|
129
|
+
};
|
|
130
|
+
|
|
131
|
+
// 检查是否达到最大数量限制
|
|
132
|
+
const isMaxCountReached = (): boolean => {
|
|
133
|
+
return props.maxCount > 1 && displayList.value.length >= props.maxCount;
|
|
134
|
+
};
|
|
135
|
+
|
|
136
|
+
// 准备文件信息
|
|
137
|
+
const prepareFileInfo = (file: any): UploadFile<any> => {
|
|
138
|
+
const fileInfo = file as UploadFile<any>;
|
|
139
|
+
if (!fileInfo.params) fileInfo.params = {};
|
|
140
|
+
fileInfo.params.FileKey = fileInfo.name;
|
|
141
|
+
if (props.parentPath) {
|
|
142
|
+
fileInfo.params.FileKey = path.join('/', props.parentPath, fileInfo.name);
|
|
123
143
|
}
|
|
144
|
+
fileInfo.status = UploadStatus.Pending;
|
|
145
|
+
return fileInfo;
|
|
146
|
+
};
|
|
124
147
|
|
|
125
|
-
|
|
126
|
-
|
|
148
|
+
// 简化的 beforeUpload - 只做基本验证,不处理业务逻辑
|
|
149
|
+
const beforeUpload: UploadProps['beforeUpload'] = (file) => {
|
|
150
|
+
return validateFile(file) && props.autoUpload;
|
|
127
151
|
};
|
|
128
152
|
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
153
|
+
// 处理文件选择(核心逻辑)
|
|
154
|
+
const handleFileSelect = async (newFiles: any[]) => {
|
|
155
|
+
if (newFiles.length === 0) return;
|
|
156
|
+
|
|
157
|
+
const tempFiles = [...displayList.value];
|
|
158
|
+
let hasError = false;
|
|
159
|
+
|
|
160
|
+
for (const file of newFiles) {
|
|
161
|
+
// 验证文件
|
|
162
|
+
if (!validateFile(file)) {
|
|
163
|
+
hasError = true;
|
|
164
|
+
continue;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
const fileInfo = prepareFileInfo(file);
|
|
168
|
+
|
|
169
|
+
// 如果maxCount=1,直接替换
|
|
170
|
+
if (props.maxCount === 1) {
|
|
171
|
+
tempFiles.length = 0;
|
|
172
|
+
tempFiles.push(fileInfo);
|
|
173
|
+
break;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
// 检查是否达到最大数量限制
|
|
177
|
+
if (isMaxCountReached()) {
|
|
178
|
+
message.error(`最多上传 ${props.maxCount} 个文件`);
|
|
179
|
+
hasError = true;
|
|
180
|
+
break;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
// 检查是否为同名文件(严格判断)
|
|
184
|
+
const existingIndex = tempFiles.findIndex(f =>
|
|
185
|
+
(f.name === fileInfo.name)
|
|
186
|
+
);
|
|
187
|
+
|
|
188
|
+
if (existingIndex > -1) {
|
|
189
|
+
// 同名文件:替换
|
|
190
|
+
tempFiles[existingIndex] = fileInfo;
|
|
191
|
+
} else {
|
|
192
|
+
// 新文件:添加到列表
|
|
193
|
+
tempFiles.push(fileInfo);
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
// 更新显示列表
|
|
198
|
+
if (!hasError || tempFiles.length > 0) {
|
|
199
|
+
displayList.value = tempFiles;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
// 清空 Upload 组件的内部列表
|
|
203
|
+
uploadList.value = [];
|
|
204
|
+
};
|
|
205
|
+
|
|
206
|
+
// Upload 组件的文件列表更新
|
|
207
|
+
const updateUploadList: UploadProps['onUpdate:fileList'] = (newFiles) => {
|
|
208
|
+
// 只更新 Upload 内部列表
|
|
209
|
+
uploadList.value = newFiles as unknown as UploadFile[];
|
|
210
|
+
|
|
211
|
+
// 处理新选择的文件
|
|
212
|
+
handleFileSelect(newFiles);
|
|
138
213
|
};
|
|
139
214
|
|
|
140
215
|
const uploadProps = computed<UploadProps>(() => ({
|
|
141
216
|
accept: acceptString.value,
|
|
142
|
-
multiple:
|
|
143
|
-
fileList:
|
|
144
|
-
'onUpdate:fileList':
|
|
217
|
+
multiple: props.maxCount !== 1,
|
|
218
|
+
fileList: uploadList.value as UploadProps['fileList'],
|
|
219
|
+
'onUpdate:fileList': updateUploadList,
|
|
145
220
|
beforeUpload: beforeUpload,
|
|
146
221
|
listType: 'text',
|
|
147
|
-
maxCount: props.maxCount,
|
|
148
222
|
showUploadList: false,
|
|
149
|
-
|
|
150
|
-
if (
|
|
151
|
-
|
|
223
|
+
customRequest: async () => {
|
|
224
|
+
if (props.autoUpload && props.uploadUrl) {
|
|
225
|
+
for (const file of displayList.value) {
|
|
226
|
+
if (file.percent === 0 && file.status === UploadStatus.Pending) {
|
|
227
|
+
await fastUpload(props.uploadUrl, file);
|
|
228
|
+
}
|
|
229
|
+
}
|
|
152
230
|
}
|
|
153
231
|
},
|
|
154
232
|
}));
|
|
@@ -156,13 +234,13 @@ const uploadProps = computed<UploadProps>(() => ({
|
|
|
156
234
|
watch(
|
|
157
235
|
() => props.fileList,
|
|
158
236
|
(newVal) => {
|
|
159
|
-
|
|
237
|
+
displayList.value = newVal;
|
|
160
238
|
},
|
|
161
239
|
{ deep: true, immediate: true },
|
|
162
240
|
);
|
|
163
241
|
|
|
164
242
|
watch(
|
|
165
|
-
() =>
|
|
243
|
+
() => displayList.value,
|
|
166
244
|
(newVal) => {
|
|
167
245
|
emit('update:file-list', newVal);
|
|
168
246
|
},
|
|
@@ -173,7 +251,7 @@ watch(
|
|
|
173
251
|
() => props.parentPath,
|
|
174
252
|
(newVal) => {
|
|
175
253
|
if (newVal) {
|
|
176
|
-
|
|
254
|
+
displayList.value.forEach((file) => {
|
|
177
255
|
file.params.FileKey = path.join('/', newVal, file.fileName);
|
|
178
256
|
});
|
|
179
257
|
}
|
|
@@ -181,7 +259,7 @@ watch(
|
|
|
181
259
|
);
|
|
182
260
|
|
|
183
261
|
const downloadFile = (index: number) => {
|
|
184
|
-
const minioFile =
|
|
262
|
+
const minioFile = displayList.value[index].minioFile!;
|
|
185
263
|
const url: IUrlInfo = {
|
|
186
264
|
api: props.downloadUrl!.api,
|
|
187
265
|
authorize: props.downloadUrl!.authorize,
|
|
@@ -203,7 +281,7 @@ const onlineOrOffline = (file: UploadFile) => {
|
|
|
203
281
|
};
|
|
204
282
|
|
|
205
283
|
const previewFile = (index: number) => {
|
|
206
|
-
const minioFile =
|
|
284
|
+
const minioFile = displayList.value[index].minioFile!;
|
|
207
285
|
const url: IUrlInfo = {
|
|
208
286
|
api: props.previewUrl!.api,
|
|
209
287
|
authorize: props.previewUrl!.authorize,
|
|
@@ -219,8 +297,8 @@ const previewFile = (index: number) => {
|
|
|
219
297
|
};
|
|
220
298
|
|
|
221
299
|
const removeFile = (index: number) => {
|
|
222
|
-
const file =
|
|
223
|
-
if (props.deleteUrl) {
|
|
300
|
+
const file = displayList.value[index];
|
|
301
|
+
if (props.deleteUrl && file.minioFile && file.minioFile.Key) {
|
|
224
302
|
httpPost<ApiResponse>(props.deleteUrl, {
|
|
225
303
|
Query: {
|
|
226
304
|
FileKey: file.minioFile!.Key,
|
|
@@ -228,23 +306,28 @@ const removeFile = (index: number) => {
|
|
|
228
306
|
}).then((res) => {
|
|
229
307
|
if (res && res.status === ResStatus.SUCCESS) {
|
|
230
308
|
message.success('删除文件成功!');
|
|
231
|
-
|
|
309
|
+
displayList.value.splice(index, 1);
|
|
232
310
|
}
|
|
233
311
|
});
|
|
234
312
|
} else {
|
|
235
|
-
|
|
313
|
+
displayList.value.splice(index, 1);
|
|
236
314
|
}
|
|
237
|
-
|
|
315
|
+
delIndex.value = -1;
|
|
238
316
|
};
|
|
239
317
|
|
|
318
|
+
const delIndex = ref(-1);
|
|
240
319
|
const confirmDelFile = (index: number, status?: UploadStatus) => {
|
|
320
|
+
delIndex.value = index;
|
|
241
321
|
if (status === UploadStatus.Pending) {
|
|
242
322
|
removeFile(index);
|
|
243
323
|
return;
|
|
244
324
|
}
|
|
245
|
-
confirmOpen.value = true;
|
|
246
325
|
};
|
|
247
326
|
|
|
327
|
+
const confirmIndex = (index: number) => {
|
|
328
|
+
return index === delIndex.value;
|
|
329
|
+
}
|
|
330
|
+
|
|
248
331
|
const getPlaceholder = (): string => {
|
|
249
332
|
const typeMsg =
|
|
250
333
|
props.fileExt && props.fileExt.length && props.fileExtTip ? `文件必须为 ${props.fileExt.join('/')}` : '';
|
|
@@ -301,11 +384,10 @@ const getStatus = (status?: UploadStatus) => {
|
|
|
301
384
|
<div class="flex-1 text-sm text-gray-500" :class="[errInfo?.errClass + '-text']">
|
|
302
385
|
{{ getPlaceholder() }}
|
|
303
386
|
</div>
|
|
304
|
-
<!-- <Button v-if="!autoUpload" @click="manualUpload" class="mr-3">开始上传</Button> -->
|
|
305
387
|
</div>
|
|
306
388
|
|
|
307
389
|
<div class="mt-4 px-3">
|
|
308
|
-
<div v-for="(file, index) in
|
|
390
|
+
<div v-for="(file, index) in displayList" :key="index" class="mb-2 pb-1">
|
|
309
391
|
<div class="flex items-center justify-between">
|
|
310
392
|
<div class="flex items-center">
|
|
311
393
|
<span class="text-gray-700 mr-2"
|
|
@@ -350,9 +432,9 @@ const getStatus = (status?: UploadStatus) => {
|
|
|
350
432
|
</div>
|
|
351
433
|
<div v-if="showDelete !== false"
|
|
352
434
|
class="flex items-center text-red-500 hover:text-red-700 cursor-pointer">
|
|
353
|
-
<Popconfirm
|
|
435
|
+
<Popconfirm :open="confirmIndex(index)" cancelText="否" okText="是" title="确定删除该文件吗?"
|
|
354
436
|
:okButtonProps="{ size: 'small' }" :cancelButtonProps="{ size: 'small' }"
|
|
355
|
-
@confirm="removeFile(index)" @cancel="
|
|
437
|
+
@confirm="removeFile(index)" @cancel="delIndex = -1">
|
|
356
438
|
<Tooltip title="删除">
|
|
357
439
|
<div @click="confirmDelFile(index, file.status)">
|
|
358
440
|
<ToolIcon icon="icon-new" :angle="45"
|
|
@@ -388,4 +470,4 @@ const getStatus = (status?: UploadStatus) => {
|
|
|
388
470
|
.error-text {
|
|
389
471
|
color: #ff4d4f !important;
|
|
390
472
|
}
|
|
391
|
-
</style>
|
|
473
|
+
</style>
|
package/src/index.ts
CHANGED
package/src/utils/file-upload.ts
CHANGED
|
@@ -111,7 +111,7 @@ export class AsyncUploader {
|
|
|
111
111
|
* @param onProgress 上传进度回调
|
|
112
112
|
* @param onComplete 上传完成回调
|
|
113
113
|
*/
|
|
114
|
-
|
|
114
|
+
public async uploadFiles(
|
|
115
115
|
fileList: UploadFile[],
|
|
116
116
|
onProgress?: (file: UploadFile) => void,
|
|
117
117
|
onComplete?: (files: UploadFile[]) => void,
|
|
@@ -329,6 +329,36 @@ export class AsyncUploader {
|
|
|
329
329
|
}
|
|
330
330
|
}
|
|
331
331
|
}
|
|
332
|
+
|
|
333
|
+
/**
|
|
334
|
+
* 单文件快速上传
|
|
335
|
+
* @param uploadUrl 上传地址
|
|
336
|
+
* @param file
|
|
337
|
+
* @returns
|
|
338
|
+
*/
|
|
339
|
+
export const fastUpload = async (uploadUrl: IUrlInfo, uploadFile: UploadFile): Promise<void> => {
|
|
340
|
+
const uploader = new AsyncUploader(uploadUrl!, 1);
|
|
341
|
+
const fileList = [uploadFile];
|
|
342
|
+
await uploader.uploadFiles(
|
|
343
|
+
fileList,
|
|
344
|
+
(file) => {
|
|
345
|
+
uploadFile.percent = file.percent;
|
|
346
|
+
},
|
|
347
|
+
(files) => {
|
|
348
|
+
const file = files[0];
|
|
349
|
+
uploadFile.percent = file.percent;
|
|
350
|
+
|
|
351
|
+
if (file.status === UploadStatus.Success) {
|
|
352
|
+
uploadFile.status = UploadStatus.Online;
|
|
353
|
+
message.success('文件上传成功!');
|
|
354
|
+
} else {
|
|
355
|
+
uploadFile.status = UploadStatus.Error;
|
|
356
|
+
message.error('上传文件失败!');
|
|
357
|
+
}
|
|
358
|
+
},
|
|
359
|
+
);
|
|
360
|
+
};
|
|
361
|
+
|
|
332
362
|
// // 使用示例
|
|
333
363
|
// (async () => {
|
|
334
364
|
// // 创建上传器实例,指定 API 配置和最大并发数
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { ButtonTool } from '@/typings/tools';
|
|
2
|
+
import { ref } from 'vue';
|
|
3
|
+
|
|
4
|
+
export const uploadTempOpener = ref<boolean>(false);
|
|
5
|
+
|
|
6
|
+
// 添加上传模板按钮
|
|
7
|
+
export const uploadTempBtn: ButtonTool = {
|
|
8
|
+
label: '模板管理',
|
|
9
|
+
key: 'uploadTemp',
|
|
10
|
+
type: 'primary',
|
|
11
|
+
icon: 'icon-download',
|
|
12
|
+
click: () => {
|
|
13
|
+
uploadTempOpener.value = true;
|
|
14
|
+
},
|
|
15
|
+
};
|