19 changed files with 847 additions and 21 deletions
@ -0,0 +1,5 @@ |
|||||
|
export interface UploadApiResult { |
||||
|
message: string; |
||||
|
code: number; |
||||
|
url: string; |
||||
|
} |
||||
@ -0,0 +1,23 @@ |
|||||
|
import { UploadApiResult } from './model/uploadModel'; |
||||
|
import { defHttp } from '/@/utils/http/axios'; |
||||
|
import { UploadFileParams } from '/@/utils/http/axios/types'; |
||||
|
|
||||
|
enum Api { |
||||
|
UPLOAD_URL = '/upload', |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* @description: 上传接口 |
||||
|
*/ |
||||
|
export function uploadApi( |
||||
|
params: UploadFileParams, |
||||
|
onUploadProgress: (progressEvent: ProgressEvent) => void |
||||
|
) { |
||||
|
return defHttp.uploadFile<UploadApiResult>( |
||||
|
{ |
||||
|
url: Api.UPLOAD_URL, |
||||
|
onUploadProgress, |
||||
|
}, |
||||
|
params |
||||
|
); |
||||
|
} |
||||
@ -0,0 +1,2 @@ |
|||||
|
export { default as UploadContainer } from './src/UploadContainer.vue'; |
||||
|
// export * from './src/types';
|
||||
@ -0,0 +1,29 @@ |
|||||
|
<template> |
||||
|
<span> |
||||
|
<img v-if="fileUrl" :src="fileUrl" /> |
||||
|
<span v-else>{{ fileType }}</span> |
||||
|
</span> |
||||
|
</template> |
||||
|
<script lang="ts"> |
||||
|
import { defineComponent } from 'vue'; |
||||
|
|
||||
|
export default defineComponent({ |
||||
|
props: { |
||||
|
fileUrl: { |
||||
|
type: String, |
||||
|
default: '', |
||||
|
}, |
||||
|
fileType: { |
||||
|
type: String, |
||||
|
default: '', |
||||
|
}, |
||||
|
fileName: { |
||||
|
type: String, |
||||
|
default: '', |
||||
|
}, |
||||
|
}, |
||||
|
setup() { |
||||
|
return {}; |
||||
|
}, |
||||
|
}); |
||||
|
</script> |
||||
@ -0,0 +1,62 @@ |
|||||
|
<template> |
||||
|
<div> |
||||
|
<a-button-group> |
||||
|
<a-button type="primary" @click="openUploadModal">上传</a-button> |
||||
|
<a-button @click="openPreviewModal"> |
||||
|
<Icon icon="ant-design:eye-outlined" /> |
||||
|
</a-button> |
||||
|
</a-button-group> |
||||
|
<UploadModal v-bind="$props" @register="registerUploadModal" @change="handleChange" /> |
||||
|
<UploadPreviewModal |
||||
|
:value="fileListRef" |
||||
|
@register="registerPreviewModal" |
||||
|
@change="handlePreviewChange" |
||||
|
/> |
||||
|
</div> |
||||
|
</template> |
||||
|
<script lang="ts"> |
||||
|
import { defineComponent, ref, watch, unref } from 'vue'; |
||||
|
import { useModal } from '/@/components/Modal'; |
||||
|
import UploadModal from './UploadModal.vue'; |
||||
|
import { uploadContainerProps } from './props'; |
||||
|
import UploadPreviewModal from './UploadPreviewModal.vue'; |
||||
|
import Icon from '/@/components/Icon/index'; |
||||
|
export default defineComponent({ |
||||
|
components: { UploadModal, UploadPreviewModal, Icon }, |
||||
|
props: uploadContainerProps, |
||||
|
setup(props, { emit }) { |
||||
|
// 上传modal |
||||
|
const [registerUploadModal, { openModal: openUploadModal }] = useModal(); |
||||
|
// 预览modal |
||||
|
const [registerPreviewModal, { openModal: openPreviewModal }] = useModal(); |
||||
|
|
||||
|
const fileListRef = ref<string[]>([]); |
||||
|
watch( |
||||
|
() => props.value, |
||||
|
(value) => { |
||||
|
fileListRef.value = [...(value || [])]; |
||||
|
}, |
||||
|
{ immediate: true } |
||||
|
); |
||||
|
// 上传modal保存操作 |
||||
|
function handleChange(urls: string[]) { |
||||
|
fileListRef.value = [...unref(fileListRef), ...(urls || [])]; |
||||
|
emit('change', fileListRef.value); |
||||
|
} |
||||
|
// 预览modal保存操作 |
||||
|
function handlePreviewChange(urls: string[]) { |
||||
|
fileListRef.value = [...(urls || [])]; |
||||
|
emit('change', fileListRef.value); |
||||
|
} |
||||
|
return { |
||||
|
registerUploadModal, |
||||
|
openUploadModal, |
||||
|
handleChange, |
||||
|
handlePreviewChange, |
||||
|
registerPreviewModal, |
||||
|
openPreviewModal, |
||||
|
fileListRef, |
||||
|
}; |
||||
|
}, |
||||
|
}); |
||||
|
</script> |
||||
@ -0,0 +1,244 @@ |
|||||
|
<template> |
||||
|
<BasicModal |
||||
|
v-bind="$attrs" |
||||
|
@register="register" |
||||
|
@ok="handleOk" |
||||
|
:closeFunc="handleCloseFunc" |
||||
|
:maskClosable="false" |
||||
|
width="800px" |
||||
|
title="上传组件" |
||||
|
wrapClassName="upload-modal" |
||||
|
:okButtonProps="{ disabled: isUploadingRef }" |
||||
|
:cancelButtonProps="{ disabled: isUploadingRef }" |
||||
|
> |
||||
|
<template #centerdFooter> |
||||
|
<a-button @click="handleStartUpload" color="success" :loading="isUploadingRef"> |
||||
|
{{ isUploadingRef ? '上传中' : '开始上传' }} |
||||
|
</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" /> |
||||
|
</BasicModal> |
||||
|
</template> |
||||
|
<script lang="ts"> |
||||
|
import { defineComponent, reactive, ref, toRef, unref } from 'vue'; |
||||
|
import { Upload } from 'ant-design-vue'; |
||||
|
import { BasicModal, useModalInner } from '/@/components/Modal'; |
||||
|
import { BasicTable, useTable } from '/@/components/Table'; |
||||
|
// hooks |
||||
|
import { useUploadType } from './useUpload'; |
||||
|
import { useMessage } from '/@/hooks/web/useMessage'; |
||||
|
// types |
||||
|
import { FileItem, UploadResultStatus } from './types'; |
||||
|
import { basicProps } from './props'; |
||||
|
import { createTableColumns, createActionColumn } from './data'; |
||||
|
// utils |
||||
|
import { checkFileType, checkImgType, getBase64WithFile } from './utils'; |
||||
|
import { buildUUID } from '/@/utils/uuid'; |
||||
|
import { createImgPreview } from '/@/components/Preview/index'; |
||||
|
import { uploadApi } from '/@/api/demo/upload'; |
||||
|
|
||||
|
export default defineComponent({ |
||||
|
components: { BasicModal, Upload, BasicTable }, |
||||
|
props: basicProps, |
||||
|
setup(props, { emit }) { |
||||
|
const [register, { closeModal }] = useModalInner(); |
||||
|
const { getAccept, getStringAccept, getHelpText } = useUploadType({ |
||||
|
acceptRef: toRef(props, 'accept'), |
||||
|
helpTextRef: toRef(props, 'helpText'), |
||||
|
maxNumberRef: toRef(props, 'maxNumber'), |
||||
|
maxSizeRef: toRef(props, 'maxSize'), |
||||
|
}); |
||||
|
|
||||
|
const fileListRef = ref<FileItem[]>([]); |
||||
|
const state = reactive<{ fileList: FileItem[] }>({ fileList: [] }); |
||||
|
const { createMessage } = useMessage(); |
||||
|
// 上传前校验 |
||||
|
function beforeUpload(file: File) { |
||||
|
const { size, name } = file; |
||||
|
const { maxSize } = props; |
||||
|
const accept = unref(getAccept); |
||||
|
|
||||
|
// 设置最大值,则判断 |
||||
|
if (maxSize && file.size / 1024 / 1024 >= maxSize) { |
||||
|
createMessage.error(`只能上传不超过${maxSize}MB的文件!`); |
||||
|
return false; |
||||
|
} |
||||
|
|
||||
|
// 设置类型,则判断 |
||||
|
if (accept.length > 0 && !checkFileType(file, accept)) { |
||||
|
createMessage.error!(`只能上传${accept.join(',')}格式文件`); |
||||
|
return false; |
||||
|
} |
||||
|
// 生成图片缩略图 |
||||
|
if (checkImgType(file)) { |
||||
|
// beforeUpload,如果异步会调用自带上传方法 |
||||
|
// file.thumbUrl = await getBase64(file); |
||||
|
getBase64WithFile(file).then(({ result: thumbUrl }) => { |
||||
|
fileListRef.value = [ |
||||
|
...unref(fileListRef), |
||||
|
{ |
||||
|
uuid: buildUUID(), |
||||
|
file, |
||||
|
thumbUrl, |
||||
|
size, |
||||
|
name, |
||||
|
percent: 0, |
||||
|
type: name.split('.').pop(), |
||||
|
}, |
||||
|
]; |
||||
|
}); |
||||
|
} else { |
||||
|
fileListRef.value = [ |
||||
|
...unref(fileListRef), |
||||
|
{ |
||||
|
uuid: buildUUID(), |
||||
|
|
||||
|
file, |
||||
|
size, |
||||
|
name, |
||||
|
percent: 0, |
||||
|
type: name.split('.').pop(), |
||||
|
}, |
||||
|
]; |
||||
|
} |
||||
|
return false; |
||||
|
} |
||||
|
// 删除 |
||||
|
function handleRemove(record: FileItem) { |
||||
|
const index = fileListRef.value.findIndex((item) => item.uuid === record.uuid); |
||||
|
index !== -1 && fileListRef.value.splice(index, 1); |
||||
|
} |
||||
|
// 预览 |
||||
|
function handlePreview(record: FileItem) { |
||||
|
const { thumbUrl = '' } = record; |
||||
|
createImgPreview({ |
||||
|
imageList: [thumbUrl], |
||||
|
}); |
||||
|
} |
||||
|
const [registerTable] = useTable({ |
||||
|
columns: createTableColumns(), |
||||
|
actionColumn: createActionColumn(handleRemove, handlePreview), |
||||
|
pagination: false, |
||||
|
}); |
||||
|
// 是否正在上传 |
||||
|
const isUploadingRef = ref(false); |
||||
|
async function uploadApiByItem(item: FileItem) { |
||||
|
try { |
||||
|
item.status = UploadResultStatus.UPLOADING; |
||||
|
|
||||
|
const { data } = await uploadApi( |
||||
|
{ |
||||
|
file: item.file, |
||||
|
}, |
||||
|
function onUploadProgress(progressEvent: ProgressEvent) { |
||||
|
const complete = ((progressEvent.loaded / progressEvent.total) * 100) | 0; |
||||
|
item.percent = complete; |
||||
|
} |
||||
|
); |
||||
|
item.status = UploadResultStatus.SUCCESS; |
||||
|
item.responseData = data; |
||||
|
return { |
||||
|
success: true, |
||||
|
error: null, |
||||
|
}; |
||||
|
} catch (e) { |
||||
|
console.log(e); |
||||
|
item.status = UploadResultStatus.ERROR; |
||||
|
return { |
||||
|
success: false, |
||||
|
error: e, |
||||
|
}; |
||||
|
} |
||||
|
} |
||||
|
// 点击开始上传 |
||||
|
async function handleStartUpload() { |
||||
|
try { |
||||
|
isUploadingRef.value = true; |
||||
|
const data = await Promise.all( |
||||
|
unref(fileListRef).map((item) => { |
||||
|
return uploadApiByItem(item); |
||||
|
}) |
||||
|
); |
||||
|
isUploadingRef.value = false; |
||||
|
// 生产环境:抛出错误 |
||||
|
const errorList = data.filter((item) => !item.success); |
||||
|
if (errorList.length > 0) { |
||||
|
throw errorList; |
||||
|
} |
||||
|
} catch (e) { |
||||
|
isUploadingRef.value = false; |
||||
|
throw e; |
||||
|
} |
||||
|
} |
||||
|
// 点击保存 |
||||
|
function handleOk() { |
||||
|
// TODO: 没起作用:okButtonProps={{ disabled: state.isUploading }} |
||||
|
if (isUploadingRef.value) { |
||||
|
createMessage.warning('请等待文件上传后,保存'); |
||||
|
return; |
||||
|
} |
||||
|
const fileList: string[] = []; |
||||
|
|
||||
|
for (const item of fileListRef.value) { |
||||
|
const { status, responseData } = item; |
||||
|
if (status === UploadResultStatus.SUCCESS && responseData) { |
||||
|
fileList.push(responseData.url); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
// 存在一个上传成功的即可保存 |
||||
|
|
||||
|
if (fileList.length <= 0) { |
||||
|
createMessage.warning('没有上传成功的文件,无法保存'); |
||||
|
return; |
||||
|
} |
||||
|
console.log(fileList); |
||||
|
emit('change', fileList); |
||||
|
fileListRef.value = []; |
||||
|
closeModal(); |
||||
|
} |
||||
|
// 点击关闭:则所有操作不保存,包括上传的 |
||||
|
function handleCloseFunc() { |
||||
|
if (!isUploadingRef.value) { |
||||
|
fileListRef.value = []; |
||||
|
return true; |
||||
|
} else { |
||||
|
createMessage.warning('请等待文件上传结束后操作'); |
||||
|
return false; |
||||
|
} |
||||
|
} |
||||
|
return { |
||||
|
register, |
||||
|
closeModal, |
||||
|
getHelpText, |
||||
|
getStringAccept, |
||||
|
beforeUpload, |
||||
|
registerTable, |
||||
|
fileListRef, |
||||
|
state, |
||||
|
isUploadingRef, |
||||
|
handleStartUpload, |
||||
|
handleOk, |
||||
|
handleCloseFunc, |
||||
|
}; |
||||
|
}, |
||||
|
}); |
||||
|
</script> |
||||
|
<style lang="less"> |
||||
|
// /deep/ .ant-upload-list { |
||||
|
// display: none; |
||||
|
// } |
||||
|
.upload-modal { |
||||
|
.ant-upload-list { |
||||
|
display: none; |
||||
|
} |
||||
|
|
||||
|
.ant-table-wrapper .ant-spin-nested-loading { |
||||
|
padding: 0; |
||||
|
} |
||||
|
} |
||||
|
</style> |
||||
@ -0,0 +1,93 @@ |
|||||
|
<template> |
||||
|
<BasicModal |
||||
|
wrapClassName="upload-preview-modal" |
||||
|
v-bind="$attrs" |
||||
|
width="800px" |
||||
|
@register="register" |
||||
|
title="预览" |
||||
|
:showOkBtn="false" |
||||
|
> |
||||
|
<BasicTable @register="registerTable" :dataSource="fileListRef" /> |
||||
|
</BasicModal> |
||||
|
</template> |
||||
|
<script lang="ts"> |
||||
|
import { defineComponent, watch, ref, unref } from 'vue'; |
||||
|
import { BasicTable, useTable } from '/@/components/Table'; |
||||
|
import { createPreviewColumns, createPreviewActionColumn } from './data'; |
||||
|
import { BasicModal, useModalInner } from '/@/components/Modal'; |
||||
|
import { priviewProps } from './props'; |
||||
|
import { PreviewFileItem } from './types'; |
||||
|
import { createImgPreview } from '/@/components/Preview/index'; |
||||
|
import { downloadByUrl } from '/@/utils/file/FileDownload'; |
||||
|
|
||||
|
export default defineComponent({ |
||||
|
components: { BasicModal, BasicTable }, |
||||
|
props: priviewProps, |
||||
|
setup(props, { emit }) { |
||||
|
const [register, { closeModal }] = useModalInner(); |
||||
|
const fileListRef = ref<PreviewFileItem[]>([]); |
||||
|
watch( |
||||
|
() => props.value, |
||||
|
(value) => { |
||||
|
fileListRef.value = []; |
||||
|
value.forEach((item) => { |
||||
|
fileListRef.value = [ |
||||
|
...unref(fileListRef), |
||||
|
{ |
||||
|
url: item, |
||||
|
type: item.split('.').pop() || '', |
||||
|
name: item.split('/').pop() || '', |
||||
|
}, |
||||
|
]; |
||||
|
}); |
||||
|
}, |
||||
|
{ immediate: true } |
||||
|
); |
||||
|
// 删除 |
||||
|
function handleRemove(record: PreviewFileItem) { |
||||
|
const index = fileListRef.value.findIndex((item) => item.url === record.url); |
||||
|
if (index !== -1) { |
||||
|
fileListRef.value.splice(index, 1); |
||||
|
emit( |
||||
|
'change', |
||||
|
fileListRef.value.map((item) => item.url) |
||||
|
); |
||||
|
} |
||||
|
} |
||||
|
// 预览 |
||||
|
function handlePreview(record: PreviewFileItem) { |
||||
|
const { url = '' } = record; |
||||
|
createImgPreview({ |
||||
|
imageList: [url], |
||||
|
}); |
||||
|
} |
||||
|
// 下载 |
||||
|
function handleDownload(record: PreviewFileItem) { |
||||
|
const { url = '' } = record; |
||||
|
downloadByUrl({ url }); |
||||
|
} |
||||
|
const [registerTable] = useTable({ |
||||
|
columns: createPreviewColumns(), |
||||
|
pagination: false, |
||||
|
actionColumn: createPreviewActionColumn({ handleRemove, handlePreview, handleDownload }), |
||||
|
}); |
||||
|
return { |
||||
|
register, |
||||
|
closeModal, |
||||
|
fileListRef, |
||||
|
registerTable, |
||||
|
}; |
||||
|
}, |
||||
|
}); |
||||
|
</script> |
||||
|
<style lang="less"> |
||||
|
.upload-preview-modal { |
||||
|
.ant-upload-list { |
||||
|
display: none; |
||||
|
} |
||||
|
|
||||
|
.ant-table-wrapper .ant-spin-nested-loading { |
||||
|
padding: 0; |
||||
|
} |
||||
|
} |
||||
|
</style> |
||||
@ -0,0 +1,159 @@ |
|||||
|
// import { BasicColumn, TableAction, ActionItem } from '@/components/table';
|
||||
|
import { checkImgType, isImgTypeByName } from './utils'; |
||||
|
// import ThumnUrl from './ThumbUrl.vue';
|
||||
|
import { Progress } from 'ant-design-vue'; |
||||
|
import { FileItem, PreviewFileItem, UploadResultStatus } from './types'; |
||||
|
// import { ElecArchivesSaveResult } from '@/api/biz/file/model/fileModel';
|
||||
|
// import { quryFile } from '@/api/biz/file/file';
|
||||
|
import { BasicColumn, ActionItem, TableAction } from '/@/components/Table/index'; |
||||
|
|
||||
|
// 文件上传列表
|
||||
|
export function createTableColumns(): BasicColumn[] { |
||||
|
return [ |
||||
|
{ |
||||
|
dataIndex: 'thumbUrl', |
||||
|
title: '图例', |
||||
|
width: 100, |
||||
|
customRender: ({ record }) => { |
||||
|
const { thumbUrl, type } = (record as FileItem) || {}; |
||||
|
return <span>{thumbUrl ? <img src={thumbUrl} style={{ width: '50px' }} /> : type}</span>; |
||||
|
// return <ThumnUrl fileUrl={thumbUrl} fileType={type} fileName={type} />;
|
||||
|
}, |
||||
|
}, |
||||
|
{ |
||||
|
dataIndex: 'name', |
||||
|
title: '文件名', |
||||
|
align: 'left', |
||||
|
customRender: ({ text, record }) => { |
||||
|
const { percent, status: uploadStatus } = (record as FileItem) || {}; |
||||
|
let status = 'normal'; |
||||
|
if (uploadStatus === UploadResultStatus.ERROR) { |
||||
|
status = 'exception'; |
||||
|
} else if (uploadStatus === UploadResultStatus.UPLOADING) { |
||||
|
status = 'active'; |
||||
|
} else if (uploadStatus === UploadResultStatus.SUCCESS) { |
||||
|
status = 'success'; |
||||
|
} |
||||
|
return ( |
||||
|
<span> |
||||
|
<p class="ellipsis mb-1" title={text}> |
||||
|
{text} |
||||
|
</p> |
||||
|
<Progress percent={percent} size="small" status={status} /> |
||||
|
</span> |
||||
|
); |
||||
|
}, |
||||
|
}, |
||||
|
{ |
||||
|
dataIndex: 'size', |
||||
|
title: '文件大小', |
||||
|
width: 100, |
||||
|
customRender: ({ text = 0 }) => { |
||||
|
return text && (text / 1024).toFixed(2) + 'KB'; |
||||
|
}, |
||||
|
}, |
||||
|
// {
|
||||
|
// dataIndex: 'type',
|
||||
|
// title: '文件类型',
|
||||
|
// width: 100,
|
||||
|
// },
|
||||
|
{ |
||||
|
dataIndex: 'status', |
||||
|
title: '状态', |
||||
|
width: 100, |
||||
|
customRender: ({ text }) => { |
||||
|
if (text === UploadResultStatus.SUCCESS) { |
||||
|
return '上传成功'; |
||||
|
} else if (text === UploadResultStatus.ERROR) { |
||||
|
return '上传失败'; |
||||
|
} else if (text === UploadResultStatus.UPLOADING) { |
||||
|
return '上传中'; |
||||
|
} |
||||
|
|
||||
|
return text; |
||||
|
}, |
||||
|
}, |
||||
|
]; |
||||
|
} |
||||
|
export function createActionColumn(handleRemove: Function, handlePreview: Function): BasicColumn { |
||||
|
return { |
||||
|
width: 120, |
||||
|
title: '操作', |
||||
|
dataIndex: 'action', |
||||
|
fixed: false, |
||||
|
customRender: ({ record }) => { |
||||
|
const actions: ActionItem[] = [ |
||||
|
{ |
||||
|
label: '删除', |
||||
|
onClick: handleRemove.bind(null, record), |
||||
|
}, |
||||
|
]; |
||||
|
if (checkImgType(record)) { |
||||
|
actions.unshift({ |
||||
|
label: '预览', |
||||
|
onClick: handlePreview.bind(null, record), |
||||
|
}); |
||||
|
} |
||||
|
return <TableAction actions={actions} />; |
||||
|
}, |
||||
|
}; |
||||
|
} |
||||
|
// 文件预览列表
|
||||
|
export function createPreviewColumns(): BasicColumn[] { |
||||
|
return [ |
||||
|
{ |
||||
|
dataIndex: 'url', |
||||
|
title: '图例', |
||||
|
width: 100, |
||||
|
customRender: ({ record }) => { |
||||
|
const { url, type } = (record as PreviewFileItem) || {}; |
||||
|
return ( |
||||
|
<span>{isImgTypeByName(url) ? <img src={url} style={{ width: '50px' }} /> : type}</span> |
||||
|
); |
||||
|
}, |
||||
|
}, |
||||
|
{ |
||||
|
dataIndex: 'name', |
||||
|
title: '文件名', |
||||
|
align: 'left', |
||||
|
}, |
||||
|
]; |
||||
|
} |
||||
|
|
||||
|
export function createPreviewActionColumn({ |
||||
|
handleRemove, |
||||
|
handlePreview, |
||||
|
handleDownload, |
||||
|
}: { |
||||
|
handleRemove: Function; |
||||
|
handlePreview: Function; |
||||
|
handleDownload: Function; |
||||
|
}): BasicColumn { |
||||
|
return { |
||||
|
width: 160, |
||||
|
title: '操作', |
||||
|
dataIndex: 'action', |
||||
|
fixed: false, |
||||
|
customRender: ({ record }) => { |
||||
|
const { url } = (record as PreviewFileItem) || {}; |
||||
|
|
||||
|
const actions: ActionItem[] = [ |
||||
|
{ |
||||
|
label: '删除', |
||||
|
onClick: handleRemove.bind(null, record), |
||||
|
}, |
||||
|
{ |
||||
|
label: '下载', |
||||
|
onClick: handleDownload.bind(null, record), |
||||
|
}, |
||||
|
]; |
||||
|
if (isImgTypeByName(url)) { |
||||
|
actions.unshift({ |
||||
|
label: '预览', |
||||
|
onClick: handlePreview.bind(null, record), |
||||
|
}); |
||||
|
} |
||||
|
return <TableAction actions={actions} />; |
||||
|
}, |
||||
|
}; |
||||
|
} |
||||
@ -0,0 +1,42 @@ |
|||||
|
import type { PropType } from 'vue'; |
||||
|
|
||||
|
export const basicProps = { |
||||
|
helpText: { |
||||
|
type: String as PropType<string>, |
||||
|
default: '', |
||||
|
}, |
||||
|
// 文件最大多少MB
|
||||
|
maxSize: { |
||||
|
type: Number as PropType<number>, |
||||
|
default: 2, |
||||
|
}, |
||||
|
// 最大数量的文件,0不限制
|
||||
|
maxNumber: { |
||||
|
type: Number as PropType<number>, |
||||
|
default: 0, |
||||
|
}, |
||||
|
// 根据后缀,或者其他
|
||||
|
accept: { |
||||
|
type: Array as PropType<string[]>, |
||||
|
default: () => [], |
||||
|
}, |
||||
|
multiple: { |
||||
|
type: Boolean, |
||||
|
default: true, |
||||
|
}, |
||||
|
}; |
||||
|
|
||||
|
export const uploadContainerProps = { |
||||
|
value: { |
||||
|
type: Array as PropType<string[]>, |
||||
|
default: () => [], |
||||
|
}, |
||||
|
...basicProps, |
||||
|
}; |
||||
|
|
||||
|
export const priviewProps = { |
||||
|
value: { |
||||
|
type: Array as PropType<string[]>, |
||||
|
default: () => [], |
||||
|
}, |
||||
|
}; |
||||
@ -0,0 +1,25 @@ |
|||||
|
import { UploadApiResult } from '/@/api/demo/model/uploadModel'; |
||||
|
|
||||
|
export enum UploadResultStatus { |
||||
|
SUCCESS = 'success', |
||||
|
ERROR = 'error', |
||||
|
UPLOADING = 'uploading', |
||||
|
} |
||||
|
|
||||
|
export interface FileItem { |
||||
|
thumbUrl?: string; |
||||
|
name: string; |
||||
|
size: string | number; |
||||
|
type?: string; |
||||
|
percent: number; |
||||
|
file: File; |
||||
|
status?: UploadResultStatus; |
||||
|
responseData?: UploadApiResult; |
||||
|
uuid: string; |
||||
|
} |
||||
|
|
||||
|
export interface PreviewFileItem { |
||||
|
url: string; |
||||
|
name: string; |
||||
|
type: string; |
||||
|
} |
||||
@ -0,0 +1,55 @@ |
|||||
|
import { Ref, unref, computed } from 'vue'; |
||||
|
|
||||
|
export function useUploadType({ |
||||
|
acceptRef, |
||||
|
// uploadTypeRef,
|
||||
|
helpTextRef, |
||||
|
maxNumberRef, |
||||
|
maxSizeRef, |
||||
|
}: { |
||||
|
acceptRef: Ref<string[]>; |
||||
|
// uploadTypeRef: Ref<UploadTypeEnum>;
|
||||
|
helpTextRef: Ref<string>; |
||||
|
maxNumberRef: Ref<number>; |
||||
|
maxSizeRef: Ref<number>; |
||||
|
}) { |
||||
|
// 文件类型限制
|
||||
|
const getAccept = computed(() => { |
||||
|
// const uploadType = unref(uploadTypeRef);
|
||||
|
const accept = unref(acceptRef); |
||||
|
if (accept && accept.length > 0) { |
||||
|
return accept; |
||||
|
} |
||||
|
return []; |
||||
|
}); |
||||
|
const getStringAccept = computed(() => { |
||||
|
return unref(getAccept) |
||||
|
.map((item) => `.${item}`) |
||||
|
.join(','); |
||||
|
}); |
||||
|
// 支持jpg、jpeg、png格式,不超过2M,最多可选择10张图片,。
|
||||
|
const getHelpText = computed(() => { |
||||
|
const helpText = unref(helpTextRef); |
||||
|
if (helpText) { |
||||
|
return helpText; |
||||
|
} |
||||
|
const helpTexts: string[] = []; |
||||
|
|
||||
|
const accept = unref(acceptRef); |
||||
|
if (accept.length > 0) { |
||||
|
helpTexts.push(`支持${accept.join(',')}格式`); |
||||
|
} |
||||
|
|
||||
|
const maxSize = unref(maxSizeRef); |
||||
|
if (maxSize) { |
||||
|
helpTexts.push(`不超过${maxSize}MB`); |
||||
|
} |
||||
|
|
||||
|
const maxNumber = unref(maxNumberRef); |
||||
|
if (maxNumber) { |
||||
|
helpTexts.push(`最多可选择${maxNumber}个文件`); |
||||
|
} |
||||
|
return helpTexts.join(','); |
||||
|
}); |
||||
|
return { getAccept, getStringAccept, getHelpText }; |
||||
|
} |
||||
@ -0,0 +1,28 @@ |
|||||
|
export function checkFileType(file: File, accepts: string[]) { |
||||
|
const newTypes = accepts.join('|'); |
||||
|
// const reg = /\.(jpg|jpeg|png|gif|txt|doc|docx|xls|xlsx|xml)$/i;
|
||||
|
const reg = new RegExp('\\.(' + newTypes + ')$', 'i'); |
||||
|
|
||||
|
if (!reg.test(file.name)) { |
||||
|
return false; |
||||
|
} else { |
||||
|
return true; |
||||
|
} |
||||
|
} |
||||
|
export function checkImgType(file: File) { |
||||
|
return /\.(jpg|jpeg|png|gif)$/i.test(file.name); |
||||
|
} |
||||
|
export function isImgTypeByName(name: string) { |
||||
|
return /\.(jpg|jpeg|png|gif)$/i.test(name); |
||||
|
} |
||||
|
export function getBase64WithFile(file: File) { |
||||
|
return new Promise<{ |
||||
|
result: string; |
||||
|
file: File; |
||||
|
}>((resolve, reject) => { |
||||
|
const reader = new FileReader(); |
||||
|
reader.readAsDataURL(file); |
||||
|
reader.onload = () => resolve({ result: reader.result as string, file }); |
||||
|
reader.onerror = (error) => reject(error); |
||||
|
}); |
||||
|
} |
||||
@ -0,0 +1,17 @@ |
|||||
|
<template> |
||||
|
<div class="p-4"> |
||||
|
<UploadContainer :maxSize="5" /> |
||||
|
</div> |
||||
|
</template> |
||||
|
<script lang="ts"> |
||||
|
import { defineComponent } from 'vue'; |
||||
|
import { UploadContainer } from '/@/components/Upload/index'; |
||||
|
|
||||
|
// import { Alert } from 'ant-design-vue'; |
||||
|
export default defineComponent({ |
||||
|
components: { UploadContainer }, |
||||
|
setup() { |
||||
|
return {}; |
||||
|
}, |
||||
|
}); |
||||
|
</script> |
||||
Loading…
Reference in new issue