<template>
|
<el-dialog
|
v-model="upload.open"
|
:title="title"
|
width="500px"
|
append-to-body
|
@close="handleCancel"
|
>
|
<DialRightTop :optMessgDesc="optMessgDesc" />
|
|
<div class="upload-container">
|
<el-upload
|
ref="uploadRef"
|
v-model:file-list="fileList"
|
drag
|
:limit="1"
|
accept=".xlsx, .xls"
|
:headers="elHeader"
|
:action="upload.url + uploadUrl"
|
:disabled="upload.isUploading"
|
:data="paramsData"
|
:auto-upload="false"
|
:on-progress="handleProgress"
|
:on-error="handleFileError"
|
:on-success="handleSuccess"
|
:on-exceed="handleExceed"
|
class="upload-wrapper"
|
>
|
<el-icon class="el-icon--upload"><upload-filled /></el-icon>
|
<div class="el-upload__text">
|
将文件拖到此处,或 <em>点击上传</em>
|
</div>
|
|
<template #tip>
|
<div class="el-upload__tip text-center">
|
<span>仅允许导入 xls、xlsx 格式文件。</span>
|
<el-link
|
type="primary"
|
:underline="false"
|
class="template-link"
|
@click="handleDownloadTemplate"
|
>下载模板</el-link>
|
</div>
|
</template>
|
</el-upload>
|
</div>
|
|
<template #footer>
|
<div class="dialog-footer">
|
<el-button type="primary" :loading="upload.isUploading" @click="handleSubmit">确 定</el-button>
|
<el-button @click="handleCancel">取 消</el-button>
|
</div>
|
</template>
|
</el-dialog>
|
</template>
|
|
<script setup lang="ts">
|
import { reactive, ref, watch, computed } from "vue";
|
import { getToken } from "@/utils/auth";
|
import useCurrentInstance from "@/utils/useCurrentInstance";
|
import { download } from '@/utils/request'
|
import { genFileId, type UploadInstance, type UploadRawFile } from 'element-plus';
|
import { UploadFilled } from '@element-plus/icons-vue';
|
|
interface Props {
|
uploadUrl: string; // 上传接口地址
|
templateUrl?: string; // 模板下载地址
|
title?: string; // 弹窗标题
|
open: boolean; // 弹窗开关
|
paramsData?: object; // 上传携带的额外参数
|
optMessgDesc?: string; // 提示文字内容
|
}
|
|
const props = withDefaults(defineProps<Props>(), {
|
title: '文件导入',
|
open: false,
|
paramsData: () => ({}),
|
optMessgDesc: '1.请下载正确的模板导入数据; 2.表头请勿改动; 3.必填项需填充; 4.字典数据需校验合法性。'
|
});
|
|
const emit = defineEmits(['submit', 'cancel']);
|
const { proxy } = useCurrentInstance();
|
const uploadRef = ref<UploadInstance>();
|
const fileList = ref([]);
|
|
// 状态管理
|
const upload = reactive({
|
open: false,
|
isUploading: false,
|
url: import.meta.env.VITE_APP_BASE_API
|
});
|
|
// 请求头
|
const elHeader = computed(() => ({
|
Authorization: "Bearer " + getToken(),
|
}));
|
|
// 监听弹窗打开状态
|
watch(() => props.open, (val) => {
|
upload.open = val;
|
if (val) {
|
fileList.value = [];
|
upload.isUploading = false;
|
}
|
});
|
|
/** 核心方法:提交上传 */
|
const handleSubmit = () => {
|
if (fileList.value.length === 0) {
|
proxy.$modal.msgError('请选择需要上传的文件');
|
return;
|
}
|
upload.isUploading = true;
|
uploadRef.value!.submit();
|
};
|
|
/** 核心方法:下载模板 */
|
const handleDownloadTemplate = () => {
|
if (!props.templateUrl) return;
|
|
const [url, search] = props.templateUrl.split('?');
|
const params: Record<string, any> = {};
|
if (search) {
|
search.split('&').forEach(item => {
|
const [k, v] = item.split('=');
|
params[k] = v;
|
});
|
}
|
|
// 直接使用引入的方法,不再通过 proxy
|
download(url, params, `template_${new Date().getTime()}.xlsx`);
|
};
|
/** 上传进度 */
|
const handleProgress = () => {
|
upload.isUploading = true;
|
};
|
|
/** 上传结果处理(保留业务逻辑策略) */
|
const handleSuccess = (response: any) => {
|
upload.isUploading = false;
|
uploadRef.value!.clearFiles();
|
|
const { code, msg, data } = response;
|
|
if (code === 200) {
|
proxy.$modal.msgSuccess(msg || "导入成功");
|
emit('submit', response);
|
handleCancel();
|
}
|
else if (code === 201) {
|
// 部分成功或有错误文件
|
if (!data || data === 'null') {
|
proxy.$alert(msg, "导入提示");
|
} else {
|
proxy.$confirm(msg, "导入结果", {
|
confirmButtonText: '确定',
|
cancelButtonText: '下载错误文件',
|
type: 'warning',
|
distinguishCancelAndClose: true
|
}).catch((action) => {
|
if (action === 'cancel') proxy.downloadFile(data);
|
});
|
}
|
emit('submit', response); // 部分成功也触发刷新
|
}
|
else {
|
proxy.$alert(msg || "导入失败", "错误提示", { type: 'error' });
|
}
|
};
|
|
/** 文件超出限制(自动替换) */
|
const handleExceed = (files: File[]) => {
|
uploadRef.value!.clearFiles();
|
const file = files[0] as UploadRawFile;
|
file.uid = genFileId();
|
uploadRef.value!.handleStart(file);
|
};
|
|
const handleCancel = () => {
|
upload.open = false;
|
emit('cancel');
|
};
|
/** 上传失败处理 */
|
const handleFileError = (err: any) => {
|
upload.isUploading = false; // 恢复按钮点击状态
|
uploadRef.value!.clearFiles(); // 清空失败的文件列表,方便用户重选
|
|
// 解析报错信息
|
try {
|
const response = JSON.parse(err.message);
|
proxy.$modal.msgError(`上传失败(404):接口地址 ${response.path} 不存在`);
|
} catch (e) {
|
proxy.$modal.msgError("上传失败,请检查网络或联系管理员");
|
}
|
};
|
</script>
|
|
<style scoped>
|
.upload-container {
|
padding: 20px 0;
|
display: flex;
|
justify-content: center;
|
}
|
|
.upload-wrapper {
|
width: 100%;
|
}
|
|
.template-link {
|
font-size: 12px;
|
vertical-align: baseline;
|
margin-left: 10px;
|
}
|
|
:deep(.el-upload-dragger) {
|
padding: 40px;
|
}
|
|
/* 覆盖错误按钮样式 */
|
:deep(.el-button--danger) {
|
background-color: #f56c6c;
|
border-color: #f56c6c;
|
}
|
</style>
|