CustomImportModal 是一个封装的 Excel 导入弹窗组件,提供以下能力:
.xls / .xlsx 文件,内置前端格式和大小校验(最大 2MB)auth 属性配置导入按钮的权限标识success 事件,通常用于刷新列表组件只会处理
code === 201(部分成功)、code === 500(有结构化校验结果 / 普通失败)、其他(成功)三种情况。业务后端需按约定格式返回数据。
| 属性 | 类型 | 必填 | 默认值 | 说明 |
|---|---|---|---|---|
auth |
String |
否 | '' |
按钮权限标识,为空时按钮不进行权限控制 |
importUrl |
String |
是 | - | 后端 Excel 导入接口 URL |
downloadUrl |
String |
否 | '' |
后端模板下载接口 URL,为空时不显示下载模板区域 |
templateName |
String |
否 | '下载导入模板' |
模板下载按钮显示的文字 |
buttonText |
String |
否 | '导入' |
触发按钮显示文字 |
buttonIcon |
String |
否 | 'ant-design:import-outlined' |
触发按钮图标 |
buttonType |
String |
否 | 'primary' |
触发按钮类型(Ant Design Button type) |
success |
Function |
否 | - | 导入成功后的回调函数(也可通过 @success 事件监听) |
| 事件名 | 参数 | 触发时机 |
|---|---|---|
success |
无 | 文件导入成功且无错误;或部分成功(code=201)关闭弹窗后 |
POST {importUrl}请求方式:multipart/form-data,字段名为 file
响应格式(与组件逻辑的对应关系):
响应 code |
组件行为 |
|---|---|
200 或 其他非 201/500 |
提示 message,关闭弹窗,触发 success |
201 |
部分成功,弹出警告框,若 result.fileUrl 存在则提供错误详情下载链接,关闭弹窗,触发 success |
500 且 result 为 ImportResultItem[] 非空数组 |
弹出"导入校验结果"表格弹窗,展示每行错误详情,不关闭导入手弹窗,不触发 success |
500 且 result 不是数组或为空 |
仅提示 message 错误信息 |
ImportResultItem 结构public class ImportResultItem {
private Integer rowNum; // Excel 行号
private Map<String, String> rowData; // 该行的列名→列值映射
private String errorMsg; // 错误信息(多个错误用分号拼接)
}
GET {downloadUrl}返回 Excel 文件的二进制流(Content-Type: application/vnd.openxmlformats-officedocument.spreadsheetml.sheet)。
以"个人基本信息"页面的导入为例。
// 文件:jeecgboot-vue3/src/views/recruitment/personal/PersonalInfo.api.ts
enum Api {
importExcel = '/personal/personalInfo/importExcel',
importTemplate = '/personal/personalInfo/importTemplate',
}
// 导入接口URL
export const getImportUrl = Api.importExcel;
// 模板下载接口URL
export const getImportTemplateUrl = Api.importTemplate;
<template>
<!-- 在表格标题区域内放置导入按钮 -->
<template #tableTitle>
<CustomImportModal
auth="personal:personal_info:importExcel"
:importUrl="getImportUrl"
:downloadUrl="getImportTemplateUrl"
templateName="个人基本信息导入模板"
@success="handleSuccess"
/>
</template>
</template>
<script lang="ts" setup>
import { CustomImportModal } from '/@/components/CustomImportModal';
import { getImportUrl, getImportTemplateUrl } from './PersonalInfo.api';
// 导入成功后刷新表格
function handleSuccess() {
reload();
}
</script>
// 文件:jeecg-boot/jeecg-boot-module/jeecg-module-zjrs/.../personal/controller/PersonalInfoController.java
/**
* 通过excel导入数据
* 解析Excel → 调用Service进行字典反向翻译、校验、补充字段、批量保存
*/
@RequiresPermissions("personal:personal_info:importExcel")
@RequestMapping(value = "/importExcel", method = RequestMethod.POST)
public Result<?> importExcel(HttpServletRequest request, HttpServletResponse response) {
MultipartHttpServletRequest multipartRequest = (MultipartHttpServletRequest) request;
Map<String, MultipartFile> fileMap = multipartRequest.getFileMap();
for (Map.Entry<String, MultipartFile> entity : fileMap.entrySet()) {
MultipartFile file = entity.getValue();
ImportParams params = new ImportParams();
params.setTitleRows(1);
params.setHeadRows(1);
params.setNeedSave(true);
try {
// 解析Excel为实体列表
List<PersonalInfo> list = ExcelImportUtil.importExcel(
file.getInputStream(), PersonalInfo.class, params);
// 调用Service进行校验和处理
List<ImportResultItem> resultItems = personalInfoService.importExcelData(list);
// 检查是否有校验错误
boolean hasError = false;
for (ImportResultItem item : resultItems) {
if (item.getErrorMsg() != null && !item.getErrorMsg().isEmpty()) {
hasError = true;
break;
}
}
if (hasError) {
return Result.error("导入数据校验失败", resultItems);
}
return Result.ok("文件导入成功!数据行数:" + list.size());
} catch (Exception e) {
String msg = e.getMessage();
if (msg != null && msg.contains("Duplicate entry")) {
return Result.error("文件导入失败:有重复数据!");
} else {
return Result.error("文件导入失败:" + e.getMessage());
}
}
}
return Result.error("文件导入失败!");
}
| 步骤 | 说明 |
|---|---|
| 1. 字典反向翻译 | 根据 IMPORT_DICT_FIELD_MAP(字段→字典编码映射),批量查询字典项,构建 文本→字典码 的反向映射表,将 Excel 中的字典中文文本转为字典码值 |
| 2. 证件号码去重 | 检查 Excel 内部是否有重复证件号 + 数据库中是否已存在相同证件号,重复则标记错误 |
| 3. 逐行校验 | 捕获原始列值 → 字典文本转码 → 必填字段校验 → 补充成对字段(如户口所在地和区域名称) → 设置数据来源为"2"(导入) |
| 4. 批量保存或返回错误 | 若有任何错误行则不保存,直接返回 ImportResultItem 列表;全部通过则 saveBatch 批量插入 |
涉及的字典字段映射(IMPORT_DICT_FIELD_MAP):
IDTypeGenderEthnicityNationalityMaritalStatusEducationPoliticalStatusWorkExperienceHouseholdTypeJobSeekerCategoryJobSeekerStatus/**
* 下载导入模板
* 包含指定字段的表头 + 一条示例数据,方便用户参照填写
*/
@RequiresPermissions("personal:personal_info:importExcel")
@RequestMapping(value = "/importTemplate")
public ModelAndView importTemplate(HttpServletRequest request, HttpServletResponse response) {
ModelAndView mv = new ModelAndView(new JeecgEntityExcelView());
String title = "个人基本信息导入模板";
mv.addObject(NormalExcelConstants.FILE_NAME, title);
mv.addObject(NormalExcelConstants.CLASS, PersonalInfo.class);
ExportParams exportParams = new ExportParams(title, "导入模板", ExcelType.XSSF);
mv.addObject(NormalExcelConstants.PARAMS, exportParams);
// 指定导出字段
String exportFields = "idType,idNumber,fullName,gender,birthDate,...";
mv.addObject(NormalExcelConstants.EXPORT_FIELDS, exportFields);
// 一条示例数据
mv.addObject(NormalExcelConstants.DATA_LIST, new ArrayList<>(List.of(buildExampleData())));
return mv;
}
用户点击"导入"按钮
│
▼
打开导入弹窗 → 用户选择Excel文件(前端校验格式、大小)
│
├── 可选:点击下载模板按钮 → GET {downloadUrl} → 返回Excel二进制流 → 浏览器下载
│
▼
用户点击"开始导入"
│
▼
POST {importUrl} (multipart/form-data, file字段)
│
▼
后端 Controller 解析Excel → Service 处理
├── 字典文本 → 字典码反向翻译
├── 证件号码去重(Excel内部 + 数据库)
├── 逐行必填校验
├── 补充关联字段
│
▼
┌─ 全部通过 ───────────────────────────────┐
│ saveBatch 批量保存 → Result.ok → 前端提示成功 → 关闭弹窗 → 触发 @success → 刷新列表
│
├─ 有校验错误 ─────────────────────────────┐
│ Result.error(resultItems) → 前端弹出"导入校验结果"弹窗 → 表格展示每行错误 → 用户关闭 → 可重新选择文件再导入
│
└─ 数据库唯一键冲突 ────────────────────────┐
Result.error("有重复数据") → 前端提示错误
| 文件 | 角色 |
|---|---|
jeecgboot-vue3/src/components/CustomImportModal/index.ts |
组件入口,注册全局组件 |
jeecgboot-vue3/src/components/CustomImportModal/src/CustomImportModal.vue |
导入弹窗核心实现(上传、校验展示、模板下载) |
jeecgboot-vue3/src/views/recruitment/personal/PersonalInfoList.vue |
使用示例:引入组件并配置属性 |
jeecgboot-vue3/src/views/recruitment/personal/PersonalInfo.api.ts |
API 定义:getImportUrl、getImportTemplateUrl |
jeecg-boot/.../personal/controller/PersonalInfoController.java |
后端导入接口 POST /importExcel、模板下载 GET /importTemplate |
jeecg-boot/.../personal/service/impl/PersonalInfoServiceImpl.java |
导入核心逻辑:字典翻译、去重校验、逐行校验、批量保存 |
jeecg-boot/.../personal/dto/ImportResultItem.java |
导入结果行 DTO:rowNum、rowData、errorMsg |