3 Revize c287298342 ... 92c7e03165

Autor SHA1 Zpráva Datum
  code4eat 92c7e03165 fixed bug před 4 dny
  code4eat e8cb8e77cf 修复定性选项管理界面表单bug před 2 roky
  code4eat 566711b4a9 修复可编辑表格删除bug před 2 roky
100 změnil soubory, kde provedl 21296 přidání a 2909 odebrání
  1. binární
      .DS_Store
  2. 2 7
      .lintstagedrc
  3. 55 21
      .umirc.ts
  4. 3 3
      .vscode/settings.json
  5. 0 3
      config/proxy.ts
  6. 448 0
      mock/checkGroup.js
  7. 375 0
      mock/checkItem.js
  8. 61 0
      mock/emp.js
  9. 45 50
      mock/home.ts
  10. 158 170
      mock/monthlyData.ts
  11. 164 181
      mock/monthlyInfo.ts
  12. 41 44
      mock/monthlySet.ts
  13. binární
      mock/关闭@2x.png
  14. 2 1
      package.json
  15. binární
      src/.DS_Store
  16. 26 38
      src/access.ts
  17. 187 134
      src/app.tsx
  18. 29 0
      src/assets/images/README.md
  19. 0 0
      src/assets/images/backgrounds/.gitkeep
  20. 0 0
      src/assets/images/common/.gitkeep
  21. 0 0
      src/assets/images/icons/.gitkeep
  22. 63 0
      src/assets/images/icons/BaobiaotubiaoIcon.tsx
  23. binární
      src/assets/images/icons/baobiaotubiao.png
  24. 14 0
      src/assets/images/icons/baobiaotubiao.svg
  25. binární
      src/assets/images/icons/chahe.png
  26. binární
      src/assets/images/icons/fenzhi.png
  27. binární
      src/assets/images/icons/guanbi.png
  28. 5 0
      src/assets/images/icons/index.ts
  29. binární
      src/assets/images/icons/leixing.png
  30. binární
      src/assets/images/icons/shangyiye.png
  31. binární
      src/assets/images/icons/tongji.png
  32. binární
      src/assets/images/icons/xiayiye.png
  33. 0 0
      src/assets/images/illustrations/.gitkeep
  34. 0 0
      src/assets/images/logos/.gitkeep
  35. 19 0
      src/components/KCIMDrawerForm/index.tsx
  36. 17 0
      src/components/KCIMDrawerForm/style.less
  37. 24 29
      src/components/KCIMModalForm/index.tsx
  38. 50 36
      src/components/KCIMPageContainer/index.tsx
  39. 110 109
      src/components/KCIMPageContainer/style.less
  40. 46 45
      src/components/KCIMTable/index.tsx
  41. 134 0
      src/components/KCIMTable/rewrite.less
  42. 100 242
      src/components/KCIMTable/style.less
  43. 59 52
      src/components/KCIMUpload/index.tsx
  44. 6 7
      src/components/KCIMUpload/style.less
  45. 506 511
      src/global.less
  46. 25 25
      src/menuIcons.js
  47. 2 6
      src/pages/Home/index.tsx
  48. 28 41
      src/pages/Home/service.ts
  49. 29 48
      src/pages/Home/style.less
  50. 1366 0
      src/pages/baseInfoMana/checkGroupMana/index.tsx
  51. 347 0
      src/pages/baseInfoMana/checkGroupMana/service.ts
  52. 340 0
      src/pages/baseInfoMana/checkGroupMana/style.less
  53. 133 0
      src/pages/baseInfoMana/checkGroupMana/tableSelector.tsx
  54. 0 0
      src/pages/baseInfoMana/deptPersonnel/index.tsx
  55. 64 0
      src/pages/baseInfoMana/deptPersonnel/service.ts
  56. 101 0
      src/pages/baseInfoMana/deptPersonnel/style.less
  57. 262 0
      src/pages/baseInfoMana/empMana/components/EmployeeList/EmployeeList.tsx
  58. binární
      src/pages/baseInfoMana/empMana/components/EmployeeList/images/emp_icon.png
  59. 133 0
      src/pages/baseInfoMana/empMana/components/EmployeeList/useEmployeeList.ts
  60. 327 0
      src/pages/baseInfoMana/empMana/components/tableSelector.tsx
  61. 639 0
      src/pages/baseInfoMana/empMana/index.tsx
  62. 199 0
      src/pages/baseInfoMana/empMana/service.ts
  63. 84 0
      src/pages/baseInfoMana/empMana/style.less
  64. 382 0
      src/pages/baseInfoMana/empMana_bak/index.tsx
  65. 99 0
      src/pages/baseInfoMana/empMana_bak/service.ts
  66. 73 0
      src/pages/baseInfoMana/empMana_bak/style.less
  67. 282 0
      src/pages/baseInfoMana/empMana_bak/tableSelector.tsx
  68. 145 0
      src/pages/baseInfoMana/empMana_bak/transform.tsx
  69. 297 0
      src/pages/baseInfoMana/unitMana/index.tsx
  70. 50 0
      src/pages/baseInfoMana/unitMana/service.ts
  71. 74 0
      src/pages/baseInfoMana/unitMana/style.less
  72. 213 0
      src/pages/baseInfoMana/unitMana/tableSelector.tsx
  73. 969 0
      src/pages/checkConditionMana/index.tsx
  74. 96 0
      src/pages/checkConditionMana/service.ts
  75. 170 0
      src/pages/checkConditionMana/style.less
  76. 2740 0
      src/pages/checkItemMana/index.tsx
  77. 374 0
      src/pages/checkItemMana/service.ts
  78. 446 0
      src/pages/checkItemMana/style.less
  79. 107 0
      src/pages/checkPointMana/CheckPointTree.tsx
  80. 1979 0
      src/pages/checkPointMana/index.tsx
  81. 658 0
      src/pages/checkPointMana/service.ts
  82. 580 0
      src/pages/checkPointMana/style.less
  83. 2498 0
      src/pages/checkUnitMana/index.tsx
  84. 262 0
      src/pages/checkUnitMana/service.ts
  85. 490 0
      src/pages/checkUnitMana/style.less
  86. 352 305
      src/pages/setting/fenyeTemplate/index.tsx
  87. 27 40
      src/pages/setting/fenyeTemplate/service.ts
  88. 4 5
      src/pages/setting/fenyeTemplate/style.less
  89. 362 266
      src/pages/setting/qualitativeOptionsMana/index.tsx
  90. 47 50
      src/pages/setting/qualitativeOptionsMana/service.ts
  91. 4 5
      src/pages/setting/qualitativeOptionsMana/style.less
  92. 20 25
      src/services/auth.ts
  93. 24 34
      src/services/getDic.ts
  94. 3 1
      src/typings.d.ts
  95. 59 57
      src/utils/devicePixelRatio.js
  96. 6 3
      src/utils/format.ts
  97. 49 0
      src/utils/request.ts
  98. 14 7
      src/utils/tableToExcel.ts
  99. 256 237
      src/utils/tableToMultiHeaderExcel.ts
  100. 257 71
      src/utils/tooljs.ts

binární
.DS_Store


+ 2 - 7
.lintstagedrc

@@ -1,11 +1,6 @@
 {
-  "*.{md,json}": [
-    "prettier --cache --write"
-  ],
-  "*.{js,jsx}": [
-    "max lint --fix --eslint-only",
-    "prettier --cache --write"
-  ],
+  "*.{md,json}": ["prettier --cache --write"],
+  "*.{js,jsx}": ["max lint --fix --eslint-only", "prettier --cache --write"],
   "*.{css,less}": [
     "max lint --fix --stylelint-only",
     "prettier --cache --write"

+ 55 - 21
.umirc.ts

@@ -2,16 +2,14 @@
  * @Author: code4eat awesomedema@gmail.com
  * @Date: 2022-12-14 14:14:32
  * @LastEditors: code4eat awesomedema@gmail.com
- * @LastEditTime: 2023-09-27 11:08:19
+ * @LastEditTime: 2025-07-08 16:53:14
  * @FilePath: /BudgetManaSystem/.umirc.ts
  * @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
  */
 import { defineConfig } from '@umijs/max';
 
-
 const { REACT_APP_ENV } = process.env;
 
-
 export default defineConfig({
   antd: {
     configProvider: {
@@ -21,18 +19,16 @@ export default defineConfig({
   qiankun: {
     slave: {},
   },
-  mfsu:false,
+  mfsu: false,
   lessLoader: {
-    modifyVars: { 
+    modifyVars: {
       '@ant-prefix': 'pfm-ant',
       '@primary-color': '#3377FF',
-      '@border-color-base':'#dae2f2'
-    },  
+      '@border-color-base': '#dae2f2',
+    },
     javascriptEnabled: true,
   }, //对应修改生成的 antd 样式类名
 
-
-
   access: {},
   model: {},
 
@@ -43,49 +39,87 @@ export default defineConfig({
     layout: 'side',
     menuProps: {
       theme: 'light',
-    }
+    },
   },
   manifest: {
     basePath: '/',
   },
   publicPath: REACT_APP_ENV == 'dev' ? '/' : '/pfmManager/',
   proxy: {
-
     '/gateway': {
-      target: 'http://120.27.235.181:5000/gateway',//开发
+      target: 'http://120.27.235.181:5000/gateway', //开发
       //target:'http://47.96.149.190:5000/gateway', //演示
       changeOrigin: true,
       // pathRewrite: { '^/gateway': '' },
-
-    }
+    },
   },
   routes: [
     {
       path: '/',
-      redirect: '/home',
+      redirect: '/setting/fenyeTemplate',
     },
     {
       name: '首页',
       path: '/home',
       component: './Home',
     },
+    {
+      name: '基础信息管理',
+      path: '/baseInfoMana',
+      routes: [
+        {
+          name: '人员管理',
+          path: '/baseInfoMana/empMana',
+          component: './baseInfoMana/empMana',
+        },
+        {
+          name: '单位管理',
+          path: '/baseInfoMana/unitMana',
+          component: './baseInfoMana/unitMana',
+        },
+        {
+          name: '查核组管理',
+          path: '/baseInfoMana/checkGroupMana',
+          component: './baseInfoMana/checkGroupMana',
+        },
+      ],
+    },
+    {
+      name: '查核条件管理',
+      path: '/checkConditionMana',
+      component: './checkConditionMana',
+    },
+    {
+      name: '查核单位管理',
+      path: '/checkUnitMana',
+      component: './checkUnitMana',
+    },
+    {
+      name: '查核要点管理',
+      path: '/checkPointMana',
+      component: './checkPointMana',
+    },
+    {
+      name: '查核项管理',
+      path: '/checkItemMana',
+      component: './checkItemMana',
+    },
     {
       name: '通用设置',
       path: '/setting',
-      routes:[
+      routes: [
         {
           name: '定性选项管理',
           path: '/setting/qualitativeOptionsMana',
-          component:'./setting/qualitativeOptionsMana'
+          component: './setting/qualitativeOptionsMana',
         },
         {
           name: '分页模板管理',
           path: '/setting/fenyeTemplate',
-          component:'./setting/fenyeTemplate'
-        }
-      ]
+          component: './setting/fenyeTemplate',
+        },
+      ],
     },
   ],
   npmClient: 'yarn',
 });
-

+ 3 - 3
.vscode/settings.json

@@ -1,4 +1,4 @@
 {
-    "vue.features.codeActions.enable": false,
-    "typescript.tsdk": "node_modules/typescript/lib"
-}
+  "vue.features.codeActions.enable": false,
+  "typescript.tsdk": "node_modules/typescript/lib"
+}

+ 0 - 3
config/proxy.ts

@@ -6,6 +6,3 @@
  * @FilePath: /BudgetManaSystem/config/proxy.ts
  * @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
  */
-
-
-

+ 448 - 0
mock/checkGroup.js

@@ -0,0 +1,448 @@
+// 模拟查核组数据(参考截图)
+const checkGroups = [
+  {
+    id: 1,
+    name: '急诊手术查核组',
+    groupManagerName: '杨晓琳',
+    remark: '负责急诊手术相关查核工作',
+    createUserName: '系统管理员',
+    createTime: '2024-01-15 10:30:00',
+  },
+  {
+    id: 2,
+    name: '信息系统查核组',
+    groupManagerName: '杨晓琳',
+    remark: '负责信息系统安全查核',
+    createUserName: '系统管理员',
+    createTime: '2024-01-15 11:00:00',
+  },
+  {
+    id: 3,
+    name: '消防查核组',
+    groupManagerName: '杨晓琳',
+    remark: '负责消防安全相关查核工作',
+    createUserName: '系统管理员',
+    createTime: '2024-01-15 14:20:00',
+  },
+  {
+    id: 4,
+    name: '医疗组',
+    groupManagerName: '杨晓琳',
+    remark: '负责医疗相关查核工作',
+    createUserName: '系统管理员',
+    createTime: '2024-01-15 15:45:00',
+  },
+  {
+    id: 5,
+    name: '急诊急外手术核组',
+    groupManagerName: '杨晓琳',
+    remark: '负责急诊急外手术查核',
+    createUserName: '系统管理员',
+    createTime: '2024-01-15 16:10:00',
+  },
+  {
+    id: 6,
+    name: '护理组',
+    groupManagerName: '杨晓琳',
+    remark: '负责护理质量管理查核',
+    createUserName: '系统管理员',
+    createTime: '2024-01-15 16:30:00',
+  },
+  {
+    id: 7,
+    name: '药事服务组',
+    groupManagerName: '杨晓琳',
+    remark: '负责药事服务查核',
+    createUserName: '系统管理员',
+    createTime: '2024-01-15 17:00:00',
+  },
+  {
+    id: 8,
+    name: '药品器械组',
+    groupManagerName: '董丽群',
+    remark: '负责药品器械查核',
+    createUserName: '系统管理员',
+    createTime: '2024-01-15 17:30:00',
+  },
+  {
+    id: 9,
+    name: '糖尿病组',
+    groupManagerName: '何亦红',
+    remark: '负责糖尿病相关查核',
+    createUserName: '系统管理员',
+    createTime: '2024-01-15 18:00:00',
+  },
+  {
+    id: 10,
+    name: '消毒隔离组',
+    groupManagerName: '金艳艳',
+    remark: '负责消毒隔离查核',
+    createUserName: '系统管理员',
+    createTime: '2024-01-15 18:30:00',
+  },
+];
+
+// 模拟人员数据
+const employees = [
+  {
+    id: 1,
+    employeeCode: '005018',
+    employeeName: '王金鸿',
+    deptName: '急诊手术组织',
+  },
+  {
+    id: 2,
+    employeeCode: '005019',
+    employeeName: '杨晓琳',
+    deptName: '信息系统查核组织',
+  },
+  {
+    id: 3,
+    employeeCode: '005020',
+    employeeName: '张琳',
+    deptName: '消防系统查核组织',
+  },
+  {
+    id: 4,
+    employeeCode: '005030',
+    employeeName: '胡庚芳',
+    deptName: '医疗组织',
+  },
+  {
+    id: 5,
+    employeeCode: '005032',
+    employeeName: '陈雨萍',
+    deptName: '护理组织',
+  },
+  {
+    id: 6,
+    employeeCode: '005033',
+    employeeName: '陈海霞',
+    deptName: '护理组织',
+  },
+  {
+    id: 7,
+    employeeCode: '005034',
+    employeeName: '李明',
+    deptName: '急诊手术组织',
+  },
+  {
+    id: 8,
+    employeeCode: '005035',
+    employeeName: '刘强',
+    deptName: '信息系统查核组织',
+  },
+  {
+    id: 9,
+    employeeCode: '005036',
+    employeeName: '赵敏',
+    deptName: '消防系统查核组织',
+  },
+  {
+    id: 10,
+    employeeCode: '005037',
+    employeeName: '孙丽',
+    deptName: '医疗组织',
+  },
+];
+
+// 模拟查核组人员关系数据
+const checkGroupEmployees = [
+  {
+    id: 1,
+    groupId: 1,
+    employeeId: 1,
+    employeeCode: '005018',
+    employeeName: '王金鸿',
+    deptName: '急诊手术组织',
+    isManager: 1,
+  },
+  {
+    id: 2,
+    groupId: 1,
+    employeeId: 7,
+    employeeCode: '005034',
+    employeeName: '李明',
+    deptName: '急诊手术组织',
+    isManager: 0,
+  },
+  {
+    id: 3,
+    groupId: 2,
+    employeeId: 2,
+    employeeCode: '005019',
+    employeeName: '杨晓琳',
+    deptName: '信息系统查核组织',
+    isManager: 1,
+  },
+  {
+    id: 4,
+    groupId: 2,
+    employeeId: 8,
+    employeeCode: '005035',
+    employeeName: '刘强',
+    deptName: '信息系统查核组织',
+    isManager: 0,
+  },
+  {
+    id: 5,
+    groupId: 3,
+    employeeId: 6,
+    employeeCode: '005033',
+    employeeName: '陈海霞',
+    deptName: '护理组织',
+    isManager: 1,
+  },
+  {
+    id: 6,
+    groupId: 4,
+    employeeId: 3,
+    employeeCode: '005020',
+    employeeName: '张琳',
+    deptName: '消防系统查核组织',
+    isManager: 1,
+  },
+  {
+    id: 7,
+    groupId: 4,
+    employeeId: 9,
+    employeeCode: '005036',
+    employeeName: '赵敏',
+    deptName: '消防系统查核组织',
+    isManager: 0,
+  },
+  {
+    id: 8,
+    groupId: 5,
+    employeeId: 5,
+    employeeCode: '005032',
+    employeeName: '陈雨萍',
+    deptName: '护理组织',
+    isManager: 1,
+  },
+  {
+    id: 9,
+    groupId: 5,
+    employeeId: 10,
+    employeeCode: '005037',
+    employeeName: '孙丽',
+    deptName: '医疗组织',
+    isManager: 0,
+  },
+];
+
+export default {
+  // 获取查核组列表
+  'GET /manager/CheckGroup/getCheckGroupList': (req, res) => {
+    const {
+      current = 1,
+      pageSize = 10,
+      groupName = '',
+      groupManagerName = '',
+    } = req.query;
+
+    let filteredGroups = checkGroups;
+
+    // 按查核组名称过滤
+    if (groupName) {
+      filteredGroups = filteredGroups.filter((group) =>
+        group.name.includes(groupName),
+      );
+    }
+
+    // 按组长名称过滤
+    if (groupManagerName) {
+      filteredGroups = filteredGroups.filter(
+        (group) =>
+          group.groupManagerName &&
+          group.groupManagerName.includes(groupManagerName),
+      );
+    }
+
+    const startIndex = (current - 1) * pageSize;
+    const endIndex = startIndex + parseInt(pageSize);
+    const list = filteredGroups.slice(startIndex, endIndex);
+
+    res.json({
+      list,
+      totalCount: filteredGroups.length,
+      currPage: parseInt(current),
+      pageSize: parseInt(pageSize),
+      totalPage: Math.ceil(filteredGroups.length / pageSize),
+    });
+  },
+
+  // 新增查核组
+  'POST /manager/CheckGroup/addCheckGroup': (req, res) => {
+    const { name, remark, groupManagerId } = req.body;
+    const manager = employees.find((emp) => emp.id === groupManagerId);
+
+    const newGroup = {
+      id: checkGroups.length + 1,
+      name,
+      groupManagerName: manager ? manager.employeeName : '',
+      remark,
+      createUserName: '系统管理员',
+      createTime: new Date().toISOString(),
+    };
+
+    checkGroups.push(newGroup);
+
+    res.json({ success: true, message: '新增成功' });
+  },
+
+  // 编辑查核组
+  'POST /manager/CheckGroup/editCheckGroup': (req, res) => {
+    const { id, name, remark, groupManagerId } = req.body;
+    const groupIndex = checkGroups.findIndex((group) => group.id === id);
+
+    if (groupIndex !== -1) {
+      const manager = employees.find((emp) => emp.id === groupManagerId);
+      checkGroups[groupIndex] = {
+        ...checkGroups[groupIndex],
+        name,
+        remark,
+        groupManagerName: manager
+          ? manager.employeeName
+          : checkGroups[groupIndex].groupManagerName,
+      };
+      res.json({ success: true, message: '编辑成功' });
+    } else {
+      res.status(404).json({ success: false, message: '查核组不存在' });
+    }
+  },
+
+  // 删除查核组
+  'POST /manager/CheckGroup/deleteCheckGroup': (req, res) => {
+    const { id } = req.query;
+    const groupIndex = checkGroups.findIndex(
+      (group) => group.id === parseInt(id),
+    );
+
+    if (groupIndex !== -1) {
+      checkGroups.splice(groupIndex, 1);
+      // 同时删除相关的人员关系
+      const employeeIndices = [];
+      for (let i = checkGroupEmployees.length - 1; i >= 0; i--) {
+        if (checkGroupEmployees[i].groupId === parseInt(id)) {
+          employeeIndices.push(i);
+        }
+      }
+      employeeIndices.forEach((index) => checkGroupEmployees.splice(index, 1));
+
+      res.json({ success: true, message: '删除成功' });
+    } else {
+      res.status(404).json({ success: false, message: '查核组不存在' });
+    }
+  },
+
+  // 获取查核组人员列表
+  'GET /manager/CheckGroup/getCheckGroupEmployees': (req, res) => {
+    const { groupId, filter = '', current = 1, pageSize = 10 } = req.query;
+
+    let groupEmployees = checkGroupEmployees.filter(
+      (emp) => emp.groupId === parseInt(groupId),
+    );
+
+    // 按姓名或工号过滤
+    if (filter) {
+      groupEmployees = groupEmployees.filter(
+        (emp) =>
+          emp.employeeName.includes(filter) ||
+          emp.employeeCode.includes(filter),
+      );
+    }
+
+    const startIndex = (current - 1) * pageSize;
+    const endIndex = startIndex + parseInt(pageSize);
+    const list = groupEmployees.slice(startIndex, endIndex);
+
+    res.json({
+      list,
+      totalCount: groupEmployees.length,
+      currPage: parseInt(current),
+      pageSize: parseInt(pageSize),
+      totalPage: Math.ceil(groupEmployees.length / pageSize),
+    });
+  },
+
+  // 获取可添加的人员列表
+  'GET /manager/CheckGroup/getAvailableEmployees': (req, res) => {
+    const { groupId, filter = '', current = 1, pageSize = 10 } = req.query;
+
+    // 获取已经在该查核组的人员ID
+    const existingEmployeeIds = checkGroupEmployees
+      .filter((emp) => emp.groupId === parseInt(groupId))
+      .map((emp) => emp.employeeId);
+
+    // 过滤出不在该查核组的人员
+    let availableEmployees = employees.filter(
+      (emp) => !existingEmployeeIds.includes(emp.id),
+    );
+
+    // 按姓名或工号过滤
+    if (filter) {
+      availableEmployees = availableEmployees.filter(
+        (emp) =>
+          emp.employeeName.includes(filter) ||
+          emp.employeeCode.includes(filter),
+      );
+    }
+
+    const startIndex = (current - 1) * pageSize;
+    const endIndex = startIndex + parseInt(pageSize);
+    const list = availableEmployees.slice(startIndex, endIndex);
+
+    res.json({
+      list,
+      totalCount: availableEmployees.length,
+      currPage: parseInt(current),
+      pageSize: parseInt(pageSize),
+      totalPage: Math.ceil(availableEmployees.length / pageSize),
+    });
+  },
+
+  // 添加查核组人员
+  'POST /manager/CheckGroup/addCheckGroupEmployee': (req, res) => {
+    const { groupId, employeeIds } = req.body;
+
+    employeeIds.forEach((employeeId) => {
+      const employee = employees.find((emp) => emp.id === parseInt(employeeId));
+      if (employee) {
+        const newRelation = {
+          id: checkGroupEmployees.length + 1,
+          groupId: parseInt(groupId),
+          employeeId: employee.id,
+          employeeCode: employee.employeeCode,
+          employeeName: employee.employeeName,
+          deptName: employee.deptName,
+          isManager: 0,
+        };
+        checkGroupEmployees.push(newRelation);
+      }
+    });
+
+    res.json({ success: true, message: '添加成功' });
+  },
+
+  // 删除查核组人员
+  'POST /manager/CheckGroup/delCheckGroupEmployee': (req, res) => {
+    const employeeIds = req.body;
+
+    employeeIds.forEach((id) => {
+      const index = checkGroupEmployees.findIndex(
+        (emp) => emp.id === parseInt(id),
+      );
+      if (index !== -1) {
+        checkGroupEmployees.splice(index, 1);
+      }
+    });
+
+    res.json({ success: true, message: '删除成功' });
+  },
+
+  // 获取人员字典
+  'GET /manager/CheckGroup/getEmployeeDict': (req, res) => {
+    res.json(employees);
+  },
+};

+ 375 - 0
mock/checkItem.js

@@ -0,0 +1,375 @@
+/*
+ * @Author: code4eat awesomedema@gmail.com
+ * @Date: 2024-01-01 10:00:00
+ * @LastEditors: code4eat awesomedema@gmail.com
+ * @LastEditTime: 2024-01-01 10:00:00
+ * @FilePath: /pfmBackMana/mock/checkItem.js
+ * @Description: 查核项管理mock数据
+ */
+
+// 查核项分类树mock数据
+const checkItemTreeData = [
+  {
+    key: '1',
+    title: '地面干净无水迹',
+    children: [
+      {
+        key: '1-1',
+        title: '科内无专管',
+      },
+      {
+        key: '1-2',
+        title: '科内不知晓专管员',
+      },
+      {
+        key: '1-3',
+        title: '麻醉药品未放入保险柜',
+      },
+      {
+        key: '1-4',
+        title: '使用PDA时扫描帐后未查看PDA各项信息是否准确',
+      },
+      {
+        key: '1-5',
+        title: '对无法有效沟通的患者,未让陪同人员除述患者姓名或未查看腕带',
+      },
+    ],
+  },
+  {
+    key: '2',
+    title: '防火门关情况',
+    children: [
+      {
+        key: '2-1',
+        title: '防火门未正常关闭',
+      },
+      {
+        key: '2-2',
+        title: '防火门被锁死',
+      },
+    ],
+  },
+  {
+    key: '3',
+    title: '防烟面具、指挥棒、任意门其他应急物品情况',
+    children: [
+      {
+        key: '3-1',
+        title: '应急物品缺失',
+      },
+      {
+        key: '3-2',
+        title: '应急物品损坏',
+      },
+    ],
+  },
+  {
+    key: '4',
+    title: '疏散指示灯情况',
+    children: [
+      {
+        key: '4-1',
+        title: '指示灯不亮',
+      },
+      {
+        key: '4-2',
+        title: '指示灯损坏',
+      },
+    ],
+  },
+  {
+    key: '5',
+    title: '科室消防安全自查表',
+    children: [
+      {
+        key: '5-1',
+        title: '未按要求填写自查表',
+      },
+      {
+        key: '5-2',
+        title: '自查表内容不完整',
+      },
+    ],
+  },
+  {
+    key: '6',
+    title: '消防情况',
+    children: [
+      {
+        key: '6-1',
+        title: '消防设施损坏',
+      },
+      {
+        key: '6-2',
+        title: '消防通道堵塞',
+      },
+    ],
+  },
+];
+
+// 查核项列表mock数据
+const checkItemListData = {
+  1: [
+    {
+      id: 1,
+      name: '科内无专管',
+      score: 2.0,
+      description: '科室内未配置专门管理人员',
+      categoryId: '1',
+    },
+    {
+      id: 2,
+      name: '科内不知晓专管员',
+      score: 1.5,
+      description: '科室人员不知道专管员是谁',
+      categoryId: '1',
+    },
+    {
+      id: 3,
+      name: '麻醉药品未放入保险柜',
+      score: 1.0,
+      description: '麻醉药品未按规定放入保险柜',
+      categoryId: '1',
+    },
+    {
+      id: 4,
+      name: '使用PDA时扫描帐后未查看PDA各项信息是否准确',
+      score: 0.5,
+      description: '使用PDA时未核实信息准确性',
+      categoryId: '1',
+    },
+    {
+      id: 5,
+      name: '对无法有效沟通的患者,未让陪同人员除述患者姓名或未查看腕带',
+      score: 1.0,
+      description: '对沟通困难患者未按规定核实身份',
+      categoryId: '1',
+    },
+  ],
+  2: [
+    {
+      id: 6,
+      name: '防火门未正常关闭',
+      score: 3.0,
+      description: '防火门应保持关闭状态',
+      categoryId: '2',
+    },
+    {
+      id: 7,
+      name: '防火门被锁死',
+      score: 5.0,
+      description: '防火门不得锁死,影响疏散',
+      categoryId: '2',
+    },
+  ],
+  3: [
+    {
+      id: 8,
+      name: '应急物品缺失',
+      score: 2.0,
+      description: '防烟面具、指挥棒等应急物品缺失',
+      categoryId: '3',
+    },
+    {
+      id: 9,
+      name: '应急物品损坏',
+      score: 1.5,
+      description: '应急物品存在损坏情况',
+      categoryId: '3',
+    },
+  ],
+  4: [
+    {
+      id: 10,
+      name: '指示灯不亮',
+      score: 1.0,
+      description: '疏散指示灯不能正常工作',
+      categoryId: '4',
+    },
+    {
+      id: 11,
+      name: '指示灯损坏',
+      score: 2.0,
+      description: '疏散指示灯外观损坏',
+      categoryId: '4',
+    },
+  ],
+  5: [
+    {
+      id: 12,
+      name: '未按要求填写自查表',
+      score: 1.0,
+      description: '科室未按时填写消防安全自查表',
+      categoryId: '5',
+    },
+    {
+      id: 13,
+      name: '自查表内容不完整',
+      score: 0.5,
+      description: '自查表填写内容不完整或不准确',
+      categoryId: '5',
+    },
+  ],
+  6: [
+    {
+      id: 14,
+      name: '消防设施损坏',
+      score: 3.0,
+      description: '消防器材、设施存在损坏',
+      categoryId: '6',
+    },
+    {
+      id: 15,
+      name: '消防通道堵塞',
+      score: 4.0,
+      description: '消防通道被堵塞,影响疏散',
+      categoryId: '6',
+    },
+  ],
+};
+
+let currentId = 16; // 用于新增数据时的ID生成
+
+export default {
+  // 获取查核项分类树
+  'GET /manager/checkItem/getCheckItemTree': (req, res) => {
+    setTimeout(() => {
+      res.json({
+        success: true,
+        data: checkItemTreeData,
+      });
+    }, 200);
+  },
+
+  // 获取查核项列表
+  'GET /manager/checkItem/getCheckItemList': (req, res) => {
+    const { current = 1, pageSize = 10, categoryId, keywords } = req.query;
+
+    let data = checkItemListData[categoryId] || [];
+
+    // 关键字搜索
+    if (keywords) {
+      data = data.filter(
+        (item) =>
+          item.name.includes(keywords) ||
+          (item.description && item.description.includes(keywords)),
+      );
+    }
+
+    const start = (current - 1) * pageSize;
+    const end = start + parseInt(pageSize);
+    const list = data.slice(start, end);
+
+    setTimeout(() => {
+      res.json({
+        success: true,
+        list,
+        totalCount: data.length,
+        pageSize: parseInt(pageSize),
+        currPage: parseInt(current),
+        totalPage: Math.ceil(data.length / pageSize),
+      });
+    }, 300);
+  },
+
+  // 新增查核项
+  'POST /manager/checkItem/addCheckItem': (req, res) => {
+    const { name, score, description, categoryId } = req.body;
+
+    const newItem = {
+      id: currentId++,
+      name,
+      score,
+      description,
+      categoryId,
+      createTime: new Date().toISOString(),
+    };
+
+    if (!checkItemListData[categoryId]) {
+      checkItemListData[categoryId] = [];
+    }
+    checkItemListData[categoryId].push(newItem);
+
+    setTimeout(() => {
+      res.json({
+        success: true,
+        message: '新增成功',
+        data: newItem,
+      });
+    }, 500);
+  },
+
+  // 编辑查核项
+  'POST /manager/checkItem/editCheckItem': (req, res) => {
+    const { id, name, score, description, categoryId } = req.body;
+
+    if (checkItemListData[categoryId]) {
+      const index = checkItemListData[categoryId].findIndex(
+        (item) => item.id === id,
+      );
+      if (index !== -1) {
+        checkItemListData[categoryId][index] = {
+          ...checkItemListData[categoryId][index],
+          name,
+          score,
+          description,
+          updateTime: new Date().toISOString(),
+        };
+      }
+    }
+
+    setTimeout(() => {
+      res.json({
+        success: true,
+        message: '编辑成功',
+      });
+    }, 500);
+  },
+
+  // 删除查核项
+  'POST /manager/checkItem/deleteCheckItem': (req, res) => {
+    const { id } = req.query;
+
+    // 从所有分类中查找并删除
+    Object.keys(checkItemListData).forEach((categoryId) => {
+      const index = checkItemListData[categoryId].findIndex(
+        (item) => item.id == id,
+      );
+      if (index !== -1) {
+        checkItemListData[categoryId].splice(index, 1);
+      }
+    });
+
+    setTimeout(() => {
+      res.json({
+        success: true,
+        message: '删除成功',
+      });
+    }, 500);
+  },
+
+  // 获取分类详情
+  'GET /manager/checkItem/getCategoryDetail': (req, res) => {
+    const { categoryId } = req.query;
+
+    // 模拟分类详情数据
+    const categoryDetails = {
+      1: {
+        id: '1',
+        name: '地面干净无水迹',
+        type: 'multiple',
+        score: 5,
+        checkMethod: 'observe',
+        isStatistics: true,
+        description: '应知应会:疏散通道畅通,无杂物堆品',
+      },
+    };
+
+    setTimeout(() => {
+      res.json({
+        success: true,
+        data: categoryDetails[categoryId] || null,
+      });
+    }, 200);
+  },
+};

+ 61 - 0
mock/emp.js

@@ -0,0 +1,61 @@
+/*
+ * @Author: code4eat awesomedema@gmail.com
+ * @Date: 2025-04-15 15:07:50
+ * @LastEditors: code4eat awesomedema@gmail.com
+ * @LastEditTime: 2025-04-15 15:48:45
+ * @FilePath: /pfmBackMana/mock/emp.js
+ * @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
+ */
+// mock/employees.js
+
+// 模拟的所有人员数据
+const allEmployees = [
+  { id: 32, code: '0000032', name: '张晓艺' },
+  { id: 33, code: '0000033', name: '王芬芬' },
+  { id: 34, code: '0000034', name: '杨梦怡' },
+  { id: 35, code: '0000035', name: '刘欣蓉' },
+  { id: 36, code: '0000036', name: '张东霞' },
+  { id: 37, code: '0000037', name: '李天薇' },
+  { id: 38, code: '0000038', name: '王盼盼' },
+  { id: 39, code: '0000039', name: '王明明' },
+  { id: 40, code: '0000040', name: '柳飘飘' },
+  { id: 41, code: '0000041', name: '李俊' },
+  // 可根据需要增加更多数据用于分页
+];
+
+export default {
+  // 定义 GET 请求,路径为 /api/employees
+  'GET /api/manager/Employee/getEmpList': (req, res) => {
+    const { page = 1, pageSize = 10, keywords = '' } = req.query;
+    const pageNumber = parseInt(page, 10);
+    const pageSizeNumber = parseInt(pageSize, 10);
+
+    // 根据关键词过滤人员数据
+    const filteredEmployees = allEmployees.filter((emp) => {
+      return (
+        emp.name.indexOf(keywords) !== -1 || emp.code.indexOf(keywords) !== -1
+      );
+    });
+
+    const totalCount = filteredEmployees.length;
+    const totalPage = Math.ceil(totalCount / pageSizeNumber);
+    const startIndex = (pageNumber - 1) * pageSizeNumber;
+    const list = filteredEmployees.slice(
+      startIndex,
+      startIndex + pageSizeNumber,
+    );
+
+    // 返回接口数据结构与前端预期一致
+    res.json({
+      status: 200,
+      msg: 'success',
+      data: {
+        totalCount,
+        pageSize: pageSizeNumber,
+        totalPage,
+        currPage: pageNumber,
+        list,
+      },
+    });
+  },
+};

+ 45 - 50
mock/home.ts

@@ -7,54 +7,49 @@
  * @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
  */
 
-
-
-
 export default {
-    'GET /performance/index': (req: any, res: any) => {        
-        res.json(
-            {
-                success: true,
-                data: [
-                    {
-                        id: 1,
-                        computeDate: "2022-09",
-                        code: 1,
-                        name: "月度结转",
-                        preCode: 0,
-                        sort: 1,
-                        status: 2,
-                        audit: "0"
-                    },
-                    {
-                        id: 1,
-                        computeDate: "2022-09",
-                        code: 1,
-                        name: "设置信息",
-                        preCode: 0,
-                        sort: 1,
-                        status: 2,
-                        audit: "0"
-                    },
-                    {
-                        id: 1,
-                        computeDate: "2022-09",
-                        code: 1,
-                        name: "绩效数据",
-                        preCode: 0,
-                        sort: 1,
-                        status: 1,
-                        audit: "0"
-                    }
-                ],
-                errorCode: 0,
-            });
-    },
-    'GET /performance/index/getCurrentComputeDate':(req: any, res: any)=>{
-        res.json({
-              success:true,
-              data:`${Math.random()}`
-        })
-    }
-
-};
+  'GET /performance/index': (req: any, res: any) => {
+    res.json({
+      success: true,
+      data: [
+        {
+          id: 1,
+          computeDate: '2022-09',
+          code: 1,
+          name: '月度结转',
+          preCode: 0,
+          sort: 1,
+          status: 2,
+          audit: '0',
+        },
+        {
+          id: 1,
+          computeDate: '2022-09',
+          code: 1,
+          name: '设置信息',
+          preCode: 0,
+          sort: 1,
+          status: 2,
+          audit: '0',
+        },
+        {
+          id: 1,
+          computeDate: '2022-09',
+          code: 1,
+          name: '绩效数据',
+          preCode: 0,
+          sort: 1,
+          status: 1,
+          audit: '0',
+        },
+      ],
+      errorCode: 0,
+    });
+  },
+  'GET /performance/index/getCurrentComputeDate': (req: any, res: any) => {
+    res.json({
+      success: true,
+      data: `${Math.random()}`,
+    });
+  },
+};

+ 158 - 170
mock/monthlyData.ts

@@ -7,236 +7,224 @@
  * @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
  */
 
-
-
-
 export default {
-    'GET /performance/kpi/getSalary': (req: any, res: any) => {
-        res.json({
-            success: true,
-            data: {
-                current: 1,
-                list: [
-                    {
-                        "unitType": 1,
-
-                        "unitCode": 1,
-
-                        "unitName": "四病区护理组",
-
-                        "deptCode": "1",
-
-                        "deptName": "内科1",
-
-                        "empNo": "90083",
-
-                        "empName": "程静静",
-
-                        "salaryType": "1",
-
-                        "salaryTypeName": "学历工资",
-
-                        "salary": 10250
-                    },
-                    {
-                        "unitType": 1,
-
-                        "unitCode": 2,
-
-                        "unitName": "四病区护理组",
-
-                        "deptCode": "2",
+  'GET /performance/kpi/getSalary': (req: any, res: any) => {
+    res.json({
+      success: true,
+      data: {
+        current: 1,
+        list: [
+          {
+            unitType: 1,
 
-                        "deptName": "内科1",
+            unitCode: 1,
 
-                        "empNo": "90084",
+            unitName: '四病区护理组',
 
-                        "empName": "程静静",
+            deptCode: '1',
 
-                        "salaryType": "1",
+            deptName: '内科1',
 
-                        "salaryTypeName": "学历工资",
+            empNo: '90083',
 
-                        "salary": 20250
-                    }
-                ],
-                pageSize: 10,
-                totalCount: 0,
-                totalPage: 0
-            }
+            empName: '程静静',
 
-        })
-    },
-    'GET /performance/kpi/getNonAssessmentList': (req: any, res: any) => {
-        res.json({
-            success: true,
-            data: {
-                current: 1,
-                list: [
-                    {
-                        "unitType": "1",
+            salaryType: '1',
 
-                        "unitTypeName": "护理",
+            salaryTypeName: '学历工资',
 
-                        "unitCode": "1",
+            salary: 10250,
+          },
+          {
+            unitType: 1,
 
-                        "unitName": "四病区护理组",
+            unitCode: 2,
 
-                        "deptCode": 1,
+            unitName: '四病区护理组',
 
-                        "deptName": "内科1",
+            deptCode: '2',
 
-                        "assessmentCode": "1",
+            deptName: '内科1',
 
-                        "assessmentName": "最看红按头还",
+            empNo: '90084',
 
-                        "distributionType": 1,
+            empName: '程静静',
 
-                        "targetValue": "02032",
+            salaryType: '1',
 
-                        "targetValueName": "程静静",
+            salaryTypeName: '学历工资',
 
-                        "describe": "测试说明"
-                    },
-                    {
-                        "unitType": "1",
+            salary: 20250,
+          },
+        ],
+        pageSize: 10,
+        totalCount: 0,
+        totalPage: 0,
+      },
+    });
+  },
+  'GET /performance/kpi/getNonAssessmentList': (req: any, res: any) => {
+    res.json({
+      success: true,
+      data: {
+        current: 1,
+        list: [
+          {
+            unitType: '1',
 
-                        "unitTypeName": "护理",
+            unitTypeName: '护理',
 
-                        "unitCode": "1",
+            unitCode: '1',
 
-                        "unitName": "四病区护理组",
+            unitName: '四病区护理组',
 
-                        "deptCode": 1,
+            deptCode: 1,
 
-                        "deptName": "内科1",
+            deptName: '内科1',
 
-                        "assessmentCode": "2",
+            assessmentCode: '1',
 
-                        "assessmentName": "最看红按头还",
+            assessmentName: '最看红按头还',
 
-                        "distributionType": 1,
+            distributionType: 1,
 
-                        "targetValue": "02032",
+            targetValue: '02032',
 
-                        "targetValueName": "程静静",
+            targetValueName: '程静静',
 
-                        "describe": "测试说明"
-                    }
-                ],
-                pageSize: 10,
-                totalCount: 0,
-                totalPage: 0
-            }
+            describe: '测试说明',
+          },
+          {
+            unitType: '1',
 
-        })
-    },
-    'GET /performance/kpi/getAssessmentList': (req: any, res: any) => {
-        res.json({
-            success: true,
-            data: [
-                {
-                    "code": "听现音红当",
+            unitTypeName: '护理',
 
-                    "name": "物军动前压",
+            unitCode: '1',
 
-                    "child": [
-                        {
-                            "code": "太必世八目花电速点这动务史主不车每他象身民作或物员做",
+            unitName: '四病区护理组',
 
-                            "name": "文时较清斗最程后作给水",
+            deptCode: 1,
 
-                            "child": []
+            deptName: '内科1',
 
-                        }
-                    ]
+            assessmentCode: '2',
 
-                }
-            ],
+            assessmentName: '最看红按头还',
 
-        })
-    },
-    'GET /performance/getAssessmentData': (req: any, res: any) => {
-        res.json({
-            success: true,
-            data: {
-                current: 1,
-                list: [
-                    {
-                        "itemCode": "1",
+            distributionType: 1,
 
-                        "itemName": "腹腔引流袋",
+            targetValue: '02032',
 
-                        "itemType": "1",
+            targetValueName: '程静静',
 
-                        "itemTypeName": "收费项目",
+            describe: '测试说明',
+          },
+        ],
+        pageSize: 10,
+        totalCount: 0,
+        totalPage: 0,
+      },
+    });
+  },
+  'GET /performance/kpi/getAssessmentList': (req: any, res: any) => {
+    res.json({
+      success: true,
+      data: [
+        {
+          code: '听现音红当',
 
-                        "pointValue": 50,
+          name: '物军动前压',
 
-                        "value": 10000,
+          child: [
+            {
+              code: '太必世八目花电速点这动务史主不车每他象身民作或物员做',
 
-                        "createUser": "低基林算几安",
+              name: '文时较清斗最程后作给水',
 
-                        "createTime": "位派济查却精义"
-                    },
-                    {
-                        "itemCode": "1",
+              child: [],
+            },
+          ],
+        },
+      ],
+    });
+  },
+  'GET /performance/getAssessmentData': (req: any, res: any) => {
+    res.json({
+      success: true,
+      data: {
+        current: 1,
+        list: [
+          {
+            itemCode: '1',
 
-                        "itemName": "腹腔引流袋",
+            itemName: '腹腔引流袋',
 
-                        "itemType": "1",
+            itemType: '1',
 
-                        "itemTypeName": "收费项目",
+            itemTypeName: '收费项目',
 
-                        "pointValue": 50,
+            pointValue: 50,
 
-                        "value": 10000,
+            value: 10000,
 
-                        "createUser": "低基林算几安",
+            createUser: '低基林算几安',
 
-                        "createTime": "位派济查却精义"
-                    }
-                ],
-                pageSize: 10,
-                totalCount: 0,
-                totalPage: 0
-            }
+            createTime: '位派济查却精义',
+          },
+          {
+            itemCode: '1',
 
-        })
-    },
-    'GET /performance/manager/list': (req: any, res: any) => {
-        res.json({
-            success: true,
-            data: {
-                current: 1,
-                list: [
-                    {
-                        "unitCode": "1",
+            itemName: '腹腔引流袋',
 
-                        "unitName": "护理组",
+            itemType: '1',
 
-                        "deptCode": "1",
+            itemTypeName: '收费项目',
 
-                        "deptName": "内科",
+            pointValue: 50,
 
-                        "CMIData": {
-                            "targetValue": 1.1,
+            value: 10000,
 
-                            "value": 1.2,
+            createUser: '低基林算几安',
 
-                            "score": 27.7
+            createTime: '位派济查却精义',
+          },
+        ],
+        pageSize: 10,
+        totalCount: 0,
+        totalPage: 0,
+      },
+    });
+  },
+  'GET /performance/manager/list': (req: any, res: any) => {
+    res.json({
+      success: true,
+      data: {
+        current: 1,
+        list: [
+          {
+            unitCode: '1',
 
-                        },
+            unitName: '护理组',
 
-                        "totalScore": 100
+            deptCode: '1',
 
-                    }
-                ],
-                pageSize: 10,
-                totalCount: 0,
-                totalPage: 0
-            }
+            deptName: '内科',
 
-        })
-    },
-}
+            CMIData: {
+              targetValue: 1.1,
+
+              value: 1.2,
+
+              score: 27.7,
+            },
+
+            totalScore: 100,
+          },
+        ],
+        pageSize: 10,
+        totalCount: 0,
+        totalPage: 0,
+      },
+    });
+  },
+};

+ 164 - 181
mock/monthlyInfo.ts

@@ -9,197 +9,180 @@
 
 import mockjs from 'mockjs';
 
-
-
-
 const list = [...new Array(100).keys()].map((item, index) => {
-    return {
-        account: "admin",
-        avatarUrl: "",
-        departmentId: "A00002",
-        departmentName: "",
-        doctorLevel: "2",
-        entryTime: "2022-06-28 12:00:00",
-        gender: 2,
-        hospId: "30",
-        hospName: "武汉市儿童医院",
-        id: `30614${index}`,
-        idCardNum: "",
-        isOnService: 0,
-        jobTitle: "2",
-        major: "2",
-        name: `管理员${index}`,
-        password: "e10adc3949ba59abbe56e057f20f883e",
-        phoneNumber: "",
-        practiceCate: "1",
-        practiceCertificateNo: "",
-        practiceStatus: "1",
-        practiceSubject: "2",
-        qualificationCertificateNo: "",
-        remark: "",
-        title: "2",
-        userCate: "1",
-        userId: "30614",
-    }
+  return {
+    account: 'admin',
+    avatarUrl: '',
+    departmentId: 'A00002',
+    departmentName: '',
+    doctorLevel: '2',
+    entryTime: '2022-06-28 12:00:00',
+    gender: 2,
+    hospId: '30',
+    hospName: '武汉市儿童医院',
+    id: `30614${index}`,
+    idCardNum: '',
+    isOnService: 0,
+    jobTitle: '2',
+    major: '2',
+    name: `管理员${index}`,
+    password: 'e10adc3949ba59abbe56e057f20f883e',
+    phoneNumber: '',
+    practiceCate: '1',
+    practiceCertificateNo: '',
+    practiceStatus: '1',
+    practiceSubject: '2',
+    qualificationCertificateNo: '',
+    remark: '',
+    title: '2',
+    userCate: '1',
+    userId: '30614',
+  };
 });
 
-
 const users = [
-    { id: 0, name: 'Umi', nickName: 'U', gender: 'MALE' },
-    { id: 1, name: 'Fish', nickName: 'B', gender: 'FEMALE' },
+  { id: 0, name: 'Umi', nickName: 'U', gender: 'MALE' },
+  { id: 1, name: 'Fish', nickName: 'B', gender: 'FEMALE' },
 ];
 
-
 export default {
-    'GET /performance/check/getUnitList': (req: any, res: any) => {
-
-        console.log({ req, res });
-
-        res.json(
-            {
-                success: true,
-                data: [
-                    {
-                        code: 8662827685293679,
-
-                        name: "病区护理",
-
-                        child: [
-                            {
-                                code: 3994166660344023,
-
-                                name: "心内科病区",
+  'GET /performance/check/getUnitList': (req: any, res: any) => {
+    console.log({ req, res });
 
-                                child: [
-                                    {
-                                        code: 8338644021754079,
+    res.json({
+      success: true,
+      data: [
+        {
+          code: 8662827685293679,
 
-                                        name: "心内科一病区"
+          name: '病区护理',
 
-                                    }
-                                ]
+          child: [
+            {
+              code: 3994166660344023,
 
-                            }
-                        ]
+              name: '心内科病区',
 
-                    }
-                ],
-                errorCode: 0,
-            });
-    },
-    'GET /performance/check/empList': (req: any, res: any) => {
-        res.json({
-            success: true,
-            data: [
+              child: [
                 {
-                    "unitCode": 1,
-
-                    "empNo": "1",
-
-                    "empName": "程静静",
-
-                    "jobLevel": "N3",
-
-                    "jobTitle": "护士长",
-
-                    "jobRate": "1.6",
-
-                    "seniority": "17"
-
-                }
-            ],
-
-        })
-    },
-    'GET /centerSys/user/list': (req: any, res: any) => {
-        res.json({
-            success: true,
-            data: {
-                current: 1,
-                list: list,
-                pageSize: 10,
-                totalCount: 0,
-                totalPage: 0
-            }
-
-        })
-    },
-    'GET /performance/check/deptList': (req: any, res: any) => {
-        res.json({
-            success: true,
-            data: {
-                current: 1,
-                list: [
-                    {
-                        "unitCode": 1,
-
-                        "deptCode": "1",
-
-                        "deptName": "内一科",
-
-                        "deptType": 2,
-
-                        "describe": "临床科室",
-
-                        "deptTypeName": "临床"
-                    },
-                    {
-                        "unitCode": 2,
-
-                        "deptCode": "2",
-
-                        "deptName": "内一科",
-
-                        "deptType": 2,
-
-                        "describe": "临床科室",
-
-                        "deptTypeName": "临床"
-                    }
-                ],
-                pageSize: 10,
-                totalCount: 0,
-                totalPage: 0
-            }
-
-        })
-    },
-    'GET /performance/check/itemList': (req: any, res: any) => {
-        res.json({
-            success: true,
-            data: {
-                current: 1,
-                list: [
-                    {
-                        "itemCode": "000000040",
-
-                        "itemName": "I级护理",
-
-                        "itemType": "护理费",
-
-                        "itemPoint": 50,
-
-                        "coreFlag": "1"
-                    },
-                    {
-                        "itemCode": "000000041",
-
-                        "itemName": "I级护理",
-
-                        "itemType": "护理费",
-
-                        "itemPoint": 50,
-
-                        "coreFlag": "1"
-                    }
-                ],
-                pageSize: 10,
-                totalCount: 0,
-                totalPage: 0
-            }
-
-        })
-    },
-
-
-};
+                  code: 8338644021754079,
+
+                  name: '心内科一病区',
+                },
+              ],
+            },
+          ],
+        },
+      ],
+      errorCode: 0,
+    });
+  },
+  'GET /performance/check/empList': (req: any, res: any) => {
+    res.json({
+      success: true,
+      data: [
+        {
+          unitCode: 1,
+
+          empNo: '1',
+
+          empName: '程静静',
+
+          jobLevel: 'N3',
+
+          jobTitle: '护士长',
+
+          jobRate: '1.6',
+
+          seniority: '17',
+        },
+      ],
+    });
+  },
+  'GET /centerSys/user/list': (req: any, res: any) => {
+    res.json({
+      success: true,
+      data: {
+        current: 1,
+        list: list,
+        pageSize: 10,
+        totalCount: 0,
+        totalPage: 0,
+      },
+    });
+  },
+  'GET /performance/check/deptList': (req: any, res: any) => {
+    res.json({
+      success: true,
+      data: {
+        current: 1,
+        list: [
+          {
+            unitCode: 1,
+
+            deptCode: '1',
+
+            deptName: '内一科',
+
+            deptType: 2,
+
+            describe: '临床科室',
+
+            deptTypeName: '临床',
+          },
+          {
+            unitCode: 2,
+
+            deptCode: '2',
+
+            deptName: '内一科',
+
+            deptType: 2,
+
+            describe: '临床科室',
+
+            deptTypeName: '临床',
+          },
+        ],
+        pageSize: 10,
+        totalCount: 0,
+        totalPage: 0,
+      },
+    });
+  },
+  'GET /performance/check/itemList': (req: any, res: any) => {
+    res.json({
+      success: true,
+      data: {
+        current: 1,
+        list: [
+          {
+            itemCode: '000000040',
+
+            itemName: 'I级护理',
+
+            itemType: '护理费',
+
+            itemPoint: 50,
+
+            coreFlag: '1',
+          },
+          {
+            itemCode: '000000041',
+
+            itemName: 'I级护理',
+
+            itemType: '护理费',
+
+            itemPoint: 50,
+
+            coreFlag: '1',
+          },
+        ],
+        pageSize: 10,
+        totalCount: 0,
+        totalPage: 0,
+      },
+    });
+  },
+};

+ 41 - 44
mock/monthlySet.ts

@@ -7,48 +7,45 @@
  * @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
  */
 
-
-
 export default {
-    'GET /performance/carry/list': (req: any, res: any) => {
-        res.json(
-            {
-                success: true,
-                data: {
-                    preCarry: {
-                        "computeDate":`${Math.random()}`, //核算年月
-                        "dateRange": "2022/10/01-2022/10/31", //日期区间
-                        "personalRate": "10%", //人事成本比例
-                        "seniority": 30 //年资
-                    }, //上月度结转数据
-                    currentCarry: {
-                        "computeDate":`${Math.random()}`, //核算年月
-                        "dateRange": "2022/11/01-2022/11/31", //日期区间
-                        "personalRate": "10%", //人事成本比例
-                        "seniority": "30.00" //年资
-                    }, //本月度结转数据
-                    lastCarry: {
-                        "computeDate":`${Math.random()}`, //核算年月
-                        "dateRange": "2022/12/01-2022/12/31", //日期区间
-                        "personalRate": "10%", //人事成本比例
-                        "seniority": "30.00" //年资
-                    } //下月度结转数据
-                },
-                errorCode: 0,
-            });
-    },
-    'POST /performance/carry/over': (req: any, res: any) => {
-        res.json({
-            code: "200",
-            message: "返回成功",
-            data: null
-        })
-    },
-    'POST /performance/carry/back': (req: any, res: any) => {
-        res.json({
-            code: "200",
-            message: "返回成功",
-            data: null
-        })
-    }
-};
+  'GET /performance/carry/list': (req: any, res: any) => {
+    res.json({
+      success: true,
+      data: {
+        preCarry: {
+          computeDate: `${Math.random()}`, //核算年月
+          dateRange: '2022/10/01-2022/10/31', //日期区间
+          personalRate: '10%', //人事成本比例
+          seniority: 30, //年资
+        }, //上月度结转数据
+        currentCarry: {
+          computeDate: `${Math.random()}`, //核算年月
+          dateRange: '2022/11/01-2022/11/31', //日期区间
+          personalRate: '10%', //人事成本比例
+          seniority: '30.00', //年资
+        }, //本月度结转数据
+        lastCarry: {
+          computeDate: `${Math.random()}`, //核算年月
+          dateRange: '2022/12/01-2022/12/31', //日期区间
+          personalRate: '10%', //人事成本比例
+          seniority: '30.00', //年资
+        }, //下月度结转数据
+      },
+      errorCode: 0,
+    });
+  },
+  'POST /performance/carry/over': (req: any, res: any) => {
+    res.json({
+      code: '200',
+      message: '返回成功',
+      data: null,
+    });
+  },
+  'POST /performance/carry/back': (req: any, res: any) => {
+    res.json({
+      code: '200',
+      message: '返回成功',
+      data: null,
+    });
+  },
+};

binární
mock/关闭@2x.png


+ 2 - 1
package.json

@@ -4,7 +4,7 @@
   "author": "code4eat <awesomedema@gmail.com>",
   "scripts": {
     "build": "max build",
-    "dev": "cross-env REACT_APP_ENV=dev MOCK=none max dev",
+    "dev": "cross-env REACT_APP_ENV=dev MOCK=true max dev",
     "format": "prettier --cache --write .",
     "postinstall": "max setup",
     "mock": "max dev",
@@ -22,6 +22,7 @@
     "lodash": "^4.17.21",
     "mathjs": "^11.6.0",
     "react-sortable-hoc": "^2.0.0",
+    "styled-components": "^6.1.17",
     "umi-request": "^1.4.0",
     "xlsx": "^0.18.5"
   },

binární
src/.DS_Store


+ 26 - 38
src/access.ts

@@ -7,11 +7,6 @@
  * @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
  */
 
-
-
-
-
-
 interface MenuItem {
   function: any;
   path: string;
@@ -21,59 +16,52 @@ interface MenuItem {
 
 function findMenuItem(menu: MenuItem[], path: string): MenuItem | undefined {
   for (let i = 0; i < menu.length; i++) {
-      if (menu[i].path === path) {
-          return menu[i];
-      } else if (menu[i].children) {
-          let found = findMenuItem(menu[i].children as MenuItem[], path);
-          if (found) return found;
-      }
+    if (menu[i].path === path) {
+      return menu[i];
+    } else if (menu[i].children) {
+      let found = findMenuItem(menu[i].children as MenuItem[], path);
+      if (found) return found;
+    }
   }
 }
 
-
-
-
-
-export default (initialState: { name: string;memuData:any[] }) => {
+export default (initialState: { name: string; memuData: any[] }) => {
   // 在这里按照初始化数据定义项目中的权限,统一管理
   // 参考文档 https://next.umijs.org/docs/max/access
-  
+
   //console.log({initialState});
 
-  const {memuData} = initialState;
-  
+  const { memuData } = initialState;
+
   const canSeeAdmin = !!(
     initialState && initialState.name !== 'dontHaveAccess'
   );
 
-  const whatCanIDoInThisPage = (pagePath:string)=>{
-       
-        if(memuData){
-          const thisPageData = findMenuItem(memuData,pagePath);
-          // console.log({pagePath,thisPageData});
-          if(thisPageData){
-            return thisPageData.function?thisPageData.function:[];
-          }
-          return [];
-        }
-
-  }
+  const whatCanIDoInThisPage = (pagePath: string) => {
+    if (memuData) {
+      const thisPageData = findMenuItem(memuData, pagePath);
+      // console.log({pagePath,thisPageData});
+      if (thisPageData) {
+        return thisPageData.function ? thisPageData.function : [];
+      }
+      return [];
+    }
+  };
 
-  const canIReadThisPage = (pagePath:string)=>{
-    if(memuData){
-      const thisPageData = findMenuItem(memuData,pagePath);
+  const canIReadThisPage = (pagePath: string) => {
+    if (memuData) {
+      const thisPageData = findMenuItem(memuData, pagePath);
       // console.log({pagePath,thisPageData});
-      if(thisPageData){
+      if (thisPageData) {
         return true;
       }
       return false;
     }
-  }
- 
+  };
 
   return {
     canSeeAdmin,
     whatCanIDoInThisPage,
-    canIReadThisPage
+    canIReadThisPage,
   };
 };

+ 187 - 134
src/app.tsx

@@ -2,7 +2,7 @@
  * @Author: code4eat awesomedema@gmail.com
  * @Date: 2022-12-14 14:14:32
  * @LastEditors: code4eat awesomedema@gmail.com
- * @LastEditTime: 2023-09-26 16:56:00
+ * @LastEditTime: 2025-07-24 11:19:03
  * @FilePath: /BudgetManaSystem/src/app.ts
  * @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
  */
@@ -11,10 +11,7 @@
 // 全局初始化数据配置,用于 Layout 用户信息和权限初始化
 // 更多信息见文档:https://next.umijs.org/docs/api/runtime-config#getinitialstate
 
-
-
-
-import { AxiosResponse } from '@umijs/max';
+import { AxiosResponse, history } from '@umijs/max';
 import { message, notification } from 'antd';
 import type { RequestConfig } from 'umi';
 
@@ -23,23 +20,19 @@ import iconEnum from './menuIcons.js';
 // import Report from './pages/reports/index';
 // import Static from './pages/static/index';
 
-
 import DevicePixelRatio from './utils/devicePixelRatio.js';
 
 import Icon, { createFromIconfontCN, FolderOutlined } from '@ant-design/icons';
 import { getPlatformMenu } from '@/services/auth';
-import { useState } from 'react';
-
+import { Key, useState } from 'react';
 
-import './utils/zhongtaiC'
+import './utils/zhongtaiC';
 import { RuntimeAntdConfig } from '@umijs/max';
 
-
 const IconFont = createFromIconfontCN({
   scriptUrl: '',
 });
 
-
 // 错误处理方案: 错误类型
 enum ErrorShowType {
   SILENT = 0,
@@ -59,22 +52,27 @@ interface ResponseStructure {
 }
 
 interface UserData {
-  name?: string,
-  ruleVersion?: string,
-  token?: string,
-  userId?: number,
-  youshuToken?: string
+  name?: string;
+  ruleVersion?: string;
+  token?: string;
+  userId?: number;
+  youshuToken?: string;
 }
 
-
-
 export async function getInitialState(): Promise<{
-  isCollapsed: boolean, spacicalPageParamsType?: any[], userData: UserData,memuData:any[]
+  isCollapsed: boolean;
+  spacicalPageParamsType?: any[];
+  userData: UserData;
+  memuData: any[];
 }> {
-
   new DevicePixelRatio().init();
 
-  return { isCollapsed: false, spacicalPageParamsType: [], userData: {},memuData:[] };
+  return {
+    isCollapsed: false,
+    spacicalPageParamsType: [],
+    userData: {},
+    memuData: [],
+  };
 }
 
 export const request: RequestConfig = {
@@ -148,18 +146,29 @@ export const request: RequestConfig = {
         message.error('Request error, please retry.');
       }
     },
-
   },
 
   // 请求拦截器
   requestInterceptors: [
     (config: RequestConfig) => {
-      // 拦截请求配置,进行个性化处理。
+      // 从 localStorage 获取 token
       const userData = localStorage.getItem('userData');
-      const { token = '' } = JSON.parse(userData as any);
-      return { ...config, url: `/gateway${config.url}`, headers: { token } }
-      // return { ...config }
-    }
+      const { token = '' } = JSON.parse(userData || '{}');
+
+      // 如果请求配置中提供了自定义前缀(比如 gatewayOverride),就使用它,否则默认 /gateway
+      const gatewayPrefix = (config as any).gatewayOverride || '/gateway';
+      // 删除自定义属性,防止被传递到服务器
+      if ((config as any).gatewayOverride) {
+        delete (config as any).gatewayOverride;
+      }
+
+      // 拼接 URL,并且合并 headers(注意如果 config.headers 中已有 token,也可以进行覆盖)
+      return {
+        ...config,
+        url: `${gatewayPrefix}${config.url}`,
+        headers: { ...config.headers, token },
+      };
+    },
   ],
 
   // 响应拦截器
@@ -167,74 +176,70 @@ export const request: RequestConfig = {
     (response: AxiosResponse) => {
       // 拦截响应数据,进行个性化处理
 
-      const { status, data: { success, status: code, errorMessage, data: respData }, config: { method } } = response;
+      const {
+        status,
+        data: { status: code, errorMessage, msg },
+        config: { method },
+      } = response;
 
       try {
-
         if (status == 200) {
           // 网络请求成功
 
           if (method == 'post') {
-
             if (code == 200) {
-
-              return response.data.data == null ? {
-                success: true,
-                data: true
-              } : response.data
-
+              return response.data.data == null
+                ? {
+                    success: true,
+                    data: true,
+                  }
+                : response.data;
             } else {
               notification.error({
                 message: '',
-                description: errorMessage,
+                description: errorMessage || msg,
                 placement: 'topRight',
-                icon: <></>
-              })
-              return false
+                icon: <></>,
+              });
+              return false;
             }
           } else {
             if (status != 200) {
               message.error('请求失败!');
-              return false
+              return false;
             } else {
               if (code == 200) {
                 //console.log({code,'response.data':response.data});
-                return response.data
+                return response.data;
               } else {
-
                 notification.error({
                   message: '',
-                  description: errorMessage,
+                  description: errorMessage || msg,
                   placement: 'topRight',
-                  icon: <></>
-                })
+                  icon: <></>,
+                });
                 return false;
               }
             }
-
           }
-
         } else {
-          return false
+          return false;
         }
-
       } catch (error) {
         console.log({ error });
       }
-    }
-  ]
+    },
+  ],
 };
 
-
-
 export type menuDataItemType = {
-  path: string,
-  name: string,
-  icon: any,
-  component?: string,
-  softUrl?: string, // 帆软url
-  children?: menuDataItemType[]
-}
+  path: string;
+  name: string;
+  icon: any;
+  component?: string;
+  softUrl?: string; // 帆软url
+  children?: menuDataItemType[];
+};
 
 // 将服务端获取的菜单 icon 字符串映射为对应的 icon Dom
 const mappingIcon = (menuData: menuDataItemType[]) => {
@@ -245,46 +250,49 @@ const mappingIcon = (menuData: menuDataItemType[]) => {
         name: '',
         icon: '',
         component: './404',
-      }
-    ]
+      },
+    ];
   }
-  const mappingMenu: menuDataItemType[] = menuData.map(item => ({
+  const mappingMenu: menuDataItemType[] = menuData.map((item) => ({
     ...item,
-    icon: item.icon && iconEnum[item.icon],
+    icon: item.icon && iconEnum[item.icon as keyof typeof iconEnum],
     children: item.children ? mappingIcon(item.children) : [],
   }));
   return mappingMenu;
 };
 
-
-
 const imgNode = (props: any) => {
-  return <IconFont type='icon-shouye' {...props} />
+  return <IconFont type="icon-shouye" {...props} />;
 };
-const fileIcon = (params: any) => {
-  return <IconFont type='icon-jixiaoguanli' />
+const fileIcon = (props: any) => {
+  return <IconFont type="icon-jixiaoguanli" {...props} />;
 };
 
-const setting = (params: any) => {
-  return <IconFont type='icon-xitongshezhi' />
+const setting = (props: any) => {
+  return <IconFont  type="icon-xitongshezhi" {...props} />;
 };
 
-const secondaryDistribute = (params: any) => {
-  return <IconFont type='icon-ercifenpei' />
+const secondaryDistribute = () => {
+  return <IconFont type="icon-ercifenpei" />;
 };
 
-const reportCheck = (params: any) => {
-  return <IconFont type='icon-baobiaochaxun' />
+const reportCheck = () => {
+  return <IconFont type="icon-baobiaochaxun" />;
 };
 
-const crosstabReport = (params: any) => {
-  return <IconFont type='icon-baobiaochaxun' />
+const checkConfig = (props: any) => {
+  return <IconFont type="icon-chengbenkuguanli" {...props} />;
 };
 
 //布局配置
 
-export const layout = ({ initialState, setInitialState }: { initialState: any, setInitialState: any }) => {
-
+export const layout = ({
+  initialState,
+  setInitialState,
+}: {
+  initialState: any;
+  setInitialState: any;
+}) => {
   const { isCollapsed } = initialState;
   const [openKeys, set_openKeys] = useState<string[]>([]);
   const [selectedKeys, set_selectedKeys] = useState<string[]>([]);
@@ -293,12 +301,10 @@ export const layout = ({ initialState, setInitialState }: { initialState: any, s
     setInitialState({ ...initialState, isCollapsed }).then();
   };
 
-
   return {
     menuHeaderRender: false,
     token: {
       sider: {
-
         colorMenuBackground: '#fff',
         colorTextMenuActive: '#3376FE',
         colorTextMenuSelected: '#3376FE',
@@ -306,26 +312,73 @@ export const layout = ({ initialState, setInitialState }: { initialState: any, s
         colorTextMenu: '#17181A',
         //colorBgMenuItemHover:'##f0f2f5',
         colorBgMenuItemSelected: '#F2F6FF',
-
+        colorTextMenuIcon: '#17181A',
+        colorTextMenuIconActive: '#3376FE',
+        colorTextMenuIconSelected: '#3376FE',
         // colorBgMenuItemCollapsedHover:'#f0f2f5',
         // //colorBgMenuItemCollapsedSelected:'blue'
-
-      }
-
+      },
     },
     siderWidth: 200,
-
+    menuProps: {
+      openKeys: [...openKeys],
+      selectedKeys: [...selectedKeys],
+      onSelect: ({ selectedKeys }: { selectedKeys: string[] }) => {
+        set_selectedKeys(selectedKeys);
+        localStorage.setItem('selectedKeys', JSON.stringify(selectedKeys));
+      },
+      onOpenChange: (keys: string[]) => {
+        set_openKeys([...keys]);
+        localStorage.setItem('openKeys', JSON.stringify(keys));
+      },
+    },
     menu: {
       locale: false,
       request: async () => {
         const userData = localStorage.getItem('userData');
         const currentSelectedTab = localStorage.getItem('currentSelectedTab');
         if (currentSelectedTab) {
-          const { menuId, path } = JSON.parse(currentSelectedTab);
+          const { menuId } = JSON.parse(currentSelectedTab);
           const systemId = menuId;
           const data: any[] = await getPlatformMenu(systemId);
-    
+
           if (data) {
+            const selectedKeys = localStorage.getItem('selectedKeys');
+            const openKeys = localStorage.getItem('openKeys');
+
+            if (selectedKeys && openKeys) {
+              const _selectedKeys = JSON.parse(selectedKeys);
+              const _openKeys = JSON.parse(openKeys);
+              set_openKeys(_openKeys);
+              set_selectedKeys(_selectedKeys);
+            } else {
+              if (data[0].children && data[0].children.length > 0) {
+                const childs = data[0].children;
+                set_openKeys([data[0].key]);
+                set_selectedKeys([childs[0].key]);
+                localStorage.setItem('openKeys', JSON.stringify([data[0].key]));
+                localStorage.setItem(
+                  'selectedKeys',
+                  JSON.stringify([childs[0].key]),
+                );
+                history.push(`${childs[0].path}`);
+              } else {
+                if (data[0]) {
+                  set_openKeys([data[0].key]);
+                  set_selectedKeys([data[0].key]);
+                  localStorage.setItem(
+                    'openKeys',
+                    JSON.stringify([data[0].key]),
+                  );
+                  localStorage.setItem(
+                    'selectedKeys',
+                    JSON.stringify([data[0].key]),
+                  );
+                  history.push(`${data[0].path}`);
+                }
+              }
+            }
+
             const getVFromTree = (data: any[], key: string) => {
               let result: any[] = [];
               function looper(data: any[], key: string) {
@@ -349,7 +402,6 @@ export const layout = ({ initialState, setInitialState }: { initialState: any, s
                         url: t['softUrl'],
                       });
                     }
-
                   }
                   if (t.children && t.children.length > 0) {
                     looper(t.children, key);
@@ -362,81 +414,82 @@ export const layout = ({ initialState, setInitialState }: { initialState: any, s
 
             const _menu = getVFromTree(data, 'contentType');
 
-            setInitialState((t: any) => ({ ...t, spacicalPageParamsType: _menu,memuData:data, userData: JSON.parse(userData as string) }));
-
-            //return mappingIcon(data);
-
-
+            setInitialState((t: any) => ({
+              ...t,
+              spacicalPageParamsType: _menu,
+              memuData: data,
+              userData: JSON.parse(userData as string),
+            }));
 
             function addIconToPath(node: any, paths: string[]) {
               if (paths.includes(node.path)) {
                 if (node.path == '/home') {
                   node.icon = <Icon component={imgNode} />;
                 }
-                if (node.path == '/budgetMana') {
+                if (node.path == '/baseInfoMana') {
                   node.icon = <Icon component={fileIcon} />;
                 }
                 if (node.path == '/setting') {
                   node.icon = <Icon component={setting} />;
                 }
-                if (node.path == '/reportCheck') {
-                  node.icon = <Icon component={setting} />;
-                }
-                if (node.path == '/secondaryDistribute') {
-                  node.icon = <Icon component={secondaryDistribute} />;
-                }
-                if (node.path == '/reportCheck') {
-                  node.icon = <Icon component={reportCheck} />;
-                }
-                if (node.path == '/crosstabReport') {
-                  node.icon = <Icon component={reportCheck} />;
+                if (node.path == '/checkConfig') {
+                  node.icon = (
+                    <svg viewBox="0 0 1024 1024" width="1em" height="1em" fill="currentColor">
+                      <path d="M781.970286 73.142857a36.571429 36.571429 0 0 1 32.694857 20.187429l182.857143 365.714285a36.571429 36.571429 0 0 1-32.694857 52.955429h-50.614858l83.382858 166.473143a36.571429 36.571429 0 0 1-32.768 52.955428H914.285714l83.236572 166.473143a36.571429 36.571429 0 0 1-32.694857 52.955429H59.172571a36.571429 36.571429 0 0 1-32.694857-52.955429L109.641143 731.428571h-50.468572a36.571429 36.571429 0 0 1-32.694857-52.955428L109.641143 512h-50.468572a36.571429 36.571429 0 0 1-32.694857-52.955429l182.857143-365.714285A36.571429 36.571429 0 0 1 242.029714 73.142857h539.940572z m50.395428 658.285714H191.414857l-73.069714 146.285715h787.236571l-73.216-146.285715z m0-219.428571H191.341714l-72.996571 146.285714h787.236571L832.365714 512z m-73.069714-365.714286H264.630857l-146.285714 292.571429h787.236571l-146.285714-292.571429z" />
+                    </svg>
+                  );
                 }
               }
               if (node.children) {
-                node.children.forEach((child: any) => addIconToPath(child, paths));
+                node.children.forEach((child: any) =>
+                  addIconToPath(child, paths),
+                );
               }
-
-              // node.icon = <FolderOutlined />;
-              // if (node.children) {
-              //   node.children.forEach((child: any) => addIconToPath(child, paths));
-              // }
             }
 
             data.forEach((item: any) => {
-              addIconToPath(item, ['/home', '/budgetMana', '/setting', '/reportCheck', '/secondaryDistribute','/crosstabReport']);
+              addIconToPath(item, ['/home', '/baseInfoMana', '/setting', '/checkConfig']);
             });
 
-            return data
+            return data;
           }
         }
-
       },
     },
-    onPageChange: (location: Location) => {
-      // console.log({location});
-      // const {pathname} = location;
-      // localStorage.setItem('currentPath',pathname);
-    },
+    onPageChange: () => {},
     collapsedButtonRender: () => {
       return (
-        <div style={{
-          position: 'absolute', zIndex: 10, right: 17, bottom: 12,
-          display: 'flex', justifyContent: 'center', alignItems: 'center',
-          cursor: 'pointer', width: 24, height: 24
-        }} onClick={
-          () => {
+        <div
+          style={{
+            position: 'absolute',
+            zIndex: 10,
+            right: 17,
+            bottom: 12,
+            display: 'flex',
+            justifyContent: 'center',
+            alignItems: 'center',
+            cursor: 'pointer',
+            width: 24,
+            height: 24,
+          }}
+          onClick={() => {
             onCollapse(isCollapsed ? false : true);
-          }
-        }><IconFont className='menuCollapseIcon' type={isCollapsed ? 'icon-celanzhankai' : 'icon-celanshouqi'} /></div>
-      )
+          }}
+        >
+          <IconFont
+            className="menuCollapseIcon"
+            type={isCollapsed ? 'icon-celanzhankai' : 'icon-celanshouqi'}
+          />
+        </div>
+      );
     },
     collapsed: isCollapsed,
     contentStyle: {
-      border: '16px solid #F7F9FC',
-      //height: '94.5vh',  //以去除顶部导航高度
-      borderRadius: '22px',
+      // // border: '16px solid #F7F9FC',
+      // //height: '94.5vh',  //以去除顶部导航高度
+      // borderRadius: '22px',
       background: '#F7F9FC',
-      height:'calc(100vh - 48px)'
+      height: 'calc(100vh - 48px)',
       // overflow:'scroll',
       // margin:'20px 20px'
     },

+ 29 - 0
src/assets/images/README.md

@@ -0,0 +1,29 @@
+# 图片资源文件夹说明
+
+本文件夹用于存放项目中使用的所有图片资源,按照以下结构组织:
+
+## 文件夹结构
+
+- `icons/` - 存放各种图标文件(如按钮图标、状态图标等)
+- `illustrations/` - 存放插图和装饰性图片
+- `backgrounds/` - 存放背景图片
+- `logos/` - 存放 Logo 和品牌相关图片
+- `common/` - 存放通用的图片资源
+
+## 使用规范
+
+1. **命名规范**:使用小写字母和下划线,如 `add_icon.png`、`user_avatar.jpg`
+2. **格式建议**:
+   - 图标优先使用 SVG 格式
+   - 照片使用 JPG 格式
+   - 需要透明背景的图片使用 PNG 格式
+3. **尺寸规范**:提供 @1x、@2x、@3x 等不同分辨率的图片以适配不同设备
+4. **文件大小**:尽量压缩图片大小,保持合理的文件体积
+
+## 引用方式
+
+```typescript
+// 在组件中引用图片
+import iconAdd from '@/assets/images/icons/add_icon.png';
+import logoMain from '@/assets/images/logos/main_logo.svg';
+```

+ 0 - 0
src/assets/images/backgrounds/.gitkeep


+ 0 - 0
src/assets/images/common/.gitkeep


+ 0 - 0
src/assets/images/icons/.gitkeep


+ 63 - 0
src/assets/images/icons/BaobiaotubiaoIcon.tsx

@@ -0,0 +1,63 @@
+import React from 'react';
+
+interface BaobiaotubiaoIconProps {
+  width?: number;
+  height?: number;
+  fill?: string;
+  className?: string;
+  style?: React.CSSProperties;
+}
+
+/**
+ * 报表图标组件 - 用于查核项管理页面
+ */
+const BaobiaotubiaoIcon: React.FC<BaobiaotubiaoIconProps> = ({
+  width = 16,
+  height = 16,
+  fill = '#3B82F6',
+  className,
+  style,
+}) => {
+  return (
+    <svg
+      width={width}
+      height={height}
+      viewBox="0 0 16 16"
+      fill="none"
+      xmlns="http://www.w3.org/2000/svg"
+      className={className}
+      style={style}
+    >
+      {/* 背景圆角矩形 */}
+      <rect x="2" y="2" width="12" height="12" rx="2" fill={fill} />
+
+      {/* 报表图标 - 表格线条 */}
+      <rect x="4" y="4" width="8" height="1" rx="0.5" fill="white" />
+      <rect x="4" y="6" width="8" height="1" rx="0.5" fill="white" />
+      <rect x="4" y="8" width="8" height="1" rx="0.5" fill="white" />
+      <rect x="4" y="10" width="8" height="1" rx="0.5" fill="white" />
+
+      {/* 垂直分割线 */}
+      <rect
+        x="7"
+        y="4"
+        width="1"
+        height="8"
+        rx="0.5"
+        fill="white"
+        opacity="0.6"
+      />
+      <rect
+        x="10"
+        y="4"
+        width="1"
+        height="8"
+        rx="0.5"
+        fill="white"
+        opacity="0.6"
+      />
+    </svg>
+  );
+};
+
+export default BaobiaotubiaoIcon;

binární
src/assets/images/icons/baobiaotubiao.png


+ 14 - 0
src/assets/images/icons/baobiaotubiao.svg

@@ -0,0 +1,14 @@
+<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
+  <!-- 背景圆角矩形 -->
+  <rect x="2" y="2" width="12" height="12" rx="2" fill="#3B82F6"/>
+  
+  <!-- 报表图标 - 表格线条 -->
+  <rect x="4" y="4" width="8" height="1" rx="0.5" fill="white"/>
+  <rect x="4" y="6" width="8" height="1" rx="0.5" fill="white"/>
+  <rect x="4" y="8" width="8" height="1" rx="0.5" fill="white"/>
+  <rect x="4" y="10" width="8" height="1" rx="0.5" fill="white"/>
+  
+  <!-- 垂直分割线 -->
+  <rect x="7" y="4" width="1" height="8" rx="0.5" fill="white" opacity="0.6"/>
+  <rect x="10" y="4" width="1" height="8" rx="0.5" fill="white" opacity="0.6"/>
+</svg> 

binární
src/assets/images/icons/chahe.png


binární
src/assets/images/icons/fenzhi.png


binární
src/assets/images/icons/guanbi.png


+ 5 - 0
src/assets/images/icons/index.ts

@@ -0,0 +1,5 @@
+// 图标组件统一导出文件
+export { default as BaobiaotubiaoIcon } from './BaobiaotubiaoIcon';
+
+// 如果有其他图标组件,可以在这里继续添加
+// export { default as OtherIcon } from './OtherIcon';

binární
src/assets/images/icons/leixing.png


binární
src/assets/images/icons/shangyiye.png


binární
src/assets/images/icons/tongji.png


binární
src/assets/images/icons/xiayiye.png


+ 0 - 0
src/assets/images/illustrations/.gitkeep


+ 0 - 0
src/assets/images/logos/.gitkeep


+ 19 - 0
src/components/KCIMDrawerForm/index.tsx

@@ -0,0 +1,19 @@
+/*
+ * @Author: code4eat awesomedema@gmail.com
+ * @Date: 2023-10-07 10:34:43
+ * @LastEditors: code4eat awesomedema@gmail.com
+ * @LastEditTime: 2023-10-07 10:48:53
+ * @FilePath: /pfmBackMana/src/components/KCIMDrawerForm/index.tsx
+ * @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
+ */
+import { DrawerForm, DrawerFormProps } from '@ant-design/pro-components';
+
+import './style.less';
+
+const KCIMDrawerForm: React.FC<DrawerFormProps> = (props) => {
+  // 在这里可以添加任何自定义逻辑或样式
+
+  return <DrawerForm {...props} />;
+};
+
+export default KCIMDrawerForm;

+ 17 - 0
src/components/KCIMDrawerForm/style.less

@@ -0,0 +1,17 @@
+.cost-ant-drawer {
+  .cost-ant-drawer-content-wrapper {
+    .cost-ant-drawer-content {
+      .cost-ant-drawer-wrapper-body {
+        .cost-ant-drawer-footer {
+          .cost-ant-space-item {
+            .cost-ant-btn {
+              height: 24px !important;
+              padding: 0 11px;
+              border-radius: 4px !important;
+            }
+          }
+        }
+      }
+    }
+  }
+}

+ 24 - 29
src/components/KCIMModalForm/index.tsx

@@ -6,37 +6,32 @@
  * @FilePath: /BudgetManaSystem/src/components/BMSModalForm/index.tsx
  * @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
  */
-import { ModalForm, ModalFormProps } from "@ant-design/pro-components";
-import { FormLayout } from "antd/es/form/Form";
-import { useState } from "react";
-
-
-export type KCIMModalFormProps = ModalFormProps & {
-
-}
+import { ModalForm, ModalFormProps } from '@ant-design/pro-components';
+import { FormLayout } from 'antd/es/form/Form';
+import { useState } from 'react';
 
+export type KCIMModalFormProps = ModalFormProps & {};
 
 const LAYOUT_TYPE_HORIZONTAL = 'horizontal';
 
 const KCIMModalForm = (props: KCIMModalFormProps) => {
-
-    const { children, ...rest } = props;
-    const [formLayoutType, setFormLayoutType] = useState<FormLayout>('vertical');
-
-    const [grid, setGrid] = useState(true);
-
-    return (
-        <ModalForm {...rest}
-            layout={formLayoutType}
-            grid={grid}
-            rowProps={{
-                gutter: [16, formLayoutType === 'inline' ? 16 : 0],
-            }}
-        >
-            {children}
-        </ModalForm>
-    )
-}
-
-
-export default KCIMModalForm
+  const { children, ...rest } = props;
+  const [formLayoutType, setFormLayoutType] = useState<FormLayout>('vertical');
+
+  const [grid, setGrid] = useState(true);
+
+  return (
+    <ModalForm
+      {...rest}
+      layout={formLayoutType}
+      grid={grid}
+      rowProps={{
+        gutter: [16, formLayoutType === 'inline' ? 16 : 0],
+      }}
+    >
+      {children}
+    </ModalForm>
+  );
+};
+
+export default KCIMModalForm;

+ 50 - 36
src/components/KCIMPageContainer/index.tsx

@@ -12,16 +12,13 @@ import { PageContainer, PageContainerProps } from '@ant-design/pro-components';
 import { useModel } from '@umijs/max';
 import { useState } from 'react';
 import './style.less';
-import '../../utils/zhongtaiA'
+import '../../utils/zhongtaiA';
 
 const IconFont = createFromIconfontCN({
   scriptUrl: '',
 });
 
-
-export type KCIMPagecontainerPropType = PageContainerProps & {
-
-}
+export type KCIMPagecontainerPropType = PageContainerProps & {};
 
 interface MenuItem {
   function: any;
@@ -43,59 +40,76 @@ function findMenuItem(menu: MenuItem[], path: string): MenuItem | undefined {
 }
 
 const KCIMPagecontainer = (props: KCIMPagecontainerPropType) => {
-
   const { children, header, ...rest } = props;
 
   const { initialState } = useModel('@@initialState');
 
-  const [open,set_open] = useState(false);
+  const [open, set_open] = useState(false);
 
   let description: undefined | string = undefined;
 
   if (initialState) {
     const thisPageData = findMenuItem(initialState.memuData, location.pathname);
-    description = thisPageData?.description
+    description = thisPageData?.description;
   }
 
-
   return (
-    <PageContainer {...rest}
+    <PageContainer
+      {...rest}
       header={{
         ...header,
         breadcrumb: {},
-        style: { padding: 0, },
-        childrenContentStyle: { padding: 0 }
+        style: { padding: 0 },
+        childrenContentStyle: { padding: 0 },
       }}
     >
       {children}
-      {
-        description && (
-          <div className={open?'pageHelp active':'pageHelp'} style={{ position: 'fixed', bottom: 24, right: 24, zIndex: 999, width: 40, height: 40 }}
-             onClick={(e)=>{ 
-              e.stopPropagation();             
-               set_open(!open)
-            }}
+      {description && (
+        <div
+          className={open ? 'pageHelp active' : 'pageHelp'}
+          style={{
+            position: 'fixed',
+            bottom: 24,
+            right: 24,
+            zIndex: 999,
+            width: 40,
+            height: 40,
+          }}
+          onClick={(e) => {
+            e.stopPropagation();
+            set_open(!open);
+          }}
+        >
+          <div
+            className={open ? 'helpContent open' : 'helpContent'}
+            onClick={(e) => e.stopPropagation()}
           >
-            <div className={open?'helpContent open':'helpContent'} onClick={(e)=>e.stopPropagation()}>
-              <div className='contentHeader'>
-                帮助中心
-                <div className='closeBtn' onClick={()=>set_open(false)}><IconFont type="iconquxiao" /></div>
+            <div className="contentHeader">
+              帮助中心
+              <div className="closeBtn" onClick={() => set_open(false)}>
+                <IconFont type="iconquxiao" />
               </div>
-              <div className='content'>
-                <img src={require('../../../static/mingcijieshi.png')} alt="" />
-                <div className='container'>
-                  <div className='inner' dangerouslySetInnerHTML={{ __html: description ? description : '' }}></div>
-                </div>
+            </div>
+            <div className="content">
+              <img src={require('../../../static/mingcijieshi.png')} alt="" />
+              <div className="container">
+                <div
+                  className="inner"
+                  dangerouslySetInnerHTML={{
+                    __html: description ? description : '',
+                  }}
+                ></div>
               </div>
             </div>
-            <IconFont className='boolIcon' type="icon-bangzhuzhongxin-weixuanzhong" />
           </div>
-        )
-      }
-
+          <IconFont
+            className="boolIcon"
+            type="icon-bangzhuzhongxin-weixuanzhong"
+          />
+        </div>
+      )}
     </PageContainer>
-  )
-}
-
+  );
+};
 
-export default KCIMPagecontainer
+export default KCIMPagecontainer;

+ 110 - 109
src/components/KCIMPageContainer/style.less

@@ -1,132 +1,133 @@
-.pfm-ant-pro-page-container .pfm-ant-pro-page-container-warp-page-header~.pfm-ant-pro-grid-content .pfm-ant-pro-page-container-children-content {
-    padding-block: 0 !important;
+.cost-ant-pro-page-container
+  .cost-ant-pro-page-container-warp-page-header
+  ~ .cost-ant-pro-grid-content
+  .cost-ant-pro-page-container-children-content {
+  padding-block: 0 !important;
 }
 
-.pfm-ant-pro-page-container-children-content {
-    padding-inline: 0 !important;
-    padding-block: 0 !important;
-    padding: 0;
+.cost-ant-pro-page-container-children-content {
+  padding-inline: 0 !important;
+  padding-block: 0 !important;
 }
 
-
-.pfm-ant-pro-page-container-warp-page-header~.pfm-ant-pro-grid-content .pfm-ant-pro-page-container-children-content {
-    padding-top: 16px !important;
+.cost-ant-pro-page-container-warp-page-header
+  ~ .cost-ant-pro-grid-content
+  .cost-ant-pro-page-container-children-content {
+  padding-top: 16px !important;
 }
 
-
-
-.pfm-ant-page-header .pfm-ant-page-header-heading-left {
-    margin-block: 0;
+.cost-ant-page-header .cost-ant-page-header-heading-left {
+  margin-block: 0;
 }
 
-.pfm-ant-page-header .pfm-ant-page-header-heading-title {
-    font-size: 16px;
-    height: 21px;
-    font-family: SourceHanSansCN-Medium, SourceHanSansCN;
-    font-weight: 500;
-    color: #17181A;
-    line-height: 21px !important;
+.cost-ant-page-header .cost-ant-page-header-heading-title {
+  font-size: 16px;
+  height: 21px;
+  font-family: SourceHanSansCN-Medium, SourceHanSansCN;
+  font-weight: 500;
+  color: #17181a;
+  line-height: 21px !important;
 }
 
 .pageHelp {
-    position: relative;
-    display: flex;
-    justify-content: center;
-    align-items: center;
-    background: #FFFFFF;
-    box-shadow: 0px 4px 6px 0px rgba(64, 85, 128, 0.1);
+  position: relative;
+  display: flex;
+  justify-content: center;
+  align-items: center;
+  background: #ffffff;
+  box-shadow: 0px 4px 6px 0px rgba(64, 85, 128, 0.1);
+  border-radius: 8px;
+  border: 1px solid #e6eaf2;
+  cursor: pointer;
+
+  .helpContent {
+    position: absolute;
+    top: 0;
+    right: 0;
+    width: 0;
+    height: 0;
+    background: #ffffff;
+    box-shadow: 0px 8px 16px 0px rgba(64, 85, 128, 0.1);
     border-radius: 8px;
-    border: 1px solid #E6EAF2;
-    cursor: pointer;
-
-    .helpContent {
+    border: 1px solid #e6eaf2;
+    overflow: hidden;
+    transition: all 0.2s ease-in;
+
+    .contentHeader {
+      position: relative;
+      height: 40px;
+      background: #f7f9fc;
+      font-size: 16px;
+      font-weight: 500;
+      color: #17181a;
+      line-height: 40px;
+      padding-left: 16px;
+      border-bottom: 1px solid #e6eaf2;
+
+      .closeBtn {
+        display: flex;
+        justify-content: center;
+        align-items: center;
         position: absolute;
-        top:0;
-        right: 0;
-        width: 0;
-        height: 0;
-        background: #FFFFFF;
-        box-shadow: 0px 8px 16px 0px rgba(64, 85, 128, 0.1);
-        border-radius: 8px;
-        border: 1px solid #E6EAF2;
-        overflow: hidden;
-        transition: all 0.2s ease-in;
-
-        .contentHeader {
-            position: relative;
-            height: 40px;
-            background: #F7F9FC;
-            font-size: 16px;
-            font-weight: 500;
-            color: #17181A;
-            line-height: 40px;
-            padding-left: 16px;
-            border-bottom: 1px solid #E6EAF2;
+        width: 20px;
+        height: 20px;
+        right: 12px;
+        top: 12px;
 
-            .closeBtn {
-                display: flex;
-                justify-content: center;
-                align-items: center;
-                position: absolute;
-                width: 20px;
-                height: 20px;
-                right: 12px;
-                top:12px;
-
-                .anticon {
-                    font-size: 16px;
-                    color:#17181A
-                }
-            }
-        }
-        .content {
-            padding:16px;
-            img {
-                 width: 288px;
-                 height: 80px;
-                 margin-bottom: 16px;
-            }
-            .container {
-                 height: 430px;
-                 overflow-y: scroll;
-                 .inner {
-                    font-size: 12px;
-                    font-weight: 400;
-                    color: #525966;
-                    line-height: 16px;
-                    h1 {
-                        margin-bottom: 4px;
-                    }
-                    p {
-                        line-height: 16px;
-                    }
-                 }
-            }
+        .anticon {
+          font-size: 16px;
+          color: #17181a;
         }
-
-        &.open {
-         width: 320px;
-         height: 600px;
-         top: -616px;
+      }
+    }
+    .content {
+      padding: 16px;
+      img {
+        width: 288px;
+        height: 80px;
+        margin-bottom: 16px;
+      }
+      .container {
+        height: 430px;
+        overflow-y: scroll;
+        .inner {
+          font-size: 12px;
+          font-weight: 400;
+          color: #525966;
+          line-height: 16px;
+          h1 {
+            margin-bottom: 4px;
+          }
+          p {
+            line-height: 16px;
+          }
         }
+      }
     }
 
-    .anticon {
-        font-size: 27px;
-        color: #6B7A99;
+    &.open {
+      width: 320px;
+      height: 600px;
+      top: -616px;
     }
+  }
 
-    &.active {
-        .anticon {
-            font-size: 27px;
-            color: #17181A;
-        }
+  .anticon {
+    font-size: 27px;
+    color: #6b7a99;
+  }
+
+  &.active {
+    .anticon {
+      font-size: 27px;
+      color: #17181a;
     }
+  }
 
-    &:hover {
-        .anticon {
-            font-size: 27px;
-            color: #17181A;
-        }
+  &:hover {
+    .anticon {
+      font-size: 27px;
+      color: #17181a;
     }
-}
+  }
+}

+ 46 - 45
src/components/KCIMTable/index.tsx

@@ -2,60 +2,61 @@
  * @Author: code4eat awesomedema@gmail.com
  * @Date: 2022-12-20 15:24:11
  * @LastEditors: code4eat awesomedema@gmail.com
- * @LastEditTime: 2023-09-26 10:47:55
+ * @LastEditTime: 2025-07-09 14:21:59
  * @FilePath: /BudgetManaSystem/src/components/BMSTable/index.tsx
  * @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
  */
-import { deepEqual } from "@/utils/tooljs"
-import { ProTable, ProTableProps } from "@ant-design/pro-components"
-import { useEffect, useState } from "react"
+import { deepEqual } from '@/utils/tooljs';
+import { ProTable, ProTableProps } from '@ant-design/pro-components';
+import { useEffect, useState } from 'react';
 
-import './style.less'
+// import './style.less';
+import './rewrite.less';
 
-export type KCIMTablePropsType<T, U, ValueType> = ProTableProps<T, U, ValueType> & {
-    
-}
+export type KCIMTablePropsType<T, U, ValueType> = ProTableProps<
+  T,
+  U,
+  ValueType
+> & {};
 
 export const KCIMTablePageDefaultConfig = {
-    defaultPageSize:10
-}
+  defaultPageSize: 10,
+};
 
 export const KCIMTable = (props: KCIMTablePropsType<any, any, any>) => {
+  const { params, options = false, request, ...rest } = props;
+  const [prevParams, set_prevParams] = useState<any>(undefined);
 
-    const { params,options = false,...rest } = props;
-    // let prevParams:any = undefined;
-    const [prevParams,set_prevParams] = useState<any>(undefined);
+  useEffect(() => {
+    // console.log({'deepEqual(prevParams,params)':deepEqual(prevParams,params)});
+    // console.log({prevParams,params});
 
-    useEffect(()=>{
-        // console.log({'deepEqual(prevParams,params)':deepEqual(prevParams,params)});
-        // console.log({prevParams,params});
+    if (deepEqual(prevParams, params)) {
+    } else {
+    }
+    set_prevParams(params);
+  }, [params]);
 
-        if(deepEqual(prevParams,params)){
-            
-        }else{
-              
-        }
-        set_prevParams(params);
-    },[params])
-    
-    return (
-        <ProTable
-            tableClassName="KCIMTable"
-            toolBarRender={false}
-            options={options}
-            pagination={{
-                   showSizeChanger:true,
-                   ...KCIMTablePageDefaultConfig
-            }}
-            //locale={{emptyText:'暂无数据'}}
-            params={{...params}}
-            bordered={false}
-            search={false}
-            tableStyle={{
-                border:'1px solid #DAE2F2',
-                borderRadius:'8px'
-            }}
-            {...rest} 
-        />
-    )
-}
+  return (
+    <ProTable
+      tableClassName="KCIMTable"
+      toolBarRender={false}
+      options={options}
+      pagination={{
+        showSizeChanger: true,
+        ...KCIMTablePageDefaultConfig,
+      }}
+      //locale={{emptyText:'暂无数据'}}
+      params={{ ...params }}
+      bordered={false}
+      search={false}
+      tableStyle={{}}
+      request={
+        request
+          ? (params, sort, filter) => request({ ...params }, sort, filter)
+          : undefined
+      }
+      {...rest}
+    />
+  );
+};

+ 134 - 0
src/components/KCIMTable/rewrite.less

@@ -0,0 +1,134 @@
+/* KCIMTable 自定义样式重写 */
+.KCIMTable {
+  /* 直接为KCIMTable组件添加边框 */
+  border: 1px solid #DAE2F2 !important;
+  border-radius: 8px !important;
+  overflow: hidden;
+  
+  /* 确保最外层容器有边框 - 针对ProTable组件 */
+  &.ant-pro-table,
+  .ant-pro-table {
+    border: 1px solid #DAE2F2 !important;
+    border-radius: 8px !important;
+    overflow: hidden;
+  }
+  /* 通用样式 - 表格行高设置为40px */
+  .pfm-ant-table-tbody > tr:not(.pfm-ant-table-measure-row) > td {
+    height: 40px !important;
+    padding: 8px 16px !important;
+  }
+
+  
+  /* 通用样式 - 表头行高设置为40px */
+  .pfm-ant-table-thead > tr > th {
+    height: 40px !important;
+    padding: 8px 16px !important;
+    background: #EEF3FA !important; /* 表头背景色 */
+    color: #333 !important;
+    font-weight: 500 !important;
+    border-right: none !important; /* 移除表头列间的竖直分隔线 */
+  }
+  
+  /* 通用样式 - 整个表格组件最外层边框(包含分页) */
+  .ant-table-wrapper,
+  .pfm-ant-table-wrapper {
+    border: 1px solid #DAE2F2 !important;
+    border-radius: 8px !important;
+    overflow: hidden;
+  }
+  
+  /* 移除内层容器的边框,避免重复 */
+  .pfm-ant-table-container {
+    border: none !important;
+    border-radius: 0 !important;
+  }
+  
+  /* 分页组件样式 */
+  .ant-pagination,
+  .pfm-ant-pagination {
+    height: 40px !important;
+    padding: 8px 16px !important;
+    margin: 0 !important;
+    border-top: 1px solid #DAE2F2 !important;
+    background: #fff !important;
+    display: flex !important;
+    align-items: center !important;
+    justify-content: flex-end !important;
+  }
+  
+  /* 通用样式 - 悬停效果保持 */
+  .pfm-ant-table-tbody > tr:hover > td {
+    background-color: #f8f9fa !important;
+  }
+}
+
+/* bordered模式 - 完整边框 */
+.KCIMTable .pfm-ant-table-bordered {
+  /* 表头边框设置 */
+  .pfm-ant-table-thead > tr > th {
+    border-bottom: 1px solid #DAE2F2 !important;
+    border-right: 1px solid #DAE2F2 !important;
+  }
+  
+  /* 表头最后一列移除右边框 */
+  .pfm-ant-table-thead > tr > th:last-child {
+    border-right: none !important;
+  }
+  
+  /* 表体所有单元格的边框 */
+  .pfm-ant-table-tbody > tr:not(.pfm-ant-table-measure-row) > td {
+    border-bottom: 1px solid #DAE2F2 !important;
+    border-right: 1px solid #DAE2F2 !important;
+  }
+  
+  /* 表体最后一列移除右边框 - 但保留包含_item_格式data-row-key的行的右边框 */
+  .pfm-ant-table-tbody > tr:not(.pfm-ant-table-measure-row):not([data-row-key*="_item_"]) > td:last-child {
+    border-right: none !important;
+  }
+  
+  /* 移除最后一行的底边框 */
+  .pfm-ant-table-tbody > tr:last-child > td {
+    border-bottom: none !important;
+  }
+  
+  /* 移除表头第一行的顶边框 */
+  .pfm-ant-table-thead > tr:first-child > th {
+    border-top: none !important;
+  }
+}
+
+/* 非bordered模式 - 只有上下边框 */
+.KCIMTable .pfm-ant-table:not(.pfm-ant-table-bordered) {
+  /* 只保留上下边框,移除左右边框 */
+  .pfm-ant-table-thead > tr > th,
+  .pfm-ant-table-tbody > tr:not(.pfm-ant-table-measure-row) > td {
+    border-left: none !important;
+    border-right: none !important;
+    border-bottom: 1px solid #DAE2F2 !important;
+  }
+  
+  /* 移除表头第一行的顶边框 */
+  .pfm-ant-table-thead > tr:first-child > th {
+    border-top: none !important;
+  }
+  
+  /* 移除最后一行的底边框 */
+  .pfm-ant-table-tbody > tr:last-child > td {
+    border-bottom: none !important;
+  }
+}
+
+/* 隐藏表头的列宽拖拽分隔线 */
+.pfm-ant-table-column-handle,
+.ant-table-column-handle {
+  display: none !important;
+}
+
+.pfm-ant-table-cell::before {
+  display: none !important;
+}
+
+.pfm-ant-table-thead > tr > th,
+.pfm-ant-table-tbody > tr > td {
+  text-align: left !important;
+}

+ 100 - 242
src/components/KCIMTable/style.less

@@ -1,261 +1,119 @@
-.pfm-ant-table-wrapper {
-    border-radius: 4px !important;
+/* KCIMTable 统一表格样式(仅保留 pfm 前缀) */
 
-    table {
-        border-radius: 4px !important;
-    }
-
-    .pfm-ant-table-container {
-        border-start-start-radius: 4px !important;
-        border-start-end-radius: 4px !important;
-    }
-
-    .pfm-ant-table-row-indent+.pfm-ant-table-row-expand-icon {
-        //margin-top: -2px !important;
-    }
+.pfm-ant-table-bordered {
+  /* 去掉外层 border,避免与 container 重复 */
+  border: none !important;
 }
 
-.pfm-ant-table .pfm-ant-table-header {
-    border-radius: 4px !important;
+/* 外框统一 */
+.pfm-ant-table-bordered .pfm-ant-table-container {
+  /* 统一外框,只保留这一层边框 */
+  border: 1px solid #dae2f2 !important;
+  border-right: none !important;
+  border-radius: 8px !important;
+  overflow: hidden;
+  /* 移除内层 table 可能残留的外边框,避免出现双线 */
+  table {
+    border: none !important;
+  }
 }
 
-.pfm-ant-pro-table .pfm-ant-pro-card .pfm-ant-pro-card-body {
-     padding-inline: 0 !important;
+/* 表头、表体单元格边框 */
+.pfm-ant-table-bordered thead > tr > th,
+.pfm-ant-table-bordered tbody > tr > td {
+  border-right: 1px solid #dae2f2 !important;
+  border-bottom: 1px solid #dae2f2 !important;
 }
 
-.pfm-ant-table-thead > tr > th {
-    padding: 8px 8px !important;
-    background: #eef3fa !important;
-
-    &::before {
-        display: none !important;
-    }
-}
-
-// .pfm-ant-table-wrapper .pfm-ant-table-tbody>tr>td {
-//     //padding: 8px 8px !important;
-//     border-top: 1px solid rgb(218 226 242) !important;
+// /* 最后一列、最后一行去掉多余边线 */
+// .pfm-ant-table-bordered thead > tr > th:last-child,
+// .pfm-ant-table-bordered tbody > tr > td:last-child {
+//   border-right: none !important;
 // }
 
-.pfm-ant-table-wrapper .pfm-ant-table-pagination.pfm-ant-pagination {
-
-    padding-right: 16px;
+.pfm-ant-table-bordered tbody > tr:last-child > td {
+  border-bottom: none !important;
 }
 
-.pfm-ant-table-wrapper .pfm-ant-table.pfm-ant-table-bordered>.pfm-ant-table-container>.pfm-ant-table-body>table>tbody>tr>td {
-    //#dae2f2
-    border-inline-end: 1px solid #dae2f2 !important;
+.pfm-ant-table-bordered thead > tr:first-child > th {
+  border-top: none !important;
 }
 
-// .pfm-ant-table-wrapper .pfm-ant-table.pfm-ant-table-bordered>.pfm-ant-table-container>.pfm-ant-table-header>table>thead>tr {
-
-//     &:last-child {
-//         &>th {
-//             border-top: 1px solid #dae2f2 !important;
-//         }
-//     }
-// }
-
-.KCIMTable {
-    .pfm-ant-table {
-        .pfm-ant-space-align-center {
-            gap: 0 !important;
-        }
-
-        .pfm-ant-table-thead {
-            .pfm-ant-table-cell {
-                border-bottom: none !important;
-                padding: 8px 8px !important;
-                //border-top:1px solid #dae2f2 !important ;
-                background: rgb(238 243 250 / 100%) !important;
-                //border-right: 1px solid  #dae2f2 !important;
-
-                a {
-                    color: #3376FE;
-                }
-
-                .pfm-ant-table-selection {
-                    .pfm-ant-table-selection-extra {
-                        top: 5px;
-                    }
-                }
-
-                &::before {
-                    display: none;
-                }
-
-                &:hover {
-                    background: rgb(238 243 250 / 100%) !important;
-                }
-
-                &:last-child {
-                    border-right: none;
-                }
-            }
-
-            .pfm-ant-table-cell-fix-right {
-                &:last-child {
-                    right: 0 !important;
-                }
-            }
-        }
-
-        .pfm-ant-table-tbody {
-            &>tr {
-                &.pfm-ant-table-measure-row {
-                    border: none !important;
-                    &>td {
-                        border-inline-end:none !important;
-                    }
-                }
-
-                &.pfm-ant-table-row {
-                    .pfm-ant-table-cell {
-                    
-                        font-size: 14px;
-                        font-family: SourceHanSansCN-Medium, SourceHanSansCN;
-                        
-                        color: #17181A;
-                        padding: 8px 8px !important; 
-                        border-bottom: none !important;
-                        border-top: 1px solid rgb(218 226 242) !important;
-
-                        &.pfm-ant-table-cell-with-append {
-                            .pfm-ant-table-row-expand-icon {
-                                  border-radius: 4px !important;
-                                  border: 1px solid #DAE2F2 !important;
-
-                                  &:hover {
-                                    color: #3377ff;
-                                    border: 1px solid #3377ff !important;
-                                  }
-                            }
-                        }
-    
-                        &>.pfm-ant-typography {
-                            line-height:unset;
-                        }
-    
-                        &.pfm-ant-table-cell-with-append {
-                            .pfm-ant-table-row-expand-icon {
-                                 
-                            }
-                        }
-    
-                        // &:hover {
-                        //     border-radius: 0 !important;
-                        //     background: #FAFBFC !important;
-    
-                        // }
-                    }
-                }
-
-
-
-                &.pfm-ant-table-row-selected {
-                    .pfm-ant-table-cell {
-                        border-radius: 0 !important;
-                    }
-                    &:hover {
-                        background: #bae0ff !important;
-                    }
-                }
-
-               
-
-                // &:nth-child(2) {
-                //     .pfm-ant-table-cell {
-                //         border-top: none !important;
-                //     }
-                // }
-
-                &:last-child>td {
-                    border-bottom: none !important;
-                }
-            }
-        }
-
-        .pfm-ant-table-summary {
-            &>tr>td {
-                text-align: left !important;
-                padding: 12px 8px !important;
-                border-top: 1px solid #dae2f2 !important;
-            }
-        }
-
-       
-
-        &.pfm-ant-table-bordered {
-            .pfm-ant-table-container {
-                border-left: none !important;
-                .pfm-ant-table-header {
-                    table {
-                        border-top: none !important;
-                    }
-                    .pfm-ant-table-thead {
-                        &>tr {
-                            &>th {
-                                border-right: 1px solid #dae2f2 !important;
-                                border-bottom: 1px solid #dae2f2 !important;
-                                border-top: none !important;
-    
-                                &.pfm-ant-table-cell-scrollbar {
-                                    border: none !important;
-                                }
+/* 非 bordered 表格:去掉外框及占位行底边,并清理首列/尾列外边线 */
+.KCIMTable .pfm-ant-table:not(.pfm-ant-table-bordered) {
+  /* 外框 */
+  .pfm-ant-table-container {
+    border: 1px solid #dae2f2 !important;
+    border-radius: 8px !important;
+  }
+
+  /* 空数据占位行 */
+  .pfm-ant-table-placeholder > td {
+    border: none !important;
+  }
+
+  /* 行横向分隔线 */
+  thead > tr > th,
+  tbody > tr > td {
+    border-right: none !important;
+    border-left: none !important;
+    border-bottom: 1px solid #dae2f2 !important;
+  }
+
+  /* 表头顶边留给外框;底边已由上述横线提供 */
+  thead > tr > th {
+    border-top: none !important;
+  }
+
+  /* 最后一行去掉底横线,避免与外框重叠 */
+  tbody > tr:last-child > td {
+    border-bottom: none !important;
+  }
+
+  /* 首列保持无左边框,外框自身即为左边框 */
+}
 
-                                &::after {
-                                    border: none !important;
-                                }
-                            }
-    
-                            &:last-child {
-                                &>th {
-                                    border-top:none !important;
-                                }
-                            }
-                        }
-                    }
-                }
-                .pfm-ant-table-body {
-                    table > tbody > tr > td {
-                        border-right: 1px solid #dae2f2 !important;
-                        &::after {
-                            border: none !important;
-                        }
-                    }
-                }
-                
-            }
-        }
+/* 通用表头及单元格对齐 */
+.KCIMTable .pfm-ant-table {
+  .pfm-ant-table-thead > tr > th {
+    background: #f5f7fa !important;
+    color: #333 !important;
+    font-weight: 500 !important;
+    text-align: left !important;
+  }
+  .pfm-ant-table-tbody > tr > td {
+    text-align: left !important;
+    &:first-child {
+      padding-left: 8px !important;
     }
-    .pfm-ant-pagination {
-        padding: 7px 0;
-        margin: 0 !important;
-        border-top: 1px solid #dae2f2 !important;
+  }
+}
 
-        .pfm-ant-pagination-item-active {
-            font-weight: 600;
-            background-color: #37F !important;
-            border-color: #37F !important;
-        }
+/* 针对rowSpan合并单元格导致的边框断裂,补齐竖线,避免多重边框 */
+.ant-table-wrapper {
+  /* 只针对KCIMTable生效,避免影响全局 */
+  .ant-table {
+    /* 只补左右竖线,去掉上下边框,避免多重边框 */
+    .ant-table-tbody > tr > td[rowspan='0'] {
+      border-left: 1px solid #e5e6eb !important;
+      border-right: 1px solid #e5e6eb !important;
+      border-top: none !important;
+      border-bottom: none !important;
+      background: #fff;
+      padding: 0 !important;
+      height: 0 !important;
+      min-width: 0 !important;
+      max-width: 0 !important;
+      line-height: 0 !important;
+      font-size: 0 !important;
     }
-
-    .pfm-ant-pagination .pfm-ant-pagination-item-active a {
-        color: #fff !important;
+    /* 首列补齐左边框 */
+    .ant-table-tbody > tr > td[rowspan='0']:first-child {
+      border-left: 1px solid #e5e6eb !important;
     }
-
-
-}
-
-.pfm-ant-table-wrapper .pfm-ant-table-container table>thead>tr:first-child th:first-child {
-    border-start-start-radius: 4px !important;
-}
-
-.pfm-ant-table-wrapper .pfm-ant-table-container table>thead>tr:first-child th:last-child {
-    border-start-end-radius: 4px !important;
+    /* 末列补齐右边框 */
+    .ant-table-tbody > tr > td[rowspan='0']:last-child {
+      border-right: 1px solid #e5e6eb !important;
+    }
+  }
 }
-
-.pfm-ant-empty-normal {
-    margin-block: 80px !important;
-    color: rgb(0 0 0 / 25%);
-}

+ 59 - 52
src/components/KCIMUpload/index.tsx

@@ -7,8 +7,6 @@
  * @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
  */
 
-
-
 import React, { useState } from 'react';
 import { InboxOutlined } from '@ant-design/icons';
 import { Alert, UploadFile, UploadProps } from 'antd';
@@ -19,60 +17,69 @@ import './style.less';
 const { Dragger } = Upload;
 
 type KCIMUploadPropsType = {
-    onChange?: () => void;
-    ifShowTip?: boolean;  //是否展示黄底提示
-    ifShowTemplateDownload?: boolean;//是否展示模板下载
-    downloadTemplateFile?: () => void; //模板下载回调
-} & UploadProps
+  onChange?: () => void;
+  ifShowTip?: boolean; //是否展示黄底提示
+  ifShowTemplateDownload?: boolean; //是否展示模板下载
+  downloadTemplateFile?: () => void; //模板下载回调
+} & UploadProps;
 
 const KCIMUpload = (props: KCIMUploadPropsType) => {
-    const [fileList, setFileList] = useState<UploadFile[]>([]);
-    const { onChange, downloadTemplateFile, ifShowTip = true, ifShowTemplateDownload = true } = props;
+  const [fileList, setFileList] = useState<UploadFile[]>([]);
+  const {
+    onChange,
+    downloadTemplateFile,
+    ifShowTip = true,
+    ifShowTemplateDownload = true,
+  } = props;
 
-    const config: UploadProps = {
-        action: () => Promise.resolve(''),
-        name: 'file',
-        multiple: false,
-        beforeUpload: (file) => {
-            setFileList([...fileList, file]);
-            return false;
-        },
-        //onChange:onChange?onChange:()=>{},
-        onChange: (fileInfo) => {
-            onChange && onChange(fileInfo);
-        },
-        onDrop(e) {
-            console.log('Dropped files', e.dataTransfer.files);
-        },
-    };
+  const config: UploadProps = {
+    action: () => Promise.resolve(''),
+    name: 'file',
+    multiple: false,
+    beforeUpload: (file) => {
+      setFileList([...fileList, file]);
+      return false;
+    },
+    //onChange:onChange?onChange:()=>{},
+    onChange: (fileInfo) => {
+      onChange && onChange(fileInfo);
+    },
+    onDrop(e) {
+      console.log('Dropped files', e.dataTransfer.files);
+    },
+  };
 
-    const download = () => {
-        downloadTemplateFile && downloadTemplateFile()
-    }
+  const download = () => {
+    downloadTemplateFile && downloadTemplateFile();
+  };
 
-    return (
-        <div className='KCIMUpload' style={{ paddingBottom: 16 }}>
-            {
-                ifShowTemplateDownload && (
-                    <div className='toolBar'>
-                        <span>文件下载</span>
-                        <span onClick={() => download()}>模板下载</span>
-                    </div>
-                )
-            }
-            {
-               ifShowTip&&(<Alert message="注意,导入的数据将会覆盖当前数据" type="warning" style={{ height: 24, fontSize: 12, marginBottom: 16, marginTop: 16 }} showIcon closable banner />) 
-            }
-            
-            <Dragger {...config} height={140}>
-                <p className="ant-upload-drag-icon" style={{ marginBottom: 5 }}>
-                    <InboxOutlined style={{ fontSize: 50, color: '#3376FE' }} />
-                </p>
-                <p className="ant-upload-text">点击或将文件拖拽到这里上传</p>
-
-            </Dragger>
+  return (
+    <div className="KCIMUpload" style={{ paddingBottom: 16 }}>
+      {ifShowTemplateDownload && (
+        <div className="toolBar">
+          <span>文件下载</span>
+          <span onClick={() => download()}>模板下载</span>
         </div>
-    )
-}
+      )}
+      {ifShowTip && (
+        <Alert
+          message="注意,导入的数据将会覆盖当前数据"
+          type="warning"
+          style={{ height: 24, fontSize: 12, marginBottom: 16, marginTop: 16 }}
+          showIcon
+          closable
+          banner
+        />
+      )}
+
+      <Dragger {...config} height={140}>
+        <p className="ant-upload-drag-icon" style={{ marginBottom: 5 }}>
+          <InboxOutlined style={{ fontSize: 50, color: '#3376FE' }} />
+        </p>
+        <p className="ant-upload-text">点击或将文件拖拽到这里上传</p>
+      </Dragger>
+    </div>
+  );
+};
 
-export default KCIMUpload;
+export default KCIMUpload;

+ 6 - 7
src/components/KCIMUpload/style.less

@@ -1,5 +1,5 @@
 .KCIMUpload {
-  .kcmp-ant-form-item {
+  .cost-ant-form-item {
     margin-bottom: 0 !important;
   }
 
@@ -11,20 +11,19 @@
     margin-bottom: 16px;
     margin-top: 16px;
 
-    &>span {
+    & > span {
       display: inline-block;
       height: 15px;
       font-size: 14px;
       font-family: SourceHanSansCN-Normal, SourceHanSansCN;
       font-weight: 400;
-      color: #17181A;
-      line-height:15px;
+      color: #17181a;
+      line-height: 15px;
 
       &:last-child {
-           cursor: pointer;
-           color: #3376FE;
+        cursor: pointer;
+        color: #3376fe;
       }
     }
   }
-  
 }

+ 506 - 511
src/global.less

@@ -1,322 +1,281 @@
 * {
-    font-family: 'SourceHanSansCN, SourceHanSansCN';
+  font-family: 'SourceHanSansCN, SourceHanSansCN';
 }
 
 /* Safari */
-@media screen and (-webkit-min-device-pixel-ratio:0)
-{
+@media screen and (-webkit-min-device-pixel-ratio: 0) {
   * {
     margin: 0;
     padding: 0;
-    font-family:system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif !important;
+    font-family: system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI',
+      Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue',
+      sans-serif !important;
   }
   #password {
     &::placeholder {
-      font-family:system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif !important;
+      font-family: system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI',
+        Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue',
+        sans-serif !important;
     }
   }
 }
 
 body {
-    margin: 0;
-    height: 100%;
-    zoom: unset !important;
+  margin: 0;
+  height: 100%;
+  zoom: unset !important;
 }
 
-
-
-
 .pfm-ant-space-item {
-    &>a {
-        color: #3377FF !important;
-    }
+  & > a {
+    color: #3377ff !important;
+  }
 }
 
-
-
 textarea {
-    border-radius: 4px !important;
-    border: 1px solid #CFD7E6 !important;
+  border-radius: 4px !important;
+  border: 1px solid #cfd7e6 !important;
 }
 
 .pfm-ant-btn-primary {
-    background: #3377FF;
+  background: #3377ff;
 }
-.pfm-ant-btn-primary[disabled]{
-    background:#f5f5f5 ;
+.pfm-ant-btn-primary[disabled] {
+  background: #f5f5f5;
 }
 
 .pfm-ant-checkbox {
-    &.pfm-ant-checkbox-checked {
-          .pfm-ant-checkbox-inner {
-            background-color:#3377FF;
-            background: #3377FF;
-          }
+  &.pfm-ant-checkbox-checked {
+    .pfm-ant-checkbox-inner {
+      background-color: #3377ff;
+      background: #3377ff;
     }
-    // &.pfm-ant-checkbox-indeterminate {
-    //     &.pfm-ant-checkbox-inner {
-    //         &::after {
-    //             background-color:#3377FF !important;
-    //         }
-    //     }
-    // }
+  }
+  // &.pfm-ant-checkbox-indeterminate {
+  //     &.pfm-ant-checkbox-inner {
+  //         &::after {
+  //             background-color:#3377FF !important;
+  //         }
+  //     }
+  // }
 }
 
 .pfm-ant-checkbox-indeterminate .pfm-ant-checkbox-inner::after {
-    background-color:#3377FF;
+  background-color: #3377ff;
 }
 
 .pfm-ant-input-affix-wrapper {
-    border-radius: 4px !important;
+  border-radius: 4px !important;
 }
 
 .pfm-ant-input {
-    border-radius: 4px;
-    // border: 1px solid #CFD7E6 !important;
-    &::placeholder {
-        color: #99A6BF !important;
-    }
+  border-radius: 4px;
+  // border: 1px solid #CFD7E6 !important;
+  &::placeholder {
+    color: #99a6bf !important;
+  }
 }
 
 .pfm-ant-table-cell {
-    a {
-        color: #3376FE !important;
-    }
+  a {
+    color: #3376fe !important;
+  }
 }
 
 .pfm-ant-input-number {
-    border-radius: 4px !important;
+  border-radius: 4px !important;
 }
 
+//Modal
 
+.pfm-ant-modal {
+  .pfm-ant-modal-content {
+    padding: 16px !important;
+    border-radius: 4px !important;
+    .pfm-ant-modal-header {
+      padding: 0;
+      margin-bottom: 16px;
+      border-bottom: none;
+    }
+    .pfm-ant-modal-body {
+      max-height: 570px;
+      // overflow-y: scroll;
+      // overflow-x: hidden;
+      padding: 0;
+
+      .pfm-ant-modal-confirm-body-wrapper {
+        .pfm-ant-modal-confirm-body {
+          .pfm-ant-modal-confirm-title {
+            font-size: 16px;
+            height: 18px;
+          }
+          .pfm-ant-modal-confirm-content {
+            max-width: 100% !important;
+            max-height: 498px;
+            overflow-y: scroll;
+            overflow-x: hidden;
+            margin-block-start: 16px;
+
+            .TableTransfer {
+              .pfm-ant-transfer-operation {
+                //   align-self: self-end;
+                //   margin-bottom: 30px;
+
+                button {
+                  //color: #17181A;
+                  width: 24px;
+                  height: 40px !important;
+                  // background: #FAFCFF;
+                  border-radius: 4px;
+                  border: 1px solid #dae2f2;
+                }
+              }
 
+              .pfm-ant-transfer-list {
+                overflow: hidden;
+                border: 1px solid #dae2f2;
 
+                .pfm-ant-transfer-list-header {
+                  height: 32px;
+                  font-size: 12px;
+                  border-bottom: 1px solid #dae2f2;
 
+                  .anticon {
+                    color: #99a6bf;
+                  }
 
-//Modal
+                  .pfm-ant-transfer-list-header-selected {
+                    color: #515866;
+                  }
+                }
 
-.pfm-ant-modal {
-    .pfm-ant-modal-content {
-        padding: 16px !important;
-        border-radius: 4px !important;
-        .pfm-ant-modal-header {
-             padding: 0 ;
-             margin-bottom: 16px;
-             border-bottom: none;
-        }
-        .pfm-ant-modal-body {
-            max-height: 570px;
-            // overflow-y: scroll;
-            // overflow-x: hidden;
-            padding: 0;
-
-            .pfm-ant-modal-confirm-body-wrapper {
-                .pfm-ant-modal-confirm-body {
-                    .pfm-ant-modal-confirm-title {
-                        font-size: 16px;
-                        height:18px;
-                      
-                    }
-                    .pfm-ant-modal-confirm-content {
-                        max-width: 100% !important;
-                        max-height: 498px;
-                        overflow-y: scroll;
-                        overflow-x: hidden;
-                        margin-block-start:16px;
-
-                        .TableTransfer {
-                            .pfm-ant-transfer-operation {
-                                //   align-self: self-end;
-                                //   margin-bottom: 30px;
-
-                                button {
-                                    //color: #17181A;
-                                    width: 24px;
-                                    height: 40px !important;
-                                    // background: #FAFCFF;
-                                    border-radius: 4px;
-                                    border: 1px solid #DAE2F2;
-                                }
-                            }
-
-                            .pfm-ant-transfer-list {
-                                overflow: hidden;
-                                border: 1px solid #DAE2F2;
-
-                                .pfm-ant-transfer-list-header {
-                                    height: 32px;
-                                    font-size: 12px;
-                                    border-bottom: 1px solid #DAE2F2;
-
-                                    .anticon {
-                                        color: #99A6BF;
-                                    }
-
-                                    .pfm-ant-transfer-list-header-selected {
-                                        color: #515866;
-                                    }
-                                }
-
-                                .pfm-ant-transfer-list-body {
-                                    .pfm-ant-transfer-list-body-search-wrapper {
-                                        padding: 8px;
-
-                                        .pfm-ant-input-prefix {
-                                            .anticon-search {
-                                                color: #99A6BF;
-                                            }
-                                        }
-                                    }
-
-                                    .pfm-ant-transfer-list-body-customize-wrapper {
-                                        .pfmTable {
-                                            border: none !important;
-                                            border-radius: 0 !important;
-                                        }
-                                    }
-                                }
-                            }
-
-
-
-                            .pfm-ant-table {
-                                .pfm-ant-table-container {
-                                    .pfm-ant-table-content {
-                                        .pfm-ant-table-thead {
-
-                                            &>tr>th {
-                                                font-size: 14px;
-                                                font-family: SourceHanSansCN-Medium, SourceHanSansCN;
-                                                font-weight: 500;
-                                                color: #17181A;
-                                                background: #EEF3FA;
-                                                border-radius: 0 !important;
-                                                padding: 4px 8px !important;
-                                                border-bottom: none;
-                                            }
-
-                                        }
-
-                                        .pfm-ant-table-tbody {
-                                            &>tr>td {
-                                                padding-left: 8px !important;
-                                                padding-right: 8px !important;
-                                            }
-                                        }
-                                    }
-                                }
-                            }
-                        }
+                .pfm-ant-transfer-list-body {
+                  .pfm-ant-transfer-list-body-search-wrapper {
+                    padding: 8px;
+
+                    .pfm-ant-input-prefix {
+                      .anticon-search {
+                        color: #99a6bf;
+                      }
                     }
-                }
+                  }
 
-                .pfm-ant-modal-confirm-btns {
-                    display: flex;
-                    flex-direction: row;
-                    align-items: center;
-                    justify-content: flex-end;
-                    margin-top: 16px;
-
-                    &>button {
-                        display: flex;
-                        justify-content: center;
-                        align-items: center;
-                        padding: 0 14px;
-                        height: 24px;
-                        border-radius: 4px;
-
-                        &.pfm-ant-btn-default {
-                            border: 1px solid #DAE2F2;
-                            background-color: #FAFCFF;
-                        }
-
-                        &.pfm-ant-btn-primary {
-                            background-color: #3376FE;
-                        }
+                  .pfm-ant-transfer-list-body-customize-wrapper {
+                    .pfmTable {
+                      border: none !important;
+                      border-radius: 0 !important;
                     }
+                  }
                 }
+              }
+
+              /* 表格样式已统一到 src/components/KCIMTable/style.less */
             }
+          }
         }
-        .pfm-ant-modal-footer {
-            border-top: none;
-            padding: 0;
-            margin-top: 16px;
-            .pfm-ant-btn {
-                 border-radius: 4px !important;
-                 height: 24px !important;
-                 padding: 0 14px !important;
+
+        .pfm-ant-modal-confirm-btns {
+          display: flex;
+          flex-direction: row;
+          align-items: center;
+          justify-content: flex-end;
+          margin-top: 16px;
+
+          & > button {
+            display: flex;
+            justify-content: center;
+            align-items: center;
+            padding: 0 14px;
+            height: 24px;
+            border-radius: 4px;
+
+            &.pfm-ant-btn-default {
+              border: 1px solid #dae2f2;
+              background-color: #fafcff;
+            }
+
+            &.pfm-ant-btn-primary {
+              background-color: #3376fe;
             }
+          }
         }
+      }
     }
-
     .pfm-ant-modal-footer {
-        margin-top: 0;
-
-        .pfm-ant-space-item {
-            &>button {
-                display: flex;
-                justify-content: center;
-                align-items: center;
-                padding: 0 14px;
-                height: 24px;
-                ;
-                border-radius: 4px;
-
-                &>span {}
-
-                &.pfm-ant-btn-default {
-                    border: 1px solid #DAE2F2;
-                    background-color: #FAFCFF;
-                }
+      border-top: none;
+      padding: 0;
+      margin-top: 16px;
+      .pfm-ant-btn {
+        border-radius: 4px !important;
+        height: 24px !important;
+        padding: 0 14px !important;
+      }
+    }
+  }
 
-                &.pfm-ant-btn-primary {
-                    background-color: #3376FE;
-                }
-            }
+  .pfm-ant-modal-footer {
+    margin-top: 0;
+
+    .pfm-ant-space-item {
+      & > button {
+        display: flex;
+        justify-content: center;
+        align-items: center;
+        padding: 0 14px;
+        height: 24px;
+        border-radius: 4px;
+
+        & > span {
         }
-    }
-}
 
+        &.pfm-ant-btn-default {
+          border: 1px solid #dae2f2;
+          background-color: #fafcff;
+        }
 
+        &.pfm-ant-btn-primary {
+          background-color: #3376fe;
+        }
+      }
+    }
+  }
+}
 
 //message
 
 .pfm-ant-message {
-    .ant-message-notice-success {
-        .pfm-ant-message-notice-content {
-            .anticon-check-circle {
-                &>svg {
-                    color: #52c41a;
-                }
-            }
+  .ant-message-notice-success {
+    .pfm-ant-message-notice-content {
+      .anticon-check-circle {
+        & > svg {
+          color: #52c41a;
         }
+      }
     }
+  }
 }
 
 //Form
 
 .pfm-ant-form {
-    &.pfm-ant-form-vertical {
-        .pfm-ant-form-item {
-            margin-bottom: 16px;
-
-            .pfm-ant-form-item-label {
-                padding: 0 !important;
-                height: 15px;
-                margin-bottom: 8px;
-                line-height: 15px;
-            }
+  &.pfm-ant-form-vertical {
+    .pfm-ant-form-item {
+      margin-bottom: 16px;
 
-            .pfm-ant-form-item-control-input {
-                min-height: 24px;
-            }
-        }
+      .pfm-ant-form-item-label {
+        padding: 0 !important;
+        height: 15px;
+        margin-bottom: 8px;
+        line-height: 15px;
+      }
+
+      .pfm-ant-form-item-control-input {
+        min-height: 24px;
+      }
     }
+  }
 }
 
-
-
-
 // .pfm-ant-form-item-label {
 //     padding: 0;
 //     //height: 15px;
@@ -331,31 +290,30 @@ textarea {
 // }
 
 .pfm-ant-input-affix-wrapper {
-    padding: 0px 8px !important;
-    border-radius: 4px;
-    border: 1px solid #CFD7E6 !important;
+  padding: 0px 8px !important;
+  border-radius: 4px;
+  border: 1px solid #cfd7e6 !important;
 
-    &>input {
-        &::placeholder {
-            font-size: 14px;
-            font-family: SourceHanSansCN-Normal, SourceHanSansCN;
-            font-weight: 400;
-            color: #99A6BF;
-        }
+  & > input {
+    &::placeholder {
+      font-size: 14px;
+      font-family: SourceHanSansCN-Normal, SourceHanSansCN;
+      font-weight: 400;
+      color: #99a6bf;
     }
+  }
 }
 
 .pfm-ant-input-disabled {
-    &::placeholder {
-        color: #7A8599 !important;
-    }
+  &::placeholder {
+    color: #7a8599 !important;
+  }
 }
 
 .pfm-ant-input-affix-wrapper-disabled {
-    background: #F0F2F5 !important;
+  background: #f0f2f5 !important;
 }
 
-
 // //proContsiner 标题样式覆盖
 // .pfm-ant-page-header .pfm-ant-page-header-heading-title {
 //      height: 17px !important;
@@ -364,415 +322,452 @@ textarea {
 // }
 
 .pfm-ant-page-header {
-    .pfm-ant-page-header-heading-title {
-         font-size: 16px !important;
-    }
+  .pfm-ant-page-header-heading-title {
+    font-size: 16px !important;
+  }
 }
 
 .pfm-ant-page-header .pfm-ant-page-header-heading-left {
-    margin-block: 0 !important;
+  margin-block: 0 !important;
 }
 
-
 .pfm-ant-pro .pfm-ant-pro-layout .pfm-ant-pro-sider-fixed {
-    position: absolute !important;
-    // min-width:230px !important;
-    // width: 230px !important;
+  position: absolute !important;
+  // min-width:230px !important;
+  // width: 230px !important;
 }
 
 //避免logo区域的层级过高,影响父容器
 .pfm-ant-pro .pfm-ant-pro-layout .pfm-ant-pro-sider-fixed {
-    height: calc(100vh - 48px) !important;
-    z-index: 0 !important;
+  height: calc(100vh - 48px) !important;
+  z-index: 0 !important;
 }
 
-
-.pfm-ant-pro-base-menu .pfm-ant-pro-base-menu-submenu-has-icon >.pfm-ant-menu-sub {
-    padding-inline-start:0 !important;
+.pfm-ant-pro-base-menu
+  .pfm-ant-pro-base-menu-submenu-has-icon
+  > .pfm-ant-menu-sub {
+  padding-inline-start: 0 !important;
 }
 
-
-
 .pfm-ant-menu-item-selected {
-    background-color:#F2F6FF !important;
+  background-color: #f2f6ff !important;
+  &:hover {
+    color: #3376fe !important;
+  }
 }
-.pfm-ant-menu-light:not(.pfm-ant-menu-horizontal) .pfm-ant-menu-item:not(.pfm-ant-menu-item-selected):hover {
-    background-color: #f0f2f5 !important;
+.pfm-ant-menu-light:not(.pfm-ant-menu-horizontal)
+  .pfm-ant-menu-item:not(.pfm-ant-menu-item-selected):hover {
+  background-color: #f0f2f5 !important;
 }
 
-.pfm-ant-menu-light:not(.pfm-ant-menu-horizontal) .pfm-ant-menu-submenu-title:hover {
-    background-color: #f0f2f5 !important;
+.pfm-ant-menu-light:not(.pfm-ant-menu-horizontal)
+  .pfm-ant-menu-submenu-title:hover {
+  background-color: #f0f2f5 !important;
 }
 
 .pfm-ant-tree.pfm-ant-tree-directory .pfm-ant-tree-treenode:hover::before {
-    background: #f0f2f5 !important;
-    border-radius: 4px;
+  background: #f0f2f5 !important;
+  border-radius: 4px;
 }
 
 .pfm-EditableProTable {
-    .pfm-ant-pro-card-body {
-        padding-inline: 0 !important;
-        padding-block: 0 !important;
-
-        .pfm-ant-table {
-            border: 1px solid #CFD7E6 !important;
-            overflow: hidden;
-
-            .pfm-ant-table-content {
-                .pfm-ant-table-thead {
-                    &>tr>th {
-
-                        height: 15px;
-                        line-height: 15px;
-                        font-size: 14px;
-                        padding: 12px 16px !important;
-                        font-family: SourceHanSansCN-Medium, SourceHanSansCN;
-                        // font-weight: 500;
-
-                        color: #17181A;
-                        border-bottom: 1px solid #CFD7E6 !important;
-                        background: rgb(238 243 250 / 100%) !important;
-
-                        &::before {
-                            display: none !important;
-                        }
-                    }
-                }
+  .pfm-ant-pro-card-body {
+    padding-inline: 0 !important;
+    padding-block: 0 !important;
 
-                .pfm-ant-table-tbody {
-                    .pfm-ant-table-row {
-                        .pfm-ant-table-cell {
-
-                            height: 15px;
-                            line-height: 15px;
-                            font-size: 14px;
-                            font-family: SourceHanSansCN-Medium, SourceHanSansCN;
-                            color: #17181A;
-                            padding: 12px 16px !important;
-
-                            &>div {
-                                justify-content: flex-start !important;
-                            }
-                        }
-
-                        &:last-child {
-                            &>td {
-                                border-bottom-color: none !important;
-                            }
-                        }
-                    }
-                }
+    .pfm-ant-table {
+      border: 1px solid #cfd7e6 !important;
+      overflow: hidden;
+
+      .pfm-ant-table-content {
+        .pfm-ant-table-thead {
+          & > tr > th {
+            height: 15px;
+            line-height: 15px;
+            font-size: 14px;
+            padding: 12px 16px !important;
+            font-family: SourceHanSansCN-Medium, SourceHanSansCN;
+            // font-weight: 500;
+
+            color: #17181a;
+            border-bottom: 1px solid #cfd7e6 !important;
+            background: rgb(238 243 250 / 100%) !important;
+
+            &::before {
+              display: none !important;
             }
+          }
         }
+
+        .pfm-ant-table-tbody {
+          .pfm-ant-table-row {
+            .pfm-ant-table-cell {
+              height: 15px;
+              line-height: 15px;
+              font-size: 14px;
+              padding: 8px 8px !important;
+              font-family: SourceHanSansCN-Medium, SourceHanSansCN;
+              color: #17181a;
+
+              & > div {
+                justify-content: flex-start !important;
+              }
+            }
+
+            &:last-child {
+              & > td {
+                border-bottom-color: none !important;
+              }
+            }
+          }
+        }
+      }
     }
+  }
 }
 
 .pfm-ant-table-wrapper {
-    .pfm-ant-table:not(.pfm-ant-table-bordered) .pfm-ant-table-tbody>tr.pfm-ant-table-row.pfm-ant-table-row-selected>td:first-child {
-        border-radius: 0 !important;
-    }
+  .pfm-ant-table:not(.pfm-ant-table-bordered)
+    .pfm-ant-table-tbody
+    > tr.pfm-ant-table-row.pfm-ant-table-row-selected
+    > td:first-child {
+    border-radius: 0 !important;
+  }
 
-    .pfm-ant-table-content {
-        .pfm-ant-table-thead {
-            tr {
-                &:last-child {
-                    .pfm-ant-table-cell {
-                        // border-top: 1px solid #dae2f2;
-                    }
-                }
-            }
+  .pfm-ant-table-content {
+    .pfm-ant-table-thead {
+      tr {
+        &:last-child {
+          .pfm-ant-table-cell {
+            // border-top: 1px solid #dae2f2;
+          }
         }
+      }
     }
+  }
 }
 
-.pfm-ant-table-wrapper .pfm-ant-table:not(.pfm-ant-table-bordered) .pfm-ant-table-tbody>tr.pfm-ant-table-row:hover>td:first-child {
-    border-start-start-radius: 0 !important;
-    border-end-start-radius: 0 !important;
+.pfm-ant-table-wrapper
+  .pfm-ant-table:not(.pfm-ant-table-bordered)
+  .pfm-ant-table-tbody
+  > tr.pfm-ant-table-row:hover
+  > td:first-child {
+  border-start-start-radius: 0 !important;
+  border-end-start-radius: 0 !important;
 }
 
-.pfm-ant-table-wrapper .pfm-ant-table:not(.pfm-ant-table-bordered) .pfm-ant-table-tbody>tr.pfm-ant-table-row:last-child:hover>td {
-    border-bottom-color: #dae2f2 !important;
+.pfm-ant-table-wrapper
+  .pfm-ant-table:not(.pfm-ant-table-bordered)
+  .pfm-ant-table-tbody
+  > tr.pfm-ant-table-row:last-child:hover
+  > td {
+  border-bottom-color: #dae2f2 !important;
 }
 
-.pfm-ant-table-wrapper .pfm-ant-table:not(.pfm-ant-table-bordered) .pfm-ant-table-tbody>tr.pfm-ant-table-row:last-child>td {
-    border-bottom-color: #dae2f2 !important;
+.pfm-ant-table-wrapper
+  .pfm-ant-table:not(.pfm-ant-table-bordered)
+  .pfm-ant-table-tbody
+  > tr.pfm-ant-table-row:last-child
+  > td {
+  border-bottom-color: #dae2f2 !important;
 }
 
-
-.pfm-ant-table-wrapper .pfm-ant-table:not(.pfm-ant-table-bordered) .pfm-ant-table-tbody>tr.pfm-ant-table-row:hover>td:last-child {
-    border-end-end-radius: 0 !important;
-    border-start-end-radius: 0 !important;
+.pfm-ant-table-wrapper
+  .pfm-ant-table:not(.pfm-ant-table-bordered)
+  .pfm-ant-table-tbody
+  > tr.pfm-ant-table-row:hover
+  > td:last-child {
+  border-end-end-radius: 0 !important;
+  border-start-end-radius: 0 !important;
 }
 
-
 .pfm-ant-pro .pfm-ant-pro-layout .pfm-ant-pro-layout-bg-list {
-    background: #F7F9FC !important;
+  background: #f7f9fc !important;
 }
 
 // .pfm-ant-message-notice .pfm-ant-message-success .anticon {
 //     color: #52c41a !important;
 // }
 .ant-message-success {
-    .anticon .anticon-check-circle {
-        color: #52c41a !important;
-    }
+  .anticon .anticon-check-circle {
+    color: #52c41a !important;
+  }
 }
 
-
 /**
     Select
 **/
 .pfm-ant-select {
-    .pfm-ant-select-selector {
-        height: 24px !important;
-        padding: 0 8px !important;
-        border-radius: 4px !important;
-        border: 1px solid #CFD7E6 !important;
-
-        .pfm-ant-select-selection-item {
-            line-height: 24px !important;
-        }
-
-        .pfm-ant-select-selection-search-input {
-            height: 24px !important;
-        }
-
-        .pfm-ant-select-selection-placeholder {
-            line-height: 22px !important;
-            font-size: 14px;
-            font-family: SourceHanSansCN-Normal, SourceHanSansCN;
-            font-weight: 400;
-            color: #99A6BF;
-        }
+  .pfm-ant-select-selector {
+    height: 24px !important;
+    padding: 0 8px !important;
+    border-radius: 4px !important;
+    border: 1px solid #cfd7e6 !important;
 
-        .pfm-ant-select-selection-overflow {
-            .pfm-ant-select-selection-overflow-item {
-                .pfm-ant-select-selection-item {
-                    height: 22px !important;
-                    line-height: 22px !important;
-                    margin-top: 0 !important;
-                    margin-bottom: 7px !important;
-                }
-            }
-        }
+    .pfm-ant-select-selection-item {
+      line-height: 24px !important;
     }
 
-    .pfm-ant-select-arrow {
-        color: #CFD7E6;
+    .pfm-ant-select-selection-search-input {
+      height: 24px !important;
     }
 
-    &.pfm-ant-select-disabled {
-        .pfm-ant-select-selector {
-            border: 1px solid #DADEE6 !important;
-
-            .pfm-ant-select-selection-placeholder {
-                color: #7A8599;
-            }
-        }
+    .pfm-ant-select-selection-placeholder {
+      line-height: 22px !important;
+      font-size: 14px;
+      font-family: SourceHanSansCN-Normal, SourceHanSansCN;
+      font-weight: 400;
+      color: #99a6bf;
+    }
 
-        .pfm-ant-select-arrow {
-            color: #7A8599;
+    .pfm-ant-select-selection-overflow {
+      .pfm-ant-select-selection-overflow-item {
+        .pfm-ant-select-selection-item {
+          height: 22px !important;
+          line-height: 22px !important;
+          margin-top: 0 !important;
+          margin-bottom: 7px !important;
         }
+      }
     }
-}
+  }
 
+  .pfm-ant-select-arrow {
+    color: #cfd7e6;
+  }
 
+  &.pfm-ant-select-disabled {
+    .pfm-ant-select-selector {
+      border: 1px solid #dadee6 !important;
 
+      .pfm-ant-select-selection-placeholder {
+        color: #7a8599;
+      }
+    }
 
+    .pfm-ant-select-arrow {
+      color: #7a8599;
+    }
+  }
+}
 
 .pfm-ant-tabs {
-    .pfm-ant-tabs-tab {
-        padding: 5px 0 !important;
-        padding-top: 12px !important;
-    }
-    .pfm-ant-tabs-nav {
-        &::before {
-            border-bottom: none !important;
-        }
+  .pfm-ant-tabs-tab {
+    padding: 5px 0 !important;
+    padding-top: 12px !important;
+  }
+  .pfm-ant-tabs-nav {
+    &::before {
+      border-bottom: none !important;
     }
-   
+  }
 }
 
-
-
 .pfm-ant-table-wrapper {
-    overflow: hidden;
+  overflow: hidden;
 }
 
-
-
 .pfm-ant-pro-layout .pfm-ant-pro-layout-container {
-    flex: 1;
-    background: #F7F9FC !important;
+  flex: 1;
+  background: #f7f9fc !important;
 }
 
 //页面全局底色
 .pfm-ant-pro .pfm-ant-pro-layout .pfm-ant-pro-layout-bg-list {
-    background: rgb(247 249 252 / 100%);
+  background: rgb(247 249 252 / 100%);
 }
 
 .pfm-ant-pro-page-container-children-content {
-    padding-inline: 0 !important;
+  padding-inline: 0 !important;
+  padding-block: 0 !important;
 }
 
 .pfm-ant-pro-sider-actions {
-    display: none !important;
+  display: none !important;
 }
 
-
 .pfm-ant-pro-sider-collapsed-button {
-    top: 95% !important;
-    right: 17px !important;
+  top: 95% !important;
+  right: 17px !important;
 }
 
-
 .pfm-ant-menu .pfm-ant-menu-submenu-title .anticon {
-    transition: none !important;
+  transition: none !important;
 }
 
-.pfm-ant-menu .pfm-ant-menu-item .anticon+span {
-    transition: none !important;
+.pfm-ant-menu .pfm-ant-menu-item .anticon + span {
+  transition: none !important;
 }
 
 .pfm-ant-menu .pfm-ant-menu-item .anticon {
-    transition: none !important;
+  transition: none !important;
 }
 
 .pfm-ant-menu .pfm-ant-menu-item {
-    transition: none !important;
+  transition: none !important;
 }
 
-.pfm-ant-menu .pfm-ant-menu-submenu-title .anticon+span {
-    transition: none !important;
+.pfm-ant-menu .pfm-ant-menu-submenu-title .anticon + span {
+  transition: none !important;
 }
 
-.pfm-ant-tabs .pfm-ant-tabs-tab+.pfm-ant-tabs-tab {
-    margin: 0 0 0 24px !important;
+.pfm-ant-tabs .pfm-ant-tabs-tab + .pfm-ant-tabs-tab {
+  margin: 0 0 0 24px !important;
 }
 
-
 .menuCollapseIcon {
-    &>svg {
-        width: 24px;
-        height: 24px;
-    }
+  & > svg {
+    width: 24px;
+    height: 24px;
+  }
 }
 
-
-.pfm-ant-pro-form>div:not(.pfm-ant-pro-form-light-filter) .pro-field-lg {
-    width: 100% !important;
+.pfm-ant-pro-form > div:not(.pfm-ant-pro-form-light-filter) .pro-field-lg {
+  width: 100% !important;
 }
 
 .pfm-ant-select {
-    .pfm-ant-select-selector {
-        border: 1px solid #CFD7E6 !important;
-        // color: #99A6BF !important;
+  .pfm-ant-select-selector {
+    border: 1px solid #cfd7e6 !important;
+    // color: #99A6BF !important;
 
-        .pfm-ant-select-selection-placeholder {
-            color: #99A6BF !important;
-        }
+    .pfm-ant-select-selection-placeholder {
+      color: #99a6bf !important;
     }
+  }
 
-    .pfm-ant-select-arrow {
-        color: #99A6BF !important;
-    }
+  .pfm-ant-select-arrow {
+    color: #99a6bf !important;
+  }
 }
 
-
 .pfm-ant-input-number-input {
-    border-radius: 4px;
-    &::placeholder {
-        color: #99A6BF !important;
-    }
+  border-radius: 4px;
+  &::placeholder {
+    color: #99a6bf !important;
+  }
 }
 
 .pfm-ant-select-disabled {
-    .pfm-ant-select-selector {
-        border: 1px solid #DADEE6 !important;
-        background: #F0F2F5 !important;
+  .pfm-ant-select-selector {
+    border: 1px solid #dadee6 !important;
+    background: #f0f2f5 !important;
 
-        .pfm-ant-select-selection-placeholder {
-            color: #7A8599 !important;
-        }
+    .pfm-ant-select-selection-placeholder {
+      color: #7a8599 !important;
     }
+  }
 
-    .pfm-ant-select-arrow {
-        color: #7A8599 !important;
-    }
+  .pfm-ant-select-arrow {
+    color: #7a8599 !important;
+  }
 }
 
-
-
 .pfm-ant-input-number {
-    border: 1px solid #CFD7E6 !important;
-    // color: #99A6BF !important;
-    border-radius: 4px;
-    box-shadow: none !important;
-    .pfm-ant-input-number-input {
-        height: 22px !important;
-        line-height: 22px !important;
-    }
+  border: 1px solid #cfd7e6 !important;
+  // color: #99A6BF !important;
+  border-radius: 4px;
+  box-shadow: none !important;
+  .pfm-ant-input-number-input {
+    height: 22px !important;
+    line-height: 22px !important;
+  }
 }
 
 .pfm-ant-input-number-disabled {
-    border: 1px solid #DADEE6 !important;
-    background: #F0F2F5 !important;
+  border: 1px solid #dadee6 !important;
+  background: #f0f2f5 !important;
 
-    .pfm-ant-input-number-input {
-        color: #7A8599 !important;
-    }
+  .pfm-ant-input-number-input {
+    color: #7a8599 !important;
+  }
 }
 
 .pfm-ant-input-affix-wrapper-disabled {
-    border: 1px solid #DADEE6 !important;
+  border: 1px solid #dadee6 !important;
 }
 
 .pfm-ant-picker {
-    border-radius: 4px !important;
-    border: 1px solid #CFD7E6 !important;
-    height: 24px;
-    .pfm-ant-picker-input {
-        &>input {
-             &::placeholder {
-                color:#99A6BF !important;
-             }
-        }
-        .pfm-ant-picker-suffix {
-            color:#99A6BF !important;
-        }
+  border-radius: 4px !important;
+  border: 1px solid #cfd7e6 !important;
+  height: 24px;
+  .pfm-ant-picker-input {
+    & > input {
+      &::placeholder {
+        color: #99a6bf !important;
+      }
+    }
+    .pfm-ant-picker-suffix {
+      color: #99a6bf !important;
     }
+  }
 }
 
 .pfm-ant-picker-panel-container {
-    .pfm-ant-picker-panel {
-        .pfm-ant-picker-month-panel {
-            .pfm-ant-picker-body {
-                .pfm-ant-picker-content {
-                    tr {
-                        td {
-                            &.pfm-ant-picker-cell-selected {
-                                .pfm-ant-picker-cell-inner {
-                                    background:#3377FF;
-                                }
-                            }
-                        }
-                    }
+  .pfm-ant-picker-panel {
+    .pfm-ant-picker-month-panel {
+      .pfm-ant-picker-body {
+        .pfm-ant-picker-content {
+          tr {
+            td {
+              &.pfm-ant-picker-cell-selected {
+                .pfm-ant-picker-cell-inner {
+                  background: #3377ff;
                 }
+              }
             }
+          }
         }
+      }
     }
+  }
 }
 
-.pfm-ant-picker-input>input[disabled] {
-    color: #7A8599 !important;
+.pfm-ant-picker-input > input[disabled] {
+  color: #7a8599 !important;
 }
 
 .pfm-ant-picker-disabled {
-    background: #F0F2F5 !important;
-    border: 1px solid #DADEE6 !important;
+  background: #f0f2f5 !important;
+  border: 1px solid #dadee6 !important;
 
-    .anticon-swap-right,
-    .anticon-calendar {
-        color: #7A8599 !important;
+  .anticon-swap-right,
+  .anticon-calendar {
+    color: #7a8599 !important;
+  }
+}
+
+/* 查核项管理页面 - 更多操作下拉菜单样式 */
+.ant-dropdown.check-item-dropdown {
+  .ant-dropdown-menu {
+    border-radius: 4px !important;
+    overflow: hidden !important;
+    padding: 4px !important;
+    box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15) !important;
+  }
+
+  .ant-dropdown-menu-item {
+    border-radius: 4px !important;
+    padding: 8px 12px !important;
+    margin: 0 !important;
+    font-size: 14px !important;
+    line-height: 1.4 !important;
+
+    &:hover {
+      background: rgba(51, 119, 255, 0.08) !important;
+      color: #17181a !important;
     }
-}
+
+    &.ant-dropdown-menu-item-selected {
+      background: rgba(51, 119, 255, 0.08) !important;
+      color: #17181a !important;
+    }
+
+    // 删除项特殊样式
+    &:last-child {
+      &:hover {
+        background: rgba(255, 77, 79, 0.08) !important;
+      }
+    }
+  }
+}

+ 25 - 25
src/menuIcons.js

@@ -7,22 +7,22 @@
  * @FilePath: /CostAccountManaSys/src/menuIcons.js
  */
 
-
 import React from 'react';
-import { SmileOutlined,
-     HomeOutlined, 
-     PicLeftOutlined, 
-     SettingOutlined,
-     NodeExpandOutlined,
-     BorderOuterOutlined,
-     CopyrightOutlined,
-     ProfileOutlined,
-     AccountBookOutlined,
-     ControlOutlined,
-     CrownOutlined,
-     ToolOutlined,
-     TableOutlined,
-     FileDoneOutlined
+import {
+  SmileOutlined,
+  HomeOutlined,
+  PicLeftOutlined,
+  SettingOutlined,
+  NodeExpandOutlined,
+  BorderOuterOutlined,
+  CopyrightOutlined,
+  ProfileOutlined,
+  AccountBookOutlined,
+  ControlOutlined,
+  CrownOutlined,
+  ToolOutlined,
+  TableOutlined,
+  FileDoneOutlined,
 } from '@ant-design/icons';
 
 const iconEnum = {
@@ -30,16 +30,16 @@ const iconEnum = {
   home: <HomeOutlined />,
   picLeft: <PicLeftOutlined />,
   setting: <SettingOutlined />,
-  crown:<CrownOutlined />,
-  tool:<ToolOutlined />,
-  TableOutlined:<TableOutlined />,
-  NodeExpandOutlined:<NodeExpandOutlined />,
-  BorderOuterOutlined:<BorderOuterOutlined />,
-  CopyrightOutlined:<CopyrightOutlined />,
-  ProfileOutlined:<ProfileOutlined />,
-  AccountBookOutlined:<AccountBookOutlined />,
-  ControlOutlined:<ControlOutlined />,
-  FileDoneOutlined:<FileDoneOutlined />
+  crown: <CrownOutlined />,
+  tool: <ToolOutlined />,
+  TableOutlined: <TableOutlined />,
+  NodeExpandOutlined: <NodeExpandOutlined />,
+  BorderOuterOutlined: <BorderOuterOutlined />,
+  CopyrightOutlined: <CopyrightOutlined />,
+  ProfileOutlined: <ProfileOutlined />,
+  AccountBookOutlined: <AccountBookOutlined />,
+  ControlOutlined: <ControlOutlined />,
+  FileDoneOutlined: <FileDoneOutlined />,
 };
 
 // const iconEnum = {

+ 2 - 6
src/pages/Home/index.tsx

@@ -14,14 +14,10 @@ import './style.less';
 
 import KCIMPagecontainer from '@/components/KCIMPageContainer';
 
-
-
 const HomePage: React.FC = () => {
-
-
   return (
-    <KCIMPagecontainer className='HomePage'  ghost>
-        HomePage
+    <KCIMPagecontainer className="HomePage" ghost>
+      HomePage
     </KCIMPagecontainer>
   );
 };

+ 28 - 41
src/pages/Home/service.ts

@@ -7,54 +7,41 @@
  * @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
  */
 
-import { request } from '@@/plugin-request'
-
-
-
-
+import { request } from '@@/plugin-request';
 
 //获取首页信息
 
 export type getIndexDataRespType = {
-    id:number,
-    computeDate:string,
-    code:number,
-    name:string,
-    preCode:number,
-    sort:number,
-    status:number,
-    audit:string
-}
-
-export const getIndexData = (computeDate:string)=>{
-       
-       return request<getIndexDataRespType[]>('/performance/index', {
-            method: 'GET',
-            params:{computeDate},
-       })
-}
-
+  id: number;
+  computeDate: string;
+  code: number;
+  name: string;
+  preCode: number;
+  sort: number;
+  status: number;
+  audit: string;
+};
+
+export const getIndexData = (computeDate: string) => {
+  return request<getIndexDataRespType[]>('/performance/index', {
+    method: 'GET',
+    params: { computeDate },
+  });
+};
 
 //获取当前计算年月
 
-export const getComputeDate = ()=>{
-       
-    return request<string>('/performance/index/getCurrentComputeDate', {
-         method: 'GET',
-    })
-}
-
+export const getComputeDate = () => {
+  return request<string>('/performance/index/getCurrentComputeDate', {
+    method: 'GET',
+  });
+};
 
 //获取动态报表的path
 
-export const getReportPath = (params:any)=>{
-       
-   return request('/performance/parameter/getList', {
-         method: 'GET',
-         params:{...params}
-    });
-}
-
-
-
-
+export const getReportPath = (params: any) => {
+  return request('/performance/parameter/getList', {
+    method: 'GET',
+    params: { ...params },
+  });
+};

+ 29 - 48
src/pages/Home/style.less

@@ -8,7 +8,7 @@
     display: flex;
     flex-direction: row;
     justify-content: space-between;
-    background: #F7F9FC;
+    background: #f7f9fc;
     border-radius: 4px;
     padding: 16px 28px;
     margin-top: 16px;
@@ -33,9 +33,9 @@
         font-size: 16px;
         font-family: Roboto-Bold, Roboto;
         font-weight: bold;
-        color: #FFF;
+        color: #fff;
         margin-bottom: 16px;
-        background: #CFD6E6;
+        background: #cfd6e6;
 
         .point {
           position: absolute;
@@ -43,25 +43,23 @@
           height: 8px;
           top: -2px;
           right: -2px;
-          background: #FFC34D;
+          background: #ffc34d;
           border-radius: 2px;
-          border: 2px solid #FFF;
+          border: 2px solid #fff;
         }
 
         &.active {
-          background: #37F;
+          background: #37f;
         }
-
       }
 
       .name {
         font-size: 12px;
         font-family: SourceHanSansCN-Medium, SourceHanSansCN;
         font-weight: 500;
-        color: #7A8599;
+        color: #7a8599;
       }
 
-
       // &::after {
       //   right: -79%;
       //   top: -1px;
@@ -72,16 +70,15 @@
       //   color: #DAE2F2;
       // }
 
-
       &.on {
-        background: #FAFCFF;
+        background: #fafcff;
         border-radius: 8px;
-        border: 1px solid #DAE2F2;
+        border: 1px solid #dae2f2;
       }
 
       &.active {
         &::after {
-          color: #37F;
+          color: #37f;
         }
       }
 
@@ -90,8 +87,6 @@
           display: none;
         }
       }
-
-
     }
 
     .point {
@@ -99,8 +94,7 @@
       top: -7px;
       // right:-22.5px;
       font-size: 33px;
-      color: #DAE2F2;
-
+      color: #dae2f2;
     }
   }
 
@@ -122,12 +116,11 @@
         cursor: pointer;
         font-family: SourceHanSansCN-Medium, SourceHanSansCN;
         font-weight: 500;
-        color: #17181A;
+        color: #17181a;
         line-height: 17px;
         margin-bottom: 10px;
 
-
-        &>span {
+        & > span {
           display: inline-block;
           font-size: 14px;
           font-family: SourceHanSansCN-Normal, SourceHanSansCN;
@@ -135,8 +128,6 @@
           cursor: pointer;
           margin-left: 8px;
         }
-
-
       }
 
       .detail {
@@ -154,20 +145,18 @@
 
         /* 这里是超出几行省略 */
         overflow: hidden;
-        
       }
 
       &.active {
-          &:hover {
-            .title { 
-              color: rgb(51 119 255 / 100%);
+        &:hover {
+          .title {
+            color: rgb(51 119 255 / 100%);
 
-              &>span {
-                color: rgb(51 119 255 / 100%);
-              }
+            & > span {
+              color: rgb(51 119 255 / 100%);
             }
           }
-      
+        }
       }
     }
 
@@ -192,11 +181,11 @@
           font-size: 16px;
           font-family: SourceHanSansCN-Medium, SourceHanSansCN;
           font-weight: 500;
-          color: #17181A;
+          color: #17181a;
           line-height: 17px;
           margin-bottom: 10px;
 
-          &>span {
+          & > span {
             display: inline-block;
             font-size: 14px;
             font-family: SourceHanSansCN-Normal, SourceHanSansCN;
@@ -204,8 +193,6 @@
             cursor: pointer;
             margin-left: 8px;
           }
-
-
         }
 
         .detail {
@@ -219,28 +206,23 @@
           display: box;
           -webkit-box-orient: vertical;
           -webkit-line-clamp: 2;
-          
+
           /* 这里是超出几行省略 */
           overflow: hidden;
-
         }
 
         &.active {
           &:hover {
-              .title {
+            .title {
+              color: rgb(51 119 255 / 100%);
+
+              & > span {
                 color: rgb(51 119 255 / 100%);
-  
-                &>span {
-                  color: rgb(51 119 255 / 100%);
-                }
               }
-          
-  
+            }
           }
         }
       }
-
-      
     }
   }
 
@@ -255,8 +237,7 @@
     background-repeat: no-repeat;
     background-position: center;
 
-    &>img {
-        
+    & > img {
     }
   }
-}
+}

+ 1366 - 0
src/pages/baseInfoMana/checkGroupMana/index.tsx

@@ -0,0 +1,1366 @@
+/*
+ * @Author: code4eat awesomedema@gmail.com
+ * @Date: 2023-03-03 11:30:33
+ * @LastEditors: code4eat awesomedema@gmail.com
+ * @LastEditTime: 2025-11-07 11:07:51
+ * @FilePath: /KC-MiddlePlatform/src/pages/platform/setting/pubDicTypeMana/index.tsx
+ * @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
+ */
+
+import React, { useEffect, useRef, useState } from 'react';
+import {
+  Input,
+  message,
+  Card,
+  Button,
+  Modal,
+  Tabs,
+  Radio,
+  Popconfirm,
+  Dropdown,
+  Menu,
+  Checkbox,
+} from 'antd';
+import { ActionType, ProColumns } from '@ant-design/pro-table';
+import KCIMPagecontainer from '@/components/KCIMPageContainer';
+import { KCIMTable } from '@/components/KCIMTable';
+import {
+  ModalForm,
+  ProFormText,
+  ProFormTextArea,
+  ProFormSelect,
+} from '@ant-design/pro-form';
+import { PlusOutlined, SearchOutlined, MoreOutlined } from '@ant-design/icons';
+import { createFromIconfontCN } from '@ant-design/icons';
+import './style.less';
+import {
+  getChackGroupData,
+  addData,
+  editData,
+  delData,
+  getCheckGroupEmployees,
+  addCheckGroupEmployee,
+  delCheckGroupEmployee,
+  getAvailableEmployees,
+  setGroupManager,
+  getCheckGroupCheckpoints,
+  getGroupPendingCheckpoints,
+  addCheckGroupCheckpoints,
+  delCheckGroupCheckpoints,
+} from './service';
+
+const IconFont = createFromIconfontCN({
+  scriptUrl: '',
+});
+
+// 添加查核要点弹窗组件
+const AddCheckpointModal: React.FC<{
+  selectedGroup: any;
+  onSuccess: () => void;
+}> = ({ selectedGroup, onSuccess }) => {
+  const [modalVisible, setModalVisible] = useState(false);
+  const [selectedCheckpointIds, setSelectedCheckpointIds] = useState<
+    React.Key[]
+  >([]);
+  const [searchKeyword, setSearchKeyword] = useState('');
+  const [allCheckpointSelectData, setAllCheckpointSelectData] = useState<any[]>(
+    [],
+  );
+  const checkpointSelectTableRef = useRef<ActionType>();
+
+  // 获取可添加的查核要点列表
+  const getCheckpointSelectData = async (params: any) => {
+    if (!selectedGroup?.id) {
+      return {
+        data: [],
+        success: true,
+        total: 0,
+      };
+    }
+
+    try {
+      const { current = 1, pageSize = 10 } = params;
+      const res = await getGroupPendingCheckpoints(
+        selectedGroup.id,
+        searchKeyword,
+        current,
+        pageSize,
+      );
+
+      // 保存所有数据,用于后续选择时获取完整信息
+      if (current === 1) {
+        setAllCheckpointSelectData(res.list || []);
+      } else {
+        setAllCheckpointSelectData((prev) => [...prev, ...(res.list || [])]);
+      }
+
+      return {
+        data: res.list || [],
+        success: res.success,
+        total: res.total || 0,
+      };
+    } catch (error) {
+      return {
+        data: [],
+        success: false,
+        total: 0,
+      };
+    }
+  };
+
+  // 打开弹窗时重置状态
+  const handleOpenModal = () => {
+    setModalVisible(true);
+    setSelectedCheckpointIds([]);
+    setSearchKeyword('');
+    setAllCheckpointSelectData([]);
+
+    // 延迟触发表格重新加载
+    setTimeout(() => {
+      checkpointSelectTableRef.current?.reload();
+    }, 100);
+  };
+
+  // 提交添加
+  const handleSubmit = async () => {
+    if (selectedCheckpointIds.length === 0) {
+      message.warning('请至少选择一个查核要点');
+      return;
+    }
+
+    try {
+      // 直接按“要点ID”提交(与接口分页口径一致)
+      const selectedIds = Array.from(
+        new Set(
+          selectedCheckpointIds
+            .map((k) => Number(k))
+            .filter((n) => !Number.isNaN(n)),
+        ),
+      ) as number[];
+
+      const result = await addCheckGroupCheckpoints(
+        selectedGroup.id,
+        selectedIds,
+      );
+      // 检查返回值,如果是false说明业务失败
+      if (result === false) {
+        return; // 错误消息已在响应拦截器中显示,直接返回
+      }
+      message.success(`已添加 ${selectedIds.length} 个查核要点`);
+      setModalVisible(false);
+      onSuccess(); // 调用成功回调
+    } catch (error) {
+      message.error('添加失败');
+    }
+  };
+
+  // 定义表格列
+  const checkpointSelectColumns: ProColumns[] = [
+    {
+      title: '要点名称',
+      dataIndex: 'name',
+      width: 260,
+      align: 'left' as 'left',
+    },
+    {
+      title: '查核项数',
+      dataIndex: 'checkItemList',
+      width: 100,
+      align: 'center' as 'center',
+      render: (_: any, record: any) =>
+        Array.isArray(record?.checkItemList) ? record.checkItemList.length : 0,
+    },
+  ];
+
+  return (
+    <>
+      <Button
+        type="primary"
+        className="add-btn"
+        size="small"
+        onClick={handleOpenModal}
+      >
+        添加
+      </Button>
+
+      <Modal
+        title={`选择查核要点(${selectedGroup?.name || ''})`}
+        open={modalVisible}
+        onCancel={() => setModalVisible(false)}
+        width={700}
+        footer={[
+          <Button key="cancel" onClick={() => setModalVisible(false)}>
+            取消
+          </Button>,
+          <Button
+            key="submit"
+            type="primary"
+            onClick={handleSubmit}
+            disabled={selectedCheckpointIds.length === 0}
+            style={{
+              backgroundColor: '#3377FF',
+              borderColor: '#3377FF',
+              color: '#FFFFFF',
+            }}
+          >
+            确定
+            {selectedCheckpointIds.length > 0
+              ? ` (${selectedCheckpointIds.length})`
+              : ''}
+          </Button>,
+        ]}
+        className="checkpoint-select-modal"
+      >
+        <div style={{ marginBottom: 8 }}>
+          <Input
+            placeholder="要点名称"
+            value={searchKeyword}
+            allowClear
+            onChange={(e) => {
+              setSearchKeyword(e.target.value);
+              if (e.target.value === '') {
+                // 清空搜索时自动刷新
+                checkpointSelectTableRef.current?.reload();
+              }
+            }}
+            onPressEnter={() => checkpointSelectTableRef.current?.reload()}
+            suffix={
+              <SearchOutlined
+                onClick={() => checkpointSelectTableRef.current?.reload()}
+                style={{ cursor: 'pointer', color: '#99A6BF' }}
+              />
+            }
+            style={{ width: '100%' }}
+          />
+        </div>
+
+        <KCIMTable
+          actionRef={checkpointSelectTableRef}
+          columns={checkpointSelectColumns}
+          request={getCheckpointSelectData}
+          rowKey="id"
+          bordered
+          search={false}
+          pagination={{
+            simple: true,
+            pageSize: 10,
+            size: 'small',
+            showTotal: () => null,
+          }}
+          rowSelection={{
+            selectedRowKeys: selectedCheckpointIds,
+            onChange: (selectedRowKeys) => {
+              setSelectedCheckpointIds(selectedRowKeys);
+            },
+            preserveSelectedRowKeys: true,
+          }}
+          tableAlertRender={false}
+          scroll={{ y: 360 }}
+          expandable={{
+            expandedRowRender: (record: any) => (
+              <div style={{ paddingLeft: 24 }}>
+                {Array.isArray(record?.checkItemList) &&
+                record.checkItemList.length > 0 ? (
+                  <ul style={{ margin: 0, paddingLeft: 16 }}>
+                    {record.checkItemList.map((ci: any) => (
+                      <li key={ci.id} style={{ lineHeight: '22px' }}>
+                        {ci.name}
+                      </li>
+                    ))}
+                  </ul>
+                ) : (
+                  <span>无明细</span>
+                )}
+              </div>
+            ),
+          }}
+        />
+      </Modal>
+    </>
+  );
+};
+
+// 添加人员弹窗组件
+const AddEmployeeModal: React.FC<{
+  selectedGroup: any;
+  onSuccess: () => void;
+}> = ({ selectedGroup, onSuccess }) => {
+  const [modalVisible, setModalVisible] = useState(false);
+  const [selectedEmployeeIds, setSelectedEmployeeIds] = useState<React.Key[]>(
+    [],
+  );
+  const [selectedEmployeeData, setSelectedEmployeeData] = useState<any[]>([]);
+  const [searchKeyword, setSearchKeyword] = useState('');
+  const [allEmployeeData, setAllEmployeeData] = useState<any[]>([]);
+  const employeeTableRef = useRef<ActionType>();
+
+  // 获取可添加的人员列表
+  const getEmployeeSelectData = async (params: any) => {
+    if (!selectedGroup?.id) {
+      return {
+        data: [],
+        success: true,
+        total: 0,
+      };
+    }
+
+    try {
+      const { current = 1, pageSize = 10 } = params;
+      const res = await getAvailableEmployees(
+        selectedGroup.id,
+        searchKeyword,
+        current,
+        pageSize,
+      );
+
+      // 保存所有数据,用于后续选择时获取完整信息
+      if (current === 1) {
+        setAllEmployeeData(res.list || []);
+      } else {
+        setAllEmployeeData((prev) => [...prev, ...(res.list || [])]);
+      }
+
+      return {
+        data: res.list || [],
+        success: res.success,
+        total: res.total || 0,
+      };
+    } catch (error) {
+      return {
+        data: [],
+        success: false,
+        total: 0,
+      };
+    }
+  };
+
+  // 打开弹窗时重置状态
+  const handleOpenModal = () => {
+    setModalVisible(true);
+    setSelectedEmployeeIds([]);
+    setSelectedEmployeeData([]);
+    setSearchKeyword('');
+    setAllEmployeeData([]);
+
+    // 延迟触发表格重新加载
+    setTimeout(() => {
+      employeeTableRef.current?.reload();
+    }, 100);
+  };
+
+  // 提交添加
+  const handleSubmit = async () => {
+    if (selectedEmployeeIds.length === 0) {
+      message.warning('请至少选择一个人员');
+      return;
+    }
+
+    try {
+      // 根据选中的ID获取完整的人员信息
+      const selectedEmployees = allEmployeeData.filter((emp) =>
+        selectedEmployeeIds.includes(emp.id),
+      );
+
+      const result = await addCheckGroupEmployee(
+        selectedGroup.id,
+        selectedEmployees,
+      );
+      // 检查返回值,如果是false说明业务失败
+      if (result === false) {
+        return; // 错误消息已在响应拦截器中显示,直接返回
+      }
+      message.success(`已添加 ${selectedEmployeeIds.length} 个人员`);
+      setModalVisible(false);
+      onSuccess(); // 调用成功回调
+    } catch (error) {
+      message.error('添加失败');
+    }
+  };
+
+  // 定义表格列
+  const employeeSelectColumns: ProColumns[] = [
+    {
+      title: '工号',
+      dataIndex: 'code',
+      width: 120,
+    },
+    {
+      title: '姓名',
+      dataIndex: 'name',
+      width: 120,
+    },
+  ];
+
+  return (
+    <>
+      <Button
+        type="primary"
+        className="add-btn"
+        size="small"
+        onClick={handleOpenModal}
+      >
+        添加
+      </Button>
+
+      <Modal
+        title={`选择人员(${selectedGroup?.name || ''})`}
+        open={modalVisible}
+        onCancel={() => setModalVisible(false)}
+        width={352}
+        footer={[
+          <Button key="cancel" onClick={() => setModalVisible(false)}>
+            取消
+          </Button>,
+          <Button
+            key="submit"
+            type="primary"
+            onClick={handleSubmit}
+            disabled={selectedEmployeeIds.length === 0}
+            style={{
+              backgroundColor: '#3377FF',
+              borderColor: '#3377FF',
+              color: '#FFFFFF',
+            }}
+          >
+            确定
+            {selectedEmployeeIds.length > 0
+              ? ` (${selectedEmployeeIds.length})`
+              : ''}
+          </Button>,
+        ]}
+        className="employee-select-modal"
+      >
+        <div style={{ marginBottom: 8 }}>
+          <Input
+            placeholder="请输入姓名或工号"
+            value={searchKeyword}
+            allowClear
+            onChange={(e) => {
+              setSearchKeyword(e.target.value);
+              if (e.target.value === '') {
+                // 清空搜索时自动刷新
+                employeeTableRef.current?.reload();
+              }
+            }}
+            onPressEnter={() => employeeTableRef.current?.reload()}
+            suffix={
+              <SearchOutlined
+                onClick={() => employeeTableRef.current?.reload()}
+                style={{ cursor: 'pointer', color: '#99A6BF' }}
+              />
+            }
+            style={{ width: '100%' }}
+          />
+        </div>
+
+        <KCIMTable
+          actionRef={employeeTableRef}
+          columns={employeeSelectColumns}
+          request={getEmployeeSelectData}
+          rowKey="id"
+          search={false}
+          pagination={{
+            simple: true,
+            pageSize: 10,
+            size: 'small',
+            showTotal: () => null,
+          }}
+          rowSelection={{
+            selectedRowKeys: selectedEmployeeIds,
+            onChange: (selectedRowKeys) => {
+              setSelectedEmployeeIds(selectedRowKeys);
+            },
+          }}
+          tableAlertRender={false}
+          scroll={{ y: 360 }}
+        />
+      </Modal>
+    </>
+  );
+};
+
+// 主页面组件
+const CheckGroupMana: React.FC = () => {
+  // 左侧查核组数据
+  const [groupData, setGroupData] = useState<any[]>([]);
+  const [filteredGroupData, setFilteredGroupData] = useState<any[]>([]);
+  const [selectedGroup, setSelectedGroup] = useState<any>(null);
+  const [groupSearchKeywords, setGroupSearchKeywords] = useState<string>('');
+  const [hoveredNode, setHoveredNode] = useState<string | null>(null);
+  const [openDropdownNode, setOpenDropdownNode] = useState<string | null>(null);
+
+  // 分页加载相关状态
+  const [currentPage, setCurrentPage] = useState(1);
+  const [totalCount, setTotalCount] = useState(0);
+  const [loading, setLoading] = useState(false);
+  const [hasMore, setHasMore] = useState(true);
+
+  // 右侧人员表格相关
+  const [employeeSearchKeywords, setEmployeeSearchKeywords] =
+    useState<string>('');
+  const [selectedEmployeeKeys, setSelectedEmployeeKeys] = useState<string[]>(
+    [],
+  );
+  const [allEmployeeList, setAllEmployeeList] = useState<any[]>([]); // 存储原始组员数据
+  const [filteredEmployeeList, setFilteredEmployeeList] = useState<any[]>([]); // 存储过滤后的组员数据
+  const employeeTableRef = useRef<ActionType>();
+
+  // 查核要点相关状态
+  const [checkpointSearchKeywords, setCheckpointSearchKeywords] =
+    useState<string>('');
+  const [selectedCheckpointKeys, setSelectedCheckpointKeys] = useState<
+    string[]
+  >([]);
+  const [allCheckpointList, setAllCheckpointList] = useState<any[]>([]); // 存储原始查核要点数据
+  const [filteredCheckpointList, setFilteredCheckpointList] = useState<any[]>(
+    [],
+  ); // 存储过滤后的查核要点数据
+  const checkpointTableRef = useRef<ActionType>();
+
+  // 右侧内容区ref和操作栏定位状态
+  const deptContentRef = useRef<HTMLDivElement>(null);
+  const [actionBarStyle, setActionBarStyle] = useState<{
+    left: number;
+    width: number;
+  }>({ left: 0, width: 0 });
+
+  // 更新操作栏定位,支持ResizeObserver
+  const updateActionBarPosition = () => {
+    if (deptContentRef.current) {
+      const rect = deptContentRef.current.getBoundingClientRect();
+      setActionBarStyle({ left: rect.left, width: rect.width });
+    }
+  };
+
+  useEffect(() => {
+    updateActionBarPosition();
+    window.addEventListener('resize', updateActionBarPosition);
+    window.addEventListener('scroll', updateActionBarPosition, true);
+    let resizeObserver: ResizeObserver | null = null;
+    if (deptContentRef.current && typeof ResizeObserver !== 'undefined') {
+      resizeObserver = new ResizeObserver(() => {
+        updateActionBarPosition();
+      });
+      resizeObserver.observe(deptContentRef.current);
+    }
+    return () => {
+      window.removeEventListener('resize', updateActionBarPosition);
+      window.removeEventListener('scroll', updateActionBarPosition, true);
+      if (resizeObserver && deptContentRef.current) {
+        resizeObserver.unobserve(deptContentRef.current);
+        resizeObserver.disconnect();
+      }
+    };
+  }, []);
+
+  // 获取左侧查核组数据
+  useEffect(() => {
+    fetchGroupData();
+  }, []);
+
+  const fetchGroupData = async (
+    page: number = 1,
+    isLoadMore: boolean = false,
+    keyword?: string,
+  ) => {
+    try {
+      setLoading(true);
+      // 如果传入了keyword参数,使用它;否则使用状态中的groupSearchKeywords
+      const searchKeyword =
+        keyword !== undefined ? keyword : groupSearchKeywords;
+      const res = await getChackGroupData({
+        current: page,
+        pageSize: 100,
+        groupName: searchKeyword,
+      });
+
+      if (res) {
+        // 转换数据格式,符合树形组件需要的格式,同时保留原始数据
+        const transformedData = res.list.map((item: any) => ({
+          title: item.name,
+          key: item.id,
+          type: 'checkGroup', // 标记为查核组
+          // 保留原始数据
+          id: item.id,
+          name: item.name,
+          groupManagerName: item.groupManagerName,
+          remark: item.remark,
+          createUserName: item.createUserName,
+        }));
+
+        if (isLoadMore) {
+          // 加载更多时,追加到现有数据
+          setGroupData((prev) => [...prev, ...transformedData]);
+          setFilteredGroupData((prev) => [...prev, ...transformedData]);
+        } else {
+          // 首次加载或搜索时,替换数据
+          setGroupData(transformedData);
+          setFilteredGroupData(transformedData);
+
+          // 默认选中第一个
+          if (transformedData.length > 0) {
+            setSelectedGroup(transformedData[0]);
+          }
+        }
+
+        // 更新分页信息
+        setTotalCount(res.totalCount);
+        setCurrentPage(page);
+        setHasMore(res.list.length === 100 && page * 100 < res.totalCount);
+      }
+    } catch (error) {
+      message.error('获取查核组数据失败');
+    } finally {
+      setLoading(false);
+    }
+  };
+
+  // 本地过滤组员数据
+  const filterEmployeeData = (data: any[], keyword: string) => {
+    if (!keyword.trim()) {
+      return data;
+    }
+    return data.filter(
+      (emp) =>
+        emp.employeeId?.toString().includes(keyword) ||
+        emp.employeeName?.includes(keyword),
+    );
+  };
+
+  // 监听搜索关键词变化,进行本地过滤
+  useEffect(() => {
+    const filtered = filterEmployeeData(
+      allEmployeeList,
+      employeeSearchKeywords,
+    );
+    setFilteredEmployeeList(filtered);
+  }, [allEmployeeList, employeeSearchKeywords]);
+
+  // 监听选中项变化,加载组员数据和查核要点数据
+  useEffect(() => {
+    if (selectedGroup?.key) {
+      loadEmployeeData();
+      loadCheckpointData();
+    } else {
+      setAllEmployeeList([]);
+      setFilteredEmployeeList([]);
+      setAllCheckpointList([]);
+      setFilteredCheckpointList([]);
+    }
+  }, [selectedGroup?.key]);
+
+  // 加载组员数据
+  const loadEmployeeData = async () => {
+    if (!selectedGroup?.id) return;
+
+    try {
+      const result = await getCheckGroupEmployees({
+        groupId: selectedGroup.id,
+      });
+      if (result.success) {
+        setAllEmployeeList(result.data || []);
+      }
+    } catch (error) {
+      console.error('加载组员数据失败:', error);
+      setAllEmployeeList([]);
+    }
+  };
+
+  // 本地过滤查核要点数据
+  const filterCheckpointData = (data: any[], keyword: string) => {
+    if (!keyword.trim()) {
+      return data;
+    }
+    return data.filter(
+      (item) =>
+        item.pointName?.includes(keyword) ||
+        item.checkItemName?.includes(keyword),
+    );
+  };
+
+  // 监听查核要点搜索关键词变化,进行本地过滤
+  useEffect(() => {
+    const filtered = filterCheckpointData(
+      allCheckpointList,
+      checkpointSearchKeywords,
+    );
+    setFilteredCheckpointList(filtered);
+  }, [allCheckpointList, checkpointSearchKeywords]);
+
+  // 加载查核要点数据
+  const loadCheckpointData = async () => {
+    if (!selectedGroup?.id) return;
+
+    try {
+      const result = await getCheckGroupCheckpoints({
+        groupId: selectedGroup.id,
+      });
+      if (result.success) {
+        setAllCheckpointList(result.data || []);
+      }
+    } catch (error) {
+      console.error('加载查核要点数据失败:', error);
+      setAllCheckpointList([]);
+    }
+  };
+
+  // 处理查核组搜索 - 点击搜索图标或回车时触发
+  const handleGroupSearch = () => {
+    setCurrentPage(1);
+    setHasMore(true);
+    fetchGroupData(1, false);
+  };
+
+  // 处理搜索输入框的变化
+  const handleSearchInputChange = (value: string) => {
+    setGroupSearchKeywords(value);
+    // 如果清空搜索,立即重新加载数据,传入空字符串确保清空搜索
+    if (!value.trim()) {
+      setCurrentPage(1);
+      setHasMore(true);
+      // 直接传入空字符串,而不是依赖状态更新
+      fetchGroupData(1, false, '');
+    }
+  };
+
+  // 加载更多数据
+  const loadMoreData = () => {
+    if (!loading && hasMore) {
+      const nextPage = currentPage + 1;
+      fetchGroupData(nextPage, true);
+    }
+  };
+
+  // 滚动事件处理
+  const handleScroll = (e: React.UIEvent<HTMLDivElement>) => {
+    const { scrollTop, scrollHeight, clientHeight } = e.currentTarget;
+    // 当滚动到底部附近时加载更多数据
+    if (scrollHeight - scrollTop <= clientHeight + 10) {
+      loadMoreData();
+    }
+  };
+
+  // 新增查核组弹窗组件
+  const AddCheckGroupModal = () => {
+    return (
+      <ModalForm
+        title="新增查核组"
+        width={400}
+        trigger={<Button icon={<PlusOutlined />} className="add-button" />}
+        onFinish={async (val: any) => {
+          try {
+            await addData(val);
+            message.success('新增成功!');
+            fetchGroupData(1, false); // 重新获取数据
+            return true;
+          } catch (error) {
+            message.error('新增失败');
+            return false;
+          }
+        }}
+        modalProps={{
+          destroyOnClose: true,
+          maskClosable: false,
+        }}
+      >
+        <ProFormText
+          name="name"
+          label="查核组"
+          placeholder="请输入"
+          rules={[{ required: true, message: '查核组不能为空!' }]}
+        />
+      </ModalForm>
+    );
+  };
+
+  // 更多操作菜单
+  const getMoreMenu = (node: any) => (
+    <Menu>
+      <Menu.Item key="edit">
+        <ModalForm
+          title="编辑查核组"
+          width={400}
+          initialValues={{
+            name: node.name,
+          }}
+          trigger={<span>编辑</span>}
+          onFinish={async (val: any) => {
+            try {
+              await editData({
+                id: node.id,
+                name: val.name,
+              });
+              message.success('编辑成功!');
+              fetchGroupData(1, false);
+              return true;
+            } catch (error) {
+              message.error('编辑失败');
+              return false;
+            }
+          }}
+          modalProps={{ destroyOnClose: true }}
+        >
+          <ProFormText
+            name="name"
+            label="查核组"
+            placeholder="请输入"
+            rules={[{ required: true, message: '查核组不能为空!' }]}
+          />
+        </ModalForm>
+      </Menu.Item>
+      <Menu.Item key="delete">
+        <a
+          onClick={() => {
+            // 删除确认
+            Modal.confirm({
+              title: '确认删除',
+              content: `确定要删除查核组"${node.name}"吗?`,
+              okText: '确定',
+              cancelText: '取消',
+              onOk: async () => {
+                try {
+                  await delData(node.id);
+                  message.success('删除成功!');
+                  fetchGroupData(1, false);
+                  // 如果删除的是当前选中的组,清空选中状态
+                  if (selectedGroup?.id === node.id) {
+                    setSelectedGroup(null);
+                  }
+                } catch (error) {
+                  message.error('删除失败');
+                }
+              },
+            });
+          }}
+        >
+          删除
+        </a>
+      </Menu.Item>
+    </Menu>
+  );
+
+  // 左侧查核组树形组件渲染
+  const renderGroupTree = () => {
+    return (
+      <div className="dept-tree">
+        <div className="search-wrapper">
+          <Input
+            placeholder="查核组"
+            suffix={
+              <SearchOutlined
+                style={{ color: '#99A6BF', cursor: 'pointer' }}
+                onClick={handleGroupSearch}
+              />
+            }
+            className="search-input"
+            value={groupSearchKeywords}
+            onChange={(e) => handleSearchInputChange(e.target.value)}
+            onPressEnter={handleGroupSearch}
+            allowClear
+          />
+          <AddCheckGroupModal />
+        </div>
+
+        <div className="dept-list" onScroll={handleScroll}>
+          {filteredGroupData.map((item) => (
+            <div key={item.key} className="dept-group">
+              <div
+                className={`dept-leaf ${
+                  selectedGroup?.key === item.key ? 'selected' : ''
+                }`}
+                onClick={() => {
+                  setSelectedGroup(item);
+                }}
+                onMouseEnter={() => setHoveredNode(item.key)}
+                onMouseLeave={() => setHoveredNode(null)}
+              >
+                <span>{item.title}</span>
+                {(selectedGroup?.key === item.key ||
+                  hoveredNode === item.key ||
+                  openDropdownNode === item.key) && (
+                  <Dropdown
+                    overlay={getMoreMenu(item)}
+                    trigger={['click']}
+                    placement="bottomRight"
+                    overlayClassName="check-unit-add-dropdown"
+                    onVisibleChange={(visible) => {
+                      if (visible) {
+                        setOpenDropdownNode(item.key);
+                      } else {
+                        setOpenDropdownNode(null);
+                      }
+                    }}
+                  >
+                    <Button
+                      type="text"
+                      icon={<MoreOutlined />}
+                      className="more-button"
+                      onClick={(e) => e.stopPropagation()}
+                    />
+                  </Dropdown>
+                )}
+              </div>
+            </div>
+          ))}
+
+          {/* 加载更多提示 */}
+          {loading && (
+            <div className="loading-more">
+              <span>加载中...</span>
+            </div>
+          )}
+
+          {!hasMore && filteredGroupData.length > 0 && (
+            <div className="no-more-data">
+              <span>已加载全部数据(共{totalCount}条)</span>
+            </div>
+          )}
+        </div>
+      </div>
+    );
+  };
+
+  // 设置组长
+  const handleSetManager = async (employeeId: number) => {
+    if (!selectedGroup?.id) return;
+
+    try {
+      const result = await setGroupManager(selectedGroup.id, employeeId);
+      // 检查返回值,如果是false说明业务失败
+      if (result === false) {
+        return; // 错误消息已在响应拦截器中显示,直接返回
+      }
+      message.success('设置组长成功!');
+      // 只需要刷新组员表格数据
+      loadEmployeeData();
+    } catch (error) {
+      message.error('设置组长失败');
+    }
+  };
+
+  // 处理删除查核要点
+  const handleDeleteCheckpoint = async (record: any) => {
+    try {
+      const result = await delCheckGroupCheckpoints([record.originalPointId]);
+      // 检查返回值,如果是false说明业务失败
+      if (result === false) {
+        return; // 错误消息已在响应拦截器中显示,直接返回
+      }
+      message.success('删除要点成功');
+      loadCheckpointData();
+    } catch (error) {
+      message.error('删除要点失败');
+    }
+  };
+
+  // 计算全选状态
+  const getAllSelectableKeys = () => {
+    return filteredCheckpointList
+      .filter((item) => item.rowSpan > 0) // 只获取合并行的ID
+      .map((item) => item.id);
+  };
+
+  const allSelectableKeys = getAllSelectableKeys();
+  const isAllSelected =
+    allSelectableKeys.length > 0 &&
+    allSelectableKeys.every((key) => selectedCheckpointKeys.includes(key));
+  const isIndeterminate =
+    selectedCheckpointKeys.length > 0 &&
+    selectedCheckpointKeys.length < allSelectableKeys.length;
+
+  // 处理全选/取消全选
+  const handleSelectAll = (checked: boolean) => {
+    if (checked) {
+      setSelectedCheckpointKeys(allSelectableKeys);
+    } else {
+      setSelectedCheckpointKeys([]);
+    }
+  };
+
+  // 查核要点表格列定义
+  const checkpointColumns: ProColumns[] = [
+    {
+      title: (
+        <div style={{ display: 'flex', alignItems: 'center' }}>
+          <Checkbox
+            checked={isAllSelected}
+            indeterminate={isIndeterminate}
+            onChange={(e) => handleSelectAll(e.target.checked)}
+          />
+          <span style={{ marginLeft: 8 }}>要点名称</span>
+        </div>
+      ),
+      dataIndex: 'pointName',
+      width: 200,
+      align: 'left' as 'left',
+      render: (dom: React.ReactNode, record: any) => {
+        if (record.rowSpan > 0) {
+          return {
+            children: (
+              <div style={{ display: 'flex', alignItems: 'center' }}>
+                <Checkbox
+                  checked={selectedCheckpointKeys.includes(record.id)}
+                  onChange={() => {
+                    if (selectedCheckpointKeys.includes(record.id)) {
+                      setSelectedCheckpointKeys(
+                        selectedCheckpointKeys.filter((k) => k !== record.id),
+                      );
+                    } else {
+                      setSelectedCheckpointKeys([
+                        ...selectedCheckpointKeys,
+                        record.id,
+                      ]);
+                    }
+                  }}
+                />
+                <span style={{ marginLeft: 8 }}>{dom}</span>
+              </div>
+            ),
+            props: { rowSpan: record.rowSpan },
+          };
+        }
+        return { children: null, props: { rowSpan: 0 } };
+      },
+    },
+    {
+      title: '查核项名称',
+      dataIndex: 'checkItemName',
+      align: 'left' as 'left',
+    },
+    {
+      title: '操作',
+      dataIndex: 'option',
+      width: 80,
+      align: 'center' as 'center',
+      render: (_: any, record: any) => {
+        if (record.rowSpan > 0) {
+          return {
+            children: (
+              <Popconfirm
+                title="确认删除该要点及其所有查核项?"
+                okText="确定"
+                cancelText="取消"
+                onConfirm={() => handleDeleteCheckpoint(record)}
+              >
+                <a style={{ color: '#3377ff' }}>删除</a>
+              </Popconfirm>
+            ),
+            props: { rowSpan: record.rowSpan },
+          };
+        }
+        return { children: null, props: { rowSpan: 0 } };
+      },
+    },
+  ];
+
+  // 人员表格列定义
+  const employeeColumns: ProColumns[] = [
+    {
+      title: '工号',
+      dataIndex: 'employeeCode',
+      width: 120,
+    },
+    {
+      title: '姓名',
+      dataIndex: 'employeeName',
+      width: 120,
+    },
+    {
+      title: '组长',
+      dataIndex: 'isManager',
+      width: 100,
+      render: (text: any, record: any) => (
+        <Radio
+          checked={text === 1}
+          onChange={() => handleSetManager(record.employeeId)}
+        />
+      ),
+    },
+    {
+      title: '操作',
+      key: 'option',
+      width: 100,
+      valueType: 'option',
+      render: (_: any, record: any) => [
+        <a
+          key="delete"
+          style={{ color: '#3377FF' }}
+          onClick={async () => {
+            Modal.confirm({
+              title: '确认移除该人员吗?',
+              onOk: async () => {
+                try {
+                  const result = await delCheckGroupEmployee([record.id]);
+                  // 检查返回值,如果是false说明业务失败
+                  if (result === false) {
+                    return; // 错误消息已在响应拦截器中显示,直接返回
+                  }
+                  message.success('移除成功!');
+                  loadEmployeeData();
+                } catch (error) {
+                  message.error('移除失败');
+                }
+              },
+              okText: '确定',
+              cancelText: '取消',
+            });
+          }}
+        >
+          删除
+        </a>,
+      ],
+    },
+  ];
+
+  return (
+    <KCIMPagecontainer title={false}>
+      <div className="check-unit-mana">
+        <div className="check-unit-container">
+          {/* 左侧查核组树 */}
+          {renderGroupTree()}
+
+          {/* 右侧内容区 */}
+          <div className="dept-content" ref={deptContentRef}>
+            {selectedGroup && (
+              <Card className="pfm-ant-card" bordered={false}>
+                <Tabs defaultActiveKey="members">
+                  <Tabs.TabPane tab="组员" key="members">
+                    <div className="tab-content">
+                      <div className="toolbar">
+                        <div
+                          style={{
+                            display: 'flex',
+                            alignItems: 'center',
+                            gap: 8,
+                          }}
+                        >
+                          <span style={{ color: '#17181A', fontSize: 14 }}>
+                            检索:
+                          </span>
+                          <Input
+                            placeholder="工号、姓名"
+                            allowClear
+                            suffix={
+                              <SearchOutlined style={{ color: '#99A6BF' }} />
+                            }
+                            style={{ width: 250 }}
+                            value={employeeSearchKeywords}
+                            onChange={(e) => {
+                              setEmployeeSearchKeywords(e.target.value);
+                            }}
+                          />
+                        </div>
+                        <AddEmployeeModal
+                          selectedGroup={selectedGroup}
+                          onSuccess={() => loadEmployeeData()}
+                        />
+                      </div>
+
+                      <KCIMTable
+                        columns={employeeColumns as ProColumns[]}
+                        actionRef={employeeTableRef}
+                        rowKey="id"
+                        dataSource={filteredEmployeeList}
+                        search={false}
+                        options={false}
+                        pagination={false}
+                        rowSelection={{
+                          selectedRowKeys: selectedEmployeeKeys,
+                          onChange: (keys) =>
+                            setSelectedEmployeeKeys(keys as string[]),
+                        }}
+                        tableAlertRender={false}
+                      />
+                    </div>
+                  </Tabs.TabPane>
+                  <Tabs.TabPane tab="查核要点" key="checkpoints">
+                    <div className="tab-content">
+                      <div className="toolbar">
+                        <div
+                          style={{
+                            display: 'flex',
+                            alignItems: 'center',
+                            gap: 8,
+                          }}
+                        >
+                          <span style={{ color: '#17181A', fontSize: 14 }}>
+                            检索:
+                          </span>
+                          <Input
+                            placeholder="要点名称"
+                            allowClear
+                            suffix={
+                              <SearchOutlined style={{ color: '#99A6BF' }} />
+                            }
+                            style={{ width: 250 }}
+                            value={checkpointSearchKeywords}
+                            onChange={(e) => {
+                              setCheckpointSearchKeywords(e.target.value);
+                            }}
+                          />
+                        </div>
+                        <AddCheckpointModal
+                          selectedGroup={selectedGroup}
+                          onSuccess={() => loadCheckpointData()}
+                        />
+                      </div>
+
+                      <KCIMTable
+                        columns={checkpointColumns as ProColumns[]}
+                        actionRef={checkpointTableRef}
+                        rowKey="id"
+                        dataSource={filteredCheckpointList}
+                        search={false}
+                        options={false}
+                        pagination={false}
+                        bordered
+                        scroll={{ y: 'calc(100vh - 236px)' }}
+                        tableAlertRender={false}
+                      />
+                    </div>
+                  </Tabs.TabPane>
+                </Tabs>
+              </Card>
+            )}
+
+            {!selectedGroup && (
+              <div className="empty-state">
+                <div className="empty-content">
+                  <div className="empty-text">请选择左侧查核组查看详情</div>
+                </div>
+              </div>
+            )}
+          </div>
+        </div>
+      </div>
+
+      {/* 批量操作栏 - 固定在页面底部,宽度跟dept-content一致 */}
+      {(selectedEmployeeKeys.length > 0 ||
+        selectedCheckpointKeys.length > 0) && (
+        <div
+          style={{
+            position: 'fixed',
+            left: actionBarStyle.left + 16,
+            width: actionBarStyle.width - 16,
+            bottom: 16,
+            height: 56,
+            backgroundColor: '#fff',
+            zIndex: 1000,
+            boxShadow: 'rgba(0, 0, 0, 0.04) 0px -2px 8px',
+            borderRadius: 4,
+            display: 'flex',
+            alignItems: 'center',
+            justifyContent: 'space-between',
+            padding: '0 16px',
+            transition: 'left 0.2s, width 0.2s',
+          }}
+        >
+          <span style={{ color: '#3377FF', fontWeight: 500 }}>
+            已选 {selectedEmployeeKeys.length + selectedCheckpointKeys.length}{' '}
+            项
+          </span>
+          <div style={{ display: 'flex', gap: 8 }}>
+            <Button
+              onClick={() => {
+                setSelectedEmployeeKeys([]);
+                setSelectedCheckpointKeys([]);
+              }}
+              style={{ height: 32, borderRadius: 4, fontSize: 14 }}
+              size="small"
+            >
+              取消选择
+            </Button>
+            {selectedEmployeeKeys.length > 0 && (
+              <Popconfirm
+                title="确认批量移除所选人员?"
+                okText="确定"
+                cancelText="取消"
+                onConfirm={async () => {
+                  try {
+                    const result = await delCheckGroupEmployee(
+                      selectedEmployeeKeys,
+                    );
+                    // 检查返回值,如果是false说明业务失败
+                    if (result === false) {
+                      return; // 错误消息已在响应拦截器中显示,直接返回
+                    }
+                    message.success('批量移除成功');
+                    setSelectedEmployeeKeys([]);
+                    loadEmployeeData();
+                  } catch (e) {
+                    message.error('批量移除失败');
+                  }
+                }}
+              >
+                <Button
+                  type="primary"
+                  style={{
+                    background: '#3377FF',
+                    borderColor: '#3377FF',
+                    height: 32,
+                    borderRadius: 4,
+                    fontSize: 14,
+                  }}
+                  size="small"
+                >
+                  批量移除人员
+                </Button>
+              </Popconfirm>
+            )}
+            {selectedCheckpointKeys.length > 0 && (
+              <Popconfirm
+                title="确认批量删除所选查核要点?"
+                okText="确定"
+                cancelText="取消"
+                onConfirm={async () => {
+                  try {
+                    // 从选中的ID中提取原始要点ID(去重)
+                    const originalIds = Array.from(
+                      new Set(
+                        selectedCheckpointKeys
+                          .map((id) => {
+                            const record = filteredCheckpointList.find(
+                              (item: any) => item.id === id,
+                            );
+                            return record?.originalPointId;
+                          })
+                          .filter((id) => id !== undefined),
+                      ),
+                    );
+
+                    const result = await delCheckGroupCheckpoints(originalIds);
+                    // 检查返回值,如果是false说明业务失败
+                    if (result === false) {
+                      return; // 错误消息已在响应拦截器中显示,直接返回
+                    }
+                    message.success('批量删除成功');
+                    setSelectedCheckpointKeys([]);
+                    loadCheckpointData();
+                  } catch (e) {
+                    message.error('批量删除失败');
+                  }
+                }}
+              >
+                <Button
+                  type="primary"
+                  style={{
+                    background: '#3377FF',
+                    borderColor: '#3377FF',
+                    height: 32,
+                    borderRadius: 4,
+                    fontSize: 14,
+                  }}
+                  size="small"
+                >
+                  批量删除要点
+                </Button>
+              </Popconfirm>
+            )}
+          </div>
+        </div>
+      )}
+    </KCIMPagecontainer>
+  );
+};
+
+export default CheckGroupMana;

+ 347 - 0
src/pages/baseInfoMana/checkGroupMana/service.ts

@@ -0,0 +1,347 @@
+/*
+ * @Author: code4eat awesomedema@gmail.com
+ * @Date: 2023-03-03 16:31:27
+ * @LastEditors: code4eat awesomedema@gmail.com
+ * @LastEditTime: 2025-06-24 11:26:30
+ * @FilePath: /KC-MiddlePlatform/src/pages/platform/setting/pubDicTypeMana/service.ts
+ * @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
+ */
+
+import { request } from 'umi';
+
+//获取table列表数据
+
+export type FenyeTemplateItemType = {
+  id: number;
+  hiId: number;
+  name: string;
+  content: string;
+  remark: string;
+  createUserId: number;
+  createUserName: string;
+  date: string;
+};
+
+export const getChackGroupData = (params?: any) => {
+  return request<{
+    currPage: number;
+    current: number;
+    list: any[];
+    pageSize: number;
+    totalCount: number;
+    totalPage: number;
+  }>('/manager/CheckGroup/getCheckGroup', {
+    method: 'GET',
+    params: { ...params },
+  });
+};
+
+//新增表格数据
+export type AddTableDataType = {
+  name: string;
+};
+export const addData = (data: AddTableDataType) => {
+  return request('/manager/CheckGroup/addCheckGroup', {
+    method: 'POST',
+    data,
+  });
+};
+
+//编辑表格数据
+export const editData = (data: { id: number; name: string }) => {
+  return request('/manager/CheckGroup/updateCheckGroup', {
+    method: 'POST',
+    data,
+  });
+};
+
+//删除表格操作
+export const delData = (id: number) => {
+  return request('/manager/CheckGroup/deleteCheckGroup', {
+    method: 'POST',
+    data: [id],
+  });
+};
+
+// 获取查核组人员列表
+export const getCheckGroupEmployees = async (params: any) => {
+  try {
+    const { groupId } = params;
+
+    const res = await request('/manager/CheckGroup/getGroupEmployee', {
+      method: 'GET',
+      params: {
+        groupId: groupId || '',
+      },
+    });
+
+    // 根据截图1,接口直接返回数组,不是分页格式
+    const dataList = Array.isArray(res) ? res : [];
+
+    return {
+      data: dataList,
+      success: true,
+      total: dataList.length,
+    };
+  } catch (error) {
+    console.error('获取查核组人员失败:', error);
+    throw error;
+  }
+};
+
+// 获取可添加的人员列表
+export const getAvailableEmployees = async (
+  groupId: number,
+  filter: string = '',
+  current: number = 1,
+  pageSize: number = 10,
+) => {
+  try {
+    const res = await request(
+      '/manager/CheckGroup/getGroupPendingPageEmployee',
+      {
+        method: 'GET',
+        params: {
+          groupId,
+          filter,
+          current,
+          pageSize,
+        },
+      },
+    );
+
+    return {
+      list: res?.list || [],
+      total: res?.totalCount || 0,
+      success: true,
+    };
+  } catch (error) {
+    throw error;
+  }
+};
+
+// 添加查核组人员
+export const addCheckGroupEmployee = async (
+  groupId: number,
+  employees: any[],
+) => {
+  try {
+    const res = await request('/manager/CheckGroup/addGroupEmployee', {
+      method: 'POST',
+      data: {
+        id: groupId.toString(),
+        name: '', // 查核组名称,如果需要的话
+        mapObjList: employees.map((emp) => ({
+          id: emp.id.toString(),
+          name: emp.name,
+        })),
+      },
+    });
+
+    return res;
+  } catch (error) {
+    throw error;
+  }
+};
+
+// 删除查核组人员
+export const delCheckGroupEmployee = async (employeeIds: React.Key[]) => {
+  try {
+    const res = await request('/manager/CheckGroup/delGroupEmp', {
+      method: 'POST',
+      data: employeeIds,
+    });
+
+    return res;
+  } catch (error) {
+    throw error;
+  }
+};
+
+// 设置查核组组长
+export const setGroupManager = async (groupId: number, employeeId: number) => {
+  try {
+    const res = await request('/manager/CheckGroup/setGroupManager', {
+      method: 'POST',
+      data: {
+        checkGroupId: groupId,
+        employeeId: employeeId,
+      },
+    });
+
+    return res;
+  } catch (error) {
+    throw error;
+  }
+};
+
+// 获取查核组的查核要点数据
+export const getCheckGroupCheckpoints = async (params: any) => {
+  try {
+    const { groupId, filter = '' } = params;
+
+    const res = await request('/manager/CheckGroup/getGroupCheckpoint', {
+      method: 'GET',
+      params: {
+        groupId: groupId || '',
+        filter: filter,
+      },
+    });
+
+    // 转换数据格式以适配表格的合并行结构
+    const convertToMergedRowData = (items: any[]): any[] => {
+      if (!items || !Array.isArray(items)) return [];
+
+      const flatData: any[] = [];
+
+      items.forEach((point: any) => {
+        if (
+          Array.isArray(point.checkItemList) &&
+          point.checkItemList.length > 0
+        ) {
+          // 如果有查核项,为每个查核项创建一行
+          point.checkItemList.forEach((item: any, idx: number) => {
+            flatData.push({
+              id: idx === 0 ? String(point.id) : `${point.id}_item_${idx}`,
+              pointName: point.checkPointName, // 使用checkPointName字段
+              checkItemName: item.name,
+              rowSpan: idx === 0 ? point.checkItemList.length : 0, // 第一行显示合并行数,其他行为0
+              isFirst: idx === 0, // 标记是否为第一行
+              originalPointId: point.id, // 保存原始关联ID用于删除
+              checkPointId: point.checkPointId, // 保存查核要点ID
+            });
+          });
+        } else {
+          // 如果没有查核项,创建一行显示要点名称
+          flatData.push({
+            id: String(point.id),
+            pointName: point.checkPointName, // 使用checkPointName字段
+            checkItemName: '-',
+            rowSpan: 1,
+            isFirst: true,
+            originalPointId: point.id, // 保存原始关联ID用于删除
+            checkPointId: point.checkPointId, // 保存查核要点ID
+          });
+        }
+      });
+
+      return flatData;
+    };
+
+    // 处理数据
+    let dataArray = [];
+
+    if (Array.isArray(res)) {
+      // 直接是数组
+      dataArray = convertToMergedRowData(res);
+    } else if (res && res.data && Array.isArray(res.data)) {
+      // 数据在data字段中
+      dataArray = convertToMergedRowData(res.data);
+    } else if (res && res.list && Array.isArray(res.list)) {
+      // 数据在list字段中
+      dataArray = convertToMergedRowData(res.list);
+    } else if (res && typeof res === 'object') {
+      // 如果是单个对象,包装成数组
+      dataArray = convertToMergedRowData([res]);
+    }
+
+    return {
+      data: dataArray,
+      success: true,
+      total: dataArray.length,
+    };
+  } catch (error) {
+    console.error('获取查核组查核要点失败:', error);
+    throw error;
+  }
+};
+
+// 获取可添加的查核要点列表
+export const getGroupPendingCheckpoints = async (
+  groupId: number,
+  filter: string = '',
+  current: number = 1,
+  pageSize: number = 10,
+) => {
+  try {
+    const res = await request(
+      '/manager/CheckGroup/getGroupPendingPageCheckpoint',
+      {
+        method: 'GET',
+        params: {
+          groupId,
+          filter,
+          current,
+          pageSize,
+        },
+      },
+    );
+
+    // 直接按“要点”为单位返回,不做铺平,保持与接口分页一致
+    let list: any[] = [];
+    let total = 0;
+
+    if (res && res.data && Array.isArray(res.data.list)) {
+      list = res.data.list;
+      total = res.data.totalCount || res.data.list.length || 0;
+    } else if (Array.isArray(res?.list)) {
+      list = res.list;
+      total = res.totalCount || res.total || res.list.length || 0;
+    } else if (Array.isArray(res)) {
+      list = res;
+      total = res.length;
+    } else if (Array.isArray(res?.data)) {
+      list = res.data;
+      total = res.total || res.totalCount || res.data.length || 0;
+    } else if (res && typeof res === 'object') {
+      list = [res];
+      total = 1;
+    }
+
+    return {
+      list,
+      total,
+      success: true,
+    };
+  } catch (error) {
+    throw error;
+  }
+};
+
+// 添加查核组查核要点
+export const addCheckGroupCheckpoints = async (
+  groupId: number,
+  checkpointIds: number[],
+) => {
+  try {
+    const res = await request('/manager/CheckGroup/addGroupCheckpoint', {
+      method: 'POST',
+      data: {
+        id: groupId,
+        name: '', // 查核组名称,如果需要的话
+        mapObjList: checkpointIds.map((id) => ({
+          id: id,
+          name: '', // 要点名称,如果需要的话
+        })),
+      },
+    });
+
+    return res;
+  } catch (error) {
+    throw error;
+  }
+};
+
+// 删除查核组查核要点
+export const delCheckGroupCheckpoints = async (checkpointIds: React.Key[]) => {
+  try {
+    const res = await request('/manager/CheckGroup/delGroupCheckpoint', {
+      method: 'POST',
+      data: checkpointIds,
+    });
+
+    return res;
+  } catch (error) {
+    throw error;
+  }
+};

+ 340 - 0
src/pages/baseInfoMana/checkGroupMana/style.less

@@ -0,0 +1,340 @@
+// 查核组管理页面样式(参考查核要点管理界面)
+
+.check-unit-mana {
+  .check-unit-container {
+    display: flex;
+    height: 100%;
+    padding: 16px;
+  }
+
+  // 左侧查核组树样式
+  .dept-tree {
+    display: flex;
+    flex-direction: column;
+    width: 220px;
+    min-width: 220px;
+    max-width: 220px;
+    background: #fff;
+    border-radius: 4px;
+    height: calc(100vh - 80px);
+    overflow: hidden;
+
+    .search-wrapper {
+      display: flex;
+      align-items: center;
+      padding: 12px;
+      padding-bottom: 8px;
+      flex-shrink: 0; // 防止搜索栏被压缩
+
+      .search-input {
+        flex: 1;
+        border-radius: 4px;
+        border: 1px solid #d9d9d9;
+
+        .ant-input {
+          border: none;
+          box-shadow: none;
+        }
+
+        &:hover,
+        &:focus-within {
+          border-color: #3377ff;
+        }
+      }
+
+      .add-button {
+        margin-left: 8px;
+        width: 24px;
+        height: 24px;
+        background: #fafcff;
+        border-radius: 4px;
+        border: 1px solid #dae2f2;
+        padding: 0;
+        display: flex;
+        align-items: center;
+        justify-content: center;
+      }
+    }
+
+    .dept-list {
+      flex: 1;
+      overflow-y: auto;
+      max-height: calc(100vh - 140px); // 限制最大高度,确保滚动可用
+
+      // 加载更多提示样式
+      .loading-more {
+        text-align: center;
+        padding: 12px;
+        color: #999;
+        font-size: 12px;
+
+        span {
+          color: #999;
+        }
+      }
+
+      .no-more-data {
+        text-align: center;
+        padding: 12px;
+        color: #999;
+        font-size: 12px;
+        border-top: 1px solid #f0f0f0;
+        margin-top: 8px;
+
+        span {
+          color: #999;
+        }
+      }
+
+      .dept-group {
+        margin-bottom: 0;
+
+        .dept-leaf {
+          display: flex;
+          align-items: center;
+          justify-content: space-between;
+          padding-left: 8px;
+          padding-right: 8px;
+          margin: 0 8px;
+          cursor: pointer;
+          height: 32px;
+          box-sizing: border-box;
+          border-radius: 4px;
+          position: relative;
+
+          > span {
+            flex: 1;
+            font-size: 14px;
+            color: #17181a;
+            overflow: hidden;
+            text-overflow: ellipsis;
+            white-space: nowrap;
+            margin-right: 8px;
+          }
+
+          .more-button {
+            position: absolute;
+            top: 8px;
+            right: 8px;
+            padding: 0;
+            width: 16px;
+            height: 16px;
+            display: flex;
+            align-items: center;
+            justify-content: center;
+            color: #99a6bf;
+            border-radius: 4px;
+            background: #fff;
+            opacity: 0;
+            transition: opacity 0.2s;
+
+            .anticon {
+              transform: rotate(90deg);
+            }
+
+            &:hover {
+              background: #fff;
+            }
+          }
+
+          &:hover {
+            background-color: #f0f2f5;
+
+            .more-button {
+              opacity: 1;
+            }
+          }
+
+          &.selected {
+            background: #f0f2f5;
+            border: none;
+
+            > span {
+              color: #17181a;
+              font-weight: 500;
+            }
+
+            .more-button {
+              opacity: 1;
+            }
+          }
+        }
+      }
+    }
+  }
+
+  // 右侧内容区样式
+  .dept-content {
+    flex: 1;
+    padding-left: 16px;
+    height: 100%;
+    overflow: auto;
+
+    .pfm-ant-card {
+      box-shadow: none;
+      background: #fff;
+      border-radius: 4px;
+
+      .pfm-ant-card-body {
+        padding: 16px;
+        padding-top: 0;
+      }
+    }
+
+    .pfm-ant-tabs-nav {
+      margin-bottom: 16px;
+    }
+
+    .tab-content {
+      .toolbar {
+        display: flex;
+        justify-content: space-between;
+        align-items: center;
+        margin-bottom: 16px;
+      }
+
+      .add-btn {
+        background-color: #3377ff;
+        border-color: #3377ff;
+        border-radius: 4px;
+        height: 24px;
+        box-shadow: none;
+        font-weight: 400;
+        font-size: 14px;
+        color: #ffffff;
+        width: 56px;
+        padding: 0;
+        line-height: 22px;
+
+        &:hover {
+          background-color: #5591ff;
+          border-color: #5591ff;
+        }
+
+        &:active {
+          background-color: #1b5ee6;
+          border-color: #1b5ee6;
+        }
+      }
+    }
+
+    .tab-content {
+      .toolbar {
+        display: flex;
+        justify-content: space-between;
+        align-items: center;
+        margin-bottom: 16px;
+      }
+
+      .add-btn {
+        background-color: #3377ff;
+        border-color: #3377ff;
+        border-radius: 4px;
+        height: 24px;
+        box-shadow: none;
+        font-weight: 400;
+        font-size: 14px;
+        color: #ffffff;
+        width: 56px;
+        padding: 0;
+        line-height: 22px;
+
+        &:hover {
+          background-color: #5591ff;
+          border-color: #5591ff;
+        }
+
+        &:active {
+          background-color: #1b5ee6;
+          border-color: #1b5ee6;
+        }
+      }
+    }
+
+    .empty-state {
+      height: calc(100vh - 96px);
+      display: flex;
+      align-items: center;
+      justify-content: center;
+
+      .empty-content {
+        text-align: center;
+
+        .empty-text {
+          font-size: 14px;
+          color: #8c8c8c;
+        }
+      }
+    }
+  }
+
+  // 表格样式
+  /* 表格样式已统一至 KCIMTable/style.less */
+
+  .delete-btn {
+    color: #3377ff;
+  }
+
+  // 表格内的删除链接样式
+  .pfm-ant-table-tbody {
+    tr {
+      td {
+        a {
+          color: #3377ff;
+          text-decoration: none;
+
+          &:hover {
+            color: #5591ff;
+          }
+        }
+      }
+    }
+  }
+
+  // 员工选择相关样式
+  .employee-select-modal {
+    .pfm-ant-modal-header {
+      border-bottom: 1px solid #f0f0f0;
+      padding: 12px 16px;
+
+      .pfm-ant-modal-title {
+        font-size: 16px;
+        font-weight: 600;
+        color: #17181a;
+      }
+    }
+
+    .pfm-ant-modal-body {
+      padding: 16px;
+    }
+
+    .pfm-ant-modal-footer {
+      padding: 12px 16px;
+      border-top: 1px solid #f0f0f0;
+
+      .pfm-ant-btn {
+        height: 32px;
+        border-radius: 4px;
+        font-size: 14px;
+      }
+    }
+  }
+}
+
+// 更多操作下拉菜单样式
+.check-unit-add-dropdown {
+  .pfm-ant-dropdown-menu {
+    border-radius: 4px;
+    overflow: hidden;
+    padding: 4px;
+  }
+
+  .pfm-ant-dropdown-menu-item {
+    &:hover,
+    &.pfm-ant-dropdown-menu-item-selected {
+      background: rgba(51, 119, 255, 0.08);
+      color: #17181a;
+      border-radius: 4px;
+    }
+  }
+}

+ 133 - 0
src/pages/baseInfoMana/checkGroupMana/tableSelector.tsx

@@ -0,0 +1,133 @@
+import React, { useEffect, useState } from 'react';
+import { Input } from 'antd';
+import { KCIMTable } from '@/components/KCIMTable';
+import { createFromIconfontCN } from '@ant-design/icons';
+import { getCheckData } from '../checkGroupMana/service';
+import { getData } from '@/pages/setting/qualitativeOptionsMana/service';
+import { ModalForm } from '@ant-design/pro-components';
+
+// import './style.less';
+
+interface TableSelecterProps {
+  tableSelecterColumn: any[];
+  record: any;
+}
+
+const IconFont = createFromIconfontCN({
+  scriptUrl: '',
+});
+
+const TableSelecter = ({
+  tableSelecterColumn,
+  record,
+  open,
+  title,
+  onVisibleChange,
+  rowKey = 'id',
+  request,
+  onFinish,
+}: {
+  tableSelecterColumn: any[];
+  record: any;
+  open: boolean;
+  title: string;
+  onVisibleChange: (bool: boolean) => void;
+  rowKey?: string;
+  request?: (params: any) => Promise<any>;
+  onFinish?: (selectedKeys: React.Key[], selectedRows: any[]) => void;
+}) => {
+  const Table = React.forwardRef(({}: TableSelecterProps, ref) => {
+    const [datasource, set_datasource] = useState<any[]>([]);
+    const [selectedKeys, setSelectedKeys] = useState<React.Key[]>([]);
+    const [selectedRows, set_selectedRows] = useState<any[]>([]);
+    const [showList, set_showList] = useState<any[]>([]);
+
+    const onSelectChange = (
+      newSelectedRowKeys: React.Key[],
+      selectedRows: any,
+    ) => {
+      setSelectedKeys([...newSelectedRowKeys]);
+    };
+
+    const saveHandle = async () => {
+      onFinish && onFinish(selectedKeys, selectedRows);
+    };
+
+    return (
+      <div>
+        <Input
+          placeholder={'请输入项目名称'}
+          allowClear
+          suffix={<IconFont style={{ color: '#99A6BF' }} type="iconsousuo" />}
+          style={{ marginBottom: 8 }}
+          onChange={(e) => {
+            if (e.target.value.length != 0) {
+              const result = datasource.filter(
+                (item) => item.indicatorName.indexOf(e.target.value) != -1,
+              );
+              set_showList(result);
+            } else {
+              set_showList(datasource);
+            }
+          }}
+        />
+        <KCIMTable
+          columns={tableSelecterColumn}
+          options={{
+            density: true,
+            setting: {
+              listsHeight: 100,
+            },
+          }}
+          rowKey={rowKey}
+          scroll={{ y: 400 }}
+          tableAlertRender={false}
+          rowSelection={{
+            // 自定义选择项参考: https://ant.design/components/table-cn/#components-table-demo-row-selection-custom
+            // 注释该行则默认不显示下拉选项
+            // selections: [Table.SELECTION_ALL, Table.SELECTION_INVERT],
+            onChange: onSelectChange,
+          }}
+          pagination={{ showTitle: false, showSizeChanger: true }}
+          //dataSource={showList}
+          request={request ? async (params) => request(params) : undefined}
+        />
+        <div className="footer">
+          <span className="cancel" onClick={() => close()}>
+            取消
+          </span>
+          <span className="ok" onClick={() => saveHandle()}>{`确认(${
+            selectedKeys.length > 0 && selectedKeys.length
+          })`}</span>
+        </div>
+      </div>
+    );
+  });
+
+  const close = () => {
+    onVisibleChange && onVisibleChange(false);
+  };
+
+  return (
+    <ModalForm
+      className="TableSelecter"
+      title={title}
+      width={600}
+      submitter={{
+        render: false,
+      }}
+      open={open}
+      modalProps={{
+        closable: false,
+      }}
+    >
+      <Table
+        // ref={tableSelecterRef}
+        record={undefined}
+        tableSelecterColumn={tableSelecterColumn}
+      ></Table>
+    </ModalForm>
+  );
+};
+
+export default TableSelecter;

+ 0 - 0
src/pages/baseInfoMana/deptPersonnel/index.tsx


+ 64 - 0
src/pages/baseInfoMana/deptPersonnel/service.ts

@@ -0,0 +1,64 @@
+/*
+ * @Author: code4eat awesomedema@gmail.com
+ * @Date: 2025-06-10 12:15:25
+ * @LastEditors: code4eat awesomedema@gmail.com
+ * @LastEditTime: 2025-06-10 12:15:25
+ * @FilePath: /pfmBackMana/src/pages/baseInfoMana/deptPersonnel/service.ts
+ * @Description: 部门人员管理服务
+ */
+
+import { request } from 'umi';
+
+// 获取部门树
+export async function getDeptTree(params?: any) {
+  return request('/api/dept/tree', {
+    method: 'GET',
+    params,
+  });
+}
+
+// 获取部门详情
+export async function getDeptDetail(id: string) {
+  return request(`/api/dept/${id}`, {
+    method: 'GET',
+  });
+}
+
+// 获取部门人员列表
+export async function getDeptPersonnel(params: any) {
+  return request('/api/dept/personnel', {
+    method: 'GET',
+    params,
+  });
+}
+
+// 添加部门人员
+export async function addDeptPersonnel(data: any) {
+  return request('/api/dept/personnel', {
+    method: 'POST',
+    data,
+  });
+}
+
+// 删除部门人员
+export async function deleteDeptPersonnel(id: string) {
+  return request(`/api/dept/personnel/${id}`, {
+    method: 'DELETE',
+  });
+}
+
+// 设置部门负责人
+export async function setDeptManager(data: any) {
+  return request('/api/dept/manager', {
+    method: 'POST',
+    data,
+  });
+}
+
+// 获取部门责任事项
+export async function getDeptResponsibilities(params: any) {
+  return request('/api/dept/responsibilities', {
+    method: 'GET',
+    params,
+  });
+}

+ 101 - 0
src/pages/baseInfoMana/deptPersonnel/style.less

@@ -0,0 +1,101 @@
+.dept-personnel {
+  .dept-personnel-container {
+    display: flex;
+    height: calc(100vh - 160px);
+    background: #fff;
+    border-radius: 4px;
+    overflow: hidden;
+  }
+
+  // 左侧科室树样式
+  .dept-tree {
+    width: 200px;
+    border-right: 1px solid #eee;
+    padding: 16px 0;
+    background-color: #f8f9fa;
+    overflow-y: auto;
+
+    .search-container {
+      padding: 0 8px 16px;
+      display: flex;
+      align-items: center;
+
+      .pfm-ant-input-affix-wrapper {
+        flex: 1;
+      }
+
+      .pfm-ant-btn {
+        margin-left: 4px;
+      }
+    }
+
+    .pfm-ant-tree {
+      padding: 0 8px;
+    }
+  }
+
+  // 右侧内容区样式
+  .dept-content {
+    flex: 1;
+    padding: 16px;
+    overflow-y: auto;
+
+    .dept-header {
+      margin-bottom: 16px;
+      padding-bottom: 16px;
+      border-bottom: 1px solid #f0f0f0;
+
+      h2 {
+        font-size: 18px;
+        margin-bottom: 8px;
+        font-weight: 500;
+      }
+
+      p {
+        color: #666;
+        margin: 0;
+        font-size: 13px;
+      }
+    }
+
+    .pfm-ant-tabs-nav {
+      margin-bottom: 16px;
+    }
+
+    .tab-content {
+      .toolbar {
+        display: flex;
+        justify-content: space-between;
+        align-items: center;
+        margin-bottom: 16px;
+
+        .search {
+          width: 250px;
+        }
+      }
+    }
+  }
+
+  // 表格相关样式
+  .radio-dot {
+    display: flex;
+    justify-content: center;
+
+    .dot {
+      display: inline-block;
+      width: 16px;
+      height: 16px;
+      border-radius: 50%;
+      border: 1px solid #d9d9d9;
+
+      &.filled {
+        background-color: #1890ff;
+        border-color: #1890ff;
+      }
+    }
+  }
+
+  .delete-btn {
+    color: #3377ff;
+  }
+}

+ 262 - 0
src/pages/baseInfoMana/empMana/components/EmployeeList/EmployeeList.tsx

@@ -0,0 +1,262 @@
+import React, { FC, useState, useRef, useEffect } from 'react';
+import styled from 'styled-components';
+import { Input } from 'antd';
+import { createFromIconfontCN } from '@ant-design/icons';
+import { useEmployeeList } from './useEmployeeList';
+
+const IconFont = createFromIconfontCN({
+  scriptUrl: '',
+});
+
+const Container = styled.div`
+  width: 220px;
+  height: calc(100vh - 77px);
+  border-radius: 4px;
+  background-color: #fff;
+  display: flex;
+  padding: 0 8px;
+  box-sizing: border-box;
+  flex-direction: column;
+`;
+
+const SearchBar = styled.div`
+  display: flex;
+  flex-direction: row;
+  justify-content: space-between;
+  align-items: center;
+  padding: 16px 8px;
+`;
+
+const ListContainer = styled.div`
+  flex: 1;
+  overflow-y: auto;
+`;
+
+const FreshButton = styled.div<{ $syncing?: boolean }>`
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  background-color: ${({ $syncing }) => $syncing ? '#f5f5f5' : '#fafcff'};
+  border-radius: 4px;
+  width: 24px;
+  height: 24px;
+  border: 1px solid #dae2f2;
+  cursor: ${({ $syncing }) => $syncing ? 'not-allowed' : 'pointer'};
+  margin-left: 8px;
+  opacity: ${({ $syncing }) => $syncing ? 0.6 : 1};
+  
+  .icon {
+    animation: ${({ $syncing }) => $syncing ? 'spin 1s linear infinite' : 'none'};
+  }
+  
+  @keyframes spin {
+    from {
+      transform: rotate(0deg);
+    }
+    to {
+      transform: rotate(360deg);
+    }
+  }
+`;
+
+interface ListItemProps {
+  active?: boolean;
+}
+
+const ListItem = styled.div<ListItemProps>`
+  display: flex;
+  align-items: center;
+  padding: 8px;
+  cursor: pointer;
+  border-radius: 4px;
+  transition: background-color 0.2s ease;
+  background-color: ${({ active }) => (active ? '#F0F2F5' : 'transparent')};
+
+  &:hover {
+    background-color: #f0f2f5;
+  }
+
+  & > img {
+    width: 24px;
+    height: 24px;
+  }
+
+  .info {
+    margin-left: 8px;
+    display: flex;
+    flex-direction: column;
+
+    .name {
+      font-size: 14px;
+      height: 14px;
+      line-height: 14px;
+      color: #17181a;
+      font-weight: bold;
+      margin-bottom: 4px;
+    }
+
+    .id {
+      height: 12px;
+      line-height: 12px;
+      font-size: 12px;
+      color: #7a8599;
+    }
+  }
+`;
+
+// 假设 employee 的数据结构如下
+interface Employee {
+  id: number;
+  name: string;
+  code: string;
+}
+
+// 定义组件的 props
+interface EmployeeListProps {
+  // 父组件通过 onSelect 接收选中的员工信息
+  onSelect?: (employee: Employee) => void;
+}
+
+const EmployeeList: FC<EmployeeListProps> = ({ onSelect }) => {
+  // 搜索框输入值
+  const [searchValue, setSearchValue] = useState('');
+
+  const [selectedId, setSelectedId] = useState<number | null>(null);
+  // 使用自定义 Hook 获取员工数据及操作方法
+  const { employees, loadMore, refresh, syncEmpData, hasMore, loading, syncing, error } =
+    useEmployeeList(searchValue, 50);
+
+  // 列表容器 ref,用于绑定滚动监听事件
+  const listContainerRef = useRef<HTMLDivElement>(null);
+
+  // 当员工数据更新时,如果没有选中项或选中项在新数据中不存在,则默认选中第一项
+  useEffect(() => {
+    if (employees.length > 0) {
+      const exists = employees.some((emp) => emp.id === selectedId);
+      if (!exists) {
+        setSelectedId(employees[0].id);
+        onSelect && onSelect(employees[0]);
+      }
+    }
+  }, [employees, selectedId, onSelect]);
+
+  useEffect(() => {
+    const container = listContainerRef.current;
+    if (!container) return;
+
+    let scrollTimeout: NodeJS.Timeout | null = null;
+
+    const handleScroll = () => {
+      // 清除之前的定时器
+      if (scrollTimeout) {
+        clearTimeout(scrollTimeout);
+      }
+      
+      // 设置防抖延迟
+      scrollTimeout = setTimeout(() => {
+        const { scrollTop, clientHeight, scrollHeight } = container;
+        const isNearBottom = scrollTop + clientHeight >= scrollHeight - 10;
+        
+        // 当滚动到底部时,加载更多
+        if (isNearBottom && hasMore && !loading) {
+          loadMore();
+        }
+      }, 100); // 100ms 防抖延迟
+    };
+
+    container.addEventListener('scroll', handleScroll);
+    return () => {
+      container.removeEventListener('scroll', handleScroll);
+      if (scrollTimeout) {
+        clearTimeout(scrollTimeout);
+      }
+    };
+  }, [loadMore, hasMore, loading]); // 添加 hasMore 和 loading 到依赖数组
+
+  return (
+    <Container>
+      {/* 搜索区域 */}
+      <SearchBar>
+        <Input
+          placeholder={'姓名/工号'}
+          allowClear
+          style={{ width: 155 }}
+          suffix={
+            <IconFont
+              type="iconsousuo"
+              style={{ color: '#99A6BF', cursor: 'pointer' }}
+              onClick={() => {}}
+            />
+          }
+          onChange={(e) => setSearchValue(e.target.value)}
+          onPressEnter={() => {}}
+        />
+        <FreshButton 
+          $syncing={syncing}
+          onClick={syncing ? undefined : syncEmpData}
+        >
+          <IconFont 
+            type="icon-shuaxin"
+            className="icon"
+            style={{ color: '#17181A' }} 
+          />
+        </FreshButton>
+      </SearchBar>
+
+      {/* 列表区域 */}
+      <ListContainer ref={listContainerRef}>
+        {employees.map((employee) => {
+          const { id, name, code } = employee;
+          const isActive = selectedId === id;
+          return (
+            <ListItem
+              key={id}
+              active={isActive}
+              onClick={() => {
+                setSelectedId(id);
+                onSelect && onSelect(employee);
+              }}
+            >
+              <img src={require('./images/emp_icon.png')} alt="" />
+              <div className="info">
+                <span className="name">{name}</span>
+                <span className="id">{code}</span>
+              </div>
+            </ListItem>
+          );
+        })}
+        
+        {/* 状态显示区域 */}
+        <div style={{ padding: '8px', fontSize: '12px', color: '#999', textAlign: 'center' }}>
+          {syncing && (
+            <div style={{ color: '#cfd7e6', marginBottom: 4 }}>
+              正在同步员工数据...
+            </div>
+          )}
+          {loading && (
+            <div style={{ color: '#cfd7e6' }}>
+              加载中...
+            </div>
+          )}
+          {error && (
+            <div style={{ color: 'red', marginTop: 4 }}>
+              {error}
+            </div>
+          )}
+          {!hasMore && !loading && employees.length > 0 && (
+            <div style={{ color: '#999', marginTop: 4 }}>
+              没有更多数据了 (共 {employees.length} 条)
+            </div>
+          )}
+          {employees.length === 0 && !loading && !syncing && (
+            <div style={{ color: '#999', marginTop: 4 }}>
+              暂无数据
+            </div>
+          )}
+        </div>
+      </ListContainer>
+    </Container>
+  );
+};
+
+export default EmployeeList;

binární
src/pages/baseInfoMana/empMana/components/EmployeeList/images/emp_icon.png


+ 133 - 0
src/pages/baseInfoMana/empMana/components/EmployeeList/useEmployeeList.ts

@@ -0,0 +1,133 @@
+/*
+ * @Author: code4eat awesomedema@gmail.com
+ * @Date: 2025-04-15 14:47:27
+ * @LastEditors: code4eat awesomedema@gmail.com
+ * @LastEditTime: 2025-05-27 15:04:38
+ * @FilePath: /pfmBackMana/src/pages/baseInfoMana/empMana/components/EmployeeList/useEmployeeList.ts
+ * @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
+ */
+// useEmployeeList.ts
+import { useState, useEffect, useCallback, useRef } from 'react';
+import { Employee, fetchEmployeeList, syncEmployeeList } from '../../service';
+import { message } from 'antd';
+
+export const useEmployeeList = (keywords: string, pageSize: number = 10) => {
+  const [employees, setEmployees] = useState<Employee[]>([]);
+  const [curPage, setCurPage] = useState<number>(1);
+  const [totalPage, setTotalPage] = useState<number>(1);
+  const [hasMore, setHasMore] = useState<boolean>(true);
+  const [loading, setLoading] = useState<boolean>(false);
+  const [error, setError] = useState<string | null>(null);
+  const [syncing, setSyncing] = useState<boolean>(false); // 添加同步状态
+  
+  // 添加请求状态引用
+  const isLoadingRef = useRef<boolean>(false);
+
+  /**
+   * 根据 page 及关键词获取数据
+   * @param page 要获取的页码
+   * @param isRefresh 是否重置数据(刷新或搜索时使用)
+   */
+  const fetchData = useCallback(
+    async (page: number, isRefresh: boolean = false) => {
+      // 防止重复请求 - 使用ref来检查状态
+      if (isLoadingRef.current && !isRefresh) {
+        return;
+      }
+      
+      isLoadingRef.current = true;
+      setLoading(true);
+      setError(null);
+      
+      try {
+        const res = await fetchEmployeeList({
+          current: page,
+          pageSize,
+          filter: keywords,
+        });
+
+        if (res) {
+          const {
+            currPage: returnedPage,
+            totalPage: returnedTotalPage,
+            list,
+          } = res;
+          
+          if (isRefresh) {
+            setEmployees(list);
+          } else {
+            setEmployees((prev) => [...prev, ...list]);
+          }
+          
+          setCurPage(returnedPage);
+          setTotalPage(returnedTotalPage);
+          const hasMoreData = returnedPage < returnedTotalPage;
+          setHasMore(hasMoreData);
+        } else {
+          setError('接口返回错误');
+        }
+      } catch (err: any) {
+        const errorMessage = err.message || '网络错误';
+        setError(errorMessage);
+      } finally {
+        isLoadingRef.current = false;
+        setLoading(false);
+      }
+    },
+    [keywords, pageSize],
+  );
+
+  // 当关键词发生变化时,重置页码并重新请求数据
+  useEffect(() => {
+    setCurPage(1);
+    setHasMore(true); // 重置hasMore状态
+    fetchData(1, true);
+  }, [keywords, fetchData]);
+
+  // 加载更多数据
+  const loadMore = useCallback(async () => {
+    if (!hasMore || isLoadingRef.current) {
+      return;
+    }
+    
+    await fetchData(curPage + 1);
+  }, [hasMore, curPage, fetchData]);
+
+  // 刷新数据
+  const refresh = async () => {
+    setCurPage(1);
+    setHasMore(true);
+    await fetchData(1, true);
+  };
+
+  // 同步员工数据
+  const syncEmpData = async () => {
+    try {
+      setSyncing(true); // 使用独立的同步状态
+      const response = await syncEmployeeList();
+      if (response) {
+        message.success('员工数据同步成功');
+        // 同步成功后刷新列表
+        await refresh();
+      }
+    } catch (error) {
+      console.error('同步员工数据失败', error);
+      message.error('员工数据同步失败');
+    } finally {
+      setSyncing(false); // 重置同步状态
+    }
+  };
+
+  return {
+    employees,
+    loadMore,
+    refresh,
+    syncEmpData,
+    hasMore,
+    loading,
+    syncing,
+    error,
+    curPage,
+    totalPage,
+  };
+};

+ 327 - 0
src/pages/baseInfoMana/empMana/components/tableSelector.tsx

@@ -0,0 +1,327 @@
+import React, { useEffect, useState } from 'react';
+import { Input, message } from 'antd';
+import { KCIMTable } from '@/components/KCIMTable';
+import { createFromIconfontCN } from '@ant-design/icons';
+
+import { ModalForm, ProColumns } from '@ant-design/pro-components';
+import { roleColumns, unitColumns, groupColumns } from '..';
+import {
+  getEmpRoleDict,
+  getEmpDeptDict,
+  getEmpGroupDict,
+  RoleDictItem,
+  DeptDictItem,
+  GroupDictItem,
+} from '../service';
+
+// import './style.less';
+
+interface TableSelecterProps {
+  record: any;
+}
+
+const IconFont = createFromIconfontCN({
+  scriptUrl: '',
+});
+
+// 字典数据的列定义
+const dictRoleColumns: ProColumns[] = [
+  {
+    title: '角色代码',
+    dataIndex: 'roleCode',
+  },
+  {
+    title: '角色名称',
+    dataIndex: 'roleName',
+  },
+];
+
+const dictUnitColumns: ProColumns[] = [
+  {
+    title: '单位名称',
+    dataIndex: 'deptName',
+    render: (_, record: any) => record.deptName || record.name,
+  },
+  {
+    title: '单位负责人',
+    dataIndex: 'manager_name',
+    render: (_, record: any) => record.manager_name || record.deptManagerName,
+  },
+];
+
+const dictGroupColumns: ProColumns[] = [
+  {
+    title: '查核组',
+    dataIndex: 'name',
+    render: (_, record: any) => record.name || record.checkGroupName,
+  },
+  {
+    title: '组长',
+    dataIndex: 'groupManagerName',
+    render: (_, record: any) => record.groupManagerName || record.manager_name || record.managerName,
+  },
+];
+
+const TableSelecter = ({
+  tableSelecterType,
+  record,
+  open,
+  title,
+  onVisibleChange,
+  rowKey = 'id',
+  onFinish,
+  selectedEmployee,
+}: {
+  record: any;
+  open: boolean;
+  title: string;
+  onVisibleChange: (bool: boolean) => void;
+  tableSelecterType: 'ROLE' | 'UNIT' | 'GROUP';
+  rowKey?: string;
+  onFinish?: (selectedKeys: React.Key[], selectedRows: any[]) => void;
+  selectedEmployee?: any;
+}) => {
+  console.log('rowKey', rowKey);
+  const Table = React.forwardRef(({}: TableSelecterProps, ref) => {
+    const [datasource, set_datasource] = useState<any[]>([]);
+    const [selectedKeys, setSelectedKeys] = useState<React.Key[]>([]);
+    const [selectedRows, set_selectedRows] = useState<any[]>([]);
+    const [showList, set_showList] = useState<any[]>([]);
+    const [searchKeyword, setSearchKeyword] = useState<string>('');
+
+    const onSelectChange = (
+      newSelectedRowKeys: React.Key[],
+      selectedRows: any,
+    ) => {
+      setSelectedKeys([...newSelectedRowKeys]);
+      set_selectedRows([...selectedRows]);
+    };
+
+    const saveHandle = async () => {
+      // 检查是否有必要的ID字段
+      const hasRequiredFields = selectedRows.every((row) => {
+        if (tableSelecterType === 'ROLE' && !row.roleId) {
+          message.error('选中的角色缺少roleId字段');
+          return false;
+        } else if (tableSelecterType === 'UNIT' && !row.id) {
+          message.error('选中的部门缺少id字段');
+          return false;
+        } else if (tableSelecterType === 'GROUP' && !row.id) {
+          message.error('选中的查核组缺少id字段');
+          return false;
+        }
+        return true;
+      });
+
+      if (!hasRequiredFields) return;
+
+      onFinish && onFinish(selectedKeys, selectedRows);
+      onVisibleChange(false);
+    };
+
+    // 获取字典数据
+    const getTableData = async () => {
+      try {
+        if (!selectedEmployee) {
+          message.error('请先选择员工');
+          return Promise.resolve([]);
+        }
+
+        const empId = selectedEmployee.id;
+        let response: any[] = [];
+
+        // 根据当前tab类型确定使用哪个接口
+        if (tableSelecterType === 'ROLE') {
+          // 获取角色字典
+          response = await getEmpRoleDict({ empId });
+        } else if (tableSelecterType === 'UNIT') {
+          // 获取部门字典
+          response = await getEmpDeptDict({ empId });
+        } else if (tableSelecterType === 'GROUP') {
+          // 获取查核组字典
+          response = await getEmpGroupDict({ empId });
+        }
+
+        // 处理响应数据
+        if (response && response.length > 0) {
+          // 确保所有数据都有id字段作为唯一标识
+          const processedData = response.map((item) => {
+            // 检查是否有必要的字段
+            if (tableSelecterType === 'ROLE') {
+              if (!item.id && item.roleId) {
+                return { ...item, id: item.roleId };
+              }
+            } else if (tableSelecterType === 'GROUP') {
+              // 查核组字典数据确保有正确的id字段
+              if (!item.id && item.checkGroupId) {
+                return { ...item, id: item.checkGroupId };
+              }
+            }
+            return item;
+          });
+
+          set_datasource(processedData);
+          set_showList(processedData);
+        } else {
+          set_datasource([]);
+          set_showList([]);
+        }
+
+        return Promise.resolve(response);
+      } catch (error) {
+        console.error('获取字典数据失败', error);
+        message.error('获取数据失败');
+        return Promise.resolve([]);
+      }
+    };
+
+    // 本地过滤数据
+    const filterDataByKeyword = () => {
+      if (!searchKeyword || searchKeyword.trim() === '') {
+        set_showList(datasource);
+        return;
+      }
+
+      const lowercaseKeyword = searchKeyword.toLowerCase();
+
+      let filtered = [];
+
+      if (tableSelecterType === 'ROLE') {
+        filtered = datasource.filter((item: any) => {
+          const matchRoleCode =
+            item.roleCode &&
+            item.roleCode.toLowerCase().includes(lowercaseKeyword);
+          const matchRoleName =
+            item.roleName &&
+            item.roleName.toLowerCase().includes(lowercaseKeyword);
+          return matchRoleCode || matchRoleName;
+        });
+      } else if (tableSelecterType === 'UNIT') {
+        filtered = datasource.filter((item: any) => {
+          // 尝试不同可能的字段名
+          const deptNameMatch =
+            (item.deptName &&
+              item.deptName.toLowerCase().includes(lowercaseKeyword)) ||
+            (item.name && item.name.toLowerCase().includes(lowercaseKeyword));
+
+          const managerNameMatch =
+            (item.manager_name &&
+              item.manager_name.toLowerCase().includes(lowercaseKeyword)) ||
+            (item.deptManagerName &&
+              item.deptManagerName.toLowerCase().includes(lowercaseKeyword));
+
+          return deptNameMatch || managerNameMatch;
+        });
+      } else if (tableSelecterType === 'GROUP') {
+        filtered = datasource.filter((item: any) => {
+          // 尝试不同可能的字段名
+          const groupNameMatch =
+            (item.name &&
+              item.name.toLowerCase().includes(lowercaseKeyword)) ||
+            (item.checkGroupName &&
+              item.checkGroupName.toLowerCase().includes(lowercaseKeyword));
+
+          const managerNameMatch =
+            (item.groupManagerName &&
+              item.groupManagerName.toLowerCase().includes(lowercaseKeyword)) ||
+            (item.manager_name &&
+              item.manager_name.toLowerCase().includes(lowercaseKeyword)) ||
+            (item.managerName &&
+              item.managerName.toLowerCase().includes(lowercaseKeyword));
+
+          return groupNameMatch || managerNameMatch;
+        });
+      }
+
+      set_showList(filtered);
+    };
+
+    useEffect(() => {
+      if (selectedEmployee) {
+        getTableData();
+      }
+    }, [tableSelecterType, selectedEmployee]);
+
+    return (
+      <div>
+        <Input
+          placeholder={'请输入搜索关键词'}
+          allowClear
+          value={searchKeyword}
+          suffix={
+            <IconFont
+              style={{ color: '#99A6BF' }}
+              type="iconsousuo"
+              onClick={filterDataByKeyword}
+            />
+          }
+          style={{ marginBottom: 8 }}
+          onChange={(e) => {
+            setSearchKeyword(e.target.value);
+            // 如果清空搜索框,立即显示所有数据
+            if (!e.target.value) {
+              set_showList(datasource);
+            }
+          }}
+          onPressEnter={() => filterDataByKeyword()}
+        />
+        <KCIMTable
+          columns={
+            tableSelecterType === 'ROLE'
+              ? dictRoleColumns
+              : tableSelecterType === 'UNIT'
+              ? dictUnitColumns
+              : dictGroupColumns
+          }
+          options={{
+            density: true,
+            setting: {
+              listsHeight: 100,
+            },
+          }}
+          rowKey={tableSelecterType === 'ROLE' ? 'roleId' : 'id'}
+          scroll={{ y: 400 }}
+          tableAlertRender={false}
+          rowSelection={{
+            selectedRowKeys: selectedKeys,
+            onChange: onSelectChange,
+          }}
+          pagination={false}
+          dataSource={showList}
+        />
+        <div className="footer">
+          <span className="cancel" onClick={() => close()}>
+            取消
+          </span>
+          <span
+            className="ok"
+            onClick={() => saveHandle()}
+          >{`确认(${selectedKeys.length})`}</span>
+        </div>
+      </div>
+    );
+  });
+
+  const close = () => {
+    onVisibleChange && onVisibleChange(false);
+  };
+
+  return (
+    <ModalForm
+      className="TableSelecter"
+      title={title}
+      width={560}
+      submitter={{
+        render: false,
+      }}
+      open={open}
+      modalProps={{
+        closable: false,
+      }}
+    >
+      <Table record={record}></Table>
+    </ModalForm>
+  );
+};
+
+export default TableSelecter;

+ 639 - 0
src/pages/baseInfoMana/empMana/index.tsx

@@ -0,0 +1,639 @@
+/*
+ * @Author: code4eat awesomedema@gmail.com
+ * @Date: 2025-04-14 10:28:42
+ * @LastEditors: code4eat awesomedema@gmail.com
+ * @LastEditTime: 2025-07-09 15:04:26
+ * @FilePath: /pfmBackMana/src/pages/baseInfoMana/empMana/index.tsx
+ * @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
+ */
+
+import KCIMPagecontainer from '@/components/KCIMPageContainer';
+import EmployeeList from './components/EmployeeList/EmployeeList';
+import './style.less';
+
+import { Tabs, Input, Popconfirm, message, Button } from 'antd';
+import { createFromIconfontCN } from '@ant-design/icons';
+import { KCIMTable } from '@/components/KCIMTable';
+import { useRef, useState } from 'react';
+import { ActionType, ProColumns } from '@ant-design/pro-components';
+import TableSelecter from './components/tableSelector';
+import {
+  fetchEmpDeptDict,
+  fetchEmpGroup,
+  fetchEmpRoleDict,
+  deleteEmpRole,
+  deleteEmpDept,
+  deleteEmpGroup,
+  addEmpRole,
+  addEmpDept,
+  addEmpGroup,
+} from './service';
+import { Employee } from './service';
+
+const items = [
+  {
+    label: `角色`,
+    key: 'ROLE',
+  },
+  {
+    label: `查核单位`,
+    key: 'UNIT',
+  },
+  {
+    label: `查核组`,
+    key: 'GROUP',
+  },
+];
+
+export const roleColumns: ProColumns[] = [
+  {
+    title: '角色代码',
+    dataIndex: 'roleCode',
+  },
+  {
+    title: '角色名称',
+    dataIndex: 'roleName',
+  },
+];
+
+export const unitColumns: ProColumns[] = [
+  {
+    title: '单位名称',
+    dataIndex: 'deptName',
+  },
+  {
+    title: '单位负责人',
+    dataIndex: 'empName',
+  },
+];
+
+export const groupColumns: ProColumns[] = [
+  {
+    title: '查核组',
+    dataIndex: 'checkGroupName',
+  },
+  {
+    title: '组长',
+    dataIndex: 'employeeName',
+  },
+];
+
+const IconFont = createFromIconfontCN({
+  scriptUrl: '',
+});
+
+function EmpMana() {
+  const tableRef = useRef<ActionType>();
+  const [selectedEmployee, setSelectedEmployee] = useState<Employee | null>(
+    null,
+  );
+  const [searchKeyword, setSearchKeyword] = useState<string>('');
+  const [searchPlaceholder, setSearchPlaceholder] =
+    useState<string>('单位名称、负责人');
+
+  // 保存原始数据,用于本地搜索
+  const [originalRoleData, setOriginalRoleData] = useState<any[]>([]);
+  const [originalUnitData, setOriginalUnitData] = useState<any[]>([]);
+  const [originalGroupData, setOriginalGroupData] = useState<any[]>([]);
+
+  // 当前过滤后的数据
+  const [filteredData, setFilteredData] = useState<any[]>([]);
+
+  // 批量选择相关状态
+  const [selectedRowKeys, setSelectedRowKeys] = useState<React.Key[]>([]);
+  const [selectedRows, setSelectedRows] = useState<any[]>([]);
+
+  // 将optionColumns改为函数
+  const getOptionColumns = (type: string): ProColumns[] => [
+    {
+      title: '操作',
+      key: 'option',
+      width: 80,
+      valueType: 'option',
+      render: (_: any, record: any) => {
+        return [
+          <Popconfirm
+            title={'是否确认删除?'}
+            onConfirm={() => {
+              // 这里直接用type参数,确保和tab一致
+              if (type === 'ROLE') {
+                deleteTableHandle({ ...record, _type: 'ROLE' });
+              } else if (type === 'UNIT') {
+                deleteTableHandle({ ...record, _type: 'UNIT' });
+              } else if (type === 'GROUP') {
+                deleteTableHandle({ ...record, _type: 'GROUP' });
+              }
+            }}
+            okText="确定"
+            cancelText="取消"
+          >
+            <a key="emp">删除</a>
+          </Popconfirm>,
+        ];
+      },
+    },
+  ];
+
+  // set_tableColumns时动态生成操作列
+  const [tableColumns, set_tableColumns] = useState<ProColumns[]>([
+    ...unitColumns,
+    ...getOptionColumns('UNIT'),
+  ]);
+  const [tableSelecterVisible, set_tableSelecterVisible] =
+    useState<boolean>(false);
+  const [tableSelecterType, set_tableSelecterType] = useState<any>('UNIT');
+  const [currentEdit, set_currentEdit] = useState<any>({});
+
+  const getEmpListData = async (params: any) => {};
+
+  const tableSelecterCommit = async (keys: React.Key[], rows: any[]) => {
+    if (!selectedEmployee) {
+      message.error('请先选择员工');
+      return;
+    }
+
+    const empId = selectedEmployee.id;
+
+    try {
+      let response;
+
+      // 根据当前tab类型确定要使用的接口
+      if (tableSelecterType === 'ROLE') {
+        // 添加角色关联
+        const mapIdList = rows.map((row) => row.roleId.toString());
+        response = await addEmpRole({
+          id: empId.toString(),
+          mapIdList,
+        });
+      } else if (tableSelecterType === 'UNIT') {
+        // 添加部门关联 - 修正入参格式
+        const mapObjList = rows.map((row) => ({
+          id: row.id.toString(),
+          name: row.deptName || row.name,
+          type: "0" // 默认类型为0
+        }));
+        response = await addEmpDept({
+          id: empId.toString(),
+          name: selectedEmployee.name,
+          mapObjList,
+        });
+      } else if (tableSelecterType === 'GROUP') {
+        // 添加查核组关联
+        const mapIdList = rows.map((row) => row.id.toString());
+        response = await addEmpGroup({
+          id: empId.toString(),
+          mapIdList,
+        });
+      }
+
+      if (response) {
+        message.success('添加成功');
+        // 刷新当前表格数据
+        tableRef.current?.reload();
+      }
+    } catch (error) {
+      console.error('添加关联失败', error);
+      message.error('添加失败,请稍后重试');
+    }
+
+    // 关闭选择器
+    set_tableSelecterVisible(false);
+  };
+
+  const getTableData = async (params: any) => {
+    if (!selectedEmployee) {
+      return {
+        data: [],
+        success: true,
+        total: 0,
+      };
+    }
+
+    const empId = selectedEmployee.id;
+
+    try {
+      // 根据当前选中的tab调用不同的接口
+      if (tableSelecterType === 'ROLE') {
+        const response = await fetchEmpRoleDict({
+          empId,
+          // 不传递filter参数,全部在前端过滤
+        });
+
+        // 保存原始数据用于本地搜索
+        setOriginalRoleData(response || []);
+
+        // 根据搜索关键词过滤数据
+        const filtered = filterDataByKeyword(response || [], searchKeyword);
+        setFilteredData(filtered);
+
+        // 返回过滤后的数据
+        return {
+          data: filtered,
+          success: true,
+        };
+      } else if (tableSelecterType === 'UNIT') {
+        const response = await fetchEmpDeptDict({
+          empId,
+          // 不传递filter参数,全部在前端过滤
+        });
+
+        // 保存原始数据用于本地搜索
+        setOriginalUnitData(response || []);
+
+        // 根据搜索关键词过滤数据
+        const filtered = filterDataByKeyword(response || [], searchKeyword);
+        setFilteredData(filtered);
+
+        // 返回过滤后的数据
+        return {
+          data: filtered,
+          success: true,
+        };
+      } else if (tableSelecterType === 'GROUP') {
+        const response = await fetchEmpGroup({
+          empId,
+          // 不传递filter参数,全部在前端过滤
+        });
+
+        // 保存原始数据用于本地搜索
+        setOriginalGroupData(response || []);
+
+        // 根据搜索关键词过滤数据
+        const filtered = filterDataByKeyword(response || [], searchKeyword);
+        setFilteredData(filtered);
+
+        // 返回过滤后的数据
+        return {
+          data: filtered,
+          success: true,
+        };
+      }
+
+      return { data: [], success: true };
+    } catch (error) {
+      console.error('获取数据失败', error);
+      message.error('获取数据失败');
+      return { data: [], success: false };
+    }
+  };
+
+  // 本地搜索函数,根据关键词过滤数据
+  const filterDataByKeyword = (data: any[], keyword: string) => {
+    if (!keyword || keyword.trim() === '') {
+      return data;
+    }
+
+    const lowercaseKeyword = keyword.toLowerCase();
+
+    return data.filter((item) => {
+      // 根据不同的tab类型,搜索不同的字段
+      if (tableSelecterType === 'ROLE') {
+        return (
+          (item.roleCode &&
+            item.roleCode.toLowerCase().includes(lowercaseKeyword)) ||
+          (item.roleName &&
+            item.roleName.toLowerCase().includes(lowercaseKeyword))
+        );
+      } else if (tableSelecterType === 'UNIT') {
+        return (
+          (item.deptName && item.deptName.toLowerCase().includes(lowercaseKeyword)) ||
+          (item.empName &&
+            item.empName.toLowerCase().includes(lowercaseKeyword))
+        );
+      } else if (tableSelecterType === 'GROUP') {
+        return (
+          (item.checkGroupName &&
+            item.checkGroupName.toLowerCase().includes(lowercaseKeyword)) ||
+          (item.employeeName &&
+            item.employeeName.toLowerCase().includes(lowercaseKeyword))
+        );
+      }
+      return false;
+    });
+  };
+
+  // 执行本地搜索的函数
+  const handleSearch = () => {
+    let dataToFilter: any[] = [];
+
+    // 根据当前tab选择相应的原始数据
+    if (tableSelecterType === 'ROLE') {
+      dataToFilter = originalRoleData;
+    } else if (tableSelecterType === 'UNIT') {
+      dataToFilter = originalUnitData;
+    } else if (tableSelecterType === 'GROUP') {
+      dataToFilter = originalGroupData;
+    }
+
+    // 过滤数据
+    const filtered = filterDataByKeyword(dataToFilter, searchKeyword);
+    setFilteredData(filtered);
+
+    // 刷新表格
+    tableRef.current?.reload();
+  };
+
+  // 批量删除处理函数
+  const handleBatchDelete = async () => {
+    if (selectedRowKeys.length === 0) {
+      message.error('请选择要删除的数据');
+      return;
+    }
+
+    try {
+      let response;
+      let idsToDelete: number[] = [];
+
+      // 根据当前tab类型确定要使用的ID字段和对应的删除接口
+      if (tableSelecterType === 'ROLE') {
+        idsToDelete = selectedRows.map((row) => row.id);
+        response = await deleteEmpRole(idsToDelete);
+      } else if (tableSelecterType === 'UNIT') {
+        const itemsToDelete = selectedRows.map((row) => ({ 
+          id: row.id.toString(), 
+          type: row.type || '0' 
+        }));
+        response = await deleteEmpDept(itemsToDelete);
+      } else if (tableSelecterType === 'GROUP') {
+        idsToDelete = selectedRows.map((row) => row.id);
+        response = await deleteEmpGroup(idsToDelete);
+      }
+
+      if (response) {
+        message.success(`批量删除成功,共删除 ${selectedRowKeys.length} 条数据`);
+
+        // 从本地缓存的数据中删除对应记录
+        if (tableSelecterType === 'ROLE') {
+          setOriginalRoleData(
+            originalRoleData.filter((item) => !idsToDelete.includes(item.id)),
+          );
+        } else if (tableSelecterType === 'UNIT') {
+          const deletedIds = selectedRows.map(row => row.id);
+          setOriginalUnitData(
+            originalUnitData.filter((item) => !deletedIds.includes(item.id)),
+          );
+        } else if (tableSelecterType === 'GROUP') {
+          setOriginalGroupData(
+            originalGroupData.filter((item) => !idsToDelete.includes(item.id)),
+          );
+        }
+
+        // 清空选中状态
+        setSelectedRowKeys([]);
+        setSelectedRows([]);
+
+        // 刷新当前搜索结果
+        handleSearch();
+      }
+    } catch (error) {
+      console.error('批量删除失败', error);
+      message.error('批量删除失败');
+    }
+  };
+
+  // 处理行选择变化
+  const handleRowSelectionChange = (newSelectedRowKeys: React.Key[], newSelectedRows: any[]) => {
+    setSelectedRowKeys(newSelectedRowKeys);
+    setSelectedRows(newSelectedRows);
+  };
+
+  // deleteTableHandle里优先用record._type(如果有)
+  const deleteTableHandle = async (record: any) => {
+    try {
+      let response;
+      let idToDelete: number;
+      // 优先用record._type,否则用tableSelecterType
+      const type = record._type || tableSelecterType;
+      if (type === 'ROLE') {
+        idToDelete = record.id;
+        response = await deleteEmpRole([idToDelete]);
+      } else if (type === 'UNIT') {
+        // 修正:使用record.id与批量删除保持一致
+        idToDelete = record.id;
+        response = await deleteEmpDept([{ id: idToDelete.toString(), type: record.type || '0' }]);
+      } else if (type === 'GROUP') {
+        idToDelete = record.id;
+        response = await deleteEmpGroup([idToDelete]);
+      }
+      if (response) {
+        message.success('删除成功');
+        if (type === 'ROLE') {
+          setOriginalRoleData(
+            originalRoleData.filter((item) => item.id !== idToDelete),
+          );
+        } else if (type === 'UNIT') {
+          // 修正:使用id过滤与批量删除保持一致
+          setOriginalUnitData(
+            originalUnitData.filter((item) => item.id !== idToDelete),
+          );
+        } else if (type === 'GROUP') {
+          setOriginalGroupData(
+            originalGroupData.filter((item) => item.id !== idToDelete),
+          );
+        }
+        handleSearch();
+      }
+      return true;
+    } catch (error) {
+      console.error('删除失败', error);
+      message.error('删除失败');
+      return false;
+    }
+  };
+
+  // onChange里动态生成操作列
+  const onChange = (key: string) => {
+    if (key == 'ROLE') {
+      set_tableSelecterType('ROLE');
+      set_tableColumns([...roleColumns, ...getOptionColumns('ROLE')]);
+      setSearchPlaceholder('角色代码、名称');
+    } else if (key == 'UNIT') {
+      set_tableSelecterType('UNIT');
+      set_tableColumns([...unitColumns, ...getOptionColumns('UNIT')]);
+      setSearchPlaceholder('单位名称、负责人');
+    } else if (key == 'GROUP') {
+      set_tableSelecterType('GROUP');
+      set_tableColumns([...groupColumns, ...getOptionColumns('GROUP')]);
+      setSearchPlaceholder('查核组、组长');
+    }
+    // 重置搜索关键词和选中状态
+    setSearchKeyword('');
+    setSelectedRowKeys([]);
+    setSelectedRows([]);
+    // 切换tab后,触发表格重新加载数据
+    tableRef.current?.reload();
+  };
+
+  const addhandle = () => {
+    if (!selectedEmployee) {
+      message.warning('请先选择一个员工');
+      return;
+    }
+    set_tableSelecterVisible(true);
+    set_currentEdit({});
+  };
+
+  return (
+    <KCIMPagecontainer className="empMana" title={false} style={{ position: 'relative' }}>
+      <TableSelecter
+        onVisibleChange={(bool) => set_tableSelecterVisible(bool)}
+        title={selectedEmployee ? `关联 - ${selectedEmployee.name}` : '关联'}
+        rowKey={tableSelecterType === 'ROLE' ? 'roleId' : 'id'}
+        tableSelecterType={tableSelecterType}
+        record={currentEdit}
+        open={tableSelecterVisible}
+        onFinish={(keys, rows) => tableSelecterCommit(keys, rows)}
+        selectedEmployee={selectedEmployee}
+      />
+      <EmployeeList
+        onSelect={(employee) => {
+          setSelectedEmployee(employee);
+          // 选择员工后,触发表格重新加载数据
+          tableRef.current?.reload();
+        }}
+      />
+      <div className="content">
+        <Tabs items={items} activeKey={tableSelecterType} onChange={onChange} />
+        <div className="toolBar">
+          <div className="filter">
+            <div className="filterItem" style={{ marginRight: 16, width: 220 }}>
+              <span className="label" style={{ whiteSpace: 'nowrap' }}>
+                {' '}
+                检索:
+              </span>
+              <Input
+                placeholder={searchPlaceholder}
+                allowClear
+                value={searchKeyword}
+                suffix={
+                  <IconFont
+                    type="iconsousuo"
+                    style={{ color: '#99A6BF' }}
+                    onClick={() => {
+                      handleSearch();
+                    }}
+                  />
+                }
+                onChange={(e) => {
+                  setSearchKeyword(e.target.value);
+                  // 如果清空了搜索框,立即恢复显示所有数据
+                  if (!e.target.value) {
+                    handleSearch();
+                  }
+                }}
+                onPressEnter={(e) => {
+                  // 搜索框按回车时执行本地搜索
+                  handleSearch();
+                }}
+              />
+            </div>
+          </div>
+          <div className="btnGroup">
+            <div className="add" onClick={() => addhandle()}>
+              添加
+            </div>
+          </div>
+        </div>
+
+        <div style={{ marginTop: 16, paddingBottom: selectedRowKeys.length > 0 ? 68 : 0 }}>
+          <KCIMTable
+            columns={tableColumns}
+            actionRef={tableRef}
+            rowKey="id"
+            request={(params) => getTableData(params)}
+            pagination={false}
+            rowSelection={{
+              selectedRowKeys,
+              onChange: handleRowSelectionChange,
+            }}
+            tableAlertRender={false}
+          />
+        </div>
+
+      </div>
+
+      {/* 批量操作栏 - 固定在右侧内容区域底部 */}
+      {selectedRowKeys.length > 0 && (
+        <div
+          style={{
+            position: 'absolute',
+            bottom:0, // empMana的padding
+            left:0, // 从人员列表右侧开始
+            right: 0, // empMana的padding
+            height: 48,
+            background: '#FFFFFF',
+            boxShadow: '0px -8px 16px 0px rgba(64,85,128,0.1)',
+            display: 'flex',
+            justifyContent: 'space-between',
+            alignItems: 'center',
+            padding: '0 16px',
+            zIndex: 1000,
+          }}
+        >
+          <span style={{ 
+            color: '#333333', 
+            fontSize: 14,
+            fontFamily: 'SourceHanSansCN-Normal, SourceHanSansCN',
+            fontWeight: 400,
+          }}>
+            已选择 {selectedRowKeys.length} 项,将进行批量删除
+          </span>
+          <div style={{ display: 'flex', gap: 12 }}>
+            <div
+              onClick={() => {
+                setSelectedRowKeys([]);
+                setSelectedRows([]);
+              }}
+              style={{
+                cursor: 'pointer',
+                display: 'inline-block',
+                fontSize: 14,
+                fontFamily: 'SourceHanSansCN-Normal, SourceHanSansCN',
+                fontWeight: 400,
+                color: '#666666',
+                lineHeight: '24px',
+                padding: '0 14px',
+                background: '#ffffff',
+                border: '1px solid #dae2f2',
+                borderRadius: 4,
+              }}
+            >
+              取消选择
+            </div>
+            <Popconfirm
+              title={`确认批量删除所选${
+                tableSelecterType === 'ROLE'
+                  ? '角色'
+                  : tableSelecterType === 'UNIT'
+                  ? '查核单位'
+                  : '查核组'
+              }?`}
+              okText="确定"
+              cancelText="取消"
+              onConfirm={handleBatchDelete}
+            >
+              <div
+                style={{
+                  cursor: 'pointer',
+                  display: 'inline-block',
+                  fontSize: 14,
+                  fontFamily: 'SourceHanSansCN-Normal, SourceHanSansCN',
+                  fontWeight: 400,
+                  color: '#ffffff',
+                  lineHeight: '24px',
+                  padding: '0 14px',
+                  background: '#3377ff',
+                  borderRadius: 4,
+                }}
+              >
+                批量删除
+              </div>
+            </Popconfirm>
+          </div>
+        </div>
+      )}
+    </KCIMPagecontainer>
+  );
+}
+
+export default EmpMana;

+ 199 - 0
src/pages/baseInfoMana/empMana/service.ts

@@ -0,0 +1,199 @@
+/*
+ * @Author: code4eat awesomedema@gmail.com
+ * @Date: 2025-04-15 14:49:07
+ * @LastEditors: code4eat awesomedema@gmail.com
+ * @LastEditTime: 2025-05-27 15:16:16
+ * @FilePath: /pfmBackMana/src/pages/baseInfoMana/empMana/service.ts
+ * @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
+ */
+import { request } from 'umi';
+
+export interface Employee {
+  id: number; // 接口返回的 id
+  code: string; // 工号
+  name: string; // 员工姓名
+  password?: string;
+  tsType?: number;
+  headUrl?: string;
+  permission?: string;
+  manageRole?: string;
+  lastLoginPermission?: string;
+}
+
+export interface EmployeeAPIResponse {
+  totalCount: number;
+  pageSize: number;
+  totalPage: number;
+  currPage: number;
+  list: Employee[];
+}
+
+// 角色接口数据结构(单个角色项)
+export interface RoleItem {
+  id: number; // 记录ID
+  empId: number; // 职工ID
+  roleId: number; // 角色ID
+  empCode: string; // 职工工号
+  empName: string; // 职工姓名
+  roleCode: string; // 角色代码
+  roleName: string; // 角色名称
+}
+
+// 查核单位接口数据结构(单个单位项)
+export interface DeptItem {
+  deptId: number; // 查核单位ID
+  deptName: string; // 查核单位名称
+  manager_id: string; // 单位负责人ID
+  manager_name: string; // 单位负责人姓名
+}
+
+// 角色字典项
+export interface RoleDictItem {
+  roleId: number; // 角色ID
+  roleCode: string; // 角色代码
+  roleName: string; // 角色名称
+}
+
+// 部门字典项
+export interface DeptDictItem {
+  deptId: number; // 部门ID
+  deptName: string; // 部门名称
+  manager_id: string; // 单位负责人ID
+  manager_name: string; // 单位负责人姓名
+}
+
+// 查核组接口数据结构(单个查核组项)
+export interface GroupItem {
+  id: number; // 记录ID
+  employeeId: number; // 职工ID
+  manager_id: number; // 查核组长ID
+  checkGroupId: number; // 查核组id
+  employeeName: string; // 职工姓名
+  manager_name: string; // 查核组长姓名
+  checkGroupName: string; // 查核组名称
+}
+
+// 查核组字典项
+export interface GroupDictItem {
+  manager_id: number; // 查核组长ID
+  checkGroupId: number; // 查核组ID
+  manager_name: string; // 查核组长姓名
+  checkGroupName: string; // 查核组名称
+}
+
+export const fetchEmployeeList = (params?: any) => {
+  return request<EmployeeAPIResponse>('/manager/Employee/getEmpList', {
+    method: 'GET',
+    params: { ...params },
+  });
+};
+
+// 获取员工部门字典 - 直接返回部门数组 (已关联的)
+export const fetchEmpDeptDict = (params?: { empId: number }) => {
+  return request<DeptItem[]>('/manager/Employee/getEmpDept', {
+    method: 'GET',
+    params: { ...params },
+  });
+};
+
+// 获取员工角色字典 - 直接返回角色数组 (已关联的)
+export const fetchEmpRoleDict = (params?: { empId: number }) => {
+  return request<RoleItem[]>('/manager/Employee/getEmpRole', {
+    method: 'GET',
+    params: { ...params },
+  });
+};
+
+// 获取员工查核组 - 直接返回查核组数组 (已关联的)
+export const fetchEmpGroup = (params?: { empId: number }) => {
+  return request<GroupItem[]>('/manager/Employee/getEmpGroup', {
+    method: 'GET',
+    params: { ...params },
+  });
+};
+
+// 获取可绑定的角色字典数据 - 用于TableSelecter
+export const getEmpRoleDict = (params?: { empId: number; filter?: string }) => {
+  return request<RoleDictItem[]>('/manager/Employee/getEmpRoleDict', {
+    method: 'GET',
+    params: { ...params },
+  });
+};
+
+// 获取可绑定的部门字典数据 - 用于TableSelecter
+export const getEmpDeptDict = (params?: { empId: number; filter?: string }) => {
+  return request<DeptDictItem[]>('/manager/Employee/getEmpDeptDict', {
+    method: 'GET',
+    params: { ...params },
+  });
+};
+
+// 获取可绑定的查核组字典数据 - 用于TableSelecter
+export const getEmpGroupDict = (params?: {
+  empId: number;
+  filter?: string;
+}) => {
+  return request<GroupDictItem[]>('/manager/Employee/getEmpGroupDict', {
+    method: 'GET',
+    params: { ...params },
+  });
+};
+
+// 删除员工角色
+export const deleteEmpRole = (ids: number[]) => {
+  return request('/manager/Employee/delEmpRole', {
+    method: 'POST',
+    data: ids,
+  });
+};
+
+// 删除员工部门
+export const deleteEmpDept = (items: { id: string; type: string }[]) => {
+  return request('/manager/Employee/delEmpDept', {
+    method: 'POST',
+    data: items,
+  });
+};
+
+// 删除员工查核组
+export const deleteEmpGroup = (ids: number[]) => {
+  return request('/manager/Employee/delEmpGroup', {
+    method: 'POST',
+    data: ids,
+  });
+};
+
+// 添加员工角色关联
+export const addEmpRole = (data: { id: string; mapIdList: string[] }) => {
+  return request('/manager/Employee/addEmpRole', {
+    method: 'POST',
+    data,
+  });
+};
+
+// 添加员工部门关联
+export const addEmpDept = (data: { 
+  id: string; 
+  name: string; 
+  mapObjList: { id: string; name: string; type: string; }[] 
+}) => {
+  return request('/manager/Employee/addEmpDept', {
+    method: 'POST',
+    data,
+  });
+};
+
+// 添加员工查核组关联
+export const addEmpGroup = (data: { id: string; mapIdList: string[] }) => {
+  return request('/manager/Employee/addEmpGroup', {
+    method: 'POST',
+    data,
+  });
+};
+
+// 同步/刷新员工列表
+export const syncEmployeeList = () => {
+  return request('/manager/Employee/synEmpList', {
+    method: 'POST',
+  });
+};

+ 84 - 0
src/pages/baseInfoMana/empMana/style.less

@@ -0,0 +1,84 @@
+.TableSelecter {
+  .pfm-ant-modal-footer {
+    display: none !important;
+  }
+
+  .footer {
+    display: flex;
+    flex-direction: row;
+    justify-content: flex-end;
+    margin-top: 15px;
+    span {
+      display: inline-block;
+      width: 56px;
+      height: 24px;
+      font-size: 14px;
+      line-height: 23px;
+      text-align: center;
+      border-radius: 4px;
+      cursor: pointer;
+
+      &.ok {
+        color: #ffffff;
+        background: #3377ff;
+        margin-left: 8px;
+      }
+      &.cancel {
+        border: 1px solid #dae2f2;
+      }
+    }
+  }
+}
+
+.empMana {
+  padding: 16px;
+  .pfm-ant-pro-page-container-children-content {
+    display: flex;
+    flex-direction: row;
+    justify-content: space-between;
+    align-items: flex-start;
+    .content {
+      flex: 1;
+      padding: 16px;
+      padding-top: 0;
+      margin-left: 16px;
+      background: #ffffff;
+      border-radius: 4px;
+      .toolBar {
+        display: flex;
+        flex-direction: row;
+        justify-content: space-between;
+        align-items: center;
+
+        .filter {
+          display: flex;
+          flex-direction: row;
+          justify-content: flex-start;
+          align-items: center;
+
+          .filterItem {
+            display: flex;
+            flex-direction: row;
+            justify-content: center;
+            align-items: center;
+          }
+        }
+
+        .btnGroup {
+          .add {
+            cursor: pointer;
+            display: inline-block;
+            font-size: 14px;
+            font-family: SourceHanSansCN-Normal, SourceHanSansCN;
+            font-weight: 400;
+            color: #ffffff;
+            line-height: 24px;
+            padding: 0 14px;
+            background: #3377ff;
+            border-radius: 4px;
+          }
+        }
+      }
+    }
+  }
+}

+ 382 - 0
src/pages/baseInfoMana/empMana_bak/index.tsx

@@ -0,0 +1,382 @@
+/*
+ * @Author: code4eat awesomedema@gmail.com
+ * @Date: 2023-03-03 11:30:33
+ * @LastEditors: code4eat awesomedema@gmail.com
+ * @LastEditTime: 2023-10-13 16:40:53
+ * @FilePath: /KC-MiddlePlatform/src/pages/platform/setting/pubDicTypeMana/index.tsx
+ * @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
+ */
+
+import { createFromIconfontCN } from '@ant-design/icons';
+
+import {
+  ActionType,
+  DrawerForm,
+  EditableFormInstance,
+  EditableProTable,
+  ProFormCheckbox,
+  ProFormInstance,
+  ProFormSelect,
+} from '@ant-design/pro-components';
+import {
+  ModalForm,
+  ProFormDigit,
+  ProFormText,
+  ProFormTextArea,
+} from '@ant-design/pro-form';
+import { ProColumns } from '@ant-design/pro-table';
+import { Input, message, Switch } from 'antd';
+import { createRef, useEffect, useRef, useState } from 'react';
+
+import {
+  addData,
+  bindCheckGroupReq,
+  bindUnitReq,
+  delData,
+  editData,
+  getData,
+} from './service';
+
+import './style.less';
+
+import KCIMPagecontainer from '@/components/KCIMPageContainer';
+import { KCIMTable } from '@/components/KCIMTable';
+import KCIMDrawerForm from '@/components/KCIMDrawerForm';
+import TableTransfer from './transform';
+import TableSelecter from './tableSelector';
+import { getCheckData } from '../checkGroupMana/service';
+import { debounce } from 'lodash';
+
+const IconFont = createFromIconfontCN({
+  scriptUrl: '',
+});
+
+type TableDataResponse = {
+  data: any[];
+  success: boolean;
+  total: number;
+  pageSize: number;
+  totalPage: number;
+};
+
+export default function FenyeTemplate() {
+  const [tableDataFilterParams, set_tableDataFilterParams] = useState<
+    any | undefined
+  >();
+  const [tableDataSearchKeywords, set_tableDataSearchKeywords] =
+    useState<string>('');
+  const tableRef = useRef<ActionType>();
+  const [tableSelecterVisible, set_tableSelecterVisible] = useState(false);
+  const [tableSelecterType, set_tableSelecterType] = useState<
+    'UNIT' | 'CHECKGROUP'
+  >('UNIT'); //穿梭框类型
+
+  const [currentEdit, set_currentEdit] = useState<any | undefined>(undefined);
+
+  const columns: ProColumns[] = [
+    {
+      title: 'ID',
+      dataIndex: 'id',
+    },
+    // {
+    //     title: '工号',
+    //     dataIndex: 'employeeId',
+    // },
+    {
+      title: '用户名',
+      dataIndex: 'account',
+    },
+    {
+      title: '姓名',
+      dataIndex: 'name',
+    },
+    {
+      title: '查核组',
+      dataIndex: 'groupList',
+      ellipsis: true,
+      renderText(groupList, record, index, action) {
+        if (groupList) {
+          return groupList.reduce(
+            (prev: string, next: any) =>
+              `${prev.length > 0 ? prev + ',' : prev}${next.name}`,
+            '',
+          );
+        }
+      },
+    },
+    {
+      title: '单位名',
+      dataIndex: 'departList',
+      ellipsis: true,
+      renderText(departList, record, index, action) {
+        if (departList) {
+          return departList.reduce(
+            (prev: string, next: any) =>
+              `${prev.length > 0 ? prev + ',' : prev}${next.name}`,
+            '',
+          );
+        }
+      },
+    },
+    {
+      title: '操作',
+      key: 'option',
+      width: 160,
+      valueType: 'option',
+      render: (_: any, record: any) => {
+        return [
+          <UpDataActBtn key={'act'} record={record} type="EDIT" />,
+          <a
+            key="unit"
+            onClick={() => {
+              openTransformModal(record);
+              set_tableSelecterType('UNIT');
+            }}
+          >
+            单位
+          </a>,
+          <a
+            key="checkGroup"
+            onClick={() => {
+              openTransformModal(record);
+              set_tableSelecterType('CHECKGROUP');
+            }}
+          >
+            查核组
+          </a>,
+        ];
+      },
+    },
+  ];
+
+  const getTableData = async (params: any) => {
+    const resp = await getData(params);
+    if (resp) {
+      return {
+        data: resp.list,
+        success: true,
+        total: resp.totalCount,
+      };
+    }
+    return [];
+  };
+
+  const updateTable = async (formVal: any, type: 'EDIT' | 'ADD') => {
+    if (type == 'ADD') {
+      const result = {
+        name: formVal.name,
+        content: JSON.stringify(formVal.table),
+        remark: formVal.remark,
+      };
+      const resp = await addData({ ...result });
+      if (resp) {
+        tableRef.current?.reload();
+        message.success('操作成功!');
+      }
+    }
+    if (type == 'EDIT') {
+      const result = {
+        id: formVal.id,
+        name: formVal.name,
+        employeeId: formVal.employeeId,
+        account: formVal.account,
+        permission: formVal.permission.join(','),
+      };
+      const resp = await editData({ ...result });
+      if (resp) {
+        tableRef.current?.reload();
+        message.success('操作成功!');
+      }
+    }
+
+    return true;
+  };
+
+  const UpDataActBtn = ({
+    record,
+    type,
+  }: {
+    record: any;
+    type: 'EDIT' | 'ADD';
+  }) => {
+    const formRef = useRef<ProFormInstance<any>>();
+
+    return (
+      <ModalForm
+        title={`${type == 'EDIT' ? '编辑' : '新增'}权限`}
+        width={600}
+        initialValues={
+          type == 'EDIT'
+            ? {
+                ...record,
+                permission: record.roleList ? record.roleList.split(',') : [],
+              }
+            : {}
+        }
+        trigger={
+          type == 'EDIT' ? (
+            <a key="edit">编辑权限</a>
+          ) : (
+            <span className="add">新增</span>
+          )
+        }
+        onFinish={(val: any) => {
+          return updateTable(
+            type == 'EDIT' ? { ...record, ...val } : { ...val },
+            type,
+          );
+        }}
+        formRef={formRef}
+        modalProps={{ destroyOnClose: true }}
+        colProps={{ span: 24 }}
+        grid
+      >
+        <ProFormCheckbox.Group
+          name="permission"
+          layout="horizontal"
+          label="行业分布"
+          options={[
+            { label: '管理员', value: '1' },
+            { label: '查核组长', value: '2' },
+            { label: '查核组员', value: '3' },
+            { label: '单位负责人', value: '4' },
+            { label: '改善人', value: '5' },
+            { label: '职能科室负责人', value: '6' },
+            { label: '职能科室普通人员', value: '7' },
+            { label: '自查人', value: '8' },
+          ]}
+        />
+      </ModalForm>
+    );
+  };
+
+  const tableDataSearchHandle = (paramName: string) => {
+    set_tableDataFilterParams({
+      ...tableDataFilterParams,
+      [`${paramName}`]: tableDataSearchKeywords,
+    });
+  };
+
+  const openTransformModal = (record: any) => {
+    set_currentEdit(record);
+    set_tableSelecterVisible(true);
+  };
+
+  const tableSelecterCommit = async (keys: React.Key[], rows: any[]) => {
+    if (tableSelecterType == 'CHECKGROUP') {
+      const result = {
+        employeeId: currentEdit.employeeId,
+        employeeName: currentEdit.name,
+        groupList: rows.map((a: any) => ({
+          code: a.code,
+          name: a.name,
+          isManager: a.flag ? 1 : 0,
+        })),
+      };
+      const resp = await bindCheckGroupReq(result);
+      if (resp) {
+        set_tableSelecterVisible(false);
+        tableRef.current?.reload();
+      }
+    }
+    if (tableSelecterType == 'UNIT') {
+      const result = {
+        employeeId: currentEdit.employeeId,
+        employeeName: currentEdit.name,
+        departmentList: rows.map((a: any) => ({
+          code: a.code,
+          name: a.name,
+          isManager: a.flag ? 1 : 0,
+          isFunction: 0,
+        })),
+      };
+      const resp = await bindUnitReq(result);
+      if (resp) {
+        set_tableSelecterVisible(false);
+        tableRef.current?.reload();
+      }
+    }
+  };
+
+  const debouncedFetch = debounce(async (keyWords, callback) => {
+    const resp = await getCheckData({
+      pageSize: 20,
+      current: 1,
+      filter: keyWords,
+    });
+    if (resp) {
+      callback(
+        resp.list.map((a: any) => ({
+          label: a.name,
+          value: a.code,
+        })),
+      );
+    } else {
+      callback([]);
+    }
+  }, 300);
+
+  useEffect(() => {}, []);
+
+  return (
+    <KCIMPagecontainer className="empMana" title={false}>
+      <TableSelecter
+        onVisibleChange={(bool) => set_tableSelecterVisible(bool)}
+        title="关联"
+        rowKey={tableSelecterType == 'UNIT' ? 'code' : 'id'}
+        tableSelecterType={tableSelecterType}
+        record={currentEdit}
+        open={tableSelecterVisible}
+        onFinish={(keys, rows) => tableSelecterCommit(keys, rows)}
+      />
+      <div className="toolBar">
+        <div className="filter">
+          <div className="filterItem" style={{ marginRight: 16, width: 220 }}>
+            <span className="label" style={{ whiteSpace: 'nowrap' }}>
+              {' '}
+              检索:
+            </span>
+            <Input
+              placeholder={'请输入姓名/工号'}
+              allowClear
+              suffix={
+                <IconFont
+                  type="iconsousuo"
+                  style={{ color: '#99A6BF' }}
+                  onClick={() => tableDataSearchHandle('filter')}
+                />
+              }
+              onChange={(e) => {
+                set_tableDataSearchKeywords(e.target.value);
+                if (e.target.value.length == 0) {
+                  set_tableDataFilterParams({
+                    ...tableDataFilterParams,
+                    filter: '',
+                  });
+                }
+              }}
+              onPressEnter={(e) => {
+                set_tableDataFilterParams({
+                  ...tableDataFilterParams,
+                  filter: (e.target as HTMLInputElement).value,
+                });
+              }}
+            />
+          </div>
+        </div>
+        {/* <div className='btnGroup'>
+                    <UpDataActBtn record type='ADD' />
+                </div> */}
+      </div>
+      <div style={{ marginTop: 16 }}>
+        <KCIMTable
+          columns={columns as ProColumns[]}
+          actionRef={tableRef}
+          rowKey="id"
+          params={tableDataFilterParams}
+          request={(params) => getTableData(params)}
+        />
+      </div>
+    </KCIMPagecontainer>
+  );
+}

+ 99 - 0
src/pages/baseInfoMana/empMana_bak/service.ts

@@ -0,0 +1,99 @@
+/*
+ * @Author: code4eat awesomedema@gmail.com
+ * @Date: 2023-03-03 16:31:27
+ * @LastEditors: code4eat awesomedema@gmail.com
+ * @LastEditTime: 2023-10-12 15:56:04
+ * @FilePath: /KC-MiddlePlatform/src/pages/platform/setting/pubDicTypeMana/service.ts
+ * @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
+ */
+
+import { request } from 'umi';
+
+//获取table列表数据
+
+export type FenyeTemplateItemType = {
+  id: number;
+  hiId: number;
+  name: string;
+  content: string;
+  remark: string;
+  createUserId: number;
+  createUserName: string;
+  date: string;
+};
+
+export const getData = (params?: any) => {
+  return request('/manager/Employee/getEmployee', {
+    method: 'GET',
+    params: { ...params },
+  });
+};
+
+//新增表格数据
+export type AddTableDataType = {
+  name: string;
+  content: string;
+  remark: string;
+};
+export const addData = (data: AddTableDataType) => {
+  return request('/manager/setting/addPageTemplate', {
+    method: 'POST',
+    data,
+  });
+};
+
+//编辑权限
+export const editData = (data: {
+  id: number;
+  account: string;
+  name: string;
+  employeeId: number;
+  permission: string;
+}) => {
+  return request('/manager/Employee/editEmployeePermission', {
+    method: 'POST',
+    data,
+  });
+};
+
+//关联单位
+export const bindUnitReq = (data: {
+  employeeId: string;
+  employeeName: string;
+  departmentList: {
+    code: string;
+    name: string;
+    isManager: number;
+    isFunction: number;
+  }[];
+}) => {
+  return request('/manager/Employee/editDepartment', {
+    method: 'POST',
+    data,
+  });
+};
+
+//关联查核组
+export const bindCheckGroupReq = (data: {
+  employeeId: string;
+  employeeName: string;
+  groupList: {
+    code: string;
+    name: string;
+    isManager: number;
+  }[];
+}) => {
+  return request('/manager/Employee/editCheckGroup', {
+    method: 'POST',
+    data,
+  });
+};
+
+//删除表格操作
+
+export const delData = (id: string) => {
+  return request('/manager/setting/deletePageTemplate', {
+    method: 'POST',
+    params: { id },
+  });
+};

+ 73 - 0
src/pages/baseInfoMana/empMana_bak/style.less

@@ -0,0 +1,73 @@
+.TableSelecter {
+  .pfm-ant-modal-footer {
+    display: none !important;
+  }
+
+  .footer {
+    display: flex;
+    flex-direction: row;
+    justify-content: flex-end;
+    margin-top: 15px;
+    span {
+      display: inline-block;
+      width: 56px;
+      height: 24px;
+      font-size: 14px;
+      line-height: 23px;
+      text-align: center;
+      border-radius: 4px;
+      cursor: pointer;
+
+      &.ok {
+        color: #ffffff;
+        background: #3377ff;
+        margin-left: 8px;
+      }
+      &.cancel {
+        border: 1px solid #dae2f2;
+      }
+    }
+  }
+}
+
+.empMana {
+  padding: 16px;
+  background: #ffffff;
+  border-radius: 4px;
+
+  .toolBar {
+    display: flex;
+    flex-direction: row;
+    justify-content: space-between;
+    align-items: center;
+
+    .filter {
+      display: flex;
+      flex-direction: row;
+      justify-content: flex-start;
+      align-items: center;
+
+      .filterItem {
+        display: flex;
+        flex-direction: row;
+        justify-content: center;
+        align-items: center;
+      }
+    }
+
+    .btnGroup {
+      .add {
+        cursor: pointer;
+        display: inline-block;
+        font-size: 14px;
+        font-family: SourceHanSansCN-Normal, SourceHanSansCN;
+        font-weight: 400;
+        color: #ffffff;
+        line-height: 24px;
+        padding: 0 14px;
+        background: #3377ff;
+        border-radius: 4px;
+      }
+    }
+  }
+}

+ 282 - 0
src/pages/baseInfoMana/empMana_bak/tableSelector.tsx

@@ -0,0 +1,282 @@
+import React, { useEffect, useState } from 'react';
+import { Input, Switch } from 'antd';
+import { KCIMTable } from '@/components/KCIMTable';
+import { createFromIconfontCN } from '@ant-design/icons';
+import { getChackGroupData, getCheckData } from '../checkGroupMana/service';
+
+import { ModalForm, ProColumns } from '@ant-design/pro-components';
+import { getData } from '../unitMana/service';
+import { set } from 'lodash';
+
+// import './style.less';
+
+interface TableSelecterProps {
+  record: any;
+}
+
+const IconFont = createFromIconfontCN({
+  scriptUrl: '',
+});
+
+const TableSelecter = ({
+  tableSelecterType,
+  record,
+  open,
+  title,
+  onVisibleChange,
+  rowKey = 'id',
+  request,
+  onFinish,
+}: {
+  record: any;
+  open: boolean;
+  title: string;
+  onVisibleChange: (bool: boolean) => void;
+  tableSelecterType: 'UNIT' | 'CHECKGROUP';
+  rowKey?: string;
+  request?: (params: any) => Promise<any>;
+  onFinish?: (selectedKeys: React.Key[], selectedRows: any[]) => void;
+}) => {
+  const Table = React.forwardRef(({}: TableSelecterProps, ref) => {
+    const [datasource, set_datasource] = useState<any[]>([]);
+    const [selectedKeys, setSelectedKeys] = useState<React.Key[]>([]);
+    const [selectedRows, set_selectedRows] = useState<any[]>([]);
+    const [showList, set_showList] = useState<any[]>([]);
+
+    const unitColumns: ProColumns[] = [
+      {
+        title: 'Code',
+        dataIndex: 'code',
+      },
+      {
+        title: '单位名',
+        dataIndex: 'name',
+      },
+      {
+        title: '是否单位管理人',
+        dataIndex: 'flag',
+        renderText(flag, record, index, action) {
+          return (
+            <Switch
+              size="small"
+              checked={flag}
+              onChange={(bool) => onSwitchChange(bool, record)}
+            />
+          );
+        },
+      },
+    ];
+    const checkGroupColumns: ProColumns[] = [
+      {
+        title: 'ID',
+        dataIndex: 'id',
+      },
+      {
+        title: '查核组名',
+        dataIndex: 'name',
+      },
+      {
+        title: '是否查核组长',
+        dataIndex: 'flag',
+        renderText(flag, record, index, action) {
+          return (
+            <Switch
+              size="small"
+              checked={flag}
+              onChange={(bool) => onSwitchChange(bool, record)}
+            />
+          );
+        },
+      },
+    ];
+
+    const onSwitchChange = (bool: boolean, record: any) => {
+      const newData = datasource.map((item) => {
+        if (item.code == record.code) {
+          return {
+            ...item,
+            flag: bool,
+          };
+        } else {
+          return item;
+        }
+      });
+      const newShowList = showList.map((item) => {
+        if (item.code == record.code) {
+          return {
+            ...item,
+            flag: bool,
+          };
+        } else {
+          return item;
+        }
+      });
+
+      set_datasource([...newData]);
+      set_showList([...newShowList]);
+    };
+
+    const onSelectChange = (
+      newSelectedRowKeys: React.Key[],
+      selectedRows: any,
+    ) => {
+      setSelectedKeys([...newSelectedRowKeys]);
+      set_selectedRows([...selectedRows]);
+    };
+
+    const saveHandle = async () => {
+      const selectedRowCodes = selectedRows.map((a) => a.code);
+      const rows = datasource.filter((a) => selectedRowCodes.includes(a.code));
+      onFinish && onFinish(selectedKeys, rows);
+    };
+
+    const getTableData = async () => {
+      if (tableSelecterType == 'UNIT') {
+        const resp = await getData();
+        const checkedUnitCodes = record.departList
+          ? record.departList.map((a: any) => a.code)
+          : [];
+        let selectedCodes: string[] = [];
+        let selectedRows: any[] = [];
+        if (resp) {
+          const data = resp.map((a: any) => {
+            if (checkedUnitCodes.includes(a.code)) {
+              selectedCodes.push(a.code);
+              selectedRows.push(a);
+              const needItem = record.departList.filter(
+                (b: any) => a.code == b.code,
+              );
+
+              return {
+                ...a,
+                flag: needItem[0].isManager ? true : false,
+              };
+            } else {
+              return a;
+            }
+          });
+
+          set_showList(data);
+          set_selectedRows([...selectedRows]);
+          setSelectedKeys([...selectedCodes]);
+          set_datasource([...datasource, ...data]);
+        }
+      }
+      if (tableSelecterType == 'CHECKGROUP') {
+        const resp = await getCheckData({ pageSize: 500, current: 1 });
+        const checkedUnitCodes = record.groupList
+          ? record.groupList.map((a: any) => a.code)
+          : [];
+        let selectedCodes: string[] = [];
+        let selectedRows: any[] = [];
+        if (resp) {
+          const data = resp.list.map((a: any) => {
+            if (checkedUnitCodes.includes(a.code)) {
+              selectedCodes.push(a.code);
+              selectedRows.push(a);
+              const needItem = record.groupList.filter(
+                (b: any) => a.code == b.code,
+              );
+              return {
+                ...a,
+                flag: needItem[0].isManager ? true : false,
+              };
+            } else {
+              return a;
+            }
+          });
+          set_showList(data);
+          set_selectedRows([...selectedRows]);
+          setSelectedKeys([...selectedCodes]);
+          set_datasource([...datasource, ...data]);
+        }
+      }
+
+      return Promise.resolve([]);
+    };
+
+    useEffect(() => {
+      getTableData();
+    }, []);
+
+    return (
+      <div>
+        <Input
+          placeholder={
+            tableSelecterType == 'UNIT' ? '请输入单位名称' : '请输入查核组名称'
+          }
+          allowClear
+          suffix={<IconFont style={{ color: '#99A6BF' }} type="iconsousuo" />}
+          style={{ marginBottom: 8 }}
+          onChange={(e) => {
+            if (e.target.value.length != 0) {
+              const result = datasource.filter(
+                (item) => item.name.indexOf(e.target.value) != -1,
+              );
+              set_showList(result);
+            } else {
+              set_showList(datasource);
+            }
+          }}
+        />
+        <KCIMTable
+          columns={
+            tableSelecterType == 'UNIT' ? unitColumns : checkGroupColumns
+          }
+          options={{
+            density: true,
+            setting: {
+              listsHeight: 100,
+            },
+          }}
+          rowKey={rowKey}
+          scroll={{ y: 400 }}
+          tableAlertRender={false}
+          rowSelection={{
+            // 自定义选择项参考: https://ant.design/components/table-cn/#components-table-demo-row-selection-custom
+            // 注释该行则默认不显示下拉选项
+            // selections: [Table.SELECTION_ALL, Table.SELECTION_INVERT],
+            selectedRowKeys: selectedKeys,
+            onChange: onSelectChange,
+          }}
+          pagination={{ showTitle: false, showSizeChanger: true }}
+          dataSource={showList}
+        />
+        <div className="footer">
+          <span className="cancel" onClick={() => close()}>
+            取消
+          </span>
+          <span className="ok" onClick={() => saveHandle()}>{`确认(${
+            selectedKeys.length > 0 && selectedKeys.length
+          })`}</span>
+        </div>
+      </div>
+    );
+  });
+
+  const close = () => {
+    onVisibleChange && onVisibleChange(false);
+  };
+
+  return (
+    <ModalForm
+      className="TableSelecter"
+      title={title}
+      width={500}
+      submitter={{
+        render: false,
+      }}
+      open={open}
+      modalProps={{
+        closable: false,
+      }}
+    >
+      <Table
+        // ref={tableSelecterRef}
+        record={undefined}
+      ></Table>
+    </ModalForm>
+  );
+};
+
+export default TableSelecter;

+ 145 - 0
src/pages/baseInfoMana/empMana_bak/transform.tsx

@@ -0,0 +1,145 @@
+import React, { useEffect, useImperativeHandle, useState } from 'react';
+import { Transfer } from 'antd';
+import { TransferItem, TransferProps } from 'antd/es/transfer';
+import { ColumnsType } from 'antd/es/table';
+import { difference } from 'lodash';
+import { TableRowSelection } from 'antd/es/table/interface';
+import { KCIMTable } from '@/components/KCIMTable';
+
+interface TableTransferProps extends TransferProps<TransferItem> {
+  leftColumns: ColumnsType<any>;
+  rightColumns: ColumnsType<any>;
+}
+
+const TableTransfer = React.forwardRef(
+  ({ leftColumns, rightColumns, ...restProps }: TableTransferProps, ref) => {
+    const [_data, _set_data] = useState<any>();
+    const [targetKeys, setTargetKeys] = useState<string[]>([]);
+    const [datasource, set_datasource] = useState<any[]>([]);
+    const [selectedKeys, setSelectedKeys] = useState<string[]>([]);
+
+    //获取单元
+    const getFuncList = async () => {
+      // const resp = await getTotalEmps({
+      //     computeDate: currentComputeDate as string,
+      //     unitCode: currentSelectedTreeNode.code
+      // });
+      // if (resp) {
+      //     //_set_data(resp);
+      //     const allData = resp.allEmployees.concat(resp.checkEmployees);
+      //     set_datasource(allData);
+      //     const defaultSelctedkeys = resp.checkEmployees.map((item: any) => item.empNo);
+      //     setTargetKeys(defaultSelctedkeys);
+      // }
+    };
+
+    const onChange = (nextTargetKeys: string[]) => {
+      setTargetKeys(nextTargetKeys);
+    };
+
+    const onSelectChange = (
+      sourceSelectedKeys: string[],
+      targetSelectedKeys: string[],
+    ) => {
+      //console.log('sourceSelectedKeys:', sourceSelectedKeys,'targetSelectedKeys:',targetSelectedKeys);
+      setSelectedKeys([...sourceSelectedKeys, ...targetSelectedKeys]);
+    };
+
+    useImperativeHandle(ref, () => ({
+      save: async () => {
+        // const items = datasource.filter(a => targetKeys.includes(a.empNo));
+        // const resp = await saveEmpsRequest(items);
+        // if (resp) {
+        //     tableRef.current?.reload();
+        // }
+      },
+    }));
+
+    useEffect(() => {
+      getFuncList();
+    }, []);
+
+    return (
+      <Transfer
+        className="TableTransfer"
+        showSearch
+        titles={['待选项', '已选项']}
+        locale={{
+          itemUnit: '项',
+          itemsUnit: '项',
+          searchPlaceholder: '请输入',
+        }}
+        oneWay={true}
+        onChange={onChange}
+        onSelectChange={onSelectChange}
+        dataSource={datasource}
+        rowKey={(record) => record.empNo}
+        targetKeys={targetKeys}
+        selectedKeys={selectedKeys}
+        filterOption={(inputValue, item) => {
+          return item.name!.indexOf(inputValue) !== -1;
+        }}
+      >
+        {({
+          direction,
+          filteredItems,
+          onItemSelectAll,
+          onItemSelect,
+          selectedKeys: listSelectedKeys,
+          disabled: listDisabled,
+        }) => {
+          // console.log({ filteredItems, listSelectedKeys,direction });
+          const columns = direction === 'left' ? leftColumns : rightColumns;
+
+          const rowSelection: TableRowSelection<TransferItem> = {
+            getCheckboxProps: (item) => ({
+              disabled: listDisabled || item.disabled,
+            }),
+            onSelectAll(selected, selectedRows) {
+              const treeSelectedKeys = selectedRows.map(({ empNo }) => empNo);
+              const diffKeys = selected
+                ? difference(treeSelectedKeys, listSelectedKeys)
+                : difference(listSelectedKeys, treeSelectedKeys);
+              onItemSelectAll(diffKeys as string[], selected);
+            },
+            onSelect({ empNo }, selected) {
+              onItemSelect(empNo as string, selected);
+            },
+            selectedRowKeys: listSelectedKeys,
+          };
+
+          return (
+            <KCIMTable
+              rowSelection={rowSelection}
+              columns={columns as TransferItem[]}
+              dataSource={filteredItems}
+              size="small"
+              bordered={false}
+              rowKey={'empNo'}
+              pagination={{
+                showTitle: false,
+                pageSize: 9,
+                showLessItems: false,
+                simple: true,
+                showTotal: () => false,
+              }}
+              tableAlertRender={false}
+              style={{ pointerEvents: listDisabled ? 'none' : undefined }}
+              onRow={({ empNo, disabled: itemDisabled }) => ({
+                onClick: () => {
+                  if (itemDisabled || listDisabled) return;
+                  onItemSelect(
+                    empNo as string,
+                    !listSelectedKeys.includes(empNo as string),
+                  );
+                },
+              })}
+            />
+          );
+        }}
+      </Transfer>
+    );
+  },
+);
+
+export default TableTransfer;

+ 297 - 0
src/pages/baseInfoMana/unitMana/index.tsx

@@ -0,0 +1,297 @@
+/*
+ * @Author: code4eat awesomedema@gmail.com
+ * @Date: 2023-03-03 11:30:33
+ * @LastEditors: code4eat awesomedema@gmail.com
+ * @LastEditTime: 2023-10-13 11:20:45
+ * @FilePath: /KC-MiddlePlatform/src/pages/platform/setting/pubDicTypeMana/index.tsx
+ * @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
+ */
+
+import { createFromIconfontCN } from '@ant-design/icons';
+
+import {
+  ActionType,
+  DrawerForm,
+  EditableFormInstance,
+  EditableProTable,
+  ProFormInstance,
+  ProFormSelect,
+} from '@ant-design/pro-components';
+import {
+  ModalForm,
+  ProFormDigit,
+  ProFormText,
+  ProFormTextArea,
+} from '@ant-design/pro-form';
+import { ProColumns } from '@ant-design/pro-table';
+import { Input, message, Popconfirm } from 'antd';
+import React, { useEffect, useRef, useState } from 'react';
+
+import { addData, delData, editData, getData } from './service';
+
+import './style.less';
+
+import KCIMPagecontainer from '@/components/KCIMPageContainer';
+import { KCIMTable } from '@/components/KCIMTable';
+import KCIMDrawerForm from '@/components/KCIMDrawerForm';
+import TableSelecter from './tableSelector';
+
+const IconFont = createFromIconfontCN({
+  scriptUrl: '',
+});
+
+type TableDataResponse = {
+  data: any[];
+  success: boolean;
+  total: number;
+  pageSize: number;
+  totalPage: number;
+};
+
+export default function FenyeTemplate() {
+  const [tableDataFilterParams, set_tableDataFilterParams] = useState<
+    any | undefined
+  >();
+  const [tableDataSearchKeywords, set_tableDataSearchKeywords] =
+    useState<string>('');
+  const tableRef = useRef<ActionType>();
+
+  const [tableSelecterVisible, set_tableSelecterVisible] = useState(false);
+  const [tableSelecterType, set_tableSelecterType] = useState<
+    'EMP' | 'CHECKGROUP'
+  >('EMP'); //穿梭框类型
+
+  const [currentEdit, set_currentEdit] = useState<any | undefined>(undefined);
+
+  const columns: ProColumns[] = [
+    {
+      title: 'Code',
+      dataIndex: 'code',
+    },
+    {
+      title: '展示排序',
+      dataIndex: 'sort',
+    },
+    {
+      title: '单位名',
+      dataIndex: 'name',
+    },
+    {
+      title: '职能科室',
+      dataIndex: 'createUserName',
+    },
+    {
+      title: '单位负责人',
+      dataIndex: 'manager',
+      renderText(list, record, index, action) {
+        if (list) {
+          return list.reduce(
+            (prev: string, cur: any) =>
+              `${prev.length > 0 ? prev + ',' : ''}${cur.name}`,
+            '',
+          );
+        }
+      },
+    },
+    {
+      title: '备注',
+      dataIndex: 'remark',
+    },
+    {
+      title: '是否作废',
+      dataIndex: 'remark',
+    },
+    {
+      title: '操作',
+      key: 'option',
+      width: 140,
+      valueType: 'option',
+      render: (_: any, record: any) => {
+        return [
+          <UpDataActBtn key={'act'} record={record} type="EDIT" />,
+          <a
+            key={'emp'}
+            onClick={() => {
+              openTransformModal(record);
+              set_tableSelecterType('EMP');
+            }}
+          >
+            人员
+          </a>,
+          // <a key={'theme'}>主题</a>,
+          // <a key={'checkPoint'}>查核要点</a>
+        ];
+      },
+    },
+  ];
+
+  const getTableData = async (params: any) => {
+    const resp = await getData(params);
+    if (resp) {
+      return {
+        data: resp,
+        success: true,
+      };
+    }
+    return [];
+  };
+
+  const openTransformModal = (record: any) => {
+    set_currentEdit(record);
+    set_tableSelecterVisible(true);
+  };
+
+  const updateTable = async (formVal: any, type: 'EDIT' | 'ADD') => {
+    const content = formVal.table.map((a: any) => ({}));
+
+    if (type == 'ADD') {
+      const result = {
+        name: formVal.name,
+        content: JSON.stringify(formVal.table),
+        remark: formVal.remark,
+      };
+      const resp = await addData({ ...result });
+      if (resp) {
+        tableRef.current?.reload();
+        message.success('操作成功!');
+      }
+    }
+    if (type == 'EDIT') {
+      const result = {
+        id: formVal.id,
+        name: formVal.name,
+        content: JSON.stringify(formVal.table),
+        remark: formVal.remark,
+      };
+      const resp = await editData({ ...result });
+      if (resp) {
+        tableRef.current?.reload();
+        message.success('操作成功!');
+      }
+    }
+
+    return true;
+  };
+
+  const UpDataActBtn = ({
+    record,
+    type,
+  }: {
+    record: any;
+    type: 'EDIT' | 'ADD';
+  }) => {
+    const formRef = useRef<ProFormInstance<any>>();
+
+    return (
+      <ModalForm
+        title={`${type == 'EDIT' ? '编辑' : '新增'}单位`}
+        width={400}
+        initialValues={
+          type == 'EDIT'
+            ? { ...record, filedType: 'Text' }
+            : { filedType: 'Text' }
+        }
+        trigger={
+          type == 'EDIT' ? (
+            <a key="edit">编辑</a>
+          ) : (
+            <span className="add">新增</span>
+          )
+        }
+        onFinish={(val: any) => {
+          return updateTable(
+            type == 'EDIT' ? { ...record, ...val } : { ...val },
+            type,
+          );
+        }}
+        formRef={formRef}
+        modalProps={{ destroyOnClose: true }}
+        colProps={{ span: 24 }}
+        grid
+      >
+        <ProFormText
+          name="name"
+          label="单位名称:"
+          placeholder="请输入"
+          rules={[{ required: true, message: '名称不能为空!' }]}
+        />
+        <ProFormDigit name="sort" label="展示排序" />
+        <ProFormTextArea name="remark" label="备注:" placeholder="请输入" />
+      </ModalForm>
+    );
+  };
+
+  const tableDataSearchHandle = (paramName: string) => {
+    set_tableDataFilterParams({
+      ...tableDataFilterParams,
+      [`${paramName}`]: tableDataSearchKeywords,
+    });
+  };
+
+  const tableSelecterCommit = async (keys: React.Key[], rows: any[]) => {};
+
+  useEffect(() => {}, []);
+
+  return (
+    <KCIMPagecontainer className="unitMana" title={false}>
+      <TableSelecter
+        onVisibleChange={(bool) => set_tableSelecterVisible(bool)}
+        title="关联"
+        rowKey="id"
+        tableSelecterType={tableSelecterType}
+        record={currentEdit}
+        open={tableSelecterVisible}
+        onFinish={(keys, rows) => tableSelecterCommit(keys, rows)}
+      />
+
+      <div className="toolBar">
+        <div className="filter">
+          <div className="filterItem" style={{ marginRight: 16, width: 250 }}>
+            <span className="label" style={{ whiteSpace: 'nowrap' }}>
+              {' '}
+              检索:
+            </span>
+            <Input
+              placeholder={'请输入单位/负责人'}
+              allowClear
+              suffix={
+                <IconFont
+                  type="iconsousuo"
+                  style={{ color: '#99A6BF' }}
+                  onClick={() => tableDataSearchHandle('filter')}
+                />
+              }
+              onChange={(e) => {
+                set_tableDataSearchKeywords(e.target.value);
+                if (e.target.value.length == 0) {
+                  set_tableDataFilterParams({
+                    ...tableDataFilterParams,
+                    filter: '',
+                  });
+                }
+              }}
+              onPressEnter={(e) => {
+                set_tableDataFilterParams({
+                  ...tableDataFilterParams,
+                  filter: (e.target as HTMLInputElement).value,
+                });
+              }}
+            />
+          </div>
+        </div>
+        {/* <div className='btnGroup'>
+                    <UpDataActBtn record type='ADD' />
+                </div> */}
+      </div>
+      <div style={{ marginTop: 16 }}>
+        <KCIMTable
+          columns={columns as ProColumns[]}
+          actionRef={tableRef}
+          rowKey="id"
+          params={tableDataFilterParams}
+          request={(params) => getTableData(params)}
+        />
+      </div>
+    </KCIMPagecontainer>
+  );
+}

+ 50 - 0
src/pages/baseInfoMana/unitMana/service.ts

@@ -0,0 +1,50 @@
+/*
+ * @Author: code4eat awesomedema@gmail.com
+ * @Date: 2023-03-03 16:31:27
+ * @LastEditors: code4eat awesomedema@gmail.com
+ * @LastEditTime: 2023-10-12 17:11:48
+ * @FilePath: /KC-MiddlePlatform/src/pages/platform/setting/pubDicTypeMana/service.ts
+ * @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
+ */
+
+import { request } from 'umi';
+
+//获取table列表数据
+
+export const getData = (params?: any) => {
+  return request('/manager/Department/getDepartmentList', {
+    method: 'GET',
+    params: { ...params },
+  });
+};
+
+//新增表格数据
+export type AddTableDataType = {
+  name: string;
+  content: string;
+  remark: string;
+};
+export const addData = (data: AddTableDataType) => {
+  return request('/manager/setting/addPageTemplate', {
+    method: 'POST',
+    data,
+  });
+};
+
+//编辑表格数据
+
+export const editData = (data: { id: number } & AddTableDataType) => {
+  return request('/manager/setting/editPageTemplate', {
+    method: 'POST',
+    data,
+  });
+};
+
+//删除表格操作
+
+export const delData = (id: string) => {
+  return request('/manager/setting/deletePageTemplate', {
+    method: 'POST',
+    params: { id },
+  });
+};

+ 74 - 0
src/pages/baseInfoMana/unitMana/style.less

@@ -0,0 +1,74 @@
+.TableSelecter {
+  .pfm-ant-modal-footer {
+    display: none !important;
+  }
+
+  .footer {
+    display: flex;
+    flex-direction: row;
+    justify-content: flex-end;
+    margin-top: 15px;
+    span {
+      display: inline-block;
+      width: 56px;
+      height: 24px;
+      font-size: 14px;
+      line-height: 23px;
+      text-align: center;
+      border-radius: 4px;
+      cursor: pointer;
+
+      &.ok {
+        color: #ffffff;
+        background: #3377ff;
+        margin-left: 8px;
+      }
+      &.cancel {
+        border: 1px solid #dae2f2;
+      }
+    }
+  }
+}
+
+.unitMana {
+  margin: 16px;
+  padding: 16px;
+  background: #ffffff;
+  border-radius: 4px;
+
+  .toolBar {
+    display: flex;
+    flex-direction: row;
+    justify-content: space-between;
+    align-items: center;
+
+    .filter {
+      display: flex;
+      flex-direction: row;
+      justify-content: flex-start;
+      align-items: center;
+
+      .filterItem {
+        display: flex;
+        flex-direction: row;
+        justify-content: center;
+        align-items: center;
+      }
+    }
+
+    .btnGroup {
+      .add {
+        cursor: pointer;
+        display: inline-block;
+        font-size: 14px;
+        font-family: SourceHanSansCN-Normal, SourceHanSansCN;
+        font-weight: 400;
+        color: #ffffff;
+        line-height: 24px;
+        padding: 0 14px;
+        background: #3377ff;
+        border-radius: 4px;
+      }
+    }
+  }
+}

+ 213 - 0
src/pages/baseInfoMana/unitMana/tableSelector.tsx

@@ -0,0 +1,213 @@
+import React, { useEffect, useState } from 'react';
+import { Input, Switch } from 'antd';
+import { KCIMTable } from '@/components/KCIMTable';
+import { createFromIconfontCN } from '@ant-design/icons';
+import { getCheckData } from '../checkGroupMana/service';
+
+import { ModalForm, ProColumns } from '@ant-design/pro-components';
+// import { getData } from "../empMana/service";
+
+// import './style.less';
+
+interface TableSelecterProps {
+  record: any;
+}
+
+const IconFont = createFromIconfontCN({
+  scriptUrl: '',
+});
+
+const TableSelecter = ({
+  tableSelecterType,
+  record,
+  open,
+  title,
+  onVisibleChange,
+  rowKey = 'id',
+  request,
+  onFinish,
+}: {
+  record: any;
+  open: boolean;
+  title: string;
+  onVisibleChange: (bool: boolean) => void;
+  tableSelecterType: string;
+  rowKey?: string;
+  request?: (params: any) => Promise<any>;
+  onFinish?: (selectedKeys: React.Key[], selectedRows: any[]) => void;
+}) => {
+  const Table = React.forwardRef(({}: TableSelecterProps, ref) => {
+    const [datasource, set_datasource] = useState<any[]>([]);
+    const [selectedKeys, setSelectedKeys] = useState<React.Key[]>([]);
+    const [selectedRows, set_selectedRows] = useState<any[]>([]);
+    const [showList, set_showList] = useState<any[]>([]);
+
+    const empColumns: ProColumns[] = [
+      {
+        title: 'ID',
+        dataIndex: 'id',
+      },
+      {
+        title: '姓名',
+        dataIndex: 'name',
+      },
+    ];
+    const checkGroupColumns: ProColumns[] = [
+      {
+        title: 'ID',
+        dataIndex: 'id',
+      },
+      {
+        title: '查核组名',
+        dataIndex: 'id',
+      },
+      {
+        title: '是否查核组长',
+        dataIndex: 'flag',
+        renderText(flag, record, index, action) {
+          return (
+            <Switch
+              size="small"
+              checked={flag == '1'}
+              onChange={(bool) => onSwitchChange(bool, record)}
+            />
+          );
+        },
+      },
+    ];
+
+    const onSwitchChange = (bool: boolean, record: any) => {
+      const newData = datasource.map((item) => {
+        if (item.id == record.id) {
+          return {
+            ...item,
+            flag: bool,
+          };
+        } else {
+          return item;
+        }
+      });
+      const newShowList = showList.map((item) => {
+        if (item.id == record.id) {
+          return {
+            ...item,
+            flag: bool,
+          };
+        } else {
+          return item;
+        }
+      });
+
+      // console.log({newData,newShowList});
+      set_datasource([...newData]);
+      set_showList([...newShowList]);
+    };
+
+    const onSelectChange = (
+      newSelectedRowKeys: React.Key[],
+      selectedRows: any,
+    ) => {
+      setSelectedKeys([...newSelectedRowKeys]);
+    };
+
+    const saveHandle = async () => {
+      onFinish && onFinish(selectedKeys, selectedRows);
+    };
+
+    const getTableData = async () => {
+      // const resp = await getData({
+      //     current: 1,
+      //     pageSize: 500
+      // });
+      // if (resp) {
+      //     set_showList(resp.list);
+      //     set_datasource([...datasource, ...resp.list]);
+      // }
+
+      return Promise.resolve([]);
+    };
+
+    useEffect(() => {
+      console.log({ record });
+    }, [record]);
+
+    useEffect(() => {
+      getTableData();
+    }, []);
+
+    return (
+      <div>
+        <Input
+          placeholder={'请输入名称'}
+          allowClear
+          suffix={<IconFont style={{ color: '#99A6BF' }} type="iconsousuo" />}
+          style={{ marginBottom: 8 }}
+          onChange={(e) => {
+            if (e.target.value.length != 0) {
+              const result = datasource.filter(
+                (item) => item.name.indexOf(e.target.value) != -1,
+              );
+              set_showList(result);
+            } else {
+              set_showList(datasource);
+            }
+          }}
+        />
+        <KCIMTable
+          columns={tableSelecterType == 'EMP' ? empColumns : checkGroupColumns}
+          options={{
+            density: true,
+            setting: {
+              listsHeight: 100,
+            },
+          }}
+          rowKey={rowKey}
+          scroll={{ y: 400 }}
+          tableAlertRender={false}
+          rowSelection={{
+            // 自定义选择项参考: https://ant.design/components/table-cn/#components-table-demo-row-selection-custom
+            // 注释该行则默认不显示下拉选项
+            // selections: [Table.SELECTION_ALL, Table.SELECTION_INVERT],
+            onChange: onSelectChange,
+          }}
+          pagination={{ showTitle: false, showSizeChanger: true }}
+          dataSource={showList}
+        />
+        <div className="footer">
+          <span className="cancel" onClick={() => close()}>
+            取消
+          </span>
+          <span className="ok" onClick={() => saveHandle()}>{`确认(${
+            selectedKeys.length > 0 && selectedKeys.length
+          })`}</span>
+        </div>
+      </div>
+    );
+  });
+
+  const close = () => {
+    onVisibleChange && onVisibleChange(false);
+  };
+
+  return (
+    <ModalForm
+      className="TableSelecter"
+      title={title}
+      width={600}
+      submitter={{
+        render: false,
+      }}
+      open={open}
+      modalProps={{
+        closable: false,
+      }}
+    >
+      <Table
+        // ref={tableSelecterRef}
+        record={undefined}
+      ></Table>
+    </ModalForm>
+  );
+};
+
+export default TableSelecter;

+ 969 - 0
src/pages/checkConditionMana/index.tsx

@@ -0,0 +1,969 @@
+/*
+ * @Author: code4eat awesomedema@gmail.com
+ * @Date: 2025-04-16 10:09:18
+ * @LastEditors: code4eat awesomedema@gmail.com
+ * @LastEditTime: 2025-07-09 18:55:02
+ * @FilePath: /pfmBackMana/src/pages/checkConditionMana/index.tsx
+ * @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
+ */
+
+/*
+ * @Description: 条件管理页面
+ */
+
+import { createFromIconfontCN } from '@ant-design/icons';
+import {
+  ActionType,
+  ProColumns,
+  ProFormInstance,
+} from '@ant-design/pro-components';
+import {
+  ModalForm,
+  ProFormDigit,
+  ProFormSelect,
+  ProFormText,
+  ProFormTextArea,
+  ProFormRadio,
+  ProFormTreeSelect,
+} from '@ant-design/pro-form';
+import { Button, Empty, Input, message, Popconfirm, Tabs, Tooltip } from 'antd';
+import React, { useEffect, useRef, useState } from 'react';
+
+import {
+  addCondition,
+  delCondition,
+  editCondition,
+  getConditionDeptClassList,
+  getConditionList,
+  saveConditionDeptClass,
+} from './service';
+
+import './style.less';
+
+import KCIMPagecontainer from '@/components/KCIMPageContainer';
+import { KCIMTable } from '@/components/KCIMTable';
+
+const IconFont = createFromIconfontCN({
+  scriptUrl: '',
+});
+
+// 定义表单样式
+const formItemLayout = {
+  style: { marginBottom: '8px' },
+};
+
+export default function CheckConditionMana() {
+  const [tableDataFilterParams, setTableDataFilterParams] = useState<
+    any | undefined
+  >();
+  const [tableDataSearchKeywords, setTableDataSearchKeywords] =
+    useState<string>('');
+  const tableRef = useRef<ActionType>();
+  const [activeTab, setActiveTab] = useState('系统追踪');
+
+  // 分类相关状态
+  const [classifyModalVisible, setClassifyModalVisible] = useState(false);
+  const [classifyRecord, setClassifyRecord] = useState<any>(null);
+  const [classifyList, setClassifyList] = useState<any[]>([]);
+  const [classifyLoading, setClassifyLoading] = useState(false);
+  const [selectedClassifyIds, setSelectedClassifyIds] = useState<React.Key[]>(
+    [],
+  );
+
+
+
+  // 条件列表数据,用于子项选择
+  const [conditionOptions, setConditionOptions] = useState<any[]>([]);
+
+  // 选项卡项目
+  const tabItems = [
+    { label: '系统追踪', key: '系统追踪' },
+    { label: '个案追踪', key: '个案追踪' },
+  ];
+
+  // 诊别选项
+  const typeOptions = [
+    { label: '门诊', value: 1 },
+    { label: '急诊', value: 2 },
+  ];
+
+  // 是否选项
+  const yesNoOptions = [
+    { label: '否', value: 0 },
+    { label: '是', value: 1 },
+  ];
+
+  // 选择类型选项
+  const multipleOptions = [
+    { label: '不选', value: 0 },
+    { label: '单选', value: 1 },
+    { label: '多选', value: 2 },
+  ];
+
+  // 系统追踪表格列定义
+  const systemColumns: ProColumns[] = [
+    {
+      title: '条件名称',
+      dataIndex: 'name',
+    },
+    {
+      title: '诊别',
+      dataIndex: 'depType',
+      width: 100,
+      valueEnum: {
+        1: { text: '门诊' },
+        2: { text: '急诊' },
+      },
+    },
+    {
+      title: '自查督查',
+      dataIndex: 'checkType',
+      width: 100,
+      valueEnum: {
+        0: { text: '否' },
+        1: { text: '是' },
+      },
+    },
+    {
+      title: '顺序号',
+      dataIndex: 'sortNo',
+      width: 80,
+    },
+    {
+      title: '单位分类',
+      dataIndex: 'deptClassList',
+      render: (_, record) => {
+        if (record.deptClassList && record.deptClassList.length > 0) {
+          return record.deptClassList
+            .map((item: any) => item.deptClassName)
+            .join(', ');
+        }
+        return '-';
+      },
+    },
+    {
+      title: '说明',
+      dataIndex: 'description',
+      ellipsis: true,
+    },
+    {
+      title: '操作',
+      key: 'option',
+      width: 180,
+      valueType: 'option',
+      render: (_: any, record: any) => {
+        // 判断是否有子项
+        const hasChildren = record.children && record.children.length > 0;
+        // 判断是否已选择单位分类
+        const hasDeptClass =
+          record.deptClassList && record.deptClassList.length > 0;
+
+        return (
+          <div className="operationBtns">
+            {hasChildren ? (
+              <Tooltip title="已添加子项,不可设置单位分类">
+                <span className="disabled-btn">分类</span>
+              </Tooltip>
+            ) : (
+              <a onClick={() => handleClassify(record)}>分类</a>
+            )}
+
+            {hasDeptClass ? (
+              <Tooltip title="已选单位分类,不可添加子项">
+                <span className="disabled-btn">添加</span>
+              </Tooltip>
+            ) : (
+              <UpDataActBtn 
+                record={null} 
+                type="ADD" 
+                parentRecord={{
+                  id: record.id,
+                  name: record.name,
+                  hasChildren: record.children && record.children.length > 0,
+                }}
+                triggerText="添加"
+              />
+            )}
+
+            <UpDataActBtn record={record} type="EDIT" />
+            <Popconfirm
+              title={`确定要删除该条件吗?${hasChildren ? '(包括所有子项)' : ''}`}
+              onConfirm={() => handleDelete(record.id, record)}
+              okText="确定"
+              cancelText="取消"
+            >
+              <a>删除</a>
+            </Popconfirm>
+          </div>
+        );
+      },
+    },
+  ];
+
+  // 个案追踪表格列定义
+  const caseColumns: ProColumns[] = [
+    {
+      title: '条件名称',
+      dataIndex: 'name',
+    },
+    {
+      title: '条件设置',
+      dataIndex: 'code',
+      width: 120,
+      render: (text: any, record: any) => {
+        const tags = [];
+
+        // 添加初显标签
+        if (record.initialize === 1) {
+          tags.push(
+            <span key="initialize" className="tag-gray">
+              初显
+            </span>,
+          );
+        }
+
+        // 添加选择类型标签
+        if (record.multiple === 1) {
+          tags.push(
+            <span key="multiple" className="tag-blue">
+              单选
+            </span>,
+          );
+        } else if (record.multiple === 2) {
+          tags.push(
+            <span key="multiple" className="tag-blue">
+              多选
+            </span>,
+          );
+        }
+
+        // 添加必填标签
+        if (record.required === 1) {
+          tags.push(
+            <span key="required" className="tag-red">
+              必填
+            </span>,
+          );
+        }
+
+        return <div style={{ marginTop: '4px' }}>{tags}</div>;
+      },
+    },
+    {
+      title: '序列号',
+      dataIndex: 'sortNo',
+      width: 80,
+    },
+    {
+      title: '子项',
+      dataIndex: 'children',
+      width: 80,
+      render: (_, record) => {
+        if (record.children && record.children.length > 0) {
+          return record.children.length;
+        }
+        return '-';
+      },
+    },
+    {
+      title: '单位分类',
+      dataIndex: 'deptClassList',
+      width: 150,
+      render: (_, record) => {
+        if (record.deptClassList && record.deptClassList.length > 0) {
+          return record.deptClassList
+            .map((item: any) => item.deptClassName)
+            .join(', ');
+        }
+        return '-';
+      },
+    },
+    {
+      title: '说明',
+      dataIndex: 'description',
+      ellipsis: true,
+    },
+    {
+      title: '操作',
+      key: 'option',
+      width: 180,
+      valueType: 'option',
+      render: (_: any, record: any) => {
+        // 判断是否有子项
+        const hasChildren = record.children && record.children.length > 0;
+        // 判断是否已选择单位分类
+        const hasDeptClass =
+          record.deptClassList && record.deptClassList.length > 0;
+
+        return (
+          <div className="operationBtns">
+            {hasChildren ? (
+              <Tooltip title="已添加子项,不可设置单位分类">
+                <span className="disabled-btn">分类</span>
+              </Tooltip>
+            ) : (
+              <a onClick={() => handleClassify(record)}>分类</a>
+            )}
+
+            {hasDeptClass ? (
+              <Tooltip title="已选单位分类,不可添加子项">
+                <span className="disabled-btn">添加</span>
+              </Tooltip>
+            ) : (
+              <UpDataActBtn 
+                record={null} 
+                type="ADD" 
+                parentRecord={{
+                  id: record.id,
+                  name: record.name,
+                  hasChildren: record.children && record.children.length > 0,
+                }}
+                triggerText="添加"
+              />
+            )}
+
+            <UpDataActBtn record={record} type="EDIT" />
+            <Popconfirm
+              title={`确定要删除该条件吗?${hasChildren ? '(包括所有子项)' : ''}`}
+              onConfirm={() => handleDelete(record.id, record)}
+              okText="确定"
+              cancelText="取消"
+            >
+              <a>删除</a>
+            </Popconfirm>
+          </div>
+        );
+      },
+    },
+  ];
+
+  // 获取表格数据
+  const getTableData = async (params: any) => {
+    // 根据当前选项卡设置conditionType
+    const conditionType = activeTab === '系统追踪' ? 1 : 0;
+    const resp = await getConditionList({
+      ...params,
+      conditionType,
+      filter: tableDataSearchKeywords || undefined,
+    });
+    if (resp) {
+      return {
+        data: resp,
+        success: true,
+      };
+    }
+    return { data: [], success: false };
+  };
+
+  // 处理分类
+  const handleClassify = async (record: any) => {
+    // 先重置状态
+    setClassifyRecord(record);
+    setSelectedClassifyIds([]);
+    setClassifyList([]);
+    setClassifyModalVisible(true);
+    setClassifyLoading(true);
+
+    try {
+      const resp = await getConditionDeptClassList({
+        filterConditionId: record.id,
+      });
+
+      if (resp) {
+        // 先设置数据源
+        setClassifyList(resp.itemList || []);
+        
+        // 然后设置选中项,确保数据类型正确
+        const selectedIds = (resp.mapIdList || []).map((id: any) => String(id));
+        
+        // 使用 setTimeout 确保数据渲染完成后再设置选中状态
+        setTimeout(() => {
+          setSelectedClassifyIds(selectedIds);
+        }, 100);
+      }
+    } catch (error) {
+      console.error('获取分类列表失败:', error);
+      message.error('获取分类列表失败');
+    } finally {
+      setClassifyLoading(false);
+    }
+  };
+
+  // 处理分类保存
+  const handleClassifySave = async () => {
+    if (!classifyRecord) return;
+
+    try {
+      const resp = await saveConditionDeptClass({
+        id: classifyRecord.id,
+        mapIdList: selectedClassifyIds,
+      });
+
+      if (resp) {
+        message.success('保存成功');
+        setClassifyModalVisible(false);
+        tableRef.current?.reload();
+      }
+    } catch (error) {
+      console.error('保存分类失败:', error);
+      message.error('保存分类失败');
+    }
+  };
+
+
+
+  // 收集所有子项ID的递归函数
+  const collectAllChildrenIds = (record: any): string[] => {
+    let ids: string[] = [record.id];
+    
+    if (record.children && record.children.length > 0) {
+      record.children.forEach((child: any) => {
+        ids = ids.concat(collectAllChildrenIds(child));
+      });
+    }
+    
+    return ids;
+  };
+
+  // 处理删除
+  const handleDelete = async (recordId: string, record?: any) => {
+    // 如果有完整的record对象,收集所有需要删除的ID
+    let idsToDelete: string[] = [recordId];
+    
+    if (record && record.children && record.children.length > 0) {
+      idsToDelete = collectAllChildrenIds(record);
+    }
+    
+    // 如果有子项,传递数组;否则传递单个ID
+    const resp = await delCondition(idsToDelete.length > 1 ? idsToDelete : recordId);
+    if (resp) {
+      const deletedCount = idsToDelete.length;
+      message.success(`删除成功!${deletedCount > 1 ? `(包括${deletedCount - 1}个子项)` : ''}`);
+      tableRef.current?.reload();
+    }
+  };
+
+  // 获取条件列表用于子项选择
+  const getConditionOptionsForSubItem = async () => {
+    const conditionType = activeTab === '系统追踪' ? 1 : 0;
+    const resp = await getConditionList({ conditionType });
+    if (resp) {
+      setConditionOptions(resp);
+    }
+  };
+
+
+
+  // 编辑/新增按钮组件
+  const UpDataActBtn = ({
+    record,
+    type,
+    parentRecord = null,
+    triggerText,
+  }: {
+    record: any;
+    type: 'EDIT' | 'ADD';
+    parentRecord?: any;
+    triggerText?: string;
+  }) => {
+    const formRef = useRef<ProFormInstance<any>>();
+
+    // 系统追踪表单
+    const renderSystemForm = () => (
+      <>
+        <ProFormText
+          name="name"
+          label="条件名称:"
+          placeholder="请输入"
+          rules={[{ required: true, message: '条件名称不能为空!' }]}
+          {...formItemLayout}
+        />
+        <ProFormRadio.Group
+          name="depType"
+          label="诊别:"
+          options={[
+            { label: '门诊', value: 1 },
+            { label: '急诊', value: 2 },
+          ]}
+          rules={[{ required: true, message: '请选择诊别!' }]}
+          {...formItemLayout}
+        />
+        <ProFormRadio.Group
+          name="checkType"
+          label="自查督查:"
+          options={[
+            { label: '否', value: 0 },
+            { label: '是', value: 1 },
+          ]}
+          rules={[{ required: true, message: '请选择是否自查督查!' }]}
+          {...formItemLayout}
+        />
+        <ProFormDigit
+          name="sortNo"
+          label="序号:"
+          placeholder="请输入"
+          min={1}
+          rules={[{ required: true, message: '请输入序号!' }]}
+          {...formItemLayout}
+        />
+        <ProFormTextArea
+          name="description"
+          label="说明:"
+          placeholder="请输入"
+          {...formItemLayout}
+        />
+      </>
+    );
+
+    // 个案追踪表单
+    const renderCaseForm = () => (
+      <>
+        <ProFormText
+          name="name"
+          label="条件名称:"
+          placeholder="请输入"
+          rules={[{ required: true, message: '条件名称不能为空!' }]}
+          {...formItemLayout}
+        />
+        <ProFormRadio.Group
+          name="required"
+          label="必填:"
+          options={[
+            { label: '否', value: 0 },
+            { label: '是', value: 1 },
+          ]}
+          rules={[{ required: true, message: '请选择是否必填!' }]}
+          {...formItemLayout}
+        />
+        <ProFormRadio.Group
+          name="multiple"
+          label="选择类型:"
+          options={[
+            { label: '不选', value: 0 },
+            { label: '单选', value: 1 },
+            { label: '多选', value: 2 },
+          ]}
+          rules={[{ required: true, message: '请选择选择类型!' }]}
+          {...formItemLayout}
+        />
+        <ProFormRadio.Group
+          name="initialize"
+          label="初显:"
+          options={[
+            { label: '否', value: 0 },
+            { label: '是', value: 1 },
+          ]}
+          rules={[{ required: true, message: '请选择是否初显!' }]}
+          {...formItemLayout}
+        />
+        <ProFormTreeSelect
+          name="subOptionId"
+          label="子项:"
+          placeholder="请选择"
+          allowClear
+          fieldProps={{
+            onFocus: () => {
+              // 在获取焦点时加载数据,避免影响弹窗状态
+              if (conditionOptions.length === 0) {
+                getConditionOptionsForSubItem();
+              }
+            },
+            showSearch: true,
+            treeData: conditionOptions,
+            fieldNames: { label: 'name', value: 'id', children: 'children' },
+            dropdownStyle: { maxHeight: 400, overflow: 'auto' },
+          }}
+          {...formItemLayout}
+        />
+        <ProFormDigit
+          name="sortNo"
+          label="序号:"
+          placeholder="请输入"
+          min={1}
+          rules={[{ required: true, message: '请输入序号!' }]}
+          {...formItemLayout}
+        />
+        <ProFormTextArea
+          name="description"
+          label="说明:"
+          placeholder="请输入"
+          {...formItemLayout}
+        />
+        <ProFormDigit name="depType" hidden initialValue={1} />
+        <ProFormDigit name="checkType" hidden initialValue={0} />
+        <ProFormDigit name="subOptions" hidden initialValue={0} />
+        <ProFormDigit name="parentId" hidden initialValue={0} />
+        <ProFormText name="parentName" hidden initialValue="" />
+      </>
+    );
+
+    return (
+      <ModalForm
+        title={`${
+          type === 'EDIT' 
+            ? '编辑' 
+            : parentRecord 
+            ? `添加子${activeTab === '系统追踪' ? '系统' : '个案'}条件 (${parentRecord.name})`
+            : '新增'
+        }${
+          activeTab === '系统追踪' ? '系统条件' : '个案条件'
+        }`}
+        width={352}
+        initialValues={
+          type === 'EDIT'
+            ? (() => {
+                // 编辑时处理 subOptionId,如果为 0 则不设置该字段(显示为空)
+                const editValues = { ...record };
+                if (editValues.subOptionId === 0) {
+                  delete editValues.subOptionId;
+                }
+                return editValues;
+              })()
+            : activeTab === '系统追踪'
+            ? { depType: 1, checkType: 0, sortNo: 1 }
+            : {
+                required: 0,
+                multiple: 0,
+                initialize: 0,
+                checkType: 0,
+                sortNo: 1,
+                parentId: parentRecord?.id || 0,
+                parentName: parentRecord?.name || '',
+                subOptions: parentRecord?.hasChildren ? 1 : 0,
+                subOptionId: undefined, // 新增时默认为 undefined,不显示 0
+                description: '',
+                depType: 1,
+              }
+        }
+        trigger={
+          type === 'EDIT' ? (
+            <a>修改</a>
+          ) : triggerText ? (
+            <a>{triggerText}</a>
+          ) : (
+            <span className="add">新增</span>
+          )
+        }
+        onFinish={async (val: any) => {
+
+          
+          const conditionType = activeTab === '系统追踪' ? 1 : 0;
+          
+          // 准备表单数据
+          let submitData = {
+            ...val,
+            conditionType,
+          };
+          
+          // 个案追踪的子项逻辑处理(新增和编辑都需要)
+          if (activeTab === '个案追踪') {
+            // 处理子选项逻辑
+            if (submitData.subOptionId && submitData.subOptionId !== 0) {
+              // 如果选择了子项,设置subOptions为1
+              submitData.subOptions = 1;
+            } else {
+              // 如果没有选择子项或清空了子项,重置相关字段
+              submitData.subOptionId = 0;
+              submitData.subOptions = 0;
+            }
+          }
+          
+          // 编辑时只传递需要的字段,避免传递多余字段
+          if (type === 'EDIT') {
+            // 编辑时:添加id,保持原有的父级关系
+            submitData = {
+              id: record.id,
+              ...submitData,
+              // 如果是个案追踪,需要保持原有的父级关系
+              ...(activeTab === '个案追踪' && {
+                parentId: record.parentId || 0,
+                parentName: record.parentName || '',
+              })
+            };
+          }
+
+          
+          // 直接调用接口,不走updateTable
+          if (type === 'ADD') {
+            // 如果是系统追踪,补充 parentId 和 parentName 字段
+            if (activeTab === '系统追踪') {
+              submitData = {
+                ...submitData,
+                parentId: parentRecord ? parentRecord.id : 0, // 有父级记录时使用父级id,否则为0
+                parentName: parentRecord ? parentRecord.name : '', // 有父级记录时使用父级名称,否则为空
+              };
+            }
+            const resp = await addCondition(submitData);
+            if (resp) {
+              tableRef.current?.reload();
+              message.success('添加成功!');
+            }
+            return true;
+          }
+          if (type === 'EDIT') {
+            const resp = await editCondition(submitData);
+            if (resp) {
+              tableRef.current?.reload();
+              message.success('编辑成功!');
+            }
+            return true;
+          }
+        }}
+        formRef={formRef}
+        modalProps={{ destroyOnClose: true }}
+        colProps={{ span: 24 }}
+        grid
+      >
+        {activeTab === '系统追踪' ? renderSystemForm() : renderCaseForm()}
+      </ModalForm>
+    );
+  };
+
+  // 处理搜索
+  const tableDataSearchHandle = () => {
+    tableRef.current?.reload();
+  };
+
+  // 处理选项卡切换
+  const handleTabChange = (key: string) => {
+    setActiveTab(key);
+    // 重置搜索条件
+    setTableDataSearchKeywords('');
+    setTableDataFilterParams({});
+    // 切换选项卡后,重新加载数据
+    tableRef.current?.reload();
+  };
+
+  // 组件初始化时预加载条件选项
+  useEffect(() => {
+    if (activeTab === '个案追踪') {
+      getConditionOptionsForSubItem();
+    }
+  }, [activeTab]);
+
+  // 自定义空状态
+  const customizeRenderEmpty = () => (
+    <div style={{ textAlign: 'center', padding: '50px 0' }}>
+      <Empty image={Empty.PRESENTED_IMAGE_SIMPLE} description="暂无数据" />
+    </div>
+  );
+
+  // 渲染系统追踪内容
+  const renderSystemTabContent = () => {
+    return (
+      <>
+        <div className="toolBar">
+          <div className="filter">
+            <div className="filterItem" style={{ marginRight: 16, width: 250 }}>
+              <span className="label">检索:</span>
+              <Input
+                placeholder="请输入条件名称"
+                allowClear
+                suffix={
+                  <IconFont
+                    type="iconsousuo"
+                    style={{ color: '#99A6BF', cursor: 'pointer' }}
+                    onClick={() => tableDataSearchHandle()}
+                  />
+                }
+                onChange={(e) => {
+                  setTableDataSearchKeywords(e.target.value);
+                  if (e.target.value.length === 0) {
+                    tableRef.current?.reload();
+                  }
+                }}
+                onPressEnter={() => tableDataSearchHandle()}
+              />
+            </div>
+          </div>
+          <div className="btnGroup">
+            <UpDataActBtn type="ADD" record={null} />
+          </div>
+        </div>
+
+        <KCIMTable
+          actionRef={tableRef}
+          columns={systemColumns}
+          request={getTableData}
+          rowKey="id"
+          search={false}
+          locale={{ emptyText: customizeRenderEmpty() }}
+          scroll={{ y: 'calc(100vh - 272px)' }}
+          pagination={{
+            pageSize: 10,
+            showSizeChanger: true,
+          }}
+          expandable={{
+            childrenColumnName: 'children',
+            defaultExpandAllRows: true,
+          }}
+        />
+      </>
+    );
+  };
+
+  // 渲染个案追踪内容
+  const renderCaseTrackingContent = () => {
+    return (
+      <>
+        <div className="toolBar">
+          <div className="filter">
+            <div className="filterItem" style={{ marginRight: 16, width: 250 }}>
+              <span className="label">检索:</span>
+              <Input
+                placeholder="请输入条件名称"
+                allowClear
+                suffix={
+                  <IconFont
+                    type="iconsousuo"
+                    style={{ color: '#99A6BF', cursor: 'pointer' }}
+                    onClick={() => tableDataSearchHandle()}
+                  />
+                }
+                onChange={(e) => {
+                  setTableDataSearchKeywords(e.target.value);
+                  if (e.target.value.length === 0) {
+                    tableRef.current?.reload();
+                  }
+                }}
+                onPressEnter={() => tableDataSearchHandle()}
+              />
+            </div>
+          </div>
+          <div className="btnGroup">
+            <UpDataActBtn type="ADD" record={null} />
+          </div>
+        </div>
+
+        <KCIMTable
+          actionRef={tableRef}
+          columns={caseColumns}
+          request={getTableData}
+          rowKey="id"
+          search={false}
+          locale={{ emptyText: customizeRenderEmpty() }}
+          pagination={{
+            pageSize: 10,
+            showSizeChanger: true,
+          }}
+          scroll={{ y: 'calc(100vh - 272px)' }}
+          expandable={{
+            childrenColumnName: 'children',
+            defaultExpandAllRows: true,
+          }}
+        />
+      </>
+    );
+  };
+
+
+
+  return (
+    <KCIMPagecontainer className="checkConditionMana" title={false}>
+      <div
+        style={{
+          padding: '16px',
+          background: '#fff',
+          borderRadius: '4px',
+          paddingTop: 0,
+        }}
+      >
+        <Tabs
+          activeKey={activeTab}
+          items={tabItems}
+          onChange={handleTabChange}
+          className="condition-tabs"
+        />
+
+        {activeTab === '系统追踪'
+          ? renderSystemTabContent()
+          : renderCaseTrackingContent()}
+
+
+
+        {/* 分类选择弹窗 */}
+        <ModalForm
+          className="TableSelecter"
+          title={`选择单位分类 (${classifyRecord?.name || ''})`}
+          width={352}
+          submitter={{
+            render: false,
+          }}
+          open={classifyModalVisible}
+          modalProps={{
+            closable: false,
+          }}
+        >
+       
+          <div>
+            <Input
+              placeholder="请输入"
+              allowClear
+              suffix={
+                <IconFont style={{ color: '#99A6BF' }} type="iconsousuo" />
+              }
+              style={{ marginBottom: 8 }}
+              onChange={(e) => {
+                if (e.target.value.length === 0) {
+                  // 重置搜索
+                  if (classifyRecord) {
+                    handleClassify(classifyRecord);
+                  }
+                } else {
+                  // 本地搜索
+                  const filteredList = classifyList.filter(
+                    (item) => item.name.indexOf(e.target.value) !== -1,
+                  );
+                  setClassifyList(filteredList);
+                }
+              }}
+            />
+
+            <KCIMTable
+              columns={[
+                {
+                  title: '单位分类名称',
+                  dataIndex: 'name',
+                  key: 'name',
+                },
+                {
+                  title: '角色名称',
+                  dataIndex: 'depTypeName',
+                  key: 'depTypeName',
+                },
+              ]}
+              options={{
+                density: false,
+                setting: false,
+              }}
+              rowKey={(record) => String(record.id)}
+              scroll={{ y: 465 }}
+              tableAlertRender={false}
+              rowSelection={{
+                selectedRowKeys: selectedClassifyIds,
+                onChange: (newSelectedRowKeys: React.Key[]) => {
+                  setSelectedClassifyIds(newSelectedRowKeys);
+                },
+              }}
+              pagination={false}
+              dataSource={classifyList}
+              loading={classifyLoading}
+            />
+
+            <div className="footer">
+              <span
+                className="cancel"
+                onClick={() => setClassifyModalVisible(false)}
+              >
+                取消
+              </span>
+              <span className="ok" onClick={handleClassifySave}>
+                {`确认(${selectedClassifyIds.length})`}
+              </span>
+            </div>
+          </div>
+        </ModalForm>
+      </div>
+    </KCIMPagecontainer>
+  );
+}

+ 96 - 0
src/pages/checkConditionMana/service.ts

@@ -0,0 +1,96 @@
+/*
+ * @Author: code4eat awesomedema@gmail.com
+ * @Date: 2025-05-28 11:11:21
+ * @LastEditors: code4eat awesomedema@gmail.com
+ * @LastEditTime: 2025-07-03 15:55:44
+ * @FilePath: /pfmBackMana/src/pages/checkConditionMana/service.ts
+ * @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
+ */
+/*
+ * @Description: 条件管理服务
+ */
+
+import { request } from 'umi';
+
+// 获取条件列表数据
+export const getConditionList = (params?: any) => {
+  return request('/manager/FilterCondition/getSysFilterConditionList', {
+    method: 'GET',
+    params: { ...params },
+  });
+};
+
+// 获取条件分类列表
+export const getConditionDeptClassList = (params: {
+  filterConditionId: string | number;
+  filter?: string;
+  page?: number;
+  pageSize?: number;
+}) => {
+  return request('/manager/FilterCondition/getFilterConditionDeptclassList', {
+    method: 'GET',
+    params,
+  });
+};
+
+// 保存条件分类关联
+export const saveConditionDeptClass = (data: {
+  id: string;
+  mapIdList: (string | number)[];
+}) => {
+  return request('/manager/FilterCondition/applyFilterConditionDeptclass', {
+    method: 'POST',
+    data,
+  });
+};
+
+// 新增条件数据
+export type AddConditionType = {
+  name: string; // 条件名称
+  parentId?: number; // 父级条件id
+  parentName?: string; // 父级条件名称
+  conditionType: string; // 条件类型:0个案/1系统
+  description: string; // 说明
+  sortNo: number; // 顺序号
+  depType: number; // 门诊类型:1门诊/2急诊
+  checkType: number; // 是否自宣督查:0否/1是
+  // 个案追踪专用字段
+  required?: number; // 是否必填:0否/1是
+  multiple?: number; // 选择类型:0不选/1单选/2多选
+  initialize?: number; // 是否初显:0否/1是
+  subOptions?: number; // 是否有子选项:0无/1有
+  subOptionId?: number; // 子选项id
+};
+
+export const addCondition = (data: AddConditionType) => {
+  return request('/manager/FilterCondition/addSysFilterCondition', {
+    method: 'POST',
+    data,
+  });
+};
+
+// 编辑条件数据
+export const editCondition = (data: { id: number } & AddConditionType) => {
+  return request('/manager/FilterCondition/updateSysFilterCondition', {
+    method: 'POST',
+    data,
+  });
+};
+
+// 删除条件
+export const delCondition = (id: string | number | (string | number)[]) => {
+  // 如果传入的是数组,直接使用;否则包装成数组
+  const idsArray = Array.isArray(id) ? id : [id];
+  return request('/manager/FilterCondition/deleteSysFilterCondition', {
+    method: 'POST',
+    data: idsArray, // 入参格式是数组
+  });
+};
+
+// 添加子条件
+export const addSubCondition = (data: any) => {
+  return request('/manager/FilterCondition/addSubFilterCondition', {
+    method: 'POST',
+    data,
+  });
+};

+ 170 - 0
src/pages/checkConditionMana/style.less

@@ -0,0 +1,170 @@
+.checkConditionMana {
+  padding: 16px;
+  border-radius: 4px;
+  .condition-tabs {
+    .pfm-ant-tabs-nav {
+      &::before {
+        border-bottom: none !important;
+      }
+
+      .pfm-ant-tabs-tab {
+        padding: 8px 0 !important;
+        font-size: 14px;
+
+        &.pfm-ant-tabs-tab-active {
+          .pfm-ant-tabs-tab-btn {
+            color: #3377ff;
+          }
+        }
+
+        .pfm-ant-tabs-tab-btn {
+          color: #606c80;
+        }
+      }
+
+      .pfm-ant-tabs-ink-bar {
+        background-color: #3377ff;
+      }
+    }
+  }
+
+  .toolBar {
+    display: flex;
+    flex-direction: row;
+    justify-content: space-between;
+    align-items: center;
+    margin-bottom: 16px;
+
+    .filter {
+      display: flex;
+      flex-direction: row;
+      justify-content: flex-start;
+      align-items: center;
+
+      .filterItem {
+        display: flex;
+        flex-direction: row;
+        justify-content: flex-start;
+        align-items: center;
+
+        .label {
+          color: #606c80;
+          margin-right: 8px;
+          white-space: nowrap;
+        }
+      }
+    }
+
+    .btnGroup {
+      .add {
+        cursor: pointer;
+        display: inline-block;
+        padding: 2px 14px;
+        font-size: 14px;
+        font-weight: 400;
+        color: #ffffff;
+        background: #3377ff;
+        border-radius: 4px;
+      }
+    }
+  }
+
+  .operationBtns {
+    display: flex;
+    gap: 8px;
+
+    a {
+      color: #3377ff;
+      cursor: pointer;
+    }
+
+    .disabled-btn {
+      color: #99a6bf;
+      cursor: not-allowed;
+      display: inline-block;
+      padding: 0;
+    }
+  }
+
+  // 标签样式
+  .tag-blue {
+    display: inline-block;
+    padding: 0px 4px;
+    font-size: 12px;
+    color: #0080ff;
+    background: #ebf5ff;
+    border-radius: 2px;
+    margin-right: 2px;
+  }
+
+  .tag-gray {
+    display: inline-block;
+    padding: 0px 4px;
+    font-size: 12px;
+    color: #525866;
+    background: #dae2f2;
+    border-radius: 2px;
+    margin-right: 2px;
+  }
+
+  .tag-red {
+    display: inline-block;
+    padding: 0px 4px;
+    font-size: 12px;
+    color: #ff4c4c;
+    background: #ffebeb;
+    border-radius: 2px;
+    margin-right: 4px;
+  }
+
+  .expandedRow {
+    background-color: #f8f8f8;
+  }
+
+  .addSubBtn {
+    margin-left: 8px;
+    color: #1677ff;
+    cursor: pointer;
+  }
+
+  .tipText {
+    color: #999;
+    font-size: 12px;
+    margin-top: 4px;
+  }
+}
+
+// 标准的TableSelecter样式
+.TableSelecter {
+  .pfm-ant-modal-footer {
+    display: none !important;
+  }
+
+  .footer {
+    display: flex;
+    flex-direction: row;
+    justify-content: flex-end;
+    margin-top: 15px;
+
+    span {
+      display: inline-block;
+      width: 56px;
+      height: 24px;
+      font-size: 14px;
+      line-height: 23px;
+      text-align: center;
+      border-radius: 4px;
+      cursor: pointer;
+
+      &.ok {
+        color: #ffffff;
+        background: #3377ff;
+        margin-left: 8px;
+      }
+
+      &.cancel {
+        border: 1px solid #dae2f2;
+      }
+    }
+  }
+}

+ 2740 - 0
src/pages/checkItemMana/index.tsx

@@ -0,0 +1,2740 @@
+/*
+ * @Author: code4eat awesomedema@gmail.com
+ * @Date: 2024-01-01 10:00:00
+ * @LastEditors: code4eat awesomedema@gmail.com
+ * @LastEditTime: 2025-07-25 15:25:35
+ * @FilePath: /pfmBackMana/src/pages/checkItemMana/index.tsx
+ * @Description: 查核项管理页面
+ */
+
+import { createFromIconfontCN } from '@ant-design/icons';
+import {
+  ActionType,
+  ProFormInstance,
+  ModalForm,
+  ProFormDigit,
+  ProFormText,
+  ProFormTextArea,
+  ProFormSelect,
+  ProFormRadio,
+  ProColumns,
+  ProFormUploadButton,
+} from '@ant-design/pro-components';
+import {
+  Input,
+  message,
+  Popconfirm,
+  Tree,
+  Button,
+  Dropdown,
+  Menu,
+  Tooltip,
+  Modal,
+  Tabs,
+  Card,
+  Radio,
+  Checkbox,
+  Pagination,
+} from 'antd';
+import { useEffect, useRef, useState } from 'react';
+import { SearchOutlined, MoreOutlined, PlusOutlined } from '@ant-design/icons';
+
+const { TabPane } = Tabs;
+
+import {
+  addData,
+  delData,
+  editData,
+  getData,
+  getDefectData,
+  addDefectData,
+  editDefectData,
+  delDefectData,
+  getCheckResultData,
+  addCheckResultData,
+  editCheckResultData,
+  delCheckResultData,
+  getCheckItemTree,
+  getCheckItemModeList,
+  copyCheckItemResult,
+  getCheckPointData,
+  addCheckPointData,
+  editCheckPointData,
+  delCheckPointData,
+  getItemPendingPageCheckpoint,
+  addCheckItemCheckpoint,
+} from './service';
+import './style.less';
+
+import KCIMPagecontainer from '@/components/KCIMPageContainer';
+import { KCIMTable } from '@/components/KCIMTable';
+import baobiaotubiaoIcon from '@/assets/images/icons/baobiaotubiao.png';
+import leixingIcon from '@/assets/images/icons/leixing.png';
+import fenzhiIcon from '@/assets/images/icons/fenzhi.png';
+import chaheIcon from '@/assets/images/icons/chahe.png';
+import tongjiIcon from '@/assets/images/icons/tongji.png';
+import guanbiIcon from '@/assets/images/icons/guanbi.png';
+import shangyiyeIcon from '@/assets/images/icons/shangyiye.png';
+import xiayiyeIcon from '@/assets/images/icons/xiayiye.png';
+
+const { Search } = Input;
+
+const IconFont = createFromIconfontCN({
+  scriptUrl: '',
+});
+
+type TableDataResponse = {
+  data: any[];
+  success: boolean;
+  total: number;
+  pageSize: number;
+  totalPage: number;
+};
+
+// 模拟树形数据
+const mockTreeData = [
+  {
+    key: '1',
+    title: '地面干净无水迹',
+    children: [
+      { key: '1-1', title: '科内无专管' },
+      { key: '1-2', title: '科内不知晓专管员' },
+      { key: '1-3', title: '麻醉药品未放入保险柜' },
+      { key: '1-4', title: '使用PDA时扫描帐后未查看PDA各项信息是否准确' },
+      {
+        key: '1-5',
+        title: '对无法有效沟通的患者,未让陪同人员除述患者姓名或未查看腕带',
+      },
+    ],
+  },
+  {
+    key: '2',
+    title: '防火门关情况',
+    children: [
+      { key: '2-1', title: '防火门未正常关闭' },
+      { key: '2-2', title: '防火门被锁死' },
+    ],
+  },
+  {
+    key: '3',
+    title: '防烟面具、指挥棒、任意门其他应急物品情况',
+  },
+  {
+    key: '4',
+    title: '疏散指示灯情况',
+  },
+  {
+    key: '5',
+    title: '科室消防安全自查表',
+  },
+  {
+    key: '6',
+    title: '消防情况',
+  },
+];
+
+// 模拟表格数据
+const mockTableData = [
+  {
+    id: 1,
+    name: '科内无专管',
+    score: 2.0,
+    description: '科室内未配置专门管理人员',
+  },
+  {
+    id: 2,
+    name: '科内不知晓专管员',
+    score: 1.5,
+    description: '科室人员不知道专管员是谁',
+  },
+  {
+    id: 3,
+    name: '麻醉药品未放入保险柜',
+    score: 1.0,
+    description: '麻醉药品未按规定放入保险柜',
+  },
+  {
+    id: 4,
+    name: '使用PDA时扫描帐后未查看PDA各项信息是否准确',
+    score: 0.5,
+    description: '使用PDA时未核实信息准确性',
+  },
+  {
+    id: 5,
+    name: '对无法有效沟通的患者,未让陪同人员除述患者姓名或未查看腕带',
+    score: 1.0,
+    description: '对沟通困难患者未按规定核实身份',
+  },
+];
+
+export default function CheckItemMana() {
+  const [tableDataSearchKeywords, set_tableDataSearchKeywords] =
+    useState<string>('');
+  const [checkResultSearchKeywords, set_checkResultSearchKeywords] =
+    useState<string>(''); // 查核结果搜索关键字
+  const [checkPointSearchKeywords, set_checkPointSearchKeywords] =
+    useState<string>(''); // 查核要点搜索关键字
+  const [selectedTreeNode, set_selectedTreeNode] = useState<any>(null);
+  const [treeData, set_treeData] = useState<any[]>(mockTreeData);
+  const [leftSideCheckItems, set_leftSideCheckItems] = useState<any[]>([]); // 左侧查核项列表数据
+  const [leftSideLoading, set_leftSideLoading] = useState<boolean>(false); // 左侧列表加载状态
+  const [leftSideHasMore, set_leftSideHasMore] = useState<boolean>(true); // 是否还有更多数据
+  const [leftSideCurrentPage, set_leftSideCurrentPage] = useState<number>(1); // 当前页码
+  const [expandedKeys, set_expandedKeys] = useState<(string | number)[]>([
+    '1',
+    '2',
+  ]);
+  const [checkItemSearchKeywords, set_checkItemSearchKeywords] =
+    useState<string>('');
+  const [hoveredNode, set_hoveredNode] = useState<string | null>(null);
+  const [openDropdownNode, set_openDropdownNode] = useState<string | null>(
+    null,
+  );
+  const [activeTabKey, set_activeTabKey] = useState<string>('defects'); // 当前激活的tab
+  const [checkModeOptions, set_checkModeOptions] = useState<any[]>([]);
+  const [addCheckItemVisible, set_addCheckItemVisible] =
+    useState<boolean>(false); // 新增查核项弹窗显示状态
+  const [editCheckItemVisible, set_editCheckItemVisible] =
+    useState<boolean>(false); // 编辑查核项弹窗显示状态
+  const [currentEditItem, set_currentEditItem] = useState<any>(null); // 当前编辑的查核项
+  const [copySettingVisible, set_copySettingVisible] = useState<boolean>(false); // 复制设置弹窗显示状态
+  const [selectedCopyItems, set_selectedCopyItems] = useState<string[]>([]); // 选中的复制项(多选)
+  const [copySearchKeywords, set_copySearchKeywords] = useState<string>(''); // 复制设置搜索关键字
+  const [addCheckPointVisible, set_addCheckPointVisible] = useState<boolean>(false); // 添加查核要点弹窗显示状态
+  const [pendingCheckPoints, set_pendingCheckPoints] = useState<any[]>([]); // 待添加的查核要点列表
+  const [selectedCheckPoints, set_selectedCheckPoints] = useState<string[]>([]); // 选中的查核要点ID列表
+  const [addCheckPointSearchKeywords, set_addCheckPointSearchKeywords] = useState<string>(''); // 添加查核要点搜索关键字
+  const [selectedCheckPointTableKeys, set_selectedCheckPointTableKeys] = useState<string[]>([]); // 查核要点表格选中的行
+  const [actionBarStyle, set_actionBarStyle] = useState<{
+    left: number;
+    width: number;
+  }>({ left: 0, width: 0 }); // 批量操作栏定位样式
+  const [imagePreviewVisible, set_imagePreviewVisible] = useState<boolean>(false); // 图片预览弹窗显示状态
+  const [imagePreviewIndex, set_imagePreviewIndex] = useState<number>(0); // 当前预览图片索引
+  const isInitializedRef = useRef<boolean>(false); // 防止重复初始化
+  const tableRef = useRef<ActionType>(); // 缺陷项表格引用
+  const checkResultTableRef = useRef<ActionType>(); // 查核结果表格引用
+  const checkPointTableRef = useRef<ActionType>(); // 查核要点表格引用
+  const copySettingTableRef = useRef<ActionType>(); // 复制设置表格引用
+  const modalFormRef = useRef<ProFormInstance>();
+  const addCheckItemFormRef = useRef<ProFormInstance>(); // 新增查核项表单引用
+  const leftSideListRef = useRef<HTMLDivElement>(null); // 左侧列表容器引用
+  const mainContentRef = useRef<HTMLDivElement>(null); // 右侧主内容区域引用
+  const [copySettingTableData, set_copySettingTableData] = useState<any[]>([]); // 复制设置表格数据
+
+  // 更新操作栏定位
+  const updateActionBarPosition = () => {
+    if (mainContentRef.current) {
+      const rect = mainContentRef.current.getBoundingClientRect();
+      set_actionBarStyle({ left: rect.left, width: rect.width });
+    }
+  };
+
+  // 初始化操作栏定位
+  useEffect(() => {
+    updateActionBarPosition();
+    window.addEventListener('resize', updateActionBarPosition);
+    window.addEventListener('scroll', updateActionBarPosition, true);
+    let resizeObserver: ResizeObserver | null = null;
+    if (mainContentRef.current && typeof ResizeObserver !== 'undefined') {
+      resizeObserver = new ResizeObserver(() => {
+        updateActionBarPosition();
+      });
+      resizeObserver.observe(mainContentRef.current);
+    }
+    return () => {
+      window.removeEventListener('resize', updateActionBarPosition);
+      window.removeEventListener('scroll', updateActionBarPosition, true);
+      if (resizeObserver && mainContentRef.current) {
+        resizeObserver.unobserve(mainContentRef.current);
+        resizeObserver.disconnect();
+      }
+    };
+  }, []);
+
+  // 初始化加载数据
+  useEffect(() => {
+    const initializeData = async () => {
+      // 防止重复初始化
+      if (isInitializedRef.current) {
+        return;
+      }
+      isInitializedRef.current = true;
+
+      try {
+        // 加载左侧查核项列表(第一页)
+        await loadMoreLeftSideItems(1, false);
+      } catch (error) {
+        console.error('初始化数据失败:', error);
+        // 如果初始化失败,重置标记,允许重试
+        isInitializedRef.current = false;
+      }
+    };
+    initializeData();
+  }, []);
+
+  // 监听选中项变化,刷新表格数据
+  useEffect(() => {
+    if (selectedTreeNode?.key) {
+      // 根据当前激活的tab决定刷新哪个表格
+      if (activeTabKey === 'defects' && tableRef.current) {
+        tableRef.current.reload();
+      } else if (activeTabKey === 'results' && checkResultTableRef.current) {
+        checkResultTableRef.current.reload();
+      } else if (activeTabKey === 'points' && checkPointTableRef.current) {
+        checkPointTableRef.current.reload();
+      }
+    }
+  }, [selectedTreeNode?.key, activeTabKey]);
+
+  // 监听左侧列表滚动事件
+  useEffect(() => {
+    const handleScroll = () => {
+      if (!leftSideListRef.current || leftSideLoading || !leftSideHasMore)
+        return;
+
+      const { scrollTop, scrollHeight, clientHeight } = leftSideListRef.current;
+      // 当滚动到距离底部50px时开始加载更多
+      if (scrollHeight - scrollTop - clientHeight < 50) {
+        loadMoreLeftSideItems(leftSideCurrentPage + 1, true);
+      }
+    };
+
+    const listElement = leftSideListRef.current;
+    if (listElement) {
+      listElement.addEventListener('scroll', handleScroll);
+      return () => {
+        listElement.removeEventListener('scroll', handleScroll);
+      };
+    }
+  }, [leftSideLoading, leftSideHasMore, leftSideCurrentPage]);
+
+  // 加载更多左侧查核项数据
+  const loadMoreLeftSideItems = async (
+    page: number = 1,
+    isAppend: boolean = false,
+  ) => {
+    if (leftSideLoading) return; // 防止重复加载
+
+    set_leftSideLoading(true);
+    try {
+      const response = await getData({
+        current: page,
+        pageSize: 30,
+        filter: checkItemSearchKeywords || '',
+      });
+
+      if (response && response.list) {
+        if (isAppend) {
+          // 追加数据
+          set_leftSideCheckItems((prev) => [...prev, ...response.list]);
+        } else {
+          // 替换数据(初始加载或搜索)
+          set_leftSideCheckItems(response.list);
+          // 如果有数据,总是设置第一个为默认选中
+          if (response.list.length > 0) {
+            set_selectedTreeNode({
+              key: response.list[0].id,
+              title: response.list[0].name,
+              ...response.list[0],
+            });
+          } else {
+            // 如果没有数据,清空选中项
+            set_selectedTreeNode(null);
+          }
+        }
+
+        // 判断是否还有更多数据
+        const hasMore =
+          response.list.length === 30 && page < response.totalPage;
+        set_leftSideHasMore(hasMore);
+        set_leftSideCurrentPage(page);
+      }
+    } catch (error) {
+      console.error('加载左侧列表数据失败:', error);
+    } finally {
+      set_leftSideLoading(false);
+    }
+  };
+
+  // 缺陷项表格列定义
+  const defectColumns: ProColumns[] = [
+    {
+      title: '缺陷项',
+      dataIndex: 'resultName',
+      width: 600,
+    },
+    {
+      title: '分值',
+      dataIndex: 'percentScore',
+      width: 120,
+      align: 'center',
+      renderText: (text) => `${text}分`,
+    },
+    {
+      title: '操作',
+      key: 'option',
+      width: 120,
+      align: 'center',
+      valueType: 'option',
+      render: (_: any, record: any) => {
+        return [
+          <UpDataActBtn key={'edit'} record={record} type="EDIT" />,
+          <Popconfirm
+            key={'del'}
+            title="确定要删除这个缺陷项吗?"
+            onConfirm={() => handleDelete(record)}
+            okText="确定"
+            cancelText="取消"
+          >
+            <a style={{ color: '#ff4d4f' }}>删除</a>
+          </Popconfirm>,
+        ];
+      },
+    },
+  ];
+
+  // 查核结果表格列定义
+  const checkResultColumns: ProColumns[] = [
+    {
+      title: '查核结果',
+      dataIndex: 'name',
+      width: 300,
+    },
+    {
+      title: '分值',
+      dataIndex: 'score',
+      width: 120,
+      align: 'center',
+      render: (text, record) => {
+        if (record.scoreType === 1) {
+          // 占比类型:将小数转换为百分比显示,颜色为绿色
+          const percentage = (parseFloat(String(text || 0)) * 100).toFixed(1);
+          return <span style={{ color: '#00BF8F' }}>{percentage}%</span>;
+        } else {
+          // 数值类型:显示分数
+          return <span>{String(text || 0)}分</span>;
+        }
+      },
+    },
+    {
+      title: '默认项',
+      dataIndex: 'defaultFlag',
+      width: 80,
+      align: 'center',
+      render: (_: any, record: any) => {
+        return (
+          <Radio
+            checked={record.defaultFlag === 1}
+            onChange={() => handleDefaultFlagChange(record)}
+            style={{ cursor: 'pointer' }}
+          />
+        );
+      },
+    },
+    {
+      title: '快捷项',
+      dataIndex: 'fastFlag',
+      width: 80,
+      align: 'center',
+      render: (_: any, record: any) => {
+        return (
+          <Radio
+            checked={record.fastFlag === 1}
+            onChange={() => handleFastFlagChange(record)}
+            style={{ cursor: 'pointer' }}
+          />
+        );
+      },
+    },
+    {
+      title: '操作',
+      key: 'option',
+      width: 120,
+      align: 'center',
+      valueType: 'option',
+      render: (_: any, record: any) => {
+        return [
+          <CheckResultUpDataActBtn key={'edit'} record={record} type="EDIT" />,
+          <Popconfirm
+            key={'del'}
+            title="确定要删除这个查核结果吗?"
+            onConfirm={() => handleCheckResultDelete(record)}
+            okText="确定"
+            cancelText="取消"
+          >
+            <a style={{ color: '#ff4d4f' }}>删除</a>
+          </Popconfirm>,
+        ];
+      },
+    },
+  ];
+
+  // 查核要点表格列定义
+  const checkPointColumns: ProColumns[] = [
+    {
+      title: '查核要点ID',
+      dataIndex: 'checkPointId',
+      width: 150,
+      align: 'center',
+    },
+    {
+      title: '查核要点名称',
+      dataIndex: 'checkPointName',
+      ellipsis: true,
+    },
+    {
+      title: '操作',
+      key: 'option',
+      width: 80,
+      align: 'center',
+      valueType: 'option',
+      render: (_: any, record: any) => {
+        return [
+          <Popconfirm
+            key={'del'}
+            title="确定要删除这个查核要点吗?"
+            onConfirm={() => handleCheckPointDelete(record)}
+            okText="确定"
+            cancelText="取消"
+          >
+            <a style={{ color: '#ff4d4f' }}>删除</a>
+          </Popconfirm>,
+        ];
+      },
+    },
+  ];
+
+  // 获取表格数据 - 根据选中的查核项获取其缺陷项列表
+  const getTableData = async (
+    params: any,
+  ): Promise<TableDataResponse | any[]> => {
+    // 如果没有选中的查核项,返回空数据
+    if (!selectedTreeNode?.key) {
+      return {
+        data: [],
+        success: true,
+        total: 0,
+        pageSize: 10,
+        totalPage: 0,
+      };
+    }
+
+    try {
+      // 获取所有数据,不传递filter参数
+      const response = await getDefectData({
+        checkItemId: selectedTreeNode.key, // 查核项ID
+      });
+
+      if (response && Array.isArray(response)) {
+        // 在前端进行本地过滤
+        let filteredData = response;
+        if (tableDataSearchKeywords && tableDataSearchKeywords.trim()) {
+          filteredData = response.filter((item) =>
+            item.resultName &&
+            item.resultName.toLowerCase().includes(tableDataSearchKeywords.toLowerCase())
+          );
+        }
+
+        // 手动处理分页
+        const current = params.current || 1;
+        const pageSize = params.pageSize || 10;
+        const start = (current - 1) * pageSize;
+        const end = start + pageSize;
+        const paginatedData = filteredData.slice(start, end);
+
+        return {
+          data: paginatedData,
+          success: true,
+          total: filteredData.length,
+          pageSize: pageSize,
+          totalPage: Math.ceil(filteredData.length / pageSize),
+        };
+      }
+    } catch (error) {
+      console.error('获取缺陷项数据失败:', error);
+    }
+
+    return {
+      data: [],
+      success: false,
+      total: 0,
+      pageSize: 10,
+      totalPage: 0,
+    };
+  };
+
+  // 删除缺陷项处理
+  const handleDelete = async (record: any) => {
+    try {
+      const response = await delDefectData([record.id]);
+      if (response) {
+        message.success('删除缺陷项成功!');
+        tableRef.current?.reload();
+      }
+    } catch (error) {
+      console.error('删除缺陷项失败:', error);
+      message.error('删除缺陷项失败!');
+    }
+  };
+
+  // 获取查核结果数据
+  const getCheckResultTableData = async (
+    params: any,
+  ): Promise<TableDataResponse | any[]> => {
+    // 如果没有选中的查核项,返回空数据
+    if (!selectedTreeNode?.key) {
+      return {
+        data: [],
+        success: true,
+        total: 0,
+        pageSize: 10,
+        totalPage: 0,
+      };
+    }
+
+    try {
+      // 获取所有数据,不传递filter参数
+      const response = await getCheckResultData({
+        checkItemId: selectedTreeNode.key, // 查核项ID
+      });
+
+      if (response && Array.isArray(response)) {
+        // 在前端进行本地过滤
+        let filteredData = response;
+        if (checkResultSearchKeywords && checkResultSearchKeywords.trim()) {
+          filteredData = response.filter((item) =>
+            item.name &&
+            item.name.toLowerCase().includes(checkResultSearchKeywords.toLowerCase())
+          );
+        }
+
+        // 手动处理分页
+        const current = params.current || 1;
+        const pageSize = params.pageSize || 10;
+        const start = (current - 1) * pageSize;
+        const end = start + pageSize;
+        const paginatedData = filteredData.slice(start, end);
+
+        return {
+          data: paginatedData,
+          success: true,
+          total: filteredData.length,
+          pageSize: pageSize,
+          totalPage: Math.ceil(filteredData.length / pageSize),
+        };
+      }
+    } catch (error) {
+      console.error('获取查核结果数据失败:', error);
+    }
+
+    return {
+      data: [],
+      success: false,
+      total: 0,
+      pageSize: 10,
+      totalPage: 0,
+    };
+  };
+
+  // 删除查核结果处理
+  const handleCheckResultDelete = async (record: any) => {
+    try {
+      const response = await delCheckResultData([record.id]);
+      if (response) {
+        message.success('删除查核结果成功!');
+        checkResultTableRef.current?.reload();
+      }
+    } catch (error) {
+      console.error('删除查核结果失败:', error);
+      message.error('删除查核结果失败!');
+    }
+  };
+
+  // 处理默认项变更
+  const handleDefaultFlagChange = async (record: any) => {
+    try {
+      const response = await editCheckResultData({
+        ...record,
+        defaultFlag: record.defaultFlag === 1 ? 0 : 1,
+        checkItemId: selectedTreeNode.key,
+      });
+      if (response) {
+        message.success('默认项设置成功!');
+        checkResultTableRef.current?.reload();
+      }
+    } catch (error) {
+      console.error('设置默认项失败:', error);
+      message.error('设置默认项失败!');
+    }
+  };
+
+  // 处理快捷项变更
+  const handleFastFlagChange = async (record: any) => {
+    try {
+      const response = await editCheckResultData({
+        ...record,
+        fastFlag: record.fastFlag === 1 ? 0 : 1,
+        checkItemId: selectedTreeNode.key,
+      });
+      if (response) {
+        message.success('快捷项设置成功!');
+        checkResultTableRef.current?.reload();
+      }
+    } catch (error) {
+      console.error('设置快捷项失败:', error);
+      message.error('设置快捷项失败!');
+    }
+  };
+
+  // 新增查核项处理
+  const handleAddCheckItem = async () => {
+    console.log('点击新增查核项按钮');
+
+    // 懒加载查核方式选项
+    if (checkModeOptions.length === 0) {
+      try {
+        const checkModeResponse = await getCheckItemModeList();
+        if (checkModeResponse) {
+          const options = checkModeResponse.map((item) => ({
+            label: item.itemName,
+            value: item.itemCode,
+          }));
+          set_checkModeOptions(options);
+        }
+      } catch (error) {
+        console.error('加载查核方式选项失败:', error);
+        message.error('加载查核方式选项失败!');
+        return;
+      }
+    }
+
+    set_addCheckItemVisible(true);
+  };
+
+  // 新增查核项提交处理
+  const handleAddCheckItemSubmit = async (values: any) => {
+    try {
+      console.log('提交新增查核项数据:', values);
+
+      // 处理图片上传
+      let rightAnswerImage = '';
+      if (values.rightAnswerImage && Array.isArray(values.rightAnswerImage) && values.rightAnswerImage.length > 0) {
+        try {
+          const uploadedUrls = await uploadFiles(values.rightAnswerImage);
+          rightAnswerImage = uploadedUrls.join(',');
+          console.log('图片上传成功,URLs:', uploadedUrls);
+        } catch (uploadError) {
+          console.error('图片上传失败:', uploadError);
+          message.error('图片上传失败,请重试!');
+          return false;
+        }
+      }
+
+      const submitData = {
+        ...values,
+        rightAnswerImage,
+      };
+
+      console.log('最终提交数据:', submitData);
+
+      const response = await addData(submitData);
+      if (response) {
+        message.success('新增查核项成功!');
+        set_addCheckItemVisible(false);
+        // 重新加载左侧列表数据
+        set_leftSideCurrentPage(1);
+        set_leftSideHasMore(true);
+        await loadMoreLeftSideItems(1, false);
+        return true;
+      }
+      return false;
+    } catch (error) {
+      console.error('新增查核项失败:', error);
+      message.error('新增查核项失败!');
+      return false;
+    }
+  };
+
+  // 更新表格数据 - 缺陷项的新增和编辑
+  const updateTable = async (formVal: any, type: 'EDIT' | 'ADD') => {
+    try {
+      // 如果没有选中的查核项,不能进行操作
+      if (!selectedTreeNode?.key) {
+        message.error('请先选择查核项!');
+        return false;
+      }
+
+      const submitData = {
+        ...formVal,
+        checkItemId: selectedTreeNode.key,
+      };
+
+      let response;
+      if (type === 'ADD') {
+        response = await addDefectData(submitData);
+      } else {
+        response = await editDefectData(submitData);
+      }
+
+      if (response) {
+        message.success(`${type === 'ADD' ? '新增' : '编辑'}缺陷项成功!`);
+        tableRef.current?.reload();
+        return true;
+      }
+      return false;
+    } catch (error) {
+      console.error(`${type === 'ADD' ? '新增' : '编辑'}缺陷项失败:`, error);
+      message.error(`${type === 'ADD' ? '新增' : '编辑'}缺陷项失败!`);
+      return false;
+    }
+  };
+
+  // 更新查核结果数据 - 查核结果的新增和编辑
+  const updateCheckResultTable = async (formVal: any, type: 'EDIT' | 'ADD') => {
+    try {
+      // 如果没有选中的查核项,不能进行操作
+      if (!selectedTreeNode?.key) {
+        message.error('请先选择查核项!');
+        return false;
+      }
+
+      const submitData = {
+        ...formVal,
+        checkItemId: selectedTreeNode.key,
+      };
+
+      let response;
+      if (type === 'ADD') {
+        response = await addCheckResultData(submitData);
+      } else {
+        response = await editCheckResultData(submitData);
+      }
+
+      if (response) {
+        message.success(`${type === 'ADD' ? '新增' : '编辑'}查核结果成功!`);
+        checkResultTableRef.current?.reload();
+        return true;
+      }
+      return false;
+    } catch (error) {
+      console.error(`${type === 'ADD' ? '新增' : '编辑'}查核结果失败:`, error);
+      message.error(`${type === 'ADD' ? '新增' : '编辑'}查核结果失败!`);
+      return false;
+    }
+  };
+
+  // 新增/编辑弹窗组件
+  const UpDataActBtn = ({
+    record,
+    type,
+  }: {
+    record: any;
+    type: 'EDIT' | 'ADD';
+  }) => {
+   
+
+    return (
+      <ModalForm
+        title={`${type == 'EDIT' ? '编辑' : '新增'}缺陷项`}
+        width={352}
+        initialValues={
+          type == 'EDIT'
+            ? {
+              resultName: record.resultName,
+              percentScore: record.percentScore,
+            }
+            : {
+              resultName: '',
+              percentScore: 0,
+            }
+        }
+        trigger={
+          type == 'EDIT' ? (
+            <a key="edit" style={{ color: '#1890ff' }}>
+              编辑
+            </a>
+          ) : (
+            <Button type="primary" className="add-btn" size="small">
+              新增
+            </Button>
+          )
+        }
+        formRef={modalFormRef}
+        onFinish={async (val) => {
+          console.log('缺陷项表单提交:', val);
+          // 编辑时需要传递ID
+          if (type === 'EDIT') {
+            val.id = record.id;
+          }
+          return updateTable(val, type);
+        }}
+        modalProps={{
+          destroyOnClose: true,
+          onCancel: () => console.log('缺陷项表单取消'),
+        }}
+        onVisibleChange={(visible) => {
+          console.log('缺陷项表单可见性变化:', visible);
+        }}
+      >
+        <ProFormTextArea
+          name="resultName"
+          label="缺陷项名称:"
+          placeholder="请输入缺陷项名称"
+          fieldProps={{
+            rows: 3,
+            maxLength: 200,
+            showCount: true,
+          }}
+          rules={[{ required: true, message: '请输入缺陷项名称' }]}
+        />
+
+        <ProFormDigit
+          name="percentScore"
+          label="分值:"
+          placeholder="0"
+          min={0}
+          max={100}
+          fieldProps={{ precision: 1 }}
+          rules={[{ required: true, message: '请输入分值' }]}
+        />
+      </ModalForm>
+    );
+  };
+
+  // 查核结果新增/编辑弹窗组件
+  const CheckResultUpDataActBtn = ({
+    record,
+    type,
+  }: {
+    record: any;
+    type: 'EDIT' | 'ADD';
+  }) => {
+    console.log('CheckResultUpDataActBtn 渲染:', { type, record });
+
+    return (
+      <ModalForm
+        title={`${type == 'EDIT' ? '编辑' : '新增'}查核结果`}
+        width={352}
+        initialValues={
+          type == 'EDIT'
+            ? {
+              name: record.name,
+              score: record.score,
+              scoreType: record.scoreType,
+            }
+            : {
+              name: '',
+              score: 0,
+              scoreType: 0,
+            }
+        }
+        trigger={
+          type == 'EDIT' ? (
+            <a key="edit" style={{ color: '#1890ff' }}>
+              编辑
+            </a>
+          ) : (
+            <Button type="primary" className="add-btn" size="small">
+              新增
+            </Button>
+          )
+        }
+        formRef={modalFormRef}
+        onFinish={async (val) => {
+          console.log('查核结果表单提交:', val);
+          // 编辑时需要传递ID,新增时设置默认值
+          if (type === 'EDIT') {
+            val.id = record.id;
+            // 编辑时保持原有的默认项和快捷项状态
+            val.defaultFlag = record.defaultFlag;
+            val.fastFlag = record.fastFlag;
+          } else {
+            // 新增时设置默认值
+            val.defaultFlag = 0;
+            val.fastFlag = 0;
+          }
+          return updateCheckResultTable(val, type);
+        }}
+        modalProps={{
+          destroyOnClose: true,
+          onCancel: () => console.log('查核结果表单取消'),
+        }}
+        onVisibleChange={(visible) => {
+          console.log('查核结果表单可见性变化:', visible);
+        }}
+      >
+        <ProFormText
+          name="name"
+          label="查核结果:"
+          placeholder="请输入"
+          rules={[{ required: true, message: '请输入查核结果' }]}
+        />
+
+        <ProFormRadio.Group
+          name="scoreType"
+          label="分值类型:"
+          options={[
+            { label: '数值', value: 0 },
+            { label: '占比', value: 1 },
+          ]}
+          rules={[{ required: true, message: '请选择分值类型' }]}
+        />
+
+        <ProFormDigit
+          name="score"
+          label="分值数值:"
+          placeholder="0"
+          max={100}
+          fieldProps={{ precision: 1, min: -100000 }}
+          rules={[{ required: true, message: '请输入分值' }]}
+        />
+      </ModalForm>
+    );
+  };
+
+
+
+  // 树节点选择
+  const onTreeSelect = (selectedKeys: any[], info: any) => {
+    if (selectedKeys.length > 0) {
+      set_selectedTreeNode(info.node);
+    }
+  };
+
+  // 搜索处理
+  const tableDataSearchHandle = (keywords: string) => {
+    set_tableDataSearchKeywords(keywords);
+  };
+
+  // 搜索处理 - 重置列表并重新加载
+  const handleCheckItemSearch = async () => {
+    set_leftSideCurrentPage(1);
+    set_leftSideHasMore(true);
+    await loadMoreLeftSideItems(1, false);
+  };
+
+  // 搜索框内容变化处理
+  const handleSearchInputChange = async (
+    e: React.ChangeEvent<HTMLInputElement>,
+  ) => {
+    const value = e.target.value;
+    set_checkItemSearchKeywords(value);
+
+    // 如果搜索框被清空,自动重新加载数据
+    if (value === '') {
+      set_leftSideCurrentPage(1);
+      set_leftSideHasMore(true);
+      // 直接调用接口,传递空字符串作为filter参数
+      try {
+        set_leftSideLoading(true);
+        const response = await getData({
+          current: 1,
+          pageSize: 30,
+          filter: '', // 直接传递空字符串
+        });
+
+        if (response && response.list) {
+          set_leftSideCheckItems(response.list);
+          // 如果有数据,总是设置第一个为默认选中
+          if (response.list.length > 0) {
+            set_selectedTreeNode({
+              key: response.list[0].id,
+              title: response.list[0].name,
+              ...response.list[0],
+            });
+          } else {
+            // 如果没有数据,清空选中项
+            set_selectedTreeNode(null);
+          }
+
+          // 判断是否还有更多数据
+          const hasMore = response.list.length === 30 && 1 < response.totalPage;
+          set_leftSideHasMore(hasMore);
+          set_leftSideCurrentPage(1);
+        }
+      } catch (error) {
+        console.error('加载左侧列表数据失败:', error);
+      } finally {
+        set_leftSideLoading(false);
+      }
+    }
+  };
+
+  // 删除查核项处理
+  const handleDeleteCheckItem = async (item: any) => {
+    console.log('点击删除查核项按钮', item);
+    try {
+      const response = await delData([item.id]);
+      if (response) {
+        message.success('删除查核项成功!');
+        // 重新加载左侧列表数据
+        set_leftSideCurrentPage(1);
+        set_leftSideHasMore(true);
+        await loadMoreLeftSideItems(1, false);
+        // 如果删除的是当前选中项,清空选中状态
+        if (selectedTreeNode?.key === item.id) {
+          set_selectedTreeNode(null);
+        }
+      }
+    } catch (error) {
+      console.error('删除查核项失败:', error);
+      message.error('删除查核项失败!');
+    }
+  };
+
+  // 获取更多菜单
+  const getMoreMenu = (node: any) => (
+    <Menu
+      items={[
+        {
+          key: 'edit',
+          label: '编辑',
+          onClick: () => {
+            handleEditCheckItem(node);
+          },
+        },
+        {
+          key: 'delete',
+          label: '删除',
+          onClick: (e) => {
+            e.domEvent.stopPropagation();
+          },
+        },
+      ]}
+      onClick={({ key, domEvent }) => {
+        if (key === 'delete') {
+          domEvent.stopPropagation();
+          // 阻止菜单关闭,显示确认对话框
+          setTimeout(() => {
+            Modal.confirm({
+              title: '删除确认',
+              content: `确定要删除查核项"${node.name}"吗?`,
+              okText: '确定',
+              cancelText: '取消',
+              onOk: () => handleDeleteCheckItem(node),
+            });
+          }, 100);
+        }
+      }}
+    />
+  );
+
+  // 渲染左侧查核项分类列表
+  const renderCheckItemList = () => {
+    return (
+      <div className="item-tree">
+        <div className="search-wrapper">
+          <Input
+            placeholder="查核项"
+            suffix={
+              <SearchOutlined
+                style={{ color: '#99A6BF', cursor: 'pointer' }}
+                onClick={handleCheckItemSearch}
+              />
+            }
+            className="search-input"
+            value={checkItemSearchKeywords}
+            onChange={handleSearchInputChange}
+            onPressEnter={handleCheckItemSearch}
+            allowClear
+          />
+          <Tooltip title="新增查核项">
+            <Button
+              className="add-button"
+              icon={<PlusOutlined style={{ fontSize: 12 }} />}
+              onClick={handleAddCheckItem}
+            />
+          </Tooltip>
+        </div>
+
+        <div className="item-list" ref={leftSideListRef}>
+          {leftSideCheckItems.map((item) => (
+            <div key={item.id} className="item-group">
+              <div
+                className={`item-leaf ${selectedTreeNode?.key === item.id ? 'selected' : ''
+                  }`}
+                onClick={() => {
+                  set_selectedTreeNode({
+                    key: item.id,
+                    title: item.name,
+                    ...item,
+                  });
+                }}
+                onMouseEnter={() => set_hoveredNode(item.id)}
+                onMouseLeave={() => set_hoveredNode(null)}
+              >
+                <div className="item-icon">
+                  <img src={baobiaotubiaoIcon} alt="查核项图标" />
+                </div>
+                <div className="item-content">
+                  <span className="item-title" title={item.name}>
+                    {item.name.length > 8 ? `${item.name.substring(0, 8)}...` : item.name}
+                  </span>
+                  <span className="item-score">分值:{item.score || 0}分</span>
+                </div>
+                {(selectedTreeNode?.key === item.id ||
+                  hoveredNode === item.id ||
+                  openDropdownNode === item.id) && (
+                    <Dropdown
+                      overlay={getMoreMenu(item)}
+                      trigger={['click']}
+                      placement="bottomRight"
+                      overlayClassName="check-item-dropdown"
+                      onVisibleChange={(visible) => {
+                        if (visible) {
+                          set_openDropdownNode(item.id);
+                        } else {
+                          set_openDropdownNode(null);
+                        }
+                      }}
+                    >
+                      <Button
+                        type="text"
+                        icon={<MoreOutlined />}
+                        className="more-button"
+                        onClick={(e) => e.stopPropagation()}
+                      />
+                    </Dropdown>
+                  )}
+              </div>
+            </div>
+          ))}
+
+          {/* 加载更多状态 */}
+          {leftSideLoading && (
+            <div
+              style={{
+                padding: '12px',
+                textAlign: 'center',
+                color: '#999',
+                fontSize: '12px',
+              }}
+            >
+              加载中...
+            </div>
+          )}
+
+          {/* 没有更多数据提示 */}
+          {!leftSideHasMore && leftSideCheckItems.length > 0 && (
+            <div
+              style={{
+                padding: '12px',
+                textAlign: 'center',
+                color: '#999',
+                fontSize: '12px',
+              }}
+            >
+              已加载全部数据
+            </div>
+          )}
+
+          {/* 暂无数据提示 */}
+          {!leftSideLoading && leftSideCheckItems.length === 0 && (
+            <div
+              style={{
+                padding: '20px',
+                textAlign: 'center',
+                color: '#999',
+                fontSize: '14px',
+              }}
+            >
+              暂无数据
+            </div>
+          )}
+        </div>
+      </div>
+    );
+  };
+
+  // 编辑查核项处理
+  const handleEditCheckItem = async (item: any) => {
+
+    // 懒加载查核方式选项
+    if (checkModeOptions.length === 0) {
+      try {
+        const checkModeResponse = await getCheckItemModeList();
+        if (checkModeResponse) {
+          const options = checkModeResponse.map((optionItem) => ({
+            label: optionItem.itemName,
+            value: optionItem.itemCode,
+          }));
+          set_checkModeOptions(options);
+        }
+      } catch (error) {
+        console.error('加载查核方式选项失败:', error);
+        message.error('加载查核方式选项失败!');
+        return;
+      }
+    }
+
+    set_currentEditItem(item);
+    set_editCheckItemVisible(true);
+  };
+
+  // 编辑查核项提交处理
+  const handleEditCheckItemSubmit = async (values: any) => {
+    try {
+      const submitData = { ...values, id: currentEditItem?.id };
+      const response = await editData(submitData);
+      if (response) {
+        message.success('编辑查核项成功!');
+        set_editCheckItemVisible(false);
+        set_currentEditItem(null);
+        set_leftSideCurrentPage(1);
+        set_leftSideHasMore(true);
+        await loadMoreLeftSideItems(1, false);
+        return true;
+      }
+      return false;
+    } catch (error) {
+      console.error('编辑查核项失败:', error);
+      message.error('编辑查核项失败!');
+      return false;
+    }
+  };
+
+  // tab切换处理函数
+  const handleTabChange = (tabKey: string) => {
+    set_activeTabKey(tabKey);
+    // 切换tab时,如果有选中的查核项,立即加载对应的数据
+    if (selectedTreeNode?.key) {
+      setTimeout(() => {
+        if (tabKey === 'defects' && tableRef.current) {
+          tableRef.current.reload();
+        } else if (tabKey === 'results' && checkResultTableRef.current) {
+          checkResultTableRef.current.reload();
+        } else if (tabKey === 'points' && checkPointTableRef.current) {
+          checkPointTableRef.current.reload();
+        }
+      }, 100); // 稍微延迟一下,确保tab切换完成
+    }
+  };
+
+  // 处理复制设置按钮点击
+  const handleCopySettingClick = async () => {
+    if (!selectedTreeNode?.key) {
+      message.error('请先选择查核项!');
+      return;
+    }
+
+    try {
+      // 初始化弹窗状态
+      set_selectedCopyItems([]);
+      set_copySearchKeywords('');
+      set_copySettingVisible(true);
+    } catch (error) {
+      console.error('打开复制设置弹窗失败:', error);
+      message.error('打开复制设置弹窗失败!');
+    }
+  };
+
+  // 处理复制设置确认
+  const handleCopySettingConfirm = async () => {
+    if (selectedCopyItems.length === 0) {
+      message.error('请选择要复制的查核项!');
+      return;
+    }
+    if (!selectedTreeNode?.key) {
+      message.error('请先选择源查核项!');
+      return;
+    }
+    try {
+      const response = await copyCheckItemResult({
+        sourceId: String(selectedTreeNode.key),
+        targetIdList: selectedCopyItems,
+      });
+      if (response) {
+        message.success('批量复制查核结果成功!');
+        set_copySettingVisible(false);
+        set_selectedCopyItems([]);
+        checkResultTableRef.current?.reload();
+      } else {
+        message.error(response?.msg || '批量复制查核结果失败!');
+      }
+    } catch (error) {
+      console.error('批量复制查核结果失败:', error);
+      message.error('批量复制查核结果失败!');
+    }
+  };
+
+  // 获取复制设置数据
+  const getCopySettingTableData = async (params: any): Promise<TableDataResponse | any[]> => {
+    if (!selectedTreeNode?.key) {
+      return {
+        data: [],
+        success: true,
+        total: 0,
+        pageSize: 100,
+        totalPage: 0,
+      };
+    }
+
+    try {
+      const response = await getData({
+        current: params.current || 1,
+        pageSize: params.pageSize || 100,
+        filter: copySearchKeywords || '',
+      });
+
+      if (response && response.list) {
+        // 过滤掉当前选中的查核项
+        const filteredItems = response.list.filter(
+          (item) => item.id !== selectedTreeNode.key,
+        );
+
+        return {
+          data: filteredItems,
+          success: true,
+          total: response.totalCount > 0 ? response.totalCount - 1 : 0, // 减去当前选中项
+          pageSize: params.pageSize || 100,
+          totalPage: response.totalPage,
+        };
+      }
+    } catch (error) {
+      console.error('获取复制设置数据失败:', error);
+    }
+
+    return {
+      data: [],
+      success: false,
+      total: 0,
+      pageSize: 100,
+      totalPage: 0,
+    };
+  };
+
+  // 处理复制设置搜索
+  const handleCopySearchChange = (e: React.ChangeEvent<HTMLInputElement>) => {
+    const value = e.target.value;
+    set_copySearchKeywords(value);
+
+    // 如果搜索框被清空,立即刷新表格
+    if (value === '') {
+      copySettingTableRef.current?.reload();
+    }
+  };
+
+  // 处理复制设置搜索确认
+  const handleCopySearchConfirm = () => {
+    // 刷新表格数据
+    copySettingTableRef.current?.reload();
+  };
+
+  // 获取查核要点数据
+  const getCheckPointTableData = async (
+    params: any,
+  ): Promise<TableDataResponse | any[]> => {
+    // 如果没有选中的查核项,返回空数据
+    if (!selectedTreeNode?.key) {
+      return {
+        data: [],
+        success: true,
+        total: 0,
+        pageSize: 10,
+        totalPage: 0,
+      };
+    }
+
+    try {
+      // 获取所有数据,不传递filter参数
+      const response = await getCheckPointData({
+        checkItemId: selectedTreeNode.key, // 查核项ID
+      });
+
+      if (response && Array.isArray(response)) {
+        // 在前端进行本地过滤
+        let filteredData = response;
+        if (checkPointSearchKeywords && checkPointSearchKeywords.trim()) {
+          filteredData = response.filter((item) =>
+            (item.checkPointName &&
+              item.checkPointName.toLowerCase().includes(checkPointSearchKeywords.toLowerCase())) ||
+            (item.checkPointId &&
+              item.checkPointId.toString().includes(checkPointSearchKeywords))
+          );
+        }
+
+        // 手动处理分页
+        const current = params.current || 1;
+        const pageSize = params.pageSize || 10;
+        const start = (current - 1) * pageSize;
+        const end = start + pageSize;
+        const paginatedData = filteredData.slice(start, end);
+
+        return {
+          data: paginatedData,
+          success: true,
+          total: filteredData.length,
+          pageSize: pageSize,
+          totalPage: Math.ceil(filteredData.length / pageSize),
+        };
+      }
+    } catch (error) {
+      console.error('获取查核要点数据失败:', error);
+    }
+
+    return {
+      data: [],
+      success: false,
+      total: 0,
+      pageSize: 10,
+      totalPage: 0,
+    };
+  };
+
+  // 删除查核要点处理
+  const handleCheckPointDelete = async (record: any) => {
+    try {
+      // 使用 id 字段作为删除参数
+      const response = await delCheckPointData([record.id]);
+      if (response) {
+        message.success('删除查核要点成功!');
+        checkPointTableRef.current?.reload();
+      }
+    } catch (error) {
+      console.error('删除查核要点失败:', error);
+      message.error('删除查核要点失败!');
+    }
+  };
+
+  // 批量删除查核要点处理
+  const handleBatchDeleteCheckPoints = async () => {
+    if (selectedCheckPointTableKeys.length === 0) {
+      message.error('请选择要删除的查核要点!');
+      return;
+    }
+
+    try {
+      const response = await delCheckPointData(selectedCheckPointTableKeys);
+      if (response) {
+        message.success(`成功删除 ${selectedCheckPointTableKeys.length} 个查核要点!`);
+        set_selectedCheckPointTableKeys([]);
+        checkPointTableRef.current?.reload();
+      }
+    } catch (error) {
+      console.error('批量删除查核要点失败:', error);
+      message.error('批量删除查核要点失败!');
+    }
+  };
+
+  // 处理添加查核要点按钮点击
+  const handleAddCheckPointClick = async () => {
+    if (!selectedTreeNode?.key) {
+      message.error('请先选择查核项!');
+      return;
+    }
+
+    try {
+      // 获取待添加的查核要点列表
+      const response = await getItemPendingPageCheckpoint({
+        current: 1,
+        pageSize: 1000, // 获取足够多的数据
+        checkItemId: selectedTreeNode.key,
+        filter: '',
+      });
+
+      if (response && response.list) {
+        set_pendingCheckPoints(response.list);
+        set_selectedCheckPoints([]);
+        set_addCheckPointSearchKeywords('');
+        set_addCheckPointVisible(true);
+      }
+    } catch (error) {
+      console.error('获取待添加查核要点列表失败:', error);
+      message.error('获取待添加查核要点列表失败!');
+    }
+  };
+
+  // 处理添加查核要点确认
+  const handleAddCheckPointConfirm = async () => {
+    if (selectedCheckPoints.length === 0) {
+      message.error('请选择要添加的查核要点!');
+      return;
+    }
+
+    if (!selectedTreeNode?.key) {
+      message.error('请先选择查核项!');
+      return;
+    }
+
+    try {
+      // 构造提交数据
+      const mapObjList = selectedCheckPoints.map((id) => {
+        const point = pendingCheckPoints.find((item) => item.id === id);
+        return {
+          id: id,
+          name: point?.name || '',
+        };
+      });
+
+      const submitData = {
+        id: String(selectedTreeNode.key),
+        name: selectedTreeNode.name || selectedTreeNode.title || '',
+        mapObjList: mapObjList,
+      };
+
+      const response = await addCheckItemCheckpoint(submitData);
+
+      if (response) {
+        message.success(`成功添加 ${selectedCheckPoints.length} 个查核要点!`);
+        set_addCheckPointVisible(false);
+        set_selectedCheckPoints([]);
+        checkPointTableRef.current?.reload();
+      }
+    } catch (error) {
+      console.error('添加查核要点失败:', error);
+      message.error('添加查核要点失败!');
+    }
+  };
+
+  // 处理查核要点选择变化
+  const handleCheckPointSelection = (id: string, checked: boolean) => {
+    if (checked) {
+      set_selectedCheckPoints((prev) => [...prev, id]);
+    } else {
+      set_selectedCheckPoints((prev) => prev.filter((item) => item !== id));
+    }
+  };
+
+  // 处理添加查核要点搜索
+  const handleAddCheckPointSearchChange = (
+    e: React.ChangeEvent<HTMLInputElement>,
+  ) => {
+    set_addCheckPointSearchKeywords(e.target.value);
+  };
+
+  // 获取过滤后的待添加查核要点列表
+  const getFilteredPendingCheckPoints = () => {
+    if (addCheckPointSearchKeywords.trim() === '') {
+      return pendingCheckPoints;
+    }
+    return pendingCheckPoints.filter(
+      (item) =>
+        item.name
+          .toLowerCase()
+          .includes(addCheckPointSearchKeywords.toLowerCase()) ||
+        (item.pointCategoryName &&
+          item.pointCategoryName
+            .toLowerCase()
+            .includes(addCheckPointSearchKeywords.toLowerCase())),
+    );
+  };
+
+  // 处理全选/取消全选
+  const handleSelectAll = (checked: boolean) => {
+    if (checked) {
+      // 全选:将当前过滤后的所有项目ID添加到选中列表
+      const filteredItems = getFilteredPendingCheckPoints();
+      const allIds = filteredItems.map((item) => item.id);
+      set_selectedCheckPoints(allIds);
+    } else {
+      // 取消全选:清空选中列表
+      set_selectedCheckPoints([]);
+    }
+  };
+
+  // 判断是否全选状态
+  const isAllSelected = () => {
+    const filteredItems = getFilteredPendingCheckPoints();
+    if (filteredItems.length === 0) return false;
+    return filteredItems.every((item) => selectedCheckPoints.includes(item.id));
+  };
+
+  // 判断是否部分选中状态(用于indeterminate)
+  const isIndeterminate = () => {
+    const filteredItems = getFilteredPendingCheckPoints();
+    if (filteredItems.length === 0) return false;
+    const selectedCount = filteredItems.filter((item) =>
+      selectedCheckPoints.includes(item.id),
+    ).length;
+    return selectedCount > 0 && selectedCount < filteredItems.length;
+  };
+
+  // 解析图片URL列表
+  const parseImageUrls = (imageString: string | null | undefined) => {
+    if (!imageString || typeof imageString !== 'string') {
+      return [];
+    }
+    return imageString.split(',').filter(url => url.trim()).map(url => url.trim());
+  };
+
+  // 处理图片预览
+  const handleImagePreview = (imageIndex: number) => {
+    set_imagePreviewIndex(imageIndex);
+    set_imagePreviewVisible(true);
+  };
+
+  // 上传文件到服务器
+  const uploadFiles = async (fileList: any[]): Promise<string[]> => {
+    const uploadedUrls: string[] = [];
+
+    for (const file of fileList) {
+      if (file.originFileObj) {
+        // 新选择的文件,需要上传
+        const formData = new FormData();
+        formData.append('file', file.originFileObj);
+
+        try {
+          const response = await fetch('/gateway/centerSys/api/batchUpload', {
+            method: 'POST',
+            headers: {
+              token: localStorage.getItem('userData')
+                ? JSON.parse(localStorage.getItem('userData') || '{}').token
+                : '',
+            },
+            body: formData,
+          });
+
+          const result = await response.json();
+
+          if (result?.data && Array.isArray(result.data)) {
+            // 批量上传返回数组格式
+            const urls = result.data.map((item: any) => item.downUrl);
+            uploadedUrls.push(...urls);
+          } else if (result?.data?.downUrl) {
+            // 单个上传格式
+            uploadedUrls.push(result.data.downUrl);
+          } else if (result?.downUrl) {
+            uploadedUrls.push(result.downUrl);
+          }
+        } catch (error) {
+          console.error('文件上传失败:', error);
+          throw new Error(`文件 ${file.name} 上传失败`);
+        }
+      } else if (file.url) {
+        // 已存在的文件(编辑时的回显文件)
+        uploadedUrls.push(file.url);
+      } else if (file.response?.data?.downUrl) {
+        // 已经上传过的文件
+        uploadedUrls.push(file.response.data.downUrl);
+      }
+    }
+
+    return uploadedUrls;
+  };
+
+
+
+  return (
+    <KCIMPagecontainer title={false}>
+      <div className="CheckItemMana">
+        <div className="check-item-container">
+          {/* 左侧查核项分类 */}
+          {renderCheckItemList()}
+
+          {/* 右侧内容区域 */}
+          <div className="main-content" ref={mainContentRef}>
+            {/* 顶部标题和信息区域 */}
+            <div className="content-header">
+              <div className="header-left">
+                <h2 className="title" title={selectedTreeNode?.title || selectedTreeNode?.name || '请选择查核项'}>
+                  {(() => {
+                    const fullTitle = selectedTreeNode?.title || selectedTreeNode?.name || '请选择查核项';
+                    return fullTitle.length > 25 ? `${fullTitle.substring(0, 25)}...` : fullTitle;
+                  })()}
+                </h2>
+                <div className="description">
+                  应知应会:{selectedTreeNode?.rightAnswerText || '暂无内容'}
+                </div>
+                <div className="info-tags">
+                  <div className="info-tag">
+                    <div className="tag-icon">
+                      <img src={leixingIcon} alt="选项类型" />
+                    </div>
+                    <div className="tag-content">
+                      <span className="tag-label">选项类型</span>
+                      <span className="tag-value">
+                        {selectedTreeNode?.moreDeduction === 0
+                          ? '单选'
+                          : selectedTreeNode?.moreDeduction === 1
+                            ? '多选'
+                            : '未知'}
+                      </span>
+                    </div>
+                  </div>
+                  <div className="info-tag">
+                    <div className="tag-icon">
+                      <img src={fenzhiIcon} alt="项目分值" />
+                    </div>
+                    <div className="tag-content">
+                      <span className="tag-label">项目分值</span>
+                      <span className="tag-value">
+                        {selectedTreeNode?.score || 0}分
+                      </span>
+                    </div>
+                  </div>
+                  <div className="info-tag">
+                    <div className="tag-icon">
+                      <img src={chaheIcon} alt="查核方式" />
+                    </div>
+                    <div className="tag-content">
+                      <span className="tag-label">查核方式</span>
+                      <span className="tag-value">
+                        {selectedTreeNode?.checkModeNameList || '未知'}
+                      </span>
+                    </div>
+                  </div>
+                  <div className="info-tag">
+                    <div className="tag-icon">
+                      <img src={tongjiIcon} alt="统计小计" />
+                    </div>
+                    <div className="tag-content">
+                      <span className="tag-label">统计小计</span>
+                      <span className="tag-value">
+                        {selectedTreeNode?.subtotal === 1 ? '是' : '否'}
+                      </span>
+                    </div>
+                  </div>
+                </div>
+              </div>
+              <div className="header-right">
+                <div className="image-gallery">
+                  {(() => {
+                    const imageUrls = parseImageUrls(selectedTreeNode?.rightAnswerImage);
+
+                    // 如果没有图片,不显示任何内容
+                    if (imageUrls.length === 0) {
+                      return null;
+                    }
+
+                    // 只显示前2张图片
+                    const displayImages = imageUrls.slice(0, 2);
+                    const remainingCount = imageUrls.length - 2;
+
+                    return (
+                      <>
+                        {displayImages.map((url, index) => (
+                          <div
+                            key={index}
+                            className="image-item"
+                            onClick={() => handleImagePreview(index)}
+                            style={{
+                              cursor: 'pointer',
+                              position: 'relative'
+                            }}
+                          >
+                            <img
+                              src={url}
+                              alt={`查核项图片${index + 1}`}
+                            />
+
+                            {/* 如果是第二张图片且有更多图片,显示遮罩层 */}
+                            {index === 1 && remainingCount > 0 && (
+                              <div
+                                style={{
+                                  position: 'absolute',
+                                  top: 0,
+                                  left: 0,
+                                  right: 0,
+                                  bottom: 0,
+                                  backgroundColor: 'rgba(0, 0, 0, 0.6)',
+                                  color: 'white',
+                                  display: 'flex',
+                                  flexDirection: 'column',
+                                  alignItems: 'center',
+                                  justifyContent: 'center',
+                                  fontSize: '14px',
+                                  fontWeight: 'normal'
+                                }}
+                              >
+                                <span style={{ fontSize: '24px', marginBottom: '4px' }}>
+                                  +{remainingCount}
+                                </span>
+                                <span style={{ opacity: 0.8 }}>查看更多图例</span>
+                              </div>
+                            )}
+                          </div>
+                        ))}
+                      </>
+                    );
+                  })()}
+                </div>
+              </div>
+            </div>
+
+            {/* Tab区域和表格 */}
+            <div className="content-body">
+              <Card className="pfm-ant-card" bordered={false}>
+                <Tabs defaultActiveKey="defects" onChange={handleTabChange}>
+                  <TabPane tab="缺陷项" key="defects">
+                    <div className="tab-content">
+                      <div className="toolbar">
+                        <div
+                          style={{
+                            display: 'flex',
+                            alignItems: 'center',
+                            gap: 8,
+                          }}
+                        >
+                          <span style={{ color: '#17181A', fontSize: 14 }}>
+                            检索:
+                          </span>
+                          <Input
+                            placeholder="缺陷项"
+                            allowClear
+                            suffix={
+                              <SearchOutlined style={{ color: '#99A6BF' }} />
+                            }
+                            style={{ width: 250 }}
+                            value={tableDataSearchKeywords}
+                            onChange={(e) => {
+                              set_tableDataSearchKeywords(e.target.value);
+                              if (e.target.value === '') {
+                                tableRef.current?.reload();
+                              }
+                            }}
+                            onPressEnter={(e) =>
+                              tableDataSearchHandle(e.currentTarget.value)
+                            }
+                          />
+                        </div>
+                        <UpDataActBtn record={{}} type="ADD" />
+                      </div>
+
+                      <KCIMTable
+                        actionRef={tableRef}
+                        columns={defectColumns}
+                        request={getTableData}
+                        params={{ keywords: tableDataSearchKeywords }}
+                        rowKey="id"
+                        size="middle"
+                        manualRequest={true}
+                        search={false}
+                        pagination={{
+                          showTotal: (total, range) =>
+                            `第 ${range[0]}-${range[1]} 条/共计 ${total} 条`,
+                          showSizeChanger: true,
+                          showQuickJumper: true,
+                          defaultPageSize: 10,
+                        }}
+                      />
+                    </div>
+                  </TabPane>
+                  <TabPane tab="查核结果" key="results">
+                    <div className="tab-content">
+                      <div className="toolbar">
+                        <div
+                          style={{
+                            display: 'flex',
+                            alignItems: 'center',
+                            gap: 8,
+                          }}
+                        >
+                          <span style={{ color: '#17181A', fontSize: 14 }}>
+                            检索:
+                          </span>
+                          <Input
+                            placeholder="查核结果"
+                            allowClear
+                            suffix={
+                              <SearchOutlined style={{ color: '#99A6BF' }} />
+                            }
+                            style={{ width: 250 }}
+                            value={checkResultSearchKeywords}
+                            onChange={(e) => {
+                              set_checkResultSearchKeywords(e.target.value);
+                              if (e.target.value === '') {
+                                checkResultTableRef.current?.reload();
+                              }
+                            }}
+                            onPressEnter={(e) => {
+                              set_checkResultSearchKeywords(e.currentTarget.value);
+                              checkResultTableRef.current?.reload();
+                            }}
+                          />
+                        </div>
+                        <div style={{ display: 'flex', gap: 8 }}>
+                          <Button
+                            onClick={handleCopySettingClick}
+                            size="small"
+                            style={{
+                              height: 24,
+                              borderRadius: 4,
+                              fontSize: 14,
+                              backgroundColor: '#f5f7fa',
+                              borderColor: '#d9d9d9',
+                              color: '#17181a',
+                            }}
+                          >
+                            复制设置
+                          </Button>
+                          <CheckResultUpDataActBtn record={{}} type="ADD" />
+                        </div>
+                      </div>
+
+                      <KCIMTable
+                        actionRef={checkResultTableRef}
+                        columns={checkResultColumns}
+                        request={getCheckResultTableData}
+                        params={{ keywords: checkResultSearchKeywords }}
+                        rowKey="id"
+                        size="middle"
+                        manualRequest={true}
+                        search={false}
+                        pagination={{
+                          showTotal: (total, range) =>
+                            `第 ${range[0]}-${range[1]} 条/共计 ${total} 条`,
+                          showSizeChanger: true,
+                          showQuickJumper: true,
+                          defaultPageSize: 10,
+                        }}
+                      />
+                    </div>
+                  </TabPane>
+                  <TabPane tab="查核要点" key="points">
+                    <div className="tab-content">
+                      <div className="toolbar">
+                        <div
+                          style={{
+                            display: 'flex',
+                            alignItems: 'center',
+                            gap: 8,
+                          }}
+                        >
+                          <span style={{ color: '#17181A', fontSize: 14 }}>
+                            检索:
+                          </span>
+                          <Input
+                            placeholder="查核要点"
+                            allowClear
+                            suffix={
+                              <SearchOutlined style={{ color: '#99A6BF' }} />
+                            }
+                            style={{ width: 250 }}
+                            value={checkPointSearchKeywords}
+                            onChange={(e) => {
+                              set_checkPointSearchKeywords(e.target.value);
+                              if (e.target.value === '') {
+                                checkPointTableRef.current?.reload();
+                              }
+                            }}
+                            onPressEnter={(e) => {
+                              set_checkPointSearchKeywords(e.currentTarget.value);
+                              checkPointTableRef.current?.reload();
+                            }}
+                          />
+                        </div>
+                        <Button
+                          type="primary"
+                          className="add-btn"
+                          size="small"
+                          onClick={handleAddCheckPointClick}
+                        >
+                          添加
+                        </Button>
+                      </div>
+
+                      <KCIMTable
+                        actionRef={checkPointTableRef}
+                        columns={checkPointColumns}
+                        request={getCheckPointTableData}
+                        params={{ keywords: checkPointSearchKeywords }}
+                        rowKey="id"
+                        size="middle"
+                        manualRequest={true}
+                        search={false}
+                        pagination={{
+                          showTotal: (total, range) =>
+                            `第 ${range[0]}-${range[1]} 条/共计 ${total} 条`,
+                          showSizeChanger: true,
+                          showQuickJumper: true,
+                          defaultPageSize: 10,
+                        }}
+                        rowSelection={{
+                          selectedRowKeys: selectedCheckPointTableKeys,
+                          onChange: (selectedRowKeys) => {
+                            set_selectedCheckPointTableKeys(selectedRowKeys as string[]);
+                          },
+                        }}
+                        tableAlertRender={false}
+                      />
+
+                      {/* 批量操作栏 */}
+                      {selectedCheckPointTableKeys.length > 0 && (
+                        <div
+                          style={{
+                            position: 'fixed',
+                            left: actionBarStyle.left,
+                            width: actionBarStyle.width,
+                            bottom: 16,
+                            height: 56,
+                            backgroundColor: '#fff',
+                            zIndex: 999,
+                            boxShadow: 'rgba(0, 0, 0, 0.04) 0px -2px 8px',
+                            borderRadius: 4,
+                            display: 'flex',
+                            alignItems: 'center',
+                            justifyContent: 'space-between',
+                            padding: '0 16px',
+                            transition: 'left 0.2s, width 0.2s',
+                          }}
+                        >
+                          <span style={{ color: '#3377FF', fontWeight: 500 }}>
+                            已选 {selectedCheckPointTableKeys.length} 项
+                          </span>
+                          <div style={{ display: 'flex', gap: 8 }}>
+                            <Button
+                              onClick={() => {
+                                set_selectedCheckPointTableKeys([]);
+                              }}
+                              style={{ height: 32, borderRadius: 4, fontSize: 14 }}
+                              size="small"
+                            >
+                              取消选择
+                            </Button>
+                            <Popconfirm
+                              title={`删除${selectedCheckPointTableKeys.length} 个查核要点?`}
+                              okText="确定"
+                              cancelText="取消"
+                              onConfirm={handleBatchDeleteCheckPoints}
+                              placement="top"
+                              getPopupContainer={(triggerNode) => triggerNode.parentElement || document.body}
+                            >
+                              <Button
+                                type="primary"
+                                style={{
+                                  background: '#3377FF',
+                                  borderColor: '#3377FF',
+                                  height: 32,
+                                  borderRadius: 4,
+                                  fontSize: 14,
+                                }}
+                                size="small"
+                              >
+                                批量删除
+                              </Button>
+                            </Popconfirm>
+                          </div>
+                        </div>
+                      )}
+                    </div>
+                  </TabPane>
+                </Tabs>
+              </Card>
+            </div>
+          </div>
+        </div>
+      </div>
+
+      {/* 新增查核项弹窗 */}
+      <ModalForm
+        title="新增查核项"
+        width={352}
+        visible={addCheckItemVisible}
+        onVisibleChange={set_addCheckItemVisible}
+        formRef={addCheckItemFormRef}
+        onFinish={handleAddCheckItemSubmit}
+        initialValues={{
+          subtotal: 1,
+          moreDeduction: 0,
+          score: 0,
+          rightAnswerText: '',
+        }}
+        modalProps={{
+          destroyOnClose: true,
+          maskClosable: false,
+          className: 'add-check-item-modal',
+          bodyStyle: {
+            maxHeight: '60vh',
+            overflowY: 'auto',
+          },
+        }}
+      >
+        <ProFormText
+          name="name"
+          label="查核项"
+          placeholder="请输入"
+          rules={[{ required: true, message: '请输入查核项' }]}
+        />
+
+        <ProFormRadio.Group
+          name="subtotal"
+          label="统计小计"
+          options={[
+            { label: '是', value: 1 },
+            { label: '否', value: 0 },
+          ]}
+          rules={[{ required: true, message: '请选择是否统计小计' }]}
+        />
+
+        <ProFormRadio.Group
+          name="moreDeduction"
+          label="缺陷项类型"
+          options={[
+            { label: '单选', value: 0 },
+            { label: '多选', value: 1 },
+          ]}
+          rules={[{ required: true, message: '请选择缺陷项类型' }]}
+        />
+
+        <ProFormSelect
+          name="checkModeList"
+          label="查核方式"
+          placeholder="请选择"
+          options={checkModeOptions}
+          rules={[{ required: true, message: '请选择查核方式' }]}
+        />
+
+        <ProFormDigit
+          name="score"
+          label="分值"
+          placeholder="0"
+          min={0}
+          max={100}
+          fieldProps={{ precision: 1 }}
+          rules={[{ required: true, message: '请输入分值' }]}
+        />
+
+        <ProFormTextArea
+          name="rightAnswerText"
+          label="应知应会"
+          placeholder="请输入"
+          fieldProps={{
+            rows: 3,
+            maxLength: 500,
+            showCount: true,
+          }}
+          rules={[{ required: true, message: '请输入应知应会内容' }]}
+        />
+
+        <ProFormUploadButton
+          name="rightAnswerImage"
+          label="附加图片"
+          max={10}
+          fieldProps={{
+            accept: 'image/*',
+            listType: 'picture',
+            multiple: true,
+            beforeUpload: () => {
+              // 阻止自动上传,只做本地预览
+              return false;
+            },
+          }}
+          title="点击选择图片"
+        />
+      </ModalForm>
+
+      {/* 编辑查核项弹窗 */}
+      <ModalForm
+        title="编辑查核项"
+        width={352}
+        visible={editCheckItemVisible}
+        onVisibleChange={set_editCheckItemVisible}
+        formRef={addCheckItemFormRef}
+        onFinish={async (values) => {
+            // 健壮处理图片字段,兼容字符串和数组两种情况
+            let rightAnswerImage = '';
+            if (typeof values.rightAnswerImage === 'string') {
+              // 用户未编辑图片,直接用原字符串
+              rightAnswerImage = values.rightAnswerImage;
+            } else if (Array.isArray(values.rightAnswerImage)) {
+              // 用户编辑过图片,处理为字符串
+              const uploadedUrls = [];
+              for (const file of values.rightAnswerImage) {
+                if (file.originFileObj) {
+                  // 新上传的图片,需上传
+                  try {
+                    const res = await uploadFiles([file]);
+                    if (res && res.length > 0) {
+                      uploadedUrls.push(res[0]);
+                    }
+                  } catch (uploadError) {
+                    console.error('编辑时图片上传失败:', uploadError);
+                    message.error('图片上传失败,请重试!');
+                    return false;
+                  }
+                } else if (file.url) {
+                  // 已有图片
+                  uploadedUrls.push(file.url);
+                } else if (file.response?.data?.downUrl) {
+                  // 兼容已上传格式
+                  uploadedUrls.push(file.response.data.downUrl);
+                }
+              }
+              rightAnswerImage = uploadedUrls.join(',');
+            } else {
+              // 兜底
+              rightAnswerImage = '';
+            }
+            // 组装最终提交数据
+            const submitData = {
+              ...values,
+              rightAnswerImage,
+            };
+            return handleEditCheckItemSubmit(submitData);
+        }}
+        initialValues={
+          currentEditItem
+            ? {
+                name: currentEditItem.name,
+                subtotal: currentEditItem.subtotal,
+                moreDeduction: currentEditItem.moreDeduction,
+                checkModeList: currentEditItem.checkModeList,
+                score: currentEditItem.score,
+                rightAnswerText: currentEditItem.rightAnswerText,
+                // 直接传字符串,回显由convertValue处理
+                rightAnswerImage: currentEditItem.rightAnswerImage || ''
+              }
+            : {}
+        }
+        modalProps={{
+          destroyOnClose: true,
+          maskClosable: false,
+          className: 'add-check-item-modal',
+          bodyStyle: {
+            maxHeight: '60vh',
+            overflowY: 'auto',
+          },
+        }}
+      >
+        <ProFormText
+          name="name"
+          label="查核项"
+          placeholder="请输入"
+          rules={[{ required: true, message: '请输入查核项' }]}
+        />
+
+        <ProFormRadio.Group
+          name="subtotal"
+          label="统计小计"
+          options={[
+            { label: '是', value: 1 },
+            { label: '否', value: 0 },
+          ]}
+          rules={[{ required: true, message: '请选择是否统计小计' }]}
+        />
+
+        <ProFormRadio.Group
+          name="moreDeduction"
+          label="缺陷项类型"
+          options={[
+            { label: '单选', value: 0 },
+            { label: '多选', value: 1 },
+          ]}
+          rules={[{ required: true, message: '请选择缺陷项类型' }]}
+        />
+
+        <ProFormSelect
+          name="checkModeList"
+          label="查核方式"
+          placeholder="请选择"
+          options={checkModeOptions}
+          rules={[{ required: true, message: '请选择查核方式' }]}
+        />
+
+        <ProFormDigit
+          name="score"
+          label="分值"
+          placeholder="0"
+          min={0}
+          max={100}
+          fieldProps={{ precision: 1 }}
+          rules={[{ required: true, message: '请输入分值' }]}
+        />
+
+        <ProFormTextArea
+          name="rightAnswerText"
+          label="应知应会"
+          placeholder="请输入"
+          fieldProps={{
+            rows: 3,
+            maxLength: 500,
+            showCount: true,
+          }}
+          rules={[{ required: true, message: '请输入应知应会内容' }]}
+        />
+
+        <ProFormUploadButton
+          name="rightAnswerImage"
+          label="附加图片"
+          max={10}
+          convertValue={(value) => {
+            // 编辑时:将URL字符串转换为文件列表格式用于回显
+            if (typeof value === 'string' && value.trim()) {
+              const urls = value.split(',').filter((url) => url.trim());
+              return urls.map((url, index) => {
+                const trimmedUrl = url.trim();
+                let fileName = `图片${index + 1}`;
+                const uploadIndex = trimmedUrl.indexOf('upload/');
+                if (uploadIndex !== -1) {
+                  const encodedFileName = trimmedUrl.substring(uploadIndex + 7);
+                  try {
+                    fileName = decodeURIComponent(encodedFileName);
+                  } catch (error) {
+                    fileName = encodedFileName;
+                  }
+                }
+                return {
+                  uid: `existing-${index}`,
+                  name: fileName,
+                  status: 'done',
+                  url: trimmedUrl,
+                  response: {
+                    data: {
+                      downUrl: trimmedUrl,
+                    },
+                  },
+                };
+              });
+            }
+            return value || [];
+          }}
+          fieldProps={{
+            accept: 'image/*',
+            listType: 'picture',
+            multiple: true,
+            beforeUpload: () => {
+              // 阻止自动上传,只做本地预览
+              return false;
+            },
+          }}
+          title="点击选择图片"
+        />
+      </ModalForm>
+
+      {/* 复制设置弹窗 */}
+      <Modal
+        title={`复制查核项设置 (${selectedTreeNode?.name || selectedTreeNode?.title || '未选择'})`}
+        width={700}
+        open={copySettingVisible}
+        onCancel={() => set_copySettingVisible(false)}
+        footer={[
+          <Button key="cancel" onClick={() => set_copySettingVisible(false)}>
+            取消
+          </Button>,
+          <Button
+            key="submit"
+            type="primary"
+            onClick={handleCopySettingConfirm}
+            disabled={selectedCopyItems.length === 0}
+            style={{
+              backgroundColor: '#3377FF',
+              borderColor: '#3377FF',
+              color: '#FFFFFF',
+            }}
+          >
+            确定
+            {selectedCopyItems.length > 0 ? ` (${selectedCopyItems.length})` : ''}
+          </Button>,
+        ]}
+        destroyOnClose
+        className="copy-setting-modal"
+      >
+        <div style={{ marginBottom: 8 }}>
+          <Input
+            placeholder="请输入"
+            suffix={<SearchOutlined style={{ color: '#99A6BF' }} />}
+            style={{ width: '100%' }}
+            value={copySearchKeywords}
+            onChange={handleCopySearchChange}
+            onPressEnter={handleCopySearchConfirm}
+            allowClear
+          />
+        </div>
+
+        <KCIMTable
+          actionRef={copySettingTableRef}
+          columns={[
+            {
+              title: (
+                <Checkbox
+                  checked={copySettingTableData.length > 0 && selectedCopyItems.length === copySettingTableData.length}
+                  indeterminate={selectedCopyItems.length > 0 && selectedCopyItems.length < copySettingTableData.length}
+                  onChange={e => {
+                    if (e.target.checked) {
+                      set_selectedCopyItems(copySettingTableData.map((item: any) => String(item.id)));
+                    } else {
+                      set_selectedCopyItems([]);
+                    }
+                  }}
+                />
+              ),
+              dataIndex: 'selected',
+              width: 50,
+              align: 'center' as 'center',
+              render: (_: any, record: any) => (
+                <Checkbox
+                  checked={selectedCopyItems.includes(String(record.id))}
+                  onChange={e => {
+                    if (e.target.checked) {
+                      set_selectedCopyItems(prev => [...prev, String(record.id)]);
+                    } else {
+                      set_selectedCopyItems(prev => prev.filter(id => id !== String(record.id)));
+                    }
+                  }}
+                />
+              ),
+            },
+            {
+              title: '查核项',
+              dataIndex: 'name',
+              align: 'left' as 'left',
+              ellipsis: true,
+              render: (text) => <span>{text}</span>,
+            },
+            {
+              title: '应知应会',
+              dataIndex: 'rightAnswerText',
+              align: 'left' as 'left',
+              ellipsis: true,
+              render: (text) => text || '无相关物品...',
+            },
+          ]}
+          request={async (params) => {
+            const data = await getCopySettingTableData(params);
+            // 记录当前表格数据用于全选判断
+            if (data && typeof data === 'object' && 'data' in data) {
+              set_copySettingTableData(data.data || []);
+            } else {
+              set_copySettingTableData([]);
+            }
+            return data;
+          }}
+          params={{ keywords: copySearchKeywords }}
+          rowKey="id"
+          search={false}
+          pagination={{
+            simple: true,
+            pageSize: 100,
+            size: 'small',
+            showTotal: () => null,
+          }}
+          tableAlertRender={false}
+          scroll={{ y: 360 }}
+        />
+      </Modal>
+
+      {/* 添加查核要点弹窗 */}
+      <Modal
+        title={`选择查核要点 (${selectedTreeNode?.name || selectedTreeNode?.title || ''})`}
+        width={700}
+        open={addCheckPointVisible}
+        onCancel={() => set_addCheckPointVisible(false)}
+        footer={[
+          <Button key="cancel" onClick={() => set_addCheckPointVisible(false)}>
+            取消
+          </Button>,
+          <Button
+            key="submit"
+            type="primary"
+            onClick={handleAddCheckPointConfirm}
+            disabled={selectedCheckPoints.length === 0}
+            style={{
+              backgroundColor: '#3377FF',
+              borderColor: '#3377FF',
+              color: '#FFFFFF',
+            }}
+          >
+            确定
+            {selectedCheckPoints.length > 0 ? ` (${selectedCheckPoints.length})` : ''}
+          </Button>,
+        ]}
+        destroyOnClose
+        className="add-check-point-modal"
+      >
+        <div style={{ marginBottom: 8 }}>
+          <Input
+            placeholder="请输入"
+            suffix={<SearchOutlined style={{ color: '#99A6BF' }} />}
+            style={{ width: '100%' }}
+            value={addCheckPointSearchKeywords}
+            onChange={handleAddCheckPointSearchChange}
+            allowClear
+          />
+        </div>
+
+        <KCIMTable
+          actionRef={useRef<ActionType>()}
+          columns={[
+            {
+              title: (
+                <Checkbox
+                  checked={isAllSelected()}
+                  indeterminate={isIndeterminate()}
+                  onChange={(e) => handleSelectAll(e.target.checked)}
+                />
+              ),
+              dataIndex: 'selected',
+              width: 50,
+              align: 'center' as 'center',
+              render: (_, record) => (
+                <Checkbox
+                  checked={selectedCheckPoints.includes(record.id)}
+                  onChange={(e) => {
+                    handleCheckPointSelection(record.id, e.target.checked);
+                  }}
+                />
+              ),
+            },
+            {
+              title: '要点ID',
+              dataIndex: 'id',
+              width: 120,
+              align: 'left' as 'left',
+            },
+            {
+              title: '查核要点名称',
+              dataIndex: 'name',
+              align: 'left' as 'left',
+              ellipsis: true,
+            },
+          ]}
+          dataSource={getFilteredPendingCheckPoints()}
+          rowKey="id"
+          search={false}
+          pagination={{
+            simple: true,
+            pageSize: 10,
+            size: 'small',
+            showTotal: () => null,
+          }}
+          tableAlertRender={false}
+          scroll={{ y: 360 }}
+        />
+      </Modal>
+
+      {/* 全局图片预览遮罩层 */}
+      {imagePreviewVisible && (
+        <div
+          style={{
+            position: 'fixed',
+            top: 0,
+            left: 0,
+            right: 0,
+            bottom: 0,
+            backgroundColor: 'rgba(0, 0, 0, 0.8)',
+            zIndex: 9999,
+            display: 'flex',
+            alignItems: 'center',
+            justifyContent: 'center',
+            cursor: 'pointer'
+          }}
+          onClick={() => set_imagePreviewVisible(false)}
+        >
+          {(() => {
+            const imageUrls = parseImageUrls(selectedTreeNode?.rightAnswerImage);
+            if (imageUrls.length === 0) return null;
+
+            return (
+              <>
+                <div
+                  style={{
+                    position: 'relative',
+                    maxWidth: '90vw',
+                    maxHeight: '90vh',
+                    display: 'flex',
+                    alignItems: 'center',
+                    justifyContent: 'center'
+                  }}
+                  onClick={(e) => e.stopPropagation()} // 阻止点击图片区域关闭
+                >
+                  {/* 当前图片 */}
+                  <img
+                    src={imageUrls[imagePreviewIndex]}
+                    alt={`图片 ${imagePreviewIndex + 1}`}
+                    style={{
+                      maxWidth: '70%',
+                      maxHeight: '70%',
+                      objectFit: 'contain',
+                      borderRadius: '8px'
+                    }}
+                  />
+                </div>
+
+                {/* 固定位置的控制按钮 */}
+                {/* 关闭按钮 - 固定在右上角 */}
+                <img
+                  onClick={(e) => {
+                    e.stopPropagation();
+                    set_imagePreviewVisible(false);
+                  }}
+                  src={guanbiIcon}
+                  alt="关闭"
+                  style={{
+                    position: 'fixed',
+                    top: '20px',
+                    right: '20px',
+                    width: '40px',
+                    height: '40px',
+                    cursor: 'pointer',
+                    zIndex: 10001
+                  }}
+                />
+
+                {/* 图片导航 */}
+                {imageUrls.length > 1 && (
+                  <>
+                    {/* 左箭头 - 固定在屏幕左边 */}
+                    <img
+                      onClick={(e) => {
+                        e.stopPropagation();
+                        // 循环切换:如果是第一张,跳转到最后一张;否则跳转到上一张
+                        const nextIndex = imagePreviewIndex === 0 ? imageUrls.length - 1 : imagePreviewIndex - 1;
+                        set_imagePreviewIndex(nextIndex);
+                      }}
+                      src={shangyiyeIcon}
+                      alt="上一页"
+                      style={{
+                        position: 'fixed',
+                        left: '20px',
+                        top: '50%',
+                        transform: 'translateY(-50%)',
+                        width: '80px',
+                        height: '160px',
+                        cursor: 'pointer',
+                        zIndex: 10001
+                      }}
+                    />
+
+                    {/* 右箭头 - 固定在屏幕右边 */}
+                    <img
+                      onClick={(e) => {
+                        e.stopPropagation();
+                        // 循环切换:如果是最后一张,跳转到第一张;否则跳转到下一张
+                        const nextIndex = imagePreviewIndex === imageUrls.length - 1 ? 0 : imagePreviewIndex + 1;
+                        set_imagePreviewIndex(nextIndex);
+                      }}
+                      src={xiayiyeIcon}
+                      alt="下一页"
+                      style={{
+                        position: 'fixed',
+                        right: '20px',
+                        top: '50%',
+                        transform: 'translateY(-50%)',
+                        width: '80px',
+                        height: '160px',
+                        cursor: 'pointer',
+                        zIndex: 10001
+                      }}
+                    />
+
+                    {/* 页码指示器 - 固定在底部 */}
+                    <div
+                      onClick={(e) => e.stopPropagation()}
+                      style={{
+                        position: 'fixed',
+                        bottom: '20px',
+                        left: '50%',
+                        transform: 'translateX(-50%)',
+                        backgroundColor: 'rgba(255, 255, 255, 0.9)',
+                        color: '#333',
+                        padding: '8px 16px',
+                        borderRadius: '20px',
+                        fontSize: '14px',
+                        fontWeight: 'normal',
+                        boxShadow: '0 2px 8px rgba(0, 0, 0, 0.3)',
+                        zIndex: 10001
+                      }}
+                    >
+                      {imagePreviewIndex + 1} / {imageUrls.length}
+                    </div>
+                  </>
+                )}
+              </>
+            );
+          })()}
+        </div>
+      )}
+    </KCIMPagecontainer>
+  );
+} 

+ 374 - 0
src/pages/checkItemMana/service.ts

@@ -0,0 +1,374 @@
+/*
+ * @Author: code4eat awesomedema@gmail.com
+ * @Date: 2024-01-01 10:00:00
+ * @LastEditors: code4eat awesomedema@gmail.com
+ * @LastEditTime: 2025-06-30 10:30:00
+ * @FilePath: /pfmBackMana/src/pages/checkItemMana/service.ts
+ * @Description: 查核项管理页面接口服务
+ */
+
+import { request } from 'umi';
+
+// 查核项数据类型定义
+export type CheckItemType = {
+  id: number;
+  name: string;
+  rightAnswerText: string;
+  rightAnswerImage: string | null;
+  checkModeList: string;
+  checkModeNameList: string; // 查核方式名称列表
+  moreDeduction: number;
+  score: number | null;
+  subtotal: number | null;
+};
+
+// 缺陷项数据类型定义
+export type DefectItemType = {
+  id: number;
+  checkItemId: number; // 查核项ID
+  resultName: string; // 缺陷项名称
+  percentScore: number; // 分数
+};
+
+// 查核结果数据类型定义
+export type CheckResultItemType = {
+  id: number;
+  checkItemId: number; // 查核项ID
+  name: string; // 查核结果名称
+  score: number; // 分值
+  scoreType: number; // 分值类型:0-数值,1-占比
+  defaultFlag: number; // 默认项标识:0-否,1-是
+  fastFlag: number; // 快捷项标识:0-否,1-是
+};
+
+// 查核项分类树节点类型
+export type CheckItemTreeNode = {
+  key: string;
+  title: string;
+  children?: CheckItemTreeNode[];
+  type?: string;
+  score?: number;
+  checkMethod?: string;
+  isStatistics?: boolean;
+};
+
+// 查核方式类型定义
+export type CheckModeType = {
+  itemCode: string;
+  itemName: string;
+};
+
+// 查核要点数据类型定义
+export type CheckPointItemType = {
+  id: number;
+  checkItemId: number; // 查核项ID
+  name: string; // 查核要点名称
+  checkPointId: string; // 查核要点ID(显示用)
+  checkPointName: string; // 查核要点名称(显示用)
+};
+
+// 获取查核项列表数据
+export const getData = (params?: any) => {
+  return request<{
+    totalCount: number;
+    pageSize: number;
+    totalPage: number;
+    currPage: number;
+    list: CheckItemType[];
+  }>('/manager/CheckItem/getCheckItem', {
+    method: 'GET',
+    params: {
+      current: 1,
+      pageSize: 10,
+      filter: '',
+      ...params,
+    },
+  });
+};
+
+// 获取缺陷项列表数据(无分页)
+export const getDefectData = (params?: any) => {
+  return request<DefectItemType[]>('/manager/CheckItem/getCheckItemDefect', {
+    method: 'GET',
+    params: {
+      checkItemId: '',
+      filter: '',
+      ...params,
+    },
+  });
+};
+
+// 获取查核结果列表数据(无分页)
+export const getCheckResultData = (params?: any) => {
+  return request<CheckResultItemType[]>(
+    '/manager/CheckItem/getCheckItemResult',
+    {
+      method: 'GET',
+      params: {
+        checkItemId: '',
+        filter: '',
+        ...params,
+      },
+    },
+  );
+};
+
+// 获取查核项分类树
+export const getCheckItemTree = () => {
+  return request<{
+    data: CheckItemTreeNode[];
+    success: boolean;
+  }>('/manager/checkItem/getCheckItemTree', {
+    method: 'GET',
+  });
+};
+
+// 新增查核项
+export type AddCheckItemType = {
+  name: string; // 查核项名称
+  rightAnswerText: string; // 应知应会文字
+  rightAnswerImage?: string; // 应知应会图片
+  checkModeList: string; // 查核方式代码
+  moreDeduction: number; // 缺陷项类型:0单选 1多选
+  score: number; // 该查核项总分
+  subtotal: number; // 是否统计小计:0不是 1是
+};
+
+export const addData = (data: AddCheckItemType) => {
+  return request('/manager/CheckItem/addCheckItem', {
+    method: 'POST',
+    data,
+  });
+};
+
+// 新增缺陷项
+export type AddDefectItemType = {
+  checkItemId: number; // 查核项ID
+  resultName: string; // 缺陷项名称
+  percentScore: number; // 分数
+};
+
+export const addDefectData = (data: AddDefectItemType) => {
+  return request('/manager/CheckItem/addCheckItemDefect', {
+    method: 'POST',
+    data,
+  });
+};
+
+// 新增查核结果
+export type AddCheckResultItemType = {
+  checkItemId: number; // 查核项ID
+  name: string; // 查核结果名称
+  score: number; // 分值
+  scoreType: number; // 分值类型:0-数值,1-占比
+  defaultFlag: number; // 默认项标识:0-否,1-是
+  fastFlag: number; // 快捷项标识:0-否,1-是
+};
+
+export const addCheckResultData = (data: AddCheckResultItemType) => {
+  return request('/manager/CheckItem/addCheckItemResult', {
+    method: 'POST',
+    data,
+  });
+};
+
+// 编辑查核项
+export type UpdateCheckItemType = {
+  id: number; // 查核项ID
+  name: string; // 查核项名称
+  rightAnswerText: string; // 应知应会文字
+  rightAnswerImage?: string; // 应知应会图片
+  checkModeList: string; // 查核方式代码
+  moreDeduction: number; // 缺陷项类型:0单选 1多选
+  score: number; // 该查核项总分
+  subtotal: number; // 是否统计小计:0不是 1是
+};
+
+export const editData = (data: UpdateCheckItemType) => {
+  return request('/manager/CheckItem/updateCheckItem', {
+    method: 'POST',
+    data,
+  });
+};
+
+// 编辑缺陷项
+export type UpdateDefectItemType = {
+  id: number; // 缺陷项ID
+  checkItemId: number; // 查核项ID
+  resultName: string; // 缺陷项名称
+  percentScore: number; // 分数
+};
+
+export const editDefectData = (data: UpdateDefectItemType) => {
+  return request('/manager/CheckItem/updateCheckItemDefect', {
+    method: 'POST',
+    data,
+  });
+};
+
+// 编辑查核结果
+export type UpdateCheckResultItemType = {
+  id: number; // 查核结果ID
+  checkItemId: number; // 查核项ID
+  name: string; // 查核结果名称
+  score: number; // 分值
+  scoreType: number; // 分值类型:0-数值,1-占比
+  defaultFlag: number; // 默认项标识:0-否,1-是
+  fastFlag: number; // 快捷项标识:0-否,1-是
+};
+
+export const editCheckResultData = (data: UpdateCheckResultItemType) => {
+  return request('/manager/CheckItem/updateCheckItemResult', {
+    method: 'POST',
+    data,
+  });
+};
+
+// 删除查核项
+export const delData = (ids: Array<string | number>) => {
+  return request('/manager/CheckItem/deleteCheckItem', {
+    method: 'POST',
+    data: ids,
+  });
+};
+
+// 删除缺陷项
+export const delDefectData = (ids: Array<string | number>) => {
+  return request('/manager/CheckItem/delCheckItemDefect', {
+    method: 'POST',
+    data: ids,
+  });
+};
+
+// 删除查核结果
+export const delCheckResultData = (ids: Array<string | number>) => {
+  return request('/manager/CheckItem/delCheckItemResult', {
+    method: 'POST',
+    data: ids,
+  });
+};
+
+// 获取查核方式列表
+export const getCheckItemModeList = () => {
+  return request<CheckModeType[]>('/manager/CheckItem/getCheckItemModeList', {
+    method: 'GET',
+  });
+};
+
+// 复制查核结果
+export type CopyCheckItemResultType = {
+  sourceId: string; // 来源查核项ID
+  targetIdList: string[]; // 目标查核项ID列表
+};
+
+export const copyCheckItemResult = (data: CopyCheckItemResultType) => {
+  return request('/manager/CheckItem/copyBatchCheckItemResult', {
+    method: 'POST',
+    data,
+  });
+};
+
+// 获取查核项分类详情
+export const getCategoryDetail = (categoryId: string) => {
+  return request<{
+    id: string;
+    name: string;
+    type: string;
+    score: number;
+    checkMethod: string;
+    isStatistics: boolean;
+    description: string;
+  }>('/manager/checkItem/getCategoryDetail', {
+    method: 'GET',
+    params: { categoryId },
+  });
+};
+
+// 获取查核要点列表数据(无分页)
+export const getCheckPointData = (params?: any) => {
+  return request<CheckPointItemType[]>('/manager/CheckItem/getCheckItemCheckpoint', {
+    method: 'GET',
+    params: {
+      checkItemId: '',
+      filter: '',
+      ...params,
+    },
+  });
+};
+
+// 新增查核要点
+export type AddCheckPointItemType = {
+  checkItemId: number; // 查核项ID
+  name: string; // 查核要点名称
+};
+
+export const addCheckPointData = (data: AddCheckPointItemType) => {
+  return request('/manager/CheckItem/addCheckItemCheckpoint', {
+    method: 'POST',
+    data,
+  });
+};
+
+// 编辑查核要点
+export type UpdateCheckPointItemType = {
+  id: number; // 查核要点ID
+  checkItemId: number; // 查核项ID
+  name: string; // 查核要点名称
+};
+
+export const editCheckPointData = (data: UpdateCheckPointItemType) => {
+  return request('/manager/CheckItem/updateCheckItemCheckpoint', {
+    method: 'POST',
+    data,
+  });
+};
+
+// 删除查核要点
+export const delCheckPointData = (ids: Array<string | number>) => {
+  return request('/manager/CheckItem/delCheckItemCheckpoint', {
+    method: 'POST',
+    data: ids,
+  });
+};
+
+// 获取待添加查核要点列表数据
+export const getItemPendingPageCheckpoint = (params?: any) => {
+  return request<{
+    totalCount: number;
+    pageSize: number;
+    totalPage: number;
+    currPage: number;
+    list: Array<{
+      id: string;
+      name: string;
+      pointCategory: number;
+      pointCategoryName: string;
+    }>;
+  }>('/manager/CheckItem/getItemPendingPageCheckpoint', {
+    method: 'GET',
+    params: {
+      current: 1,
+      pageSize: 10,
+      checkItemId: '',
+      filter: '',
+      ...params,
+    },
+  });
+};
+
+// 添加查核要点到查核项
+export type AddCheckItemCheckpointType = {
+  id: string; // 查核项ID
+  name: string; // 查核项名称
+  mapObjList: Array<{
+    id: string; // 查核要点ID
+    name: string; // 查核要点名称
+  }>;
+};
+
+export const addCheckItemCheckpoint = (data: AddCheckItemCheckpointType) => {
+  return request('/manager/CheckItem/addCheckItemCheckpoint', {
+    method: 'POST',
+    data,
+  });
+};

+ 446 - 0
src/pages/checkItemMana/style.less

@@ -0,0 +1,446 @@
+// 查核项管理页面样式(参考checkGroupMana)
+
+.CheckItemMana {
+  .check-item-container {
+    display: flex;
+    height: 100%;
+    padding: 16px;
+  }
+
+  // 左侧查核项树样式
+  .item-tree {
+    display: flex;
+    flex-direction: column;
+    width: 220px;
+    min-width: 220px;
+    max-width: 220px;
+    background: #fff;
+    border-radius: 4px;
+    height: calc(100vh - 80px);
+    overflow: hidden;
+
+    .search-wrapper {
+      display: flex;
+      align-items: center;
+      padding: 12px;
+      padding-bottom: 8px;
+      flex-shrink: 0;
+
+      .search-input {
+        flex: 1;
+        border-radius: 4px;
+        border: 1px solid #d9d9d9;
+
+        .ant-input {
+          border: none;
+          box-shadow: none;
+        }
+
+        &:hover,
+        &:focus-within {
+          border-color: #3377ff;
+        }
+      }
+
+      .add-button {
+        margin-left: 8px;
+        width: 24px;
+        height: 24px;
+        background: #fafcff;
+        border-radius: 4px;
+        border: 1px solid #dae2f2;
+        padding: 0;
+        display: flex;
+        align-items: center;
+        justify-content: center;
+      }
+    }
+
+    .item-list {
+      flex: 1;
+      overflow-y: auto;
+      max-height: calc(100vh - 140px);
+
+      .item-group {
+        margin-bottom: 0;
+
+        .item-leaf {
+          display: flex;
+          align-items: center;
+          padding: 10px 8px;
+          margin: 0 8px;
+          cursor: pointer;
+          height: auto;
+          box-sizing: border-box;
+          border-radius: 4px;
+          position: relative;
+
+          .item-icon {
+            margin-right: 4px;
+            flex-shrink: 0;
+            display: flex;
+            align-items: center;
+            justify-content: center;
+
+            & > img {
+              width: 25px;
+              height: 25px;
+            }
+          }
+
+          .item-content {
+            flex: 1;
+            min-width: 0;
+            display: flex;
+            flex-direction: column;
+
+            .item-title {
+              font-size: 14px;
+              height: 14px;
+              line-height: 14px;
+              color: #17181a;
+              overflow: hidden;
+              text-overflow: ellipsis;
+              white-space: nowrap;
+              margin-bottom: 4px;
+              font-weight: 500;
+            }
+
+            .item-score {
+              font-size: 12px;
+              height: 12px;
+              line-height: 12px;
+              color: #7a8599;
+            }
+          }
+
+          .more-button {
+            position: absolute;
+            top: 50%;
+            right: 8px;
+            transform: translateY(-50%);
+            padding: 0;
+            width: 16px;
+            height: 16px;
+            display: flex;
+            align-items: center;
+            justify-content: center;
+            color: #999;
+            border-radius: 4px;
+            background: #fff;
+            opacity: 0;
+            transition: opacity 0.2s;
+
+            .anticon {
+              transform: rotate(90deg);
+            }
+
+            &:hover {
+              background: #fff;
+            }
+          }
+
+          &:hover {
+            background-color: #f0f2f5;
+
+            .more-button {
+              opacity: 1;
+            }
+          }
+
+          &.selected {
+            background: #f0f2f5;
+            border: none;
+
+            .item-title {
+              color: #17181a;
+              font-weight: 500;
+            }
+
+            .more-button {
+              opacity: 1;
+            }
+          }
+        }
+      }
+    }
+  }
+
+  
+
+  // 右侧内容区样式
+  .main-content {
+    flex: 1;
+    margin-left: 16px;
+    height: 100%;
+    overflow: auto;
+    background: #fff;
+    border-radius: 4px;
+
+    // 顶部标题和信息区域
+    .content-header {
+      padding: 16px;
+      display: flex;
+      justify-content: space-between;
+      align-items: flex-start;
+
+      .header-left {
+        flex: 1;
+
+        .title {
+          font-size: 20px;
+          height: auto;
+          max-height: 40px;
+          line-height: 20px;
+          font-weight: 600;
+          color: #111827;
+          margin-bottom: 8px;
+          overflow: hidden;
+          text-overflow: ellipsis;
+          display: -webkit-box;
+          -webkit-line-clamp: 2;
+          -webkit-box-orient: vertical;
+          word-wrap: break-word;
+        }
+
+        .description {
+          font-size: 12px;
+          color: #7a8599;
+          line-height: 16px;
+          margin-bottom: 12px;
+          padding-right: 32px;
+        }
+
+        .info-tags {
+          display: flex;
+          flex-wrap: wrap;
+          gap: 12px;
+
+          .info-tag {
+            display: flex;
+            align-items: center;
+            padding: 8px;
+            padding-right: 12px;
+            background: #f5f7fa;
+            border-radius: 4px;
+            white-space: nowrap;
+
+            .tag-icon {
+              display: flex;
+              align-items: center;
+              justify-content: center;
+              width: 16px;
+              height: 16px;
+              margin-right: 8px;
+
+              img {
+                width: 100%;
+                height: 100%;
+                object-fit: contain;
+              }
+            }
+
+            .tag-content {
+              display: flex;
+              flex-direction: column;
+              gap: 2px;
+
+              .tag-label {
+                font-size: 12px;
+                color: #7a8599;
+                line-height: 1;
+                margin-bottom: 4px;
+              }
+
+              .tag-value {
+                font-size: 16px;
+                color: #17181a;
+                font-weight: 500;
+                line-height: 1;
+              }
+            }
+          }
+        }
+      }
+
+      .header-right {
+        .image-gallery {
+          display: flex;
+          gap: 12px;
+
+          .image-item {
+            width: 140px;
+            height: 100px;
+            border-radius: 8px;
+            overflow: hidden;
+
+            img {
+              width: 100%;
+              height: 100%;
+              object-fit: cover;
+            }
+          }
+        }
+      }
+    }
+
+    // 内容主体
+    .content-body {
+      .pfm-ant-card {
+        box-shadow: none;
+        border-radius: 4px;
+
+        .pfm-ant-card-body {
+          padding: 16px;
+          padding-top: 0;
+        }
+      }
+
+      .pfm-ant-tabs-nav {
+        margin-bottom: 16px;
+      }
+
+      .tab-content {
+        .toolbar {
+          display: flex;
+          justify-content: space-between;
+          align-items: center;
+          margin-bottom: 16px;
+        }
+
+        .add-btn {
+          background-color: #3377ff;
+          border-color: #3377ff;
+          border-radius: 4px;
+          height: 24px;
+          box-shadow: none;
+          font-weight: 400;
+          font-size: 14px;
+          color: #ffffff;
+          width: 56px;
+          padding: 0;
+          line-height: 22px;
+
+          &:hover {
+            background-color: #5591ff;
+            border-color: #5591ff;
+          }
+
+          &:active {
+            background-color: #1b5ee6;
+            border-color: #1b5ee6;
+          }
+        }
+      }
+    }
+  }
+
+  // 新增查核项弹窗样式
+  .add-check-item-modal {
+    .ant-modal-body {
+      max-height: 60vh;
+      overflow-y: auto;
+    }
+  }
+}
+
+// 全局弹窗滚动样式
+.add-check-item-modal {
+  .ant-modal-body {
+    max-height: 60vh !important;
+    overflow-y: auto !important;
+  }
+}
+
+// 查核项管理更多操作下拉菜单样式
+.check-item-dropdown {
+  .pfm-ant-dropdown-menu,
+  .ant-dropdown-menu {
+    border-radius: 4px;
+    overflow: hidden;
+    padding: 4px;
+  }
+
+  .pfm-ant-dropdown-menu-item,
+  .ant-dropdown-menu-item {
+    &:hover,
+    &.pfm-ant-dropdown-menu-item-selected,
+    &.ant-dropdown-menu-item-selected {
+      background: rgba(51, 119, 255, 0.08);
+      color: #17181a;
+      border-radius: 4px;
+    }
+  }
+}
+
+// 复制设置弹窗样式
+.copy-setting-modal {
+  .pfm-ant-modal-header,
+  .ant-modal-header {
+    border-bottom: 1px solid #f0f0f0;
+    padding: 12px 16px;
+
+    .pfm-ant-modal-title,
+    .ant-modal-title {
+      font-size: 14px;
+      font-weight: 500;
+      color: #333;
+    }
+  }
+
+  .pfm-ant-modal-close,
+  .ant-modal-close {
+    top: 12px;
+    right: 16px;
+    display: flex;
+    align-items: center;
+    justify-content: center;
+    height: 22px;
+    width: 22px;
+  }
+
+  .pfm-ant-modal-body,
+  .ant-modal-body {
+    padding: 16px;
+    padding-bottom: 8px;
+  }
+
+  .pfm-ant-modal-footer,
+  .ant-modal-footer {
+    border-top: 1px solid #f0f0f0;
+    padding: 12px 16px;
+  }
+
+  // 单选框样式调整
+  .pfm-ant-radio-checked .pfm-ant-radio-inner,
+  .ant-radio-checked .ant-radio-inner {
+    background-color: #3377ff;
+    border-color: #3377ff;
+  }
+
+  .pfm-ant-radio-wrapper:hover .pfm-ant-radio-inner,
+  .pfm-ant-radio:hover .pfm-ant-radio-inner,
+  .pfm-ant-radio-input:focus + .pfm-ant-radio-inner,
+  .ant-radio-wrapper:hover .ant-radio-inner,
+  .ant-radio:hover .ant-radio-inner,
+  .ant-radio-input:focus + .ant-radio-inner {
+    border-color: #3377ff;
+  }
+
+  // 按钮样式调整
+  .pfm-ant-btn-primary,
+  .ant-btn-primary {
+    border-radius: 2px;
+
+    &:hover,
+    &:focus {
+      background-color: #5591ff;
+      border-color: #5591ff;
+    }
+
+    &:active {
+      background-color: #1b5ee6;
+      border-color: #1b5ee6;
+    }
+  }
+}

+ 107 - 0
src/pages/checkPointMana/CheckPointTree.tsx

@@ -0,0 +1,107 @@
+import React, { useState } from 'react';
+import { Button, Dropdown, Menu } from 'antd';
+import { DownOutlined, EllipsisOutlined } from '@ant-design/icons';
+
+// 树节点类型定义
+type TreeNode = {
+  id: string;
+  name: string;
+  children?: TreeNode[];
+};
+
+interface CheckPointTreeProps {
+  treeData: TreeNode[];
+  selectedKey: string | null;
+  onSelect: (key: string) => void;
+  onMoreAction?: (node: TreeNode) => void;
+}
+
+// 递归渲染树节点
+const renderTreeNodes = (
+  nodes: TreeNode[],
+  selectedKey: string | null,
+  onSelect: (key: string) => void,
+  onMoreAction?: (node: TreeNode) => void,
+) => {
+  return nodes.map((node) => {
+    const [expanded, setExpanded] = useState(true);
+    const hasChildren = node.children && node.children.length > 0;
+    return (
+      <div key={node.id} className="tree-group">
+        <div className="tree-group-title">
+          {hasChildren && (
+            <span
+              className="tree-group-arrow"
+              onClick={(e) => {
+                e.stopPropagation();
+                setExpanded(!expanded);
+              }}
+            >
+              <DownOutlined rotate={expanded ? 0 : -90} />
+            </span>
+          )}
+          <span
+            className={
+              selectedKey === node.id ? 'tree-item selected' : 'tree-item'
+            }
+            onClick={() => onSelect(node.id)}
+          >
+            {node.name}
+          </span>
+          <Dropdown
+            overlay={
+              <Menu>
+                <Menu.Item
+                  key="edit"
+                  onClick={() => onMoreAction && onMoreAction(node)}
+                >
+                  编辑
+                </Menu.Item>
+                <Menu.Item
+                  key="delete"
+                  onClick={() => onMoreAction && onMoreAction(node)}
+                >
+                  删除
+                </Menu.Item>
+              </Menu>
+            }
+            trigger={['click']}
+          >
+            <EllipsisOutlined
+              style={{ marginLeft: 8 }}
+              onClick={(e) => e.stopPropagation()}
+            />
+          </Dropdown>
+        </div>
+        {hasChildren && expanded && (
+          <div className="tree-group-children">
+            {renderTreeNodes(
+              node.children!,
+              selectedKey,
+              onSelect,
+              onMoreAction,
+            )}
+          </div>
+        )}
+      </div>
+    );
+  });
+};
+
+/**
+ * 查核要点左侧树形结构组件
+ */
+const CheckPointTree: React.FC<CheckPointTreeProps> = ({
+  treeData,
+  selectedKey,
+  onSelect,
+  onMoreAction,
+}) => {
+  return (
+    <div className="check-point-tree">
+      {renderTreeNodes(treeData, selectedKey, onSelect, onMoreAction)}
+    </div>
+  );
+};
+
+export default CheckPointTree;

+ 1979 - 0
src/pages/checkPointMana/index.tsx

@@ -0,0 +1,1979 @@
+/*
+ * @Description: 查核要点管理页面
+ */
+import React, { useEffect, useRef, useState } from 'react';
+import {
+  Input,
+  message,
+  Card,
+  Button,
+  Dropdown,
+  Menu,
+  Tabs,
+  Modal,
+  Popconfirm,
+  Select,
+  Table,
+} from 'antd';
+import { ActionType, ProColumns } from '@ant-design/pro-table';
+
+const { TabPane } = Tabs;
+import KCIMPagecontainer from '@/components/KCIMPageContainer';
+import { KCIMTable } from '@/components/KCIMTable';
+import {
+  ModalForm,
+  ProFormDigit,
+  ProFormText,
+  ProFormTextArea,
+  ProFormSelect,
+} from '@ant-design/pro-form';
+import { PlusOutlined, SearchOutlined, MoreOutlined } from '@ant-design/icons';
+import { createFromIconfontCN } from '@ant-design/icons';
+import './style.less';
+import {
+  getTreeData,
+  getTableData,
+  addTableData,
+  deleteTableData,
+  getPointCategoryList,
+  addCheckpoint,
+  updateCheckpoint,
+  deleteCheckpoint,
+  getCheckpointPendingDept,
+  addCheckpointDept,
+  delCheckpointDept,
+  getCheckpointGroup,
+  getCheckpointPendingGroup,
+  addCheckpointGroup,
+  delCheckpointGroup,
+  getCheckpointItem,
+  getCheckpointPendingItem,
+  addCheckpointItem,
+  delCheckpointItem,
+  getCheckpointRule,
+  getReviewRuleVersion,
+  getCheckpointPendingRule,
+  addCheckpointRule,
+  delCheckpointRule,
+} from './service';
+import { getExpandedSelectedNodes, handleTreeSelection } from '@/utils/tooljs';
+
+const IconFont = createFromIconfontCN({
+  scriptUrl: '',
+});
+
+// 添加查核组弹窗组件 - 移到主组件外部
+const AddCheckpointGroupModal: React.FC<{
+  selectedDept: any;
+  onSuccess: () => void;
+}> = ({ selectedDept, onSuccess }) => {
+  const [modalVisible, setModalVisible] = useState(false);
+  const [selectedGroupIds, setSelectedGroupIds] = useState<React.Key[]>([]);
+  const [searchKeyword, setSearchKeyword] = useState('');
+  const groupTableRef = useRef<ActionType>();
+
+  // 获取可添加的查核组列表
+  const getGroupSelectData = async (params: any) => {
+    if (!selectedDept?.id) {
+      return {
+        data: [],
+        success: true,
+        total: 0,
+      };
+    }
+
+    try {
+      const { current = 1, pageSize = 10 } = params;
+      const res = await getCheckpointPendingGroup(
+        selectedDept.id,
+        searchKeyword,
+        current,
+        pageSize,
+      );
+
+      return {
+        data: res.list,
+        success: res.success,
+        total: res.total,
+      };
+    } catch (error) {
+      return {
+        data: [],
+        success: false,
+        total: 0,
+      };
+    }
+  };
+
+  // 打开弹窗时重置状态
+  const handleOpenModal = () => {
+    setModalVisible(true);
+    setSelectedGroupIds([]);
+    setSearchKeyword('');
+
+    // 延迟触发表格重新加载
+    setTimeout(() => {
+      groupTableRef.current?.reload();
+    }, 100);
+  };
+
+  // 提交添加
+  const handleSubmit = async () => {
+    if (selectedGroupIds.length === 0) {
+      message.warning('请至少选择一个查核组');
+      return;
+    }
+
+    try {
+      await addCheckpointGroup(selectedDept.id, selectedGroupIds as number[]);
+      message.success(`已添加 ${selectedGroupIds.length} 个查核组`);
+      setModalVisible(false);
+      onSuccess(); // 调用成功回调
+    } catch (error) {
+      message.error('添加失败');
+    }
+  };
+
+  // 定义表格列
+  const groupSelectColumns: ProColumns[] = [
+    {
+      title: '查核组名称',
+      dataIndex: 'name',
+      align: 'left',
+      ellipsis: true,
+    },
+    {
+      title: '组长',
+      dataIndex: 'groupManagerName',
+      align: 'left',
+      width: 120,
+    },
+  ];
+
+  return (
+    <>
+      <Button
+        type="primary"
+        className="add-btn"
+        size="small"
+        onClick={handleOpenModal}
+      >
+        添加
+      </Button>
+
+      <Modal
+        title={`选择查核组(${selectedDept?.name || ''})`}
+        open={modalVisible}
+        onCancel={() => setModalVisible(false)}
+        width={352}
+        footer={[
+          <Button key="cancel" onClick={() => setModalVisible(false)}>
+            取消
+          </Button>,
+          <Button
+            key="submit"
+            type="primary"
+            onClick={handleSubmit}
+            disabled={selectedGroupIds.length === 0}
+            style={{
+              backgroundColor: '#3377FF',
+              borderColor: '#3377FF',
+              color: '#FFFFFF',
+            }}
+          >
+            确定
+            {selectedGroupIds.length > 0 ? ` (${selectedGroupIds.length})` : ''}
+          </Button>,
+        ]}
+        className="employee-select-modal"
+      >
+        <div style={{ marginBottom: 8 }}>
+          <Input
+            placeholder="请输入"
+            value={searchKeyword}
+            allowClear
+            onChange={(e) => {
+              setSearchKeyword(e.target.value);
+              if (e.target.value === '') {
+                // 清空搜索时自动刷新
+                groupTableRef.current?.reload();
+              }
+            }}
+            onPressEnter={() => groupTableRef.current?.reload()}
+            suffix={
+              <SearchOutlined
+                onClick={() => groupTableRef.current?.reload()}
+                style={{ cursor: 'pointer', color: '#99A6BF' }}
+              />
+            }
+            style={{ width: '100%' }}
+          />
+        </div>
+
+        <KCIMTable
+          actionRef={groupTableRef}
+          columns={groupSelectColumns}
+          request={getGroupSelectData}
+          rowKey="id"
+          search={false}
+          pagination={{
+            simple: true,
+            pageSize: 10,
+            size: 'small',
+            showTotal: () => null,
+          }}
+          rowSelection={{
+            selectedRowKeys: selectedGroupIds,
+            onChange: (selectedRowKeys) => {
+              setSelectedGroupIds(selectedRowKeys);
+            },
+          }}
+          tableAlertRender={false}
+          scroll={{ y: 360 }}
+        />
+      </Modal>
+    </>
+  );
+};
+
+// 添加查核项弹窗组件 - 移到主组件外部
+const AddCheckpointItemModal: React.FC<{
+  selectedDept: any;
+  onSuccess: () => void;
+}> = ({ selectedDept, onSuccess }) => {
+  const [modalVisible, setModalVisible] = useState(false);
+  const [selectedItemIds, setSelectedItemIds] = useState<React.Key[]>([]);
+  const [searchKeyword, setSearchKeyword] = useState('');
+  const itemTableRef = useRef<ActionType>();
+
+  // 获取可添加的查核项列表
+  const getItemSelectData = async (params: any) => {
+    if (!selectedDept?.id) {
+      return {
+        data: [],
+        success: true,
+        total: 0,
+      };
+    }
+
+    try {
+      const { current = 1, pageSize = 10 } = params;
+      const res = await getCheckpointPendingItem(
+        selectedDept.id,
+        searchKeyword,
+        current,
+        pageSize,
+      );
+
+      return {
+        data: res.list,
+        success: res.success,
+        total: res.total,
+      };
+    } catch (error) {
+      return {
+        data: [],
+        success: false,
+        total: 0,
+      };
+    }
+  };
+
+  // 打开弹窗时重置状态
+  const handleOpenModal = () => {
+    setModalVisible(true);
+    setSelectedItemIds([]);
+    setSearchKeyword('');
+
+    // 延迟触发表格重新加载
+    setTimeout(() => {
+      itemTableRef.current?.reload();
+    }, 100);
+  };
+
+  // 提交添加
+  const handleSubmit = async () => {
+    if (selectedItemIds.length === 0) {
+      message.warning('请至少选择一个查核项');
+      return;
+    }
+
+    try {
+      await addCheckpointItem(selectedDept.id, selectedItemIds as number[]);
+      message.success(`已添加 ${selectedItemIds.length} 个查核项`);
+      setModalVisible(false);
+      onSuccess(); // 调用成功回调
+    } catch (error) {
+      message.error('添加失败');
+    }
+  };
+
+  // 定义表格列
+  const itemSelectColumns: ProColumns[] = [
+    {
+      title: '查核项',
+      dataIndex: 'name',
+      align: 'left',
+      ellipsis: true,
+    },
+    {
+      title: '应知应会',
+      dataIndex: 'rightAnswerText',
+      align: 'left',
+      ellipsis: true,
+    },
+  ];
+
+  return (
+    <>
+      <Button
+        type="primary"
+        className="add-btn"
+        size="small"
+        onClick={handleOpenModal}
+      >
+        添加
+      </Button>
+
+      <Modal
+        title={`选择查核项(${selectedDept?.name || ''})`}
+        open={modalVisible}
+        onCancel={() => setModalVisible(false)}
+        width={632}
+        footer={[
+          <Button key="cancel" onClick={() => setModalVisible(false)}>
+            取消
+          </Button>,
+          <Button
+            key="submit"
+            type="primary"
+            onClick={handleSubmit}
+            disabled={selectedItemIds.length === 0}
+            style={{
+              backgroundColor: '#3377FF',
+              borderColor: '#3377FF',
+              color: '#FFFFFF',
+            }}
+          >
+            确定
+            {selectedItemIds.length > 0 ? ` (${selectedItemIds.length})` : ''}
+          </Button>,
+        ]}
+        className="employee-select-modal"
+      >
+        <div style={{ marginBottom: 8 }}>
+          <Input
+            placeholder="请输入"
+            value={searchKeyword}
+            allowClear
+            onChange={(e) => {
+              setSearchKeyword(e.target.value);
+              if (e.target.value === '') {
+                // 清空搜索时自动刷新
+                itemTableRef.current?.reload();
+              }
+            }}
+            onPressEnter={() => itemTableRef.current?.reload()}
+            suffix={
+              <SearchOutlined
+                onClick={() => itemTableRef.current?.reload()}
+                style={{ cursor: 'pointer', color: '#99A6BF' }}
+              />
+            }
+            style={{ width: '100%' }}
+          />
+        </div>
+
+        <KCIMTable
+          actionRef={itemTableRef}
+          columns={itemSelectColumns}
+          request={getItemSelectData}
+          rowKey="id"
+          search={false}
+          pagination={{
+            simple: true,
+            pageSize: 10,
+            size: 'small',
+            showTotal: () => null,
+          }}
+          rowSelection={{
+            selectedRowKeys: selectedItemIds,
+            onChange: (selectedRowKeys) => {
+              setSelectedItemIds(selectedRowKeys);
+            },
+          }}
+          tableAlertRender={false}
+          scroll={{ y: 360 }}
+        />
+      </Modal>
+    </>
+  );
+};
+
+// 添加条文条款弹窗组件
+const AddCheckpointRuleModal: React.FC<{
+  selectedDept: any;
+  onSuccess: () => void;
+}> = ({ selectedDept, onSuccess }) => {
+  const [modalVisible, setModalVisible] = useState(false);
+  const [selectedRuleIds, setSelectedRuleIds] = useState<React.Key[]>([]);
+  const [selectedRuleData, setSelectedRuleData] = useState<any[]>([]); // 存储完整的选中条文数据
+  const [prevSelectedRuleIds, setPrevSelectedRuleIds] = useState<React.Key[]>(
+    [],
+  ); // 存储之前的选中项
+  const [searchKeyword, setSearchKeyword] = useState('');
+  const [selectedVersion, setSelectedVersion] = useState<string>('');
+  const [versionOptions, setVersionOptions] = useState<any[]>([]);
+  const [allTableData, setAllTableData] = useState<any[]>([]); // 存储表格中的所有数据
+  const [modalExpandedRowKeys, setModalExpandedRowKeys] = useState<React.Key[]>(
+    [],
+  ); // 弹窗中的展开状态
+  const ruleTableRef = useRef<ActionType>();
+
+  // 获取版本选项数据
+  const fetchVersionOptions = async () => {
+    try {
+      const options = await getReviewRuleVersion();
+      setVersionOptions(options);
+      // 默认选择第一个版本
+      if (options.length > 0) {
+        setSelectedVersion(options[0].value);
+      }
+    } catch (error) {
+      message.error('获取版本选项失败');
+    }
+  };
+
+  // 获取可添加的条文条款列表
+  const getRuleSelectData = async (params: any) => {
+    if (!selectedDept?.id || !selectedVersion) {
+      return {
+        data: [],
+        success: true,
+        total: 0,
+      };
+    }
+
+    try {
+      const { current = 1, pageSize = 10 } = params;
+      const res = await getCheckpointPendingRule(
+        selectedDept.id,
+        selectedVersion,
+        searchKeyword,
+        current,
+        pageSize,
+      );
+
+      // 保存表格数据,用于父子节点处理
+      setAllTableData(res.list || []);
+
+      // 获取前5个顶层节点的展开键(只展开一级,不递归)
+      const getTop5ExpandableKeys = (data: any[]): React.Key[] => {
+        const keys: React.Key[] = [];
+        const topLevelItems = data.slice(0, 5); // 只取前5个
+        topLevelItems.forEach((item) => {
+          if (item.children && item.children.length > 0) {
+            keys.push(item.id);
+          }
+        });
+        return keys;
+      };
+
+      // 设置弹窗中的默认展开前5个顶层节点
+      const top5Keys = getTop5ExpandableKeys(res.list || []);
+      setModalExpandedRowKeys(top5Keys);
+
+      return {
+        data: res.list,
+        success: res.success,
+        total: res.total,
+      };
+    } catch (error) {
+      return {
+        data: [],
+        success: false,
+        total: 0,
+      };
+    }
+  };
+
+  // 加载条文条款数据
+  const loadRuleData = async () => {
+    if (!selectedDept?.id || !selectedVersion) {
+      setAllTableData([]);
+      return;
+    }
+
+    try {
+      const res = await getCheckpointPendingRule(
+        selectedDept.id,
+        selectedVersion,
+        searchKeyword,
+        1,
+        100, // 加载更多数据用于测试
+      );
+
+      setAllTableData(res.list || []);
+    } catch (error) {
+      setAllTableData([]);
+    }
+  };
+
+  // 打开弹窗时重置状态并获取版本数据
+  const handleOpenModal = async () => {
+    setModalVisible(true);
+    setSelectedRuleIds([]);
+    setSelectedRuleData([]);
+    setPrevSelectedRuleIds([]); // 清空之前的选中项
+    setSearchKeyword('');
+    setAllTableData([]); // 清空表格数据
+    setModalExpandedRowKeys([]); // 清空弹窗展开状态
+
+    // 获取版本选项
+    await fetchVersionOptions();
+
+    // 延迟触发表格加载,确保版本选择完成后能正确加载数据
+    setTimeout(() => {
+      ruleTableRef.current?.reload();
+    }, 200);
+  };
+
+  // 当版本选择后加载数据
+  useEffect(() => {
+    if (modalVisible && selectedVersion) {
+      // 延迟触发表格重新加载,确保组件已经渲染完成
+      setTimeout(() => {
+        ruleTableRef.current?.reload();
+      }, 100);
+    }
+  }, [modalVisible, selectedVersion, searchKeyword]);
+
+  // 版本变化时重新加载数据
+  const handleVersionChange = (version: string) => {
+    setSelectedVersion(version);
+    setSelectedRuleIds([]); // 清空选中项
+    setSelectedRuleData([]); // 清空选中数据
+    setPrevSelectedRuleIds([]); // 清空之前的选中项
+    setAllTableData([]); // 清空表格数据
+    setTimeout(() => {
+      ruleTableRef.current?.reload();
+    }, 100);
+  };
+
+  // 提交添加
+  const handleSubmit = async () => {
+    if (selectedRuleIds.length === 0) {
+      message.warning('请至少选择一个条文条款');
+      return;
+    }
+
+    if (!selectedVersion) {
+      message.warning('请选择版本');
+      return;
+    }
+
+    try {
+      // 使用工具函数获取展开后的节点(包含父节点选中时的所有子节点)
+      const expandedNodes = getExpandedSelectedNodes(
+        selectedRuleData,
+        allTableData,
+        'children',
+      );
+
+      // 使用numStr作为id传递给后端,去重处理
+      const uniqueRuleDataWithNumStr = Array.from(
+        new Set(expandedNodes.map((item) => item.numStr || item.id)),
+      );
+
+      await addCheckpointRule(
+        selectedDept.id,
+        uniqueRuleDataWithNumStr,
+        selectedVersion,
+      );
+      message.success(`已添加 ${uniqueRuleDataWithNumStr.length} 个条文条款`);
+      setModalVisible(false);
+      onSuccess(); // 调用成功回调
+    } catch (error) {
+      message.error('添加失败');
+    }
+  };
+
+  // 定义表格列
+  const ruleSelectColumns: ProColumns[] = [
+    {
+      title: '条文四码',
+      dataIndex: 'numStr',
+      width: 200,
+      render: (text: any) => text || '-',
+    },
+    {
+      title: '条文内容',
+      dataIndex: 'name',
+      ellipsis: true,
+    },
+    {
+      title: '版本',
+      dataIndex: 'version',
+      width: 120,
+      ellipsis: true,
+    },
+  ];
+
+  return (
+    <>
+      <Button
+        type="primary"
+        className="add-btn"
+        size="small"
+        onClick={handleOpenModal}
+      >
+        添加
+      </Button>
+
+      <Modal
+        title={`选择条文条款(${selectedDept?.name || ''})`}
+        open={modalVisible}
+        onCancel={() => setModalVisible(false)}
+        width={832}
+        footer={[
+          <Button key="cancel" onClick={() => setModalVisible(false)}>
+            取消
+          </Button>,
+          <Button
+            key="submit"
+            type="primary"
+            onClick={handleSubmit}
+            disabled={selectedRuleIds.length === 0}
+            style={{
+              backgroundColor: '#3377FF',
+              borderColor: '#3377FF',
+              color: '#FFFFFF',
+            }}
+          >
+            确定
+            {selectedRuleIds.length > 0 ? ` (${selectedRuleIds.length})` : ''}
+          </Button>,
+        ]}
+        className="employee-select-modal"
+      >
+        <div
+          style={{
+            marginBottom: 16,
+            display: 'flex',
+            gap: 8,
+            alignItems: 'center',
+          }}
+        >
+          <div style={{ display: 'flex', alignItems: 'center', gap: 8 }}>
+            <Select
+              value={selectedVersion}
+              onChange={handleVersionChange}
+              style={{ width: 120 }}
+              placeholder="请选择版本"
+              options={versionOptions}
+            />
+          </div>
+          <div style={{ flex: 1 }}>
+            <Input
+              placeholder="请输入条文内容"
+              value={searchKeyword}
+              allowClear
+              onChange={(e) => {
+                setSearchKeyword(e.target.value);
+                if (e.target.value === '') {
+                  // 清空搜索时自动刷新
+                  ruleTableRef.current?.reload();
+                }
+              }}
+              onPressEnter={() => ruleTableRef.current?.reload()}
+              suffix={
+                <SearchOutlined
+                  onClick={() => ruleTableRef.current?.reload()}
+                  style={{ cursor: 'pointer', color: '#99A6BF' }}
+                />
+              }
+              style={{ width: '100%' }}
+            />
+          </div>
+        </div>
+
+        <KCIMTable
+          actionRef={ruleTableRef}
+          columns={ruleSelectColumns}
+          request={getRuleSelectData}
+          rowKey="id"
+          search={false}
+          pagination={{
+            simple: true,
+            pageSize: 10,
+            size: 'small',
+            showTotal: () => null,
+          }}
+          rowSelection={{
+            selectedRowKeys: selectedRuleIds,
+            checkStrictly: false, // 启用父子关联选择
+            onChange: (selectedRowKeys, selectedRows) => {
+              // 使用Ant Design内置的父子关联功能
+              setSelectedRuleIds(selectedRowKeys);
+              setSelectedRuleData(selectedRows);
+            },
+          }}
+          tableAlertRender={false}
+          scroll={{ y: 360 }}
+          expandable={{
+            expandedRowKeys: modalExpandedRowKeys, // 受控的展开状态
+            onExpandedRowsChange: (expandedKeys) => {
+              setModalExpandedRowKeys([...expandedKeys]);
+            },
+            expandRowByClick: false, // 禁止点击行展开
+            childrenColumnName: 'children', // 指定子数据的字段名
+            indentSize: 20, // 设置缩进大小
+          }}
+        />
+      </Modal>
+    </>
+  );
+};
+
+// 主页面组件
+const CheckPointMana: React.FC = () => {
+  // 左侧分组/树数据
+  const [treeData, setTreeData] = useState<any[]>([]);
+  const [filteredTreeData, setFilteredTreeData] = useState<any[]>([]);
+  const [selectedDept, setSelectedDept] = useState<any>(null);
+  const [treeSearchKeywords, setTreeSearchKeywords] = useState<string>('');
+  const [hoveredNode, setHoveredNode] = useState<string | null>(null);
+  const [openDropdownNode, setOpenDropdownNode] = useState<string | null>(null);
+
+  // 分页加载相关状态
+  const [currentPage, setCurrentPage] = useState(1);
+  const [totalCount, setTotalCount] = useState(0);
+  const [loading, setLoading] = useState(false);
+  const [hasMore, setHasMore] = useState(true);
+
+  // 表格相关
+  const [searchKeywords, setSearchKeywords] = useState<string>('');
+  const [activeTab, setActiveTab] = useState('units'); // 当前活动标签页
+  const [selectedRowKeys, setSelectedRowKeys] = useState<string[]>([]); // 选中的行
+  const [pointCategoryOptions, setPointCategoryOptions] = useState<any[]>([]); // 要点分类选项
+  const tableRef = useRef<ActionType>();
+
+  // 查核组相关
+  const [groupSearchKeywords, setGroupSearchKeywords] = useState<string>('');
+  const [selectedGroupKeys, setSelectedGroupKeys] = useState<string[]>([]);
+  const groupTableRef = useRef<ActionType>();
+
+  // 查核项相关
+  const [itemSearchKeywords, setItemSearchKeywords] = useState<string>('');
+  const [selectedItemKeys, setSelectedItemKeys] = useState<string[]>([]);
+  const itemTableRef = useRef<ActionType>();
+
+  // 条文条款相关
+  const [ruleSearchKeywords, setRuleSearchKeywords] = useState<string>('');
+  const [selectedRuleKeys, setSelectedRuleKeys] = useState<string[]>([]);
+  const [selectedRuleData, setSelectedRuleData] = useState<any[]>([]); // 存储完整的选中条文数据
+  const [expandedRowKeys, setExpandedRowKeys] = useState<React.Key[]>([]); // 控制展开的行
+  const [allRuleTableData, setAllRuleTableData] = useState<any[]>([]); // 存储条文条款表格的全部数据
+  const ruleTableRef = useRef<ActionType>();
+
+  // 右侧内容区ref和操作栏定位状态
+  const deptContentRef = useRef<HTMLDivElement>(null);
+  const [actionBarStyle, setActionBarStyle] = useState<{
+    left: number;
+    width: number;
+  }>({ left: 0, width: 0 });
+
+  // 更新操作栏定位,支持ResizeObserver
+  const updateActionBarPosition = () => {
+    if (deptContentRef.current) {
+      const rect = deptContentRef.current.getBoundingClientRect();
+      setActionBarStyle({ left: rect.left, width: rect.width });
+    }
+  };
+
+  useEffect(() => {
+    updateActionBarPosition();
+    window.addEventListener('resize', updateActionBarPosition);
+    window.addEventListener('scroll', updateActionBarPosition, true);
+    let resizeObserver: ResizeObserver | null = null;
+    if (deptContentRef.current && typeof ResizeObserver !== 'undefined') {
+      resizeObserver = new ResizeObserver(() => {
+        updateActionBarPosition();
+      });
+      resizeObserver.observe(deptContentRef.current);
+    }
+    return () => {
+      window.removeEventListener('resize', updateActionBarPosition);
+      window.removeEventListener('scroll', updateActionBarPosition, true);
+      if (resizeObserver && deptContentRef.current) {
+        resizeObserver.unobserve(deptContentRef.current);
+        resizeObserver.disconnect();
+      }
+    };
+  }, []);
+
+  // 获取左侧分组/树数据
+  useEffect(() => {
+    fetchTreeData();
+    // 初始化时也获取分类数据,确保用户点击新增时有选项
+    fetchPointCategoryOptions();
+  }, []);
+
+  const fetchTreeData = async (
+    current: number = 1,
+    isLoadMore: boolean = false,
+    keyword?: string,
+    pageSize: number = 50,
+  ) => {
+    try {
+      setLoading(true);
+      // 如果传入了keyword参数,使用它;否则使用状态中的treeSearchKeywords
+      const searchKeyword =
+        keyword !== undefined ? keyword : treeSearchKeywords;
+      const res = await getTreeData(current, searchKeyword, pageSize);
+      if (res) {
+        // 转换数据格式,符合树形组件需要的格式,同时保留原始数据
+        const transformedData = res.list.map((item: any) => ({
+          title: item.name,
+          key: item.id,
+          type: 'checkPoint', // 标记为查核要点
+          // 保留原始数据
+          id: item.id,
+          name: item.name,
+          sort: item.sort,
+          pointCategory: item.pointCategory,
+        }));
+
+        if (isLoadMore) {
+          // 加载更多时,追加到现有数据
+          setTreeData((prev) => [...prev, ...transformedData]);
+          setFilteredTreeData((prev) => [...prev, ...transformedData]);
+        } else {
+          // 首次加载或搜索时,替换数据
+          setTreeData(transformedData);
+          setFilteredTreeData(transformedData);
+
+          // 默认选中第一个
+          if (transformedData.length > 0) {
+            setSelectedDept(transformedData[0]);
+          }
+        }
+
+        // 更新分页信息
+        setTotalCount(res.totalCount);
+        setCurrentPage(current);
+        setHasMore(res.list.length === pageSize && current * pageSize < res.totalCount);
+      }
+    } catch (error) {
+      message.error('获取要点分类失败');
+    } finally {
+      setLoading(false);
+    }
+  };
+
+  // 获取要点分类选项
+  const fetchPointCategoryOptions = async () => {
+    try {
+      const res = await getPointCategoryList();
+      setPointCategoryOptions(res);
+    } catch (error) {
+      message.error('获取要点分类选项失败');
+    }
+  };
+
+  // 监听选中项变化,刷新表格
+  useEffect(() => {
+    if (selectedDept?.key) {
+      tableRef.current?.reload();
+    }
+  }, [selectedDept?.key]);
+
+  // 树形数据搜索过滤
+  const filterTreeData = (data: any[], keywords: string) => {
+    if (!keywords.trim()) {
+      return data;
+    }
+    return data.filter((item) =>
+      item.title.toLowerCase().includes(keywords.toLowerCase()),
+    );
+  };
+
+  // 处理树形搜索 - 点击搜索图标或回车时触发
+  const handleTreeSearch = () => {
+    setCurrentPage(1);
+    setHasMore(true);
+    fetchTreeData(1, false);
+  };
+
+  // 处理搜索输入框的变化
+  const handleSearchInputChange = (value: string) => {
+    setTreeSearchKeywords(value);
+    // 如果清空搜索,立即重新加载数据,传入空字符串确保清空搜索
+    if (!value.trim()) {
+      setCurrentPage(1);
+      setHasMore(true);
+      // 直接传入空字符串,而不是依赖状态更新
+      fetchTreeData(1, false, '', 50);
+    }
+  };
+
+  // 加载更多数据
+  const loadMoreData = () => {
+    if (!loading && hasMore) {
+      const nextPage = currentPage + 1;
+      fetchTreeData(nextPage, true, undefined, 50);
+    }
+  };
+
+  // 滚动事件处理
+  const handleScroll = (e: React.UIEvent<HTMLDivElement>) => {
+    const { scrollTop, scrollHeight, clientHeight } = e.currentTarget;
+    // 当滚动到底部附近时加载更多数据
+    if (scrollHeight - scrollTop <= clientHeight + 10) {
+      loadMoreData();
+    }
+  };
+
+  // 新增查核要点弹窗组件
+  const AddCheckPointModal = () => {
+    return (
+      <ModalForm
+        title="新增查核要点"
+        width={400}
+        trigger={<Button icon={<PlusOutlined />} className="add-button" />}
+        onFinish={async (val: any) => {
+          try {
+            await addCheckpoint(val);
+            message.success('新增成功!');
+            fetchTreeData(1, false); // 重新获取数据
+            return true;
+          } catch (error) {
+            message.error('新增失败');
+            return false;
+          }
+        }}
+        modalProps={{
+          destroyOnClose: true,
+          maskClosable: false,
+        }}
+      >
+        <ProFormText
+          name="name"
+          label="查核要点"
+          placeholder="请输入"
+          rules={[{ required: true, message: '查核要点不能为空!' }]}
+        />
+        <ProFormDigit
+          name="sort"
+          label="序号"
+          placeholder="请输入序号"
+          min={0}
+          rules={[{ required: true, message: '序号不能为空!' }]}
+        />
+        <ProFormSelect
+          name="pointCategory"
+          label="要点分类"
+          placeholder="请选择"
+          options={pointCategoryOptions}
+          rules={[{ required: true, message: '要点分类不能为空!' }]}
+        />
+      </ModalForm>
+    );
+  };
+
+  // 更多操作菜单
+  const getMoreMenu = (node: any) => (
+    <Menu>
+      <Menu.Item key="edit">
+        <ModalForm
+          title="编辑查核要点"
+          width={400}
+          initialValues={{
+            name: node.name,
+            sort: node.sort,
+            pointCategory: node.pointCategory,
+          }}
+          trigger={<span>编辑</span>}
+          onFinish={async (val: any) => {
+            try {
+              await updateCheckpoint({
+                id: node.id,
+                name: val.name,
+                sort: val.sort,
+                pointCategory: val.pointCategory,
+              });
+              message.success('编辑成功!');
+              fetchTreeData(1, false);
+              return true;
+            } catch (error) {
+              message.error('编辑失败');
+              return false;
+            }
+          }}
+          modalProps={{ destroyOnClose: true }}
+        >
+          <ProFormText
+            name="name"
+            label="查核要点"
+            placeholder="请输入"
+            rules={[{ required: true, message: '查核要点不能为空!' }]}
+          />
+          <ProFormDigit
+            name="sort"
+            label="序号"
+            placeholder="请输入序号"
+            min={0}
+            rules={[{ required: true, message: '序号不能为空!' }]}
+          />
+          <ProFormSelect
+            name="pointCategory"
+            label="要点分类"
+            placeholder="请选择"
+            options={pointCategoryOptions}
+            rules={[{ required: true, message: '要点分类不能为空!' }]}
+          />
+        </ModalForm>
+      </Menu.Item>
+      <Menu.Item key="delete">
+        <a
+          onClick={() => {
+            // 删除确认
+            const modal = Modal.confirm({
+              title: '确认删除',
+              content: `确定要删除查核要点"${node.name}"吗?`,
+              okText: '确定',
+              cancelText: '取消',
+              onOk: async () => {
+                try {
+                  await deleteCheckpoint(node.id);
+                  message.success('删除成功!');
+                  fetchTreeData(1, false);
+                } catch (error) {
+                  message.error('删除失败');
+                }
+              },
+            });
+          }}
+        >
+          删除
+        </a>
+      </Menu.Item>
+    </Menu>
+  );
+
+  // 添加查核单位弹窗组件
+  const AddCheckpointDeptModal = () => {
+    const [modalVisible, setModalVisible] = useState(false);
+    const [selectedDeptIds, setSelectedDeptIds] = useState<React.Key[]>([]);
+    const [searchKeyword, setSearchKeyword] = useState('');
+    const deptTableRef = useRef<ActionType>();
+
+    // 获取可添加的查核单位列表
+    const getDeptSelectData = async (params: any) => {
+      if (!selectedDept?.id) {
+        return {
+          data: [],
+          success: true,
+          total: 0,
+        };
+      }
+
+      try {
+        const { current = 1, pageSize = 10 } = params;
+        const res = await getCheckpointPendingDept(
+          selectedDept.id,
+          searchKeyword,
+          current,
+          pageSize,
+        );
+
+        return {
+          data: res.list,
+          success: res.success,
+          total: res.total,
+        };
+      } catch (error) {
+        return {
+          data: [],
+          success: false,
+          total: 0,
+        };
+      }
+    };
+
+    // 打开弹窗时重置状态
+    const handleOpenModal = () => {
+      setModalVisible(true);
+      setSelectedDeptIds([]);
+      setSearchKeyword('');
+
+      // 延迟触发表格重新加载
+      setTimeout(() => {
+        deptTableRef.current?.reload();
+      }, 100);
+    };
+
+    // 提交添加
+    const handleSubmit = async () => {
+      if (selectedDeptIds.length === 0) {
+        message.warning('请至少选择一个查核单位');
+        return;
+      }
+
+      try {
+        await addCheckpointDept(selectedDept.id, selectedDeptIds as number[]);
+        message.success(`已添加 ${selectedDeptIds.length} 个查核单位`);
+        setModalVisible(false);
+        tableRef.current?.reload(); // 刷新表格
+      } catch (error) {
+        message.error('添加失败');
+      }
+    };
+
+    // 定义表格列
+    const deptSelectColumns: ProColumns[] = [
+      {
+        title: '查核单位名称',
+        dataIndex: 'name',
+        align: 'left',
+        ellipsis: true,
+      },
+      {
+        title: '单位负责人',
+        dataIndex: 'deptManagerName',
+        align: 'left',
+        width: 120,
+      },
+    ];
+
+    return (
+      <>
+        <Button
+          type="primary"
+          className="add-btn"
+          size="small"
+          onClick={handleOpenModal}
+        >
+          添加
+        </Button>
+
+        <Modal
+          title={`选择查核单位(${selectedDept?.name || ''})`}
+          open={modalVisible}
+          onCancel={() => setModalVisible(false)}
+          width={352}
+          footer={[
+            <Button key="cancel" onClick={() => setModalVisible(false)}>
+              取消
+            </Button>,
+            <Button
+              key="submit"
+              type="primary"
+              onClick={handleSubmit}
+              disabled={selectedDeptIds.length === 0}
+              style={{
+                backgroundColor: '#3377FF',
+                borderColor: '#3377FF',
+                color: '#FFFFFF',
+              }}
+            >
+              确定
+              {selectedDeptIds.length > 0 ? ` (${selectedDeptIds.length})` : ''}
+            </Button>,
+          ]}
+          className="employee-select-modal"
+        >
+          <div style={{ marginBottom: 8 }}>
+            <Input
+              placeholder="请输入"
+              value={searchKeyword}
+              allowClear
+              onChange={(e) => {
+                setSearchKeyword(e.target.value);
+                if (e.target.value === '') {
+                  // 清空搜索时自动刷新
+                  deptTableRef.current?.reload();
+                }
+              }}
+              onPressEnter={() => deptTableRef.current?.reload()}
+              suffix={
+                <SearchOutlined
+                  onClick={() => deptTableRef.current?.reload()}
+                  style={{ cursor: 'pointer', color: '#99A6BF' }}
+                />
+              }
+              style={{ width: '100%' }}
+            />
+          </div>
+
+          <KCIMTable
+            actionRef={deptTableRef}
+            columns={deptSelectColumns}
+            request={getDeptSelectData}
+            rowKey="id"
+            search={false}
+            pagination={{
+              simple: true,
+              pageSize: 10,
+              size: 'small',
+              showTotal: () => null,
+            }}
+            rowSelection={{
+              selectedRowKeys: selectedDeptIds,
+              onChange: (selectedRowKeys) => {
+                setSelectedDeptIds(selectedRowKeys);
+              },
+            }}
+            tableAlertRender={false}
+            scroll={{ y: 360 }}
+          />
+        </Modal>
+      </>
+    );
+  };
+
+  // 左侧树形组件渲染
+  const renderDeptTree = () => {
+    return (
+      <div className="dept-tree">
+        <div className="search-wrapper">
+          <Input
+            placeholder="要点名称"
+            suffix={
+              <SearchOutlined
+                style={{ color: '#99A6BF', cursor: 'pointer' }}
+                onClick={handleTreeSearch}
+              />
+            }
+            className="search-input"
+            value={treeSearchKeywords}
+            onChange={(e) => handleSearchInputChange(e.target.value)}
+            onPressEnter={handleTreeSearch}
+            allowClear
+          />
+          <AddCheckPointModal />
+        </div>
+
+        <div className="dept-list" onScroll={handleScroll}>
+          {filteredTreeData.map((item) => (
+            <div key={item.key} className="dept-group">
+              <div
+                className={`dept-leaf ${
+                  selectedDept?.key === item.key ? 'selected' : ''
+                }`}
+                onClick={() => {
+                  setSelectedDept(item);
+                }}
+                onMouseEnter={() => setHoveredNode(item.key)}
+                onMouseLeave={() => setHoveredNode(null)}
+              >
+                <span>{item.title}</span>
+                {(selectedDept?.key === item.key ||
+                  hoveredNode === item.key ||
+                  openDropdownNode === item.key) && (
+                  <Dropdown
+                    overlay={getMoreMenu(item)}
+                    trigger={['click']}
+                    placement="bottomRight"
+                    overlayClassName="check-unit-add-dropdown"
+                    onVisibleChange={(visible) => {
+                      if (visible) {
+                        setOpenDropdownNode(item.key);
+                      } else {
+                        setOpenDropdownNode(null);
+                      }
+                    }}
+                  >
+                    <Button
+                      type="text"
+                      icon={<MoreOutlined />}
+                      className="more-button"
+                      onClick={(e) => e.stopPropagation()}
+                    />
+                  </Dropdown>
+                )}
+              </div>
+            </div>
+          ))}
+
+          {/* 加载更多提示 */}
+          {loading && (
+            <div className="loading-more">
+              <span>加载中...</span>
+            </div>
+          )}
+
+          {!hasMore && filteredTreeData.length > 0 && (
+            <div className="no-more-data">
+              <span>已加载全部数据(共{totalCount}条)</span>
+            </div>
+          )}
+        </div>
+      </div>
+    );
+  };
+
+  // 处理标签页切换
+  const handleTabChange = (key: string) => {
+    setActiveTab(key);
+    setSelectedRowKeys([]); // 切换标签页时清空选中项
+    setSelectedGroupKeys([]); // 切换标签页时清空查核组选中项
+    setSelectedItemKeys([]); // 切换标签页时清空查核项选中项
+    setSelectedRuleKeys([]); // 切换标签页时清空条文条款选中项
+    setSelectedRuleData([]); // 切换标签页时清空条文条款选中数据
+    setAllRuleTableData([]); // 切换标签页时清空条文条款全部数据
+  };
+
+  // 查核单位表格列定义
+  const deptColumns: ProColumns[] = [
+    {
+      title: '查核单位名称',
+      dataIndex: 'deptName',
+      width: 200,
+      ellipsis: true,
+    },
+    {
+      title: '单位负责人',
+      dataIndex: 'deptManagerName',
+      width: 150,
+      ellipsis: true,
+    },
+    {
+      title: '操作',
+      key: 'option',
+      width: 100,
+      valueType: 'option',
+      render: (_: any, record: any) => [
+        <Popconfirm
+          title="确认删除该查核单位吗?"
+          onConfirm={async () => {
+            try {
+              await delCheckpointDept([record.id]);
+              message.success('删除成功!');
+              tableRef.current?.reload();
+            } catch (error) {
+              message.error('删除失败');
+            }
+          }}
+          okText="确定"
+          cancelText="取消"
+        >
+          <a>删除</a>
+        </Popconfirm>,
+      ],
+    },
+  ];
+
+  // 查核组表格列定义
+  const groupColumns: ProColumns[] = [
+    {
+      title: '查核组',
+      dataIndex: 'checkGroupName',
+      width: 200,
+      ellipsis: true,
+    },
+    {
+      title: '组长',
+      dataIndex: 'groupManagerName',
+      width: 150,
+      ellipsis: true,
+    },
+    {
+      title: '操作',
+      key: 'option',
+      width: 100,
+      valueType: 'option',
+      render: (_: any, record: any) => [
+        <Popconfirm
+          title="确认删除该查核组吗?"
+          onConfirm={async () => {
+            try {
+              await delCheckpointGroup([record.id]);
+              message.success('删除成功!');
+              groupTableRef.current?.reload();
+            } catch (error) {
+              message.error('删除失败');
+            }
+          }}
+          okText="确定"
+          cancelText="取消"
+          key="delete"
+        >
+          <a>删除</a>
+        </Popconfirm>,
+      ],
+    },
+  ];
+
+  // 查核项表格列定义
+  const itemColumns: ProColumns[] = [
+    {
+      title: '查核项',
+      dataIndex: 'checkItemName',
+      width: 200,
+      ellipsis: true,
+    },
+    {
+      title: '应知应会',
+      dataIndex: 'rightAnswerText',
+      width: 300,
+      ellipsis: true,
+    },
+    {
+      title: '操作',
+      key: 'option',
+      width: 100,
+      valueType: 'option',
+      render: (_: any, record: any) => [
+        <Popconfirm
+          title="确认删除该查核项吗?"
+          onConfirm={async () => {
+            try {
+              await delCheckpointItem([record.id]);
+              message.success('删除成功!');
+              itemTableRef.current?.reload();
+            } catch (error) {
+              message.error('删除失败');
+            }
+          }}
+          okText="确定"
+          cancelText="取消"
+          key="delete"
+        >
+          <a>删除</a>
+        </Popconfirm>,
+      ],
+    },
+  ];
+
+  // 条文条款表格列定义
+  const ruleColumns: ProColumns[] = [
+    {
+      title: '条文四码',
+      dataIndex: 'numStr',
+      width: 200,
+      render: (text: any) => text || '-',
+    },
+    {
+      title: '条文内容',
+      dataIndex: 'name',
+      ellipsis: true,
+    },
+    {
+      title: '版本',
+      dataIndex: 'version',
+      width: 120,
+      ellipsis: true,
+    },
+    {
+      title: '操作',
+      key: 'option',
+      width: 100,
+      valueType: 'option',
+      render: (_: any, record: any) => [
+        <Popconfirm
+          title="确认删除该条文条款吗?删除父节点时会同时删除所有子节点"
+          onConfirm={async () => {
+            try {
+              // 使用工具函数获取当前节点及其所有子节点
+              const expandedNodes = getExpandedSelectedNodes(
+                [record], // 当前选中的节点
+                allRuleTableData, // 全部表格数据
+                'children', // 子节点字段名
+              );
+
+              // 使用numStr作为id传递给后端,去重处理
+              const uniqueRuleDataWithNumStr = Array.from(
+                new Set(expandedNodes.map((item) => item.numStr || item.id)),
+              );
+
+              // 构造删除参数
+              const ruleData = uniqueRuleDataWithNumStr.map((id) => {
+                const node = expandedNodes.find((item) => (item.numStr || item.id) === id);
+                return {
+                  id: id,
+                  version: node?.version || '',
+                };
+              });
+
+              await delCheckpointRule(selectedDept.id, ruleData);
+              message.success(`删除成功!共删除 ${ruleData.length} 个条文条款`);
+              ruleTableRef.current?.reload();
+            } catch (error) {
+              message.error('删除失败');
+            }
+          }}
+          okText="确定"
+          cancelText="取消"
+          key="delete"
+        >
+          <a>删除</a>
+        </Popconfirm>,
+      ],
+    },
+  ];
+
+  return (
+    <KCIMPagecontainer title={false}>
+      <div className="check-unit-mana">
+        <div className="check-unit-container">
+          {/* 左侧要点分类树 */}
+          {renderDeptTree()}
+
+          {/* 右侧内容区 */}
+          <div className="dept-content" ref={deptContentRef}>
+            {selectedDept && (
+              <Card className="pfm-ant-card" bordered={false}>
+                <Tabs activeKey={activeTab} onChange={handleTabChange}>
+                  <TabPane tab="查核单位" key="units">
+                    <div className="tab-content">
+                      <div className="toolbar">
+                        <div
+                          style={{
+                            display: 'flex',
+                            alignItems: 'center',
+                            gap: 8,
+                          }}
+                        >
+                          <span style={{ color: '#17181A', fontSize: 14 }}>
+                            检索:
+                          </span>
+                          <Input
+                            placeholder="查核单位名称"
+                            allowClear
+                            suffix={
+                              <SearchOutlined style={{ color: '#99A6BF' }} />
+                            }
+                            style={{ width: 250 }}
+                            value={searchKeywords}
+                            onChange={(e) => {
+                              setSearchKeywords(e.target.value);
+                              if (e.target.value === '') {
+                                tableRef.current?.reload();
+                              }
+                            }}
+                            onPressEnter={(e) => {
+                              tableRef.current?.reload();
+                            }}
+                          />
+                        </div>
+                        <AddCheckpointDeptModal />
+                      </div>
+
+                      <KCIMTable
+                        columns={deptColumns as ProColumns[]}
+                        actionRef={tableRef}
+                        rowKey="id"
+                        params={{
+                          parentId: selectedDept?.key,
+                          filter: searchKeywords,
+                        }}
+                        request={async (params) => {
+                          try {
+                            const result = await getTableData(params);
+                            return result;
+                          } catch (error) {
+                            return {
+                              data: [],
+                              total: 0,
+                              success: false,
+                            };
+                          }
+                        }}
+                        search={false}
+                        options={false}
+                        pagination={false}
+                        rowSelection={{
+                          selectedRowKeys,
+                          onChange: (keys) =>
+                            setSelectedRowKeys(keys as string[]),
+                        }}
+                        tableAlertRender={false}
+                      />
+                    </div>
+                  </TabPane>
+
+                  <TabPane tab="查核组" key="groups">
+                    <div className="tab-content">
+                      <div className="toolbar">
+                        <div
+                          style={{
+                            display: 'flex',
+                            alignItems: 'center',
+                            gap: 8,
+                          }}
+                        >
+                          <span style={{ color: '#17181A', fontSize: 14 }}>
+                            检索:
+                          </span>
+                          <Input
+                            placeholder="查核组名称"
+                            allowClear
+                            suffix={
+                              <SearchOutlined style={{ color: '#99A6BF' }} />
+                            }
+                            style={{ width: 250 }}
+                            value={groupSearchKeywords}
+                            onChange={(e) => {
+                              setGroupSearchKeywords(e.target.value);
+                              if (e.target.value === '') {
+                                groupTableRef.current?.reload();
+                              }
+                            }}
+                            onPressEnter={(e) => {
+                              groupTableRef.current?.reload();
+                            }}
+                          />
+                        </div>
+                        <AddCheckpointGroupModal
+                          selectedDept={selectedDept}
+                          onSuccess={() => groupTableRef.current?.reload()}
+                        />
+                      </div>
+
+                      <KCIMTable
+                        columns={groupColumns as ProColumns[]}
+                        actionRef={groupTableRef}
+                        rowKey="id"
+                        params={{
+                          parentId: selectedDept?.key,
+                          filter: groupSearchKeywords,
+                        }}
+                        request={async (params) => {
+                          try {
+                            const result = await getCheckpointGroup(params);
+                            return result;
+                          } catch (error) {
+                            return {
+                              data: [],
+                              total: 0,
+                              success: false,
+                            };
+                          }
+                        }}
+                        search={false}
+                        options={false}
+                        pagination={false}
+                        rowSelection={{
+                          selectedRowKeys: selectedGroupKeys,
+                          onChange: (keys) =>
+                            setSelectedGroupKeys(keys as string[]),
+                        }}
+                        tableAlertRender={false}
+                      />
+                    </div>
+                  </TabPane>
+
+                  <TabPane tab="查核项" key="items">
+                    <div className="tab-content">
+                      <div className="toolbar">
+                        <div
+                          style={{
+                            display: 'flex',
+                            alignItems: 'center',
+                            gap: 8,
+                          }}
+                        >
+                          <span style={{ color: '#17181A', fontSize: 14 }}>
+                            检索:
+                          </span>
+                          <Input
+                            placeholder="查核项名称"
+                            allowClear
+                            suffix={
+                              <SearchOutlined style={{ color: '#99A6BF' }} />
+                            }
+                            style={{ width: 250 }}
+                            value={itemSearchKeywords}
+                            onChange={(e) => {
+                              setItemSearchKeywords(e.target.value);
+                              if (e.target.value === '') {
+                                itemTableRef.current?.reload();
+                              }
+                            }}
+                            onPressEnter={(e) => {
+                              itemTableRef.current?.reload();
+                            }}
+                          />
+                        </div>
+                        <AddCheckpointItemModal
+                          selectedDept={selectedDept}
+                          onSuccess={() => itemTableRef.current?.reload()}
+                        />
+                      </div>
+
+                      <KCIMTable
+                        columns={itemColumns as ProColumns[]}
+                        actionRef={itemTableRef}
+                        rowKey="id"
+                        params={{
+                          parentId: selectedDept?.key,
+                          filter: itemSearchKeywords,
+                        }}
+                        request={async (params) => {
+                          try {
+                            const result = await getCheckpointItem(params);
+                            return result;
+                          } catch (error) {
+                            return {
+                              data: [],
+                              total: 0,
+                              success: false,
+                            };
+                          }
+                        }}
+                        search={false}
+                        options={false}
+                        pagination={false}
+                        rowSelection={{
+                          selectedRowKeys: selectedItemKeys,
+                          onChange: (keys) =>
+                            setSelectedItemKeys(keys as string[]),
+                        }}
+                        tableAlertRender={false}
+                      />
+                    </div>
+                  </TabPane>
+
+                  <TabPane tab="条文条款" key="clauses">
+                    <div className="tab-content">
+                      <div className="toolbar">
+                        <div
+                          style={{
+                            display: 'flex',
+                            alignItems: 'center',
+                            gap: 8,
+                          }}
+                        >
+                          <span style={{ color: '#17181A', fontSize: 14 }}>
+                            检索:
+                          </span>
+                          <Input
+                            placeholder="条文条款"
+                            allowClear
+                            suffix={
+                              <SearchOutlined style={{ color: '#99A6BF' }} />
+                            }
+                            style={{ width: 250 }}
+                            value={ruleSearchKeywords}
+                            onChange={(e) => {
+                              setRuleSearchKeywords(e.target.value);
+                              if (e.target.value === '') {
+                                ruleTableRef.current?.reload();
+                              }
+                            }}
+                            onPressEnter={(e) => {
+                              ruleTableRef.current?.reload();
+                            }}
+                          />
+                        </div>
+                        <AddCheckpointRuleModal
+                          selectedDept={selectedDept}
+                          onSuccess={() => ruleTableRef.current?.reload()}
+                        />
+                      </div>
+
+                      <KCIMTable
+                        columns={ruleColumns as ProColumns[]}
+                        actionRef={ruleTableRef}
+                        rowKey="id"
+                        params={{
+                          parentId: selectedDept?.key,
+                          filter: ruleSearchKeywords,
+                        }}
+                        request={async (params) => {
+                          try {
+                            const result = await getCheckpointRule(params);
+                            
+                            // 保存全部数据,用于单个删除时的父子节点处理
+                            setAllRuleTableData(result.data || []);
+                            
+                            // 获取前5个顶层节点的展开键(只展开一级,不递归)
+                            const getTop5ExpandableKeys = (
+                              data: any[],
+                            ): React.Key[] => {
+                              const keys: React.Key[] = [];
+                              const topLevelItems = data.slice(0, 5); // 只取前5个
+                              topLevelItems.forEach((item) => {
+                                if (item.children && item.children.length > 0) {
+                                  keys.push(item.id);
+                                }
+                              });
+                              return keys;
+                            };
+
+                            // 设置默认展开前5个顶层节点
+                            const top5Keys = getTop5ExpandableKeys(
+                              result.data || [],
+                            );
+                            setExpandedRowKeys(top5Keys);
+
+                            return result;
+                          } catch (error) {
+                            return {
+                              data: [],
+                              total: 0,
+                              success: false,
+                            };
+                          }
+                        }}
+                        search={false}
+                        options={false}
+                        pagination={false}
+                        rowSelection={{
+                          selectedRowKeys: selectedRuleKeys,
+                          checkStrictly: false, // 启用父子关联选择
+                          onChange: (keys, selectedRows) => {
+                            setSelectedRuleKeys(keys as string[]);
+                            setSelectedRuleData(selectedRows); // 存储完整的选中行数据
+                          },
+                        }}
+                        tableAlertRender={false}
+                        expandable={{
+                          expandedRowKeys: expandedRowKeys, // 受控的展开状态
+                          onExpandedRowsChange: (expandedKeys) => {
+                            setExpandedRowKeys([...expandedKeys]);
+                          },
+                          expandRowByClick: false, // 禁止点击行展开
+                          childrenColumnName: 'children', // 指定子数据的字段名
+                          indentSize: 20, // 设置缩进大小
+                        }}
+                      />
+                    </div>
+                  </TabPane>
+                </Tabs>
+              </Card>
+            )}
+
+            {!selectedDept && (
+              <div className="empty-state">
+                <div className="empty-content">
+                  <div className="empty-text">请选择左侧要点分类查看详情</div>
+                </div>
+              </div>
+            )}
+          </div>
+        </div>
+      </div>
+
+      {/* 批量操作栏 - 固定在页面底部,宽度跟dept-content一致 */}
+      {((activeTab === 'units' && selectedRowKeys.length > 0) ||
+        (activeTab === 'groups' && selectedGroupKeys.length > 0) ||
+        (activeTab === 'items' && selectedItemKeys.length > 0) ||
+        (activeTab === 'clauses' && selectedRuleKeys.length > 0)) && (
+        <div
+          style={{
+            position: 'fixed',
+            left: actionBarStyle.left + 16,
+            width: actionBarStyle.width - 16,
+            bottom: 16,
+            height: 56,
+            backgroundColor: '#fff',
+            zIndex: 1000,
+            boxShadow: 'rgba(0, 0, 0, 0.04) 0px -2px 8px',
+            borderRadius: 4,
+            display: 'flex',
+            alignItems: 'center',
+            justifyContent: 'space-between',
+            padding: '0 16px',
+            transition: 'left 0.2s, width 0.2s',
+          }}
+        >
+          <span style={{ color: '#3377FF', fontWeight: 500 }}>
+            已选{' '}
+            {activeTab === 'units'
+              ? selectedRowKeys.length
+              : activeTab === 'groups'
+              ? selectedGroupKeys.length
+              : activeTab === 'items'
+              ? selectedItemKeys.length
+              : selectedRuleKeys.length}{' '}
+            项
+          </span>
+          <div style={{ display: 'flex', gap: 8 }}>
+            <Button
+              onClick={() => {
+                if (activeTab === 'units') {
+                  setSelectedRowKeys([]);
+                } else if (activeTab === 'groups') {
+                  setSelectedGroupKeys([]);
+                } else if (activeTab === 'items') {
+                  setSelectedItemKeys([]);
+                } else if (activeTab === 'clauses') {
+                  setSelectedRuleKeys([]);
+                  setSelectedRuleData([]);
+                }
+              }}
+              style={{ height: 32, borderRadius: 4, fontSize: 14 }}
+              size="small"
+            >
+              取消选择
+            </Button>
+            <Popconfirm
+              title={`确认批量删除所选${
+                activeTab === 'units'
+                  ? '查核单位'
+                  : activeTab === 'groups'
+                  ? '查核组'
+                  : activeTab === 'items'
+                  ? '查核项'
+                  : '条文条款'
+              }?`}
+              okText="确定"
+              cancelText="取消"
+              onConfirm={async () => {
+                try {
+                  if (activeTab === 'units') {
+                    await delCheckpointDept(selectedRowKeys);
+                    message.success('批量删除成功');
+                    setSelectedRowKeys([]);
+                    tableRef.current?.reload();
+                  } else if (activeTab === 'groups') {
+                    await delCheckpointGroup(selectedGroupKeys);
+                    message.success('批量删除成功');
+                    setSelectedGroupKeys([]);
+                    groupTableRef.current?.reload();
+                  } else if (activeTab === 'items') {
+                    await delCheckpointItem(selectedItemKeys);
+                    message.success('批量删除成功');
+                    setSelectedItemKeys([]);
+                    itemTableRef.current?.reload();
+                  } else if (activeTab === 'clauses') {
+                    // 使用numStr作为id,从完整数据中获取版本信息
+                    const ruleData = selectedRuleData.map((item) => ({
+                      id: item.numStr || item.id,
+                      version: item.version || '',
+                    }));
+                    await delCheckpointRule(selectedDept.id, ruleData);
+                    message.success('批量删除成功');
+                    setSelectedRuleKeys([]);
+                    setSelectedRuleData([]);
+                    ruleTableRef.current?.reload();
+                  }
+                } catch (e) {
+                  message.error('批量删除失败');
+                }
+              }}
+            >
+              <Button
+                type="primary"
+                style={{
+                  background: '#3377FF',
+                  borderColor: '#3377FF',
+                  height: 32,
+                  borderRadius: 4,
+                  fontSize: 14,
+                }}
+                size="small"
+              >
+                批量删除
+              </Button>
+            </Popconfirm>
+          </div>
+        </div>
+      )}
+    </KCIMPagecontainer>
+  );
+};
+
+export default CheckPointMana;

+ 658 - 0
src/pages/checkPointMana/service.ts

@@ -0,0 +1,658 @@
+// 查核要点管理相关接口
+import { request } from 'umi';
+
+// 获取左侧树形菜单数据(查核要点列表)
+export async function getTreeData(current: number = 1, filter: string = '', pageSize: number = 50) {
+  try {
+    const res = await request('/manager/Checkpoint/getCheckpointList', {
+      method: 'GET',
+      params: {
+        current,
+        pageSize,
+        filter,
+      },
+    });
+
+    if (res && res.list) {
+      return {
+        list: res.list,
+        totalCount: res.totalCount || res.list.length,
+        success: true,
+      };
+    } else if (res && Array.isArray(res)) {
+      // 如果直接返回数组
+      return {
+        list: res,
+        totalCount: res.length,
+        success: true,
+      };
+    } else {
+      return {
+        list: [],
+        totalCount: 0,
+        success: false,
+      };
+    }
+  } catch (error) {
+    throw error;
+  }
+}
+
+// 获取要点分类选项
+export async function getPointCategoryList() {
+  try {
+    const res = await request('/manager/Checkpoint/getPointCategoryList', {
+      method: 'GET',
+    });
+
+    let dataArray: any[] = [];
+
+    // 处理不同的数据结构
+    if (Array.isArray(res)) {
+      dataArray = res;
+    } else if (res && res.data && Array.isArray(res.data)) {
+      dataArray = res.data;
+    } else if (res && res.list && Array.isArray(res.list)) {
+      dataArray = res.list;
+    }
+
+    // 根据接口字段说明转换数据
+    const options = dataArray.map((item: any) => ({
+      label: item.itemName,
+      value: item.id,
+    }));
+
+    return options;
+  } catch (error) {
+    throw error;
+  }
+}
+
+// 获取右侧表格数据 - 查核单位
+export async function getTableData(params: any) {
+  try {
+    const { current = 1, pageSize = 10, parentId, filter = '' } = params;
+
+    const res = await request('/manager/Checkpoint/getCheckpointDept', {
+      method: 'GET',
+      params: {
+        checkpointId: parentId || '',
+        filter: filter,
+      },
+    });
+
+    // 由于接口返回的就是data数组,直接使用
+    let dataArray = [];
+    let total = 0;
+
+    // 兼容不同的数据结构
+    if (Array.isArray(res)) {
+      // 如果直接是数组
+      dataArray = res;
+      total = res.length;
+    } else if (res && res.data && Array.isArray(res.data)) {
+      // 如果是 {data: [...]}
+      dataArray = res.data;
+      total = res.total || res.data.length;
+    } else if (res && res.list && Array.isArray(res.list)) {
+      // 如果是 {list: [...]}
+      dataArray = res.list;
+      total = res.total || res.list.length;
+    }
+
+    return {
+      data: dataArray,
+      success: true,
+      total: total,
+    };
+  } catch (error) {
+    throw error;
+  }
+}
+
+// 添加表格数据
+export async function addTableData(data: any) {
+  return { success: true };
+}
+
+// 删除表格数据
+export async function deleteTableData(id: string) {
+  return { success: true };
+}
+
+// 新增查核要点
+export async function addCheckpoint(data: any) {
+  try {
+    const res = await request('/manager/Checkpoint/addCheckpoint', {
+      method: 'POST',
+      data: {
+        name: data.name,
+        sort: data.sort,
+        pointCategory: data.pointCategory,
+      },
+    });
+
+    return res;
+  } catch (error) {
+    throw error;
+  }
+}
+
+// 编辑查核要点
+export async function updateCheckpoint(data: any) {
+  try {
+    const res = await request('/manager/Checkpoint/updateCheckpoint', {
+      method: 'POST',
+      data: {
+        id: data.id,
+        name: data.name,
+        sort: data.sort,
+        pointCategory: data.pointCategory,
+      },
+    });
+
+    return res;
+  } catch (error) {
+    throw error;
+  }
+}
+
+// 删除查核要点
+export async function deleteCheckpoint(id: number) {
+  try {
+    const res = await request('/manager/Checkpoint/deleteCheckpoint', {
+      method: 'POST',
+      data: { id },
+    });
+
+    return res;
+  } catch (error) {
+    throw error;
+  }
+}
+
+// 获取可添加的查核单位列表
+export async function getCheckpointPendingDept(
+  checkpointId: number,
+  filter: string = '',
+  current: number = 1,
+  pageSize: number = 10,
+) {
+  try {
+    const res = await request('/manager/Checkpoint/getCheckpointPendingDept', {
+      method: 'GET',
+      params: {
+        checkpointId,
+        filter,
+        current,
+        pageSize,
+      },
+    });
+
+    // 由于接口返回的就是data数组,直接使用
+    let dataArray = [];
+    let total = 0;
+
+    // 兼容不同的数据结构
+    if (Array.isArray(res)) {
+      // 如果直接是数组
+      dataArray = res;
+      total = res.length;
+    } else if (res && res.data && Array.isArray(res.data)) {
+      // 如果是 {data: [...]}
+      dataArray = res.data;
+      total = res.total || res.data.length;
+    } else if (res && res.list && Array.isArray(res.list)) {
+      // 如果是 {list: [...]}
+      dataArray = res.list;
+      total = res.total || res.list.length;
+    }
+
+    return {
+      list: dataArray,
+      total: total,
+      success: true,
+    };
+  } catch (error) {
+    throw error;
+  }
+}
+
+// 添加查核单位
+export async function addCheckpointDept(
+  checkpointId: number,
+  deptIds: number[],
+) {
+  try {
+    const res = await request('/manager/Checkpoint/addCheckpointDept', {
+      method: 'POST',
+      data: {
+        id: checkpointId.toString(),
+        mapObjList: deptIds.map((id) => ({ id: id.toString(), type: '0' })),
+      },
+    });
+
+    return res;
+  } catch (error) {
+    throw error;
+  }
+}
+
+// 删除查核单位
+export async function delCheckpointDept(deptIds: React.Key[]) {
+  try {
+    const res = await request('/manager/Checkpoint/delCheckpointDept', {
+      method: 'POST',
+      data: deptIds,
+    });
+
+    return res;
+  } catch (error) {
+    throw error;
+  }
+}
+
+// 获取查核组数据
+export async function getCheckpointGroup(params: any) {
+  try {
+    const { current = 1, pageSize = 10, parentId, filter = '' } = params;
+
+    const res = await request('/manager/Checkpoint/getCheckpointGroup', {
+      method: 'GET',
+      params: {
+        checkpointId: parentId || '',
+        filter: filter,
+      },
+    });
+
+    // 由于接口返回的就是data数组,直接使用
+    return {
+      data: res || [],
+      success: true,
+      total: res ? res.length : 0,
+    };
+  } catch (error) {
+    throw error;
+  }
+}
+
+// 获取可添加的查核组列表
+export async function getCheckpointPendingGroup(
+  checkpointId: number,
+  filter: string = '',
+  current: number = 1,
+  pageSize: number = 10,
+) {
+  try {
+    const res = await request('/manager/Checkpoint/getCheckpointPendingGroup', {
+      method: 'GET',
+      params: {
+        checkpointId,
+        filter,
+        current,
+        pageSize,
+      },
+    });
+
+    // 由于接口返回的就是data数组,直接使用
+    return {
+      list: res || [],
+      total: res ? res.length : 0,
+      success: true,
+    };
+  } catch (error) {
+    throw error;
+  }
+}
+
+// 添加查核组
+export async function addCheckpointGroup(
+  checkpointId: number,
+  groupIds: number[],
+) {
+  try {
+    const res = await request('/manager/Checkpoint/addCheckpointGroup', {
+      method: 'POST',
+      data: {
+        id: checkpointId.toString(),
+        mapIdList: groupIds.map((id) => id.toString()),
+      },
+    });
+
+    return res;
+  } catch (error) {
+    throw error;
+  }
+}
+
+// 删除查核组
+export async function delCheckpointGroup(groupIds: React.Key[]) {
+  try {
+    const res = await request('/manager/Checkpoint/delCheckpointGroup', {
+      method: 'POST',
+      data: groupIds,
+    });
+
+    return res;
+  } catch (error) {
+    throw error;
+  }
+}
+
+// 获取查核项数据
+export async function getCheckpointItem(params: any) {
+  try {
+    const { current = 1, pageSize = 10, parentId, filter = '' } = params;
+
+    const res = await request('/manager/Checkpoint/getCheckpointItem', {
+      method: 'GET',
+      params: {
+        checkpointId: parentId || '',
+        filter: filter,
+      },
+    });
+
+    // 兼容不同的数据结构
+    let dataArray = [];
+    let total = 0;
+
+    if (Array.isArray(res)) {
+      // 如果直接是数组
+      dataArray = res;
+      total = res.length;
+    } else if (res && res.data && Array.isArray(res.data)) {
+      // 如果是 {data: [...]}
+      dataArray = res.data;
+      total = res.total || res.data.length;
+    } else if (res && res.list && Array.isArray(res.list)) {
+      // 如果是 {list: [...]}
+      dataArray = res.list;
+      total = res.total || res.list.length;
+    } else {
+      // 其他情况,可能是对象包装
+      dataArray = res ? [res] : [];
+      total = dataArray.length;
+    }
+
+    return {
+      data: dataArray,
+      success: true,
+      total: total,
+    };
+  } catch (error) {
+    throw error;
+  }
+}
+
+// 获取可添加的查核项列表
+export async function getCheckpointPendingItem(
+  checkpointId: number,
+  filter: string = '',
+  current: number = 1,
+  pageSize: number = 10,
+) {
+  try {
+    const res = await request('/manager/Checkpoint/getCheckpointPendingItem', {
+      method: 'GET',
+      params: {
+        checkpointId,
+        filter,
+        current,
+        pageSize,
+      },
+    });
+
+    // 兼容不同的数据结构
+    let dataArray = [];
+    let total = 0;
+
+    if (Array.isArray(res)) {
+      dataArray = res;
+      total = res.length;
+    } else if (res && res.data && Array.isArray(res.data)) {
+      dataArray = res.data;
+      total = res.total || res.data.length;
+    } else if (res && res.list && Array.isArray(res.list)) {
+      dataArray = res.list;
+      total = res.total || res.list.length;
+    }
+
+    return {
+      list: dataArray,
+      total: total,
+      success: true,
+    };
+  } catch (error) {
+    throw error;
+  }
+}
+
+// 添加查核项
+export async function addCheckpointItem(
+  checkpointId: number,
+  itemIds: number[],
+) {
+  try {
+    const res = await request('/manager/Checkpoint/addCheckpointItem', {
+      method: 'POST',
+      data: {
+        id: checkpointId.toString(),
+        mapIdList: itemIds.map((id) => id.toString()),
+      },
+    });
+
+    return res;
+  } catch (error) {
+    throw error;
+  }
+}
+
+// 删除查核项
+export async function delCheckpointItem(itemIds: React.Key[]) {
+  try {
+    const res = await request('/manager/Checkpoint/delCheckpointItem', {
+      method: 'POST',
+      data: itemIds,
+    });
+
+    return res;
+  } catch (error) {
+    throw error;
+  }
+}
+
+// 获取条文条款数据
+export async function getCheckpointRule(params: any) {
+  try {
+    const { current = 1, pageSize = 10, parentId, filter = '' } = params;
+
+    const res = await request('/manager/Checkpoint/getCheckpointRule', {
+      method: 'GET',
+      params: {
+        checkpointId: parentId || '',
+        filter: filter,
+      },
+    });
+
+    // 转换数据格式以适配 ProTable 的树形结构
+    const convertToTreeData = (items: any[]): any[] => {
+      if (!items || !Array.isArray(items)) return [];
+
+      return items.map((item) => {
+        const convertedItem = {
+          ...item,
+          key: item.id,
+        };
+
+        // 如果有子项,递归转换子项
+        if (item.child && Array.isArray(item.child) && item.child.length > 0) {
+          convertedItem.children = convertToTreeData(item.child);
+        }
+
+        return convertedItem;
+      });
+    };
+
+    // 处理数据
+    let dataArray = [];
+
+    if (res && res.data && res.data.items && Array.isArray(res.data.items)) {
+      dataArray = convertToTreeData(res.data.items);
+    } else if (res && res.items && Array.isArray(res.items)) {
+      dataArray = convertToTreeData(res.items);
+    } else if (Array.isArray(res)) {
+      dataArray = convertToTreeData(res);
+    }
+
+    return {
+      data: dataArray,
+      success: true,
+      total: dataArray.length,
+    };
+  } catch (error) {
+    throw error;
+  }
+}
+
+// 获取版本下拉数据
+export async function getReviewRuleVersion() {
+  try {
+    const res = await request('/manager/Checkpoint/getReviewRuleVersion', {
+      method: 'GET',
+    });
+
+    let dataArray: any[] = [];
+
+    // 处理不同的数据结构
+    if (Array.isArray(res)) {
+      dataArray = res;
+    } else if (res && res.data && Array.isArray(res.data)) {
+      dataArray = res.data;
+    } else if (res && res.list && Array.isArray(res.list)) {
+      dataArray = res.list;
+    }
+
+    // 转换为下拉选项格式
+    const options = dataArray.map((item: any) => ({
+      label: `${item.versionNo}`,
+      value: item.versionNo,
+    }));
+
+    return options;
+  } catch (error) {
+    throw error;
+  }
+}
+
+// 获取可添加的条文条款
+export async function getCheckpointPendingRule(
+  checkpointId: number,
+  version: string = '',
+  filter: string = '',
+  current: number = 1,
+  pageSize: number = 10,
+) {
+  try {
+    const res = await request('/manager/Checkpoint/getCheckpointPendingRule', {
+      method: 'GET',
+      params: {
+        checkpointId,
+        version,
+        filter,
+        page: current,
+        size: pageSize,
+      },
+    });
+
+    let dataArray: any[] = [];
+    let total = 0;
+
+    // 处理不同的数据结构
+    if (Array.isArray(res)) {
+      dataArray = res;
+      total = res.length;
+    } else if (res && res.data && Array.isArray(res.data)) {
+      dataArray = res.data;
+      total = res.total || res.data.length;
+    } else if (res && res.list && Array.isArray(res.list)) {
+      dataArray = res.list;
+      total = res.total || res.list.length;
+    }
+
+    // 转换数据格式以适配 ProTable 的树形结构
+    const convertToTreeData = (items: any[]): any[] => {
+      if (!items || !Array.isArray(items)) return [];
+
+      return items.map((item) => {
+        const convertedItem = {
+          ...item,
+          key: item.id,
+        };
+
+        // 如果有子项,递归转换子项
+        if (item.child && Array.isArray(item.child) && item.child.length > 0) {
+          convertedItem.children = convertToTreeData(item.child);
+        }
+
+        return convertedItem;
+      });
+    };
+
+    const treeData = convertToTreeData(dataArray);
+
+    return {
+      list: treeData,
+      success: true,
+      total: total,
+    };
+  } catch (error) {
+    throw error;
+  }
+}
+
+// 添加条文条款
+export async function addCheckpointRule(
+  checkpointId: number,
+  ruleNumStrs: string[],
+  version: string = '',
+) {
+  try {
+    const res = await request('/manager/Checkpoint/addCheckpointRule', {
+      method: 'POST',
+      data: {
+        id: checkpointId.toString(),
+        mapObjList: ruleNumStrs.map((numStr) => ({
+          id: numStr, // 使用numStr作为id
+          version: version,
+        })),
+      },
+    });
+
+    return res;
+  } catch (error) {
+    throw error;
+  }
+}
+
+// 删除条文条款
+export async function delCheckpointRule(
+  checkpointId: number,
+  ruleData: Array<{ id: React.Key; version?: string }>,
+) {
+  try {
+    const res = await request('/manager/Checkpoint/delCheckpointRule', {
+      method: 'POST',
+      data: {
+        id: checkpointId.toString(),
+        mapObjList: ruleData.map((item) => ({
+          id: item.id.toString(),
+          version: item.version || '',
+        })),
+      },
+    });
+
+    return res;
+  } catch (error) {
+    throw error;
+  }
+}

+ 580 - 0
src/pages/checkPointMana/style.less

@@ -0,0 +1,580 @@
+// 查核要点管理页面样式
+
+.check-unit-mana {
+  .check-unit-container {
+    display: flex;
+    height: 100%;
+    padding: 16px;
+  }
+
+  // 左侧科室树样式
+  .dept-tree {
+    display: flex;
+    flex-direction: column;
+    width: 220px;
+    min-width: 220px;
+    max-width: 220px;
+    background: #fff;
+    border-radius: 4px;
+    height: calc(100vh - 80px);
+    overflow: hidden;
+
+    .search-wrapper {
+      display: flex;
+      align-items: center;
+      padding: 12px;
+      padding-bottom: 8px;
+      flex-shrink: 0; // 防止搜索栏被压缩
+
+      .search-input {
+        flex: 1;
+        border-radius: 4px;
+        border: 1px solid #d9d9d9;
+
+        .ant-input {
+          border: none;
+          box-shadow: none;
+        }
+
+        &:hover,
+        &:focus-within {
+          border-color: #3377ff;
+        }
+      }
+
+      .add-button {
+        margin-left: 8px;
+        width: 24px;
+        height: 24px;
+        background: #fafcff;
+        border-radius: 4px;
+        border: 1px solid #dae2f2;
+        padding: 0;
+        display: flex;
+        align-items: center;
+        justify-content: center;
+      }
+    }
+
+    .dept-list {
+      flex: 1;
+      overflow-y: auto;
+      max-height: calc(100vh - 140px); // 限制最大高度,确保滚动可用
+
+      // 加载更多提示样式
+      .loading-more {
+        text-align: center;
+        padding: 12px;
+        color: #999;
+        font-size: 12px;
+
+        span {
+          color: #999;
+        }
+      }
+
+      .no-more-data {
+        text-align: center;
+        padding: 12px;
+        color: #999;
+        font-size: 12px;
+        border-top: 1px solid #f0f0f0;
+        margin-top: 8px;
+
+        span {
+          color: #999;
+        }
+      }
+
+      .dept-group {
+        margin-bottom: 0;
+
+        .dept-parent {
+          display: flex;
+          align-items: center;
+          padding: 6px 12px 6px 8px;
+          margin: 0 8px;
+          cursor: pointer;
+          height: 32px;
+          box-sizing: border-box;
+          border-radius: 4px;
+          justify-content: space-between;
+
+          .toggle-button {
+            padding: 0;
+            width: 16px;
+            height: 16px;
+            margin-right: 4px;
+            border: none;
+            line-height: 1;
+            display: flex;
+            align-items: center;
+            justify-content: center;
+            color: #999;
+            background: transparent;
+
+            .anticon {
+              font-size: 16px;
+            }
+
+            &:hover {
+              background: transparent;
+              color: #333;
+            }
+          }
+
+          .dept-title {
+            flex: 1;
+            font-size: 14px;
+            color: #333;
+            overflow: hidden;
+            text-overflow: ellipsis;
+            white-space: nowrap;
+            margin-right: 8px; // 给右侧按钮留出空间
+          }
+
+          .more-button {
+            padding: 0;
+            width: 16px;
+            height: 16px;
+            display: flex;
+            align-items: center;
+            justify-content: center;
+            color: #99a6bf;
+            border-radius: 4px;
+            background: #fff;
+
+            .anticon {
+              transform: rotate(90deg);
+            }
+
+            &:hover {
+              background: #fff;
+            }
+          }
+
+          &:hover {
+            background-color: #f0f2f5;
+          }
+
+          &.selected {
+            background: #f0f2f5;
+            border: none;
+
+            .dept-title {
+              color: #17181a;
+              font-weight: 500;
+            }
+          }
+        }
+
+        .dept-leaf {
+          display: flex;
+          align-items: center;
+          justify-content: space-between;
+          padding-left: 8px;
+          padding-right: 8px;
+          margin: 0 8px;
+          cursor: pointer;
+          height: 32px;
+          box-sizing: border-box;
+          border-radius: 4px;
+
+          > span {
+            flex: 1;
+            font-size: 14px;
+            color: #17181a; // 颜色稍浅,区别于父节点
+            overflow: hidden;
+            text-overflow: ellipsis;
+            white-space: nowrap;
+            margin-right: 8px; // 给右侧按钮留出空间
+          }
+
+          &:hover {
+            background-color: #f0f2f5;
+          }
+
+          &.selected {
+            background: #f0f2f5;
+            border: none;
+
+            > span {
+              color: #17181a;
+              font-weight: 500;
+            }
+          }
+
+          .more-button {
+            padding: 0;
+            width: 16px;
+            height: 16px;
+            display: flex;
+            align-items: center;
+            justify-content: center;
+            color: #99a6bf;
+            border-radius: 4px;
+            background: #fff;
+
+            .anticon {
+              transform: rotate(90deg);
+            }
+
+            &:hover {
+              background: #fff;
+            }
+          }
+        }
+
+        .dept-children {
+          padding: 4px 0;
+
+          .dept-item {
+            display: flex;
+            align-items: center;
+            justify-content: space-between;
+            padding: 0 12px 0 36px;
+            margin: 0 8px;
+            cursor: pointer;
+            height: 32px;
+            box-sizing: border-box;
+            border-radius: 4px;
+
+            > span {
+              flex: 1;
+              font-size: 14px;
+              color: #333;
+              overflow: hidden;
+              text-overflow: ellipsis;
+              white-space: nowrap;
+              margin-right: 8px; // 给右侧按钮留出空间
+            }
+
+            &:hover {
+              background-color: #f0f2f5;
+            }
+
+            &.selected {
+              background: #f0f2f5;
+              border: none;
+
+              > span {
+                color: #17181a;
+                font-weight: 500;
+              }
+            }
+
+            .more-button {
+              padding: 0;
+              width: 16px;
+              height: 16px;
+              display: flex;
+              align-items: center;
+              justify-content: center;
+              color: #999;
+              border-radius: 4px;
+              background: #fff;
+
+              .anticon {
+                transform: rotate(90deg);
+              }
+
+              &:hover {
+                background: #fff;
+              }
+            }
+          }
+        }
+
+        .dept-empty {
+          padding: 6px 10px 6px 30px;
+          margin: 0 8px;
+          color: #999;
+          font-size: 13px;
+        }
+      }
+
+      // 叶子节点样式(临床诊疗、后勤支援等)
+      .dept-leaf-node {
+        display: flex;
+        align-items: center;
+        padding: 6px 8px;
+        margin: 4px 8px;
+        cursor: pointer;
+        height: 32px;
+        box-sizing: border-box;
+        background: transparent;
+        border-radius: 0;
+
+        .leaf-button {
+          padding: 0;
+          width: 20px;
+          height: 20px;
+          margin-right: 8px;
+          border: none;
+          display: flex;
+          align-items: center;
+          justify-content: center;
+          color: #666;
+
+          .anticon {
+            font-size: 12px;
+          }
+        }
+
+        .leaf-title {
+          flex: 1;
+          font-size: 14px;
+          color: #333;
+          overflow: hidden;
+          text-overflow: ellipsis;
+          white-space: nowrap;
+          margin-right: 8px; // 给右侧按钮留出空间
+        }
+
+        &:hover {
+          background: #f5f5f5;
+        }
+      }
+    }
+  }
+
+  // 右侧内容区样式
+  .dept-content {
+    flex: 1;
+    padding-left: 16px;
+    height: 100%;
+    overflow: auto;
+
+    .pfm-ant-card {
+      box-shadow: none;
+      background: #fff;
+      border-radius: 4px;
+
+      .pfm-ant-card-body {
+        padding: 16px;
+        padding-top: 0;
+      }
+    }
+
+    .dept-header {
+      padding: 0 0 16px;
+      border-bottom: 1px solid #e6eaf2;
+      display: flex;
+      align-items: baseline;
+      gap: 8px;
+
+      .dept-title {
+        font-size: 20px;
+        font-weight: 600;
+        color: #17181a;
+      }
+
+      .dept-info {
+        font-size: 14px;
+        color: #515866;
+      }
+    }
+
+    .pfm-ant-tabs-nav {
+      margin-bottom: 16px;
+    }
+
+    .tab-content {
+      .toolbar {
+        display: flex;
+        justify-content: space-between;
+        align-items: center;
+        margin-bottom: 16px;
+      }
+
+      .add-btn {
+        background-color: #3377ff;
+        border-color: #3377ff;
+        border-radius: 4px;
+        height: 24px;
+        box-shadow: none;
+        font-weight: 400;
+        font-size: 14px;
+        color: #ffffff;
+        width: 56px;
+        padding: 0;
+        line-height: 22px;
+
+        &:hover {
+          background-color: #5591ff;
+          border-color: #5591ff;
+        }
+
+        &:active {
+          background-color: #1b5ee6;
+          border-color: #1b5ee6;
+        }
+      }
+    }
+
+    .empty-state {
+      height: calc(100vh - 96px);
+      display: flex;
+      align-items: center;
+      justify-content: center;
+
+      .empty-content {
+        text-align: center;
+
+        .empty-text {
+          font-size: 14px;
+          color: #8c8c8c;
+        }
+      }
+    }
+  }
+
+  // 表格样式
+  /* 表格样式已统一至 KCIMTable/style.less */
+
+  .delete-btn {
+    color: #3377ff;
+  }
+
+  // 员工选择相关样式
+  .employee-select-modal {
+    .pfm-ant-modal-header {
+      border-bottom: 1px solid #f0f0f0;
+      padding: 12px 16px;
+
+      .pfm-ant-modal-title {
+        font-size: 14px;
+        font-weight: 500;
+        color: #333;
+      }
+    }
+  }
+}
+
+// 覆盖 KCIMTable 的样式
+.check-point-mana-container {
+  .ant-pro-table {
+    .ant-pro-table-list-toolbar {
+      display: none; // 隐藏默认的工具栏
+    }
+
+    .ant-table-wrapper {
+      .ant-table {
+        .ant-table-thead > tr > th {
+          background: #fafafa;
+          font-weight: 600;
+        }
+
+        .ant-table-tbody > tr:hover > td {
+          background: #f5f5f5;
+        }
+      }
+    }
+  }
+}
+
+// 新增按钮样式优化
+.ant-btn-primary {
+  box-shadow: 0 2px 4px rgba(24, 144, 255, 0.2);
+
+  &:hover {
+    box-shadow: 0 4px 8px rgba(24, 144, 255, 0.3);
+  }
+}
+
+// 操作按钮样式
+.ant-table-tbody {
+  .ant-table-cell {
+    a {
+      margin-right: 12px;
+
+      &:last-child {
+        margin-right: 0;
+      }
+    }
+  }
+}
+
+.unitMana {
+  margin: 16px;
+  padding: 16px;
+  background: #ffffff;
+  border-radius: 4px;
+
+  .toolBar {
+    display: flex;
+    flex-direction: row;
+    justify-content: space-between;
+    align-items: center;
+
+    .filter {
+      display: flex;
+      flex-direction: row;
+      justify-content: flex-start;
+      align-items: center;
+
+      .filterItem {
+        display: flex;
+        flex-direction: row;
+        justify-content: center;
+        align-items: center;
+      }
+    }
+
+    .btnGroup {
+      .add {
+        cursor: pointer;
+        display: inline-block;
+        font-size: 14px;
+        font-family: SourceHanSansCN-Normal, SourceHanSansCN;
+        font-weight: 400;
+        color: #ffffff;
+        line-height: 24px;
+        padding: 0 14px;
+        background: #3377ff;
+        border-radius: 4px;
+      }
+    }
+  }
+}
+
+// 更多操作下拉菜单样式
+.check-unit-add-dropdown {
+  .pfm-ant-dropdown-menu {
+    border-radius: 4px;
+    overflow: hidden;
+    padding: 4px;
+  }
+
+  .pfm-ant-dropdown-menu-item {
+    &:hover,
+    &.pfm-ant-dropdown-menu-item-selected {
+      background: rgba(51, 119, 255, 0.08);
+      color: #17181a;
+      border-radius: 4px;
+    }
+  }
+}
+
+// 员工选择弹窗样式
+.employee-select-modal {
+  .ant-modal-footer {
+    .ant-btn-primary {
+      background-color: #3377ff !important;
+      border-color: #3377ff !important;
+      color: #ffffff !important;
+
+      &:hover {
+        background-color: #5591ff !important;
+        border-color: #5591ff !important;
+        color: #ffffff !important;
+      }
+
+      &:disabled {
+        background-color: #f5f5f5 !important;
+        border-color: #d9d9d9 !important;
+        color: rgba(0, 0, 0, 0.25) !important;
+      }
+    }
+  }
+}

+ 2498 - 0
src/pages/checkUnitMana/index.tsx

@@ -0,0 +1,2498 @@
+/*
+ * @Author: code4eat awesomedema@gmail.com
+ * @Date: 2025-06-10 12:15:25
+ * @LastEditors: code4eat awesomedema@gmail.com
+ * @LastEditTime: 2025-07-25 16:09:17
+ * @FilePath: /pfmBackMana/src/pages/checkUnitMana/index.tsx
+ * @Description: 查核单位管理
+ */
+
+import {
+  createFromIconfontCN,
+  SearchOutlined,
+  PlusOutlined,
+  MoreOutlined,
+  InboxOutlined,
+} from '@ant-design/icons';
+import {
+  ActionType,
+  ProColumns,
+  ModalForm,
+  ProFormText,
+  ProFormRadio,
+  ProFormUploadDragger,
+  ProFormDigit,
+} from '@ant-design/pro-components';
+import {
+  Button,
+  Input,
+  message,
+  Tabs,
+  Radio,
+  Empty,
+  Card,
+  Dropdown,
+  Menu,
+  Modal,
+  Select,
+  Checkbox,
+  Pagination,
+  Popconfirm,
+  Tooltip,
+} from 'antd';
+import React, { useRef, useState, useEffect } from 'react';
+import { request } from 'umi';
+
+import {
+  getDeptClassList,
+  addDeptClass,
+  addDepartmentByClass,
+  getDeptClassTree,
+  getDeptClassFilterConditionList,
+  applyDeptClassFilterCondition,
+  updateDeptClass,
+  delDeptClass,
+  updateDepartmentByClass,
+  delDepartmentByClass,
+  getDeptEmployee,
+  getDeptPendingEmployee,
+  addDeptEmployee,
+  setDeptManager,
+  delDeptEmp,
+  getDeptCheckpoint,
+  getDeptPendingCheckpoint,
+  addDeptCheckpoint,
+  delDeptCheckpoint,
+} from './service';
+
+import './style.less';
+
+import KCIMPagecontainer from '@/components/KCIMPageContainer';
+import { KCIMTable } from '@/components/KCIMTable';
+
+const IconFont = createFromIconfontCN({
+  scriptUrl: '',
+});
+
+const { TabPane } = Tabs;
+
+// 下载模板方法
+const downloadTemplate = async (type: number) => {
+  try {
+    // 获取token(如有)
+    let token = '';
+    const userDataStr = localStorage.getItem('userData');
+    if (userDataStr) {
+      try {
+        token = JSON.parse(userDataStr).token || '';
+      } catch (e) {
+        token = '';
+      }
+    }
+    const fetchOptions: any = { method: 'GET' };
+    if (token) {
+      fetchOptions.headers = { token };
+    }
+    const res = await fetch(`/gateway/manager/Department/exportPfmKnowledgeTemp?type=${type}`, fetchOptions);
+    if (!res.ok) {
+      message.error('下载模板失败');
+      return;
+    }
+    const blob = await res.blob();
+    // 尝试从header获取文件名
+    let fileName = '';
+    const disposition = res.headers.get('content-disposition');
+    if (disposition && disposition.indexOf('filename=') !== -1) {
+      fileName = decodeURIComponent(disposition.split('filename=')[1].replace(/"/g, ''));
+    } else {
+      fileName = type === 1 ? '系统追踪模板.xlsx' : '自查督查模板.xlsx';
+    }
+    const url = window.URL.createObjectURL(blob);
+    const a = document.createElement('a');
+    a.href = url;
+    a.download = fileName;
+    document.body.appendChild(a);
+    a.click();
+    a.remove();
+    window.URL.revokeObjectURL(url);
+  } catch (e) {
+    message.error('下载模板失败');
+  }
+};
+
+export default function CheckUnitMana() {
+  // 状态定义
+  const [selectedDept, setSelectedDept] = useState<any>(null);
+  const [deptInfo, setDeptInfo] = useState<string>('');
+  const [searchKeywords, setSearchKeywords] = useState<string>('');
+  const [personnelSearchInput, setPersonnelSearchInput] = useState<string>(''); // 人员搜索输入框的值
+  const [activeTab, setActiveTab] = useState('personnel');
+  const tableRef = useRef<ActionType>();
+  const conditionTableRef = useRef<ActionType>();
+
+  const [expandedKeys, setExpandedKeys] = useState<string[]>([]);
+  const [treeData, setTreeData] = useState<any[]>([]);
+  const [treeSearchKeywords, setTreeSearchKeywords] = useState<string>(''); // 树形搜索关键词
+  const [filteredTreeData, setFilteredTreeData] = useState<any[]>([]); // 过滤后的树形数据
+  const [hoveredNode, setHoveredNode] = useState<string | null>(null); // 当前悬停的节点
+  const [openDropdownNode, setOpenDropdownNode] = useState<string | null>(null); // 当前打开下拉菜单的节点
+
+  // 查核条件弹窗相关状态
+  const [conditionModalVisible, setConditionModalVisible] = useState(false); // 条件弹窗显示状态
+  const [conditionSearchType, setConditionSearchType] = useState('个案'); // 条件搜索类型
+  const [conditionSearchKeyword, setConditionSearchKeyword] = useState(''); // 条件搜索关键词
+  const [selectedConditions, setSelectedConditions] = useState<string[]>([]); // 已选择的条件ID列表
+  const [currentNode, setCurrentNode] = useState<any>(null); // 当前操作的节点
+
+  // 右侧内容区ref和操作栏定位状态
+  const cardRef = useRef<HTMLDivElement>(null);
+  const [actionBarStyle, setActionBarStyle] = useState<{
+    left: number;
+    width: number;
+  }>({ left: 0, width: 0 });
+
+  // 1. 新增弹窗相关状态
+  const [checkPointModalVisible, setCheckPointModalVisible] = useState(false);
+  const [checkPointTableRef, setCheckPointTableRef] = useState<any>(null);
+  const [selectedCheckPointKeys, setSelectedCheckPointKeys] = useState<
+    string[]
+  >([]);
+
+  // 记录当前页数据用于全选逻辑
+  const [currentCheckPointPageData, setCurrentCheckPointPageData] = useState<
+    any[]
+  >([]);
+
+  // 获取当前页面所有可选的要点ID(用于全选逻辑)
+  const getCurrentPageCheckPointIds = () => {
+    return currentCheckPointPageData
+      .filter((item) => item.rowSpan > 0) // 只选择有rowSpan的行(要点行)
+      .map((item) => item.id);
+  };
+
+  // 更新操作栏定位,支持ResizeObserver
+  const updateActionBarPosition = () => {
+    if (cardRef.current) {
+      const rect = cardRef.current.getBoundingClientRect();
+      setActionBarStyle({ left: rect.left, width: rect.width });
+    }
+  };
+
+  useEffect(() => {
+    updateActionBarPosition();
+    window.addEventListener('resize', updateActionBarPosition);
+    window.addEventListener('scroll', updateActionBarPosition, true);
+    let resizeObserver: ResizeObserver | null = null;
+    if (cardRef.current && typeof ResizeObserver !== 'undefined') {
+      resizeObserver = new ResizeObserver(() => {
+        updateActionBarPosition();
+      });
+      resizeObserver.observe(cardRef.current);
+    }
+    return () => {
+      window.removeEventListener('resize', updateActionBarPosition);
+      window.removeEventListener('scroll', updateActionBarPosition, true);
+      if (resizeObserver && cardRef.current) {
+        resizeObserver.unobserve(cardRef.current);
+        resizeObserver.disconnect();
+      }
+    };
+  }, []);
+
+  // 获取单位分类树
+  const fetchDeptClassList = async () => {
+    try {
+      const res = await getDeptClassTree();
+      // 尝试多种数据结构格式
+      let dataArray = null;
+
+      if (res && res.data && Array.isArray(res.data)) {
+        // 格式1: { data: [...] }
+        dataArray = res.data;
+      } else if (Array.isArray(res)) {
+        // 格式2: 直接返回数组
+        dataArray = res;
+      } else if (res && Array.isArray(res.items)) {
+        // 格式3: { items: [...] }
+        dataArray = res.items;
+      }
+
+      if (dataArray && dataArray.length > 0) {
+        const transformedData = dataArray.map((item: any) => {
+          // 处理子节点数据 - 只处理children字段,不处理filterConditionList
+          let children = [];
+
+          // 只使用 children 字段作为子节点
+          if (item.children && Array.isArray(item.children)) {
+            children = item.children.map((child: any) => ({
+              title: child.name,
+              key: child.id || child.code,
+              type: child.type || 'department', // 标记为查核单位
+              visitType: child.visitType,
+              deptType: child.deptType ?? 0, // 确保有默认值
+              sort: child.sort,
+              filterConditionList: child.filterConditionList || [], // 保留查核条件
+            }));
+          }
+          // 如果没有children,说明这是叶子节点,不需要处理filterConditionList作为子节点
+
+          return {
+            title: item.name,
+            key: item.id,
+            type: item.type, // 类型:1单位分类 2查核单位
+            visitType: item.visitType, // 诊别:1门诊 2急诊
+            deptType: item.deptType ?? 0, // 单位类型:0普通单位 1职能科室,确保有默认值
+            sort: item.sort,
+            children: children,
+            filterConditionList: item.filterConditionList || [], // 保留查核条件
+          };
+        });
+
+        setTreeData(transformedData);
+        setFilteredTreeData(transformedData); // 初始化过滤数据
+
+
+
+        // 默认展开所有父节点
+        setExpandedKeys(transformedData.map((item: any) => item.key));
+
+        // 默认选中逻辑:选择第一个叶子节点(没有子节点的节点本身就是叶子节点)
+        if (transformedData.length > 0) {
+          let defaultSelected = null;
+
+          // 遍历所有节点,找到第一个叶子节点
+          for (const parent of transformedData) {
+            if (parent.children && parent.children.length > 0) {
+              // 如果有子节点,选择第一个子节点
+              const firstChild = parent.children[0];
+              defaultSelected = {
+                ...firstChild,
+                parentTitle: parent.title,
+                filterConditionList: firstChild.filterConditionList || [],
+              };
+              break;
+            } else {
+              // 如果没有子节点,说明它本身就是叶子节点,直接选中
+              defaultSelected = {
+                ...parent,
+                parentTitle: '单位分类',
+                filterConditionList: parent.filterConditionList || [],
+              };
+              break;
+            }
+          }
+
+          setSelectedDept(defaultSelected);
+        }
+      } else {
+        message.error('获取单位分类树失败');
+      }
+    } catch (error) {
+      console.error('接口调用异常:', error);
+      message.error('获取单位分类树失败');
+    }
+  };
+
+  useEffect(() => {
+    fetchDeptClassList();
+  }, []);
+
+  // 监听selectedDept变化,加载人员数据
+  useEffect(() => {
+    if (selectedDept?.key) {
+      if (activeTab === 'personnel') {
+        tableRef.current?.reload();
+      } else if (activeTab === 'checkPoints') {
+        checkPointsTableRef.current?.reload();
+      }
+    }
+  }, [selectedDept?.key, activeTab]);
+
+  // 树形数据搜索过滤函数
+  const filterTreeData = (data: any[], keywords: string) => {
+    if (!keywords.trim()) {
+      return data;
+    }
+
+    const filtered = data
+      .map((parent) => {
+        // 检查父节点是否匹配
+        const parentMatches = parent.title
+          .toLowerCase()
+          .includes(keywords.toLowerCase());
+
+        // 过滤子节点
+        const filteredChildren = parent.children.filter((child: any) =>
+          child.title.toLowerCase().includes(keywords.toLowerCase()),
+        );
+
+        // 如果父节点匹配或有匹配的子节点,则保留该父节点
+        if (parentMatches || filteredChildren.length > 0) {
+          return {
+            ...parent,
+            children: parentMatches ? parent.children : filteredChildren, // 如果父节点匹配,显示所有子节点;否则只显示匹配的子节点
+          };
+        }
+
+        return null;
+      })
+      .filter(Boolean); // 移除 null 值
+
+    return filtered;
+  };
+
+  // 处理树形搜索
+  const handleTreeSearch = (value: string) => {
+    setTreeSearchKeywords(value);
+    const filtered = filterTreeData(treeData, value);
+    setFilteredTreeData(filtered);
+
+    // 如果有搜索关键词,自动展开所有匹配的父节点
+    if (value.trim()) {
+      const expandKeys = filtered.map((item: any) => item.key);
+      setExpandedKeys(expandKeys);
+    } else {
+      // 如果清空搜索,恢复默认展开状态
+      setExpandedKeys(treeData.map((item: any) => item.key));
+    }
+  };
+
+  // 获取人员数据
+  const getPersonnelData = async (params: any) => {
+    try {
+      // 如果没有选中的部门,返回空数据
+      if (!selectedDept?.key) {
+        return {
+          data: [],
+          success: true,
+          total: 0,
+        };
+      }
+
+      // 调用真实API获取人员列表(不传filter参数,获取全部数据)
+      const res = await getDeptEmployee({
+        deptId: Number(selectedDept.key), // 当前选中的查核单位ID
+        filter: '', // 不传搜索关键词,获取全部数据
+        deptType: selectedDept.deptType ?? 0, // 单位类型,使用空值合并操作符,默认为0
+      });
+
+      // 尝试多种数据结构格式
+      let dataArray: any[] | null = null;
+
+      if (res) {
+        if (Array.isArray(res.data)) {
+          dataArray = res.data; // { data: [...] }
+        } else if (Array.isArray(res.items)) {
+          dataArray = res.items; // { items: [...] }
+        } else if (res.data && Array.isArray(res.data.items)) {
+          dataArray = res.data.items; // { data: { items: [...] } }
+        } else if (Array.isArray(res)) {
+          dataArray = res; // 直接返回数组
+        }
+      }
+
+      if (dataArray && dataArray.length >= 0) {
+        // 保留接口字段,不做映射,仅把 id 转成字符串,避免 rowKey 警告
+        let formattedData = dataArray.map((item: any) => ({
+          ...item,
+          id: String(item.id),
+        }));
+
+        // 前端本地搜索过滤
+        if (searchKeywords && searchKeywords.trim()) {
+          formattedData = formattedData.filter((item: any) =>
+            (item.empCode && item.empCode.includes(searchKeywords)) ||
+            (item.empName && item.empName.includes(searchKeywords))
+          );
+        }
+
+        return {
+          data: formattedData,
+          success: true,
+          total: formattedData.length,
+        };
+      }
+
+      return {
+        data: [],
+        success: true,
+        total: 0,
+      };
+    } catch (error) {
+      console.error('获取人员列表失败:', error);
+      message.error('获取人员列表失败');
+      return {
+        data: [],
+        success: false,
+        total: 0,
+      };
+    }
+  };
+
+  // 查核条件表格列定义
+  const conditionColumns: ProColumns[] = [
+    {
+      title: '条件名称',
+      dataIndex: 'name',
+      width: 200,
+      align: 'left' as 'left',
+    },
+    {
+      title: '说明',
+      dataIndex: 'description',
+      align: 'left' as 'left',
+      ellipsis: true,
+    },
+  ];
+
+  // 人员表格列定义
+  const [selectedPersonnelKeys, setSelectedPersonnelKeys] = useState<string[]>(
+    [],
+  );
+  const personnelColumns: ProColumns[] = [
+    {
+      title: '工号',
+      dataIndex: 'empCode',
+      width: 120,
+      align: 'center',
+    },
+    {
+      title: '姓名',
+      dataIndex: 'empName',
+      width: 120,
+      align: 'center',
+    },
+    {
+      title: '负责人',
+      dataIndex: 'isManager',
+      width: 120,
+      align: 'center',
+      render: (_, record) => {
+        return (
+          <Radio
+            checked={record.isManager}
+            disabled={settingManagerId === record.id}
+            onChange={() => handleSetManager(record)}
+          />
+        );
+      },
+    },
+    {
+      title: '操作',
+      key: 'option',
+      width: 100,
+      align: 'center',
+      render: (_, record) => {
+        return (
+          <Popconfirm
+            title="确认删除?"
+            okText="确定"
+            cancelText="取消"
+            onConfirm={() => handleDeletePersonnel(record)}
+          >
+            <a className="delete-btn">删除</a>
+          </Popconfirm>
+        );
+      },
+    },
+  ];
+
+  // 处理科室选择
+  const handleSelectDept = (dept: string) => {
+    setSelectedDept(dept);
+  };
+
+  // 处理部门展开/收起
+  const toggleExpand = (dept: string) => {
+    if (expandedKeys.includes(dept)) {
+      setExpandedKeys(expandedKeys.filter((key) => key !== dept));
+    } else {
+      setExpandedKeys([...expandedKeys, dept]);
+    }
+  };
+
+  // 人员选择相关状态
+  const [employeeSearchKeyword, setEmployeeSearchKeyword] = useState('');
+  const [selectedEmployees, setSelectedEmployees] = useState<string[]>([]);
+  const [employeeModalVisible, setEmployeeModalVisible] = useState(false);
+  const employeeTableRef = useRef<ActionType>();
+
+  // 查核要点相关状态
+  const [checkPointsSearchKeyword, setCheckPointsSearchKeyword] = useState('');
+  const checkPointsTableRef = useRef<ActionType>();
+
+  // 处理添加人员
+  const handleAddPersonnel = () => {
+    // 如果没有选择单位,提示先选择单位
+    if (!selectedDept?.key) {
+      message.warning('请先选择一个查核单位');
+      return;
+    }
+
+    // 重置状态
+    setEmployeeSearchKeyword('');
+    setSelectedEmployees([]);
+
+    // 打开选择人员弹窗
+    setEmployeeModalVisible(true);
+
+    // 延迟触发表格重新加载,确保弹窗打开后重新请求数据
+    setTimeout(() => {
+      employeeTableRef.current?.reload();
+    }, 100);
+  };
+
+  // 获取选择人员列表的数据
+  const getEmployeeSelectData = async (params: any) => {
+    try {
+      // 如果没有选中的部门,返回空数据
+      if (!selectedDept?.key) {
+        return {
+          data: [],
+          success: true,
+          total: 0,
+        };
+      }
+
+      // 调用真实API获取可选人员列表
+      const res = await getDeptPendingEmployee({
+        deptId: Number(selectedDept.key), // 当前选中的查核单位ID
+        filter: employeeSearchKeyword || '', // 搜索关键词
+        deptType: selectedDept.deptType ?? 0, // 单位类型,使用空值合并操作符,默认为0
+      });
+
+      if (res && res.data) {
+        // 尝试多种数据结构格式
+        let dataArray = null;
+
+        if (Array.isArray(res.data)) {
+          // 格式1: { data: [...] }
+          dataArray = res.data;
+        } else if (res.data.items && Array.isArray(res.data.items)) {
+          // 格式2: { data: { items: [...] } }
+          dataArray = res.data.items;
+        } else if (res.data.list && Array.isArray(res.data.list)) {
+          // 格式3: { data: { list: [...] } }
+          dataArray = res.data.list;
+        }
+
+        if (dataArray && dataArray.length >= 0) {
+          // 格式化返回的数据
+          const formattedData = dataArray.map((item: any) => ({
+            id: String(item.id || item.empId), // 人员ID
+            empCode: item.code || item.empCode, // 人员工号
+            empName: item.name || item.empName, // 人员姓名
+            userId: item.userId, // 人员中台ID
+          }));
+
+          return {
+            data: formattedData,
+            success: true,
+            total: formattedData.length,
+          };
+        }
+      } else if (Array.isArray(res)) {
+        // 格式4: 直接返回数组
+        const formattedData = res.map((item: any) => ({
+          id: String(item.id || item.empId), // 人员ID
+          empCode: item.code || item.empCode, // 人员工号
+          empName: item.name || item.empName, // 人员姓名
+          userId: item.userId, // 人员中台ID
+        }));
+
+        return {
+          data: formattedData,
+          success: true,
+          total: formattedData.length,
+        };
+      }
+
+      return {
+        data: [],
+        success: true,
+        total: 0,
+      };
+    } catch (error) {
+      console.error('获取可选人员列表失败:', error);
+      message.error('获取可选人员列表失败');
+      return {
+        data: [],
+        success: false,
+        total: 0,
+      };
+    }
+  };
+
+  // 处理员工选择
+  const handleEmployeeSelect = (employeeId: string, checked: boolean) => {
+    if (checked) {
+      setSelectedEmployees([...selectedEmployees, employeeId]);
+    } else {
+      setSelectedEmployees(selectedEmployees.filter((id) => id !== employeeId));
+    }
+  };
+
+  // 确认添加选择的人员
+  const handleConfirmAddEmployees = async () => {
+    if (selectedEmployees.length === 0) {
+      message.warning('请至少选择一名人员');
+      return;
+    }
+
+    try {
+      // 调用真实API保存添加的人员
+      const res = await addDeptEmployee({
+        id: selectedDept.key, // 查核单位ID
+        type: String(selectedDept.deptType ?? 0), // 单位类型:0普通单位1职能科室
+        mapIdList: selectedEmployees, // 人员ID列表
+      });
+
+      if (res) {
+        message.success(`已添加 ${selectedEmployees.length} 名人员`);
+        setEmployeeModalVisible(false);
+
+        // 重置选择状态
+        setSelectedEmployees([]);
+
+        // 刷新人员列表
+        tableRef.current?.reload();
+      } else {
+        message.error('添加人员失败');
+      }
+    } catch (error) {
+      console.error('添加人员失败:', error);
+      message.error('添加人员失败');
+    }
+  };
+
+  // 处理搜索
+  const handleSearch = (value: string) => {
+    setSearchKeywords(value);
+    // 使用新接口时,不需要本地过滤,直接重新请求API
+    tableRef.current?.reload();
+  };
+
+  // 处理标签页切换
+  const handleTabChange = (key: string) => {
+    setActiveTab(key);
+    // 切换tab时清空相关选择状态
+    if (key !== 'personnel') {
+      setSelectedPersonnelKeys([]);
+    }
+    if (key !== 'checkPoints') {
+      setSelectedCheckPointKeys([]);
+    }
+  };
+
+  // 获取查核要点数据
+  const getCheckPointsData = async (params: any) => {
+    try {
+      // 如果没有选中的部门,返回空数据
+      if (!selectedDept?.key) {
+        return {
+          data: [],
+          success: true,
+          total: 0,
+        };
+      }
+
+      // 模拟数据,后续替换为真实API调用
+      const mockData = [
+        {
+          id: '1',
+          pointName: '麻醉科管理规程',
+          checkItemName: '审阅麻醉前评估记录内容完整、(10分)',
+          rowSpan: 4, // 麻醉科管理规程有4行
+        },
+        {
+          id: '2',
+          pointName: '麻醉科管理规程',
+          checkItemName: '审阅麻醉记录单内手术时间与用药记录(10分)',
+          rowSpan: 0, // 合并到上一行
+        },
+        {
+          id: '3',
+          pointName: '麻醉科管理规程',
+          checkItemName: '审阅麻醉前评估记录内容完整、(10分)',
+          rowSpan: 0, // 合并到上一行
+        },
+        {
+          id: '4',
+          pointName: '麻醉科管理规程',
+          checkItemName: '审阅麻醉记录单内手术时间与用药记录(10分)',
+          rowSpan: 0, // 合并到上一行
+        },
+        {
+          id: '5',
+          pointName: '药品管理规程',
+          checkItemName: '审阅麻醉前评估记录内容完整、(10分)',
+          rowSpan: 2, // 药品管理规程有2行
+        },
+        {
+          id: '6',
+          pointName: '药品管理规程',
+          checkItemName: '审阅麻醉记录单内手术时间与用药记录(10分)',
+          rowSpan: 0, // 合并到上一行
+        },
+        {
+          id: '7',
+          pointName: '急救药品管理规程',
+          checkItemName: '如果发现冰箱温度不正常,应该如何处理?(15分)',
+          rowSpan: 1, // 急救药品管理规程有1行
+        },
+      ];
+
+      // 根据搜索关键词过滤数据
+      const filteredData = checkPointsSearchKeyword
+        ? mockData.filter(
+          (item) =>
+            item.pointName.includes(checkPointsSearchKeyword) ||
+            item.checkItemName.includes(checkPointsSearchKeyword),
+        )
+        : mockData;
+
+      return {
+        data: filteredData,
+        success: true,
+        total: filteredData.length,
+      };
+    } catch (error) {
+      console.error('获取查核要点失败:', error);
+      message.error('获取查核要点失败');
+      return {
+        data: [],
+        success: false,
+        total: 0,
+      };
+    }
+  };
+
+  // 处理查核要点搜索
+  const handleSearchCheckPoints = (value: string) => {
+    setCheckPointsSearchKeyword(value);
+    checkPointsTableRef.current?.reload();
+  };
+
+  // 处理添加查核要点
+  const handleAddCheckPoint = () => {
+    if (!selectedDept?.key) {
+      message.warning('请先选择一个查核单位');
+      return;
+    }
+
+    // TODO: 实现添加查核要点的弹窗
+    message.info('添加查核要点功能待实现');
+  };
+
+  // 处理删除查核要点
+  const handleDeleteCheckPoint = async (record: any) => {
+    try {
+      await delDeptCheckpoint([record.id]);
+      message.success('删除要点成功');
+      checkPointsTableRef.current?.reload();
+    } catch (e) {
+      message.error('删除要点失败');
+    }
+  };
+
+  // 获取查核条件列表 - 用于KCIMTable
+  const getConditionData = async (params: any) => {
+    try {
+      const { current = 1, pageSize = 10 } = params;
+
+      // 调用真实API获取条件列表(不传filter,获取所有数据)
+      const requestParams = {
+        deptClassId: currentNode?.key, // 当前选中的部门分类ID
+        conditionType: conditionSearchType === '个案' ? 0 : 1, // 条件类型:0个案1系统
+        filter: '', // 不传搜索关键词,获取所有数据
+      };
+      
+      const res = await getDeptClassFilterConditionList(requestParams);
+
+      if (res && res.itemList) {
+        // 直接从res中获取数据
+        const { itemList = [], mapIdList = [] } = res;
+
+        // 更新已选择的条件ID列表 - 确保ID类型一致
+        const selectedIds = (mapIdList || []).map((id: any) => String(id));
+        setSelectedConditions(selectedIds);
+
+        // 转换数据格式,保持树形结构
+        const transformConditions = (conditions: any[]): any[] => {
+          return conditions.map((item: any) => {
+            const transformed: any = {
+              id: String(item.id), // 确保ID为字符串类型
+              name: item.name,
+              description: item.description || '暂无说明',
+            };
+
+            // 如果有子条件,递归处理
+            if (
+              item.children &&
+              Array.isArray(item.children) &&
+              item.children.length > 0
+            ) {
+              transformed.children = transformConditions(item.children);
+            }
+
+            return transformed;
+          });
+        };
+
+        // 保持树形结构的条件数据
+        const allConditions = transformConditions(itemList);
+
+        // 递归搜索树形数据
+        const searchInTree = (data: any[], keyword: string): any[] => {
+          if (!keyword.trim()) return data;
+
+          const result: any[] = [];
+
+          data.forEach((item) => {
+            // 检查当前节点是否匹配
+            const currentMatches =
+              item.name.includes(keyword) || item.description.includes(keyword);
+
+            // 递归搜索子节点
+            let filteredChildren: any[] = [];
+            if (item.children && Array.isArray(item.children)) {
+              filteredChildren = searchInTree(item.children, keyword);
+            }
+
+            // 如果当前节点匹配或有匹配的子节点,则保留
+            if (currentMatches || filteredChildren.length > 0) {
+              const newItem = { ...item };
+              if (filteredChildren.length > 0) {
+                newItem.children = filteredChildren;
+              } else if (!currentMatches) {
+                // 如果当前节点不匹配但有匹配的子节点,保留原有children
+                // 这种情况在上面的逻辑中已经处理了
+              }
+              result.push(newItem);
+            }
+          });
+
+          return result;
+        };
+
+        // 根据搜索关键词过滤(本地搜索)
+        const filteredData = searchInTree(
+          allConditions,
+          conditionSearchKeyword,
+        );
+
+        // 前端分页处理
+        const startIndex = (current - 1) * pageSize;
+        const endIndex = startIndex + pageSize;
+        const pageData = filteredData.slice(startIndex, endIndex);
+
+        return {
+          data: pageData,
+          success: true,
+          total: filteredData.length,
+        };
+      } else {
+        return {
+          data: [],
+          success: true,
+          total: 0,
+        };
+      }
+    } catch (error) {
+      console.error('进入了catch块,获取条件列表失败:', error);
+      message.error('获取条件列表失败');
+      return {
+        data: [],
+        success: false,
+        total: 0,
+      };
+    }
+  };
+
+  // 打开条件选择弹窗
+  const handleOpenConditionModal = (node: any) => {
+    setCurrentNode(node);
+    setConditionModalVisible(true);
+    setSelectedConditions([]); // 重置选择,实际选择状态会在API调用后更新
+    setConditionSearchKeyword(''); // 重置搜索关键词
+    setConditionSearchType('个案'); // 重置搜索类型
+
+    // 延迟触发表格重新加载,确保弹窗打开后重新请求数据
+    setTimeout(() => {
+      conditionTableRef.current?.reload();
+    }, 100);
+  };
+
+  // 处理条件选择
+  const handleConditionSelect = (conditionId: string, checked: boolean) => {
+    if (checked) {
+      setSelectedConditions([...selectedConditions, conditionId]);
+    } else {
+      setSelectedConditions(
+        selectedConditions.filter((id) => id !== conditionId),
+      );
+    }
+  };
+
+  // 处理级联选择(父子关联)
+  const handleConditionSelectWithCascade = (record: any, selected: boolean) => {
+    // 获取当前记录及其所有子记录的ID
+    const getAllIds = (item: any): string[] => {
+      const ids = [item.id];
+      if (
+        item.children &&
+        Array.isArray(item.children) &&
+        item.children.length > 0
+      ) {
+        item.children.forEach((child: any) => {
+          ids.push(...getAllIds(child));
+        });
+      }
+      return ids;
+    };
+
+    const allIds = getAllIds(record);
+
+    if (selected) {
+      // 选中:添加当前记录及其所有子记录
+      const newSelectedIds = [...new Set([...selectedConditions, ...allIds])];
+      setSelectedConditions(newSelectedIds);
+    } else {
+      // 取消选中:移除当前记录及其所有子记录
+      const newSelectedIds = selectedConditions.filter(
+        (id) => !allIds.includes(id),
+      );
+      setSelectedConditions(newSelectedIds);
+    }
+  };
+
+  // 确认选择条件
+  const handleConfirmConditions = async () => {
+    try {
+      // 调用保存接口
+      const res = await applyDeptClassFilterCondition({
+        id: currentNode?.key, // 单位分类ID
+        mapIdList: selectedConditions, // 查核条件ID列表
+      });
+
+      if (res) {
+        message.success(
+          `已为 ${currentNode?.title} 保存了 ${selectedConditions.length} 个查核条件`,
+        );
+        setConditionModalVisible(false);
+
+        // 重新获取列表,更新条件信息
+        await fetchDeptClassList();
+
+        // 查询选中的条件名称
+        if (res.items && Array.isArray(res.items)) {
+          // 如果API返回了条件列表,可以直接从返回值获取条件名称
+          const conditionNames = res.items
+            .map((item: any) => item.name)
+            .join('、');
+
+          // 更新treeData中对应节点的conditions属性
+          const updatedTreeData = treeData.map((item: any) => {
+            if (item.key === currentNode?.key) {
+              return {
+                ...item,
+                conditions: conditionNames || '手术室、重症加护病房、医学影像',
+              };
+            }
+            return item;
+          });
+
+          setTreeData(updatedTreeData);
+          setFilteredTreeData(updatedTreeData);
+        }
+      } else {
+        message.error('保存查核条件失败');
+      }
+    } catch (error) {
+      console.error('保存查核条件失败:', error);
+      message.error('保存查核条件失败');
+    }
+  };
+
+  // 处理删除节点
+  const handleDeleteNode = (node: any, isParent: boolean) => {
+    Modal.confirm({
+      title: '确认删除',
+      content: `确定要删除${isParent ? '单位分类' : '查核单位'} "${node.title
+        }" 吗?`,
+      okText: '确定',
+      cancelText: '取消',
+      onOk: async () => {
+        try {
+          if (isParent) {
+            // 删除单位分类
+            const res = await delDeptClass([Number(node.key)]);
+            if (res) {
+              message.success('删除单位分类成功');
+              // 重新获取列表数据
+              await fetchDeptClassList();
+            } else {
+              message.error('删除单位分类失败');
+            }
+          } else {
+            // 删除查核单位
+            const res = await delDepartmentByClass({
+              deptId: Number(node.key), // 查核单位ID
+              deptType: node.deptType ?? 0, // 单位类型:0普通单位 1职能科室
+            });
+            if (res) {
+              message.success('删除查核单位成功');
+              // 重新获取列表数据
+              await fetchDeptClassList();
+            } else {
+              message.error('删除查核单位失败');
+            }
+          }
+        } catch (error) {
+          message.error('删除失败');
+        }
+      },
+    });
+  };
+
+  // 自定义空状态
+  const customizeRenderEmpty = () => (
+    <div style={{ textAlign: 'center', padding: '50px 0' }}>
+      <Empty image={Empty.PRESENTED_IMAGE_SIMPLE} description="暂无数据" />
+    </div>
+  );
+
+  // 生成更多按钮的菜单
+  const getMoreMenu = (
+    node: any,
+    isParent: boolean = false,
+    parentNode?: any,
+  ) => {
+    const menuItems = [];
+
+    if (isParent) {
+      // 父级节点菜单:新增单位、条件、修改、删除
+      menuItems.push(
+        <Menu.Item key="addUnit" style={{ padding: 0 }}>
+          <ModalForm
+            title="新增查核单位"
+            trigger={<div style={{ padding: '5px 12px' }}>新增单位</div>}
+            width={352}
+            onFinish={async (values) => {
+              try {
+                const { name, sort, deptType } = values;
+                const res = await addDepartmentByClass({
+                  deptClassId: node.key, // 使用当前选中的单位分类ID
+                  name,
+                  sort,
+                  deptType, // 使用表单中用户选择的职能科室值
+                });
+
+                if (res) {
+                  message.success('新增单位成功');
+                  // 重新获取列表数据
+                  await fetchDeptClassList();
+                  return true;
+                } else {
+                  message.error('新增单位失败');
+                  return false;
+                }
+              } catch (error) {
+                message.error('新增单位失败');
+                return false;
+              }
+            }}
+            modalProps={{
+              destroyOnClose: true,
+            }}
+          >
+            <ProFormText
+              name="name"
+              label="单位名称"
+              placeholder="请输入"
+              rules={[{ required: true, message: '请输入单位名称' }]}
+            />
+            <ProFormDigit
+              name="sort"
+              label="序号"
+              placeholder="请输入"
+              initialValue={0}
+              min={0}
+              rules={[{ required: true, message: '请输入序号' }]}
+            />
+            <ProFormRadio.Group
+              name="deptType"
+              label="职能科室"
+              initialValue={0}
+              rules={[{ required: true, message: '请选择是否为职能科室' }]}
+              options={[
+                { label: '否', value: 0 },
+                { label: '是', value: 1 },
+              ]}
+            />
+          </ModalForm>
+        </Menu.Item>,
+        <Menu.Item
+          key="condition"
+          onClick={() => handleOpenConditionModal(node)}
+        >
+          条件
+        </Menu.Item>,
+      );
+    }
+
+    // 公共菜单项:修改、删除
+    menuItems.push(
+      <Menu.Item key="edit" style={{ padding: 0 }}>
+        <ModalForm
+          title={isParent ? '修改单位分类' : '修改查核单位'}
+          trigger={<div style={{ padding: '5px 12px' }}>修改</div>}
+          width={352}
+          initialValues={{
+            name: node.title,
+            depType: node.visitType || 1,
+            sort: node.sort || 0,
+            deptType: node.deptType ?? 0, // 单位类型:0普通单位 1职能科室
+          }}
+          onFinish={async (values) => {
+            try {
+              if (isParent) {
+                // 修改单位分类
+                const { name, depType } = values;
+                const res = await updateDeptClass({
+                  id: Number(node.key), // 单位分类ID
+                  name,
+                  depType, // 门急诊类型:1-门诊 2-急诊
+                });
+
+                if (res) {
+                  message.success('修改单位分类成功');
+                } else {
+                  message.error('修改单位分类失败');
+                  return false;
+                }
+              } else {
+                // 修改查核单位
+                const { name, sort, deptType } = values;
+                const res = await updateDepartmentByClass({
+                  deptClassId: Number(parentNode?.key), // 单位分类ID
+                  id: Number(node.key), // 查核单位ID
+                  name,
+                  sort,
+                  deptType, // 使用表单中用户选择的职能科室值
+                });
+
+                if (res) {
+                  message.success('修改查核单位成功');
+                } else {
+                  message.error('修改查核单位失败');
+                  return false;
+                }
+              }
+
+              // 重新获取列表数据
+              await fetchDeptClassList();
+              return true;
+            } catch (error) {
+              message.error('修改失败');
+              return false;
+            }
+          }}
+          modalProps={{
+            destroyOnClose: true,
+          }}
+        >
+          <ProFormText
+            name="name"
+            label={isParent ? '单位分类名称' : '单位名称'}
+            placeholder="请输入"
+            rules={[
+              {
+                required: true,
+                message: `请输入${isParent ? '单位分类名称' : '单位名称'}`,
+              },
+            ]}
+          />
+          {isParent ? (
+            <ProFormRadio.Group
+              name="depType"
+              label="诊别"
+              rules={[{ required: true, message: '请选择诊别' }]}
+              options={[
+                { label: '门诊', value: 1 },
+                { label: '急诊', value: 2 },
+              ]}
+            />
+          ) : (
+            <>
+              <ProFormDigit
+                name="sort"
+                label="序号"
+                placeholder="请输入"
+                min={0}
+                rules={[{ required: true, message: '请输入序号' }]}
+              />
+              <ProFormRadio.Group
+                name="deptType"
+                label="职能科室"
+                disabled={true} // 编辑时不可修改
+                rules={[{ required: true, message: '请选择是否为职能科室' }]}
+                options={[
+                  { label: '否', value: 0 },
+                  { label: '是', value: 1 },
+                ]}
+              />
+            </>
+          )}
+        </ModalForm>
+      </Menu.Item>,
+      <Menu.Item key="delete" onClick={() => handleDeleteNode(node, isParent)}>
+        删除
+      </Menu.Item>,
+    );
+
+    return <Menu>{menuItems}</Menu>;
+  };
+
+  // 新增按钮的菜单
+  const menu = (
+    <Menu>
+      <Menu.Item key="1" style={{ padding: 0 }}>
+        <ModalForm
+          title="新增单位分类"
+          trigger={<div style={{ padding: '5px 12px' }}>新增单位分类</div>}
+          width={352}
+          onFinish={async (values) => {
+            try {
+              const { name, depType } = values;
+              const res = await addDeptClass({ name, depType });
+
+              if (res) {
+                message.success('新增单位分类成功');
+                // 重新获取列表数据
+                await fetchDeptClassList();
+                return true;
+              } else {
+                message.error('新增失败');
+                return false;
+              }
+            } catch (error) {
+              message.error('新增失败');
+              return false;
+            }
+          }}
+          modalProps={{
+            destroyOnClose: true,
+          }}
+        >
+          <ProFormText
+            name="name"
+            label="单位分类名称"
+            placeholder="请输入"
+            rules={[{ required: true, message: '请输入单位分类名称' }]}
+          />
+          <ProFormRadio.Group
+            name="depType"
+            label="诊别"
+            initialValue={1}
+            rules={[{ required: true, message: '请选择诊别' }]}
+            options={[
+              { label: '门诊', value: 1 },
+              { label: '急诊', value: 2 },
+            ]}
+          />
+        </ModalForm>
+      </Menu.Item>
+      <Menu.Item key="2" style={{ padding: 0 }}>
+        <ModalForm
+          title="导入系统追踪知识库"
+          trigger={<div style={{ padding: '5px 12px' }}>导入系统追踪</div>}
+          width={600}
+          onFinish={async (values) => {
+            try {
+              let token = '';
+              const userDataStr = localStorage.getItem('userData');
+              if (userDataStr) {
+                try {
+                  token = JSON.parse(userDataStr).token || '';
+                } catch (e) {
+                  token = '';
+                }
+              }
+              const formData = new FormData();
+              formData.append('type', '1');
+              if (values.systemTrackingFile && values.systemTrackingFile[0]?.originFileObj) {
+                formData.append('file', values.systemTrackingFile[0].originFileObj);
+              } else {
+                message.error('请上传文件');
+                return false;
+              }
+              const res = await fetch('/gateway/manager/Department/importPfmKnowledge', {
+                method: 'POST',
+                headers: {
+                  token: token,
+                },
+                body: formData,
+              });
+              let result = null;
+              try {
+                result = await res.json();
+              } catch (e) {
+                message.error('导入失败');
+                return false;
+              }
+              if (result && result.status === 200) {
+                message.success('导入成功');
+                // 重新获取列表数据,刷新左侧树形结构
+                await fetchDeptClassList();
+                return true;
+              } else {
+                message.error(result.errorMessage || '导入失败');
+                return false;
+              }
+            } catch (error) {
+              message.error('导入失败');
+              return false;
+            }
+          }}
+          modalProps={{
+            destroyOnClose: true,
+          }}
+        >
+          <div
+            style={{
+              display: 'flex',
+              justifyContent: 'space-between',
+              alignItems: 'center',
+              marginBottom: 16,
+            }}
+          >
+            <span style={{ fontSize: 16, fontWeight: 500 }}>文件上传</span>
+            <Button type="link" style={{ padding: 0 }} onClick={() => downloadTemplate(1)}>
+              模板下载
+            </Button>
+          </div>
+          <ProFormUploadDragger
+            name="systemTrackingFile"
+            icon={<InboxOutlined />}
+            title="点击或将文件拖拽到这里上传"
+            description="支持扩展名:.rar .zip .doc .docx .pdf .jpg..."
+            fieldProps={{
+              name: 'file',
+              multiple: false,
+              accept: '.rar,.zip,.doc,.docx,.pdf,.jpg,.jpeg,.png,.xls,.xlsx',
+              beforeUpload: () => false, // 阻止自动上传
+            }}
+            rules={[{ required: true, message: '请上传文件' }]}
+          />
+        </ModalForm>
+      </Menu.Item>
+      {/* <Menu.Item key="3" style={{ padding: 0 }}>
+        <ModalForm
+          title="导入自查督查知识库"
+          trigger={<div style={{ padding: '5px 12px' }}>导入自查督查</div>}
+          width={600}
+          onFinish={async (values) => {
+            try {
+              let token = '';
+              const userDataStr = localStorage.getItem('userData');
+              if (userDataStr) {
+                try {
+                  token = JSON.parse(userDataStr).token || '';
+                } catch (e) {
+                  token = '';
+                }
+              }
+              const formData = new FormData();
+              formData.append('type', '2');
+              if (values.selfInspectionFile && values.selfInspectionFile[0]?.originFileObj) {
+                formData.append('file', values.selfInspectionFile[0].originFileObj);
+              } else {
+                message.error('请上传文件');
+                return false;
+              }
+              const res = await fetch('/manager/Department/importPfmKnowledge', {
+                method: 'POST',
+                headers: {
+                  token: token,
+                },
+                body: formData,
+              });
+              let result = null;
+              try {
+                result = await res.json();
+              } catch (e) {
+                message.error('导入失败');
+                return false;
+              }
+              if (result && result.status === 200) {
+                message.success('导入成功');
+                return true;
+              } else {
+                message.error(result.errorMessage || '导入失败');
+                return false;
+              }
+            } catch (error) {
+              message.error('导入失败');
+              return false;
+            }
+          }}
+          modalProps={{
+            destroyOnClose: true,
+          }}
+        >
+          <div
+            style={{
+              display: 'flex',
+              justifyContent: 'space-between',
+              alignItems: 'center',
+              marginBottom: 16,
+            }}
+          >
+            <span style={{ fontSize: 16, fontWeight: 500 }}>文件上传</span>
+            <Button type="link" style={{ padding: 0 }} onClick={() => downloadTemplate(2)}>
+              模板下载
+            </Button>
+          </div>
+          <ProFormUploadDragger
+            name="selfInspectionFile"
+            icon={<InboxOutlined />}
+            title="点击或将文件拖拽到这里上传"
+            description="支持扩展名:.rar .zip .doc .docx .pdf .jpg..."
+            fieldProps={{
+              name: 'file',
+              multiple: false,
+              accept: '.rar,.zip,.doc,.docx,.pdf,.jpg,.jpeg,.png,.xls,.xlsx',
+              beforeUpload: () => false, // 阻止自动上传
+            }}
+            rules={[{ required: true, message: '请上传文件' }]}
+          />
+        </ModalForm>
+      </Menu.Item> */}
+    </Menu>
+  );
+
+  // 渲染左侧部门树
+  const renderDeptTree = () => {
+    return (
+      <div className="dept-tree">
+        <div className="search-wrapper">
+          <Input
+            placeholder="单位分类、名称"
+            suffix={<SearchOutlined style={{ color: '#99A6BF' }} />}
+            className="search-input"
+            value={treeSearchKeywords}
+            onChange={(e) => handleTreeSearch(e.target.value)}
+            allowClear
+          />
+          <Dropdown
+            overlay={menu}
+            trigger={['hover']}
+            overlayClassName="check-unit-add-dropdown"
+          >
+            <Button icon={<PlusOutlined />} className="add-button" />
+          </Dropdown>
+        </div>
+
+        <div className="dept-list">
+          {filteredTreeData.map((parent) => {
+            // 判断是否有子项
+            const hasChildren = parent.children && parent.children.length > 0;
+
+            return (
+              <div key={parent.key} className="dept-group">
+                {hasChildren ? (
+                  <div
+                    className="dept-parent"
+                    onClick={() => toggleExpand(parent.key)}
+                    onMouseEnter={() => setHoveredNode(parent.key)}
+                    onMouseLeave={() => setHoveredNode(null)}
+                  >
+                    <Button
+                      type="text"
+                      icon={
+                        expandedKeys.includes(parent.key) ? (
+                          <IconFont type="icon-shouqi1" />
+                        ) : (
+                          <IconFont type="icon-zhankai1" />
+                        )
+                      }
+                      className="toggle-button"
+                    />
+                    <Tooltip title={parent.title}>
+                      <span className="dept-title">{parent.title}</span>
+                    </Tooltip>
+                    {(selectedDept?.key === parent.key ||
+                      hoveredNode === parent.key ||
+                      openDropdownNode === parent.key) && (
+                        <Dropdown
+                          overlay={getMoreMenu(parent, true)}
+                          trigger={['click']}
+                          placement="bottomRight"
+                          overlayClassName="check-unit-add-dropdown"
+                          onVisibleChange={(visible) => {
+                            if (visible) {
+                              setOpenDropdownNode(parent.key);
+                            } else {
+                              setOpenDropdownNode(null);
+                            }
+                          }}
+                        >
+                          <Button
+                            type="text"
+                            icon={<MoreOutlined />}
+                            className="more-button"
+                            onClick={(e) => e.stopPropagation()}
+                          />
+                        </Dropdown>
+                      )}
+                  </div>
+                ) : (
+                  <div
+                    className={`dept-leaf ${selectedDept?.key === parent.key ? 'selected' : ''
+                      }`}
+                    onClick={() => {
+                      // 设置分类自身的信息
+                      setSelectedDept({
+                        ...parent,
+                        parentTitle: '单位分类',
+                        // 保留原始的filterConditionList数据
+                        filterConditionList: parent.filterConditionList || [],
+                      });
+                      // 不需要手动重新加载,useEffect会监听selectedDept变化
+                    }}
+                    onMouseEnter={() => setHoveredNode(parent.key)}
+                    onMouseLeave={() => setHoveredNode(null)}
+                  >
+                    <Tooltip title={parent.title}>
+                      <span className="dept-title">{parent.title}</span>
+                    </Tooltip>
+                    {(selectedDept?.key === parent.key ||
+                      hoveredNode === parent.key ||
+                      openDropdownNode === parent.key) && (
+                        <Dropdown
+                          overlay={getMoreMenu(parent, true)}
+                          trigger={['click']}
+                          placement="bottomRight"
+                          overlayClassName="check-unit-add-dropdown"
+                          onVisibleChange={(visible) => {
+                            if (visible) {
+                              setOpenDropdownNode(parent.key);
+                            } else {
+                              setOpenDropdownNode(null);
+                            }
+                          }}
+                        >
+                          <Button
+                            type="text"
+                            icon={<MoreOutlined />}
+                            className="more-button"
+                            onClick={(e) => e.stopPropagation()}
+                          />
+                        </Dropdown>
+                      )}
+                  </div>
+                )}
+
+                {hasChildren && expandedKeys.includes(parent.key) && (
+                  <div className="dept-children">
+                    {parent.children.map((child: any) => (
+                      <div
+                        key={child.key}
+                        className={`dept-item ${selectedDept?.key === child.key ? 'selected' : ''
+                          }`}
+                        onClick={() => {
+                          // 添加父节点信息,保留原始查核条件数据
+                          setSelectedDept({
+                            ...child,
+                            parentTitle: parent.title,
+                            // 保留原始的filterConditionList数据
+                            filterConditionList:
+                              child.filterConditionList || [],
+                          });
+                          // 不需要手动重新加载,useEffect会监听selectedDept变化
+                        }}
+                        onMouseEnter={() => setHoveredNode(child.key)}
+                        onMouseLeave={() => setHoveredNode(null)}
+                      >
+                        <Tooltip title={child.title}>
+                          <span className="dept-title-text">
+                            {child.title}
+                            {child.deptType === 1 && (
+                              <span
+                                style={{
+                                  marginLeft: 4,
+                                  fontSize: 12,
+                                  padding: '0 4px',
+                                  background: '#f0f0f0',
+                                  color: '#666',
+                                  borderRadius: 2,
+                                  display: 'inline-block',
+                                  lineHeight: '18px',
+                                  verticalAlign: 'text-top',
+                                }}
+                              >
+                                职
+                              </span>
+                            )}
+                          </span>
+                        </Tooltip>
+                        {(selectedDept?.key === child.key ||
+                          hoveredNode === child.key ||
+                          openDropdownNode === child.key) && (
+                            <Dropdown
+                              overlay={getMoreMenu(child, false, parent)}
+                              trigger={['click']}
+                              placement="bottomRight"
+                              overlayClassName="check-unit-add-dropdown"
+                              onVisibleChange={(visible) => {
+                                if (visible) {
+                                  setOpenDropdownNode(child.key);
+                                } else {
+                                  setOpenDropdownNode(null);
+                                }
+                              }}
+                            >
+                              <Button
+                                type="text"
+                                icon={<MoreOutlined />}
+                                className="more-button"
+                                onClick={(e) => e.stopPropagation()}
+                              />
+                            </Dropdown>
+                          )}
+                      </div>
+                    ))}
+                  </div>
+                )}
+              </div>
+            );
+          })}
+        </div>
+      </div>
+    );
+  };
+
+  // 处理设置负责人
+  const [settingManagerId, setSettingManagerId] = useState<string>('');
+
+  const handleSetManager = async (record: any) => {
+    if (record.isManager) return; // 已是负责人
+    setSettingManagerId(record.id);
+    try {
+      await setDeptManager({
+        empId: Number(record.empId || record.id),
+        empName: record.empName,
+        deptId: Number(selectedDept.key),
+        deptType: selectedDept.deptType ?? 0, // 使用空值合并操作符,默认为0
+      });
+      message.success(`已设置 ${record.empName} 为负责人`);
+      tableRef.current?.reload();
+    } catch (e) {
+      message.error('设置负责人失败');
+    } finally {
+      setSettingManagerId('');
+    }
+  };
+
+  const handleDeletePersonnel = async (record: any) => {
+    try {
+      await delDeptEmp([
+        { id: String(record.id), type: String(selectedDept.deptType ?? 0) },
+      ]);
+      message.success('删除成功');
+      tableRef.current?.reload();
+    } catch (e) {
+      message.error('删除失败');
+    }
+  };
+
+  // Tab下table专用数据获取方法
+  const getDeptCheckpointData = async (params: any) => {
+    try {
+      if (!selectedDept?.key) {
+        return { data: [], success: true, total: 0 };
+      }
+      // 不传搜索关键词,获取全部数据
+      const res = await getDeptCheckpoint({
+        deptId: selectedDept.key,
+        filter: '', // 获取全部数据
+        deptType: selectedDept.deptType ?? 0,
+      });
+      // 这里根据接口返回结构处理数据
+      if (Array.isArray(res)) {
+        const flatData: any[] = [];
+        res.forEach((point: any) => {
+          if (
+            Array.isArray(point.checkItemList) &&
+            point.checkItemList.length > 0
+          ) {
+            point.checkItemList.forEach((item: any, idx: number) => {
+              flatData.push({
+                id: idx === 0 ? String(point.id) : `${point.id}_item_${idx}`,
+                checkPointName: point.checkPointName, // 直接用接口字段
+                checkItemName: item.name,
+                rowSpan: idx === 0 ? point.checkItemList.length : 0,
+                isFirst: idx === 0,
+              });
+            });
+          } else {
+            flatData.push({
+              id: String(point.id),
+              checkPointName: point.checkPointName, // 直接用接口字段
+              checkItemName: '-',
+              rowSpan: 1,
+              isFirst: true,
+            });
+          }
+        });
+
+        // 前端本地搜索过滤
+        let filteredData = flatData;
+        if (checkPointsSearchKeyword && checkPointsSearchKeyword.trim()) {
+          filteredData = flatData.filter((item: any) =>
+            (item.checkPointName && item.checkPointName.includes(checkPointsSearchKeyword)) ||
+            (item.checkItemName && item.checkItemName.includes(checkPointsSearchKeyword))
+          );
+        }
+
+        // 更新当前页数据用于全选逻辑
+        setCurrentCheckPointPageData(filteredData);
+        return {
+          data: filteredData,
+          success: true,
+          total: filteredData.length,
+        };
+      }
+      return { data: [], success: true, total: 0 };
+    } catch (e) {
+      return { data: [], success: false, total: 0 };
+    }
+  };
+
+  // Tab页columns
+  const checkPointTabColumns = [
+    {
+      title: (
+        <Checkbox
+          indeterminate={
+            getCurrentPageCheckPointIds().length > 0 &&
+            selectedCheckPointKeys.length > 0 &&
+            selectedCheckPointKeys.length < getCurrentPageCheckPointIds().length
+          }
+          checked={
+            getCurrentPageCheckPointIds().length > 0 &&
+            selectedCheckPointKeys.length === getCurrentPageCheckPointIds().length
+          }
+          onChange={(e) => {
+            const ids = getCurrentPageCheckPointIds();
+            if (e.target.checked) {
+              setSelectedCheckPointKeys(Array.from(new Set([...selectedCheckPointKeys, ...ids])));
+            } else {
+              setSelectedCheckPointKeys(selectedCheckPointKeys.filter((id) => !ids.includes(id)));
+            }
+          }}
+        >
+          要点名称
+        </Checkbox>
+      ),
+      dataIndex: 'checkPointName',
+      width: 200,
+      align: 'left' as 'left',
+      render: (dom: React.ReactNode, record: any) => {
+        if (record.rowSpan > 0) {
+          return {
+            children: (
+              <div style={{ display: 'flex', alignItems: 'center' }}>
+                <Checkbox
+                  checked={selectedCheckPointKeys.includes(record.id)}
+                  onChange={() => {
+                    if (selectedCheckPointKeys.includes(record.id)) {
+                      setSelectedCheckPointKeys(
+                        selectedCheckPointKeys.filter((k) => k !== record.id),
+                      );
+                    } else {
+                      setSelectedCheckPointKeys([
+                        ...selectedCheckPointKeys,
+                        record.id,
+                      ]);
+                    }
+                  }}
+                />
+                <span style={{ marginLeft: 8 }}>{dom}</span>
+              </div>
+            ),
+            props: { rowSpan: record.rowSpan },
+          };
+        }
+        return { children: null, props: { rowSpan: 0 } };
+      },
+    },
+    {
+      title: '查核项名称',
+      dataIndex: 'checkItemName',
+      align: 'left' as 'left',
+    },
+    {
+      title: '操作',
+      dataIndex: 'option',
+      width: 80,
+      align: 'center' as 'center',
+      render: (_: any, record: any) => {
+        if (record.rowSpan > 0) {
+          return {
+            children: (
+              <Popconfirm
+                title="确认删除该要点及其所有查核项?"
+                okText="确定"
+                cancelText="取消"
+                onConfirm={() => handleDeleteCheckPoint(record)}
+              >
+                <a style={{ color: '#3377ff' }}>删除</a>
+              </Popconfirm>
+            ),
+            props: { rowSpan: record.rowSpan },
+          };
+        }
+        return { children: null, props: { rowSpan: 0 } };
+      },
+    },
+  ];
+
+  // 4. 打开弹窗方法
+  const handleOpenCheckPointModal = () => {
+    setCheckPointModalVisible(true);
+    setSelectedCheckPointKeys([]);
+    setTimeout(() => {
+      checkPointTableRef?.reload?.();
+    }, 100);
+  };
+
+  // 选择查核要点弹窗表格数据获取方法
+  const getDeptPendingCheckpointData = async (params: any) => {
+    try {
+      if (!selectedDept?.key) {
+        return { data: [], success: true, total: 0 };
+      }
+      const res = await getDeptPendingCheckpoint({
+        deptId: selectedDept.key,
+        filter: '',
+        deptType: selectedDept.deptType ?? 0,
+      });
+      if (Array.isArray(res)) {
+        const flatData: any[] = [];
+        res.forEach((point: any) => {
+          if (
+            Array.isArray(point.checkItemList) &&
+            point.checkItemList.length > 0
+          ) {
+            point.checkItemList.forEach((item: any, idx: number) => {
+              flatData.push({
+                id: idx === 0 ? String(point.id) : `${point.id}_item_${idx}`,
+                name: point.name,
+                checkItemName: item.name,
+                rowSpan: idx === 0 ? point.checkItemList.length : 0,
+                isFirst: idx === 0,
+              });
+            });
+          } else {
+            flatData.push({
+              id: String(point.id),
+              name: point.name,
+              checkItemName: '-',
+              rowSpan: 1,
+              isFirst: true,
+            });
+          }
+        });
+        return {
+          data: flatData,
+          success: true,
+          total: flatData.length,
+        };
+      }
+      return { data: [], success: true, total: 0 };
+    } catch (e) {
+      return { data: [], success: false, total: 0 };
+    }
+  };
+
+  // 选择查核要点弹窗"确定"按钮逻辑
+  const handleConfirmAddCheckPoints = async () => {
+    if (!selectedDept?.key) {
+      message.warning('请先选择查核单位');
+      return;
+    }
+    if (selectedCheckPointKeys.length === 0) {
+      message.warning('请至少选择一个查核要点');
+      return;
+    }
+    try {
+      await addDeptCheckpoint({
+        id: String(selectedDept.key),
+        type: String(selectedDept.deptType ?? 0),
+        mapIdList: selectedCheckPointKeys,
+      });
+      message.success('添加查核要点成功');
+      setCheckPointModalVisible(false);
+      // 可选:刷新Tab下表格
+      checkPointsTableRef.current?.reload();
+    } catch (e) {
+      message.error('添加查核要点失败');
+    }
+  };
+
+  // 弹窗columns
+  const checkPointModalColumns = [
+    {
+      title: '要点名称',
+      dataIndex: 'name',
+      width: 200,
+      align: 'left' as 'left',
+      render: (dom: React.ReactNode, record: any) => {
+        if (record.rowSpan > 0) {
+          return {
+            children: (
+              <div style={{ display: 'flex', alignItems: 'center' }}>
+                <Checkbox
+                  checked={selectedCheckPointKeys.includes(record.id)}
+                  onChange={() => {
+                    if (selectedCheckPointKeys.includes(record.id)) {
+                      setSelectedCheckPointKeys(
+                        selectedCheckPointKeys.filter((k) => k !== record.id),
+                      );
+                    } else {
+                      setSelectedCheckPointKeys([
+                        ...selectedCheckPointKeys,
+                        record.id,
+                      ]);
+                    }
+                  }}
+                />
+                <span style={{ marginLeft: 8 }}>{dom}</span>
+              </div>
+            ),
+            props: { rowSpan: record.rowSpan },
+          };
+        }
+        return { children: null, props: { rowSpan: 0 } };
+      },
+    },
+    {
+      title: '查核项名称',
+      dataIndex: 'checkItemName',
+      align: 'left' as 'left',
+    },
+  ];
+
+
+
+  return (
+    <KCIMPagecontainer className="check-unit-mana" title={false} style={{ position: 'relative' }}>
+      <div className="check-unit-container" style={{ position: 'relative' }}>
+        {/* 左侧科室树 */}
+        {renderDeptTree()}
+
+        {/* 右侧内容区 */}
+        <div className="dept-content" style={{ position: 'relative', paddingBottom: (activeTab === 'personnel' && selectedPersonnelKeys.length > 0) || (activeTab === 'checkPoints' && selectedCheckPointKeys.length > 0) ? 68 : 0 }}>
+          <Card bordered={false} ref={cardRef}>
+            <div className="dept-header content-padding">
+              {selectedDept && (
+                <>
+                  <span className="dept-title">{selectedDept.title}</span>
+                  <span className="dept-info">
+                    单位分类:{selectedDept.parentTitle || '暂无'} &nbsp;
+                    分类查核条件:
+                    {selectedDept.filterConditionList
+                      ? selectedDept.filterConditionList
+                        .map((item: any) => item.name)
+                        .join('、')
+                      : '手术室、重症加护病房、医学影像'}
+                  </span>
+                </>
+              )}
+            </div>
+            <Tabs activeKey={activeTab} onChange={handleTabChange}>
+              <TabPane tab="单位人员" key="personnel">
+                <div className="tab-content">
+                  <div className="toolbar">
+                    <Input
+                      placeholder="工号、姓名"
+                      allowClear
+                      value={personnelSearchInput}
+                      suffix={
+                        <SearchOutlined
+                          onClick={() => handleSearch(personnelSearchInput)}
+                          style={{ cursor: 'pointer', color: '#99A6BF' }}
+                        />
+                      }
+                      style={{ width: 250 }}
+                      onChange={(e) => {
+                        setPersonnelSearchInput(e.target.value);
+                        if (e.target.value === '') {
+                          setSearchKeywords('');
+                          tableRef.current?.reload();
+                        }
+                      }}
+                      onPressEnter={(e) => handleSearch(e.currentTarget.value)}
+                    />
+                    <Button
+                      type="primary"
+                      onClick={handleAddPersonnel}
+                      className="add-btn"
+                      size="small"
+                    >
+                      添加
+                    </Button>
+                  </div>
+                  <KCIMTable
+                    actionRef={tableRef}
+                    columns={personnelColumns}
+                    request={getPersonnelData}
+                    rowKey="id"
+                    search={false}
+                    rowSelection={{
+                      selectedRowKeys: selectedPersonnelKeys,
+                      onChange: (keys) => setSelectedPersonnelKeys(keys as string[]),
+                    }}
+                    locale={{ emptyText: customizeRenderEmpty() }}
+                    pagination={false}
+                    tableAlertRender={false}
+                  />
+                </div>
+              </TabPane>
+              <TabPane tab="单位查核要点" key="checkPoints">
+                <div className="tab-content">
+                  <div className="toolbar" style={{ display: 'flex', alignItems: 'center' }}>
+                    <Input
+                      placeholder="要点名称"
+                      allowClear
+                      suffix={
+                        <SearchOutlined
+                          onClick={() => handleSearchCheckPoints(checkPointsSearchKeyword)}
+                          style={{ cursor: 'pointer', color: '#99A6BF' }}
+                        />
+                      }
+                      style={{ width: 250 }}
+                      value={checkPointsSearchKeyword}
+                      onChange={(e) => {
+                        setCheckPointsSearchKeyword(e.target.value);
+                        if (e.target.value === '') {
+                          // 清空搜索时自动刷新
+                          checkPointsTableRef.current?.reload();
+                        }
+                      }}
+                      onPressEnter={(e) =>
+                        handleSearchCheckPoints(e.currentTarget.value)
+                      }
+                    />
+                    <Button
+                      type="primary"
+                      onClick={handleOpenCheckPointModal}
+                      className="add-btn"
+                      size="small"
+                      style={{ marginLeft: 8 }}
+                    >
+                      添加
+                    </Button>
+                  </div>
+                  <KCIMTable
+                    actionRef={checkPointsTableRef}
+                    columns={checkPointTabColumns}
+                    request={getDeptCheckpointData}
+                    rowKey="id"
+                    bordered
+                    search={false}
+                    pagination={false}
+                    tableAlertRender={false}
+                    scroll={{ y: 360 }}
+                  />
+                </div>
+              </TabPane>
+            </Tabs>
+          </Card>
+        </div>
+      </div>
+      {/* 批量操作栏 - 固定在页面底部,参考empMana样式 */}
+      {activeTab === 'personnel' && selectedPersonnelKeys.length > 0 && (
+        <div
+          style={{
+            position: 'absolute',
+            bottom: 0,
+            left: 0,
+            right: 0,
+            height: 48,
+            background: '#FFFFFF',
+            boxShadow: '0px -8px 16px 0px rgba(64,85,128,0.1)',
+            display: 'flex',
+            justifyContent: 'space-between',
+            alignItems: 'center',
+            padding: '0 16px',
+            zIndex: 1000,
+          }}
+        >
+          <span style={{
+            color: '#333333',
+            fontSize: 14,
+            fontFamily: 'SourceHanSansCN-Normal, SourceHanSansCN',
+            fontWeight: 400,
+          }}>
+            已选择 <span style={{ color: '#3377ff' }}>{selectedPersonnelKeys.length}</span> 项,将进行批量删除
+          </span>
+          <div style={{ display: 'flex', gap: 12 }}>
+            <div
+              onClick={() => {
+                setSelectedPersonnelKeys([]);
+              }}
+              style={{
+                cursor: 'pointer',
+                display: 'inline-block',
+                fontSize: 14,
+                fontFamily: 'SourceHanSansCN-Normal, SourceHanSansCN',
+                fontWeight: 400,
+                color: '#666666',
+                lineHeight: '24px',
+                padding: '0 14px',
+                background: '#ffffff',
+                border: '1px solid #dae2f2',
+                borderRadius: 4,
+              }}
+            >
+              取消选择
+            </div>
+            <Popconfirm
+              title="确认批量删除所选人员?"
+              okText="确定"
+              cancelText="取消"
+              onConfirm={async () => {
+                try {
+                  await delDeptEmp(
+                    selectedPersonnelKeys.map((id) => ({
+                      id: String(id),
+                      type: String(selectedDept.deptType ?? 0),
+                    })),
+                  );
+                  message.success('批量删除成功');
+                  setSelectedPersonnelKeys([]);
+                  tableRef.current?.reload();
+                } catch (e) {
+                  message.error('批量删除失败');
+                }
+              }}
+            >
+              <div
+                style={{
+                  cursor: 'pointer',
+                  display: 'inline-block',
+                  fontSize: 14,
+                  fontFamily: 'SourceHanSansCN-Normal, SourceHanSansCN',
+                  fontWeight: 400,
+                  color: '#ffffff',
+                  lineHeight: '24px',
+                  padding: '0 14px',
+                  background: '#3377ff',
+                  borderRadius: 4,
+                }}
+              >
+                批量删除
+              </div>
+            </Popconfirm>
+          </div>
+        </div>
+      )}
+      {activeTab === 'checkPoints' && !checkPointModalVisible && selectedCheckPointKeys.length > 0 && (
+        <div
+          style={{
+            position: 'absolute',
+            bottom: 0,
+            left: 0,
+            right: 0,
+            height: 48,
+            background: '#FFFFFF',
+            boxShadow: '0px -8px 16px 0px rgba(64,85,128,0.1)',
+            display: 'flex',
+            justifyContent: 'space-between',
+            alignItems: 'center',
+            padding: '0 16px',
+            zIndex: 1000,
+          }}
+        >
+          <span style={{
+            color: '#333333',
+            fontSize: 14,
+            fontFamily: 'SourceHanSansCN-Normal, SourceHanSansCN',
+            fontWeight: 400,
+          }}>
+            已选择 <span style={{ color: '#3377ff' }}>{selectedCheckPointKeys.length}</span> 项,将进行批量删除
+          </span>
+          <div style={{ display: 'flex', gap: 12 }}>
+            <div
+              onClick={() => {
+                setSelectedCheckPointKeys([]);
+              }}
+              style={{
+                cursor: 'pointer',
+                display: 'inline-block',
+                fontSize: 14,
+                fontFamily: 'SourceHanSansCN-Normal, SourceHanSansCN',
+                fontWeight: 400,
+                color: '#666666',
+                lineHeight: '24px',
+                padding: '0 14px',
+                background: '#ffffff',
+                border: '1px solid #dae2f2',
+                borderRadius: 4,
+              }}
+            >
+              取消选择
+            </div>
+            <Popconfirm
+              title="确认批量删除所选查核要点?"
+              okText="确定"
+              cancelText="取消"
+              onConfirm={async () => {
+                try {
+                  await delDeptCheckpoint(selectedCheckPointKeys);
+                  message.success('批量删除成功');
+                  setSelectedCheckPointKeys([]);
+                  checkPointsTableRef.current?.reload();
+                } catch (e) {
+                  message.error('批量删除失败');
+                }
+              }}
+            >
+              <div
+                style={{
+                  cursor: 'pointer',
+                  display: 'inline-block',
+                  fontSize: 14,
+                  fontFamily: 'SourceHanSansCN-Normal, SourceHanSansCN',
+                  fontWeight: 400,
+                  color: '#ffffff',
+                  lineHeight: '24px',
+                  padding: '0 14px',
+                  background: '#3377ff',
+                  borderRadius: 4,
+                }}
+              >
+                批量删除
+              </div>
+            </Popconfirm>
+          </div>
+        </div>
+      )}
+      {/* 查核条件选择弹窗 */}
+      <Modal
+        title="选择查核条件(病房)"
+        open={conditionModalVisible}
+        onCancel={() => setConditionModalVisible(false)}
+        width={532}
+        footer={[
+          <Button key="cancel" onClick={() => setConditionModalVisible(false)}>
+            取消
+          </Button>,
+          <Button
+            key="confirm"
+            type="primary"
+            onClick={handleConfirmConditions}
+          >
+            确定
+            {selectedConditions.length > 0
+              ? `(${selectedConditions.length})`
+              : ''}
+          </Button>,
+        ]}
+      >
+        <div style={{ marginBottom: 8 }}>
+          <div style={{ display: 'flex', gap: 8, marginBottom: 16 }}>
+            <Select
+              value={conditionSearchType}
+              onChange={(value) => {
+                setConditionSearchType(value);
+                // 切换类型时重置选择状态和搜索关键词
+                setSelectedConditions([]);
+                setConditionSearchKeyword('');
+                // 切换类型时重新拉取数据
+                conditionTableRef.current?.reload();
+              }}
+              style={{ width: 120 }}
+              options={[
+                { label: '个案', value: '个案' },
+                { label: '系统', value: '系统' },
+              ]}
+            />
+            <Input
+              placeholder="条件名称"
+              value={conditionSearchKeyword}
+              allowClear
+              onChange={(e) => {
+                setConditionSearchKeyword(e.target.value);
+                // 实时搜索,触发表格重新渲染
+                conditionTableRef.current?.reload();
+              }}
+              onPressEnter={() => conditionTableRef.current?.reload()}
+              suffix={
+                <SearchOutlined
+                  onClick={() => conditionTableRef.current?.reload()}
+                  style={{ cursor: 'pointer', color: '#99A6BF' }}
+                />
+              }
+              style={{ flex: 1 }}
+            />
+          </div>
+        </div>
+
+        <KCIMTable
+          actionRef={conditionTableRef}
+          columns={conditionColumns}
+          request={getConditionData}
+          rowKey="id"
+          search={false}
+          expandable={{
+            defaultExpandAllRows: true, // 默认展开所有行
+            childrenColumnName: 'children', // 指定子数据的字段名
+          }}
+          pagination={{
+            simple: true,
+            pageSize: 10,
+            size: 'small',
+            showTotal: () => null,
+          }}
+          rowSelection={{
+            selectedRowKeys: selectedConditions,
+            onChange: (selectedRowKeys) => {
+              setSelectedConditions(selectedRowKeys as string[]);
+            },
+            checkStrictly: false, // 启用父子关联选择
+          }}
+          tableAlertRender={false}
+          scroll={{ y: 360 }}
+        />
+      </Modal>
+
+      {/* 选择人员弹窗 */}
+      <Modal
+        title="选择人员(产科病房)"
+        open={employeeModalVisible}
+        onCancel={() => setEmployeeModalVisible(false)}
+        width={372}
+        footer={[
+          <Button key="cancel" onClick={() => setEmployeeModalVisible(false)}>
+            取消
+          </Button>,
+          <Button
+            key="confirm"
+            type="primary"
+            onClick={handleConfirmAddEmployees}
+            style={{ backgroundColor: '#3377FF', borderColor: '#3377FF' }}
+          >
+            确定
+            {selectedEmployees.length > 0
+              ? ` (${selectedEmployees.length})`
+              : ''}
+          </Button>,
+        ]}
+      >
+        <div style={{ marginBottom: 8 }}>
+          <Input
+            placeholder="请输入"
+            value={employeeSearchKeyword}
+            allowClear
+            onChange={(e) => {
+              setEmployeeSearchKeyword(e.target.value);
+              if (e.target.value === '') {
+                // 清空搜索时自动刷新
+                employeeTableRef.current?.reload();
+              }
+            }}
+            onPressEnter={() => employeeTableRef.current?.reload()}
+            suffix={
+              <SearchOutlined
+                onClick={() => employeeTableRef.current?.reload()}
+                style={{ cursor: 'pointer', color: '#99A6BF' }}
+              />
+            }
+            style={{ width: '100%' }}
+          />
+        </div>
+
+        <KCIMTable
+          actionRef={employeeTableRef}
+          columns={[
+            {
+              title: '工号',
+              dataIndex: 'empCode',
+              width: 120,
+              align: 'left',
+            },
+            {
+              title: '姓名',
+              dataIndex: 'empName',
+              align: 'left',
+            },
+          ]}
+          request={getEmployeeSelectData}
+          rowKey="id"
+          search={false}
+          pagination={{
+            simple: true,
+            pageSize: 10,
+            size: 'small',
+            showTotal: () => null,
+          }}
+          rowSelection={{
+            selectedRowKeys: selectedEmployees,
+            onChange: (selectedRowKeys) => {
+              setSelectedEmployees(selectedRowKeys as string[]);
+            },
+          }}
+          tableAlertRender={false}
+          scroll={{ y: 360 }}
+        />
+      </Modal>
+
+      {/* 选择查核要点弹窗 */}
+      <Modal
+        title={`选择查核要点(${selectedDept?.title || ''})`}
+        open={checkPointModalVisible}
+        onCancel={() => setCheckPointModalVisible(false)}
+        width={700}
+        footer={[
+          <Button key="cancel" onClick={() => setCheckPointModalVisible(false)}>
+            取消
+          </Button>,
+          <Button
+            key="confirm"
+            type="primary"
+            onClick={handleConfirmAddCheckPoints}
+          >
+            确定
+            {selectedCheckPointKeys.length > 0
+              ? `(${selectedCheckPointKeys.length})`
+              : ''}
+          </Button>,
+        ]}
+      >
+        {/* fragment 包裹,防止JSX语法错误 */}
+        <>
+          <KCIMTable
+            actionRef={checkPointTableRef}
+            columns={checkPointModalColumns}
+            request={getDeptPendingCheckpointData}
+            rowKey="id"
+            bordered
+            search={false}
+            pagination={{
+              simple: true,
+              pageSize: 10,
+              size: 'small',
+              showTotal: () => null,
+            }}
+            tableAlertRender={false}
+            scroll={{ y: 360 }}
+          />
+        </>
+      </Modal>
+    </KCIMPagecontainer>
+  );
+}

+ 262 - 0
src/pages/checkUnitMana/service.ts

@@ -0,0 +1,262 @@
+/*
+ * @Author: code4eat awesomedema@gmail.com
+ * @Date: 2025-06-10 12:15:25
+ * @LastEditors: code4eat awesomedema@gmail.com
+ * @LastEditTime: 2025-07-04 16:14:15
+ * @FilePath: /pfmBackMana/src/pages/checkUnitMana/service.ts
+ * @Description: 查核单位管理服务
+ */
+
+import { request } from 'umi';
+
+// 获取部门树
+export async function getDeptTree(params?: any) {
+  return request('/api/dept/tree', {
+    method: 'GET',
+    params,
+  });
+}
+
+// 获取部门详情
+export async function getDeptDetail(id: string) {
+  return request(`/api/dept/${id}`, {
+    method: 'GET',
+  });
+}
+
+// 获取部门人员列表
+export async function getDeptPersonnel(params: any) {
+  return request('/api/dept/personnel', {
+    method: 'GET',
+    params,
+  });
+}
+
+// 获取查核单位人员列表
+export async function getDeptEmployee(params: {
+  deptId: number;
+  filter?: string;
+  deptType?: number;
+}) {
+  return request('/manager/Department/getDeptEmployee', {
+    method: 'GET',
+    params,
+  });
+}
+
+// 获取可选择的待添加人员列表
+export async function getDeptPendingEmployee(params: {
+  deptId: number;
+  filter?: string;
+  deptType?: number;
+}) {
+  return request('/manager/Department/getDeptPendingEmployee', {
+    method: 'GET',
+    params,
+  });
+}
+
+// 添加部门人员
+export async function addDeptPersonnel(data: any) {
+  return request('/api/dept/personnel', {
+    method: 'POST',
+    data,
+  });
+}
+
+// 删除部门人员
+export async function deleteDeptPersonnel(id: string) {
+  return request(`/api/dept/personnel/${id}`, {
+    method: 'DELETE',
+  });
+}
+
+// 设置部门负责人
+export async function setDeptManager(data: {
+  empId: number;
+  empName: string;
+  deptId: number;
+  deptType: number;
+}) {
+  return request('/manager/Department/setDeptManager', {
+    method: 'POST',
+    data,
+  });
+}
+
+// 获取部门责任事项
+export async function getDeptResponsibilities(params: any) {
+  return request('/api/dept/responsibilities', {
+    method: 'GET',
+    params,
+  });
+}
+
+// 获取单位分类列表
+export async function getDeptClassList(params?: any) {
+  return request('/manager/Department/getDeptClassList', {
+    method: 'GET',
+    params,
+  });
+}
+
+// 获取单位分类树
+export async function getDeptClassTree(params?: any) {
+  return request('/manager/Department/getDeptClassTree', {
+    method: 'GET',
+    params,
+  });
+}
+
+// 新增单位分类
+export async function addDeptClass(data: { name: string; depType: number }) {
+  return request('/manager/Department/addDeptClass', {
+    method: 'POST',
+    data,
+  });
+}
+
+// 修改单位分类
+export async function updateDeptClass(data: {
+  id: number;
+  name: string;
+  depType: number;
+}) {
+  return request('/manager/Department/updateDeptClass', {
+    method: 'POST',
+    data,
+  });
+}
+
+// 删除单位分类
+export async function delDeptClass(data: number[]) {
+  return request('/manager/Department/delDeptClass', {
+    method: 'POST',
+    data,
+  });
+}
+
+// 新增查核单位
+export async function addDepartmentByClass(data: {
+  deptClassId: string;
+  name: string;
+  sort: number;
+  deptType: number;
+}) {
+  return request('/manager/Department/addDepartmentByClass', {
+    method: 'POST',
+    data,
+  });
+}
+
+// 修改查核单位
+export async function updateDepartmentByClass(data: {
+  deptClassId: number;
+  id: number;
+  name: string;
+  sort: number;
+  deptType: number;
+}) {
+  return request('/manager/Department/updateDepartmentByClass', {
+    method: 'POST',
+    data,
+  });
+}
+
+// 删除查核单位
+export async function delDepartmentByClass(params: {
+  deptId: number;
+  deptType: number;
+}) {
+  return request('/manager/Department/delDepartmentByClass', {
+    method: 'POST',
+    params,
+  });
+}
+
+// 获取条件列表
+export async function getDeptClassFilterConditionList(params: {
+  deptClassId?: string;
+  conditionType?: number;
+  filter?: string;
+}) {
+  return request('/manager/Department/getDeptclassFilterConditionList', {
+    method: 'GET',
+    params,
+  });
+}
+
+// 保存查核条件
+export async function applyDeptClassFilterCondition(data: {
+  id: string;
+  mapIdList: string[];
+}) {
+  return request('/manager/Department/applyDeptclassFilterCondition', {
+    method: 'POST',
+    data,
+  });
+}
+
+// 添加部门人员
+export async function addDeptEmployee(data: {
+  id: string; // 查核单位ID
+  type: string; // 单位类型:0普通单位1职能科室
+  mapIdList: string[]; // 人员ID列表
+}) {
+  return request('/manager/Department/addDeptEmployee', {
+    method: 'POST',
+    data,
+  });
+}
+
+// 删除单位人员
+export async function delDeptEmp(data: { id: string; type: string }[]) {
+  return request('/manager/Department/delDeptEmp', {
+    method: 'POST',
+    data,
+  });
+}
+
+// 获取可选择的查核要点列表
+export async function getDeptPendingCheckpoint(params: {
+  deptId: number | string;
+  filter?: string;
+  deptType?: number;
+}) {
+  return request('/manager/Department/getDeptPendingCheckpoint', {
+    method: 'GET',
+    params,
+  });
+}
+
+// 获取查核要点列表
+export async function getDeptCheckpoint(params: {
+  deptId: number | string;
+  filter?: string;
+  deptType?: number;
+}) {
+  return request('/manager/Department/getDeptCheckpoint', {
+    method: 'GET',
+    params,
+  });
+}
+
+// 添加查核要点
+export async function addDeptCheckpoint(data: {
+  id: string;
+  type: string;
+  mapIdList: string[];
+}) {
+  return request('/manager/Department/addDeptCheckpoint', {
+    method: 'POST',
+    data,
+  });
+}
+
+// 删除查核要点
+export async function delDeptCheckpoint(data: (string | number)[]) {
+  return request('/manager/Department/delDeptCheckpoint', {
+    method: 'POST',
+    data,
+  });
+}

+ 490 - 0
src/pages/checkUnitMana/style.less

@@ -0,0 +1,490 @@
+.check-unit-mana {
+  .check-unit-container {
+    display: flex;
+    height: 100%;
+    padding: 16px;
+  }
+
+  // 左侧科室树样式
+  .dept-tree {
+    display: flex;
+    flex-direction: column;
+    width: 220px;
+    min-width: 220px;
+    max-width: 220px;
+    background: #fff;
+    height: calc(100vh - 80px);
+    overflow: hidden;
+
+    .search-wrapper {
+      display: flex;
+      align-items: center;
+      padding: 12px;
+      padding-bottom: 8px;
+      flex-shrink: 0; // 防止搜索栏被压缩
+
+      .search-input {
+        flex: 1;
+        border-radius: 4px;
+        border: 1px solid #d9d9d9;
+
+        .ant-input {
+          border: none;
+          box-shadow: none;
+        }
+
+        &:hover,
+        &:focus-within {
+          border-color: #3377ff;
+        }
+      }
+
+      .add-button {
+        margin-left: 8px;
+        width: 24px;
+        height: 24px;
+        background: #fafcff;
+        border-radius: 4px;
+        border: 1px solid #dae2f2;
+        padding: 0;
+        display: flex;
+        align-items: center;
+        justify-content: center;
+      }
+    }
+
+    .dept-list {
+      flex: 1;
+      overflow-y: auto;
+
+      .dept-group {
+        margin-bottom: 0;
+
+        .dept-parent {
+          display: flex;
+          align-items: center;
+          padding: 6px 12px 6px 8px;
+          margin: 0 8px;
+          cursor: pointer;
+          height: 32px;
+          box-sizing: border-box;
+          border-radius: 4px;
+          justify-content: space-between;
+
+          .toggle-button {
+            padding: 0;
+            width: 16px;
+            height: 16px;
+            margin-right: 4px;
+            border: none;
+            line-height: 1;
+            display: flex;
+            align-items: center;
+            justify-content: center;
+            color: #999;
+            background: transparent;
+
+            .anticon {
+              font-size: 16px;
+            }
+
+            &:hover {
+              background: transparent;
+              color: #333;
+            }
+          }
+
+          .dept-title {
+            flex: 1;
+            font-size: 14px;
+            color: #17181A;
+          }
+
+          .more-button {
+            padding: 0;
+            width: 16px;
+            height: 16px;
+            display: flex;
+            align-items: center;
+            justify-content: center;
+            color: #999;
+            border-radius: 4px;
+            background: #fff;
+            border: 1px solid #e8e8e8;
+
+            .anticon {
+              transform: rotate(90deg);
+            }
+
+            &:hover {
+              background: #fff;
+            }
+          }
+
+          &:hover {
+            background-color: #f0f2f5;
+          }
+
+          &.selected {
+            background: #f0f2f5;
+            border: none;
+
+            .dept-title {
+              color: #17181A;
+              font-weight: 500;
+            }
+          }
+        }
+
+        .dept-leaf {
+          display: flex;
+          align-items: center;
+          justify-content: space-between;
+          padding: 0 12px 0 20px; // 左侧缩进,区别于父节点
+          margin: 0 8px;
+          cursor: pointer;
+          height: 32px;
+          box-sizing: border-box;
+          border-radius: 4px;
+
+          span {
+            flex: 1;
+            font-size: 14px;
+            color: #17181A; // 统一颜色
+          }
+
+          &:hover {
+            background-color: #f0f2f5;
+          }
+
+          &.selected {
+            background: #f0f2f5;
+            border: none;
+
+            span {
+              color: #17181A;
+              font-weight: 500;
+            }
+          }
+
+          .more-button {
+            padding: 0;
+            width: 16px;
+            height: 16px;
+            display: flex;
+            align-items: center;
+            justify-content: center;
+            color: #999;
+            border-radius: 4px;
+            background: #fff;
+            border: 1px solid #e8e8e8;
+
+            .anticon {
+              transform: rotate(90deg);
+            }
+
+            &:hover {
+              background: #fff;
+            }
+          }
+
+          .dept-title {
+            flex: 1;
+            font-size: 14px;
+            color: #17181A;
+          }
+
+          &:hover {
+            background-color: #f0f2f5;
+          }
+        }
+
+        .dept-children {
+          padding: 4px 0;
+
+          .dept-item {
+            display: flex;
+            align-items: center;
+            justify-content: space-between;
+            padding: 0 12px 0 36px;
+            margin: 0 8px;
+            cursor: pointer;
+            height: 32px;
+            box-sizing: border-box;
+            border-radius: 4px;
+
+            span {
+              flex: 1;
+              font-size: 14px;
+              color: #17181A;
+            }
+
+            &:hover {
+              background-color: #f0f2f5;
+            }
+
+            &.selected {
+              background: #f0f2f5;
+              border: none;
+
+              span {
+                color: #17181A;
+                font-weight: 500;
+              }
+            }
+
+            .more-button {
+              padding: 0;
+              width: 16px;
+              height: 16px;
+              display: flex;
+              align-items: center;
+              justify-content: center;
+              color: #999;
+              border-radius: 4px;
+              background: #fff;
+    
+
+              .anticon {
+                transform: rotate(90deg);
+              }
+
+              &:hover {
+                background: #fff;
+              }
+            }
+          }
+        }
+
+        .dept-empty {
+          padding: 6px 10px 6px 30px;
+          margin: 0 8px;
+          color: #999;
+          font-size: 13px;
+        }
+      }
+
+      // 叶子节点样式(临床诊疗、后勤支援等)
+      .dept-leaf-node {
+        display: flex;
+        align-items: center;
+        padding: 6px 8px;
+        margin: 4px 8px;
+        cursor: pointer;
+        height: 32px;
+        box-sizing: border-box;
+        background: transparent;
+        border-radius: 0;
+
+        .leaf-button {
+          padding: 0;
+          width: 20px;
+          height: 20px;
+          margin-right: 8px;
+          border: none;
+          display: flex;
+          align-items: center;
+          justify-content: center;
+          color: #666;
+
+          .anticon {
+            font-size: 12px;
+          }
+        }
+
+        .leaf-title {
+          flex: 1;
+          font-size: 14px;
+          color: #17181A;
+        }
+
+        &:hover {
+          background: #f5f5f5;
+        }
+      }
+    }
+  }
+
+  // 右侧内容区样式
+  .dept-content {
+    flex: 1;
+    padding-left: 16px;
+    height: 100%;
+    overflow: auto;
+
+    .pfm-ant-card {
+      box-shadow: none;
+      background: #fff;
+      border-radius: 4px;
+
+      .pfm-ant-card-body {
+        padding: 16px;
+      }
+    }
+
+    .dept-header {
+      padding: 0 0 16px;
+      border-bottom: 1px solid #e6eaf2;
+      display: flex;
+      align-items: baseline;
+      gap: 8px;
+
+      .dept-title {
+        font-size: 20px;
+        font-weight: 600;
+        color: #17181A;
+      }
+
+      .dept-info {
+        font-size: 14px;
+        color: #515866;
+      }
+    }
+
+    .pfm-ant-tabs-nav {
+      margin-bottom: 16px;
+    }
+
+    .tab-content {
+      .toolbar {
+        display: flex;
+        justify-content: space-between;
+        align-items: center;
+        margin-bottom: 16px;
+      }
+
+      .add-btn {
+        background-color: #3377ff;
+        border-color: #3377ff;
+        border-radius: 4px;
+        height: 24px;
+        box-shadow: none;
+        font-weight: 400;
+        font-size: 14px;
+        color: #ffffff;
+        width: 56px;
+        padding: 0;
+        line-height: 22px;
+
+        &:hover {
+          background-color: #5591ff;
+          border-color: #5591ff;
+        }
+
+        &:active {
+          background-color: #1b5ee6;
+          border-color: #1b5ee6;
+        }
+      }
+    }
+  }
+
+  // 表格样式
+  /* 表格样式已统一至 KCIMTable/style.less */
+
+  .delete-btn {
+    color: #3377ff;
+  }
+
+  // 员工选择相关样式
+  .employee-select-modal {
+    .pfm-ant-modal-header {
+      border-bottom: 1px solid #f0f0f0;
+      padding: 12px 16px;
+
+      .pfm-ant-modal-title {
+        font-size: 14px;
+        font-weight: 500;
+        color: #17181A;
+      }
+    }
+
+    .pfm-ant-modal-close {
+      top: 12px;
+    }
+
+    .pfm-ant-modal-body {
+      padding: 16px;
+      padding-bottom: 8px;
+    }
+
+    .pfm-ant-modal-footer {
+      border-top: 1px solid #f0f0f0;
+      padding: 12px 16px;
+    }
+
+    .blue-checkbox {
+      .pfm-ant-checkbox-inner {
+        border-color: #3377ff;
+        border-radius: 2px;
+      }
+
+      &.pfm-ant-checkbox-checked .pfm-ant-checkbox-inner {
+        background-color: #3377ff;
+        border-color: #3377ff;
+      }
+    }
+
+    .pfm-ant-btn-primary {
+      border-radius: 2px;
+    }
+  }
+
+  // 复选框样式调整
+  .pfm-ant-checkbox-checked .pfm-ant-checkbox-inner {
+    background-color: #3377ff;
+    border-color: #3377ff;
+  }
+
+  .pfm-ant-checkbox-wrapper:hover .pfm-ant-checkbox-inner,
+  .pfm-ant-checkbox:hover .pfm-ant-checkbox-inner,
+  .pfm-ant-checkbox-input:focus + .pfm-ant-checkbox-inner {
+    border-color: #3377ff;
+  }
+
+  // 按钮样式调整
+  .pfm-ant-btn-primary {
+    &:hover,
+    &:focus {
+      background-color: #5591ff;
+      border-color: #5591ff;
+    }
+
+    &:active {
+      background-color: #1b5ee6;
+      border-color: #1b5ee6;
+    }
+  }
+}
+
+// 为新增按钮的下拉菜单设置特定样式
+.check-unit-add-dropdown {
+  .pfm-ant-dropdown-menu {
+    border-radius: 4px;
+    overflow: hidden;
+    padding: 4px;
+  }
+
+  .pfm-ant-dropdown-menu-item {
+    &:hover,
+    &.pfm-ant-dropdown-menu-item-selected {
+      background: rgba(51, 119, 255, 0.08);
+      color: #17181a;
+      border-radius: 4px;
+    }
+  }
+}
+
+.content-padding {
+  padding-left: 16px;
+  padding-right: 16px;
+}
+
+.dept-title-text {
+  display: inline-block;
+  max-width: 180px;
+  overflow: hidden;
+  text-overflow: ellipsis;
+  white-space: nowrap;
+  vertical-align: middle;
+}

+ 352 - 305
src/pages/setting/fenyeTemplate/index.tsx

@@ -2,23 +2,30 @@
  * @Author: code4eat awesomedema@gmail.com
  * @Date: 2023-03-03 11:30:33
  * @LastEditors: code4eat awesomedema@gmail.com
- * @LastEditTime: 2023-09-27 14:46:46
+ * @LastEditTime: 2023-10-09 14:51:33
  * @FilePath: /KC-MiddlePlatform/src/pages/platform/setting/pubDicTypeMana/index.tsx
  * @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
  */
 
-
-
-
-
-
 import { createFromIconfontCN } from '@ant-design/icons';
 
-import { ActionType, DrawerForm, EditableFormInstance, EditableProTable, ProFormSelect } from '@ant-design/pro-components';
-import { ModalForm, ProFormDigit, ProFormText, ProFormTextArea } from '@ant-design/pro-form'
+import {
+  ActionType,
+  DrawerForm,
+  EditableFormInstance,
+  EditableProTable,
+  ProFormInstance,
+  ProFormSelect,
+} from '@ant-design/pro-components';
+import {
+  ModalForm,
+  ProFormDigit,
+  ProFormText,
+  ProFormTextArea,
+} from '@ant-design/pro-form';
 import { ProColumns } from '@ant-design/pro-table';
 import { Input, message, Popconfirm } from 'antd';
-import { useEffect, useRef, useState } from 'react'
+import { useEffect, useRef, useState } from 'react';
 
 import { addData, delData, editData, getData } from './service';
 
@@ -26,323 +33,363 @@ import './style.less';
 
 import KCIMPagecontainer from '@/components/KCIMPageContainer';
 import { KCIMTable } from '@/components/KCIMTable';
+import KCIMDrawerForm from '@/components/KCIMDrawerForm';
 
 const IconFont = createFromIconfontCN({
-    scriptUrl: '',
+  scriptUrl: '',
 });
 
-const resultTypeList = [
+type TableDataResponse = {
+  data: any[];
+  success: boolean;
+  total: number;
+  pageSize: number;
+  totalPage: number;
+};
+
+export default function FenyeTemplate() {
+  const [tableDataFilterParams, set_tableDataFilterParams] = useState<
+    any | undefined
+  >();
+  const [tableDataSearchKeywords, set_tableDataSearchKeywords] =
+    useState<string>('');
+  const tableRef = useRef<ActionType>();
+
+  const columns: ProColumns[] = [
+    {
+      title: 'ID',
+      dataIndex: 'id',
+    },
     {
-        label: '不适用', value: 1
+      title: '模板名称',
+      dataIndex: 'name',
     },
     {
-        label: '无缺失', value: 1
+      title: '创建时间',
+      dataIndex: 'date',
     },
     {
-        label: '无需操作', value: 1
+      title: '创建人',
+      dataIndex: 'createUserName',
     },
     {
-        label: '需使用改善工具', value: 3
+      title: '备注说明',
+      dataIndex: 'remark',
     },
     {
-        label: '需改善回复', value: 2
+      title: '操作',
+      key: 'option',
+      width: 120,
+      valueType: 'option',
+      render: (_: any, record: any) => {
+        return [
+          <UpDataActBtn key={'act'} record={record} type="EDIT" />,
+          <Popconfirm
+            title="是否确认作废?"
+            key="del"
+            onConfirm={() => delTableData(record)}
+          >
+            <a>作废</a>
+          </Popconfirm>,
+        ];
+      },
+    },
+  ];
+
+  const getTableData = async (
+    params: any,
+  ): Promise<TableDataResponse | any[]> => {
+    const resp = await getData(params);
+    if (resp) {
+      if (resp.totalCount == 0 && resp.currPage != 1) {
+        return getTableData({ ...params, current: resp.currPage - 1 });
+      } else {
+        return {
+          data: resp.list,
+          success: true,
+          total: resp.totalCount,
+          pageSize: resp.pageSize,
+          totalPage: resp.totalPage,
+        };
+      }
+    }
+    return [];
+  };
+
+  const delTableData = async (record: any) => {
+    const resp = await delData(record.id);
+    if (resp) {
+      message.success('操作成功!');
+      tableRef.current?.reload();
+      // message.success('操作成功!');
+    }
+  };
+
+  const updateTable = async (formVal: any, type: 'EDIT' | 'ADD') => {
+    const content = formVal.table.map((a: any) => ({}));
+
+    if (type == 'ADD') {
+      const result = {
+        name: formVal.name,
+        content: JSON.stringify(formVal.table),
+        remark: formVal.remark,
+      };
+      const resp = await addData({ ...result });
+      if (resp) {
+        tableRef.current?.reload();
+        message.success('操作成功!');
+      }
+    }
+    if (type == 'EDIT') {
+      const result = {
+        id: formVal.id,
+        name: formVal.name,
+        content: JSON.stringify(formVal.table),
+        remark: formVal.remark,
+      };
+      const resp = await editData({ ...result });
+      if (resp) {
+        tableRef.current?.reload();
+        message.success('操作成功!');
+      }
     }
-]
-
-export default function FenyeTemplate() {
-
-    const [tableDataFilterParams, set_tableDataFilterParams] = useState<any | undefined>();
-    const [tableDataSearchKeywords, set_tableDataSearchKeywords] = useState<string>('');
-    const tableRef = useRef<ActionType>();
-
-    const columns: ProColumns[] = [
 
-        {
-            title: 'Id',
-            dataIndex: 'id',
-        },
-        {
-            title: '模板名称',
-            dataIndex: 'name',
-        },
-        {
-            title: '创建时间',
-            dataIndex: 'date',
-        },
-        {
-            title: '创建人',
-            dataIndex: 'createUserName',
+    return true;
+  };
+
+  const UpDataActBtn = ({
+    record,
+    type,
+  }: {
+    record: any;
+    type: 'EDIT' | 'ADD';
+  }) => {
+    const editableFormRef = useRef<EditableFormInstance>();
+    const formRef = useRef<ProFormInstance<any>>();
+    const editorFormRef = useRef<EditableFormInstance<any>>();
+
+    const editableColumns: ProColumns[] = [
+      {
+        title: '标题',
+        dataIndex: 'title',
+        editable: () => true,
+      },
+      {
+        title: '类型',
+        dataIndex: 'filedType',
+        editable: () => false,
+      },
+      {
+        title: '是否必填',
+        dataIndex: 'required',
+        request: async () => [
+          {
+            value: 1,
+            label: '是',
+          },
+          {
+            value: 0,
+            label: '否',
+          },
+        ],
+        fieldProps: (_, { rowIndex }) => {
+          return {
+            onSelect: () => {
+              // 每次选中重置参数
+              editableFormRef.current?.setRowData?.(rowIndex, { fraction: [] });
+            },
+          };
         },
-        {
-            title: '备注说明',
-            dataIndex: 'remark',
+        renderText(text) {
+          return text ? '是' : '否';
         },
-        {
-            title: '操作',
-            key: 'option',
-            width: 120,
-            valueType: 'option',
-            render: (_: any, record: any) => {
-                return [
-                    <UpDataActBtn key={'act'} record={record} type='EDIT' />,
-                    <Popconfirm
-                        title="是否确认作废?"
-                        key="del"
-                        onConfirm={() => delTableData(record)}
-                    >
-                        <a>作废</a>
-                    </Popconfirm>
-                ]
+      },
+      {
+        title: '是否展示信息',
+        dataIndex: 'showFiled',
+        request: async () => [
+          {
+            value: 1,
+            label: '是',
+          },
+          {
+            value: 0,
+            label: '否',
+          },
+        ],
+        fieldProps: (_, { rowIndex }) => {
+          return {
+            onSelect: () => {
+              // 每次选中重置参数
+              editableFormRef.current?.setRowData?.(rowIndex, { fraction: [] });
             },
+          };
         },
+        renderText(text) {
+          return text ? '是' : '否';
+        },
+      },
+      {
+        title: '操作',
+        valueType: 'option',
+        render: (text, record, _, action) => [
+          <a
+            key="editable"
+            onClick={() => {
+              console.log({ record });
+              action?.startEditable?.(record.tempId);
+            }}
+          >
+            编辑
+          </a>,
+          <a
+            key="delete"
+            onClick={() => {
+              const tableDataSource = formRef.current?.getFieldValue(
+                'table',
+              ) as any[];
+              formRef.current?.setFieldsValue({
+                table: tableDataSource.filter(
+                  (item) => item.tempId !== record.tempId,
+                ),
+              });
+            }}
+          >
+            删除
+          </a>,
+        ],
+      },
+    ];
+
+    let defaultTableData = JSON.parse(record.content ? record.content : '[]');
+    defaultTableData = defaultTableData.map((a: any) => ({
+      ...a,
+      tempId: (Math.random() * 1000000).toFixed(0),
+    }));
+
+    const [tempData, setTempData] = useState<any[]>(defaultTableData);
+    // console.log({defaultTableData});
 
-    ]
-
-
-    const getTableData = async (params: any) => {
-        const resp = await getData(params);
-        if (resp) {
-            return {
-                data: resp.list,
-                success: true,
-                total: resp.totalCount,
-                pageSize: resp.pageSize,
-                totalPage: resp.totalPage,
-            }
-        }
-        return []
-    }
-
-    const delTableData = async (record: any) => {
-        const resp = await delData(record.id);
-        if (resp) {
-            message.success('操作成功!');
-            tableRef.current?.reload();
-            // message.success('操作成功!');
-        }
-    }
-
-    const updateTable = async (formVal: any, type: 'EDIT' | "ADD") => {
-
-        const content = formVal.table.map((a:any)=>({
-               
-        }))
-
-        if (type == 'ADD') {
-            const result = {
-                name:formVal.name,
-                content:JSON.stringify(formVal.table),
-                remark:formVal.remark
-            }
-            const resp = await addData({ ...result });
-            if (resp) {
-                tableRef.current?.reload();
-                message.success('操作成功!');
-            }
-            
+    return (
+      <KCIMDrawerForm
+        title={`${type == 'EDIT' ? '编辑' : '新增'}模板数据`}
+        width={800}
+        initialValues={
+          type == 'EDIT'
+            ? { ...record, table: defaultTableData, filedType: 'Text' }
+            : { filedType: 'Text' }
         }
-        if (type == 'EDIT') {
-            const result = {
-                id:formVal.id,
-                name:formVal.name,
-                content:JSON.stringify(formVal.table),
-                remark:formVal.remark
-            }
-            const resp = await editData({ ...result });
-            if (resp) {
-                tableRef.current?.reload();
-                message.success('操作成功!');
-            }
+        trigger={
+          type == 'EDIT' ? (
+            <a key="edit">编辑</a>
+          ) : (
+            <span className="add">新增</span>
+          )
         }
-
-        return true;
-    }
-
-    const UpDataActBtn = ({ record, type }: { record: any, type: 'EDIT' | 'ADD' }) => {
-
-        const editableFormRef = useRef<EditableFormInstance>();
-
-        const editableColumns:ProColumns[] = [
-            {
-                title:'标题',
-                dataIndex: 'title',
-                editable:()=>true
-            },
-            {
-                title:'类型',
-                dataIndex: 'filedType', 
-                editable:()=>false
-            },
-            {
-                title:'是否必填',
-                dataIndex: 'required',
-                request: async () => [
-                    {
-                      value: 1,
-                      label: '是',
-                    },
-                    {
-                      value: 0,
-                      label: '否',
-                    },
-                ],
-                fieldProps: (_, { rowIndex }) => {
-                    return {
-                      onSelect: () => {
-                        // 每次选中重置参数
-                        editableFormRef.current?.setRowData?.(rowIndex, { fraction: [] });
-                      },
-                    };
-                },
-                renderText(text) {
-                      return text?'是':'否'
-                },
-            },
-            {
-                title:'是否展示信息',
-                dataIndex: 'showFiled',
-                request: async () => [
-                    {
-                      value: 1,
-                      label: '是',
-                    },
-                    {
-                      value: 0,
-                      label: '否',
-                    },
-                ],
-                fieldProps: (_, { rowIndex }) => {
-                    return {
-                      onSelect: () => {
-                        // 每次选中重置参数
-                        editableFormRef.current?.setRowData?.(rowIndex, { fraction: [] });
-                      },
-                    };
-                },
-                renderText(text) {
-                    return text?'是':'否'
-                },
-            },
-            {
-                title: '操作',
-                valueType: 'option',
-                render: (text, record, _, action) => [
-                  <a
-                    key="editable"
-                    onClick={() => {
-                      action?.startEditable?.(record.tempId);
-                    }}
-                  >
-                    编辑
-                  </a>,
-                  <a
-                    key="delete"
-                    onClick={() => {
-                      //setDataSource(dataSource.filter((item) => item.id !== record.id));
-                    }}
-                  >
-                    删除
-                  </a>,
-                ],
-              },
-        ];
-
-        let defaultTableData = JSON.parse(record.content?record.content:'[]');
-        defaultTableData = defaultTableData.map((a:any)=>({...a,tempId:Math.random()}))
-        // console.log({defaultTableData});
-
-        return (
-            <DrawerForm
-                title={`${type == 'EDIT' ? '编辑' : '新增'}模板数据`}
-                width={800}
-                initialValues={type == 'EDIT' ? { ...record,table:defaultTableData,filedType:'Text'} : {filedType:'Text'}}
-                trigger={
-                    type == 'EDIT' ? <a key="edit" >编辑</a> : <span className='add'>新增</span>
-                }
-                onFinish={(val) => {
-                    return updateTable(type == 'EDIT' ? { ...record, ...val } : { ...val }, type);
-                }}
-                drawerProps={{ destroyOnClose: true }}
-                colProps={{ span: 24 }}
-                grid
-            >
-                <ProFormText
-                    name="name"
-                    label="模板名称:"
-                    placeholder="请输入"
-                    width={300}
-                    rules={[{ required: true, message: '名称不能为空!' }]}
-                />
-                <ProFormTextArea
-                    name="remark"
-                    label="备注说明:"
-                    placeholder="请输入"
+        onFinish={(val: any) => {
+          return updateTable(
+            type == 'EDIT' ? { ...record, ...val } : { ...val },
+            type,
+          );
+        }}
+        formRef={formRef}
+        drawerProps={{ destroyOnClose: true }}
+        colProps={{ span: 24 }}
+        grid
+      >
+        <ProFormText
+          name="name"
+          label="模板名称:"
+          placeholder="请输入"
+          width={300}
+          rules={[{ required: true, message: '名称不能为空!' }]}
+        />
+        <ProFormTextArea
+          name="remark"
+          label="备注说明:"
+          placeholder="请输入"
+        />
+
+        <EditableProTable
+          className="pfm-EditableProTable"
+          name="table"
+          style={{ margin: '0 4px' }}
+          rowKey={'tempId'}
+          editableFormRef={editorFormRef}
+          tableStyle={{ padding: 0 }}
+          columns={editableColumns}
+          // controlled
+          editable={{}}
+          recordCreatorProps={{
+            record: () => ({
+              tempId: (Math.random() * 1000000).toFixed(0),
+              filedType: 'Text',
+            }),
+          }}
+        />
+      </KCIMDrawerForm>
+    );
+  };
+
+  const tableDataSearchHandle = (paramName: string) => {
+    set_tableDataFilterParams({
+      ...tableDataFilterParams,
+      [`${paramName}`]: tableDataSearchKeywords,
+    });
+  };
+
+  useEffect(() => {}, []);
+
+  return (
+    <KCIMPagecontainer className="FenyeTemplate" title={false}>
+      <div className="toolBar">
+        <div className="filter">
+          <div className="filterItem" style={{ marginRight: 16, width: 205 }}>
+            <span className="label" style={{ whiteSpace: 'nowrap' }}>
+              {' '}
+              检索:
+            </span>
+            <Input
+              placeholder={'请输入模板名称'}
+              allowClear
+              suffix={
+                <IconFont
+                  type="iconsousuo"
+                  style={{ color: '#99A6BF' }}
+                  onClick={() => tableDataSearchHandle('filter')}
                 />
-
-                <EditableProTable 
-                    name='table'
-                    rowKey={'tempId'}
-                    tableStyle={{padding:0}}
-                    columns={editableColumns}
-                    recordCreatorProps={{
-                        record: () => ({ tempId: (Math.random() * 1000000).toFixed(0),filedType:'Text' }),
-                    }}
-                />
-            </DrawerForm>
-        )
-    }
-
-    const tableDataSearchHandle = (paramName: string) => {
-
-
-        set_tableDataFilterParams({
-            ...tableDataFilterParams,
-            [`${paramName}`]: tableDataSearchKeywords
-        })
-    }
-
-
-    useEffect(() => {
-
-    }, [])
-
-    return (
-        <KCIMPagecontainer className='FenyeTemplate' title={false}>
-            <div className='toolBar'>
-                <div className='filter'>
-                    <div className='filterItem' style={{ marginRight: 16, width: 205 }}>
-                        <span className='label' style={{ whiteSpace: 'nowrap' }}> 检索:</span>
-                        <Input placeholder={'请输入模板名称'} allowClear
-                            suffix={
-                                <IconFont type="iconsousuo" style={{ color: '#99A6BF' }} onClick={() => tableDataSearchHandle('filter')} />
-                            }
-                            onChange={(e) => {
-                                set_tableDataSearchKeywords(e.target.value);
-                                if (e.target.value.length == 0) {
-                                    set_tableDataFilterParams({
-                                        ...tableDataFilterParams,
-                                        filter: ''
-                                    });
-                                }
-                            }}
-                            onPressEnter={(e) => {
-
-                                set_tableDataFilterParams({
-                                    ...tableDataFilterParams,
-                                    filter: (e.target as HTMLInputElement).value
-                                });
-                            }}
-
-                        />
-                    </div>
-                </div>
-                <div className='btnGroup'>
-                    <UpDataActBtn record type='ADD' />
-                </div>
-            </div>
-            <div style={{ marginTop: 16 }}>
-                <KCIMTable columns={columns as ProColumns[]} actionRef={tableRef} rowKey='id' params={tableDataFilterParams} request={(params) => getTableData(params)} />
-            </div>
-        </KCIMPagecontainer>
-    )
+              }
+              onChange={(e) => {
+                set_tableDataSearchKeywords(e.target.value);
+                if (e.target.value.length == 0) {
+                  set_tableDataFilterParams({
+                    ...tableDataFilterParams,
+                    filter: '',
+                  });
+                }
+              }}
+              onPressEnter={(e) => {
+                set_tableDataFilterParams({
+                  ...tableDataFilterParams,
+                  filter: (e.target as HTMLInputElement).value,
+                });
+              }}
+            />
+          </div>
+        </div>
+        <div className="btnGroup">
+          <UpDataActBtn record type="ADD" />
+        </div>
+      </div>
+      <div style={{ marginTop: 16 }}>
+        <KCIMTable
+          columns={columns as ProColumns[]}
+          actionRef={tableRef}
+          rowKey="id"
+          params={tableDataFilterParams}
+          request={(params) => getTableData(params)}
+        />
+      </div>
+    </KCIMPagecontainer>
+  );
 }

+ 27 - 40
src/pages/setting/fenyeTemplate/service.ts

@@ -7,74 +7,61 @@
  * @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
  */
 
-
-
 import { request } from 'umi';
 
 //获取table列表数据
 
 export type FenyeTemplateItemType = {
-  id:number;
-  hiId:number;
-  name:string;
-  content:string;
-  remark:string;
-  createUserId:number;
-  createUserName:string;
-  date:string
-}
-
+  id: number;
+  hiId: number;
+  name: string;
+  content: string;
+  remark: string;
+  createUserId: number;
+  createUserName: string;
+  date: string;
+};
 
-export const getData = (params?:any) => {
+export const getData = (params?: any) => {
   return request<{
-       current:number;
-       list:FenyeTemplateItemType[];
-       pageSize:number;
-       totalCount:number;
-       totalPage:number;
+    currPage: number;
+    current: number;
+    list: FenyeTemplateItemType[];
+    pageSize: number;
+    totalCount: number;
+    totalPage: number;
   }>('/manager/setting/getPageTemplateList', {
     method: 'GET',
-    params:{...params}
+    params: { ...params },
   });
 };
 
-
 //新增表格数据
 export type AddTableDataType = {
-  name:string;
-  content:string;
-  remark:string;
-}
-export const addData = (data:AddTableDataType) => {
+  name: string;
+  content: string;
+  remark: string;
+};
+export const addData = (data: AddTableDataType) => {
   return request('/manager/setting/addPageTemplate', {
     method: 'POST',
-    data
+    data,
   });
 };
 
-
-
 //编辑表格数据
 
-export const editData = (data:FenyeTemplateItemType) => {
+export const editData = (data: { id: number } & AddTableDataType) => {
   return request('/manager/setting/editPageTemplate', {
     method: 'POST',
-    data
+    data,
   });
 };
 
 //删除表格操作
-export const delData = (id:string) => {
+export const delData = (id: string) => {
   return request('/manager/setting/deletePageTemplate', {
     method: 'POST',
-    params:{id}
+    params: { id },
   });
 };
-
-
-
-
-
-
-
-

+ 4 - 5
src/pages/setting/fenyeTemplate/style.less

@@ -1,9 +1,9 @@
 .FenyeTemplate {
   padding: 16px;
-  background: #FFFFFF;
+  margin: 16px;
+  background: #ffffff;
   border-radius: 4px;
 
-
   .toolBar {
     display: flex;
     flex-direction: row;
@@ -31,13 +31,12 @@
         font-size: 14px;
         font-family: SourceHanSansCN-Normal, SourceHanSansCN;
         font-weight: 400;
-        color: #FFFFFF;
+        color: #ffffff;
         line-height: 24px;
         padding: 0 14px;
-        background: #3377FF;
+        background: #3377ff;
         border-radius: 4px;
       }
     }
-
   }
 }

+ 362 - 266
src/pages/setting/qualitativeOptionsMana/index.tsx

@@ -2,25 +2,35 @@
  * @Author: code4eat awesomedema@gmail.com
  * @Date: 2023-03-03 11:30:33
  * @LastEditors: code4eat awesomedema@gmail.com
- * @LastEditTime: 2023-09-27 11:00:08
+ * @LastEditTime: 2025-07-09 17:35:25
  * @FilePath: /KC-MiddlePlatform/src/pages/platform/setting/pubDicTypeMana/index.tsx
  * @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
  */
 
-
-
-
-
-
 import { createFromIconfontCN } from '@ant-design/icons';
 
-import { ActionType, ProFormSelect } from '@ant-design/pro-components';
-import { ModalForm, ProFormDigit, ProFormText, ProFormTextArea } from '@ant-design/pro-form'
+import {
+  ActionType,
+  ProFormInstance,
+  ProFormSelect,
+} from '@ant-design/pro-components';
+import {
+  ModalForm,
+  ProFormDigit,
+  ProFormText,
+  ProFormTextArea,
+} from '@ant-design/pro-form';
 import { ProColumns } from '@ant-design/pro-table';
 import { Input, message, Popconfirm } from 'antd';
-import { useEffect, useRef, useState } from 'react'
+import { useEffect, useRef, useState } from 'react';
 
-import { addData, delData, editData, getData } from './service';
+import {
+  addData,
+  delData,
+  editData,
+  getColorFromDic,
+  getData,
+} from './service';
 
 import './style.less';
 
@@ -28,283 +38,369 @@ import KCIMPagecontainer from '@/components/KCIMPageContainer';
 import { KCIMTable } from '@/components/KCIMTable';
 
 const IconFont = createFromIconfontCN({
-    scriptUrl: '',
+  scriptUrl: '',
 });
 
+type TableDataResponse = {
+  data: any[];
+  success: boolean;
+  total: number;
+  pageSize: number;
+  totalPage: number;
+};
+
 const resultTypeList = [
+  {
+    label: '无需操作',
+    value: 1,
+  },
+  {
+    label: '需改善回复',
+    value: 2,
+  },
+  {
+    label: '需使用改善工具',
+    value: 3,
+  },
+];
+
+export default function DicClassfication() {
+  const [tableDataFilterParams, set_tableDataFilterParams] = useState<
+    any | undefined
+  >();
+  const [tableDataSearchKeywords, set_tableDataSearchKeywords] =
+    useState<string>('');
+  const tableRef = useRef<ActionType>();
+  const modalFormRef = useRef<ProFormInstance>();
+  const [pagination, set_pagination] = useState({
+    total: 0,
+    pageSize: 10,
+    current: 1,
+  });
+
+  const columns: ProColumns[] = [
     {
-        label: '不适用', value: 1
+      title: 'ID',
+      dataIndex: 'id',
     },
     {
-        label: '无缺失', value: 1
+      title: '定性选项',
+      dataIndex: 'name',
     },
     {
-        label: '无需操作', value: 1
+      title: '选项编码',
+      dataIndex: 'code',
     },
     {
-        label: '需使用改善工具', value: 3
+      title: '类型',
+      dataIndex: 'resultTypeName',
+    },
+    {
+      title: '是否默认',
+      dataIndex: 'defaultFlag',
+      renderText(num, record, index, action) {
+        return num == 1 ? '是' : '否';
+      },
+    },
+    {
+      title: '排序序号',
+      dataIndex: 'sort',
+    },
+    {
+      title: '默认颜色',
+      dataIndex: 'color',
+      renderText(text, record, index, action) {
+        return (
+          <span
+            style={{
+              display: 'inline-block',
+              backgroundColor: `#${text}`,
+              padding: 2,
+              borderRadius: 4,
+              color: '#fff',
+            }}
+          >
+            {text}
+          </span>
+        );
+      },
     },
     {
-        label: '需改善回复', value: 2
+      title: '操作',
+      key: 'option',
+      width: 120,
+      valueType: 'option',
+      render: (_: any, record: any) => {
+        return [
+          <UpDataActBtn key={'act'} record={record} type="EDIT" />,
+          <Popconfirm
+            title="是否确认作废?"
+            key="del"
+            onConfirm={() => delTableData(record)}
+          >
+            <a>作废</a>
+          </Popconfirm>,
+        ];
+      },
+    },
+  ];
+
+  const getTableData = async (
+    params: any,
+  ): Promise<TableDataResponse | any[]> => {
+    const resp = await getData({ ...params });
+    if (resp) {
+      if (resp.totalCount == 0 && resp.currPage != 1) {
+        return getTableData({ ...params, current: resp.currPage - 1 });
+      } else {
+        return {
+          data: resp.list,
+          success: true,
+          total: resp.totalCount,
+          pageSize: resp.pageSize,
+          totalPage: resp.totalPage,
+        };
+      }
     }
-]
-
-export default function DicClassfication() {
-
-    const [tableDataFilterParams, set_tableDataFilterParams] = useState<any | undefined>();
-    const [tableDataSearchKeywords, set_tableDataSearchKeywords] = useState<string>('');
-    const tableRef = useRef<ActionType>();
-
-    const columns: ProColumns[] = [
-
-        {
-            title: 'Id',
-            dataIndex: 'id',
-        },
-        {
-            title: '定性选项',
-            dataIndex: 'name',
-        },
-        {
-            title: '选项编码',
-            dataIndex: 'code',
-        },
-        {
-            title: '类型',
-            dataIndex: 'resultTypeName',
-        },
-        {
-            title: '是否默认',
-            dataIndex: 'defaultFlag',
-            renderText(num, record, index, action) {
-                return num == 1 ? '是' : '否'
-            },
-        },
-        {
-            title: '排序序号',
-            dataIndex: 'sort',
-        },
-        {
-            title: '默认颜色',
-            dataIndex: 'color',
-            renderText(text, record, index, action) {
-                return <span style={{ display: 'inline-block', backgroundColor: `#${text}`, padding: 2, borderRadius: 4, color: '#fff' }}>{text}</span>
-            },
-        },
-        {
-            title: '操作',
-            key: 'option',
-            width: 120,
-            valueType: 'option',
-            render: (_: any, record: any) => {
-                return [
-                    <UpDataActBtn key={'act'} record={record} type='EDIT' />,
-                    <Popconfirm
-                        title="是否确认作废?"
-                        key="del"
-                        onConfirm={() => delTableData(record)}
-                    >
-                        <a>作废</a>
-                    </Popconfirm>
-                ]
-            },
-        },
-
-    ]
-
-
-    const getTableData = async (params: any) => {
-        const resp = await getData(params);
-        if (resp) {
-            return {
-                data: resp.list,
-                success: true,
-                total: resp.totalCount,
-                pageSize: resp.pageSize,
-                totalPage: resp.totalPage,
-            }
-        }
-        return []
+    return [];
+  };
+
+  const delTableData = async (record: any) => {
+    const resp = await delData(record.id);
+    if (resp) {
+      message.success('操作成功!');
+      tableRef.current?.reload();
+      // message.success('操作成功!');
     }
-
-    const delTableData = async (record: any) => {
-        const resp = await delData(record.id);
+  };
+
+  const updateTable = async (formVal: any, type: 'EDIT' | 'ADD') => {
+    if (type == 'ADD') {
+      const currentHosp = localStorage.getItem('currentSelectedSubHop');
+      const currentUser = localStorage.getItem('userData');
+      if (currentHosp && currentUser) {
+        const { id } = JSON.parse(currentHosp);
+        const { userId } = JSON.parse(currentUser);
+        const result = {
+          hiId: id,
+          code: formVal.code,
+          name: formVal.name,
+          resultType: formVal.resultType,
+          defaultFlag: formVal.defaultFlag,
+          sort: formVal.sort,
+          delFlag: '',
+          createUser: userId,
+          createTime: new Date().getTime(),
+          color: formVal.color,
+        };
+        const resp = await addData({ ...formVal });
         if (resp) {
-            message.success('操作成功!');
-            tableRef.current?.reload();
-            // message.success('操作成功!');
+          tableRef.current?.reload();
+          message.success('操作成功!');
         }
+      }
+    }
+    if (type == 'EDIT') {
+      const resp = await editData({ ...formVal });
+      if (resp) {
+        tableRef.current?.reload();
+        message.success('操作成功!');
+      }
     }
 
-    const updateTable = async (formVal: any, type: 'EDIT' | "ADD") => {
+    return true;
+  };
 
-        if (type == 'ADD') {
-            const currentHosp = localStorage.getItem('currentSelectedSubHop');
-            const currentUser = localStorage.getItem('userData');
-            if(currentHosp&&currentUser){
-                const {id} = JSON.parse(currentHosp);
-                const {userId} = JSON.parse(currentUser);
-                const result = {
-                    hiId:id,
-                    code:formVal.code,
-                    name:formVal.name,
-                    resultType:formVal.resultType,
-                    defaultFlag:formVal.defaultFlag,
-                    sort:formVal.sort,
-                    delFlag:'',
-                    createUser:userId,
-                    createTime:new Date().getTime(),
-                    color:formVal.color
-    
-                }
-                const resp = await addData({ ...formVal });
-                if (resp) {
-                    tableRef.current?.reload();
-                    message.success('操作成功!');
-                }
-            }
-            
+  const UpDataActBtn = ({
+    record,
+    type,
+  }: {
+    record: any;
+    type: 'EDIT' | 'ADD';
+  }) => {
+    return (
+      <ModalForm
+        title={`${type == 'EDIT' ? '编辑' : '新增'}定性选项`}
+        width={352}
+        initialValues={type == 'EDIT' ? { ...record } : {}}
+        trigger={
+          type == 'EDIT' ? (
+            <a key="edit">编辑</a>
+          ) : (
+            <span className="add">新增</span>
+          )
         }
-        if (type == 'EDIT') {
-            
-            const resp = await editData({ ...formVal });
-            if (resp) {
-                tableRef.current?.reload();
-                message.success('操作成功!');
+        formRef={modalFormRef}
+        onFinish={(val) => {
+          return updateTable(
+            type == 'EDIT' ? { ...record, ...val } : { ...val },
+            type,
+          );
+        }}
+        modalProps={{ destroyOnClose: true }}
+        colProps={{ span: 24 }}
+        grid
+      >
+        <ProFormText
+          name="name"
+          label="定性选项:"
+          placeholder="请输入"
+          rules={[{ required: true, message: '定性不能为空!' }]}
+        />
+        <ProFormText
+          name="code"
+          label="选项编码:"
+          placeholder="请输入"
+          rules={[{ required: true, message: '编码不能为空!' }]}
+        />
+        <ProFormSelect
+          label="类型:"
+          name="resultType"
+          request={async () => {
+            return resultTypeList;
+          }}
+          rules={[{ required: true, message: '类型不能为空!' }]}
+        />
+
+        <ProFormSelect
+          label="是否默认:"
+          name="defaultFlag"
+          request={async () => {
+            return [
+              { label: '是', value: 1 },
+              { label: '否', value: 0 },
+            ];
+          }}
+        />
+        <ProFormDigit name="sort" label="排序序号:" placeholder="请输入" />
+        <ProFormSelect
+          label="默认颜色:"
+          name="color"
+          request={async () => {
+            const currentSys = localStorage.getItem('currentSelectedTab');
+            if (currentSys) {
+              const { systemId } = JSON.parse(currentSys);
+              const resp = await getColorFromDic({
+                dictType: 'COLOR_TYPE',
+                systemId: systemId,
+              });
+      
+              if (resp) {
+                const list = resp.dataVoList.map((a: any) => {
+                  if (type == 'ADD' && a.defaultValue) {
+                    modalFormRef.current?.setFieldValue('color', a.value);
+                  }
+                  return {
+                    label: (
+                      <span
+                        style={{
+                          background: `#${a.value}`,
+                          color: '#fff',
+                          width: 20,
+                          height: 15,
+                          display: 'inline-block',
+                          position: 'relative',
+                          top: 1,
+                        }}
+                      >
+                        {}
+                      </span>
+                    ),
+                    value: a.value,
+                  };
+                });
+                return list;
+              }
             }
-        }
-
-        return true;
-    }
-
-    const UpDataActBtn = ({ record, type }: { record: any, type: 'EDIT' | 'ADD' }) => {
-
-        return (
-            <ModalForm
-                title={`${type == 'EDIT' ? '编辑' : '新增'}定性选项`}
-                width={352}
-                initialValues={type == 'EDIT' ? { ...record } : {}}
-                trigger={
-                    type == 'EDIT' ? <a key="edit" >编辑</a> : <span className='add'>新增</span>
-                }
-                onFinish={(val) => {
-                    return updateTable(type == 'EDIT' ? { ...record, ...val } : { ...val }, type);
-                }}
-                modalProps={{ destroyOnClose: true }}
-                colProps={{ span: 24 }}
-                grid
-            >
-
-                <ProFormText
-                    name="name"
-                    label="定性选项:"
-                    placeholder="请输入"
-                    rules={[{ required: true, message: '定性不能为空!' }]}
-                />
-                <ProFormText
-                    name="code"
-                    label="选项编码:"
-                    placeholder="请输入"
-                    rules={[{ required: true, message: '编码不能为空!' }]}
-                />
-                <ProFormSelect label='类型:' name='resultType'
-                    request={async () => {
-
-                        return resultTypeList
-                    }}
-                    rules={[{ required: true, message: '类型不能为空!' }]} 
-                />
 
-                <ProFormSelect label='是否默认:' name='defaultFlag'
-                    request={async () => {
-                          return [
-                            {label:'是',value:1},
-                            {label:'否',value:0}
-                          ]
-                    }}
+            return [];
+          }}
+        />
+      </ModalForm>
+    );
+  };
+
+  const tableDataSearchHandle = (paramName: string) => {
+    set_tableDataFilterParams({
+      ...tableDataFilterParams,
+      [`${paramName}`]: tableDataSearchKeywords,
+    });
+  };
+
+  useEffect(() => {}, []);
+
+  return (
+    <KCIMPagecontainer className="QualitativeOptMana" title={false}>
+      <div className="toolBar">
+        <div className="filter">
+          <div className="filterItem" style={{ marginRight: 16, width: 205 }}>
+            <span className="label" style={{ whiteSpace: 'nowrap' }}>
+              检索:
+            </span>
+            <Input
+              placeholder={'请输入定性名称'}
+              allowClear
+              suffix={
+                <IconFont
+                  type="iconsousuo"
+                  style={{ color: '#99A6BF' }}
+                  onClick={() => tableDataSearchHandle('filter')}
                 />
-                <ProFormDigit
-                    name="sort"
-                    label="排序序号:"
-                    placeholder="请输入"
-                />
-                <ProFormText
-                    name="color"
-                    label="默认颜色:"
-                    placeholder="请输入,不需要#号"
-                />
-            </ModalForm>
-        )
-    }
-
-    const tableDataSearchHandle = (paramName: string) => {
-
-
-        set_tableDataFilterParams({
-            ...tableDataFilterParams,
-            [`${paramName}`]: tableDataSearchKeywords
-        })
-    }
-
-
-    useEffect(() => {
-
-    }, [])
-
-    return (
-        <KCIMPagecontainer className='QualitativeOptMana' title={false}>
-            <div className='toolBar'>
-                <div className='filter'>
-                    <div className='filterItem' style={{ marginRight: 16, width: 205 }}>
-                        <span className='label' style={{ whiteSpace: 'nowrap' }}> 检索:</span>
-                        <Input placeholder={'请输入定性名称'} allowClear
-                            suffix={
-                                <IconFont type="iconsousuo" style={{ color: '#99A6BF' }} onClick={() => tableDataSearchHandle('filter')} />
-                            }
-                            onChange={(e) => {
-                                set_tableDataSearchKeywords(e.target.value);
-                                if (e.target.value.length == 0) {
-                                    set_tableDataFilterParams({
-                                        ...tableDataFilterParams,
-                                        filter: ''
-                                    });
-                                }
-                            }}
-                            onPressEnter={(e) => {
-
-                                set_tableDataFilterParams({
-                                    ...tableDataFilterParams,
-                                    filter: (e.target as HTMLInputElement).value
-                                });
-                            }}
-
-                        />
-                    </div>
-                    <div className='filterItem'>
-                        <span className='label' style={{ whiteSpace: 'nowrap' }}> 类型:</span>
-                        <ProFormSelect
-                            noStyle
-                            allowClear
-                            placeholder="请选择"
-                            style={{ width: 160, marginRight: 16 }}
-                            request={async () => {
-                                return resultTypeList
-                            }}
-                            fieldProps={{
-                                onChange(value, option) {
-                                    set_tableDataFilterParams({ ...tableDataFilterParams, resultType: value })
-                                },
-                            }}
-                        />
-                    </div>
-                </div>
-                <div className='btnGroup'>
-                    <UpDataActBtn record type='ADD' />
-                </div>
-            </div>
-            <div style={{ marginTop: 16 }}>
-                <KCIMTable columns={columns as ProColumns[]} actionRef={tableRef} rowKey='id' params={tableDataFilterParams} request={(params) => getTableData(params)} />
-            </div>
-        </KCIMPagecontainer>
-    )
+              }
+              onChange={(e) => {
+                set_tableDataSearchKeywords(e.target.value);
+                if (e.target.value.length == 0) {
+                  set_tableDataFilterParams({
+                    ...tableDataFilterParams,
+                    filter: '',
+                  });
+                }
+              }}
+              onPressEnter={(e) => {
+                set_tableDataFilterParams({
+                  ...tableDataFilterParams,
+                  filter: (e.target as HTMLInputElement).value,
+                });
+              }}
+            />
+          </div>
+          <div className="filterItem">
+            <span className="label" style={{ whiteSpace: 'nowrap' }}>
+              {' '}
+              类型:
+            </span>
+            <ProFormSelect
+              noStyle
+              allowClear
+              placeholder="请选择"
+              style={{ width: 160, marginRight: 16 }}
+              request={async () => {
+                return resultTypeList;
+              }}
+              fieldProps={{
+                onChange(value, option) {
+                  set_tableDataFilterParams({
+                    ...tableDataFilterParams,
+                    resultType: value,
+                  });
+                },
+              }}
+            />
+          </div>
+        </div>
+        <div className="btnGroup">
+          <UpDataActBtn record type="ADD" />
+        </div>
+      </div>
+      <div style={{ marginTop: 16 }}>
+        <KCIMTable
+          columns={columns as ProColumns[]}
+          actionRef={tableRef}
+          rowKey="id"
+          params={tableDataFilterParams}
+          request={(params) => getTableData(params)}
+        />
+      </div>
+    </KCIMPagecontainer>
+  );
 }

+ 47 - 50
src/pages/setting/qualitativeOptionsMana/service.ts

@@ -2,89 +2,86 @@
  * @Author: code4eat awesomedema@gmail.com
  * @Date: 2023-03-03 16:31:27
  * @LastEditors: code4eat awesomedema@gmail.com
- * @LastEditTime: 2023-09-27 11:02:05
+ * @LastEditTime: 2025-07-09 17:18:44
  * @FilePath: /KC-MiddlePlatform/src/pages/platform/setting/pubDicTypeMana/service.ts
  * @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
  */
 
-
-
 import { request } from 'umi';
 
 //获取table列表数据
 
 export type QualitativeOptionItemType = {
-  id:number;
-  hiId:number;
-  code:string;
-  name:string;
-  resultType:number;
-  resultTypeName:string;
-  defaultFlag:number;
-  sort:number;
-  delFlag:number;
-  color:string
-}
-
+  id: number;
+  hiId: number;
+  code: string;
+  name: string;
+  resultType: number;
+  resultTypeName: string;
+  defaultFlag: number;
+  sort: number;
+  delFlag: number;
+  color: string;
+};
 
-export const getData = (params?:any) => {
+export const getData = (params?: any) => {
   return request<{
-       current:number;
-       list:QualitativeOptionItemType[];
-       pageSize:number;
-       totalCount:number;
-       totalPage:number;
+    currPage: any;
+    current: number;
+    list: QualitativeOptionItemType[];
+    pageSize: number;
+    totalCount: number;
+    totalPage: number;
   }>('/manager/setting/getResultConfigList', {
     method: 'GET',
-    params:{...params}
+    params: { ...params },
   });
 };
 
-
 //新增表格数据
 export type AddTableDataType = {
   //hospId:number;
-  hiId:string;
-  code:string;
-  name:string;
-  resultType:string;
-  defaultFlag:number;
-  sort:number;
-  delFlag:number;
-  createUser:string;
-  createTime:string;
-  color:string;
-}
-export const addData = (data:AddTableDataType) => {
+  hiId: string;
+  code: string;
+  name: string;
+  resultType: string;
+  defaultFlag: number;
+  sort: number;
+  delFlag: number;
+  createUser: string;
+  createTime: string;
+  color: string;
+};
+export const addData = (data: AddTableDataType) => {
   return request('/manager/setting/addResultConfig', {
     method: 'POST',
-    data
+    data,
   });
 };
 
-
-
 //编辑表格数据
 
-export const editData = (data:QualitativeOptionItemType) => {
+export const editData = (data: QualitativeOptionItemType) => {
   return request('/manager/setting/editResultConfig', {
     method: 'POST',
-    data
+    data,
   });
 };
 
 //删除表格操作
-export const delData = (id:string) => {
+export const delData = (id: string) => {
   return request('/manager/setting/deleteResultConfig', {
     method: 'POST',
-    params:{id}
+    params: { id },
+  });
+};
+//获取颜色字典
+export const getColorFromDic = (params: {
+  dictType: string;
+  systemId: number;
+}) => {
+  return request('/centerSys/sysdictdata/getDictByDictType', {
+    method: 'GET',
+    params: { ...params },
   });
 };
-
-
-
-
-
-
-
-

+ 4 - 5
src/pages/setting/qualitativeOptionsMana/style.less

@@ -1,9 +1,9 @@
 .QualitativeOptMana {
   padding: 16px;
-  background: #FFFFFF;
+  margin: 16px;
+  background: #ffffff;
   border-radius: 4px;
 
-
   .toolBar {
     display: flex;
     flex-direction: row;
@@ -31,13 +31,12 @@
         font-size: 14px;
         font-family: SourceHanSansCN-Normal, SourceHanSansCN;
         font-weight: 400;
-        color: #FFFFFF;
+        color: #ffffff;
         line-height: 24px;
         padding: 0 14px;
-        background: #3377FF;
+        background: #3377ff;
         border-radius: 4px;
       }
     }
-
   }
 }

+ 20 - 25
src/services/auth.ts

@@ -7,35 +7,30 @@
  * @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
  */
 
-
-
 //获取当前审核状态
 
-import { request } from "@umijs/max"
-import { Key } from "react";
-
-export const getCurrentCheckStatus = (computeDate:string) => {
-    
-    return request('/performance/check/getStatus', {
-        method: 'GET',
-        params:{computeDate}
-    })
-    
-}
+import { request } from '@umijs/max';
+import { Key } from 'react';
 
+export const getCurrentCheckStatus = (computeDate: string) => {
+  return request('/performance/check/getStatus', {
+    method: 'GET',
+    params: { computeDate },
+  });
+};
 
 // 获取当前登录用户菜单数据
-export const getMenus = (systemId:number)=>{
-    return request(`/pfm/menu/nav`, {
-      method: 'GET',
-      params:{systemId}
-    });
-}
+export const getMenus = (systemId: number) => {
+  return request(`/pfm/menu/nav`, {
+    method: 'GET',
+    params: { systemId },
+  });
+};
 
 //获取平台菜单
-export const getPlatformMenu = (systemId?:Key) => {
-    return request('/centerSys/user/getMenuNav', {
-      method: 'GET',
-      params: { systemId },
-    });
-};
+export const getPlatformMenu = (systemId?: Key) => {
+  return request('/centerSys/user/getMenuNav', {
+    method: 'GET',
+    params: { systemId },
+  });
+};

+ 24 - 34
src/services/getDic.ts

@@ -7,49 +7,39 @@
  * @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
  */
 
-
-
-
-
-
-
-import { request } from "@umijs/max"
-
+import { request } from '@umijs/max';
 
 //从公共字典获取数据
-export const getPubDicData =  () => {
-    
-    return request('/performance/dict', {
-        method: 'GET',
-    });    
-}
+export const getPubDicData = () => {
+  return request('/performance/dict', {
+    method: 'GET',
+  });
+};
 
 // 筛选指定的字典数据
 
-export const getDataByKeyFromDic = (dicData:any[],key: string) => {
-
-    let result = dicData.filter((t: any) => (t.code == key));
-
-    if (result.length > 0) {
+export const getDataByKeyFromDic = (dicData: any[], key: string) => {
+  let result = dicData.filter((t: any) => t.code == key);
 
-      let dataArr = result[0].dataVoList;
+  if (result.length > 0) {
+    let dataArr = result[0].dataVoList;
 
-      let defaultValue = dataArr.filter((t:any) => t.defaultValue == 1);
+    let defaultValue = dataArr.filter((t: any) => t.defaultValue == 1);
 
-      dataArr.sort((prev:any, next:any) => {
-        return prev.sort - next.sort;
-      });
-
-      return {
-        defaultvalue: defaultValue[0] ? defaultValue[0].value : '',
-        list: dataArr.map((t:any) => {
-          return ({ label: t.name, value: t.code })
-        })
-      }
-    }
+    dataArr.sort((prev: any, next: any) => {
+      return prev.sort - next.sort;
+    });
 
     return {
-      defaultvalue: '',
-      list: []
+      defaultvalue: defaultValue[0] ? defaultValue[0].value : '',
+      list: dataArr.map((t: any) => {
+        return { label: t.name, value: t.code };
+      }),
     };
   }
+
+  return {
+    defaultvalue: '',
+    list: [],
+  };
+};

+ 3 - 1
src/typings.d.ts

@@ -28,6 +28,8 @@ declare module '*.js';
 
 // preview.pro.ant.design only do not use in your production ;
 // preview.pro.ant.design Dedicated environment variable, please do not use it in your project.
-declare let ANT_DESIGN_PRO_ONLY_DO_NOT_USE_IN_YOUR_PRODUCTION: 'site' | undefined;
+declare let ANT_DESIGN_PRO_ONLY_DO_NOT_USE_IN_YOUR_PRODUCTION:
+  | 'site'
+  | undefined;
 
 declare const REACT_APP_ENV: 'test' | 'dev' | 'pre' | false;

+ 59 - 57
src/utils/devicePixelRatio.js

@@ -7,61 +7,63 @@
  * @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
  */
 
-
- class DevicePixelRatio {
-	constructor() {
-		//this.flag = false;
-	}
-	//获取系统类型
-	_getSystem() {
-		let flag = false;
-		var agent = navigator.userAgent.toLowerCase();
-		//		var isMac = /macintosh|mac os x/i.test(navigator.userAgent);
-		//		if(isMac) {
-		//			return false;
-		//		}
-		//现只针对windows处理,其它系统暂无该情况,如有,继续在此添加
-		if(agent.indexOf("windows") >= 0) {
-			return true;
-		}
-	}
-	//获取页面缩放比例
-	//	_getDevicePixelRatio() {
-	//		let t = this;
-	//	}
-	//监听方法兼容写法
-	_addHandler(element, type, handler) {
-		if(element.addEventListener) {
-			element.addEventListener(type, handler, false);
-		} else if(element.attachEvent) {
-			element.attachEvent("on" + type, handler);
-		} else {
-			element["on" + type] = handler;
-		}
-	}
-	//校正浏览器缩放比例
-	_correct() {
-		let t = this;
-		//页面devicePixelRatio(设备像素比例)变化后,计算页面body标签zoom修改其大小,来抵消devicePixelRatio带来的变化。
-		document.getElementsByTagName('body')[0].style.zoom = 1 / window.devicePixelRatio;
-	}
-	//监听页面缩放
-	_watch() {
-		let t = this;
-		t._addHandler(window, 'resize', function() { //注意这个方法是解决全局有两个window.resize
-			//重新校正
-			t._correct()
-		})
-	}
-	//初始化页面比例
-	init() {
-		let t = this;
-		if(t._getSystem()) { //判断设备,目前只在windows系统下校正浏览器缩放比例
-			//初始化页面校正浏览器缩放比例
-			t._correct();
-			//开启监听页面缩放
-			t._watch();
-		}
-	}
+class DevicePixelRatio {
+  constructor() {
+    //this.flag = false;
+  }
+  //获取系统类型
+  _getSystem() {
+    let flag = false;
+    var agent = navigator.userAgent.toLowerCase();
+    //		var isMac = /macintosh|mac os x/i.test(navigator.userAgent);
+    //		if(isMac) {
+    //			return false;
+    //		}
+    //现只针对windows处理,其它系统暂无该情况,如有,继续在此添加
+    if (agent.indexOf('windows') >= 0) {
+      return true;
+    }
+  }
+  //获取页面缩放比例
+  //	_getDevicePixelRatio() {
+  //		let t = this;
+  //	}
+  //监听方法兼容写法
+  _addHandler(element, type, handler) {
+    if (element.addEventListener) {
+      element.addEventListener(type, handler, false);
+    } else if (element.attachEvent) {
+      element.attachEvent('on' + type, handler);
+    } else {
+      element['on' + type] = handler;
+    }
+  }
+  //校正浏览器缩放比例
+  _correct() {
+    let t = this;
+    //页面devicePixelRatio(设备像素比例)变化后,计算页面body标签zoom修改其大小,来抵消devicePixelRatio带来的变化。
+    document.getElementsByTagName('body')[0].style.zoom =
+      1 / window.devicePixelRatio;
+  }
+  //监听页面缩放
+  _watch() {
+    let t = this;
+    t._addHandler(window, 'resize', function () {
+      //注意这个方法是解决全局有两个window.resize
+      //重新校正
+      t._correct();
+    });
+  }
+  //初始化页面比例
+  init() {
+    let t = this;
+    if (t._getSystem()) {
+      //判断设备,目前只在windows系统下校正浏览器缩放比例
+      //初始化页面校正浏览器缩放比例
+      t._correct();
+      //开启监听页面缩放
+      t._watch();
+    }
+  }
 }
-export default DevicePixelRatio;
+export default DevicePixelRatio;

+ 6 - 3
src/utils/format.ts

@@ -12,9 +12,12 @@ export function trim(str: string) {
 }
 
 //金额数字转换
-export function formatMoneyNumber(num:number) {
+export function formatMoneyNumber(num: number) {
   if (typeof num !== 'number' || isNaN(num)) {
     return '-';
-  } 
-  return new Intl.NumberFormat('en-US', { minimumFractionDigits: 0, maximumFractionDigits: 2 }).format(num);
+  }
+  return new Intl.NumberFormat('en-US', {
+    minimumFractionDigits: 0,
+    maximumFractionDigits: 2,
+  }).format(num);
 }

+ 49 - 0
src/utils/request.ts

@@ -0,0 +1,49 @@
+/*
+ * @Author: code4eat awesomedema@gmail.com
+ * @Date: 2025-06-18 09:15:35
+ * @LastEditors: code4eat awesomedema@gmail.com
+ * @LastEditTime: 2025-06-24 11:28:49
+ * @FilePath: /pfmBackMana/src/utils/request.ts
+ * @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
+ */
+import axios from 'axios';
+import { message } from 'antd';
+
+// 创建 axios 实例
+const request = axios.create({
+  baseURL: '/api', // 设置基础URL
+  timeout: 10000, // 请求超时时间
+});
+
+// 请求拦截器
+request.interceptors.request.use(
+  (config) => {
+    // 在这里可以添加token等认证信息
+    return config;
+  },
+  (error) => {
+    return Promise.reject(error);
+  },
+);
+
+// 响应拦截器
+request.interceptors.response.use(
+  (response) => {
+    const data = response.data;
+
+    // 统一处理业务逻辑错误
+    if (data && data.success === false) {
+      const errorMessage = data.message || '操作失败';
+      message.error(errorMessage);
+      return Promise.reject(new Error(errorMessage));
+    }
+
+    return data;
+  },
+  (error) => {
+    message.error(error.message || '请求失败');
+    return Promise.reject(error);
+  },
+);
+
+export default request;

+ 14 - 7
src/utils/tableToExcel.ts

@@ -41,7 +41,10 @@ function getMerges(columns: Column[], startRow = 0, startCol = 0): Merge[] {
     }
 
     // 添加合并项
-    merges.push({ s: { r: startRow, c: colIndex }, e: { r: endRow, c: endCol } });
+    merges.push({
+      s: { r: startRow, c: colIndex },
+      e: { r: endRow, c: endCol },
+    });
 
     // 更新列索引
     colIndex = endCol + 1;
@@ -50,13 +53,18 @@ function getMerges(columns: Column[], startRow = 0, startCol = 0): Merge[] {
   return merges;
 }
 
-function exportTableToExcel(tableData: DataType[], columns: Column[], fileName = 'data') {
+function exportTableToExcel(
+  tableData: DataType[],
+  columns: Column[],
+  fileName = 'data',
+) {
   // 获取列标题
-  const columnHeaders = columns.map(column => column.title);
-  
-  // 将列标题和表格数据合并
-  const allData = [columnHeaders as any].concat(tableData.map(row => Object.values(row)));
+  const columnHeaders = columns.map((column) => column.title);
 
+  // 将列标题和表格数据合并
+  const allData = [columnHeaders as any].concat(
+    tableData.map((row) => Object.values(row)),
+  );
 
   // 获取合并项
   const merges = getMerges(columns);
@@ -81,5 +89,4 @@ function exportTableToExcel(tableData: DataType[], columns: Column[], fileName =
   saveAs(blob, `${fileName}.xlsx`);
 }
 
-
 export default exportTableToExcel;

+ 256 - 237
src/utils/tableToMultiHeaderExcel.ts

@@ -3,279 +3,298 @@
 import * as XLSX from 'xlsx';
 import { saveAs } from 'file-saver';
 
-
 type DataType = { [key: string]: string | number | null };
 
 type Column = {
-    title: string;
-    dataIndex: string;
-    key: string;
-    children?: Column[];
+  title: string;
+  dataIndex: string;
+  key: string;
+  children?: Column[];
 };
 
 type Merge = {
-    s: { r: number; c: number };
-    e: { r: number; c: number };
+  s: { r: number; c: number };
+  e: { r: number; c: number };
 };
 
-
 function getMaxDepth(columns: Column[]): number {
-    let maxDepth = 0;
-    for (const column of columns) {
-        if (column.children) {
-            // 如果有子列,递归计算子列的深度
-            const depth = 1 + getMaxDepth(column.children);
-            maxDepth = Math.max(maxDepth, depth);
-        } else {
-            // 如果没有子列,深度为1
-            maxDepth = Math.max(maxDepth, 1);
-        }
+  let maxDepth = 0;
+  for (const column of columns) {
+    if (column.children) {
+      // 如果有子列,递归计算子列的深度
+      const depth = 1 + getMaxDepth(column.children);
+      maxDepth = Math.max(maxDepth, depth);
+    } else {
+      // 如果没有子列,深度为1
+      maxDepth = Math.max(maxDepth, 1);
     }
-    return maxDepth;
+  }
+  return maxDepth;
 }
 
-
-
-
-function processColumns(columns: Column[], startRow = 0, startCol = 0, maxDepth = 0,colLength=0): { headers: any[][], merges: Merge[] } {
-
-    let headers: any[][] = Array.from({ length: maxDepth }, () => Array(colLength).fill(''));
-
-    let merges: Merge[] = [];
-    let colIndex = startCol;
-
-    // console.log({columns});
-
-    for (const column of columns) {
-        let endRow = startRow;
-        let endCol = colIndex;
-
-        if (column.children) {
-
-            const result = processColumns(column.children, startRow + 1, colIndex, maxDepth,colLength);
-
-            headers = headers.map((row, i) => {
-                
-                if (i === startRow) {
-                    return [...row.slice(0, colIndex), ...result.headers[i], ...row.slice(colIndex + result.headers[i].length)];
-                } else {
-                    return [...row.slice(0, colIndex), ...result.headers[i].slice(colIndex, result.headers[i].length), ...row.slice(colIndex + result.headers[i].length)];
-                }
-            });
-
-            merges = [...merges, ...result.merges];
-            endCol += getAllColumns(column.children).length - 1;  // 根据所有子列的总数来更新 endCol
-            endRow = startRow;  // 父列的标题应该在子列标题的上一行
-
+function processColumns(
+  columns: Column[],
+  startRow = 0,
+  startCol = 0,
+  maxDepth = 0,
+  colLength = 0,
+): { headers: any[][]; merges: Merge[] } {
+  let headers: any[][] = Array.from({ length: maxDepth }, () =>
+    Array(colLength).fill(''),
+  );
+
+  let merges: Merge[] = [];
+  let colIndex = startCol;
+
+  // console.log({columns});
+
+  for (const column of columns) {
+    let endRow = startRow;
+    let endCol = colIndex;
+
+    if (column.children) {
+      const result = processColumns(
+        column.children,
+        startRow + 1,
+        colIndex,
+        maxDepth,
+        colLength,
+      );
+
+      headers = headers.map((row, i) => {
+        if (i === startRow) {
+          return [
+            ...row.slice(0, colIndex),
+            ...result.headers[i],
+            ...row.slice(colIndex + result.headers[i].length),
+          ];
         } else {
-            endRow = maxDepth - 1;
+          return [
+            ...row.slice(0, colIndex),
+            ...result.headers[i].slice(colIndex, result.headers[i].length),
+            ...row.slice(colIndex + result.headers[i].length),
+          ];
         }
+      });
 
-        for (let i = startRow; i <= endRow; i++) {
-            headers[i][colIndex] = i === startRow ? column.title : '';
-        }
-        
-        merges.push({ s: { r: startRow, c: colIndex }, e: { r: endRow, c: endCol } });
-        colIndex = endCol + 1;
+      merges = [...merges, ...result.merges];
+      endCol += getAllColumns(column.children).length - 1; // 根据所有子列的总数来更新 endCol
+      endRow = startRow; // 父列的标题应该在子列标题的上一行
+    } else {
+      endRow = maxDepth - 1;
     }
-     
-    return { headers, merges };
-}
 
-
-function exportTableToMultiExcel(tableData: DataType[], columns: Column[], fileName = 'data', needSummary = false) {
-    const allColumns = getAllColumns(columns);  // 获取所有列(包括子列)
-
-    // 计算总结信息
-    const summary: DataType = {};
-    if (needSummary) {
-        allColumns.forEach(column => {
-            summary[column.dataIndex] = tableData.reduce((total, row) => {
-                if (typeof row[column.dataIndex] === 'number') {
-                    return total + (row[column.dataIndex] as number);
-                }
-                return total;
-            }, 0);
-        });
-        summary[allColumns[0].dataIndex] = '总计';  // 设置第一列的值为'总计'
+    for (let i = startRow; i <= endRow; i++) {
+      headers[i][colIndex] = i === startRow ? column.title : '';
     }
 
-    const dataValues = [...tableData.map(row => {
-        const rowData = [];
-        for (const column of allColumns) {
-            rowData.push(row[column.dataIndex]);
-        }
-        return rowData;
-    }), needSummary ? Object.values(summary) : []];  // 如果需要汇总,那么在数据数组的末尾添加总结信息
-
-    const maxDepth = getMaxDepth(columns);
-    const { headers, merges } = processColumns(columns, 0, 0, maxDepth,columns.length);
-
-    const wb = XLSX.utils.book_new();
-    const ws = XLSX.utils.aoa_to_sheet([...headers, ...dataValues]);
-
-    // 计算每一列的最大字符长度
-    const colWidths = allColumns.map((column, i) => {
-        let maxLength = column.title.length * 2; // 初始值设为列标题的长度的两倍
-        dataValues.forEach(row => {
-            const cellValue = row[i];
-            if (cellValue) {
-                const cellLength = cellValue.toString().length;
-                // 对于宽字符,使用较大的系数;对于窄字符,使用较小的系数
-                const cellWidth = /[\u4e00-\u9fa5]/.test(cellValue.toString()) ? cellLength * 2 : cellLength;
-                maxLength = Math.max(maxLength, cellWidth);
-            }
-        });
-        return { wch: maxLength };
+    merges.push({
+      s: { r: startRow, c: colIndex },
+      e: { r: endRow, c: endCol },
     });
+    colIndex = endCol + 1;
+  }
 
-    // 设置每一列的宽度
-    ws['!cols'] = colWidths;
+  return { headers, merges };
+}
 
-    // 设置单元格样式为水平和垂直居中
-    const range = XLSX.utils.decode_range(ws['!ref'] as string);
-    for (let R = range.s.r; R <= range.e.r; ++R) {
-        for (let C = range.s.c; C <= range.e.c; ++C) {
-            const cell_address = { c: C, r: R };
-            const cell_ref = XLSX.utils.encode_cell(cell_address);
-            if (!ws[cell_ref]) ws[cell_ref] = {};
-            ws[cell_ref].s = {
-                alignment: {
-                    horizontal: 'center',
-                    vertical: 'center'
-                }
-            };
+function exportTableToMultiExcel(
+  tableData: DataType[],
+  columns: Column[],
+  fileName = 'data',
+  needSummary = false,
+) {
+  const allColumns = getAllColumns(columns); // 获取所有列(包括子列)
+
+  // 计算总结信息
+  const summary: DataType = {};
+  if (needSummary) {
+    allColumns.forEach((column) => {
+      summary[column.dataIndex] = tableData.reduce((total, row) => {
+        if (typeof row[column.dataIndex] === 'number') {
+          return total + (row[column.dataIndex] as number);
         }
+        return total;
+      }, 0);
+    });
+    summary[allColumns[0].dataIndex] = '总计'; // 设置第一列的值为'总计'
+  }
+
+  const dataValues = [
+    ...tableData.map((row) => {
+      const rowData = [];
+      for (const column of allColumns) {
+        rowData.push(row[column.dataIndex]);
+      }
+      return rowData;
+    }),
+    needSummary ? Object.values(summary) : [],
+  ]; // 如果需要汇总,那么在数据数组的末尾添加总结信息
+
+  const maxDepth = getMaxDepth(columns);
+  const { headers, merges } = processColumns(
+    columns,
+    0,
+    0,
+    maxDepth,
+    columns.length,
+  );
+
+  const wb = XLSX.utils.book_new();
+  const ws = XLSX.utils.aoa_to_sheet([...headers, ...dataValues]);
+
+  // 计算每一列的最大字符长度
+  const colWidths = allColumns.map((column, i) => {
+    let maxLength = column.title.length * 2; // 初始值设为列标题的长度的两倍
+    dataValues.forEach((row) => {
+      const cellValue = row[i];
+      if (cellValue) {
+        const cellLength = cellValue.toString().length;
+        // 对于宽字符,使用较大的系数;对于窄字符,使用较小的系数
+        const cellWidth = /[\u4e00-\u9fa5]/.test(cellValue.toString())
+          ? cellLength * 2
+          : cellLength;
+        maxLength = Math.max(maxLength, cellWidth);
+      }
+    });
+    return { wch: maxLength };
+  });
+
+  // 设置每一列的宽度
+  ws['!cols'] = colWidths;
+
+  // 设置单元格样式为水平和垂直居中
+  const range = XLSX.utils.decode_range(ws['!ref'] as string);
+  for (let R = range.s.r; R <= range.e.r; ++R) {
+    for (let C = range.s.c; C <= range.e.c; ++C) {
+      const cell_address = { c: C, r: R };
+      const cell_ref = XLSX.utils.encode_cell(cell_address);
+      if (!ws[cell_ref]) ws[cell_ref] = {};
+      ws[cell_ref].s = {
+        alignment: {
+          horizontal: 'center',
+          vertical: 'center',
+        },
+      };
     }
-
-    // 设置表头的背景色
-    for (let r = 0; r < maxDepth; r++) {
-        for (let c = 0; c < headers[r].length; c++) {
-            if (!ws[XLSX.utils.encode_cell({ r, c })]) {
-                ws[XLSX.utils.encode_cell({ r, c })] = {};
-            }
-            ws[XLSX.utils.encode_cell({ r, c })].s = {
-                fill: {
-                    fgColor: { rgb: "FFFF00" }  // 设置为黄色
-                },
-                alignment: {
-                    horizontal: 'center',
-                    vertical: 'center'
-                }
-            };
-        }
+  }
+
+  // 设置表头的背景色
+  for (let r = 0; r < maxDepth; r++) {
+    for (let c = 0; c < headers[r].length; c++) {
+      if (!ws[XLSX.utils.encode_cell({ r, c })]) {
+        ws[XLSX.utils.encode_cell({ r, c })] = {};
+      }
+      ws[XLSX.utils.encode_cell({ r, c })].s = {
+        fill: {
+          fgColor: { rgb: 'FFFF00' }, // 设置为黄色
+        },
+        alignment: {
+          horizontal: 'center',
+          vertical: 'center',
+        },
+      };
     }
+  }
 
-    ws['!merges'] = merges;
-    XLSX.utils.book_append_sheet(wb, ws, 'Sheet1');
-    const wbout = XLSX.write(wb, { bookType: 'xlsx', type: 'array' });
-    const blob = new Blob([wbout], { type: 'application/octet-stream' });
-    saveAs(blob, `${fileName}.xlsx`);
+  ws['!merges'] = merges;
+  XLSX.utils.book_append_sheet(wb, ws, 'Sheet1');
+  const wbout = XLSX.write(wb, { bookType: 'xlsx', type: 'array' });
+  const blob = new Blob([wbout], { type: 'application/octet-stream' });
+  saveAs(blob, `${fileName}.xlsx`);
 }
 
-
-
-
 function getAllColumns(columns: Column[]): Column[] {
-    let allColumns: Column[] = [];
-    for (const column of columns) {
-        if (column.children) {
-            allColumns = [...allColumns, ...getAllColumns(column.children)];
-        } else {
-            allColumns.push(column);
-        }
+  let allColumns: Column[] = [];
+  for (const column of columns) {
+    if (column.children) {
+      allColumns = [...allColumns, ...getAllColumns(column.children)];
+    } else {
+      allColumns.push(column);
     }
-    return allColumns;
+  }
+  return allColumns;
 }
 
 export default exportTableToMultiExcel;
 
-
-
 const columns = [
-    {
-        dataIndex: "unitName",
+  {
+    dataIndex: 'unitName',
+    ellipsis: true,
+    fixed: 'left',
+    key: 'unitName',
+    title: '核算单元',
+    width: 140,
+  },
+  {
+    dataIndex: 'key-23',
+    ellipsis: true,
+    key: 'key-23',
+    title: '单元管理绩效',
+    width: 200,
+  },
+  {
+    dataIndex: 'key-1679019958216040448',
+    ellipsis: true,
+    key: 'key-1679019958216040448',
+    title: '临床诊察积分奖金',
+    width: 200,
+    children: [
+      {
+        dataIndex: 'key-6',
         ellipsis: true,
-        fixed: "left",
-        key: "unitName",
-        title: "核算单元",
-        width: 140
-    },
-    {
-        dataIndex: "key-23",
-        ellipsis: true,
-        key: "key-23",
-        title: "单元管理绩效",
-        width: 200
-    },
-    {
-        dataIndex: "key-1679019958216040448",
-        ellipsis: true,
-        key: "key-1679019958216040448",
-        title: "临床诊察积分奖金",
+        key: 'key-6',
+        title: '临床诊察积分-节假日奖金',
         width: 200,
         children: [
-            {
-                dataIndex: "key-6",
-                ellipsis: true,
-                key: "key-6",
-                title: "临床诊察积分-节假日奖金",
-                width: 200,
-                children: [
-                    {
-                        dataIndex: "key-4",
-                        ellipsis: true,
-                        key: "key-4",
-                        title: "临床诊察积分-节假日",
-                        width: 200
-                    },
-                    {
-                        dataIndex: "key-5",
-                        ellipsis: true,
-                        key: "key-5",
-                        title: "临床诊察积分-节假日点值",
-                        width: 200
-                    }
-                ]
-            },
-            {
-                dataIndex: "key-3",
-                ellipsis: true,
-                key: "key-3",
-                title: "临床诊察积分-工作日奖金",
-                width: 200,
-                children: [
-                    {
-                        dataIndex: "key-1",
-                        ellipsis: true,
-                        key: "key-1",
-                        title: "临床诊察积分-工作日",
-                        width: 200
-                    },
-                    {
-                        dataIndex: "key-2",
-                        ellipsis: true,
-                        key: "key-2",
-                        title: "临床诊察积分-工作日点值",
-                        width: 200
-                    }
-                ]
-            }
-        ]
-    },
-    {
-        dataIndex: "totalScore",
+          {
+            dataIndex: 'key-4',
+            ellipsis: true,
+            key: 'key-4',
+            title: '临床诊察积分-节假日',
+            width: 200,
+          },
+          {
+            dataIndex: 'key-5',
+            ellipsis: true,
+            key: 'key-5',
+            title: '临床诊察积分-节假日点值',
+            width: 200,
+          },
+        ],
+      },
+      {
+        dataIndex: 'key-3',
         ellipsis: true,
-        fixed: "right",
-        key: "totalScore",
-        title: "总奖金",
-        width: 140
-    }
-]
-
-
-
-
-
-
-
+        key: 'key-3',
+        title: '临床诊察积分-工作日奖金',
+        width: 200,
+        children: [
+          {
+            dataIndex: 'key-1',
+            ellipsis: true,
+            key: 'key-1',
+            title: '临床诊察积分-工作日',
+            width: 200,
+          },
+          {
+            dataIndex: 'key-2',
+            ellipsis: true,
+            key: 'key-2',
+            title: '临床诊察积分-工作日点值',
+            width: 200,
+          },
+        ],
+      },
+    ],
+  },
+  {
+    dataIndex: 'totalScore',
+    ellipsis: true,
+    fixed: 'right',
+    key: 'totalScore',
+    title: '总奖金',
+    width: 140,
+  },
+];

+ 257 - 71
src/utils/tooljs.ts

@@ -7,49 +7,50 @@
  * @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
  */
 
+let parent: any = undefined;
 
-let parent:any = undefined;
-
-export const getDeepestTreeData:any= (tree:any,childrenName:string)=>{
-     
-      if(tree[`${childrenName}`]&&tree[`${childrenName}`].length>0){
-           parent = tree;
-           return getDeepestTreeData(tree[`${childrenName}`][0],childrenName)    
-      }
-
-      return  [tree,parent] 
-}
-
-
-
+export const getDeepestTreeData: any = (tree: any, childrenName: string) => {
+  if (tree[`${childrenName}`] && tree[`${childrenName}`].length > 0) {
+    parent = tree;
+    return getDeepestTreeData(tree[`${childrenName}`][0], childrenName);
+  }
 
+  return [tree, parent];
+};
 
 /*
  * @param x {Object} 对象1
  * @param y {Object} 对象2
  * @return  {Boolean} true 为相等,false 为不等
  */
-export  const deepEqual = (x: { [x: string]: any; } | null, y: { [x: string]: any; hasOwnProperty?: any; } | null) => {
-      // 指向同一内存时
-      if (x === y) {
-        return true;
-      } else if ((typeof x == "object" && x != null) && (typeof y == "object" && y != null)) {
-        if (Object.keys(x).length !== Object.keys(y).length) {
-          return false;
-        }
-        for (var prop in x) {
-          if (y.hasOwnProperty(prop)) {  
-            if (!deepEqual(x[prop], y[prop])) return false;
-          } else {
-            return false;
-          }
-        }
-        return true;
+export const deepEqual = (
+  x: { [x: string]: any } | null,
+  y: { [x: string]: any; hasOwnProperty?: any } | null,
+) => {
+  // 指向同一内存时
+  if (x === y) {
+    return true;
+  } else if (
+    typeof x == 'object' &&
+    x != null &&
+    typeof y == 'object' &&
+    y != null
+  ) {
+    if (Object.keys(x).length !== Object.keys(y).length) {
+      return false;
+    }
+    for (var prop in x) {
+      if (y.hasOwnProperty(prop)) {
+        if (!deepEqual(x[prop], y[prop])) return false;
       } else {
         return false;
       }
-}
-
+    }
+    return true;
+  } else {
+    return false;
+  }
+};
 
 // 查找父节点的函数
 
@@ -59,45 +60,43 @@ interface Node {
   children?: Node[];
 }
 
-export const findAllParents = (data: Node[], parentCode:string): Node[] => {
+export const findAllParents = (data: Node[], parentCode: string): Node[] => {
   let result: Node[] = [];
 
   function findNodeRecursively(node: Node, path: Node[]): boolean {
-      if (node.code === `${parentCode}`) {
-          result = [...path, node];
-          return true;
-      }
+    if (node.code === `${parentCode}`) {
+      result = [...path, node];
+      return true;
+    }
 
-      if (node.children) {
-          for (const child of node.children) {
-              if (findNodeRecursively(child, [...path, node])) {
-                  return true;
-              }
-          }
+    if (node.children) {
+      for (const child of node.children) {
+        if (findNodeRecursively(child, [...path, node])) {
+          return true;
+        }
       }
+    }
 
-      return false;
+    return false;
   }
 
   for (const node of data) {
-      if (parentCode != '0' && findNodeRecursively(node, [])) {
-          break;
-      }
+    if (parentCode != '0' && findNodeRecursively(node, [])) {
+      break;
+    }
   }
 
   return result;
-}
-
+};
 
 //获取所有children节点的code
 
 interface TreeNode {
   code: string;
-  [key:string]:any,
+  [key: string]: any;
   children?: TreeNode[];
 }
 
-
 export const getNodesWithChildren = (tree: TreeNode[]): TreeNode[] => {
   let nodes: TreeNode[] = [];
 
@@ -118,14 +117,14 @@ export const getNodesWithChildren = (tree: TreeNode[]): TreeNode[] => {
   }
 
   return nodes;
-}
-
+};
 
 //找出所有父节点的code
 
-
-
-export const  findParentCodes = (node: TreeNode, parentCodes: string[] = []): string[] => {
+export const findParentCodes = (
+  node: TreeNode,
+  parentCodes: string[] = [],
+): string[] => {
   if (node.children && node.children.length > 0) {
     parentCodes.push(node.code);
 
@@ -135,9 +134,7 @@ export const  findParentCodes = (node: TreeNode, parentCodes: string[] = []): st
   }
 
   return parentCodes;
-}
-
-
+};
 
 //将动态表头转换
 
@@ -154,8 +151,8 @@ type Column = {
   key: string;
   children?: Column[];
 };
- 
-export const  convertToColumns = (json: JsonStructure[]): Column[] => {
+
+export const convertToColumns = (json: JsonStructure[]): Column[] => {
   return json.map((item) => {
     let column: Column = {
       title: item.name,
@@ -169,26 +166,215 @@ export const  convertToColumns = (json: JsonStructure[]): Column[] => {
 
     return column;
   });
-}
-
+};
 
 //搜索树结构中叶子节点的某一个属性,并返回所有匹配的集合
 
-export const searchLeaves = (nodes:any[], keywords:string|undefined, keyName: string, childrenKey: string, results: any[] = []): any[] => {
+export const searchLeaves = (
+  nodes: any[],
+  keywords: string | undefined,
+  keyName: string,
+  childrenKey: string,
+  results: any[] = [],
+): any[] => {
   for (const node of nodes) {
-      if (!Array.isArray(node[childrenKey])) {
-          if (node[keyName].indexOf(keywords) != -1) {
-              results.push(node);
-          }
-      } else if (Array.isArray(node[childrenKey])) {
-          searchLeaves(node[childrenKey], keywords, keyName, childrenKey, results);
+    if (!Array.isArray(node[childrenKey])) {
+      if (node[keyName].indexOf(keywords) != -1) {
+        results.push(node);
       }
+    } else if (Array.isArray(node[childrenKey])) {
+      searchLeaves(node[childrenKey], keywords, keyName, childrenKey, results);
+    }
   }
   return results;
-}
+};
+
+// 获取节点的所有子节点(包含所有层级的子节点)
+export const getAllChildren = (
+  node: any,
+  childrenKey: string = 'children',
+): any[] => {
+  const children: any[] = [];
+
+  if (node[childrenKey] && Array.isArray(node[childrenKey])) {
+    for (const child of node[childrenKey]) {
+      children.push(child);
+      // 递归获取子节点的子节点
+      const grandChildren = getAllChildren(child, childrenKey);
+      children.push(...grandChildren);
+    }
+  }
+
+  return children;
+};
+
+// 从选中的节点中获取所有相关节点(包含父节点选中时的所有子节点)
+export const getExpandedSelectedNodes = (
+  selectedNodes: any[],
+  allNodes: any[],
+  childrenKey: string = 'children',
+): any[] => {
+  const expandedNodes: any[] = [];
+  const processedIds = new Set();
+
+  for (const selectedNode of selectedNodes) {
+    // 如果已经处理过这个节点,跳过
+    if (processedIds.has(selectedNode.id)) {
+      continue;
+    }
+
+    // 添加当前选中的节点
+    expandedNodes.push(selectedNode);
+    processedIds.add(selectedNode.id);
+
+    // 获取该节点的所有子节点
+    const children = getAllChildren(selectedNode, childrenKey);
+    for (const child of children) {
+      if (!processedIds.has(child.id)) {
+        expandedNodes.push(child);
+        processedIds.add(child.id);
+      }
+    }
+  }
+
+  return expandedNodes;
+};
+
+// 获取节点的父节点
+export const getParentNode = (
+  nodeKey: React.Key,
+  allData: any[],
+  childrenKey: string = 'children',
+  keyField: string = 'id',
+): any | null => {
+  const findParent = (
+    nodes: any[],
+    targetKey: React.Key,
+    parent: any = null,
+  ): any | null => {
+    for (const node of nodes) {
+      if (node[keyField] === targetKey) {
+        return parent;
+      }
+      if (node[childrenKey] && Array.isArray(node[childrenKey])) {
+        const result = findParent(node[childrenKey], targetKey, node);
+        if (result !== null) {
+          return result;
+        }
+      }
+    }
+    return null;
+  };
 
+  return findParent(allData, nodeKey);
+};
+
+// 检查父节点的所有子节点是否都被选中
+export const areAllChildrenSelected = (
+  parentNode: any,
+  selectedKeys: Set<React.Key>,
+  childrenKey: string = 'children',
+  keyField: string = 'id',
+): boolean => {
+  if (!parentNode[childrenKey] || !Array.isArray(parentNode[childrenKey])) {
+    return true; // 没有子节点,认为都被选中
+  }
+
+  for (const child of parentNode[childrenKey]) {
+    if (!selectedKeys.has(child[keyField])) {
+      return false;
+    }
+    // 递归检查子节点的子节点
+    if (!areAllChildrenSelected(child, selectedKeys, childrenKey, keyField)) {
+      return false;
+    }
+  }
+  return true;
+};
+
+// 处理树形选择的父子联动逻辑
+export const handleTreeSelection = (
+  selectedRowKeys: React.Key[],
+  selectedRows: any[],
+  allData: any[],
+  prevSelectedKeys: React.Key[] = [],
+  childrenKey: string = 'children',
+  keyField: string = 'id',
+): {
+  newSelectedKeys: React.Key[];
+  newSelectedRows: any[];
+} => {
+  const newSelectedKeys = new Set<React.Key>(selectedRowKeys);
+  const prevSelectedSet = new Set<React.Key>(prevSelectedKeys);
+
+  // 创建节点映射
+  const nodeMap = new Map<React.Key, any>();
+  const buildNodeMap = (nodes: any[]) => {
+    for (const node of nodes) {
+      nodeMap.set(node[keyField], node);
+      if (node[childrenKey] && Array.isArray(node[childrenKey])) {
+        buildNodeMap(node[childrenKey]);
+      }
+    }
+  };
+  buildNodeMap(allData);
+
+  // 找出新增和删除的节点
+  const addedKeys = selectedRowKeys.filter((key) => !prevSelectedSet.has(key));
+  const removedKeys = prevSelectedKeys.filter(
+    (key) => !newSelectedKeys.has(key),
+  );
+
+  // 处理新增的节点(勾选时自动勾选子节点)
+  for (const key of addedKeys) {
+    const node = nodeMap.get(key);
+    if (!node) continue;
+
+    // 自动选中所有子节点
+    const children = getAllChildren(node, childrenKey);
+    for (const child of children) {
+      newSelectedKeys.add(child[keyField]);
+    }
+  }
 
+  // 处理删除的节点(取消勾选时自动取消勾选子节点,并检查父节点)
+  for (const key of removedKeys) {
+    const node = nodeMap.get(key);
+    if (!node) continue;
 
+    // 自动取消选中所有子节点
+    const children = getAllChildren(node, childrenKey);
+    for (const child of children) {
+      newSelectedKeys.delete(child[keyField]);
+    }
 
+    // 检查并处理父节点
+    let parent = getParentNode(key, allData, childrenKey, keyField);
+    while (parent) {
+      const parentKey = parent[keyField];
+      // 如果父节点当前被选中,但不是所有子节点都被选中,则取消父节点的选中状态
+      if (
+        newSelectedKeys.has(parentKey) &&
+        !areAllChildrenSelected(parent, newSelectedKeys, childrenKey, keyField)
+      ) {
+        newSelectedKeys.delete(parentKey);
+      }
+      // 继续检查上级父节点
+      parent = getParentNode(parentKey, allData, childrenKey, keyField);
+    }
+  }
 
+  // 构建新的选中行数据
+  const newSelectedRows: any[] = [];
+  for (const key of newSelectedKeys) {
+    const node = nodeMap.get(key);
+    if (node) {
+      newSelectedRows.push(node);
+    }
+  }
 
+  return {
+    newSelectedKeys: Array.from(newSelectedKeys),
+    newSelectedRows,
+  };
+};

Některé soubory nejsou zobrazeny, neboť je v těchto rozdílových datech změněno mnoho souborů