|
@@ -1,31 +1,44 @@
|
|
|
<template>
|
|
|
<BasicModal
|
|
|
+ width="800px"
|
|
|
+ title="上传"
|
|
|
+ okText="保存"
|
|
|
v-bind="$attrs"
|
|
|
@register="register"
|
|
|
@ok="handleOk"
|
|
|
:closeFunc="handleCloseFunc"
|
|
|
:maskClosable="false"
|
|
|
- width="800px"
|
|
|
- title="上传组件"
|
|
|
+ :keyboard="false"
|
|
|
wrapClassName="upload-modal"
|
|
|
- :okButtonProps="{ disabled: isUploadingRef }"
|
|
|
+ :okButtonProps="getOkButtonProps"
|
|
|
:cancelButtonProps="{ disabled: isUploadingRef }"
|
|
|
>
|
|
|
<template #centerdFooter>
|
|
|
- <a-button @click="handleStartUpload" color="success" :loading="isUploadingRef">
|
|
|
- {{ isUploadingRef ? '上传中' : '开始上传' }}
|
|
|
+ <a-button
|
|
|
+ @click="handleStartUpload"
|
|
|
+ color="success"
|
|
|
+ :disabled="!getIsSelectFile"
|
|
|
+ :loading="isUploadingRef"
|
|
|
+ >
|
|
|
+ {{ getUploadBtnText }}
|
|
|
</a-button>
|
|
|
</template>
|
|
|
- <Upload :accept="getStringAccept" :multiple="multiple" :before-upload="beforeUpload">
|
|
|
- <a-button type="primary"> 选择文件 </a-button>
|
|
|
- <span class="px-2">{{ getHelpText }}</span>
|
|
|
- </Upload>
|
|
|
- <BasicTable @register="registerTable" :dataSource="fileListRef" />
|
|
|
+
|
|
|
+ <BasicTable @register="registerTable" :dataSource="fileListRef">
|
|
|
+ <template #toolbar>
|
|
|
+ <Upload :accept="getStringAccept" :multiple="multiple" :before-upload="beforeUpload">
|
|
|
+ <a-button type="primary"> 选择文件 </a-button>
|
|
|
+ </Upload>
|
|
|
+ </template>
|
|
|
+ <template #tableTitle>
|
|
|
+ <Alert :message="getHelpText" type="info" banner></Alert>
|
|
|
+ </template>
|
|
|
+ </BasicTable>
|
|
|
</BasicModal>
|
|
|
</template>
|
|
|
<script lang="ts">
|
|
|
- import { defineComponent, reactive, ref, toRef, unref } from 'vue';
|
|
|
- import { Upload } from 'ant-design-vue';
|
|
|
+ import { defineComponent, reactive, ref, toRefs, unref, computed } from 'vue';
|
|
|
+ import { Upload, Alert } from 'ant-design-vue';
|
|
|
import { BasicModal, useModalInner } from '/@/components/Modal';
|
|
|
import { BasicTable, useTable } from '/@/components/Table';
|
|
|
// hooks
|
|
@@ -39,23 +52,56 @@
|
|
|
import { checkFileType, checkImgType, getBase64WithFile } from './utils';
|
|
|
import { buildUUID } from '/@/utils/uuid';
|
|
|
import { createImgPreview } from '/@/components/Preview/index';
|
|
|
- import { uploadApi } from '/@/api/demo/upload';
|
|
|
+ import { uploadApi } from '/@/api/sys/upload';
|
|
|
+ import { isFunction } from '/@/utils/is';
|
|
|
+ import { warn } from '/@/utils/log';
|
|
|
|
|
|
export default defineComponent({
|
|
|
- components: { BasicModal, Upload, BasicTable },
|
|
|
+ components: { BasicModal, Upload, BasicTable, Alert },
|
|
|
props: basicProps,
|
|
|
setup(props, { emit }) {
|
|
|
+ // 是否正在上传
|
|
|
+ const isUploadingRef = ref(false);
|
|
|
+ const fileListRef = ref<FileItem[]>([]);
|
|
|
+ const state = reactive<{ fileList: FileItem[] }>({
|
|
|
+ fileList: [],
|
|
|
+ });
|
|
|
+
|
|
|
const [register, { closeModal }] = useModalInner();
|
|
|
+
|
|
|
+ const { accept, helpText, maxNumber, maxSize } = toRefs(props);
|
|
|
const { getAccept, getStringAccept, getHelpText } = useUploadType({
|
|
|
- acceptRef: toRef(props, 'accept'),
|
|
|
- helpTextRef: toRef(props, 'helpText'),
|
|
|
- maxNumberRef: toRef(props, 'maxNumber'),
|
|
|
- maxSizeRef: toRef(props, 'maxSize'),
|
|
|
+ acceptRef: accept,
|
|
|
+ helpTextRef: helpText,
|
|
|
+ maxNumberRef: maxNumber,
|
|
|
+ maxSizeRef: maxSize,
|
|
|
});
|
|
|
|
|
|
- const fileListRef = ref<FileItem[]>([]);
|
|
|
- const state = reactive<{ fileList: FileItem[] }>({ fileList: [] });
|
|
|
const { createMessage } = useMessage();
|
|
|
+
|
|
|
+ const getIsSelectFile = computed(() => {
|
|
|
+ return (
|
|
|
+ fileListRef.value.length > 0 &&
|
|
|
+ !fileListRef.value.every((item) => item.status === UploadResultStatus.SUCCESS)
|
|
|
+ );
|
|
|
+ });
|
|
|
+
|
|
|
+ const getOkButtonProps = computed(() => {
|
|
|
+ const someSuccess = fileListRef.value.some(
|
|
|
+ (item) => item.status === UploadResultStatus.SUCCESS
|
|
|
+ );
|
|
|
+ return {
|
|
|
+ disabled: isUploadingRef.value || fileListRef.value.length === 0 || !someSuccess,
|
|
|
+ };
|
|
|
+ });
|
|
|
+
|
|
|
+ const getUploadBtnText = computed(() => {
|
|
|
+ const someError = fileListRef.value.some(
|
|
|
+ (item) => item.status === UploadResultStatus.ERROR
|
|
|
+ );
|
|
|
+ return isUploadingRef.value ? '上传中' : someError ? '重新上传失败文件' : '开始上传';
|
|
|
+ });
|
|
|
+
|
|
|
// 上传前校验
|
|
|
function beforeUpload(file: File) {
|
|
|
const { size, name } = file;
|
|
@@ -73,6 +119,14 @@
|
|
|
createMessage.error!(`只能上传${accept.join(',')}格式文件`);
|
|
|
return false;
|
|
|
}
|
|
|
+ const commonItem = {
|
|
|
+ uuid: buildUUID(),
|
|
|
+ file,
|
|
|
+ size,
|
|
|
+ name,
|
|
|
+ percent: 0,
|
|
|
+ type: name.split('.').pop(),
|
|
|
+ };
|
|
|
// 生成图片缩略图
|
|
|
if (checkImgType(file)) {
|
|
|
// beforeUpload,如果异步会调用自带上传方法
|
|
@@ -81,29 +135,13 @@
|
|
|
fileListRef.value = [
|
|
|
...unref(fileListRef),
|
|
|
{
|
|
|
- uuid: buildUUID(),
|
|
|
- file,
|
|
|
thumbUrl,
|
|
|
- size,
|
|
|
- name,
|
|
|
- percent: 0,
|
|
|
- type: name.split('.').pop(),
|
|
|
+ ...commonItem,
|
|
|
},
|
|
|
];
|
|
|
});
|
|
|
} else {
|
|
|
- fileListRef.value = [
|
|
|
- ...unref(fileListRef),
|
|
|
- {
|
|
|
- uuid: buildUUID(),
|
|
|
-
|
|
|
- file,
|
|
|
- size,
|
|
|
- name,
|
|
|
- percent: 0,
|
|
|
- type: name.split('.').pop(),
|
|
|
- },
|
|
|
- ];
|
|
|
+ fileListRef.value = [...unref(fileListRef), commonItem];
|
|
|
}
|
|
|
return false;
|
|
|
}
|
|
@@ -112,6 +150,7 @@
|
|
|
const index = fileListRef.value.findIndex((item) => item.uuid === record.uuid);
|
|
|
index !== -1 && fileListRef.value.splice(index, 1);
|
|
|
}
|
|
|
+
|
|
|
// 预览
|
|
|
function handlePreview(record: FileItem) {
|
|
|
const { thumbUrl = '' } = record;
|
|
@@ -119,19 +158,18 @@
|
|
|
imageList: [thumbUrl],
|
|
|
});
|
|
|
}
|
|
|
- const [registerTable] = useTable({
|
|
|
- columns: createTableColumns(),
|
|
|
- actionColumn: createActionColumn(handleRemove, handlePreview),
|
|
|
- pagination: false,
|
|
|
- });
|
|
|
- // 是否正在上传
|
|
|
- const isUploadingRef = ref(false);
|
|
|
+
|
|
|
async function uploadApiByItem(item: FileItem) {
|
|
|
+ const { api } = props;
|
|
|
+ if (!api || !isFunction(api)) {
|
|
|
+ return warn('upload api must exist and be a function');
|
|
|
+ }
|
|
|
try {
|
|
|
item.status = UploadResultStatus.UPLOADING;
|
|
|
|
|
|
const { data } = await uploadApi(
|
|
|
{
|
|
|
+ ...(props.uploadParams || {}),
|
|
|
file: item.file,
|
|
|
},
|
|
|
function onUploadProgress(progressEvent: ProgressEvent) {
|
|
@@ -154,32 +192,42 @@
|
|
|
};
|
|
|
}
|
|
|
}
|
|
|
+
|
|
|
// 点击开始上传
|
|
|
async function handleStartUpload() {
|
|
|
+ const { maxNumber } = props;
|
|
|
+ if (fileListRef.value.length > maxNumber) {
|
|
|
+ return createMessage.warning(`最多只能上传${maxNumber}个文件`);
|
|
|
+ }
|
|
|
try {
|
|
|
isUploadingRef.value = true;
|
|
|
+ // 只上传不是成功状态的
|
|
|
+ const uploadFileList =
|
|
|
+ fileListRef.value.filter((item) => item.status !== UploadResultStatus.SUCCESS) || [];
|
|
|
const data = await Promise.all(
|
|
|
- unref(fileListRef).map((item) => {
|
|
|
+ uploadFileList.map((item) => {
|
|
|
return uploadApiByItem(item);
|
|
|
})
|
|
|
);
|
|
|
isUploadingRef.value = false;
|
|
|
// 生产环境:抛出错误
|
|
|
- const errorList = data.filter((item) => !item.success);
|
|
|
- if (errorList.length > 0) {
|
|
|
- throw errorList;
|
|
|
- }
|
|
|
+ const errorList = data.filter((item: any) => !item.success);
|
|
|
+ if (errorList.length > 0) throw errorList;
|
|
|
} catch (e) {
|
|
|
isUploadingRef.value = false;
|
|
|
throw e;
|
|
|
}
|
|
|
}
|
|
|
+
|
|
|
// 点击保存
|
|
|
function handleOk() {
|
|
|
- // TODO: 没起作用:okButtonProps={{ disabled: state.isUploading }}
|
|
|
+ const { maxNumber } = props;
|
|
|
+
|
|
|
+ if (fileListRef.value.length > maxNumber) {
|
|
|
+ return createMessage.warning(`最多只能上传${maxNumber}个文件`);
|
|
|
+ }
|
|
|
if (isUploadingRef.value) {
|
|
|
- createMessage.warning('请等待文件上传后,保存');
|
|
|
- return;
|
|
|
+ return createMessage.warning('请等待文件上传后,保存');
|
|
|
}
|
|
|
const fileList: string[] = [];
|
|
|
|
|
@@ -189,18 +237,15 @@
|
|
|
fileList.push(responseData.url);
|
|
|
}
|
|
|
}
|
|
|
-
|
|
|
// 存在一个上传成功的即可保存
|
|
|
-
|
|
|
if (fileList.length <= 0) {
|
|
|
- createMessage.warning('没有上传成功的文件,无法保存');
|
|
|
- return;
|
|
|
+ return createMessage.warning('没有上传成功的文件,无法保存');
|
|
|
}
|
|
|
- console.log(fileList);
|
|
|
- emit('change', fileList);
|
|
|
fileListRef.value = [];
|
|
|
closeModal();
|
|
|
+ emit('change', fileList);
|
|
|
}
|
|
|
+
|
|
|
// 点击关闭:则所有操作不保存,包括上传的
|
|
|
function handleCloseFunc() {
|
|
|
if (!isUploadingRef.value) {
|
|
@@ -211,11 +256,22 @@
|
|
|
return false;
|
|
|
}
|
|
|
}
|
|
|
+
|
|
|
+ const [registerTable] = useTable({
|
|
|
+ columns: createTableColumns(),
|
|
|
+ actionColumn: createActionColumn(handleRemove, handlePreview),
|
|
|
+ pagination: false,
|
|
|
+ inset: true,
|
|
|
+ scroll: {
|
|
|
+ y: 3000,
|
|
|
+ },
|
|
|
+ });
|
|
|
return {
|
|
|
register,
|
|
|
closeModal,
|
|
|
getHelpText,
|
|
|
getStringAccept,
|
|
|
+ getOkButtonProps,
|
|
|
beforeUpload,
|
|
|
registerTable,
|
|
|
fileListRef,
|
|
@@ -224,14 +280,13 @@
|
|
|
handleStartUpload,
|
|
|
handleOk,
|
|
|
handleCloseFunc,
|
|
|
+ getIsSelectFile,
|
|
|
+ getUploadBtnText,
|
|
|
};
|
|
|
},
|
|
|
});
|
|
|
</script>
|
|
|
<style lang="less">
|
|
|
- // /deep/ .ant-upload-list {
|
|
|
- // display: none;
|
|
|
- // }
|
|
|
.upload-modal {
|
|
|
.ant-upload-list {
|
|
|
display: none;
|