123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224 |
- <template>
- <a-upload
- :action="uploadUrl"
- :headers="headers"
- :accept="accept"
- :data="uploadDataDict"
- :show-upload-list="false"
- :custom-request="customRequest"
- :disabled="disabled || loading"
- :before-upload="handleBeforeUpload"
- @change="handleFileChange"
- >
- <slot>
- <a-button>
- <template #icon><ImportOutlined /></template>点击导入
- </a-button>
- </slot>
- </a-upload>
- </template>
- <script setup lang="ts">
- import { computed, ref, watch } from "vue";
- import { DownloadOutlined, ImportOutlined } from "@ant-design/icons-vue";
- import { message } from "ant-design-vue";
- import type { UploadProps } from "ant-design-vue";
- import type { AxiosError, AxiosProgressEvent } from "axios";
- import { request } from "@/utils/request";
- import { getFileMD5 } from "@/utils/crypto";
- defineOptions({
- name: "ImportBtn",
- });
- const props = withDefaults(
- defineProps<{
- uploadUrl: string;
- format?: string[];
- uploadData?: Record<string, any>;
- maxSize?: number;
- disabled?: boolean;
- uploadFileAlias?: string;
- }>(),
- {
- uploadUrl: "",
- format: () => ["xls", "xlsx"],
- uploadData: () => {
- return {};
- },
- maxSize: 20 * 1024 * 1024,
- uploadFileAlias: "file",
- disabled: false,
- }
- );
- // interface ResultData {
- // success: boolean;
- // message: string;
- // }
- // type UploadSuccessData = ResultData & UploadResultType;
- // const emit = defineEmits<{
- // uploading: [];
- // uploadError: [value: ResultData];
- // uploadSuccess: [value: UploadSuccessData];
- // validError: [value: ResultData];
- // }>();
- const emit = defineEmits([
- "uploading",
- "uploadError",
- "uploadSuccess",
- "validError",
- ]);
- const uploadRef = ref();
- const canUpload = ref(false);
- const uploadDataDict = ref({});
- const headers = ref({ md5: "" });
- const result = ref({ success: true, message: "" });
- const loading = ref(false);
- const uploadProgress = ref(0);
- const curFileUid = ref("");
- const accept = computed(() => {
- return props.format.map((el) => `.${el}`).join();
- });
- function checkFileFormat(fileType: string) {
- const fileFormat = fileType.split(".").pop()?.toLocaleLowerCase();
- return props.format.some((item) => item.toLocaleLowerCase() === fileFormat);
- }
- const handleFileChange: UploadProps["onChange"] = ({ file, fileList }) => {
- if (!fileList.length) {
- curFileUid.value = "";
- result.value = {
- success: true,
- message: "",
- };
- return;
- }
- if (curFileUid.value !== file.uid) {
- result.value = {
- success: true,
- message: "",
- };
- }
- curFileUid.value = file.uid;
- canUpload.value = file.status === "uploading";
- };
- const handleBeforeUpload: UploadProps["beforeUpload"] = async (file) => {
- uploadDataDict.value = {
- ...props.uploadData,
- filename: file.name,
- };
- if (file.size > props.maxSize) {
- handleExceededSize();
- return Promise.reject(result.value);
- }
- if (!checkFileFormat(file.name)) {
- handleFormatError();
- return Promise.reject(result.value);
- }
- const md5 = await getFileMD5(file);
- headers.value.md5 = md5;
- loading.value = true;
- };
- interface UploadResultType {
- hasError: boolean;
- failRecords: Array<{
- msg: string;
- lineNum: number;
- }>;
- }
- const customRequest: UploadProps["customRequest"] = (option) => {
- const { file, data } = option;
- const formData = new FormData();
- const paramData: Record<string, any> = data || {};
- Object.entries(paramData).forEach(([k, v]) => {
- formData.append(k, v);
- });
- formData.append(props.uploadFileAlias, file as File);
- emit("uploading");
- (
- request({
- url: option.action as string,
- data: formData,
- headers: option.headers,
- onUploadProgress: (data: AxiosProgressEvent) => {
- uploadProgress.value = data.total
- ? Math.floor((100 * data.loaded) / data.total)
- : 0;
- },
- }) as Promise<UploadResultType>
- )
- .then((res) => {
- // 所有excel导入的特殊处理
- if (res.hasError) {
- const failRecords = res.failRecords;
- const message = failRecords
- .map((item) => `第${item.lineNum}行:${item.msg}`)
- .join("。");
- handleError(message);
- return;
- }
- handleSuccess(res);
- })
- .catch((error: AxiosError<{ message: string }> | null) => {
- handleError(error?.response?.data?.message);
- });
- };
- function handleError(message: string | undefined) {
- canUpload.value = false;
- loading.value = false;
- result.value = {
- success: false,
- message: message || "上传错误",
- };
- emit("uploadError", result.value);
- }
- function handleSuccess(data: UploadResultType) {
- canUpload.value = false;
- loading.value = false;
- result.value = {
- success: true,
- message: "上传成功!",
- };
- emit("uploadSuccess", {
- ...result.value,
- data,
- });
- }
- function handleFormatError() {
- const content = `只支持文件格式为${props.format.join("/")}`;
- result.value = {
- success: false,
- message: content,
- };
- loading.value = false;
- emit("validError", result.value);
- }
- function handleExceededSize() {
- const content = `文件大小不能超过${Math.floor(props.maxSize / 1024)}M`;
- result.value = {
- success: false,
- message: content,
- };
- loading.value = false;
- emit("validError", result.value);
- }
- </script>
|