Jelajahi Sumber

fix: 标签库第二版调整

zhangying 4 hari lalu
induk
melakukan
858a87cde0
28 mengubah file dengan 1316 tambahan dan 100 penghapusan
  1. 444 0
      .docs/260604-标签库模块开发记录.md
  2. 38 0
      .docs/sql/标签库信息.sql
  3. 40 7
      jeecg-boot/jeecg-boot-module/jeecg-module-zjrs/src/main/java/org/jeecg/modules/zjrs/tag/controller/EnterpriseTagController.java
  4. 40 7
      jeecg-boot/jeecg-boot-module/jeecg-module-zjrs/src/main/java/org/jeecg/modules/zjrs/tag/controller/PersonalTagController.java
  5. 35 0
      jeecg-boot/jeecg-boot-module/jeecg-module-zjrs/src/main/java/org/jeecg/modules/zjrs/tag/entity/EnterpriseTagRelation.java
  6. 35 0
      jeecg-boot/jeecg-boot-module/jeecg-module-zjrs/src/main/java/org/jeecg/modules/zjrs/tag/entity/PersonalTagRelation.java
  7. 16 0
      jeecg-boot/jeecg-boot-module/jeecg-module-zjrs/src/main/java/org/jeecg/modules/zjrs/tag/mapper/EnterpriseTagMapper.java
  8. 14 0
      jeecg-boot/jeecg-boot-module/jeecg-module-zjrs/src/main/java/org/jeecg/modules/zjrs/tag/mapper/EnterpriseTagRelationMapper.java
  9. 16 0
      jeecg-boot/jeecg-boot-module/jeecg-module-zjrs/src/main/java/org/jeecg/modules/zjrs/tag/mapper/PersonalTagMapper.java
  10. 14 0
      jeecg-boot/jeecg-boot-module/jeecg-module-zjrs/src/main/java/org/jeecg/modules/zjrs/tag/mapper/PersonalTagRelationMapper.java
  11. 54 0
      jeecg-boot/jeecg-boot-module/jeecg-module-zjrs/src/main/java/org/jeecg/modules/zjrs/tag/mapper/xml/EnterpriseTagMapper.xml
  12. 5 0
      jeecg-boot/jeecg-boot-module/jeecg-module-zjrs/src/main/java/org/jeecg/modules/zjrs/tag/mapper/xml/EnterpriseTagRelationMapper.xml
  13. 54 0
      jeecg-boot/jeecg-boot-module/jeecg-module-zjrs/src/main/java/org/jeecg/modules/zjrs/tag/mapper/xml/PersonalTagMapper.xml
  14. 5 0
      jeecg-boot/jeecg-boot-module/jeecg-module-zjrs/src/main/java/org/jeecg/modules/zjrs/tag/mapper/xml/PersonalTagRelationMapper.xml
  15. 20 0
      jeecg-boot/jeecg-boot-module/jeecg-module-zjrs/src/main/java/org/jeecg/modules/zjrs/tag/service/IEnterpriseTagService.java
  16. 20 0
      jeecg-boot/jeecg-boot-module/jeecg-module-zjrs/src/main/java/org/jeecg/modules/zjrs/tag/service/IPersonalTagService.java
  17. 38 0
      jeecg-boot/jeecg-boot-module/jeecg-module-zjrs/src/main/java/org/jeecg/modules/zjrs/tag/service/impl/EnterpriseTagServiceImpl.java
  18. 38 0
      jeecg-boot/jeecg-boot-module/jeecg-module-zjrs/src/main/java/org/jeecg/modules/zjrs/tag/service/impl/PersonalTagServiceImpl.java
  19. 113 0
      jeecg-boot/jeecg-boot-module/jeecg-module-zjrs/src/main/java/org/jeecg/modules/zjrs/tag/vo/EnterpriseTagVo.java
  20. 113 0
      jeecg-boot/jeecg-boot-module/jeecg-module-zjrs/src/main/java/org/jeecg/modules/zjrs/tag/vo/PersonalTagVo.java
  21. 8 0
      jeecgboot-vue3/src/views/recruitment/enterpriseTag/EnterpriseTag.api.ts
  22. 10 14
      jeecgboot-vue3/src/views/recruitment/enterpriseTag/EnterpriseTag.data.ts
  23. 46 16
      jeecgboot-vue3/src/views/recruitment/enterpriseTag/EnterpriseTagList.vue
  24. 18 13
      jeecgboot-vue3/src/views/recruitment/enterpriseTag/components/EnterpriseTagForm.vue
  25. 8 0
      jeecgboot-vue3/src/views/recruitment/personalTag/PersonalTag.api.ts
  26. 10 14
      jeecgboot-vue3/src/views/recruitment/personalTag/PersonalTag.data.ts
  27. 46 16
      jeecgboot-vue3/src/views/recruitment/personalTag/PersonalTagList.vue
  28. 18 13
      jeecgboot-vue3/src/views/recruitment/personalTag/components/PersonalTagForm.vue

+ 444 - 0
.docs/260604-标签库模块开发记录.md

@@ -0,0 +1,444 @@
+# 标签库模块开发记录
+
+**日期:** 2026-06-04
+
+---
+
+## 一、个人标签库模块
+
+### 模块概述
+
+个人标签库模块(personalTag)用于管理个人标签信息,支持省一体化平台回流和本地自定义两种来源的标签,并提供标签的启用/停用状态管理。该模块采用 JeecgBoot 的前后端分离架构,前端基于 Vue3 + Ant Design Vue,后端基于 Spring Boot + MyBatis-Plus。
+
+### 业务关系
+
+- **PersonalTag(个人标签信息)**:独立主表,存储个人标签的基础信息,通过 PERSONAL_TAG_RELATION 中间表与 PersonalInfo(个人基本信息)表建立多对多关联。
+- **PersonalTagRelation(个人标签关联)**:中间表,仅含 `tag_id` + `personal_id` 两个字段,联合主键。
+
+---
+
+### 模块文件结构
+
+#### 前端(jeecgboot-vue3/src/views/recruitment/personalTag/)
+
+| 文件                                  | 用途                             |
+|-------------------------------------|--------------------------------|
+| `PersonalTagList.vue`                  | 个人标签列表页面,提供查询、新增、编辑、详情、删除、批量删除、批量启用/停用、导入导出、高级查询等功能 |
+| `PersonalTag.api.ts`                   | 个人标签 API 封装                    |
+| `PersonalTag.data.ts`                  | 列表页表格列配置(9列)与高级查询字段定义(8个查询字段) |
+| `components/PersonalTagForm.vue`       | 个人标签新增/编辑/详情表单组件 |
+| `components/PersonalTagModal.vue`      | 个人标签弹窗组件,包裹 PersonalTagForm,全屏模式 |
+
+#### 后端(jeecg-boot/jeecg-boot-module/jeecg-module-zjrs/.../org/jeecg/modules/zjrs/tag/)
+
+| 层次           | 文件                                          |
+|--------------|---------------------------------------------|
+| Controller   | `controller/PersonalTagController.java`                   |
+| Entity       | `entity/PersonalTag.java`                             |
+| Service      | `service/IPersonalTagService.java`                     |
+| Service Impl | `service/impl/PersonalTagServiceImpl.java`                  |
+| Mapper       | `mapper/PersonalTagMapper.java`                       |
+| Mapper XML   | `mapper/xml/PersonalTagMapper.xml`             |
+
+---
+
+### 开发记录
+
+#### 功能清单
+
+| 功能            | 说明                                                             |
+|---------------|----------------------------------------------------------------|
+| 分页列表查询        | 支持按标签名称、所属一级分类、所属二级分类、启用状态、数据来源等条件查询,9 列展示(含本地关联人数) |
+| 新增            | 全屏表单,支持 8 个标签字段录入 |
+| 编辑            | 复用新增表单,预填充已有数据 |
+| 详情            | 表单禁用模式查看标签全部信息 |
+| 删除 / 批量删除     | 支持单条删除和批量删除,删除前弹窗确认 |
+| 批量启用/停用      | 支持选中多条标签后批量修改启用状态 |
+| 导入 / 导出 Excel | 基于 Jeecg 标准 Excel 导出导入功能,导出使用 VO 含本地关联人数 |
+| 高级查询          | 8 个查询字段,支持灵活的筛选条件 |
+| 标签名称唯一性校验     | 新增/编辑时后端校验标签名称不可重复 |
+
+#### 必填字段
+
+共 3 个必填字段:标签名称、启用状态、数据来源。
+
+---
+
+### 功能开发
+
+| 日期         | 内容           | 涉及文件          | 说明                     |
+|------------|--------------|---------------|------------------------|
+| 2026-06-02 | 个人标签模块初始构建 | 前端 5 个文件 + 后端 6 个文件 | 完成个人标签管理的基础 CRUD 功能,含 Excel 导入导出、批量启用/停用、标签名称唯一性校验 |
+
+### PersonalTag 实体字段一览
+
+| 分类     | 字段                   | 说明            |
+|--------|----------------------|---------------|
+| 基础信息   | provinceStandardCode | 省标编码         |
+|        | tagCode              | 标签编码         |
+|        | tagName              | 标签名称(必填)     |
+|        | firstCategory        | 所属一级分类       |
+|        | secondCategory       | 所属二级分类       |
+|        | tagDescription       | 标签说明         |
+| 状态     | tagStatus            | 启用状态(必填,1启用/0停用) |
+|        | dataSource           | 数据来源(必填,1省一体化平台回流/2本地自定义) |
+| 系统字段   | id / createBy / createTime / updateBy / updateTime / sysOrgCode | 系统内置字段 |
+
+### 表格列(9列)
+
+| 列            | 对应字段              | 说明                     |
+|---------------|-------------------|------------------------|
+| 序号            | index              | 自定义列,customRender 显示行索引 |
+| 省标编码          | provinceStandardCode | 原"省标标码"更名 |
+| 标签名称          | tagName | — |
+| 所属一级分类        | firstCategory | — |
+| 所属二级分类        | secondCategory | — |
+| 标签说明 | tagDescription | — |
+| 启用状态 | tagStatus | customRender 翻译为"启用"/"停用" |
+| 本地关联人数 | relationCount | 关联中间表 COUNT(DISTINCT personal_id) 计算 |
+| 数据来源 | dataSource | customRender 翻译为"省一体化平台回流"/"本地自定义" |
+
+### 搜索区域(5个查询条件)
+
+标签名称、所属一级分类、所属二级分类、启用状态(下拉选择)、数据来源(下拉选择)
+
+---
+
+#### 2026-06-02:个人标签模块初始构建
+
+**需求描述:** 构建个人标签管理模块,实现标签的基础 CRUD、Excel 导入导出功能,支持批量启用/停用状态管理,新增/编辑时后端校验标签名称唯一性。
+
+**涉及文件:**
+
+| 文件                                               | 操作 | 原因                                    |
+|--------------------------------------------------|----|---------------------------------------|
+| `jeecgboot-vue3/src/views/recruitment/personalTag/PersonalTagList.vue` | 新增 | 个人标签列表页,含查询区域(3个条件)、表格(9列)、操作栏、导入导出和批量操作按钮 |
+| `jeecgboot-vue3/src/views/recruitment/personalTag/PersonalTag.api.ts` | 新增 | 封装个人标签 CRUD、导入导出、批量更新状态 API 接口 |
+| `jeecgboot-vue3/src/views/recruitment/personalTag/PersonalTag.data.ts` | 新增 | 定义 9 列表格列配置与 8 个高级查询字段 |
+| `jeecgboot-vue3/src/views/recruitment/personalTag/components/PersonalTagForm.vue` | 新增 | 个人标签表单,含 3 个必填字段校验,8 个字段录入 |
+| `jeecgboot-vue3/src/views/recruitment/personalTag/components/PersonalTagModal.vue` | 新增 | 全屏弹窗包裹 PersonalTagForm,提供新增/编辑/详情入口 |
+| `jeecg-boot/.../tag/controller/PersonalTagController.java` | 新增 | 个人标签 REST 控制器,提供 CRUD + 导出导入 + 批量修改状态接口 |
+| `jeecg-boot/.../tag/entity/PersonalTag.java` | 新增 | 个人标签实体,映射 `personal_tag` 表 |
+| `jeecg-boot/.../tag/service/IPersonalTagService.java` | 新增 | 服务接口,定义 `batchUpdateStatus` 和 `checkTagNameUnique` 方法 |
+| `jeecg-boot/.../tag/service/impl/PersonalTagServiceImpl.java` | 新增 | 服务实现,批量修改状态使用 AssertUtils 校验参数合法性;标签名称唯一性校验排除自身ID |
+| `jeecg-boot/.../tag/mapper/PersonalTagMapper.java` | 新增 | Mapper 接口,继承 BaseMapper |
+| `jeecg-boot/.../tag/mapper/xml/PersonalTagMapper.xml` | 新增 | MyBatis XML 映射骨架 |
+
+---
+
+## 二、企业标签库模块
+
+### 模块概述
+
+企业标签库模块(enterpriseTag)用于管理企业标签信息,功能与个人标签库对称,支持省一体化平台回流和本地自定义两种来源的标签,并提供标签的启用/停用状态管理。
+
+### 业务关系
+
+- **EnterpriseTag(企业标签信息)**:独立主表,存储企业标签的基础信息,通过 ENTERPRISE_TAG_RELATION 中间表与 EnterpriseInfo(企业基本信息)表建立多对多关联。
+- **EnterpriseTagRelation(企业标签关联)**:中间表,仅含 `tag_id` + `enterprise_id` 两个字段,联合主键。
+
+---
+
+### 模块文件结构
+
+#### 前端(jeecgboot-vue3/src/views/recruitment/enterpriseTag/)
+
+| 文件                                  | 用途                             |
+|-------------------------------------|--------------------------------|
+| `EnterpriseTagList.vue`                  | 企业标签列表页面,提供查询、新增、编辑、详情、删除、批量删除、批量启用/停用、导入导出等功能 |
+| `EnterpriseTag.api.ts`                   | 企业标签 API 封装                    |
+| `EnterpriseTag.data.ts`                  | 列表页表格列配置(9列)与高级查询字段定义(8个查询字段) |
+| `components/EnterpriseTagForm.vue`       | 企业标签新增/编辑/详情表单组件 |
+| `components/EnterpriseTagModal.vue`      | 企业标签弹窗组件,包裹 EnterpriseTagForm,全屏模式 |
+
+#### 后端(jeecg-boot/jeecg-boot-module/jeecg-module-zjrs/.../org/jeecg/modules/zjrs/tag/)
+
+| 层次           | 文件                                          |
+|--------------|---------------------------------------------|
+| Controller   | `controller/EnterpriseTagController.java`                   |
+| Entity       | `entity/EnterpriseTag.java`                             |
+| Service      | `service/IEnterpriseTagService.java`                     |
+| Service Impl | `service/impl/EnterpriseTagServiceImpl.java`                  |
+| Mapper       | `mapper/EnterpriseTagMapper.java`                       |
+| Mapper XML   | `mapper/xml/EnterpriseTagMapper.xml`             |
+
+---
+
+### 开发记录
+
+#### 功能清单
+
+| 功能            | 说明                                                             |
+|---------------|----------------------------------------------------------------|
+| 分页列表查询        | 支持按标签名称、所属一级分类、所属二级分类、启用状态、数据来源等条件查询,9 列展示(含本地关联人数) |
+| 新增            | 全屏表单,支持 8 个标签字段录入 |
+| 编辑            | 复用新增表单,预填充已有数据 |
+| 详情            | 表单禁用模式查看标签全部信息 |
+| 删除 / 批量删除     | 支持单条删除和批量删除,删除前弹窗确认 |
+| 批量启用/停用      | 支持选中多条标签后批量修改启用状态 |
+| 导入 / 导出 Excel | 基于 Jeecg 标准 Excel 导出导入功能,导出使用 VO 含本地关联人数 |
+| 高级查询          | 已定义 superQuerySchema(8个查询字段),前端列表页中高级查询按钮已注释 |
+| 标签名称唯一性校验     | 新增/编辑时后端校验标签名称不可重复 |
+
+#### 必填字段
+
+共 3 个必填字段:标签名称、启用状态、数据来源。
+
+---
+
+### 功能开发
+
+| 日期         | 内容           | 涉及文件          | 说明                     |
+|------------|--------------|---------------|------------------------|
+| 2026-06-02 | 企业标签模块初始构建 | 前端 5 个文件 + 后端 6 个文件 | 完成企业标签管理的基础 CRUD 功能,含 Excel 导入导出、批量启用/停用、标签名称唯一性校验 |
+
+### EnterpriseTag 实体字段一览
+
+| 分类     | 字段                   | 说明            |
+|--------|----------------------|---------------|
+| 基础信息   | provinceStandardCode | 省标编码         |
+|        | tagCode              | 标签编码         |
+|        | tagName              | 标签名称(必填)     |
+|        | firstCategory        | 所属一级分类       |
+|        | secondCategory       | 所属二级分类       |
+|        | tagDescription       | 标签说明         |
+| 状态     | tagStatus            | 启用状态(必填,1启用/0停用) |
+|        | dataSource           | 数据来源(必填,1省一体化平台回流/2本地自定义) |
+| 系统字段   | id / createBy / createTime / updateBy / updateTime / sysOrgCode | 系统内置字段 |
+
+### 表格列(9列)
+
+与个人标签库完全对称:
+
+| 列            | 对应字段              | 说明                     |
+|---------------|-------------------|------------------------|
+| 序号            | index              | 自定义列,customRender 显示行索引 |
+| 省标编码          | provinceStandardCode | 原"省标标码"更名 |
+| 标签名称          | tagName | — |
+| 所属一级分类        | firstCategory | — |
+| 所属二级分类        | secondCategory | — |
+| 标签说明 | tagDescription | — |
+| 启用状态 | tagStatus | customRender 翻译为"启用"/"停用" |
+| 本地关联人数 | relationCount | 关联中间表 COUNT(DISTINCT enterprise_id) 计算 |
+| 数据来源 | dataSource | customRender 翻译为"省一体化平台回流"/"本地自定义" |
+
+### 搜索区域(5个查询条件)
+
+标签名称、所属一级分类、所属二级分类、启用状态(下拉选择)、数据来源(下拉选择)
+
+---
+
+#### 2026-06-02:企业标签模块初始构建
+
+**需求描述:** 构建企业标签管理模块,实现标签的基础 CRUD、Excel 导入导出功能,支持批量启用/停用状态管理,新增/编辑时后端校验标签名称唯一性。功能与个人标签库完全对称。
+
+**涉及文件:**
+
+| 文件                                               | 操作 | 原因                                    |
+|--------------------------------------------------|----|---------------------------------------|
+| `jeecgboot-vue3/src/views/recruitment/enterpriseTag/EnterpriseTagList.vue` | 新增 | 企业标签列表页,含查询区域(3个条件)、表格(9列)、操作栏、导入导出和批量操作按钮(高级查询按钮已注释) |
+| `jeecgboot-vue3/src/views/recruitment/enterpriseTag/EnterpriseTag.api.ts` | 新增 | 封装企业标签 CRUD、导入导出、批量更新状态 API 接口 |
+| `jeecgboot-vue3/src/views/recruitment/enterpriseTag/EnterpriseTag.data.ts` | 新增 | 定义 9 列表格列配置与 8 个高级查询字段 |
+| `jeecgboot-vue3/src/views/recruitment/enterpriseTag/components/EnterpriseTagForm.vue` | 新增 | 企业标签表单,含 3 个必填字段校验,8 个字段录入 |
+| `jeecgboot-vue3/src/views/recruitment/enterpriseTag/components/EnterpriseTagModal.vue` | 新增 | 全屏弹窗包裹 EnterpriseTagForm,提供新增/编辑/详情入口 |
+| `jeecg-boot/.../tag/controller/EnterpriseTagController.java` | 新增 | 企业标签 REST 控制器,提供 CRUD + 导出导入 + 批量修改状态接口 |
+| `jeecg-boot/.../tag/entity/EnterpriseTag.java` | 新增 | 企业标签实体,映射 `enterprise_tag` 表 |
+| `jeecg-boot/.../tag/service/IEnterpriseTagService.java` | 新增 | 服务接口,定义 `batchUpdateStatus` 和 `checkTagNameUnique` 方法 |
+| `jeecg-boot/.../tag/service/impl/EnterpriseTagServiceImpl.java` | 新增 | 服务实现,批量修改状态使用 AssertUtils 校验参数合法性;标签名称唯一性校验排除自身ID |
+| `jeecg-boot/.../tag/mapper/EnterpriseTagMapper.java` | 新增 | Mapper 接口,继承 BaseMapper |
+| `jeecg-boot/.../tag/mapper/xml/EnterpriseTagMapper.xml` | 新增 | MyBatis XML 映射骨架 |
+
+---
+
+## 三、标签关联中间表
+
+### 模块概述
+
+为支持标签库与业务信息的多对多关联,建立两个关联中间表。
+
+### 业务关系
+
+- **PERSONAL_TAG_RELATION**:关联 PERSONAL_TAG(个人标签)与 PERSONAL_INFO(个人基本信息),通过 `tag_id` + `personal_id` 建立多对多关系。
+- **ENTERPRISE_TAG_RELATION**:关联 ENTERPRISE_TAG(企业标签)与 ENTERPRISE_INFO(企业基本信息),通过 `tag_id` + `enterprise_id` 建立多对多关系。
+
+两个表均只有两个字段(标签ID + 业务实体ID),并使用联合主键。
+
+---
+
+### 模块文件结构
+
+#### 后端(jeecg-boot/jeecg-boot-module/jeecg-module-zjrs/.../org/jeecg/modules/zjrs/tag/)
+
+| 层次           | 文件                                          |
+|--------------|---------------------------------------------|
+| Entity       | `entity/PersonalTagRelation.java`                       |
+| Entity       | `entity/EnterpriseTagRelation.java`                     |
+| Mapper       | `mapper/PersonalTagRelationMapper.java`                 |
+| Mapper       | `mapper/EnterpriseTagRelationMapper.java`               |
+| Mapper XML   | `mapper/xml/PersonalTagRelationMapper.xml`              |
+| Mapper XML   | `mapper/xml/EnterpriseTagRelationMapper.xml`            |
+
+#### SQL脚本
+
+| 文件                                               | 操作 | 说明 |
+|--------------------------------------------------|----|----|
+| `.docs/sql/标签库信息.sql` | 新增 | 在个人标签表和企业标签表之后追加 PERSONAL_TAG_RELATION 和 ENTERPRISE_TAG_RELATION 两张关联中间表的建表语句 |
+
+---
+
+### 关联表字段一览
+
+#### PERSONAL_TAG_RELATION
+
+| 字段          | 类型          | 说明               | 约束            |
+|-------------|-------------|------------------|---------------|
+| tag_id      | VARCHAR(36) | 标签ID(关联PERSONAL_TAG.ID) | NOT NULL,联合主键 |
+| personal_id | VARCHAR(36) | 个人ID(关联PERSONAL_INFO.ID) | NOT NULL,联合主键 |
+
+#### ENTERPRISE_TAG_RELATION
+
+| 字段            | 类型          | 说明                  | 约束            |
+|---------------|-------------|---------------------|---------------|
+| tag_id        | VARCHAR(36) | 标签ID(关联ENTERPRISE_TAG.ID) | NOT NULL,联合主键 |
+| enterprise_id | VARCHAR(36) | 企业ID(关联ENTERPRISE_INFO.ID) | NOT NULL,联合主键 |
+
+---
+
+### 2026-06-04:关联中间表构建
+
+**需求描述:** 为标签库与业务信息建立多对多关联关系,创建两张中间表,两个中间表均只包含标签ID + 业务实体ID两个字段,使用联合主键。
+
+**涉及文件:**
+
+| 文件                                               | 操作 | 原因                                    |
+|--------------------------------------------------|----|---------------------------------------|
+| `.docs/sql/标签库信息.sql` | 修改 | 新增 PERSONAL_TAG_RELATION 和 ENTERPRISE_TAG_RELATION 两张关联中间表的建表语句,含表注释和字段注释 |
+| `jeecg-boot/.../tag/entity/PersonalTagRelation.java` | 新增 | 个人标签关联实体,映射 `personal_tag_relation` 表,仅含 `tagId` + `personalId` 两个字段 |
+| `jeecg-boot/.../tag/entity/EnterpriseTagRelation.java` | 新增 | 企业标签关联实体,映射 `enterprise_tag_relation` 表,仅含 `tagId` + `enterpriseId` 两个字段 |
+| `jeecg-boot/.../tag/mapper/PersonalTagRelationMapper.java` | 新增 | 个人标签关联 Mapper 接口,继承 BaseMapper |
+| `jeecg-boot/.../tag/mapper/EnterpriseTagRelationMapper.java` | 新增 | 企业标签关联 Mapper 接口,继承 BaseMapper |
+| `jeecg-boot/.../tag/mapper/xml/PersonalTagRelationMapper.xml` | 新增 | MyBatis XML 映射骨架 |
+| `jeecg-boot/.../tag/mapper/xml/EnterpriseTagRelationMapper.xml` | 新增 | MyBatis XML 映射骨架 |
+
+---
+
+### 2026-06-04:标签库字段调整 + 本地关联人数 + VO 查询
+
+**需求描述:** 调整个人标签库和企业标签库的查询字段与展示字段,增加"本地关联人数"列,通过关联中间表计算每个标签关联的不重复个人/企业数量;创建 VO 类承载分页列表导出数据,后端分页查询和导出接口改为 VO 映射。
+
+**查询字段调整(从3个改为5个):**
+
+| 新查询字段 | 对应字段 | 说明 |
+|-----------|---------|------|
+| 标签名称 | tagName | 保留 |
+| 所属一级分类 | firstCategory | 新增查询字段 |
+| 所属二级分类 | secondCategory | 新增查询字段 |
+| 启用状态 | tagStatus | 原"标签状态"改名为"启用状态" |
+| 数据来源 | dataSource | 保留 |
+
+**删除的查询字段:** 无(原有标签状态改为启用状态)
+
+**表格列调整(9列):**
+
+| 新列 | 对应字段 | 说明 |
+|------|---------|------|
+| 序号 | index | 保留 |
+| 省标编码 | provinceStandardCode | 原"省标标码"改名,取消标签编码列 |
+| 标签名称 | tagName | 保留 |
+| 所属一级分类 | firstCategory | 保留 |
+| 所属二级分类 | secondCategory | 保留 |
+| 标签说明 | tagDescription | 保留 |
+| 启用状态 | tagStatus | 原"标签启用状态"改名 |
+| 本地关联人数 | relationCount | 新增,关联中间表 COUNT(DISTINCT) 计算 |
+| 数据来源 | dataSource | 保留 |
+
+**删除的列:** 标签编码
+
+**涉及文件:**
+
+| 文件 | 操作 | 原因 |
+|------|------|------|
+| `jeecg-boot/.../tag/vo/PersonalTagVo.java` | 新增 | 个人标签 VO,含 entity 所有字段 + relationCount,标注 `@Excel` 用于导出 |
+| `jeecg-boot/.../tag/vo/EnterpriseTagVo.java` | 新增 | 企业标签 VO,含 entity 所有字段 + relationCount,标注 `@Excel` 用于导出 |
+| `jeecg-boot/.../tag/mapper/xml/PersonalTagMapper.xml` | 修改 | 新增 `queryPageListWithRelationCount` 和 `queryListWithRelationCount` 两个 SQL 查询,通过子查询 LEFT JOIN PERSONAL_TAG_RELATION 计算 COUNT(DISTINCT PERSONAL_ID) 并应用 `${ew.customSqlSegment}` 条件过滤 |
+| `jeecg-boot/.../tag/mapper/xml/EnterpriseTagMapper.xml` | 修改 | 同上,LEFT JOIN ENTERPRISE_TAG_RELATION 计算 COUNT(DISTINCT ENTERPRISE_ID) |
+| `jeecg-boot/.../tag/mapper/PersonalTagMapper.java` | 修改 | 新增两个 VO 查询方法声明 |
+| `jeecg-boot/.../tag/mapper/EnterpriseTagMapper.java` | 修改 | 新增两个 VO 查询方法声明 |
+| `jeecg-boot/.../tag/service/IPersonalTagService.java` | 修改 | 新增 `queryPageListWithRelationCount` 和 `queryListWithRelationCount` 方法声明 |
+| `jeecg-boot/.../tag/service/IEnterpriseTagService.java` | 修改 | 新增 `queryPageListWithRelationCount` 和 `queryListWithRelationCount` 方法声明 |
+| `jeecg-boot/.../tag/service/impl/PersonalTagServiceImpl.java` | 修改 | 实现两个 VO 查询方法;`queryListWithRelationCount` 中处理导出 selections 选中记录筛选 |
+| `jeecg-boot/.../tag/service/impl/EnterpriseTagServiceImpl.java` | 修改 | 同上 |
+| `jeecg-boot/.../tag/controller/PersonalTagController.java` | 修改 | list 接口返回类型改为 `IPage<PersonalTagVo>`,委托 Service 的 VO 查询方法;exportXls 改写为使用 `JeecgEntityExcelView` + `PersonalTagVo.class` + `queryListWithRelationCount` 导出 VO 列表 |
+| `jeecg-boot/.../tag/controller/EnterpriseTagController.java` | 修改 | 同上,使用 `EnterpriseTagVo` |
+| `jeecgboot-vue3/src/views/recruitment/personalTag/PersonalTagList.vue` | 修改 | 查询表单从 3 个字段改为 5 个(新增所属一级分类、所属二级分类,标签状态改为启用状态),布局调整为 5+1 按钮每行 |
+| `jeecgboot-vue3/src/views/recruitment/personalTag/PersonalTag.data.ts` | 修改 | 表格列去掉标签编码,省标标码改名为省标编码,标签启用状态改名为启用状态,新增本地关联人数列 |
+| `jeecgboot-vue3/src/views/recruitment/enterpriseTag/EnterpriseTagList.vue` | 修改 | 同上 |
+| `jeecgboot-vue3/src/views/recruitment/enterpriseTag/EnterpriseTag.data.ts` | 修改 | 同上 |
+
+---
+
+### 2026-06-04:标签库 dataSource 字段改为数据字典驱动
+
+**需求描述:** 将个人标签库和企业标签库的前端 dataSource 字段从硬编码 `<a-select-option>` 改为通过 `useDict` 字典工具从后端批量接口加载字典数据,遵循项目现有字典实现规范。
+
+**涉及文件:**
+
+| 文件 | 操作 | 原因 |
+|------|------|------|
+| `jeecgboot-vue3/src/views/recruitment/personalTag/PersonalTagList.vue` | 修改 | 查询表单 dataSource 的 `<a-select>` 从硬编码 `<a-select-option>` 改为 `:options="dataSourceOptions"` 绑定字典选项;`bodyCell` 插槽新增 `getDictText('DataSource', text)` 翻译渲染;脚本区导入 `useDict`、`computed`,设置 `getDictOptions` 和 `dataSourceOptions` |
+| `jeecgboot-vue3/src/views/recruitment/personalTag/PersonalTag.data.ts` | 修改 | 移除 dataSource 列的硬编码 `customRender`(改为由 `bodyCell` 统一翻译) |
+| `jeecgboot-vue3/src/views/recruitment/personalTag/components/PersonalTagForm.vue` | 修改 | 表单 dataSource 从硬编码 `<a-select-option>` 改为 `:options="dataSourceOptions"` 绑定字典选项;脚本区导入 `useDict`,设置 `getDictOptions` 和 `dataSourceOptions` |
+| `jeecgboot-vue3/src/views/recruitment/enterpriseTag/EnterpriseTagList.vue` | 修改 | 同上 PersonalTagList.vue 改动 |
+
+---
+
+### 2026-06-04:批量操作按钮常显(无选中时禁用)
+
+**需求描述:** 批量删除和批量启用/停用按钮始终显示在页面上,不随选中行数变化而显示/隐藏。当未选中任何数据行时按钮处于禁用(disabled)状态,选中数据行后按钮变为可用状态。
+
+**涉及文件:**
+
+| 文件 | 操作 | 原因 |
+|------|------|------|
+| `jeecgboot-vue3/src/views/recruitment/personalTag/PersonalTagList.vue` | 修改 | 批量操作 `<a-dropdown>` 的 `v-if="selectedRowKeys.length > 0"` 改为始终渲染,使用 `:disabled="selectedRowKeys.length === 0"` 控制禁用状态 |
+| `jeecgboot-vue3/src/views/recruitment/enterpriseTag/EnterpriseTagList.vue` | 修改 | 同上 PersonalTagList.vue 改动 |
+
+---
+
+### 2026-06-04:标签表单新增优化(默认启用 + 字段禁用 + 标签编码自动生成)
+
+**需求描述:** 优化标签新增表单体验:新增时标签状态默认启用,数据来源默认为市系统并禁止编辑,省标编码禁止编辑,标签编码改为后端自动生成流水号(个人标签格式 `ZJGR`+三位流水,企业标签格式 `ZJQY`+三位流水)。
+
+**后端改动:**
+
+| 文件 | 操作 | 原因 |
+|------|------|------|
+| `jeecg-boot/.../tag/service/IPersonalTagService.java` | 修改 | 新增 `generateTagCode()` 方法声明 |
+| `jeecg-boot/.../tag/service/IEnterpriseTagService.java` | 修改 | 新增 `generateTagCode()` 方法声明 |
+| `jeecg-boot/.../tag/service/impl/PersonalTagServiceImpl.java` | 修改 | 实现 `generateTagCode()`:查询 `tag_code LIKE 'ZJGR%'` 取最大流水号 +1,格式 `ZJGR%03d` |
+| `jeecg-boot/.../tag/service/impl/EnterpriseTagServiceImpl.java` | 修改 | 同上,前缀 `ZJQY` |
+| `jeecg-boot/.../tag/controller/PersonalTagController.java` | 修改 | 新增 `GET /generateTagCode` 接口 |
+| `jeecg-boot/.../tag/controller/EnterpriseTagController.java` | 修改 | 同上 |
+
+**前端改动:**
+
+| 文件 | 操作 | 原因 |
+|------|------|------|
+| `jeecgboot-vue3/src/views/recruitment/personalTag/PersonalTag.api.ts` | 修改 | 新增 `generateTagCode()` API 调用,使用 `isTransformResponse: false` 跳过消息提示 |
+| `jeecgboot-vue3/src/views/recruitment/enterpriseTag/EnterpriseTag.api.ts` | 修改 | 同上 |
+| `jeecgboot-vue3/src/views/recruitment/personalTag/components/PersonalTagForm.vue` | 修改 | 省标编码 `disabled` 占位"系统自动生成";标签编码 `disabled` 占位"系统自动生成",新增时调用后端生成流水号填充;数据来源 `disabled` |
+| `jeecgboot-vue3/src/views/recruitment/personalTag/components/PersonalTagForm.vue`(personaltag 小写目录) | 修改 | 同上 |
+| `jeecgboot-vue3/src/views/recruitment/enterpriseTag/components/EnterpriseTagForm.vue` | 修改 | 同 PersonalTagForm 改动;另将 `tagStatus` 默认值从 `''` 改为 `'1'`(启用),`dataSource` 默认值从 `''` 改为 `'2'`(市系统) |
+| `jeecgboot-vue3/src/views/recruitment/enterpriseTag/components/EnterpriseTagForm.vue`(enterprisetag 小写目录) | 修改 | 同上 |
+---
+
+### 2026-06-04:省系统标签禁用编辑/删除
+
+**需求描述:** dataSource为'1'(省一体化平台回流)的标签属于省系统数据,不允许编辑和删除,只允许修改启用状态。单条记录的操作栏中编辑和删除按钮保持显示但置灰不可点击;批量删除时自动过滤掉省系统标签。
+
+**涉及文件:**
+
+| 文件 | 操作 | 原因 |
+|------|------|------|
+| `jeecgboot-vue3/src/views/recruitment/personalTag/PersonalTagList.vue` | 修改 | `getTableAction` 中编辑按钮增加 `disabled: record.dataSource === '1'`;`getDropDownAction` 中删除按钮增加 `disabled: record.dataSource === '1'`;`batchHandleDelete` 增加省系统数据过滤逻辑,选中省系统标签时弹出警告并自动跳过 |
+| `jeecgboot-vue3/src/views/recruitment/enterpriseTag/EnterpriseTagList.vue` | 修改 | 同上 PersonalTagList.vue 改动 |

+ 38 - 0
.docs/sql/标签库信息.sql

@@ -118,3 +118,41 @@ COMMENT
 ON COLUMN ENTERPRISE_TAG.UPDATE_TIME IS '更新时间';
 COMMENT
 ON COLUMN ENTERPRISE_TAG.SYS_ORG_CODE IS '组织机构编号';
+
+
+-- ============================================================
+-- 表3:个人标签关联表
+-- 说明:个人标签与个人基本信息的多对多关联关系中间表
+-- ============================================================
+CREATE TABLE PERSONAL_TAG_RELATION
+(
+    TAG_ID      VARCHAR(36) NOT NULL,
+    PERSONAL_ID VARCHAR(36) NOT NULL,
+    PRIMARY KEY (TAG_ID, PERSONAL_ID)
+);
+
+COMMENT
+ON TABLE PERSONAL_TAG_RELATION IS '个人标签关联表';
+COMMENT
+ON COLUMN PERSONAL_TAG_RELATION.TAG_ID IS '标签ID(关联PERSONAL_TAG.ID)';
+COMMENT
+ON COLUMN PERSONAL_TAG_RELATION.PERSONAL_ID IS '个人ID(关联PERSONAL_INFO.ID)';
+
+
+-- ============================================================
+-- 表4:企业标签关联表
+-- 说明:企业标签与企业信息的多对多关联关系中间表
+-- ============================================================
+CREATE TABLE ENTERPRISE_TAG_RELATION
+(
+    TAG_ID        VARCHAR(36) NOT NULL,
+    ENTERPRISE_ID VARCHAR(36) NOT NULL,
+    PRIMARY KEY (TAG_ID, ENTERPRISE_ID)
+);
+
+COMMENT
+ON TABLE ENTERPRISE_TAG_RELATION IS '企业标签关联表';
+COMMENT
+ON COLUMN ENTERPRISE_TAG_RELATION.TAG_ID IS '标签ID(关联ENTERPRISE_TAG.ID)';
+COMMENT
+ON COLUMN ENTERPRISE_TAG_RELATION.ENTERPRISE_ID IS '企业ID(关联ENTERPRISE_INFO.ID)';

+ 40 - 7
jeecg-boot/jeecg-boot-module/jeecg-module-zjrs/src/main/java/org/jeecg/modules/zjrs/tag/controller/EnterpriseTagController.java

@@ -9,13 +9,21 @@ import jakarta.servlet.http.HttpServletRequest;
 import jakarta.servlet.http.HttpServletResponse;
 import jakarta.validation.Valid;
 import lombok.extern.slf4j.Slf4j;
+import org.apache.shiro.SecurityUtils;
 import org.apache.shiro.authz.annotation.RequiresPermissions;
 import org.jeecg.common.api.vo.Result;
 import org.jeecg.common.aspect.annotation.AutoLog;
 import org.jeecg.common.system.base.controller.JeecgController;
 import org.jeecg.common.system.query.QueryGenerator;
+import org.jeecg.common.system.vo.LoginUser;
+import org.jeecg.common.util.oConvertUtils;
 import org.jeecg.modules.zjrs.tag.entity.EnterpriseTag;
 import org.jeecg.modules.zjrs.tag.service.IEnterpriseTagService;
+import org.jeecg.modules.zjrs.tag.vo.EnterpriseTagVo;
+import org.jeecgframework.poi.excel.def.NormalExcelConstants;
+import org.jeecgframework.poi.excel.entity.ExportParams;
+import org.jeecgframework.poi.excel.entity.enmus.ExcelType;
+import org.jeecgframework.poi.excel.view.JeecgEntityExcelView;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.web.bind.annotation.*;
 import org.springframework.web.servlet.ModelAndView;
@@ -48,13 +56,13 @@ public class EnterpriseTagController extends JeecgController<EnterpriseTag, IEnt
      */
     @Operation(summary = "标签库-企业标签-分页列表查询")
     @GetMapping(value = "/list")
-    public Result<IPage<EnterpriseTag>> queryPageList(EnterpriseTag enterpriseTag,
-                                                      @RequestParam(name = "pageNo", defaultValue = "1") Integer pageNo,
-                                                      @RequestParam(name = "pageSize", defaultValue = "10") Integer pageSize,
-                                                      HttpServletRequest req) {
+    public Result<IPage<EnterpriseTagVo>> queryPageList(EnterpriseTag enterpriseTag,
+                                                        @RequestParam(name = "pageNo", defaultValue = "1") Integer pageNo,
+                                                        @RequestParam(name = "pageSize", defaultValue = "10") Integer pageSize,
+                                                        HttpServletRequest req) {
         QueryWrapper<EnterpriseTag> queryWrapper = QueryGenerator.initQueryWrapper(enterpriseTag, req.getParameterMap());
-        Page<EnterpriseTag> page = new Page<EnterpriseTag>(pageNo, pageSize);
-        IPage<EnterpriseTag> pageList = enterpriseTagService.page(page, queryWrapper);
+        Page<EnterpriseTagVo> page = new Page<EnterpriseTagVo>(pageNo, pageSize);
+        IPage<EnterpriseTagVo> pageList = enterpriseTagService.queryPageListWithRelationCount(page, queryWrapper, req);
         return Result.OK(pageList);
     }
 
@@ -145,7 +153,23 @@ public class EnterpriseTagController extends JeecgController<EnterpriseTag, IEnt
     @RequiresPermissions("tag:enterprise_tag:exportXls")
     @RequestMapping(value = "/exportXls")
     public ModelAndView exportXls(HttpServletRequest request, EnterpriseTag enterpriseTag) {
-        return super.exportXls(request, enterpriseTag, EnterpriseTag.class, "企业标签信息表");
+        QueryWrapper<EnterpriseTag> queryWrapper = QueryGenerator.initQueryWrapper(enterpriseTag, request.getParameterMap());
+        LoginUser sysUser = (LoginUser) SecurityUtils.getSubject().getPrincipal();
+
+        List<EnterpriseTagVo> exportList = enterpriseTagService.queryListWithRelationCount(queryWrapper, request);
+
+        ModelAndView mv = new ModelAndView(new JeecgEntityExcelView());
+        mv.addObject(NormalExcelConstants.FILE_NAME, "企业标签信息表");
+        mv.addObject(NormalExcelConstants.CLASS, EnterpriseTagVo.class);
+        ExportParams exportParams = new ExportParams("企业标签信息报表", "导出人:" + sysUser.getRealname(), "企业标签信息", ExcelType.XSSF);
+        mv.addObject(NormalExcelConstants.PARAMS, exportParams);
+        mv.addObject(NormalExcelConstants.DATA_LIST, exportList);
+
+        String exportFields = request.getParameter(NormalExcelConstants.EXPORT_FIELDS);
+        if (oConvertUtils.isNotEmpty(exportFields)) {
+            mv.addObject(NormalExcelConstants.EXPORT_FIELDS, exportFields);
+        }
+        return mv;
     }
 
     /**
@@ -178,4 +202,13 @@ public class EnterpriseTagController extends JeecgController<EnterpriseTag, IEnt
         enterpriseTagService.batchUpdateStatus(idList, tagStatus);
         return Result.OK("批量修改状态成功!");
     }
+
+    /**
+     * 生成标签编码
+     */
+    @Operation(summary = "标签库-企业标签-生成标签编码")
+    @GetMapping(value = "/generateTagCode")
+    public Result<String> generateTagCode() {
+        return Result.OK(enterpriseTagService.generateTagCode());
+    }
 }

+ 40 - 7
jeecg-boot/jeecg-boot-module/jeecg-module-zjrs/src/main/java/org/jeecg/modules/zjrs/tag/controller/PersonalTagController.java

@@ -9,13 +9,21 @@ import jakarta.servlet.http.HttpServletRequest;
 import jakarta.servlet.http.HttpServletResponse;
 import jakarta.validation.Valid;
 import lombok.extern.slf4j.Slf4j;
+import org.apache.shiro.SecurityUtils;
 import org.apache.shiro.authz.annotation.RequiresPermissions;
 import org.jeecg.common.api.vo.Result;
 import org.jeecg.common.aspect.annotation.AutoLog;
 import org.jeecg.common.system.base.controller.JeecgController;
 import org.jeecg.common.system.query.QueryGenerator;
+import org.jeecg.common.system.vo.LoginUser;
+import org.jeecg.common.util.oConvertUtils;
 import org.jeecg.modules.zjrs.tag.entity.PersonalTag;
 import org.jeecg.modules.zjrs.tag.service.IPersonalTagService;
+import org.jeecg.modules.zjrs.tag.vo.PersonalTagVo;
+import org.jeecgframework.poi.excel.def.NormalExcelConstants;
+import org.jeecgframework.poi.excel.entity.ExportParams;
+import org.jeecgframework.poi.excel.entity.enmus.ExcelType;
+import org.jeecgframework.poi.excel.view.JeecgEntityExcelView;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.web.bind.annotation.*;
 import org.springframework.web.servlet.ModelAndView;
@@ -48,13 +56,13 @@ public class PersonalTagController extends JeecgController<PersonalTag, IPersona
      */
     @Operation(summary = "标签库-个人标签-分页列表查询")
     @GetMapping(value = "/list")
-    public Result<IPage<PersonalTag>> queryPageList(PersonalTag personalTag,
-                                                    @RequestParam(name = "pageNo", defaultValue = "1") Integer pageNo,
-                                                    @RequestParam(name = "pageSize", defaultValue = "10") Integer pageSize,
-                                                    HttpServletRequest req) {
+    public Result<IPage<PersonalTagVo>> queryPageList(PersonalTag personalTag,
+                                                      @RequestParam(name = "pageNo", defaultValue = "1") Integer pageNo,
+                                                      @RequestParam(name = "pageSize", defaultValue = "10") Integer pageSize,
+                                                      HttpServletRequest req) {
         QueryWrapper<PersonalTag> queryWrapper = QueryGenerator.initQueryWrapper(personalTag, req.getParameterMap());
-        Page<PersonalTag> page = new Page<PersonalTag>(pageNo, pageSize);
-        IPage<PersonalTag> pageList = personalTagService.page(page, queryWrapper);
+        Page<PersonalTagVo> page = new Page<PersonalTagVo>(pageNo, pageSize);
+        IPage<PersonalTagVo> pageList = personalTagService.queryPageListWithRelationCount(page, queryWrapper, req);
         return Result.OK(pageList);
     }
 
@@ -145,7 +153,23 @@ public class PersonalTagController extends JeecgController<PersonalTag, IPersona
     @RequiresPermissions("tag:personal_tag:exportXls")
     @RequestMapping(value = "/exportXls")
     public ModelAndView exportXls(HttpServletRequest request, PersonalTag personalTag) {
-        return super.exportXls(request, personalTag, PersonalTag.class, "个人标签信息表");
+        QueryWrapper<PersonalTag> queryWrapper = QueryGenerator.initQueryWrapper(personalTag, request.getParameterMap());
+        LoginUser sysUser = (LoginUser) SecurityUtils.getSubject().getPrincipal();
+
+        List<PersonalTagVo> exportList = personalTagService.queryListWithRelationCount(queryWrapper, request);
+
+        ModelAndView mv = new ModelAndView(new JeecgEntityExcelView());
+        mv.addObject(NormalExcelConstants.FILE_NAME, "个人标签信息表");
+        mv.addObject(NormalExcelConstants.CLASS, PersonalTagVo.class);
+        ExportParams exportParams = new ExportParams("个人标签信息报表", "导出人:" + sysUser.getRealname(), "个人标签信息", ExcelType.XSSF);
+        mv.addObject(NormalExcelConstants.PARAMS, exportParams);
+        mv.addObject(NormalExcelConstants.DATA_LIST, exportList);
+
+        String exportFields = request.getParameter(NormalExcelConstants.EXPORT_FIELDS);
+        if (oConvertUtils.isNotEmpty(exportFields)) {
+            mv.addObject(NormalExcelConstants.EXPORT_FIELDS, exportFields);
+        }
+        return mv;
     }
 
     /**
@@ -178,4 +202,13 @@ public class PersonalTagController extends JeecgController<PersonalTag, IPersona
         personalTagService.batchUpdateStatus(idList, tagStatus);
         return Result.OK("批量修改状态成功!");
     }
+
+    /**
+     * 生成标签编码
+     */
+    @Operation(summary = "标签库-个人标签-生成标签编码")
+    @GetMapping(value = "/generateTagCode")
+    public Result<String> generateTagCode() {
+        return Result.OK(personalTagService.generateTagCode());
+    }
 }

+ 35 - 0
jeecg-boot/jeecg-boot-module/jeecg-module-zjrs/src/main/java/org/jeecg/modules/zjrs/tag/entity/EnterpriseTagRelation.java

@@ -0,0 +1,35 @@
+package org.jeecg.modules.zjrs.tag.entity;
+
+import com.baomidou.mybatisplus.annotation.TableName;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import lombok.experimental.Accessors;
+
+import java.io.Serializable;
+
+/**
+ * @Description: 企业标签关联表
+ * @Author: jeecg-boot
+ * @Date: 2026-06-04
+ * @Version: V1.0
+ */
+@Data
+@TableName("enterprise_tag_relation")
+@Accessors(chain = true)
+@EqualsAndHashCode(callSuper = false)
+@Schema(description = "企业标签关联表")
+public class EnterpriseTagRelation implements Serializable {
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * 标签ID(关联ENTERPRISE_TAG.ID)
+     */
+    @Schema(description = "标签ID")
+    private java.lang.String tagId;
+    /**
+     * 企业ID(关联ENTERPRISE_INFO.ID)
+     */
+    @Schema(description = "企业ID")
+    private java.lang.String enterpriseId;
+}

+ 35 - 0
jeecg-boot/jeecg-boot-module/jeecg-module-zjrs/src/main/java/org/jeecg/modules/zjrs/tag/entity/PersonalTagRelation.java

@@ -0,0 +1,35 @@
+package org.jeecg.modules.zjrs.tag.entity;
+
+import com.baomidou.mybatisplus.annotation.TableName;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import lombok.experimental.Accessors;
+
+import java.io.Serializable;
+
+/**
+ * @Description: 个人标签关联表
+ * @Author: jeecg-boot
+ * @Date: 2026-06-04
+ * @Version: V1.0
+ */
+@Data
+@TableName("personal_tag_relation")
+@Accessors(chain = true)
+@EqualsAndHashCode(callSuper = false)
+@Schema(description = "个人标签关联表")
+public class PersonalTagRelation implements Serializable {
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * 标签ID(关联PERSONAL_TAG.ID)
+     */
+    @Schema(description = "标签ID")
+    private java.lang.String tagId;
+    /**
+     * 个人ID(关联PERSONAL_INFO.ID)
+     */
+    @Schema(description = "个人ID")
+    private java.lang.String personalId;
+}

+ 16 - 0
jeecg-boot/jeecg-boot-module/jeecg-module-zjrs/src/main/java/org/jeecg/modules/zjrs/tag/mapper/EnterpriseTagMapper.java

@@ -1,7 +1,14 @@
 package org.jeecg.modules.zjrs.tag.mapper;
 
+import com.baomidou.mybatisplus.core.conditions.Wrapper;
 import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.core.toolkit.Constants;
+import org.apache.ibatis.annotations.Param;
 import org.jeecg.modules.zjrs.tag.entity.EnterpriseTag;
+import org.jeecg.modules.zjrs.tag.vo.EnterpriseTagVo;
+
+import java.util.List;
 
 /**
  * @Description: 企业标签信息表
@@ -11,4 +18,13 @@ import org.jeecg.modules.zjrs.tag.entity.EnterpriseTag;
  */
 public interface EnterpriseTagMapper extends BaseMapper<EnterpriseTag> {
 
+    /**
+     * 分页查询企业标签(含本地关联人数)
+     */
+    IPage<EnterpriseTagVo> queryPageListWithRelationCount(IPage<?> page, @Param(Constants.WRAPPER) Wrapper<EnterpriseTag> queryWrapper);
+
+    /**
+     * 全量查询企业标签(含本地关联人数,用于导出)
+     */
+    List<EnterpriseTagVo> queryListWithRelationCount(@Param(Constants.WRAPPER) Wrapper<EnterpriseTag> queryWrapper);
 }

+ 14 - 0
jeecg-boot/jeecg-boot-module/jeecg-module-zjrs/src/main/java/org/jeecg/modules/zjrs/tag/mapper/EnterpriseTagRelationMapper.java

@@ -0,0 +1,14 @@
+package org.jeecg.modules.zjrs.tag.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import org.jeecg.modules.zjrs.tag.entity.EnterpriseTagRelation;
+
+/**
+ * @Description: 企业标签关联表
+ * @Author: jeecg-boot
+ * @Date: 2026-06-04
+ * @Version: V1.0
+ */
+public interface EnterpriseTagRelationMapper extends BaseMapper<EnterpriseTagRelation> {
+
+}

+ 16 - 0
jeecg-boot/jeecg-boot-module/jeecg-module-zjrs/src/main/java/org/jeecg/modules/zjrs/tag/mapper/PersonalTagMapper.java

@@ -1,7 +1,14 @@
 package org.jeecg.modules.zjrs.tag.mapper;
 
+import com.baomidou.mybatisplus.core.conditions.Wrapper;
 import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.core.toolkit.Constants;
+import org.apache.ibatis.annotations.Param;
 import org.jeecg.modules.zjrs.tag.entity.PersonalTag;
+import org.jeecg.modules.zjrs.tag.vo.PersonalTagVo;
+
+import java.util.List;
 
 /**
  * @Description: 个人标签信息表
@@ -11,4 +18,13 @@ import org.jeecg.modules.zjrs.tag.entity.PersonalTag;
  */
 public interface PersonalTagMapper extends BaseMapper<PersonalTag> {
 
+    /**
+     * 分页查询个人标签(含本地关联人数)
+     */
+    IPage<PersonalTagVo> queryPageListWithRelationCount(IPage<?> page, @Param(Constants.WRAPPER) Wrapper<PersonalTag> queryWrapper);
+
+    /**
+     * 全量查询个人标签(含本地关联人数,用于导出)
+     */
+    List<PersonalTagVo> queryListWithRelationCount(@Param(Constants.WRAPPER) Wrapper<PersonalTag> queryWrapper);
 }

+ 14 - 0
jeecg-boot/jeecg-boot-module/jeecg-module-zjrs/src/main/java/org/jeecg/modules/zjrs/tag/mapper/PersonalTagRelationMapper.java

@@ -0,0 +1,14 @@
+package org.jeecg.modules.zjrs.tag.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import org.jeecg.modules.zjrs.tag.entity.PersonalTagRelation;
+
+/**
+ * @Description: 个人标签关联表
+ * @Author: jeecg-boot
+ * @Date: 2026-06-04
+ * @Version: V1.0
+ */
+public interface PersonalTagRelationMapper extends BaseMapper<PersonalTagRelation> {
+
+}

+ 54 - 0
jeecg-boot/jeecg-boot-module/jeecg-module-zjrs/src/main/java/org/jeecg/modules/zjrs/tag/mapper/xml/EnterpriseTagMapper.xml

@@ -2,4 +2,58 @@
 <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
 <mapper namespace="org.jeecg.modules.zjrs.tag.mapper.EnterpriseTagMapper">
 
+    <!-- 企业标签分页查询(含本地关联人数) -->
+    <select id="queryPageListWithRelationCount" resultType="org.jeecg.modules.zjrs.tag.vo.EnterpriseTagVo">
+        SELECT
+            et.ID,
+            et.PROVINCE_STANDARD_CODE,
+            et.TAG_CODE,
+            et.TAG_NAME,
+            et.FIRST_CATEGORY,
+            et.SECOND_CATEGORY,
+            et.TAG_DESCRIPTION,
+            et.TAG_STATUS,
+            et.DATA_SOURCE,
+            et.CREATE_BY,
+            et.CREATE_TIME,
+            et.UPDATE_BY,
+            et.UPDATE_TIME,
+            et.SYS_ORG_CODE,
+            COALESCE(etr_cnt.relation_count, 0) AS relationCount
+        FROM ENTERPRISE_TAG et
+        LEFT JOIN (
+            SELECT TAG_ID, COUNT(DISTINCT ENTERPRISE_ID) AS relation_count
+            FROM ENTERPRISE_TAG_RELATION
+            GROUP BY TAG_ID
+        ) etr_cnt ON et.ID = etr_cnt.TAG_ID
+        ${ew.customSqlSegment}
+    </select>
+
+    <!-- 企业标签全量查询(含本地关联人数,用于导出) -->
+    <select id="queryListWithRelationCount" resultType="org.jeecg.modules.zjrs.tag.vo.EnterpriseTagVo">
+        SELECT
+            et.ID,
+            et.PROVINCE_STANDARD_CODE,
+            et.TAG_CODE,
+            et.TAG_NAME,
+            et.FIRST_CATEGORY,
+            et.SECOND_CATEGORY,
+            et.TAG_DESCRIPTION,
+            et.TAG_STATUS,
+            et.DATA_SOURCE,
+            et.CREATE_BY,
+            et.CREATE_TIME,
+            et.UPDATE_BY,
+            et.UPDATE_TIME,
+            et.SYS_ORG_CODE,
+            COALESCE(etr_cnt.relation_count, 0) AS relationCount
+        FROM ENTERPRISE_TAG et
+        LEFT JOIN (
+            SELECT TAG_ID, COUNT(DISTINCT ENTERPRISE_ID) AS relation_count
+            FROM ENTERPRISE_TAG_RELATION
+            GROUP BY TAG_ID
+        ) etr_cnt ON et.ID = etr_cnt.TAG_ID
+        ${ew.customSqlSegment}
+    </select>
+
 </mapper>

+ 5 - 0
jeecg-boot/jeecg-boot-module/jeecg-module-zjrs/src/main/java/org/jeecg/modules/zjrs/tag/mapper/xml/EnterpriseTagRelationMapper.xml

@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="org.jeecg.modules.zjrs.tag.mapper.EnterpriseTagRelationMapper">
+
+</mapper>

+ 54 - 0
jeecg-boot/jeecg-boot-module/jeecg-module-zjrs/src/main/java/org/jeecg/modules/zjrs/tag/mapper/xml/PersonalTagMapper.xml

@@ -2,4 +2,58 @@
 <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
 <mapper namespace="org.jeecg.modules.zjrs.tag.mapper.PersonalTagMapper">
 
+    <!-- 个人标签分页查询(含本地关联人数) -->
+    <select id="queryPageListWithRelationCount" resultType="org.jeecg.modules.zjrs.tag.vo.PersonalTagVo">
+        SELECT
+            pt.ID,
+            pt.PROVINCE_STANDARD_CODE,
+            pt.TAG_CODE,
+            pt.TAG_NAME,
+            pt.FIRST_CATEGORY,
+            pt.SECOND_CATEGORY,
+            pt.TAG_DESCRIPTION,
+            pt.TAG_STATUS,
+            pt.DATA_SOURCE,
+            pt.CREATE_BY,
+            pt.CREATE_TIME,
+            pt.UPDATE_BY,
+            pt.UPDATE_TIME,
+            pt.SYS_ORG_CODE,
+            COALESCE(ptr_cnt.relation_count, 0) AS relationCount
+        FROM PERSONAL_TAG pt
+        LEFT JOIN (
+            SELECT TAG_ID, COUNT(DISTINCT PERSONAL_ID) AS relation_count
+            FROM PERSONAL_TAG_RELATION
+            GROUP BY TAG_ID
+        ) ptr_cnt ON pt.ID = ptr_cnt.TAG_ID
+        ${ew.customSqlSegment}
+    </select>
+
+    <!-- 个人标签全量查询(含本地关联人数,用于导出) -->
+    <select id="queryListWithRelationCount" resultType="org.jeecg.modules.zjrs.tag.vo.PersonalTagVo">
+        SELECT
+            pt.ID,
+            pt.PROVINCE_STANDARD_CODE,
+            pt.TAG_CODE,
+            pt.TAG_NAME,
+            pt.FIRST_CATEGORY,
+            pt.SECOND_CATEGORY,
+            pt.TAG_DESCRIPTION,
+            pt.TAG_STATUS,
+            pt.DATA_SOURCE,
+            pt.CREATE_BY,
+            pt.CREATE_TIME,
+            pt.UPDATE_BY,
+            pt.UPDATE_TIME,
+            pt.SYS_ORG_CODE,
+            COALESCE(ptr_cnt.relation_count, 0) AS relationCount
+        FROM PERSONAL_TAG pt
+        LEFT JOIN (
+            SELECT TAG_ID, COUNT(DISTINCT PERSONAL_ID) AS relation_count
+            FROM PERSONAL_TAG_RELATION
+            GROUP BY TAG_ID
+        ) ptr_cnt ON pt.ID = ptr_cnt.TAG_ID
+        ${ew.customSqlSegment}
+    </select>
+
 </mapper>

+ 5 - 0
jeecg-boot/jeecg-boot-module/jeecg-module-zjrs/src/main/java/org/jeecg/modules/zjrs/tag/mapper/xml/PersonalTagRelationMapper.xml

@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="org.jeecg.modules.zjrs.tag.mapper.PersonalTagRelationMapper">
+
+</mapper>

+ 20 - 0
jeecg-boot/jeecg-boot-module/jeecg-module-zjrs/src/main/java/org/jeecg/modules/zjrs/tag/service/IEnterpriseTagService.java

@@ -1,7 +1,12 @@
 package org.jeecg.modules.zjrs.tag.service;
 
+import com.baomidou.mybatisplus.core.conditions.Wrapper;
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
 import com.baomidou.mybatisplus.extension.service.IService;
+import jakarta.servlet.http.HttpServletRequest;
 import org.jeecg.modules.zjrs.tag.entity.EnterpriseTag;
+import org.jeecg.modules.zjrs.tag.vo.EnterpriseTagVo;
 
 import java.util.List;
 
@@ -13,6 +18,11 @@ import java.util.List;
  */
 public interface IEnterpriseTagService extends IService<EnterpriseTag> {
 
+    /**
+     * 生成企业标签编码(ZJQY + 三位流水)
+     */
+    String generateTagCode();
+
     void batchUpdateStatus(List<String> ids, String tagStatus);
 
     /**
@@ -22,4 +32,14 @@ public interface IEnterpriseTagService extends IService<EnterpriseTag> {
      * @param tagName 标签名称
      */
     void checkTagNameUnique(String id, String tagName);
+
+    /**
+     * 分页查询企业标签(含本地关联企业数)
+     */
+    IPage<EnterpriseTagVo> queryPageListWithRelationCount(Page<EnterpriseTagVo> page, Wrapper<EnterpriseTag> queryWrapper, HttpServletRequest req);
+
+    /**
+     * 全量查询企业标签(含本地关联企业数,用于导出)
+     */
+    List<EnterpriseTagVo> queryListWithRelationCount(Wrapper<EnterpriseTag> queryWrapper, HttpServletRequest req);
 }

+ 20 - 0
jeecg-boot/jeecg-boot-module/jeecg-module-zjrs/src/main/java/org/jeecg/modules/zjrs/tag/service/IPersonalTagService.java

@@ -1,7 +1,12 @@
 package org.jeecg.modules.zjrs.tag.service;
 
+import com.baomidou.mybatisplus.core.conditions.Wrapper;
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
 import com.baomidou.mybatisplus.extension.service.IService;
+import jakarta.servlet.http.HttpServletRequest;
 import org.jeecg.modules.zjrs.tag.entity.PersonalTag;
+import org.jeecg.modules.zjrs.tag.vo.PersonalTagVo;
 
 import java.util.List;
 
@@ -13,6 +18,11 @@ import java.util.List;
  */
 public interface IPersonalTagService extends IService<PersonalTag> {
 
+    /**
+     * 生成个人标签编码(ZJGR + 三位流水)
+     */
+    String generateTagCode();
+
     void batchUpdateStatus(List<String> ids, String tagStatus);
 
     /**
@@ -22,4 +32,14 @@ public interface IPersonalTagService extends IService<PersonalTag> {
      * @param tagName 标签名称
      */
     void checkTagNameUnique(String id, String tagName);
+
+    /**
+     * 分页查询个人标签(含本地关联人数)
+     */
+    IPage<PersonalTagVo> queryPageListWithRelationCount(Page<PersonalTagVo> page, Wrapper<PersonalTag> queryWrapper, HttpServletRequest req);
+
+    /**
+     * 全量查询个人标签(含本地关联人数,用于导出)
+     */
+    List<PersonalTagVo> queryListWithRelationCount(Wrapper<PersonalTag> queryWrapper, HttpServletRequest req);
 }

+ 38 - 0
jeecg-boot/jeecg-boot-module/jeecg-module-zjrs/src/main/java/org/jeecg/modules/zjrs/tag/service/impl/EnterpriseTagServiceImpl.java

@@ -1,16 +1,23 @@
 package org.jeecg.modules.zjrs.tag.service.impl;
 
+import com.baomidou.mybatisplus.core.conditions.Wrapper;
 import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
 import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
 import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import jakarta.servlet.http.HttpServletRequest;
 import org.jeecg.common.exception.JeecgBootBizTipException;
 import org.jeecg.common.util.AssertUtils;
+import org.jeecg.common.util.oConvertUtils;
 import org.jeecg.modules.zjrs.tag.entity.EnterpriseTag;
 import org.jeecg.modules.zjrs.tag.mapper.EnterpriseTagMapper;
 import org.jeecg.modules.zjrs.tag.service.IEnterpriseTagService;
+import org.jeecg.modules.zjrs.tag.vo.EnterpriseTagVo;
 import org.springframework.stereotype.Service;
 import org.springframework.transaction.annotation.Transactional;
 
+import java.util.Arrays;
 import java.util.List;
 
 /**
@@ -22,6 +29,21 @@ import java.util.List;
 @Service
 public class EnterpriseTagServiceImpl extends ServiceImpl<EnterpriseTagMapper, EnterpriseTag> implements IEnterpriseTagService {
 
+    @Override
+    public String generateTagCode() {
+        LambdaQueryWrapper<EnterpriseTag> queryWrapper = new LambdaQueryWrapper<>();
+        queryWrapper.likeRight(EnterpriseTag::getTagCode, "ZJQY");
+        queryWrapper.orderByDesc(EnterpriseTag::getTagCode);
+        queryWrapper.last("LIMIT 1");
+        EnterpriseTag last = this.getOne(queryWrapper);
+        int nextNum = 1;
+        if (last != null && last.getTagCode() != null) {
+            String code = last.getTagCode();
+            nextNum = Integer.parseInt(code.substring(4)) + 1;
+        }
+        return String.format("ZJQY%03d", nextNum);
+    }
+
     @Override
     @Transactional(rollbackFor = Exception.class)
     public void batchUpdateStatus(List<String> ids, String tagStatus) {
@@ -47,4 +69,20 @@ public class EnterpriseTagServiceImpl extends ServiceImpl<EnterpriseTagMapper, E
             throw new JeecgBootBizTipException("标签名称已存在");
         }
     }
+
+    @Override
+    public IPage<EnterpriseTagVo> queryPageListWithRelationCount(Page<EnterpriseTagVo> page, Wrapper<EnterpriseTag> queryWrapper, HttpServletRequest req) {
+        return baseMapper.queryPageListWithRelationCount(page, queryWrapper);
+    }
+
+    @Override
+    public List<EnterpriseTagVo> queryListWithRelationCount(Wrapper<EnterpriseTag> queryWrapper, HttpServletRequest req) {
+        // 处理导出选中记录筛选
+        String selections = req.getParameter("selections");
+        if (oConvertUtils.isNotEmpty(selections)) {
+            List<String> idList = Arrays.asList(selections.split(","));
+            queryWrapper = ((LambdaQueryWrapper<EnterpriseTag>) queryWrapper).in(EnterpriseTag::getId, idList);
+        }
+        return baseMapper.queryListWithRelationCount(queryWrapper);
+    }
 }

+ 38 - 0
jeecg-boot/jeecg-boot-module/jeecg-module-zjrs/src/main/java/org/jeecg/modules/zjrs/tag/service/impl/PersonalTagServiceImpl.java

@@ -1,16 +1,23 @@
 package org.jeecg.modules.zjrs.tag.service.impl;
 
+import com.baomidou.mybatisplus.core.conditions.Wrapper;
 import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
 import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
 import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import jakarta.servlet.http.HttpServletRequest;
 import org.jeecg.common.exception.JeecgBootBizTipException;
 import org.jeecg.common.util.AssertUtils;
+import org.jeecg.common.util.oConvertUtils;
 import org.jeecg.modules.zjrs.tag.entity.PersonalTag;
 import org.jeecg.modules.zjrs.tag.mapper.PersonalTagMapper;
 import org.jeecg.modules.zjrs.tag.service.IPersonalTagService;
+import org.jeecg.modules.zjrs.tag.vo.PersonalTagVo;
 import org.springframework.stereotype.Service;
 import org.springframework.transaction.annotation.Transactional;
 
+import java.util.Arrays;
 import java.util.List;
 
 /**
@@ -22,6 +29,21 @@ import java.util.List;
 @Service
 public class PersonalTagServiceImpl extends ServiceImpl<PersonalTagMapper, PersonalTag> implements IPersonalTagService {
 
+    @Override
+    public String generateTagCode() {
+        LambdaQueryWrapper<PersonalTag> queryWrapper = new LambdaQueryWrapper<>();
+        queryWrapper.likeRight(PersonalTag::getTagCode, "ZJGR");
+        queryWrapper.orderByDesc(PersonalTag::getTagCode);
+        queryWrapper.last("LIMIT 1");
+        PersonalTag last = this.getOne(queryWrapper);
+        int nextNum = 1;
+        if (last != null && last.getTagCode() != null) {
+            String code = last.getTagCode();
+            nextNum = Integer.parseInt(code.substring(4)) + 1;
+        }
+        return String.format("ZJGR%03d", nextNum);
+    }
+
     @Override
     @Transactional(rollbackFor = Exception.class)
     public void batchUpdateStatus(List<String> ids, String tagStatus) {
@@ -47,4 +69,20 @@ public class PersonalTagServiceImpl extends ServiceImpl<PersonalTagMapper, Perso
             throw new JeecgBootBizTipException("标签名称已存在");
         }
     }
+
+    @Override
+    public IPage<PersonalTagVo> queryPageListWithRelationCount(Page<PersonalTagVo> page, Wrapper<PersonalTag> queryWrapper, HttpServletRequest req) {
+        return baseMapper.queryPageListWithRelationCount(page, queryWrapper);
+    }
+
+    @Override
+    public List<PersonalTagVo> queryListWithRelationCount(Wrapper<PersonalTag> queryWrapper, HttpServletRequest req) {
+        // 处理导出选中记录筛选
+        String selections = req.getParameter("selections");
+        if (oConvertUtils.isNotEmpty(selections)) {
+            List<String> idList = Arrays.asList(selections.split(","));
+            queryWrapper = ((LambdaQueryWrapper<PersonalTag>) queryWrapper).in(PersonalTag::getId, idList);
+        }
+        return baseMapper.queryListWithRelationCount(queryWrapper);
+    }
 }

+ 113 - 0
jeecg-boot/jeecg-boot-module/jeecg-module-zjrs/src/main/java/org/jeecg/modules/zjrs/tag/vo/EnterpriseTagVo.java

@@ -0,0 +1,113 @@
+package org.jeecg.modules.zjrs.tag.vo;
+
+import com.fasterxml.jackson.annotation.JsonFormat;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import lombok.experimental.Accessors;
+import org.jeecgframework.poi.excel.annotation.Excel;
+import org.springframework.format.annotation.DateTimeFormat;
+
+import java.io.Serializable;
+
+/**
+ * @Description: 企业标签信息VO(含本地关联人数)
+ * @Author: jeecg-boot
+ * @Date: 2026-06-04
+ * @Version: V1.0
+ */
+@Data
+@Accessors(chain = true)
+@EqualsAndHashCode(callSuper = false)
+@Schema(description = "企业标签信息VO")
+public class EnterpriseTagVo implements Serializable {
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * 主键ID
+     */
+    @Schema(description = "主键ID")
+    private java.lang.String id;
+    /**
+     * 省标编码
+     */
+    @Excel(name = "省标编码", width = 15)
+    @Schema(description = "省标编码")
+    private java.lang.String provinceStandardCode;
+    /**
+     * 标签编码
+     */
+    @Schema(description = "标签编码")
+    private java.lang.String tagCode;
+    /**
+     * 标签名称
+     */
+    @Excel(name = "标签名称", width = 15)
+    @Schema(description = "标签名称")
+    private java.lang.String tagName;
+    /**
+     * 所属一级分类
+     */
+    @Excel(name = "所属一级分类", width = 15)
+    @Schema(description = "所属一级分类")
+    private java.lang.String firstCategory;
+    /**
+     * 所属二级分类
+     */
+    @Excel(name = "所属二级分类", width = 15)
+    @Schema(description = "所属二级分类")
+    private java.lang.String secondCategory;
+    /**
+     * 标签说明
+     */
+    @Excel(name = "标签说明", width = 15)
+    @Schema(description = "标签说明")
+    private java.lang.String tagDescription;
+    /**
+     * 启用状态
+     */
+    @Excel(name = "启用状态", width = 15)
+    @Schema(description = "启用状态")
+    private java.lang.String tagStatus;
+    /**
+     * 本地关联企业数(关联企业数)
+     */
+    @Excel(name = "本地关联企业数", width = 15)
+    @Schema(description = "本地关联企业数")
+    private java.lang.Long relationCount;
+    /**
+     * 数据来源
+     */
+    @Excel(name = "数据来源", width = 15)
+    @Schema(description = "数据来源")
+    private java.lang.String dataSource;
+    /**
+     * 创建人
+     */
+    @Schema(description = "创建人")
+    private java.lang.String createBy;
+    /**
+     * 创建时间
+     */
+    @JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm:ss")
+    @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    @Schema(description = "创建时间")
+    private java.util.Date createTime;
+    /**
+     * 修改人
+     */
+    @Schema(description = "修改人")
+    private java.lang.String updateBy;
+    /**
+     * 更新时间
+     */
+    @JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm:ss")
+    @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    @Schema(description = "更新时间")
+    private java.util.Date updateTime;
+    /**
+     * 组织机构编号
+     */
+    @Schema(description = "组织机构编号")
+    private java.lang.String sysOrgCode;
+}

+ 113 - 0
jeecg-boot/jeecg-boot-module/jeecg-module-zjrs/src/main/java/org/jeecg/modules/zjrs/tag/vo/PersonalTagVo.java

@@ -0,0 +1,113 @@
+package org.jeecg.modules.zjrs.tag.vo;
+
+import com.fasterxml.jackson.annotation.JsonFormat;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import lombok.experimental.Accessors;
+import org.jeecgframework.poi.excel.annotation.Excel;
+import org.springframework.format.annotation.DateTimeFormat;
+
+import java.io.Serializable;
+
+/**
+ * @Description: 个人标签信息VO(含本地关联人数)
+ * @Author: jeecg-boot
+ * @Date: 2026-06-04
+ * @Version: V1.0
+ */
+@Data
+@Accessors(chain = true)
+@EqualsAndHashCode(callSuper = false)
+@Schema(description = "个人标签信息VO")
+public class PersonalTagVo implements Serializable {
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * 主键ID
+     */
+    @Schema(description = "主键ID")
+    private java.lang.String id;
+    /**
+     * 省标编码
+     */
+    @Excel(name = "省标编码", width = 15)
+    @Schema(description = "省标编码")
+    private java.lang.String provinceStandardCode;
+    /**
+     * 标签编码
+     */
+    @Schema(description = "标签编码")
+    private java.lang.String tagCode;
+    /**
+     * 标签名称
+     */
+    @Excel(name = "标签名称", width = 15)
+    @Schema(description = "标签名称")
+    private java.lang.String tagName;
+    /**
+     * 所属一级分类
+     */
+    @Excel(name = "所属一级分类", width = 15)
+    @Schema(description = "所属一级分类")
+    private java.lang.String firstCategory;
+    /**
+     * 所属二级分类
+     */
+    @Excel(name = "所属二级分类", width = 15)
+    @Schema(description = "所属二级分类")
+    private java.lang.String secondCategory;
+    /**
+     * 标签说明
+     */
+    @Excel(name = "标签说明", width = 15)
+    @Schema(description = "标签说明")
+    private java.lang.String tagDescription;
+    /**
+     * 启用状态
+     */
+    @Excel(name = "启用状态", width = 15)
+    @Schema(description = "启用状态")
+    private java.lang.String tagStatus;
+    /**
+     * 本地关联人数
+     */
+    @Excel(name = "本地关联人数", width = 15)
+    @Schema(description = "本地关联人数")
+    private java.lang.Long relationCount;
+    /**
+     * 数据来源
+     */
+    @Excel(name = "数据来源", width = 15)
+    @Schema(description = "数据来源")
+    private java.lang.String dataSource;
+    /**
+     * 创建人
+     */
+    @Schema(description = "创建人")
+    private java.lang.String createBy;
+    /**
+     * 创建时间
+     */
+    @JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm:ss")
+    @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    @Schema(description = "创建时间")
+    private java.util.Date createTime;
+    /**
+     * 修改人
+     */
+    @Schema(description = "修改人")
+    private java.lang.String updateBy;
+    /**
+     * 更新时间
+     */
+    @JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm:ss")
+    @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    @Schema(description = "更新时间")
+    private java.util.Date updateTime;
+    /**
+     * 组织机构编号
+     */
+    @Schema(description = "组织机构编号")
+    private java.lang.String sysOrgCode;
+}

+ 8 - 0
jeecgboot-vue3/src/views/recruitment/enterpriseTag/EnterpriseTag.api.ts

@@ -12,6 +12,7 @@ enum Api {
   importExcel = '/tag/enterpriseTag/importExcel',
   exportXls = '/tag/enterpriseTag/exportXls',
   batchUpdateStatus = '/tag/enterpriseTag/batchUpdateStatus',
+  generateTagCode = '/tag/enterpriseTag/generateTagCode',
 }
 
 /**
@@ -72,6 +73,13 @@ export const saveOrUpdate = (params, isUpdate) => {
   return defHttp.post({ url: url, params }, { isTransformResponse: false });
 };
 
+/**
+ * 生成标签编码
+ */
+export const generateTagCode = () => {
+  return defHttp.get({ url: Api.generateTagCode }, { isTransformResponse: false });
+};
+
 /**
  * 批量修改启用状态
  * @param ids

+ 10 - 14
jeecgboot-vue3/src/views/recruitment/enterpriseTag/EnterpriseTag.data.ts

@@ -9,15 +9,10 @@ export const columns: BasicColumn[] = [
     customRender: ({ index }) => index + 1,
   },
   {
-    title: '省标码',
+    title: '省标码',
     align: 'center',
     dataIndex: 'provinceStandardCode',
   },
-  {
-    title: '标签编码',
-    align: 'center',
-    dataIndex: 'tagCode',
-  },
   {
     title: '标签名称',
     align: 'center',
@@ -39,32 +34,33 @@ export const columns: BasicColumn[] = [
     dataIndex: 'tagDescription',
   },
   {
-    title: '标签启用状态',
+    title: '启用状态',
     align: 'center',
     dataIndex: 'tagStatus',
     customRender: ({ text }) => {
       return text === '1' ? '启用' : text === '0' ? '停用' : text;
     },
   },
+  {
+    title: '本地关联企业数',
+    align: 'center',
+    dataIndex: 'relationCount',
+    customRender: ({ text }) => text ?? 0,
+  },
   {
     title: '数据来源',
     align: 'center',
     dataIndex: 'dataSource',
-    customRender: ({ text }) => {
-      if (text === '1') return '省一体化平台回流';
-      if (text === '2') return '本地自定义';
-      return text;
-    },
   },
 ];
 
 export const superQuerySchema = {
-  provinceStandardCode: { title: '省标码', order: 0, view: 'text', type: 'string' },
+  provinceStandardCode: { title: '省标编码', order: 0, view: 'text', type: 'string' },
   tagCode: { title: '标签编码', order: 1, view: 'text', type: 'string' },
   tagName: { title: '标签名称', order: 2, view: 'text', type: 'string' },
   firstCategory: { title: '所属一级分类', order: 3, view: 'text', type: 'string' },
   secondCategory: { title: '所属二级分类', order: 4, view: 'text', type: 'string' },
   tagDescription: { title: '标签说明', order: 5, view: 'text', type: 'string' },
-  tagStatus: { title: '标签启用状态', order: 6, view: 'text', type: 'string' },
+  tagStatus: { title: '启用状态', order: 6, view: 'text', type: 'string' },
   dataSource: { title: '数据来源', order: 7, view: 'text', type: 'string' },
 };

+ 46 - 16
jeecgboot-vue3/src/views/recruitment/enterpriseTag/EnterpriseTagList.vue

@@ -4,28 +4,36 @@
     <div class="jeecg-basic-table-form-container">
       <a-form ref="formRef" :label-col="labelCol" :model="queryParam" :wrapper-col="wrapperCol" @keyup.enter.native="searchQuery">
         <a-row :gutter="24">
-          <a-col :lg="6">
+          <a-col :lg="8">
             <a-form-item label="标签名称" name="tagName">
               <a-input v-model:value="queryParam.tagName" allow-clear placeholder="请输入标签名称"></a-input>
             </a-form-item>
           </a-col>
-          <a-col :lg="6">
-            <a-form-item label="标签状态" name="tagStatus">
-              <a-select v-model:value="queryParam.tagStatus" allow-clear placeholder="请选择标签状态">
+          <a-col :lg="8">
+            <a-form-item label="所属一级分类" name="firstCategory">
+              <a-input v-model:value="queryParam.firstCategory" allow-clear placeholder="请输入所属一级分类"></a-input>
+            </a-form-item>
+          </a-col>
+          <a-col :lg="8">
+            <a-form-item label="所属二级分类" name="secondCategory">
+              <a-input v-model:value="queryParam.secondCategory" allow-clear placeholder="请输入所属二级分类"></a-input>
+            </a-form-item>
+          </a-col>
+          <a-col :lg="8">
+            <a-form-item label="启用状态" name="tagStatus">
+              <a-select v-model:value="queryParam.tagStatus" allow-clear placeholder="请选择启用状态">
                 <a-select-option value="1">启用</a-select-option>
                 <a-select-option value="0">停用</a-select-option>
               </a-select>
             </a-form-item>
           </a-col>
-          <a-col :lg="6">
+          <a-col :lg="8">
             <a-form-item label="数据来源" name="dataSource">
-              <a-select v-model:value="queryParam.dataSource" allow-clear placeholder="请选择数据来源">
-                <a-select-option value="1">省一体化平台回流</a-select-option>
-                <a-select-option value="2">本地自定义</a-select-option>
-              </a-select>
+              <a-select v-model:value="queryParam.dataSource" placeholder="请选择数据来源"
+                        allow-clear :options="dataSourceOptions" />
             </a-form-item>
           </a-col>
-          <a-col :lg="6">
+          <a-col :lg="8">
             <a-button preIcon="ant-design:search-outlined" type="primary" @click="searchQuery">查询</a-button>
             <a-button preIcon="ant-design:reload-outlined" style="margin-left: 8px" type="primary" @click="searchReset">重置</a-button>
           </a-col>
@@ -41,7 +49,7 @@
         <j-upload-button v-auth="'tag:enterprise_tag:importExcel'" preIcon="ant-design:import-outlined" type="primary" @click="onImportXls"
           >导入</j-upload-button
         >
-        <a-dropdown v-if="selectedRowKeys.length > 0">
+        <a-dropdown :disabled="selectedRowKeys.length === 0">
           <template #overlay>
             <a-menu>
               <a-menu-item key="1" @click="batchHandleDelete">
@@ -64,13 +72,18 @@
           </a-button>
         </a-dropdown>
         <!-- 高级查询 -->
-        <super-query :config="superQueryConfig" @search="handleSuperQuery" />
+        <!--        <super-query :config="superQueryConfig" @search="handleSuperQuery" />-->
       </template>
       <!--操作栏-->
       <template #action="{ record }">
         <TableAction :actions="getTableAction(record)" :dropDownActions="getDropDownAction(record)" />
       </template>
-      <template v-slot:bodyCell="{ column, record, index, text }"> </template>
+      <template v-slot:bodyCell="{ column, text }">
+        <template v-if="column.dataIndex === 'dataSource'">
+          {{ getDictText('DataSource', text) }}
+        </template>
+        <!-- 非字典字段不做处理 -->
+      </template>
     </BasicTable>
     <!-- 表单区域 -->
     <EnterpriseTagModal ref="registerModal" @success="handleSuccess"></EnterpriseTagModal>
@@ -78,13 +91,17 @@
 </template>
 
 <script lang="ts" name="recruitment-enterpriseTag" setup>
-  import { reactive, ref } from 'vue';
+  import { computed, reactive, ref } from 'vue';
   import { BasicTable, TableAction } from '/@/components/Table';
   import { useListPage } from '/@/hooks/system/useListPage';
   import { columns, superQuerySchema } from './EnterpriseTag.data';
   import { batchDelete, batchUpdateStatus, deleteOne, getExportUrl, getImportUrl, list } from './EnterpriseTag.api';
   import EnterpriseTagModal from './components/EnterpriseTagModal.vue';
   import { useMessage } from '/@/hooks/web/useMessage';
+  import { useDict } from '/@/hooks/dictionary/useDict';
+
+  const { getDictText, getDictOptions } = useDict(['DataSource']);
+  const dataSourceOptions = computed(() => getDictOptions('DataSource'));
 
   const { createMessage } = useMessage();
   const formRef = ref();
@@ -174,10 +191,21 @@
   }
 
   /**
-   * 批量删除事件
+   * 批量删除事件(自动过滤省系统数据)
    */
   async function batchHandleDelete() {
-    await batchDelete({ ids: selectedRowKeys.value }, handleSuccess);
+    const allData = getDataSource();
+    const systemIds = allData.filter(r => selectedRowKeys.value.includes(r.id) && r.dataSource === '1').map(r => r.id);
+    const manualIds = selectedRowKeys.value.filter(id => !systemIds.includes(id));
+    if (manualIds.length === 0) {
+      createMessage.warning('选中的省系统标签不可删除');
+      return;
+    }
+    if (systemIds.length > 0) {
+      createMessage.warning(`省系统标签不可删除,已自动过滤 ${systemIds.length} 条`);
+      selectedRowKeys.value = manualIds;
+    }
+    await batchDelete({ ids: manualIds }, handleSuccess);
   }
 
   /**
@@ -206,6 +234,7 @@
         label: '编辑',
         onClick: handleEdit.bind(null, record),
         auth: 'tag:enterprise_tag:edit',
+        disabled: record.dataSource === '1',
       },
     ];
   }
@@ -227,6 +256,7 @@
           placement: 'topLeft',
         },
         auth: 'tag:enterprise_tag:delete',
+        disabled: record.dataSource === '1',
       },
     ];
   }

+ 18 - 13
jeecgboot-vue3/src/views/recruitment/enterpriseTag/components/EnterpriseTagForm.vue

@@ -5,18 +5,17 @@
         <a-form ref="formRef" :labelCol="labelCol" :wrapperCol="wrapperCol" class="antd-modal-form" name="EnterpriseTagForm">
           <a-row>
             <a-col :span="12">
-              <a-form-item
-                id="EnterpriseTagForm-provinceStandardCode"
-                label="省标标码"
+              <a-form-item id="EnterpriseTagForm-provinceStandardCode"
+                label="省标编码"
                 name="provinceStandardCode"
                 v-bind="validateInfos.provinceStandardCode"
               >
-                <a-input v-model:value="formData.provinceStandardCode" :maxlength="50" allow-clear placeholder="请输入省标标码"></a-input>
+                <a-input v-model:value="formData.provinceStandardCode" :maxlength="50" disabled placeholder="请输入省标编码"></a-input>
               </a-form-item>
             </a-col>
             <a-col :span="12">
               <a-form-item id="EnterpriseTagForm-tagCode" label="标签编码" name="tagCode" v-bind="validateInfos.tagCode">
-                <a-input v-model:value="formData.tagCode" :maxlength="50" allow-clear placeholder="请输入标签编码"></a-input>
+                <a-input v-model:value="formData.tagCode" disabled placeholder="请输入标签编码"></a-input>
               </a-form-item>
             </a-col>
             <a-col :span="12">
@@ -44,10 +43,8 @@
             </a-col>
             <a-col :span="12">
               <a-form-item id="EnterpriseTagForm-dataSource" label="数据来源" name="dataSource" v-bind="validateInfos.dataSource">
-                <a-select v-model:value="formData.dataSource" allow-clear placeholder="请选择数据来源">
-                  <a-select-option value="1">省一体化平台回流</a-select-option>
-                  <a-select-option value="2">本地自定义</a-select-option>
-                </a-select>
+                <a-select v-model:value="formData.dataSource" placeholder="请选择数据来源"
+                          disabled allow-clear :options="dataSourceOptions" />
               </a-form-item>
             </a-col>
             <a-col :span="24">
@@ -74,6 +71,8 @@
   import { useMessage } from '/@/hooks/web/useMessage';
   import { getDateByPicker, getValueType } from '/@/utils';
   import { saveOrUpdate } from '../EnterpriseTag.api';
+  import { generateTagCode } from '../EnterpriseTag.api';
+  import { useDict } from '/@/hooks/dictionary/useDict';
   import { Form } from 'ant-design-vue';
   import JFormContainer from '/@/components/Form/src/container/JFormContainer.vue';
 
@@ -93,9 +92,12 @@
     firstCategory: '',
     secondCategory: '',
     tagDescription: '',
-    tagStatus: '',
-    dataSource: '',
+    tagStatus: '1',
+    dataSource: '2',
   });
+  const { getDictOptions } = useDict(['DataSource']);
+  const dataSourceOptions = computed(() => getDictOptions('DataSource'));
+
   const { createMessage } = useMessage();
   const labelCol = ref<any>({ xs: { span: 24 }, sm: { span: 6 } });
   const wrapperCol = ref<any>({ xs: { span: 24 }, sm: { span: 16 } });
@@ -123,8 +125,11 @@
   /**
    * 新增
    */
-  function add() {
-    edit({});
+  async function add() {
+    resetFields();
+    // 新增时自动生成标签编码
+    const res = await generateTagCode();
+    formData.tagCode = res.result;
   }
 
   /**

+ 8 - 0
jeecgboot-vue3/src/views/recruitment/personalTag/PersonalTag.api.ts

@@ -12,6 +12,7 @@ enum Api {
   importExcel = '/tag/personalTag/importExcel',
   exportXls = '/tag/personalTag/exportXls',
   batchUpdateStatus = '/tag/personalTag/batchUpdateStatus',
+  generateTagCode = '/tag/personalTag/generateTagCode',
 }
 
 /**
@@ -72,6 +73,13 @@ export const saveOrUpdate = (params, isUpdate) => {
   return defHttp.post({ url: url, params }, { isTransformResponse: false });
 };
 
+/**
+ * 生成标签编码
+ */
+export const generateTagCode = () => {
+  return defHttp.get({ url: Api.generateTagCode }, { isTransformResponse: false });
+};
+
 /**
  * 批量修改启用状态
  * @param ids

+ 10 - 14
jeecgboot-vue3/src/views/recruitment/personalTag/PersonalTag.data.ts

@@ -9,15 +9,10 @@ export const columns: BasicColumn[] = [
     customRender: ({ index }) => index + 1,
   },
   {
-    title: '省标码',
+    title: '省标码',
     align: 'center',
     dataIndex: 'provinceStandardCode',
   },
-  {
-    title: '标签编码',
-    align: 'center',
-    dataIndex: 'tagCode',
-  },
   {
     title: '标签名称',
     align: 'center',
@@ -39,32 +34,33 @@ export const columns: BasicColumn[] = [
     dataIndex: 'tagDescription',
   },
   {
-    title: '标签启用状态',
+    title: '启用状态',
     align: 'center',
     dataIndex: 'tagStatus',
     customRender: ({ text }) => {
       return text === '1' ? '启用' : text === '0' ? '停用' : text;
     },
   },
+  {
+    title: '本地关联人数',
+    align: 'center',
+    dataIndex: 'relationCount',
+    customRender: ({ text }) => text ?? 0,
+  },
   {
     title: '数据来源',
     align: 'center',
     dataIndex: 'dataSource',
-    customRender: ({ text }) => {
-      if (text === '1') return '省一体化平台回流';
-      if (text === '2') return '本地自定义';
-      return text;
-    },
   },
 ];
 
 export const superQuerySchema = {
-  provinceStandardCode: { title: '省标码', order: 0, view: 'text', type: 'string' },
+  provinceStandardCode: { title: '省标编码', order: 0, view: 'text', type: 'string' },
   tagCode: { title: '标签编码', order: 1, view: 'text', type: 'string' },
   tagName: { title: '标签名称', order: 2, view: 'text', type: 'string' },
   firstCategory: { title: '所属一级分类', order: 3, view: 'text', type: 'string' },
   secondCategory: { title: '所属二级分类', order: 4, view: 'text', type: 'string' },
   tagDescription: { title: '标签说明', order: 5, view: 'text', type: 'string' },
-  tagStatus: { title: '标签启用状态', order: 6, view: 'text', type: 'string' },
+  tagStatus: { title: '启用状态', order: 6, view: 'text', type: 'string' },
   dataSource: { title: '数据来源', order: 7, view: 'text', type: 'string' },
 };

+ 46 - 16
jeecgboot-vue3/src/views/recruitment/personalTag/PersonalTagList.vue

@@ -4,28 +4,36 @@
     <div class="jeecg-basic-table-form-container">
       <a-form ref="formRef" :label-col="labelCol" :model="queryParam" :wrapper-col="wrapperCol" @keyup.enter.native="searchQuery">
         <a-row :gutter="24">
-          <a-col :lg="6">
+          <a-col :lg="8">
             <a-form-item label="标签名称" name="tagName">
               <a-input v-model:value="queryParam.tagName" allow-clear placeholder="请输入标签名称"></a-input>
             </a-form-item>
           </a-col>
-          <a-col :lg="6">
-            <a-form-item label="标签状态" name="tagStatus">
-              <a-select v-model:value="queryParam.tagStatus" allow-clear placeholder="请选择标签状态">
+          <a-col :lg="8">
+            <a-form-item label="所属一级分类" name="firstCategory">
+              <a-input v-model:value="queryParam.firstCategory" allow-clear placeholder="请输入所属一级分类"></a-input>
+            </a-form-item>
+          </a-col>
+          <a-col :lg="8">
+            <a-form-item label="所属二级分类" name="secondCategory">
+              <a-input v-model:value="queryParam.secondCategory" allow-clear placeholder="请输入所属二级分类"></a-input>
+            </a-form-item>
+          </a-col>
+          <a-col :lg="8">
+            <a-form-item label="启用状态" name="tagStatus">
+              <a-select v-model:value="queryParam.tagStatus" allow-clear placeholder="请选择启用状态">
                 <a-select-option value="1">启用</a-select-option>
                 <a-select-option value="0">停用</a-select-option>
               </a-select>
             </a-form-item>
           </a-col>
-          <a-col :lg="6">
+          <a-col :lg="8">
             <a-form-item label="数据来源" name="dataSource">
-              <a-select v-model:value="queryParam.dataSource" allow-clear placeholder="请选择数据来源">
-                <a-select-option value="1">省一体化平台回流</a-select-option>
-                <a-select-option value="2">本地自定义</a-select-option>
-              </a-select>
+              <a-select v-model:value="queryParam.dataSource" placeholder="请选择数据来源"
+                        allow-clear :options="dataSourceOptions" />
             </a-form-item>
           </a-col>
-          <a-col :lg="6">
+          <a-col :lg="8">
             <a-button preIcon="ant-design:search-outlined" type="primary" @click="searchQuery">查询</a-button>
             <a-button preIcon="ant-design:reload-outlined" style="margin-left: 8px" type="primary" @click="searchReset">重置</a-button>
           </a-col>
@@ -41,7 +49,7 @@
         <j-upload-button v-auth="'tag:personal_tag:importExcel'" preIcon="ant-design:import-outlined" type="primary" @click="onImportXls"
           >导入</j-upload-button
         >
-        <a-dropdown v-if="selectedRowKeys.length > 0">
+        <a-dropdown :disabled="selectedRowKeys.length === 0">
           <template #overlay>
             <a-menu>
               <a-menu-item key="1" @click="batchHandleDelete">
@@ -64,13 +72,18 @@
           </a-button>
         </a-dropdown>
         <!-- 高级查询 -->
-        <super-query :config="superQueryConfig" @search="handleSuperQuery" />
+<!--        <super-query :config="superQueryConfig" @search="handleSuperQuery" />-->
       </template>
       <!--操作栏-->
       <template #action="{ record }">
         <TableAction :actions="getTableAction(record)" :dropDownActions="getDropDownAction(record)" />
       </template>
-      <template v-slot:bodyCell="{ column, record, index, text }"> </template>
+      <template v-slot:bodyCell="{ column, text }">
+        <template v-if="column.dataIndex === 'dataSource'">
+          {{ getDictText('DataSource', text) }}
+        </template>
+        <!-- 非字典字段不做处理 -->
+      </template>
     </BasicTable>
     <!-- 表单区域 -->
     <PersonalTagModal ref="registerModal" @success="handleSuccess"></PersonalTagModal>
@@ -78,13 +91,17 @@
 </template>
 
 <script lang="ts" name="recruitment-personalTag" setup>
-  import { reactive, ref } from 'vue';
+  import { reactive, ref, computed } from 'vue';
   import { BasicTable, TableAction } from '/@/components/Table';
   import { useListPage } from '/@/hooks/system/useListPage';
   import { columns, superQuerySchema } from './PersonalTag.data';
   import { batchDelete, batchUpdateStatus, deleteOne, getExportUrl, getImportUrl, list } from './PersonalTag.api';
   import PersonalTagModal from './components/PersonalTagModal.vue';
   import { useMessage } from '/@/hooks/web/useMessage';
+  import { useDict } from '/@/hooks/dictionary/useDict';
+
+  const { getDictText, getDictOptions } = useDict(['DataSource']);
+  const dataSourceOptions = computed(() => getDictOptions('DataSource'));
 
   const { createMessage } = useMessage();
   const formRef = ref();
@@ -174,10 +191,21 @@
   }
 
   /**
-   * 批量删除事件
+   * 批量删除事件(自动过滤省系统数据)
    */
   async function batchHandleDelete() {
-    await batchDelete({ ids: selectedRowKeys.value }, handleSuccess);
+    const allData = getDataSource();
+    const systemIds = allData.filter(r => selectedRowKeys.value.includes(r.id) && r.dataSource === '1').map(r => r.id);
+    const manualIds = selectedRowKeys.value.filter(id => !systemIds.includes(id));
+    if (manualIds.length === 0) {
+      createMessage.warning('选中的省系统标签不可删除');
+      return;
+    }
+    if (systemIds.length > 0) {
+      createMessage.warning(`省系统标签不可删除,已自动过滤 ${systemIds.length} 条`);
+      selectedRowKeys.value = manualIds;
+    }
+    await batchDelete({ ids: manualIds }, handleSuccess);
   }
 
   /**
@@ -206,6 +234,7 @@
         label: '编辑',
         onClick: handleEdit.bind(null, record),
         auth: 'tag:personal_tag:edit',
+        disabled: record.dataSource === '1',
       },
     ];
   }
@@ -227,6 +256,7 @@
           placement: 'topLeft',
         },
         auth: 'tag:personal_tag:delete',
+        disabled: record.dataSource === '1',
       },
     ];
   }

+ 18 - 13
jeecgboot-vue3/src/views/recruitment/personalTag/components/PersonalTagForm.vue

@@ -5,18 +5,17 @@
         <a-form ref="formRef" :labelCol="labelCol" :wrapperCol="wrapperCol" class="antd-modal-form" name="PersonalTagForm">
           <a-row>
             <a-col :span="12">
-              <a-form-item
-                id="PersonalTagForm-provinceStandardCode"
-                label="省标标码"
+              <a-form-item id="PersonalTagForm-provinceStandardCode"
+                label="省标编码"
                 name="provinceStandardCode"
                 v-bind="validateInfos.provinceStandardCode"
               >
-                <a-input v-model:value="formData.provinceStandardCode" :maxlength="50" allow-clear placeholder="请输入省标标码"></a-input>
+                <a-input v-model:value="formData.provinceStandardCode" :maxlength="50" disabled placeholder="请输入省标编码"></a-input>
               </a-form-item>
             </a-col>
             <a-col :span="12">
               <a-form-item id="PersonalTagForm-tagCode" label="标签编码" name="tagCode" v-bind="validateInfos.tagCode">
-                <a-input v-model:value="formData.tagCode" :maxlength="50" allow-clear placeholder="请输入标签编码"></a-input>
+                <a-input v-model:value="formData.tagCode" disabled placeholder="请输入标签编码"></a-input>
               </a-form-item>
             </a-col>
             <a-col :span="12">
@@ -44,10 +43,8 @@
             </a-col>
             <a-col :span="12">
               <a-form-item id="PersonalTagForm-dataSource" label="数据来源" name="dataSource" v-bind="validateInfos.dataSource">
-                <a-select v-model:value="formData.dataSource" allow-clear placeholder="请选择数据来源">
-                  <a-select-option value="1">省一体化平台回流</a-select-option>
-                  <a-select-option value="2">本地自定义</a-select-option>
-                </a-select>
+                <a-select v-model:value="formData.dataSource" placeholder="请选择数据来源"
+                          disabled allow-clear :options="dataSourceOptions" />
               </a-form-item>
             </a-col>
             <a-col :span="24">
@@ -74,6 +71,8 @@
   import { useMessage } from '/@/hooks/web/useMessage';
   import { getDateByPicker, getValueType } from '/@/utils';
   import { saveOrUpdate } from '../PersonalTag.api';
+  import { generateTagCode } from '../PersonalTag.api';
+  import { useDict } from '/@/hooks/dictionary/useDict';
   import { Form } from 'ant-design-vue';
   import JFormContainer from '/@/components/Form/src/container/JFormContainer.vue';
 
@@ -93,9 +92,12 @@
     firstCategory: '',
     secondCategory: '',
     tagDescription: '',
-    tagStatus: '',
-    dataSource: '',
+    tagStatus: '1',
+    dataSource: '2',
   });
+  const { getDictOptions } = useDict(['DataSource']);
+  const dataSourceOptions = computed(() => getDictOptions('DataSource'));
+
   const { createMessage } = useMessage();
   const labelCol = ref<any>({ xs: { span: 24 }, sm: { span: 6 } });
   const wrapperCol = ref<any>({ xs: { span: 24 }, sm: { span: 16 } });
@@ -123,8 +125,11 @@
   /**
    * 新增
    */
-  function add() {
-    edit({});
+  async function add() {
+    resetFields();
+    // 新增时自动生成标签编码
+    const res = await generateTagCode();
+    formData.tagCode = res.result;
   }
 
   /**