Files
ruoyi-plus-soybean/src/components/custom/file-upload.vue

183 lines
5.1 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<script setup lang="ts">
import { ref, useAttrs, watch } from 'vue';
import type { UploadFileInfo, UploadProps } from 'naive-ui';
import { fetchBatchDeleteOss } from '@/service/api/system/oss';
import { getToken } from '@/store/modules/auth/shared';
import { getServiceBaseURL } from '@/utils/service';
defineOptions({
name: 'FileUpload'
});
interface Props {
action?: string;
data?: Record<string, any>;
defaultUpload?: boolean;
showTip?: boolean;
max?: number;
accept?: string;
fileSize?: number;
uploadType?: 'file' | 'image';
}
const props = withDefaults(defineProps<Props>(), {
action: `/resource/oss/upload`,
data: undefined,
defaultUpload: true,
showTip: true,
max: 5,
accept: '.doc,.docx,.xls,.xlsx,.ppt,.pptx,.txt,.pdf',
fileSize: 5,
uploadType: 'file'
});
const attrs: UploadProps = useAttrs();
let fileNum = 0;
const fileList = ref<UploadFileInfo[]>([]);
const needRelaodData = ref(false);
defineExpose({
needRelaodData
});
watch(
() => fileList.value,
newValue => {
needRelaodData.value = newValue.length > 0;
}
);
const isHttpProxy = import.meta.env.DEV && import.meta.env.VITE_HTTP_PROXY === 'Y';
const { baseURL } = getServiceBaseURL(import.meta.env, isHttpProxy);
const headers: Record<string, string> = {
Authorization: `Bearer ${getToken()}`,
clientid: import.meta.env.VITE_APP_CLIENT_ID!
};
function beforeUpload(options: { file: UploadFileInfo; fileList: UploadFileInfo[] }) {
fileNum += 1;
const { file } = options;
// 校检文件类型
if (props.accept) {
const fileName = file.name.split('.');
const fileExt = `.${fileName[fileName.length - 1]}`;
const isTypeOk = props.accept.split(',')?.includes(fileExt);
if (!isTypeOk) {
window.$message?.error(`文件格式不正确, 请上传 ${props.accept} 格式文件!`);
return false;
}
}
// 校检文件名是否包含特殊字符
if (file.name.includes(',')) {
window.$message?.error('文件名不正确,不能包含英文逗号!');
return false;
}
// 校检文件大小
if (props.fileSize && file.file?.size) {
const isLt = file.file?.size / 1024 / 1024 < props.fileSize;
if (!isLt) {
window.$message?.error(`上传文件大小不能超过 ${props.fileSize} MB!`);
return false;
}
}
return true;
}
function isErrorState(xhr: XMLHttpRequest) {
const responseText = xhr?.responseText;
const response = JSON.parse(responseText);
return response.code !== 200;
}
function handleFinish(options: { file: UploadFileInfo; event?: ProgressEvent }) {
fileNum -= 1;
const { file, event } = options;
// @ts-expect-error Ignore type errors
const responseText = event?.target?.responseText;
const response = JSON.parse(responseText);
const oss: Api.System.Oss = response.data;
fileList.value.find(item => item.id === file.id)!.id = String(oss.ossId);
file.id = String(oss.ossId);
file.url = oss.url;
file.name = oss.fileName;
if (fileNum === 0) {
window.$message?.success('上传成功');
}
return file;
}
function handleError(options: { file: UploadFileInfo; event?: ProgressEvent }) {
const { event } = options;
// @ts-expect-error Ignore type errors
const responseText = event?.target?.responseText;
const msg = JSON.parse(responseText).msg;
window.$message?.error(msg || '上传失败');
}
async function handleRemove(file: UploadFileInfo) {
if (file.status !== 'finished') {
return;
}
const { error } = await fetchBatchDeleteOss([file.id]);
if (error) return;
window.$message?.success('删除成功');
}
</script>
<template>
<NUpload
v-bind="attrs"
v-model:file-list="fileList"
:action="`${baseURL}${action}`"
:data="data"
:headers="headers"
:max="max"
:accept="accept"
:multiple="max > 1"
directory-dnd
:default-upload="defaultUpload"
:list-type="uploadType === 'image' ? 'image-card' : 'text'"
:is-error-state="isErrorState"
@finish="handleFinish"
@error="handleError"
@before-upload="beforeUpload"
@remove="({ file }) => handleRemove(file)"
>
<NUploadDragger v-if="uploadType === 'file'">
<div class="mb-12px flex-center">
<SvgIcon icon="material-symbols:unarchive-outline" class="text-58px color-#d8d8db dark:color-#a1a1a2" />
</div>
<NText class="text-16px">点击或者拖动文件到该区域来上传</NText>
<NP v-if="showTip" depth="3" class="mt-8px text-center">
请上传
<template v-if="fileSize">
大小不超过
<b class="text-red-500">{{ fileSize }}MB</b>
</template>
<template v-if="accept">
且格式为
<b class="text-red-500">{{ accept.replaceAll(',', '/') }}</b>
</template>
的文件
</NP>
</NUploadDragger>
</NUpload>
<NP v-if="showTip && uploadType === 'image'" depth="3" class="mt-12px">
请上传
<template v-if="fileSize">
大小不超过
<b class="text-red-500">{{ fileSize }}MB</b>
</template>
<template v-if="accept">
且格式为
<b class="text-red-500">{{ accept.replaceAll(',', '/') }}</b>
</template>
的文件
</NP>
</template>
<style scoped></style>