basic-upload.vue——基本上传组件
<template><div class="basic-upload-wrap"><el-uploadref="uploadRef":file-list="fileList":accept="accept"@update:file-list="(data) => emits('update:file-list', data)":http-request="uploadFile":on-exceed="handleExceed":on-progress="handleUpdate":on-success="handleUpdate":on-remove="preDelete"v-bind="$attrs"><slot><div class="upload-btn-wrapper"><el-button class="upload-btn" type="primary"><svg-icon name="cloud-upload" />上传文件</el-button><div class="prompt" v-if="accept">只支持{{ accept }}的格式文件</div></div></slot></el-upload></div>
</template><script setup>
import api from "@/api/documentInfo";
const { uploadFiles, deleteFiles } = api;const emits = defineEmits(["update:file-list"]);
const props = defineProps({fileList: {type: Array,default: () => [],},accept: {type: String,},
});const uploadRef = ref();
const pendingDels = [];
const controllerMap = {};async function uploadFile(options) {const { file, onProgress, onSuccess, onError } = options;const formData = new FormData();formData.append("files", file);const controller = new AbortController();controllerMap[file.uid] = controller;const { code, message, data } = await uploadFiles(formData, {onUploadProgress: (event) => {handleProgress(event, onProgress);},signal: controller.signal,});if (code !== "0") {onError(message || "上传失败");}onSuccess(data);
}let timer;
function handleProgress(event, onProgress) {let complete = (event.loaded / event.total) * 100;if (complete < 90) {event.percent = complete;onProgress(event);return;}if (timer) return;timer = window.setInterval(() => {complete += (100 - complete) * 0.2;if (complete > 99 && timer) {window.clearInterval(timer);timer = null;}event.percent = complete;onProgress(event);}, 500);
}function handleExceed(files, uploadFiles) {ElMessage.warning(`最多可上传${uploadFiles.length}个附件`);
}function handleUpdate(response, uploadFile, uploadFiles) {const res = uploadFiles.map((item) => {return item.response ? item.response : item;});emits("update:file-list", res);
}const preDelete = (file) => {controllerMap[file.uid]?.abort?.();delete controllerMap[file.uid];pendingDels.push(file);const updatedFileList = props.fileList.filter((f) => f.uid !== file.uid);emits("update:file-list", updatedFileList);
};const confirmDelete = async () => {if (pendingDels.length > 0) {const ids = [...new Set(pendingDels.map((item) => item.id).filter((v) => v)),];if (!ids.length) return;await deleteFiles(ids);pendingDels.length = 0;}
};const deleteUnBind = () => {pendingDels.push(...props.fileList.filter((v) => !v.kbId));confirmDelete();
};onMounted(() => {if (uploadRef.value) {uploadRef.value.preDelete = preDelete;uploadRef.value.confirmDelete = confirmDelete;uploadRef.value.deleteUnBind = deleteUnBind;}
});
defineExpose({upload: uploadRef,
});
</script><style lang="scss" scoped>
.upload-btn-wrapper {display: flex;align-items: center;.prompt {display: inline-block;color: #88c4f9;font-size: 12px;margin-left: 8px;line-height: 1.2;}
}
</style>
list-upload.vue——文件上传列表
<template><div class="list-upload-wrap"><basic-uploadref="uploadRef":show-file-list="false":file-list="fileList"@update:file-list="(data) => emits('update:file-list', data)":accept="accept"v-bind="$attrs"></basic-upload><ul class="file-list-wrap" v-if="!!fileList.length"><li v-for="item in fileList" :key="item.id"><div class="item-content"><span class="file-name" @click="handlePreview(item)">{{ item.documentName || item.name }}</span><svg-icon name="close" class="close" @click.stop="handleDel(item)" /></div><el-progressv-if="item.percentage !== undefined && item.percentage !== 100":percentage="item.percentage":format="(percentage) => ''":text-inside="true":stroke-width="4"class="el-upload-list__item-progress"></el-progress></li></ul></div>
</template><script setup>
import { handlePreview } from "./utils.js";const emits = defineEmits(["update:file-list"]);
defineProps({fileList: {type: Array,default: () => [],},accept: {type: String,},
});const uploadRef = ref();const handleDel = (val) => {uploadRef.value.upload?.preDelete?.(val);
};const upload = computed(() => uploadRef.value?.upload);
defineExpose({upload,
});
</script><style lang="scss" scoped>
.list-upload-wrap {width: 100%;
}.file-list-wrap {$hover-color: #0093ff;list-style-type: none;padding: 0;margin-top: 16px;max-height: 30vh;overflow-y: auto;padding-right: 6px;li {margin-bottom: 20px;line-height: 1.2;&:last-child {margin-bottom: 0;}.item-content {display: flex;align-items: center;justify-content: space-between;}.file-name {font-family: "Microsoft YaHei", "Microsoft YaHei";font-weight: 400;font-size: 14px;color: #ffffff;text-align: left;font-style: normal;text-transform: none;overflow: hidden;text-overflow: ellipsis;white-space: nowrap;cursor: pointer;&:hover {color: $hover-color;}}.close {font-size: 19px;flex: 19px 0 0;cursor: pointer;&:hover {color: $hover-color;}}.el-upload-list__item-progress {width: 100%;position: relative;top: 2px;margin-bottom: 12px;}}
}
</style>
utils.js——预览跳转
import router from "@/router";// 预览文件
export const handlePreview = (file) => {if (!file) {return;}const name = file.documentName;const url = (process.env.VUE_APP_FILE_PATH || "") + file.documentUrl;if (!name || !url || !router) return;const lowerCaseName = name.toLowerCase();const suffixName = "." + lowerCaseName.split(".").pop();// 1. 处理文本和文档类型if ([".txt", ".doc", ".docx", ".pdf"].includes(suffixName)) {let routeUrl = router.resolve({name: "office-preview",query: { url, name },});window.open(routeUrl.href, "_blank");return;}// 2. 处理图片和视频类型if ([".png", ".jpg", ".jpeg", ".bmp", ".gif", ".mp4"].includes(suffixName)) {window.open(url, "_blank");return;}// 3. 提示不支持的文件类型ElMessage({message: "暂不支持该格式文件预览",type: "warning",});
};
Use
<template><el-form-item label="应急预案:" prop="documentList"><list-upload ref="uploadRef" v-model:file-list="ruleForm.documentList":accept="'.doc, .docx, .txt, .pdf, .png, .jpg, .jpeg, .gif'" :limit="20"></list-upload></el-form-item>
</template><script setup>const uploadRef = ref();function close() {uploadRef.value.upload?.deleteUnBind();dialogVisible.value = false;}async function submit() {const formData = {id: ruleForm.value.id,documentList: ruleForm.value.documentList,};await update(formData);await uploadRef.value.upload?.confirmDelete();ElMessage.success("操作成功");}
</script>