refactor: 抽离导入结果弹窗为通用组件并适配所有导入页面
新增组件: - ImportResultDialog.vue: 通用导入结果弹窗组件 * 支持HTML内容渲染 * 60vh高度限制,内容独立滚动 * 美化滚动条样式(6px宽度、圆角设计) * 提供visible、content、title等props配置 适配页面: 1. 员工信息管理页面 (ccdiEmployee) - 使用ImportResultDialog组件替代内嵌Dialog - 简化数据状态管理(importResultVisible、importResultContent) - 添加handleImportResultClose方法处理关闭事件 2. 员工招聘信息页面 (ccdiStaffRecruitment) - 使用ImportResultDialog替代$modal.msgSuccess/msgError - 统一导入结果展示方式 - 支持HTML格式的错误列表展示 3. 中介黑名单导入组件 (ccdiIntermediary/ImportDialog) - 使用ImportResultDialog替代$msgbox - 保留原有的消息解析逻辑(成功/失败分类处理) - 移除内联样式,使用组件样式 优势: - 统一导入结果展示样式和交互体验 - 组件复用,减少代码重复 - 便于维护和扩展(一处修改,全局生效) - 自适应滚动,支持大量失败数据展示 Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
108
ruoyi-ui/src/components/ImportResultDialog.vue
Normal file
108
ruoyi-ui/src/components/ImportResultDialog.vue
Normal file
@@ -0,0 +1,108 @@
|
|||||||
|
<template>
|
||||||
|
<el-dialog
|
||||||
|
:title="title"
|
||||||
|
:visible.sync="dialogVisible"
|
||||||
|
:width="width"
|
||||||
|
:append-to-body="appendToBody"
|
||||||
|
:close-on-click-modal="closeOnClickModal"
|
||||||
|
@close="handleClose"
|
||||||
|
class="import-result-dialog-wrapper"
|
||||||
|
>
|
||||||
|
<div class="import-result-content" v-html="content"></div>
|
||||||
|
<div slot="footer" class="dialog-footer">
|
||||||
|
<el-button type="primary" @click="handleClose">确 定</el-button>
|
||||||
|
</div>
|
||||||
|
</el-dialog>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
name: "ImportResultDialog",
|
||||||
|
props: {
|
||||||
|
// 控制弹窗显示/隐藏
|
||||||
|
visible: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false
|
||||||
|
},
|
||||||
|
// 弹窗标题
|
||||||
|
title: {
|
||||||
|
type: String,
|
||||||
|
default: "导入结果"
|
||||||
|
},
|
||||||
|
// 导入结果内容(支持HTML)
|
||||||
|
content: {
|
||||||
|
type: String,
|
||||||
|
default: ""
|
||||||
|
},
|
||||||
|
// 弹窗宽度
|
||||||
|
width: {
|
||||||
|
type: String,
|
||||||
|
default: "700px"
|
||||||
|
},
|
||||||
|
// 是否插入到body元素上
|
||||||
|
appendToBody: {
|
||||||
|
type: Boolean,
|
||||||
|
default: true
|
||||||
|
},
|
||||||
|
// 是否可以通过点击modal关闭
|
||||||
|
closeOnClickModal: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
dialogVisible: {
|
||||||
|
get() {
|
||||||
|
return this.visible;
|
||||||
|
},
|
||||||
|
set(val) {
|
||||||
|
this.$emit("update:visible", val);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
handleClose() {
|
||||||
|
this.dialogVisible = false;
|
||||||
|
this.$emit("close");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
/* 导入结果弹窗样式 */
|
||||||
|
.import-result-dialog-wrapper .import-result-content {
|
||||||
|
max-height: 60vh;
|
||||||
|
overflow-y: auto;
|
||||||
|
overflow-x: hidden;
|
||||||
|
padding: 10px 0;
|
||||||
|
line-height: 1.8;
|
||||||
|
font-size: 14px;
|
||||||
|
color: #606266;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 滚动条美化 - WebKit浏览器(Chrome/Safari/Edge) */
|
||||||
|
.import-result-dialog-wrapper .import-result-content::-webkit-scrollbar {
|
||||||
|
width: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.import-result-dialog-wrapper .import-result-content::-webkit-scrollbar-track {
|
||||||
|
background: #f5f7fa;
|
||||||
|
border-radius: 3px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.import-result-dialog-wrapper .import-result-content::-webkit-scrollbar-thumb {
|
||||||
|
background: #c0c4cc;
|
||||||
|
border-radius: 3px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.import-result-dialog-wrapper .import-result-content::-webkit-scrollbar-thumb:hover {
|
||||||
|
background: #909399;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Firefox滚动条 */
|
||||||
|
.import-result-dialog-wrapper .import-result-content {
|
||||||
|
scrollbar-width: thin;
|
||||||
|
scrollbar-color: #c0c4cc #f5f7fa;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -249,18 +249,12 @@
|
|||||||
</el-dialog>
|
</el-dialog>
|
||||||
|
|
||||||
<!-- 导入结果对话框 -->
|
<!-- 导入结果对话框 -->
|
||||||
<el-dialog
|
<import-result-dialog
|
||||||
:title="importResult.title"
|
:visible.sync="importResultVisible"
|
||||||
:visible.sync="importResult.open"
|
:content="importResultContent"
|
||||||
width="700px"
|
title="导入结果"
|
||||||
append-to-body
|
@close="handleImportResultClose"
|
||||||
class="import-result-dialog-wrapper"
|
/>
|
||||||
>
|
|
||||||
<div class="import-result-content" v-html="importResult.content"></div>
|
|
||||||
<div slot="footer" class="dialog-footer">
|
|
||||||
<el-button type="primary" @click="importResult.open = false">确 定</el-button>
|
|
||||||
</div>
|
|
||||||
</el-dialog>
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@@ -270,6 +264,7 @@ import {deptTreeSelect} from "@/api/system/user";
|
|||||||
import {getToken} from "@/utils/auth";
|
import {getToken} from "@/utils/auth";
|
||||||
import Treeselect from "@riophae/vue-treeselect";
|
import Treeselect from "@riophae/vue-treeselect";
|
||||||
import "@riophae/vue-treeselect/dist/vue-treeselect.css";
|
import "@riophae/vue-treeselect/dist/vue-treeselect.css";
|
||||||
|
import ImportResultDialog from "@/components/ImportResultDialog.vue";
|
||||||
|
|
||||||
// 身份证号校验正则
|
// 身份证号校验正则
|
||||||
const idCardPattern = /^[1-9]\d{5}(18|19|20)\d{2}(0[1-9]|1[0-2])(0[1-9]|[12]\d|3[01])\d{3}[\dXx]$/;
|
const idCardPattern = /^[1-9]\d{5}(18|19|20)\d{2}(0[1-9]|1[0-2])(0[1-9]|[12]\d|3[01])\d{3}[\dXx]$/;
|
||||||
@@ -278,7 +273,7 @@ const phonePattern = /^1[3|4|5|6|7|8|9][0-9]\d{8}$/;
|
|||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: "Employee",
|
name: "Employee",
|
||||||
components: { Treeselect },
|
components: { Treeselect, ImportResultDialog },
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
// 遮罩层
|
// 遮罩层
|
||||||
@@ -362,11 +357,8 @@ export default {
|
|||||||
url: process.env.VUE_APP_BASE_API + "/ccdi/employee/importData"
|
url: process.env.VUE_APP_BASE_API + "/ccdi/employee/importData"
|
||||||
},
|
},
|
||||||
// 导入结果弹窗
|
// 导入结果弹窗
|
||||||
importResult: {
|
importResultVisible: false,
|
||||||
open: false,
|
importResultContent: ""
|
||||||
title: "导入结果",
|
|
||||||
content: ""
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
created() {
|
created() {
|
||||||
@@ -522,8 +514,13 @@ export default {
|
|||||||
this.upload.open = false;
|
this.upload.open = false;
|
||||||
this.getList();
|
this.getList();
|
||||||
// 显示导入结果弹窗
|
// 显示导入结果弹窗
|
||||||
this.importResult.content = response.msg;
|
this.importResultContent = response.msg;
|
||||||
this.importResult.open = true;
|
this.importResultVisible = true;
|
||||||
|
},
|
||||||
|
// 导入结果弹窗关闭
|
||||||
|
handleImportResultClose() {
|
||||||
|
this.importResultVisible = false;
|
||||||
|
this.importResultContent = "";
|
||||||
},
|
},
|
||||||
// 提交上传文件
|
// 提交上传文件
|
||||||
submitFileForm() {
|
submitFileForm() {
|
||||||
@@ -651,42 +648,6 @@ export default {
|
|||||||
font-size: 13px;
|
font-size: 13px;
|
||||||
margin-right: 8px;
|
margin-right: 8px;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 导入结果弹窗样式 */
|
|
||||||
.import-result-dialog-wrapper .import-result-content {
|
|
||||||
max-height: 60vh;
|
|
||||||
overflow-y: auto;
|
|
||||||
overflow-x: hidden;
|
|
||||||
padding: 10px 0;
|
|
||||||
line-height: 1.8;
|
|
||||||
font-size: 14px;
|
|
||||||
color: #606266;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 滚动条美化 */
|
|
||||||
.import-result-dialog-wrapper .import-result-content::-webkit-scrollbar {
|
|
||||||
width: 6px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.import-result-dialog-wrapper .import-result-content::-webkit-scrollbar-track {
|
|
||||||
background: #f5f7fa;
|
|
||||||
border-radius: 3px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.import-result-dialog-wrapper .import-result-content::-webkit-scrollbar-thumb {
|
|
||||||
background: #c0c4cc;
|
|
||||||
border-radius: 3px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.import-result-dialog-wrapper .import-result-content::-webkit-scrollbar-thumb:hover {
|
|
||||||
background: #909399;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Firefox滚动条 */
|
|
||||||
.import-result-dialog-wrapper .import-result-content {
|
|
||||||
scrollbar-width: thin;
|
|
||||||
scrollbar-color: #c0c4cc #f5f7fa;
|
|
||||||
}
|
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
<!-- 旧版MessageBox样式已废弃,现使用Dialog组件 -->
|
<!-- 导入结果弹窗已抽离为独立组件 ImportResultDialog -->
|
||||||
|
|||||||
@@ -85,13 +85,23 @@
|
|||||||
</el-button>
|
</el-button>
|
||||||
</div>
|
</div>
|
||||||
</el-dialog>
|
</el-dialog>
|
||||||
|
|
||||||
|
<!-- 导入结果对话框 -->
|
||||||
|
<import-result-dialog
|
||||||
|
:visible.sync="importResultVisible"
|
||||||
|
:content="importResultContent"
|
||||||
|
title="导入结果"
|
||||||
|
@close="handleImportResultClose"
|
||||||
|
/>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import {getToken} from "@/utils/auth";
|
import {getToken} from "@/utils/auth";
|
||||||
|
import ImportResultDialog from "@/components/ImportResultDialog.vue";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: "ImportDialog",
|
name: "ImportDialog",
|
||||||
|
components: { ImportResultDialog },
|
||||||
props: {
|
props: {
|
||||||
visible: {
|
visible: {
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
@@ -110,7 +120,10 @@ export default {
|
|||||||
},
|
},
|
||||||
headers: { Authorization: "Bearer " + getToken() },
|
headers: { Authorization: "Bearer " + getToken() },
|
||||||
isUploading: false,
|
isUploading: false,
|
||||||
isFileSelected: false
|
isFileSelected: false,
|
||||||
|
// 导入结果弹窗
|
||||||
|
importResultVisible: false,
|
||||||
|
importResultContent: ""
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
@@ -182,15 +195,16 @@ export default {
|
|||||||
displayMessage = lines[0]; // 只取错误部分
|
displayMessage = lines[0]; // 只取错误部分
|
||||||
}
|
}
|
||||||
|
|
||||||
this.$msgbox({
|
// 显示导入结果弹窗
|
||||||
title: '导入结果',
|
this.importResultContent = displayMessage;
|
||||||
dangerouslyUseHTMLString: true,
|
this.importResultVisible = true;
|
||||||
message: `<div style="overflow-y: auto; max-height: 60vh; padding-right: 10px; line-height: 1.6;">${displayMessage}</div>`,
|
|
||||||
confirmButtonText: '确定',
|
|
||||||
customClass: 'import-result-dialog'
|
|
||||||
});
|
|
||||||
this.$refs.upload.clearFiles();
|
this.$refs.upload.clearFiles();
|
||||||
},
|
},
|
||||||
|
// 导入结果弹窗关闭
|
||||||
|
handleImportResultClose() {
|
||||||
|
this.importResultVisible = false;
|
||||||
|
this.importResultContent = "";
|
||||||
|
},
|
||||||
handleFileError() {
|
handleFileError() {
|
||||||
this.isUploading = false;
|
this.isUploading = false;
|
||||||
this.$modal.msgError("导入失败,请检查文件格式是否正确");
|
this.$modal.msgError("导入失败,请检查文件格式是否正确");
|
||||||
|
|||||||
@@ -336,12 +336,21 @@
|
|||||||
<el-button @click="upload.open = false" :disabled="upload.isUploading">取 消</el-button>
|
<el-button @click="upload.open = false" :disabled="upload.isUploading">取 消</el-button>
|
||||||
</div>
|
</div>
|
||||||
</el-dialog>
|
</el-dialog>
|
||||||
|
|
||||||
|
<!-- 导入结果对话框 -->
|
||||||
|
<import-result-dialog
|
||||||
|
:visible.sync="importResultVisible"
|
||||||
|
:content="importResultContent"
|
||||||
|
title="导入结果"
|
||||||
|
@close="handleImportResultClose"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { addStaffRecruitment, delStaffRecruitment, getStaffRecruitment, listStaffRecruitment, updateStaffRecruitment, importTemplate } from "@/api/ccdiStaffRecruitment";
|
import { addStaffRecruitment, delStaffRecruitment, getStaffRecruitment, listStaffRecruitment, updateStaffRecruitment, importTemplate } from "@/api/ccdiStaffRecruitment";
|
||||||
import { getToken } from "@/utils/auth";
|
import { getToken } from "@/utils/auth";
|
||||||
|
import ImportResultDialog from "@/components/ImportResultDialog.vue";
|
||||||
|
|
||||||
// 身份证号校验正则
|
// 身份证号校验正则
|
||||||
const idCardPattern = /^[1-9]\d{5}(18|19|20)\d{2}(0[1-9]|1[0-2])(0[1-9]|[12]\d|3[01])\d{3}[\dXx]$/;
|
const idCardPattern = /^[1-9]\d{5}(18|19|20)\d{2}(0[1-9]|1[0-2])(0[1-9]|[12]\d|3[01])\d{3}[\dXx]$/;
|
||||||
@@ -350,6 +359,7 @@ const gradPattern = /^((19|20)\d{2})(0[1-9]|1[0-2])$/;
|
|||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: "StaffRecruitment",
|
name: "StaffRecruitment",
|
||||||
|
components: { ImportResultDialog },
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
// 遮罩层
|
// 遮罩层
|
||||||
@@ -453,7 +463,10 @@ export default {
|
|||||||
headers: { Authorization: "Bearer " + getToken() },
|
headers: { Authorization: "Bearer " + getToken() },
|
||||||
// 上传的地址
|
// 上传的地址
|
||||||
url: process.env.VUE_APP_BASE_API + "/ccdi/staffRecruitment/importData"
|
url: process.env.VUE_APP_BASE_API + "/ccdi/staffRecruitment/importData"
|
||||||
}
|
},
|
||||||
|
// 导入结果弹窗
|
||||||
|
importResultVisible: false,
|
||||||
|
importResultContent: ""
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
created() {
|
created() {
|
||||||
@@ -590,15 +603,18 @@ export default {
|
|||||||
// 文件上传成功处理
|
// 文件上传成功处理
|
||||||
handleFileSuccess(response, file, fileList) {
|
handleFileSuccess(response, file, fileList) {
|
||||||
this.upload.isUploading = false;
|
this.upload.isUploading = false;
|
||||||
if (response.code === 200) {
|
|
||||||
this.$modal.msgSuccess(response.msg);
|
|
||||||
this.upload.open = false;
|
this.upload.open = false;
|
||||||
this.getList();
|
this.getList();
|
||||||
} else {
|
// 显示导入结果弹窗
|
||||||
this.$modal.msgError(response.msg);
|
this.importResultContent = response.msg || response;
|
||||||
}
|
this.importResultVisible = true;
|
||||||
this.$refs.upload.clearFiles();
|
this.$refs.upload.clearFiles();
|
||||||
},
|
},
|
||||||
|
// 导入结果弹窗关闭
|
||||||
|
handleImportResultClose() {
|
||||||
|
this.importResultVisible = false;
|
||||||
|
this.importResultContent = "";
|
||||||
|
},
|
||||||
// 提交上传文件
|
// 提交上传文件
|
||||||
submitFileForm() {
|
submitFileForm() {
|
||||||
this.$refs.upload.submit();
|
this.$refs.upload.submit();
|
||||||
|
|||||||
Reference in New Issue
Block a user