20260613-自定义导入组件使用文档.md 11 KB

自定义导入组件(CustomImportModal)使用文档

一、组件概述

CustomImportModal 是一个封装的 Excel 导入弹窗组件,提供以下能力:

  1. 文件上传:选择 .xls / .xlsx 文件,内置前端格式和大小校验(最大 2MB)
  2. 模板下载:支持从后端下载 Excel 导入模板(含表头 + 示例数据)
  3. 权限控制:可通过 auth 属性配置导入按钮的权限标识
  4. 导入校验:上传后由后端进行字典反向翻译、必填校验、去重校验
  5. 错误展示:校验失败时以表格形式展示每行错误数据及具体错误原因
  6. 导入成功回调:导入成功后触发 success 事件,通常用于刷新列表

组件只会处理 code === 201(部分成功)、code === 500(有结构化校验结果 / 普通失败)、其他(成功)三种情况。业务后端需按约定格式返回数据。


二、组件 Props

属性 类型 必填 默认值 说明
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 事件监听)

三、组件 Events

事件名 参数 触发时机
success 文件导入成功且无错误;或部分成功(code=201)关闭弹窗后

四、后端 API 约定

4.1 导入接口 POST {importUrl}

请求方式multipart/form-data,字段名为 file

响应格式(与组件逻辑的对应关系):

响应 code 组件行为
200 或 其他非 201/500 提示 message,关闭弹窗,触发 success
201 部分成功,弹出警告框,若 result.fileUrl 存在则提供错误详情下载链接,关闭弹窗,触发 success
500resultImportResultItem[] 非空数组 弹出"导入校验结果"表格弹窗,展示每行错误详情,不关闭导入手弹窗,不触发 success
500result 不是数组或为空 仅提示 message 错误信息

4.2 ImportResultItem 结构

public class ImportResultItem {
    private Integer rowNum;                    // Excel 行号
    private Map<String, String> rowData;       // 该行的列名→列值映射
    private String errorMsg;                   // 错误信息(多个错误用分号拼接)
}

4.3 模板下载接口 GET {downloadUrl}

返回 Excel 文件的二进制流(Content-Type: application/vnd.openxmlformats-officedocument.spreadsheetml.sheet)。


五、前端使用示例

以"个人基本信息"页面的导入为例。

5.1 API 定义(PersonalInfo.api.ts)

// 文件: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;

5.2 Vue 模板中使用

<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>

六、后端实现参考

6.1 Controller 层(PersonalInfoController.java)

// 文件: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("文件导入失败!");
}

6.2 Service 层核心逻辑(PersonalInfoServiceImpl.importExcelData)

步骤 说明
1. 字典反向翻译 根据 IMPORT_DICT_FIELD_MAP(字段→字典编码映射),批量查询字典项,构建 文本→字典码 的反向映射表,将 Excel 中的字典中文文本转为字典码值
2. 证件号码去重 检查 Excel 内部是否有重复证件号 + 数据库中是否已存在相同证件号,重复则标记错误
3. 逐行校验 捕获原始列值 → 字典文本转码 → 必填字段校验 → 补充成对字段(如户口所在地和区域名称) → 设置数据来源为"2"(导入)
4. 批量保存或返回错误 若有任何错误行则不保存,直接返回 ImportResultItem 列表;全部通过则 saveBatch 批量插入

涉及的字典字段映射(IMPORT_DICT_FIELD_MAP)

  • 证件类型 → IDType
  • 性别 → Gender
  • 民族 → Ethnicity
  • 国籍 → Nationality
  • 婚姻状况 → MaritalStatus
  • 学历 → Education
  • 政治面貌 → PoliticalStatus
  • 工作经验 → WorkExperience
  • 户口类型 → HouseholdType
  • 求职人员类别 → JobSeekerCategory
  • 求职状态 → JobSeekerStatus

6.3 模板下载接口

/**
 * 下载导入模板
 * 包含指定字段的表头 + 一条示例数据,方便用户参照填写
 */
@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 定义:getImportUrlgetImportTemplateUrl
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