# 重点关注人员管理(3.2.1)- P0任务实现记录 ## 修改日期:2026-06-04 ## 一、任务说明 根据"需求与现有实现对比分析",实施P0优先级任务: 1. 数据库初始化(字典) 2. 详情页面完善(展示30个字段) 3. 补充查询条件 --- ## 二、修改文件清单 ### 2.1 新增文件 | 序号 | 文件路径 | 说明 | |:----:|----------|------| | 1 | `jeecg-boot/.../flyway/sql/mysql/V20260604_1__init_focus_personnel_dict.sql` | 字典数据初始化SQL脚本 | | 2 | `jeecg-boot/.../flyway/sql/mysql/V20260604_2__menu_insert_FocusPersonnel_buttons.sql` | 补充5个按钮权限SQL | | 3 | `jeecg-boot/.../flyway/sql/mysql/V20260604_3__alter_view_focus_personnel_list.sql` | 更新视图,添加birthDate和age计算字段(达梦兼容) | | 4 | `jeecg-boot/.../flyway/sql/mysql/V20260604_4__fix_minor_tag_dict.sql` | 修复旧数据与字典不匹配的映射更新SQL | | 5 | `jeecg-boot/.../focuspersonnel/entity/FocusPersonnelDetailVo.java` | 详情VO(含个人信息30个字段) | | 6 | `jeecgboot-vue3/.../focuspersonnel/components/FocusPersonnelDetail.vue` | 前端详情展示组件(Tab页签+概要头部) | ### 2.2 修改文件 | 序号 | 文件路径 | 修改内容 | |:----:|----------|----------| | 1 | `FocusPersonnel.java` | minorTag字段添加@Dict注解 | | 2 | `FocusPersonnelPageVo.java` | 添加@Dict注解和@Excel注解 | | 3 | `FocusPersonnelMapper.java` | 新增queryDetailById方法 | | 4 | `FocusPersonnelMapper.xml` | 新增detail查询SQL;列表补充5个查询条件;增加age范围过滤 | | 5 | `IFocusPersonnelService.java` | 新增queryDetailById接口 | | 6 | `FocusPersonnelServiceImpl.java` | 实现queryDetailById | | 7 | `FocusPersonnelController.java` | 新增queryDetailById;补充查询参数;增加ageBegin/ageEnd | | 8 | `FocusPersonnel.api.ts` | 新增queryDetailById API | | 9 | `FocusPersonnelList.vue` | **多次迭代**:最终改为查询条件10个、顶部按钮仅刷新生成+导出、操作栏显示3个按钮(查看+岗位推送+服务跟进)、年龄范围查询、重置按钮手动清空年龄范围 | | 10 | `FocusPersonnelModal.vue` | 新增detail方法,支持详情/编辑双模式 | | 11 | `FocusPersonnelForm.vue` | minorTag改为字典选择器 | | 12 | `FocusPersonnel.data.ts` | minorTag配置dictCode;清空superQuerySchema | --- ## 三、数据库初始化 ### 3.1 字典数据 | 字典编码 | 字典名称 | 字典项数 | 说明 | |----------|----------|:--------:|------| | `focus_major_tag` | 人员大类标签 | 2 | 就业困难人员、脱贫人员 | | `focus_minor_tag` | 人员小类标签 | 16 | 就业困难13类 + 脱贫3类 | ### 3.2 按钮权限 | 权限标识 | 名称 | 状态 | |----------|------|:----:| | focus_personnel:refresh | 刷新生成 | 占位 | | focus_personnel:messagePush | 消息推送 | 占位 | | focus_personnel:jobPush | 岗位推送 | 占位 | | focus_personnel:serviceFollow | 服务跟进 | 占位 | | focus_personnel:customTag | 自定义标签 | 占位 | ### 3.3 视图更新 在 V20260604_3 中更新 `v_focus_personnel_list`,添加了: - `birthDate` 字段(来自personal_info) - `age` 计算字段(达梦兼容使用 `FLOOR(MONTHS_BETWEEN(CURRENT_DATE, pi.birth_date) / 12)`) --- ## 四、列表页最终状态 ### 查询条件(10个) 姓名、性别、学历、户口所在地、现居住地、年龄(范围)、大类标签、小类标签、自定义标签、就业状态 ### 顶部按钮 刷新生成、导出 ### 操作栏 - 主按钮:查看 - 下拉菜单:岗位推送、服务跟进 --- ## 五、注意事项 1. **SQL脚本执行顺序**:V20260604_1 → V20260604_2 → V20260604_3(必须按顺序) 2. **字典初始化**:需要执行V20260604_1字典才会生效,否则minorTag无法翻译 3. **视图更新**:需要执行V20260604_3后才能使用年龄范围搜索 4. **年龄搜索**:使用视图中的age计算字段(`FLOOR(MONTHS_BETWEEN(CURRENT_DATE, pi.birth_date) / 12)`),达梦兼容 5. **按钮权限**:V20260604_2未执行时,因前端已去掉v-auth限制,按钮始终可见 6. **岗位推送/服务跟进**:当前为占位提示,需后续开发 7. **达梦数据库兼容**:所有SQL无反引号,使用 FROM DUAL --- ## 六、第二次迭代修改(自定义标签+推送消息按钮+详情页标签编辑) ### 修改日期:2026-06-04 ### 6.1 修改说明 根据用户需求: 1. 添加"自定义标签"和"推送消息"按钮,与"导出"按钮同行并放在右侧 2. 自定义标签搜索条件从文本输入改为下拉框 3. 此页面自定义标签只在此页面使用(从 `focus_personnel` 表中提取去重) 4. 点击"查看"进入详情页时,标签信息Tab支持添加/编辑/删除自定义标签 ### 6.2 修改文件清单 #### 后端修改 | 序号 | 文件路径 | 修改内容 | |:----:|----------|----------| | 1 | `FocusPersonnelMapper.xml` | 新增 `listCustomTags` SQL,查询所有非空 `custom_tag` 原始数据 | | 2 | `FocusPersonnelMapper.java` | 新增 `listCustomTags()` 方法定义,导入 `List` | | 3 | `IFocusPersonnelService.java` | 新增 `listCustomTags()` 接口方法 | | 4 | `FocusPersonnelServiceImpl.java` | 实现 `listCustomTags()`:拆分逗号分隔值、去重、排序 | | 5 | `FocusPersonnelController.java` | 新增 `GET /listCustomTags` 端点 | #### 前端修改 | 序号 | 文件路径 | 修改内容 | |:----:|----------|----------| | 1 | `FocusPersonnel.api.ts` | 新增 `listCustomTags` API 枚举和导出函数 | | 2 | `FocusPersonnelList.vue` | **自定义标签搜索改为 `` 下拉框**(从后端动态加载标签选项);**头部按钮区改为 flex 左右布局**:左侧"刷新生成",右侧"自定义标签"+"推送消息"+"导出";新增"自定义标签管理弹窗"(查看现有标签+添加新标签到选项列表);页面加载时调用 `listCustomTags` 初始化下拉选项 | | 3 | `FocusPersonnelDetail.vue` | **标签信息Tab自定义标签改为可编辑**:非编辑态显示标签+"编辑"按钮;编辑态显示可关闭的标签列表+输入添加框+保存/取消按钮;保存后调用 `/focusPersonnel/edit` 更新 `custom_tag` 字段;导入 `PlusOutlined` 图标、`saveOrUpdate` API、`useMessage` | ### 6.3 功能说明 #### 1. 自定义标签搜索下拉框 - 页面加载时调用 `GET /focusPersonnel/listCustomTags` 获取所有标签 - 后端从 `focus_personnel` 表提取 `custom_tag` 字段,拆分逗号分隔值、去重、排序 - 下拉框单选,支持模糊搜索匹配选项 #### 2. 顶部按钮布局 ``` [刷新生成] [自定义标签] [推送消息] [导出] ``` - 使用 `a-row justify="space-between"` 实现左右分布 - "自定义标签"按钮打开管理弹窗,可查看所有标签和添加新标签 - "推送消息"按钮暂为占位提示 - 按钮均有 `v-auth` 权限控制 #### 3. 详情页自定义标签编辑 - 标签信息Tab中自定义标签行添加"编辑"按钮 - 点击"编辑"进入编辑模式:标签可关闭删除、输入框回车或点击加号添加新标签 - 点击"保存"调用 `/focusPersonnel/edit` 接口更新数据 - 保存后自动刷新本地展示数据,退出编辑模式 ### 6.4 接口说明 | 接口 | 方法 | 说明 | |------|:----:|------| | `/focusPersonnel/listCustomTags` | GET | 获取所有自定义标签(去重排序后的列表) | | `/focusPersonnel/edit` | PUT/POST | 保存自定义标签(通过 `id` + `customTag` 字段) | ### 6.5 注意事项 1. **自定义标签选项来源**:从 `focus_personnel.custom_tag` 字段提取(已有数据的逗号分隔值)。新添加的标签若只在管理弹窗中添加而未分配给任何人,页面刷新后会消失(因为没有持久化到任何人的记录中) 2. **权限按钮**:`focus_personnel:customTag` 和 `focus_personnel:messagePush` 已在 V20260604_2 SQL 中定义,需要执行该脚本才能生效 3. **自定义标签搜索**:下拉框选择某个标签后,会以 `customTag = '选中值'` 的精确匹配方式查询(而非之前的 LIKE 模糊匹配)。如果希望模糊搜索,需要在后端 XML 中修改为 LIKE 查询 4. **需要重启后端**:Java 代码修改后需要重新编译部署 5. **详情页保存**:使用原有的 `/focusPersonnel/edit` 接口,只更新 `id` 和 `customTag` 字段,不影响其他字段 --- ## 七、第三次迭代修改(字典code重构+自定义标签字典+按钮布局修复) ### 修改日期:2026-06-04 ### 7.1 修改说明 根据用户反馈: 1. **标签写死问题**:`focus_personnel` 表中 `major_tag`/`minor_tag` 存储的是文本值(如"就业困难人员"),不是字典code,应该关联字典使用数字code 2. **自定义标签字典**:自定义标签也需要关联字典 `focus_custom_tag` 3. **按钮布局不显示**:新增的"自定义标签"和"推送消息"按钮因 `v-auth` 权限未配置未显示(V20260604_2 SQL 未执行) ### 7.2 修改文件清单 #### 新增文件 | 序号 | 文件路径 | 说明 | |:----:|----------|------| | 1 | `flyway/sql/mysql/V20260604_5__fix_tag_dict_codes.sql` | 字典code重构 + 自定义标签字典 + 数据迁移 | #### 后端修改 | 序号 | 文件路径 | 修改内容 | |:----:|----------|----------| | 1 | `FocusPersonnel.java` | `customTag` 字段添加 `@Dict(dicCode = "focus_custom_tag")` 注解 | | 2 | `FocusPersonnelPageVo.java` | `customTag` 字段添加 `@Dict(dicCode = "focus_custom_tag")` 注解 | | 3 | `FocusPersonnelDetailVo.java` | 导入 `@Dict` 并给 `customTag` 字段添加 `@Dict(dicCode = "focus_custom_tag")` 注解 | #### 前端修改 | 序号 | 文件路径 | 修改内容 | |:----:|----------|----------| | 1 | `FocusPersonnel.data.ts` | 自定义标签列添加 `dictCode: 'focus_custom_tag'` | | 2 | `FocusPersonnelList.vue` | 暂时移除"自定义标签"和"推送消息"按钮的 `v-auth` 指令(等 V20260604_2 SQL 执行后再加回) | ### 7.3 字典code映射表 #### focus_major_tag(大类标签) | item_value(code) | item_text(显示) | |:------------------:|-------------------| | 1 | 就业困难人员 | | 2 | 脱贫人员 | #### focus_minor_tag(小类标签) | item_value(code) | item_text(显示) | 类别 | |:------------------:|-------------------|:----:| | 1 | 大龄失业人员 | 就业困难 | | 2 | 残疾人员 | 就业困难 | | 3 | 享受最低生活保障待遇人员 | 就业困难 | | 4 | 城镇零就业家庭人员 | 就业困难 | | 5 | 农村零转移就业原建档立卡贫困家庭人员 | 就业困难 | | 6 | 失地农民 | 就业困难 | | 7 | 连续失业1年以上人员 | 就业困难 | | 8 | 戒毒康复人员 | 就业困难 | | 9 | 刑满释放人员 | 就业困难 | | 10 | 精神障碍康复人员 | 就业困难 | | 11 | 失业6个月以上退役军人 | 就业困难 | | 12 | 需赡养患重病直系亲属的人员 | 就业困难 | | 13 | 省人民政府规定的其他人员 | 就业困难 | | 14 | 脱贫不稳定户 | 脱贫人员 | | 15 | 边缘易致贫户 | 脱贫人员 | | 16 | 突发严重困难户 | 脱贫人员 | #### focus_custom_tag(自定义标签) | item_value | item_text | 说明 | |:----------:|:---------:|:----:| | 标签文本 | 标签文本 | `item_value = item_text`(标签本身就是值) | ### 7.4 SQL执行说明 **已存在的SQL脚本(按顺序执行):** | 序号 | SQL脚本 | 说明 | 状态 | |:----:|---------|------|:----:| | 1 | `V20260604_1` | 初始化字典(focus_major_tag、focus_minor_tag) | 已执行 | | 2 | `V20260604_2` | 补充按钮权限 | **未执行**(导致按钮不显示) | | 3 | `V20260604_3` | 重建视图(含age计算字段) | 已执行 | | 4 | `V20260604_4` | 修复旧数据字典映射 | 已执行 | | 5 | `V20260604_5` | **字典code重构 + 自定义标签字典 + 数据迁移** | **需要执行** | > **重要**:V20260604_5 必须在 V20260604_1~V20260604_4 之后执行。它依赖于前序脚本创建的字典ID和数据。 ### 7.5 按钮问题说明 - "自定义标签"和"推送消息"按钮目前移除了 `v-auth` 指令,所有用户均可看到 - 如需权限控制,需执行 V20260604_2 SQL 并在按钮上恢复 `v-auth` 指令 - "刷新生成"和"导出"按钮保留原有的 `v-auth` 权限控制 ### 7.6 注意事项 1. **数据迁移风险**:V20260604_5 会修改 `focus_personnel` 表中的 `major_tag` 和 `minor_tag` 数据(文本→code),建议执行前备份 2. **视图自动适配**:`v_focus_personnel_list` 是视图,数据迁移后自动返回code值,无需重建 3. **自定义标签字典**:`focus_custom_tag` 的 `item_value = item_text`,`@Dict` 注解翻译后和原值一致 4. **需要重启后端**:Java 代码修改后需要重新编译部署 5. **SQL执行顺序**:必须先执行 V20260604_1 ~ V20260604_4,再执行 V20260604_5 --- ## 八、第四次迭代修改(自定义标签字典化管理+序号列+标签显示修复+删除服务跟进Tab) ### 修改日期:2026-06-04 ### 8.1 修改说明 根据用户反馈: 1. **自定义标签管理和分配分离**:管理员在"自定义标签"按钮中管理字典项(增删),用户在详情页只能从字典中选择已有标签(多选下拉),不允许直接输入新标签 2. **列表加序号列** 3. **详情页标签显示名称而非code**:`majorTag`/`minorTag` 显示字典翻译后的文本 4. **详情页去掉"服务跟进"Tab**:列表已有服务跟进功能,无需重复 5. **列表页标签显示名称**:通过 `dictCode` 客户端翻译 ### 8.2 修改文件清单 #### 后端修改 | 序号 | 文件路径 | 修改内容 | |:----:|----------|----------| | 1 | `FocusPersonnelMapper.xml` | 重构 `listCustomTags` 从 `sys_dict_item` 表查询;新增 `addCustomTagItem`/`deleteCustomTagItem`/`checkCustomTagItemExists` SQL | | 2 | `FocusPersonnelMapper.java` | 新增 `addCustomTagItem`/`deleteCustomTagItem`/`checkCustomTagItemExists` 方法 | | 3 | `IFocusPersonnelService.java` | 新增 `addCustomTagItem`/`deleteCustomTagItem` 接口方法 | | 4 | `FocusPersonnelServiceImpl.java` | 实现新接口方法(UUID生成器、检查重复、增删字典项);`listCustomTags` 改为直接查字典 | | 5 | `FocusPersonnelController.java` | 新增 `POST /addCustomTagItem` 和 `DELETE /deleteCustomTagItem` 端点 | | 6 | `FocusPersonnelDetailVo.java` | `majorTag` 和 `minorTag` 添加 `@Dict(dicCode = "focus_major_tag")` 和 `@Dict(dicCode = "focus_minor_tag")` | | 7 | `FocusPersonnelPageVo.java` | `majorTag` 添加 `@Dict(dicCode = "focus_major_tag")` | #### 前端修改 | 序号 | 文件路径 | 修改内容 | |:----:|----------|----------| | 1 | `FocusPersonnel.api.ts` | 新增 `addCustomTagItem`/`deleteCustomTagItem` API 枚举和导出函数 | | 2 | `FocusPersonnel.data.ts` | 添加"序号"列(`customRender: ({index}) => index + 1`) | | 3 | `FocusPersonnelList.vue` | 自定义标签管理弹窗改为调用后端 API 增删字典项;添加删除确认弹窗;打开管理弹窗时自动刷新标签列表 | | 4 | `FocusPersonnelDetail.vue` | **去掉"服务跟进"Tab**(Tab5及相关代码全部删除);**自定义标签编辑改为 `` 多选下拉**(从 `listCustomTags` 加载选项);**标签显示改为使用 `getDictText` 函数**(优先使用 `majorTag_dictText` 等后端翻译字段);移除 `PlusOutlined` 导入 | ### 8.3 自定义标签字典化管理流程 ``` 管理员操作(列表页): 点击"自定义标签"按钮 → 弹窗展示 focus_custom_tag 字典所有项 → 输入名称点击"添加" → 调用 POST /addCustomTagItem → 持久化到 sys_dict_item → 点击"删除" → 调用 DELETE /deleteCustomTagItem → 从字典删除 用户操作(详情页): 点击"编辑" → 显示 多选下拉 → 只能从已有字典项中选择 → 不能输入新标签 → 点击"保存" → 调用 POST /focusPersonnel/edit 更新 custom_tag 字段 ``` ### 8.4 新增接口说明 | 接口 | 方法 | 说明 | |------|:----:|------| | `/focusPersonnel/addCustomTagItem` | POST | 添加自定义标签字典项(参数:itemText) | | `/focusPersonnel/deleteCustomTagItem` | DELETE | 删除自定义标签字典项(参数:itemValue) | ### 8.5 标签显示修复说明 - **后端**:`FocusPersonnelDetailVo` 和 `FocusPersonnelPageVo` 的 `majorTag`/`minorTag` 都加了 `@Dict` 注解 - **DictAspect** 自动为返回的 JSON 添加 `majorTag_dictText`/`minorTag_dictText`/`customTag_dictText` 等翻译字段 - **前端详情页**:`getDictText` 函数优先使用 `_dictText` 后缀字段,兜底返回原始值 - **前端列表页**:通过列配置的 `dictCode: 'focus_major_tag'` 等,由 BasicTable 客户端自动翻译 ### 8.6 注意事项 1. **需要执行 V20260604_5 SQL**:字典code重构后,数据存储的是数字code,需要后端 `@Dict` 或前端 `dictCode` 翻译 2. **需要重启后端**:Java 代码修改后需要重新编译部署 3. **详情页自定义标签编辑**:改为下拉多选后,用户只能从管理员预定义的标签中选择,不再支持自由输入 --- ## 九、BUG修复(API参数错误+标签显示数字) ### 修改日期:2026-06-04 ### 9.1 BUG1:addCustomTagItem 参数错误 **错误现象**: ``` Required request parameter 'itemText' for method parameter type String is not present ``` **原因**:前端 `defHttp.post({ url, params })` 中 `params` 在 POST 请求中被当作请求体(body)发送,而不是 URL 查询参数,而后端使用 `@RequestParam` 只能从查询参数或表单中读取。 **修复**: - 后端 `FocusPersonnelController.java`:`@PostMapping` 改为 `@GetMapping` - 前端不变(`params` 在 GET 请求中会自动作为 URL 查询参数) ### 9.2 BUG2:列表和详情标签显示数字而非文字 **错误现象**:字典code重构(V20260604_5)后,数据存储为数字code(如 `1`),但前端未正确翻译。 **原因**: 1. 详情页 `getDictText` 函数依赖后端 `_dictText` 字段(DictAspect可能未拦截自定义方法) 2. 列表页 BasicTable 的 `dictCode` 翻译依赖字典缓存,如果用户登录后字典才创建/修改,缓存未更新 **修复**: - **详情页**:改用 `initDictOptions` 从 `/sys/dict/getDictItems/{dictCode}` 加载字典数据,构建 `Map` 查询映射,`getDictText` 函数直接查映射 - **列表页**:`onMounted` 中预加载三个字典(`focus_major_tag`、`focus_minor_tag`、`focus_custom_tag`),确保 BasicTable 翻译生效 ### 9.3 修改文件清单 | 序号 | 文件路径 | 修改内容 | |:----:|----------|----------| | 1 | `FocusPersonnelController.java` | `addCustomTagItem` 方法改为 `@GetMapping` | | 2 | `FocusPersonnelDetail.vue` | 导入 `initDictOptions`;`loadDictData` 加载大类小类字典构建查询映射;`getDictText` 函数改为查映射 | | 3 | `FocusPersonnelList.vue` | 导入 `initDictOptions`;`onMounted` 中预加载三个字典 | ### 9.4 补充修复(第一次修复无效的原因) | 问题 | 错误原因 | 修复内容 | |:----|----------|----------| | API错误 `Request method 'POST' is not supported` | 后端改为 `@GetMapping` 但前端仍用 `defHttp.post` | 前端 API `addCustomTagItem` 改为 `defHttp.get` | | 标签仍显示数字 | `initDictOptions` 返回的字典项字段是 `text`(不是 `label`)。JeecgBoot 的 `DictModel` 只有 `{value, text}` 字段 | 将 `item.label` 改为 `item.text \|\| item.title \|\| item.label`(兼容多种字段名) | ### 9.5 注意事项 1. **字典缓存问题**:`initDictOptions` 首次调用会从服务器拉取并缓存。如果字典在用户登录后被修改,需要刷新页面或重新登录清除缓存 2. **需要重启后端**:Java 代码修改后需重新编译部署 3. **前端需刷新**:前端 `api.ts` 和 `Detail.vue` 修改后需重启前端 dev server 或硬刷新(Ctrl+F5) --- ## 十、列表和详情标签显示数字修复(_dictText + 绕过缓存) ### 修改日期:2026-06-04 ### 10.1 问题现象 列表页和详情页的人员大类标签、人员小类标签、自定义标签都显示数字(如 `1`、`2`),而不是对应的字典文本(如"就业困难人员"、"脱贫人员")。 ### 10.2 根因分析 **列表页**:JeecgBoot-vue3 的 BasicTable 组件不识别 `columns` 配置中的 `dictCode` 属性。字典翻译完全依赖后端 `DictAspect` 为返回结果添加的 `{fieldName}_dictText` 字段(如 `majorTag_dictText`)。但 `data.ts` 中列定义的 `dataIndex` 配置的是原始字段名(如 `majorTag`),导致 BasicTable 显示的是原始数字值,而不是 `majorTag_dictText` 的翻译文本。 参考 JeecgBoot 其他模块(如 `manage.data.ts`),字典列的 `dataIndex` 直接使用 `{field}_dictText` 格式: ```ts // manage.data.ts 示例 { title: '发送状态', dataIndex: 'esSendStatus_dictText' } ``` **详情页**:`loadDictData` 使用 `initDictOptions` 加载字典数据。`initDictOptions` 内部优先检查前端缓存(`userStore.getAllDictItems` 或 `authCache`),如果用户在字典被修改(V20260604_5 SQL)之前已登录,缓存中就是旧的字典数据(`value='就业困难人员'` 而非 `value='1'`),导致翻译查找失败。 ### 10.3 修复内容 #### 列表页修复 **文件**:`FocusPersonnel.data.ts` 将 `majorTag`、`minorTag`、`customTag` 列的 `dataIndex` 从原始字段名改为 `{field}_dictText`,移除无用的 `dictCode` 属性: ```ts // 修改前 { title: '人员大类标签', dataIndex: 'majorTag', dictCode: 'focus_major_tag' } { title: '人员小类标签', dataIndex: 'minorTag', dictCode: 'focus_minor_tag' } { title: '自定义标签', dataIndex: 'customTag', dictCode: 'focus_custom_tag' } // 修改后 { title: '人员大类标签', dataIndex: 'majorTag_dictText' } { title: '人员小类标签', dataIndex: 'minorTag_dictText' } { title: '自定义标签', dataIndex: 'customTag_dictText' } ``` #### 详情页修复 **文件**:`FocusPersonnelDetail.vue` 1. 导入 `ajaxGetDictItems`(从 `/@/utils/dict/index`) 2. 将 `loadDictData` 中的 `initDictOptions` 改为 `ajaxGetDictItems`,绕过前端缓存直接调用后端 API ```ts // 修改前 const majorDict = await initDictOptions('focus_major_tag'); const minorDict = await initDictOptions('focus_minor_tag'); // 修改后 const majorDict = await ajaxGetDictItems('focus_major_tag'); const minorDict = await ajaxGetDictItems('focus_minor_tag'); ``` ### 10.4 修改文件清单 | 序号 | 文件路径 | 修改内容 | |:----:|----------|----------| | 1 | `FocusPersonnel.data.ts` | `majorTag`/`minorTag`/`customTag` 列的 `dataIndex` 改为 `majorTag_dictText`/`minorTag_dictText`/`customTag_dictText`,移除 `dictCode` | | 2 | `FocusPersonnelDetail.vue` | 导入 `ajaxGetDictItems`;`loadDictData` 中使用 `ajaxGetDictItems` 替代 `initDictOptions` 加载大类小类字典 | | 3 | `FocusPersonnel.api.ts` | `deleteCustomTagItem` 增加 `{ joinParamsToUrl: true }` 选项,修复 DELETE 请求参数未传递到 URL 查询参数的问题 | ### 10.5 补充修复:deleteCustomTagItem 参数错误 **错误现象**: ``` Required request parameter 'itemValue' for method parameter type String is not present ``` **原因**:`defHttp.delete` 默认将 `params` 放在请求体(body)中发送,而后端 `@RequestParam` 只能从 URL 查询参数中读取。`deleteOne` 已有 `{ joinParamsToUrl: true }` 正确处理,但 `deleteCustomTagItem` 未添加此选项。 **修复**:在 `FocusPersonnel.api.ts` 的 `deleteCustomTagItem` 函数中增加 `{ joinParamsToUrl: true }` 选项。 ### 10.6 补充修复:删除自定义标签时检查是否被用户绑定 **需求变更**:删除自定义标签时,如果有用户绑定了该标签,应禁止删除并给出提示,而不是自动清理。 **原因**:后端 `deleteCustomTagItem` 需要先检查 `focus_personnel` 表中是否有用户使用了该标签。 **修复**(4个文件修改): 1. **`FocusPersonnelMapper.java`** — 移除 `removeCustomTagFromPersonnel`,新增 `countPersonnelByCustomTag` 方法 2. **`FocusPersonnelMapper.xml`** — 移除清理 UPDATE SQL,新增 `countPersonnelByCustomTag` SELECT SQL,使用 4 种 LIKE 条件精确匹配逗号分隔值中的标签(完全匹配、开头、结尾、中间) 3. **`FocusPersonnelServiceImpl.java`** — `deleteCustomTagItem` 先检查绑定用户数,大于 0 则返回 false 4. **`FocusPersonnelController.java`** — 处理 Service 返回结果,若不可删除则返回 `Result.error("该标签已被用户绑定,无法删除")` 5. **`FocusPersonnelList.vue`** — 错误提示改用 `e.message` 显示后端返回的具体错误信息 **修改文件清单更新**: | 序号 | 文件路径 | 修改内容 | |:----:|----------|----------| | 3 | `FocusPersonnel.api.ts` | `deleteCustomTagItem` 增加 `{ joinParamsToUrl: true }` | | 4 | `FocusPersonnelMapper.java` | 移除 `removeCustomTagFromPersonnel`,新增 `countPersonnelByCustomTag` | | 5 | `FocusPersonnelMapper.xml` | 移除清理 UPDATE,新增绑定检查 SELECT | | 6 | `FocusPersonnelServiceImpl.java` | `deleteCustomTagItem` 先检查绑定再删字典项 | | 7 | `FocusPersonnelController.java` | 返回 `Result.error` 提示已被绑定 | | 8 | `FocusPersonnelList.vue` | 错误提示显示后端消息 | ### 10.7 注意事项 1. **需重启前端 dev server** 或硬刷新(Ctrl+F5)使前端修改生效 2. **需重启后端**:Java 代码修改后需重新编译部署 3. **列表页依赖后端 DictAspect**:已在第九章为 `FocusPersonnelPageVo` 添加 `@Dict` 注解 --- ## 十一、详情页改为单页显示 + 标签去颜色 ### 修改日期:2026-06-04 ### 11.1 需求 1. 点击查看时所有字段在一页显示,不需要 Tab 页签切换 2. 标签不使用颜色(去掉 `a-tag` 的 `color` 属性) ### 11.2 修改内容 **文件**:`FocusPersonnelDetail.vue` 1. **去除 Tab 页签**:移除 `a-tabs` 组件,将四个 Tab(基本信息、教育与求职、联系与居住、标签信息)的字段全部合并到一个 `a-descriptions` 中,按原顺序排列,使用注释分组标明 2. **标签去颜色**:所有 `a-tag` 去掉 `color` 属性(原来有 `blue`、`green`、`orange`) 3. **清理冗余代码**:移除不再使用的 `activeTab` ref 变量及其在 `loadDetail` 中的赋值,移除未使用的 `initDictOptions` 导入 4. **自定义标签占整行**:设置 `:span="2"` 使自定义标签编辑区域占满整行宽度 ### 11.3 修改文件清单 | 序号 | 文件路径 | 修改内容 | |:----:|----------|----------| | 1 | `FocusPersonnelDetail.vue` | 合并所有 Tab 字段到单页 `a-descriptions`;标签去 `color`;清理 `activeTab`、`initDictOptions` | ### 11.4 自定义标签 UI 二次优化 **优化内容**: 1. **详情页(FocusPersonnelDetail.vue)**: - 查看模式:使用 flex 布局将标签和编辑按钮行内排列,标签间距一致;编辑按钮改为 `type="dashed"` 并添加 `EditOutlined` 图标,更直观 - 编辑模式:选择框和按钮使用 flex column 布局,按钮之间间距合理 - 添加 scoped 样式类 `.custom-tag-view`、`.custom-tag-edit`、`.custom-tag-item`、`.edit-tag-btn` 等 2. **列表页(FocusPersonnelList.vue)**: - 自定义标签管理弹窗:替换 `a-list` 为自定义 div 列表,每项使用 flex 布局左侧标签名、右侧"删除"按钮 - "删除"按钮默认隐藏,鼠标悬停行时显示(opacity 过渡),界面更简洁 - 添加标签数量统计显示("共 X 个") - 添加空状态提示(`a-empty`) - 添加 scoped 样式类 `.custom-tag-modal`、`.tag-list-item`、`.tag-delete-btn` 等 ### 11.5 修改文件清单(最终版) | 序号 | 文件路径 | 修改内容 | |:----:|----------|----------| | 1 | `FocusPersonnelDetail.vue` | 合并所有 Tab 到单页;标签去 `color`;自定义标签 UI 优化(flex 布局、图标按钮、scoped 样式) | | 2 | `FocusPersonnelList.vue` | 标签管理弹窗 UI 优化(自定义列表、hover 显示删除、数量统计、空状态) | ### 11.6 注意事项 1. **无需重启后端**:纯前端修改,刷新页面即可 2. **硬刷新(Ctrl+F5)** 使修改生效 --- ## 十二、标签字典迁移到 DICTIONARY_ITEM 表 ### 修改日期:2026-06-04 ### 12.1 需求说明 所有标签字典(`focus_major_tag`、`focus_minor_tag`、`focus_custom_tag`)从 JeecgBoot 系统表 `sys_dict` + `sys_dict_item` 迁移到项目自定义字典表 `DICTIONARY` + `DICTIONARY_ITEM`。 ### 12.2 后端修改 #### 12.2.1 Flyway SQL — `V20260604_6__migrate_tags_to_dictionary_item.sql` - 在 `DICTIONARY` 表创建 `focus_major_tag`、`focus_minor_tag`、`focus_custom_tag` 三条字典记录 - 从 `sys_dict_item` 迁移 `focus_major_tag` 和 `focus_minor_tag` 的字典项到 `DICTIONARY_ITEM`(Value=Integer编码,Name=标签文本) - 从 `sys_dict_item` 迁移 `focus_custom_tag` 的字典项到 `DICTIONARY_ITEM`(Value=自增ROWNUM编码,Name=标签文本) - 达梦兼容:使用 `FROM DUAL`、`NEWID()`、`NOT EXISTS` 语法 #### 12.2.2 `FocusPersonnelServiceImpl.java` - 注入 `DictionaryItemMapper` 替代 `baseMapper` 的 sys_dict 相关操作 - `listCustomTags()`:从 `DICTIONARY_ITEM` 查询 `focus_custom_tag` 字典的已启用标签 - `addCustomTagItem(itemText)`:按 Name 查重 → 查找当前最大 Value 自增 → 插入 `DICTIONARY_ITEM` - `deleteCustomTagItem(itemValue)`:按 Name 查找字典项 → 检查绑定 → 从 `DICTIONARY_ITEM` 删除 - 删除 `checkCustomTagItemExists` 方法(不再需要) #### 12.2.3 `FocusPersonnelMapper.java` - 移除 `listCustomTags()`、`addCustomTagItem()`、`deleteCustomTagItem()`、`checkCustomTagItemExists()` 方法 - 保留 `countPersonnelByCustomTag()`(仍用于检查标签是否被用户绑定) #### 12.2.4 `FocusPersonnelMapper.xml` - 移除 `listCustomTags`、`addCustomTagItem`、`deleteCustomTagItem`、`checkCustomTagItemExists` 四个 SQL - 保留 `countPersonnelByCustomTag`、`queryPageList`、`queryDetailById` #### 12.2.5 `FocusPersonnelPageVo.java` / `FocusPersonnelDetailVo.java` - 移除所有 `@Dict(dicCode = "...")` 注解(因不再使用系统 `sys_dict` 表) - 移除 `import org.jeecg.common.aspect.annotation.Dict` ### 12.3 前端修改 #### 12.3.1 `FocusPersonnelList.vue` - **导入**:`useDict` from `/@/hooks/dictionary/useDict`,替代旧的 `initDictOptions` 和 `JDictSelectTag` - **字典加载**:`const { getDictText, getDictOptions } = useDict(['focus_major_tag', 'focus_minor_tag'])` - **下拉选项**:`majorTagOptions = computed(() => getDictOptions('focus_major_tag'))`,`minorTagOptions` 同理 - **搜索框**:`` 替换为 `` - **列渲染**:添加 `#bodyCell` 插槽,使用 `getDictText('focus_major_tag', text)` 翻译字典值 - **移除**:`JDictSelectTag` 组件导入、`initDictOptions` 函数导入、`onMounted` 中的预加载调用 #### 12.3.2 `FocusPersonnelDetail.vue` - **导入**:`useDict` 替代 `ajaxGetDictItems` - **字典加载**:`const { getDictText } = useDict(['focus_major_tag', 'focus_minor_tag'])` - **移除**:`dictMap` 响应式变量、`loadDictData()` 函数、自定义 `getDictText()` 函数 - **简化**:只调用 `loadCustomTagOptions()`(从 API 加载自定义标签选项) #### 12.3.3 `FocusPersonnel.data.ts` - `majorTag`/`minorTag`/`customTag` 列的 `dataIndex` 从 `majorTag_dictText` 改回 `majorTag`(不再依赖后端 DictAspect) ### 12.4 修改文件清单 | 序号 | 文件路径 | 修改内容 | |:----:|----------|----------| | 1 | `V20260604_6__migrate_tags_to_dictionary_item.sql` | 新增:在 DICTIONARY/DICTIONARY_ITEM 创建标签字典并迁移数据 | | 2 | `FocusPersonnelServiceImpl.java` | 注入 DictionaryItemMapper;CRUD 改用 DICTIONARY_ITEM 表 | | 3 | `FocusPersonnelMapper.java` | 移除 sys_dict_item 相关方法,保留 countPersonnelByCustomTag | | 4 | `FocusPersonnelMapper.xml` | 移除 sys_dict_item 相关 SQL | | 5 | `FocusPersonnelPageVo.java` | 移除 @Dict 注解和导入 | | 6 | `FocusPersonnelDetailVo.java` | 移除 @Dict 注解和导入 | | 7 | `FocusPersonnelList.vue` | 引入 useDict;替换搜索框;添加 bodyCell 插槽 | | 8 | `FocusPersonnelDetail.vue` | 引入 useDict;移除 loadDictData/自定义 getDictText | | 9 | `FocusPersonnel.data.ts` | dataIndex 从 _dictText 改回原始字段名 | ### 12.5 注意事项 1. **需执行 Flyway SQL**:启动后端自动执行 `V20260604_6` 迁移脚本 2. **需重启后端**:Java 代码修改后需重新编译部署 3. **需硬刷新前端**:Ctrl+F5 清除旧缓存 4. **自定义标签兼容**:`focus_custom_tag` 仍通过 `listCustomTags`/`addCustomTagItem`/`deleteCustomTagItem` API 管理,后端存储从 `sys_dict_item` 改为 `DICTIONARY_ITEM` --- ## 十三、清理旧字典数据(sys_dict / sys_dict_item) ### 修改日期:2026-06-04 ### 13.1 修改说明 `V20260604_6` 仅迁移数据到 `DICTIONARY/DICTIONARY_ITEM` 表,未删除 `sys_dict` 和 `sys_dict_item` 中的旧数据。新增 `V20260604_7` 脚本清理三个标签字典的旧记录。 ### 13.2 修改文件清单 | 序号 | 文件路径 | 说明 | |:----:|----------|------| | 1 | `V20260604_7__cleanup_old_sys_dict_tags.sql` | 新增:删除 sys_dict_item 和 sys_dict 中 focus_major_tag/focus_minor_tag/focus_custom_tag 的旧数据 | ### 13.3 SQL 执行逻辑 1. 先删除 `sys_dict_item` 子表中三个字典的字典项(通过 `dict_id` 关联 `sys_dict`) 2. 再删除 `sys_dict` 主表中三个字典的字典记录 3. 最终验证确保无剩余记录 ### 13.4 注意事项 1. **需在 V20260604_6 之后执行**:必须先迁移数据再清理旧数据 2. **执行方式**:重启后端自动执行 Flyway,或手动运行此 SQL --- ## 十四、自定义标签增加编辑(重命名)功能 ### 修改日期:2026-06-04 ### 14.1 需求说明 自定义标签管理弹窗中,除了添加和删除,还需要支持编辑(重命名)已有标签。编辑标签时同步更新 `focus_personnel` 表中已绑定该标签的用户记录。 ### 14.2 后端修改 #### 14.2.1 `IFocusPersonnelService.java` 新增 `updateCustomTagItem(String oldName, String newName)` 接口方法。 #### 14.2.2 `FocusPersonnelServiceImpl.java` 实现 `updateCustomTagItem`: 1. 校验参数(非空、新旧名称不同) 2. 按旧名称查找 `DICTIONARY_ITEM` 记录 3. 检查新名称是否已存在(防止重名) 4. 更新 `DICTIONARY_ITEM` 的 `Name` 和 `Code` 字段 5. 调用 `baseMapper.updatePersonnelCustomTagName()` 同步更新 `focus_personnel` 中已绑定的标签名 #### 14.2.3 `FocusPersonnelMapper.java` 新增 `updatePersonnelCustomTagName(@Param("oldName") String oldName, @Param("newName") String newName)` 方法。 #### 14.2.4 `FocusPersonnelMapper.xml` 新增 `updatePersonnelCustomTagName` SQL,使用 `CASE WHEN` 分段处理 4 种逗号分隔位置: - 完全匹配(只有一个标签) - 在开头 - 在结尾 - 在中间 使用 `SUBSTR` + `INSTR` 函数精确替换,避免误替换子串。 #### 14.2.5 `FocusPersonnelController.java` 新增 `PUT /updateCustomTagItem` 端点,接收 `oldName` 和 `newName` 参数。 ### 14.3 前端修改 #### 14.3.1 `FocusPersonnel.api.ts` 新增 `updateCustomTagItem` API 枚举和导出函数(`defHttp.put`)。 #### 14.3.2 `FocusPersonnelList.vue` - **导入**:新增 `updateCustomTagItem` 导入 - **状态**:新增 `editingTagOldName` 和 `editingTagNewName` ref 变量 - **方法**: - `handleStartEdit(tagName)`:进入编辑模式 - `handleCancelEdit()`:取消编辑 - `handleSaveEdit()`:保存编辑,调用后端 API,更新本地列表 - **模板**:每条标签在查看模式显示"编辑"+"删除"按钮(hover 显示),点击编辑后切换为内联输入框+保存/取消按钮 - **样式**:新增 `.tag-actions`(按钮组容器)、`.tag-edit-btn`、`.tag-edit-input`、`.tag-edit-actions` 样式 ### 14.4 修改文件清单 | 序号 | 文件路径 | 修改内容 | |:----:|----------|----------| | 1 | `IFocusPersonnelService.java` | 新增 `updateCustomTagItem` 接口方法 | | 2 | `FocusPersonnelServiceImpl.java` | 实现 `updateCustomTagItem`(含重名校验 + 同步更新 focus_personnel) | | 3 | `FocusPersonnelMapper.java` | 新增 `updatePersonnelCustomTagName` 方法 | | 4 | `FocusPersonnelMapper.xml` | 新增 `updatePersonnelCustomTagName` SQL(CASE WHEN 分段精确替换) | | 5 | `FocusPersonnelController.java` | 新增 `PUT /updateCustomTagItem` 端点 | | 6 | `FocusPersonnel.api.ts` | 新增 `updateCustomTagItem` API | | 7 | `FocusPersonnelList.vue` | 弹窗标签列表添加编辑模式(内联输入+保存/取消) | ### 14.5 注意事项 1. **需重启后端**:Java 代码修改后需重新编译部署 2. **需硬刷新前端**:Ctrl+F5 清除旧缓存 3. **标签重名限制**:新名称不能与已有标签重复 4. **同步更新机制**:重命名标签时,`focus_personnel` 表中已绑定该标签的用户记录会同步更新(逗号分隔值中的精确替换) --- ## 十五、自定义标签管理弹窗改为标准表格+分页 ### 修改日期:2026-06-04 ### 15.1 需求说明 自定义标签管理弹窗从简单的列表改为标准的表格样式,支持分页查询。因为自定义标签数量可能较多,分页表格更便于管理。 ### 15.2 后端修改 #### 15.2.1 `IFocusPersonnelService.java` 新增 `IPage queryCustomTagPage(IPage page)` 分页查询接口方法。 #### 15.2.2 `FocusPersonnelServiceImpl.java` 实现 `queryCustomTagPage`:使用 `DictionaryItemMapper.selectPage()` 分页查询 `focus_custom_tag` 字典的已启用标签。 #### 15.2.3 `FocusPersonnelController.java` 新增 `GET /listCustomTagsPage?pageNo=1&pageSize=10` 端点,返回分页的 `DictionaryItem` 数据。 ### 15.3 前端修改 #### 15.3.1 `FocusPersonnel.api.ts` 新增 `listCustomTagsPage` API 枚举和导出函数。 #### 15.3.2 `FocusPersonnelList.vue` - **模板**:自定义标签管理弹窗改为 `` 表格+分页,包含三列:序号、标签名称、操作 - **状态**:新增 `customTagLoading`、`customTagPageData`、`customTagPagination` 表格相关状态 - **方法**: - `loadCustomTagPageData()`:分页加载标签数据 - `handleCustomTagTableChange(pagination)`:分页切换事件 - `closeCustomTagModal()`:关闭弹窗(取消编辑状态) - 增删改方法适配表格:操作后调用 `loadCustomTagPageData()` 刷新表格 - **样式**:移除旧列表样式(`.tag-list-wrapper`、`.tag-list-item` 等),保留简洁的输入框间距 ### 15.4 修改文件清单 | 序号 | 文件路径 | 修改内容 | |:----:|----------|----------| | 1 | `IFocusPersonnelService.java` | 新增 `queryCustomTagPage` 分页查询接口 | | 2 | `FocusPersonnelServiceImpl.java` | 实现 `queryCustomTagPage`(DictionaryItemMapper.selectPage) | | 3 | `FocusPersonnelController.java` | 新增 `GET /listCustomTagsPage` 端点 | | 4 | `FocusPersonnel.api.ts` | 新增 `listCustomTagsPage` API | | 5 | `FocusPersonnelList.vue` | 弹窗改为表格+分页,移除旧列表样式 | ### 15.5 注意事项 1. **需重启后端**:Java 代码修改后需重新编译部署 2. **需硬刷新前端**:Ctrl+F5 清除旧缓存 3. **`listCustomTags` 保留**:非分页接口仍用于详情页下拉选择框的数据源 --- ## 十六、服务跟进功能实现 ### 修改日期:2026-06-04 ### 16.1 需求说明 根据需求文档 3.9 节,实现服务跟进功能: 1. 数据库表 `focus_personnel_service_follow`:记录服务内容、服务时间、服务人员、关联人员ID 2. 后端 CRUD 接口 3. 前端服务跟进记录弹窗(表格展示,支持新增/编辑/删除) 4. 按钮权限 `focus_personnel:serviceFollow`(已在 V20260604_2 中定义) ### 16.2 数据库 #### Flyway SQL — `V20260604_8__create_service_follow_table.sql` | 字段名 | 类型 | 说明 | |--------|------|------| | `id` | VARCHAR(32) PK | 主键(UUID) | | `focus_personnel_id` | VARCHAR(32) NOT NULL | 关联重点关注人员ID | | `service_content` | TEXT | 服务内容 | | `service_time` | DATETIME | 服务时间 | | `service_provider` | VARCHAR(100) | 服务人员 | | `create_by` / `create_time` | VARCHAR(50) / DATETIME | 创建人/时间 | | `update_by` / `update_time` | VARCHAR(50) / DATETIME | 更新人/时间 | | `del_flag` | INT DEFAULT 0 | 删除标识 | ### 16.3 后端修改 | 序号 | 文件路径 | 说明 | |:----:|----------|------| | 1 | `entity/FocusPersonnelServiceFollow.java` | 新增实体类(映射 focus_personnel_service_follow 表) | | 2 | `mapper/FocusPersonnelServiceFollowMapper.java` | 新增 Mapper 接口 | | 3 | `service/IFocusPersonnelServiceFollowService.java` | 新增 Service 接口 | | 4 | `service/impl/FocusPersonnelServiceFollowServiceImpl.java` | 新增 Service 实现 | | 5 | `controller/FocusPersonnelServiceFollowController.java` | 新增 Controller(分页列表/添加/编辑/删除/批量删除/查询) | Controller 提供 6 个端点: | 端点 | 方法 | 说明 | |------|:----:|------| | `/focusPersonnelServiceFollow/list` | GET | 分页列表查询(按 focusPersonnelId 过滤,按 service_time 倒序) | | `/focusPersonnelServiceFollow/add` | POST | 新增 | | `/focusPersonnelServiceFollow/edit` | PUT/POST | 编辑 | | `/focusPersonnelServiceFollow/delete` | DELETE | 删除 | | `/focusPersonnelServiceFollow/deleteBatch` | DELETE | 批量删除 | | `/focusPersonnelServiceFollow/queryById` | GET | 按 ID 查询 | ### 16.4 前端修改 | 序号 | 文件路径 | 说明 | |:----:|----------|------| | 1 | `components/FocusPersonnelServiceFollowList.vue` | **新增**:服务跟进记录弹窗组件(表格+分页,含新增/编辑/删除表单) | | 2 | `FocusPersonnel.api.ts` | 新增 4 个服务跟进 API 枚举和导出函数 | | 3 | `FocusPersonnelList.vue` | 替换占位函数为实际弹窗逻辑;导入新组件;添加弹窗标签 | ### 16.5 功能交互流程 ``` 列表页 → 点击"服务跟进" → 弹窗显示该人员的所有服务跟进记录(表格+分页) ├── [新增服务记录] → 小弹窗(服务内容/服务时间/服务人员) ├── 每条记录 [编辑] → 小弹窗编辑 └── 每条记录 [删除] → 确认后删除 ``` ### 16.6 注意事项 1. **需执行 Flyway SQL**:启动后端自动执行 `V20260604_8` 建表 2. **需重启后端**:Java 代码修改后需重新编译部署 3. **需硬刷新前端**:Ctrl+F5 清除旧缓存 4. **按钮权限**:`focus_personnel:serviceFollow` 已在 V20260604_2 中定义,需要执行该 SQL 才会生效。若未执行,前端 `v-auth` 指令会隐藏操作按钮 --- ## 十七、BUG修复(表不存在 + 序号错误 + 搜索下拉为空) ### 修改日期:2026-06-04 ### 17.1 BUG1:服务跟进表不存在 **错误现象**: ``` 无效的表或视图名[FOCUS_PERSONNEL_SERVICE_FOLLOW] ``` **原因**:`V20260604_8__create_service_follow_table.sql` 未被执行,数据库中不存在 `focus_personnel_service_follow` 表。 **解决方案**:手动执行以下建表 SQL: ```sql CREATE TABLE IF NOT EXISTS focus_personnel_service_follow ( id VARCHAR(32) NOT NULL, focus_personnel_id VARCHAR(32) NOT NULL COMMENT '关联重点关注人员ID', service_content TEXT COMMENT '服务内容', service_time DATETIME COMMENT '服务时间', service_provider VARCHAR(100) COMMENT '服务人员', create_by VARCHAR(50) COMMENT '创建人', create_time DATETIME COMMENT '创建时间', update_by VARCHAR(50) COMMENT '更新人', update_time DATETIME COMMENT '更新时间', del_flag INT DEFAULT 0 COMMENT '删除标识(0-正常,1-已删除)', PRIMARY KEY (id) ); ``` ### 17.2 BUG2:自定义标签表格序号错误 **错误现象**:自定义标签管理弹窗中序号列全部显示为 `1`。 **原因**:序号计算使用了 `record._index`,但 MyBatis Plus 返回的分页数据中,`a-table` 的 `bodyCell` 插槽不会自动设置 `_index` 属性,导致 `record._index` 恒为 `undefined`,回退到 `0`,所以所有行序号都是 `1`。 **修复**:`bodyCell` 插槽使用 `index` 参数(Ant Design Vue 提供的行索引),替代 `record._index`: ```html