|
|
@@ -4,7 +4,7 @@
|
|
|
<el-card class="pop-card">
|
|
|
<template #header>
|
|
|
<div style="font-size: 16px; font-weight: 600">批量导入</div>
|
|
|
- <el-icon :size="18" @click="handleClose" style="cursor: pointer">
|
|
|
+ <el-icon :size="18" @click="handleMainClose" style="cursor: pointer">
|
|
|
<Close />
|
|
|
</el-icon>
|
|
|
</template>
|
|
|
@@ -17,6 +17,7 @@
|
|
|
:limit="1"
|
|
|
drag
|
|
|
:action="importApiUrl"
|
|
|
+ :data="props.data"
|
|
|
:with-credentials="true"
|
|
|
:auto-upload="false"
|
|
|
:before-upload="beforeUpload"
|
|
|
@@ -49,21 +50,28 @@
|
|
|
<WarnTriangleFilled />
|
|
|
</el-icon>
|
|
|
<div class="header-text">导入提示</div>
|
|
|
- <el-icon class="close-icon" :size="18" @click="handleClose"><Close /></el-icon>
|
|
|
+ <el-icon class="close-icon" :size="18" @click="handleErrorDialogClose"><Close /></el-icon>
|
|
|
</template>
|
|
|
- <div class="sum-count">
|
|
|
- 成功导入 <span class="succ-sum">{{ sucCount }}</span> 条, 失败 <span class="err-sum">{{ errCount }}</span> 条
|
|
|
+ <!-- 简单模式或 ImportResponseDataPerson 类型:显示字符串消息 -->
|
|
|
+ <div v-if="props.responseType === 'simple' || isPersonResponseType" class="simple-message">
|
|
|
+ {{ simpleMessage }}
|
|
|
</div>
|
|
|
- <el-table :data="errDetail" style="width: 100%" height="190">
|
|
|
- <el-table-column prop="rowNum" label="行数" width="100" fixed align="center" />
|
|
|
- <el-table-column
|
|
|
- prop="failReason"
|
|
|
- label="失败原因"
|
|
|
- show-overflow-tooltip
|
|
|
- class-name="error-reason"
|
|
|
- label-class-name="error-reason-label"
|
|
|
- />
|
|
|
- </el-table>
|
|
|
+ <!-- 详细模式:显示统计和错误详情表格 -->
|
|
|
+ <template v-else>
|
|
|
+ <div class="sum-count">
|
|
|
+ 成功导入 <span class="succ-sum">{{ sucCount }}</span> 条, 失败 <span class="err-sum">{{ errCount }}</span> 条
|
|
|
+ </div>
|
|
|
+ <el-table :data="errDetail" style="width: 100%" height="190">
|
|
|
+ <el-table-column prop="rowNum" label="行数" width="100" fixed align="center" />
|
|
|
+ <el-table-column
|
|
|
+ prop="failReason"
|
|
|
+ label="失败原因"
|
|
|
+ show-overflow-tooltip
|
|
|
+ class-name="error-reason"
|
|
|
+ label-class-name="error-reason-label"
|
|
|
+ />
|
|
|
+ </el-table>
|
|
|
+ </template>
|
|
|
<template #footer>
|
|
|
<el-button type="primary" @click="handleBackToImport">重新导入 </el-button>
|
|
|
</template>
|
|
|
@@ -78,16 +86,17 @@
|
|
|
import { genFileId, ElMessage } from 'element-plus';
|
|
|
import type { UploadInstance, UploadProps, UploadRawFile } from 'element-plus';
|
|
|
import { Close, Document, WarnTriangleFilled } from '@element-plus/icons-vue';
|
|
|
- import type { BatchImportProps, ErrorDetailItem, ImportResponseData } from './types';
|
|
|
+ import type { BatchImportProps, ErrorDetailItem, ImportResponseData, ImportResponseDataPerson } from './types';
|
|
|
|
|
|
const props = withDefaults(defineProps<BatchImportProps>(), {
|
|
|
templateName: '下载模板',
|
|
|
showTemplate: true,
|
|
|
+ responseType: 'detailed',
|
|
|
});
|
|
|
|
|
|
const emit = defineEmits<{
|
|
|
(e: 'close'): void;
|
|
|
- (e: 'update'): void;
|
|
|
+ (e: 'update', data?: ImportResponseData | ImportResponseDataPerson | string): void;
|
|
|
}>();
|
|
|
|
|
|
const upload = ref<UploadInstance>();
|
|
|
@@ -96,6 +105,9 @@
|
|
|
const sucCount = ref<number>(0);
|
|
|
const errCount = ref<number>(0);
|
|
|
const errDetail = ref<ErrorDetailItem[]>([]);
|
|
|
+ const simpleMessage = ref<string>('');
|
|
|
+ const lastResponseData = ref<ImportResponseData | ImportResponseDataPerson | string | undefined>(undefined);
|
|
|
+ const isPersonResponseType = ref<boolean>(false);
|
|
|
|
|
|
// 监听visible属性变化,重置状态
|
|
|
watch(
|
|
|
@@ -107,6 +119,9 @@
|
|
|
sucCount.value = 0;
|
|
|
errCount.value = 0;
|
|
|
errDetail.value = [];
|
|
|
+ simpleMessage.value = '';
|
|
|
+ lastResponseData.value = undefined;
|
|
|
+ isPersonResponseType.value = false;
|
|
|
// 清空上传文件
|
|
|
if (upload.value) {
|
|
|
upload.value.clearFiles();
|
|
|
@@ -178,29 +193,111 @@
|
|
|
}));
|
|
|
}
|
|
|
|
|
|
- const handleUploadSuccess = (response: { data: ImportResponseData }) => {
|
|
|
- sucCount.value = response.data.successCount;
|
|
|
- errCount.value = response.data.failCount;
|
|
|
- errDetail.value = response.data.failInfoList;
|
|
|
+ const handleUploadSuccess = (response: { data: ImportResponseData | ImportResponseDataPerson | string } | string) => {
|
|
|
+ // 简单模式:处理字符串返回
|
|
|
+ if (props.responseType === 'simple') {
|
|
|
+ try {
|
|
|
+ // 提取字符串消息
|
|
|
+ let message = '';
|
|
|
+ if (typeof response === 'string') {
|
|
|
+ message = response;
|
|
|
+ simpleMessage.value = response;
|
|
|
+ lastResponseData.value = response;
|
|
|
+ } else if (typeof response.data === 'string') {
|
|
|
+ message = response.data;
|
|
|
+ simpleMessage.value = response.data;
|
|
|
+ lastResponseData.value = response.data;
|
|
|
+ } else if (response.data && typeof response.data === 'object' && 'message' in response.data) {
|
|
|
+ message = (response.data as { message: string }).message;
|
|
|
+ simpleMessage.value = message;
|
|
|
+ lastResponseData.value = message;
|
|
|
+ } else {
|
|
|
+ message = '导入完成';
|
|
|
+ simpleMessage.value = message;
|
|
|
+ lastResponseData.value = message;
|
|
|
+ }
|
|
|
+ // 显示错误弹窗
|
|
|
+ dialogVisibleErr.value = true;
|
|
|
+ // 通知父组件更新数据(但不关闭主弹窗)
|
|
|
+ emit('update', lastResponseData.value);
|
|
|
+ } catch (error) {
|
|
|
+ ElMessage({
|
|
|
+ message: '导入失败',
|
|
|
+ type: 'error',
|
|
|
+ });
|
|
|
+ emit('update', lastResponseData.value);
|
|
|
+ }
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 详细模式:兼容字符串和对象返回
|
|
|
+ let detailedData: ImportResponseData | ImportResponseDataPerson | undefined;
|
|
|
+ if (typeof response === 'string') {
|
|
|
+ // 如果后端返回纯字符串(理论不应出现,兜底)
|
|
|
+ detailedData = undefined;
|
|
|
+ } else if (response && typeof response.data === 'object') {
|
|
|
+ const responseData = response.data;
|
|
|
+ // 判断是 ImportResponseDataPerson 类型(有 successMembers 属性)
|
|
|
+ if ('successMembers' in responseData) {
|
|
|
+ detailedData = responseData as ImportResponseDataPerson;
|
|
|
+ isPersonResponseType.value = true;
|
|
|
+ // ImportResponseDataPerson 类型:在 dialog 中只显示 message
|
|
|
+ simpleMessage.value = detailedData.message || '导入完成';
|
|
|
+ // 保存返回值
|
|
|
+ lastResponseData.value = detailedData;
|
|
|
+ // 显示错误弹窗
|
|
|
+ dialogVisibleErr.value = true;
|
|
|
+ // 触发 update 事件,通知父组件更新数据(但不关闭主弹窗)
|
|
|
+ emit('update', detailedData);
|
|
|
+ return;
|
|
|
+ } else {
|
|
|
+ // 否则是 ImportResponseData 类型(有 successCount, failCount, failInfoList)
|
|
|
+ detailedData = responseData as ImportResponseData;
|
|
|
+ isPersonResponseType.value = false;
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ // 如果 response.data 是字符串(异常情况,兜底)
|
|
|
+ detailedData = undefined;
|
|
|
+ isPersonResponseType.value = false;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 保存返回值
|
|
|
+ lastResponseData.value = detailedData;
|
|
|
+
|
|
|
+ // 处理 ImportResponseData 类型(有 successCount, failCount, failInfoList)
|
|
|
+ if (detailedData && 'successCount' in detailedData) {
|
|
|
+ const importData = detailedData as ImportResponseData;
|
|
|
+ sucCount.value = importData.successCount;
|
|
|
+ errCount.value = importData.failCount;
|
|
|
+ errDetail.value = importData.failInfoList;
|
|
|
+ } else {
|
|
|
+ sucCount.value = 0;
|
|
|
+ errCount.value = 0;
|
|
|
+ errDetail.value = [];
|
|
|
+ }
|
|
|
|
|
|
try {
|
|
|
if (sucCount.value != 0 && errCount.value === 0 && errDetail.value.length === 0) {
|
|
|
+ // 完全成功:通知父组件更新数据,然后关闭弹窗
|
|
|
ElMessage({
|
|
|
message: `导入成功!共导入${sucCount.value}条记录`,
|
|
|
type: 'success',
|
|
|
});
|
|
|
- emit('update');
|
|
|
+ emit('update', detailedData);
|
|
|
emit('close');
|
|
|
} else {
|
|
|
- dialogVisibleErr.value = true; // 显示错误dialog
|
|
|
+ // 有错误:显示错误弹窗,通知父组件更新数据(可选)
|
|
|
+ dialogVisibleErr.value = true;
|
|
|
errDetail.value = mergeFailReasons(errDetail.value);
|
|
|
+ // 可选:如果父组件需要知道有错误,可以触发 update
|
|
|
+ // emit('update', detailedData);
|
|
|
}
|
|
|
} catch (error) {
|
|
|
ElMessage({
|
|
|
message: '导入失败',
|
|
|
type: 'error',
|
|
|
});
|
|
|
- emit('update');
|
|
|
+ emit('update', lastResponseData.value);
|
|
|
}
|
|
|
};
|
|
|
|
|
|
@@ -225,8 +322,15 @@
|
|
|
isImportDisable.value = true;
|
|
|
};
|
|
|
|
|
|
- const handleClose = () => {
|
|
|
- emit('update');
|
|
|
+ // 主弹窗关闭:直接关闭,不触发 update(因为可能没有数据更新)
|
|
|
+ const handleMainClose = () => {
|
|
|
+ emit('close');
|
|
|
+ };
|
|
|
+
|
|
|
+ // 错误弹窗关闭:关闭错误弹窗,并关闭主弹窗
|
|
|
+ const handleErrorDialogClose = () => {
|
|
|
+ dialogVisibleErr.value = false;
|
|
|
+ emit('close');
|
|
|
};
|
|
|
</script>
|
|
|
|
|
|
@@ -297,6 +401,19 @@
|
|
|
.el-dialog__body {
|
|
|
padding: 20px;
|
|
|
|
|
|
+ .simple-message {
|
|
|
+ font-size: 16px;
|
|
|
+ line-height: 1.5;
|
|
|
+ text-align: center;
|
|
|
+ padding: 40px 20px;
|
|
|
+ min-height: 190px;
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ justify-content: center;
|
|
|
+ white-space: pre-wrap;
|
|
|
+ word-break: break-word;
|
|
|
+ }
|
|
|
+
|
|
|
.sum-count {
|
|
|
font-size: 20px;
|
|
|
display: flex;
|