Files
ccdi/ruoyi-ui/src/views/ccdiIntermediary/components/ImportDialog.vue

406 lines
10 KiB
Vue

<template>
<div>
<el-dialog
:title="title"
:visible.sync="visible"
width="550px"
center
append-to-body
@open="handleDialogOpen"
@close="handleDialogClose"
:close-on-click-modal="false"
:close-on-press-escape="false"
custom-class="import-dialog-wrapper"
>
<div v-show="isUploading" class="import-loading-overlay">
<i class="el-icon-loading"></i>
<p>正在导入中,请稍候...</p>
</div>
<el-form label-position="top" size="medium">
<el-form-item label="导入说明">
<div class="scene-tips">
<p v-for="item in sceneTips" :key="item">{{ item }}</p>
</div>
</el-form-item>
<el-form-item label="选择文件">
<el-upload
ref="upload"
:limit="1"
accept=".xlsx, .xls"
:headers="headers"
:action="uploadUrl"
:disabled="isUploading"
:on-progress="handleFileUploadProgress"
:on-success="handleFileSuccess"
:on-error="handleFileError"
:on-change="handleFileChange"
:on-remove="handleFileRemove"
:auto-upload="false"
drag
>
<i class="el-icon-upload"></i>
<div class="el-upload__text">将文件拖到此处,<em>点击上传</em></div>
<div class="el-upload__tip" slot="tip">
仅支持 .xls .xlsx 格式,文件大小不超过 10MB
</div>
</el-upload>
</el-form-item>
<el-form-item>
<el-link type="primary" :underline="false" @click="handleDownloadTemplate">
<i class="el-icon-download"></i>
下载导入模板
</el-link>
</el-form-item>
</el-form>
<div slot="footer" class="dialog-footer">
<el-button
type="primary"
icon="el-icon-upload2"
:loading="isUploading"
:disabled="!isFileSelected"
@click="handleSubmit"
>
{{ isUploading ? '导入中...' : '开始导入' }}
</el-button>
<el-button :disabled="isUploading" icon="el-icon-close" @click="handleCancel">
</el-button>
</div>
</el-dialog>
<import-result-dialog
:visible.sync="importResultVisible"
:content="importResultContent"
title="导入结果"
@close="handleImportResultClose"
/>
</div>
</template>
<script>
import { getToken } from "@/utils/auth";
import {
getEnterpriseRelationImportStatus,
getPersonImportStatus
} from "@/api/ccdiIntermediary";
import ImportResultDialog from "@/components/ImportResultDialog.vue";
const PERSON_SCENE = "person";
const ENTERPRISE_RELATION_SCENE = "enterpriseRelation";
export default {
name: "ImportDialog",
components: { ImportResultDialog },
props: {
visible: {
type: Boolean,
default: false
},
title: {
type: String,
default: "数据导入"
},
scene: {
type: String,
default: PERSON_SCENE
}
},
data() {
return {
headers: { Authorization: "Bearer " + getToken() },
isUploading: false,
isFileSelected: false,
importResultVisible: false,
importResultContent: "",
pollingTimer: null,
currentTaskId: null
};
},
computed: {
sceneConfig() {
if (this.scene === ENTERPRISE_RELATION_SCENE) {
return {
uploadPath: "/ccdi/intermediary/importEnterpriseRelationData",
templatePath: "ccdi/intermediary/importEnterpriseRelationTemplate",
templateName: "中介实体关联关系导入模板",
statusApi: getEnterpriseRelationImportStatus,
tips: [
"只导入中介与机构关系;",
"统一社会信用代码必须已存在于系统机构表。"
]
};
}
return {
uploadPath: "/ccdi/intermediary/importPersonData",
templatePath: "ccdi/intermediary/importPersonTemplate",
templateName: "中介和亲属信息导入模板",
statusApi: getPersonImportStatus,
tips: [
"personSubType 为字典下拉;",
"本人行 relatedNumId 为空;",
"亲属行 relatedNumId 填关联中介本人证件号码。"
]
};
},
uploadUrl() {
const baseUrl = process.env.VUE_APP_BASE_API;
return `${baseUrl}${this.sceneConfig.uploadPath}`;
},
sceneTips() {
return this.sceneConfig.tips;
}
},
methods: {
handleDialogOpen() {
this.isFileSelected = false;
},
handleDialogClose() {
if (this.$refs.upload) {
this.$refs.upload.clearFiles();
}
this.isFileSelected = false;
this.$emit("close");
},
handleCancel() {
this.$emit("update:visible", false);
},
handleDownloadTemplate() {
this.download(
this.sceneConfig.templatePath,
{},
`${this.sceneConfig.templateName}_${new Date().getTime()}.xlsx`
);
},
handleFileUploadProgress() {
this.isUploading = true;
},
handleFileChange(file, fileList) {
this.isFileSelected = fileList.length > 0;
},
handleFileRemove(file, fileList) {
this.isFileSelected = fileList.length > 0;
},
handleFileSuccess(response) {
this.isUploading = false;
if (response.code === 200 && response.data && response.data.taskId) {
const taskId = response.data.taskId;
this.currentTaskId = taskId;
this.$notify({
title: "导入任务已提交",
message: "正在后台处理中,处理完成后将通知您",
type: "info",
duration: 3000
});
this.$emit("task-created", {
scene: this.scene,
taskId,
status: "PROCESSING"
});
this.$emit("update:visible", false);
this.$emit("success", { scene: this.scene, taskId });
if (this.$refs.upload) {
this.$refs.upload.clearFiles();
}
this.startImportStatusPolling(taskId);
} else {
this.$modal.msgError(response.msg || "导入失败");
}
},
handleImportResultClose() {
this.importResultVisible = false;
this.importResultContent = "";
},
handleFileError() {
this.isUploading = false;
this.$modal.msgError("导入失败,请检查文件格式是否正确");
if (this.$refs.upload) {
this.$refs.upload.clearFiles();
}
},
handleSubmit() {
this.$emit("clear-import-history", this.scene);
this.$refs.upload.submit();
},
startImportStatusPolling(taskId) {
let pollCount = 0;
const maxPolls = 150;
this.pollingTimer = setInterval(async () => {
try {
pollCount++;
if (pollCount > maxPolls) {
clearInterval(this.pollingTimer);
this.$modal.msgWarning("导入任务处理超时,请联系管理员");
return;
}
const response = await this.sceneConfig.statusApi(taskId);
if (response.data && response.data.status !== "PROCESSING") {
clearInterval(this.pollingTimer);
this.handleImportComplete(response.data);
}
} catch (error) {
clearInterval(this.pollingTimer);
this.$modal.msgError("查询导入状态失败: " + error.message);
}
}, 2000);
},
handleImportComplete(statusResult) {
if (statusResult.status === "SUCCESS") {
this.$notify({
title: "导入完成",
message: `全部成功!共导入${statusResult.totalCount}条数据`,
type: "success",
duration: 5000
});
} else if (statusResult.failureCount > 0) {
this.$notify({
title: "导入完成",
message: `成功${statusResult.successCount}条,失败${statusResult.failureCount}`,
type: "warning",
duration: 5000
});
}
this.$emit("import-complete", {
scene: this.scene,
taskId: statusResult.taskId,
hasFailures: statusResult.failureCount > 0,
totalCount: statusResult.totalCount,
successCount: statusResult.successCount,
failureCount: statusResult.failureCount
});
}
},
beforeDestroy() {
if (this.pollingTimer) {
clearInterval(this.pollingTimer);
this.pollingTimer = null;
}
}
};
</script>
<style scoped lang="scss">
::v-deep .el-form {
.el-form-item {
margin-bottom: 22px;
}
.el-upload {
width: 100%;
.el-upload-dragger {
width: 100% !important;
height: 140px;
box-sizing: border-box;
.el-icon-upload {
font-size: 50px;
color: #409EFF;
margin: 15px 0 10px;
}
.el-upload__text {
font-size: 14px;
color: #606266;
em {
color: #409EFF;
font-style: normal;
}
}
}
}
.el-upload__tip {
font-size: 12px;
color: #909399;
margin-top: 8px;
line-height: 1.5;
}
}
.scene-tips {
padding: 12px 14px;
background: #f8fafc;
border: 1px solid #dde3ec;
border-radius: 3px;
color: #606266;
line-height: 1.7;
p {
margin: 0;
}
}
.dialog-footer {
text-align: center;
padding: 5px 0 0;
.el-button {
min-width: 110px;
margin: 0 8px;
border-radius: 4px;
}
}
.import-loading-overlay {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-color: rgba(255, 255, 255, 0.9);
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
z-index: 10000;
.el-icon-loading {
font-size: 48px;
color: #409EFF;
animation: rotating 2s linear infinite;
}
p {
margin-top: 15px;
font-size: 14px;
color: #606266;
}
}
@keyframes rotating {
from {
transform: rotate(0deg);
}
to {
transform: rotate(360deg);
}
}
::v-deep .import-dialog-wrapper {
border-radius: 6px;
overflow: hidden;
}
::v-deep .import-dialog-wrapper .el-dialog__header {
border-bottom: 1px solid #dde3ec;
background: #ffffff;
}
::v-deep .import-dialog-wrapper .el-dialog__body {
background: #f8fafc;
}
</style>