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