Преглед изворни кода

完成截止20240722前的任务

code4eat пре 1 година
родитељ
комит
9d8dbe323f
48 измењених фајлова са 4130 додато и 458 уклоњено
  1. BIN
      .DS_Store
  2. 71 8
      .umirc.ts
  3. 1 1
      package.json
  4. 165 65
      src/app.tsx
  5. 64 0
      src/authWrapper.tsx
  6. 71 0
      src/components/ResizableContainer/index.tsx
  7. 25 0
      src/components/ResizableContainer/style.less
  8. 9 2
      src/global.less
  9. 0 0
      src/global.ts
  10. 0 21
      src/pages/Access/index.tsx
  11. 183 0
      src/pages/budgetMana/personnelSalaryBudget/components/cardList/index.tsx
  12. 135 0
      src/pages/budgetMana/personnelSalaryBudget/components/cardList/style.less
  13. 422 0
      src/pages/budgetMana/personnelSalaryBudget/components/distribute/index.tsx
  14. 231 0
      src/pages/budgetMana/personnelSalaryBudget/components/distribute/style.less
  15. 197 0
      src/pages/budgetMana/personnelSalaryBudget/components/distribute/tableSelector.tsx
  16. 249 107
      src/pages/budgetMana/personnelSalaryBudget/index.tsx
  17. 84 4
      src/pages/budgetMana/personnelSalaryBudget/service.ts
  18. 175 88
      src/pages/budgetMana/personnelSalaryBudget/style.less
  19. BIN
      src/pages/noAccess/images/noAccess.png
  20. 29 0
      src/pages/noAccess/index.tsx
  21. 49 0
      src/pages/noAccess/style.less
  22. 150 0
      src/pages/reportCheck/report/SetColWidComponent.tsx
  23. 80 56
      src/pages/reportCheck/report/index.tsx
  24. 12 0
      src/pages/reportCheck/report/style.less
  25. 19 3
      src/pages/secondaryDistribute/employeeInfoCheck/index.tsx
  26. 5 1
      src/pages/secondaryDistribute/secondaryDitriComputed/index.tsx
  27. 3 1
      src/pages/setting/checkUnitSet/checkUnitDepMap/index.tsx
  28. 1 1
      src/pages/setting/checkUnitSet/checkUnitEmpSet/index.tsx
  29. 102 46
      src/pages/setting/projectSetting/checkUnitProjectSet/index.tsx
  30. 370 0
      src/pages/setting/projectSetting/jobCateRetenSet/UpDataActBtn.tsx
  31. 441 0
      src/pages/setting/projectSetting/jobCateRetenSet/index.tsx
  32. 74 0
      src/pages/setting/projectSetting/jobCateRetenSet/service.ts
  33. 134 0
      src/pages/setting/projectSetting/jobCateRetenSet/style.less
  34. 237 0
      src/pages/setting/projectSetting/retentionAssessmentProjectMana/index.tsx
  35. 74 0
      src/pages/setting/projectSetting/retentionAssessmentProjectMana/service.ts
  36. 102 0
      src/pages/setting/projectSetting/retentionAssessmentProjectMana/style.less
  37. 2 2
      src/pages/setting/projectSetting/secondaryDistriGroupSet/index.tsx
  38. 31 34
      src/pages/setting/reportSet/diySqlMana/index.tsx
  39. 39 4
      src/pages/setting/reportSet/reportSetting/index.tsx
  40. 24 8
      src/pages/static/index.tsx
  41. 22 0
      src/pages/static/service.ts
  42. 10 1
      src/services/getDic.ts
  43. 36 3
      src/utils/tooljs.ts
  44. 2 2
      src/utils/zhongtaiC.js
  45. BIN
      static/distribute.png
  46. BIN
      static/textImg_xi.png
  47. BIN
      static/textImg_zhi.png
  48. BIN
      绩效管理系统_2024_04_01.zip

+ 71 - 8
.umirc.ts

@@ -2,7 +2,7 @@
  * @Author: code4eat awesomedema@gmail.com
  * @Date: 2022-12-14 14:14:32
  * @LastEditors: code4eat awesomedema@gmail.com
- * @LastEditTime: 2024-01-15 16:02:02
+ * @LastEditTime: 2024-07-19 16:03:13
  * @FilePath: /BudgetManaSystem/.umirc.ts
  * @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
  */
@@ -43,7 +43,7 @@ export default defineConfig({
 
   access: {},
   model: {},
-
+  esbuildMinifyIIFE:true,
   initialState: {},
   request: {},
   layout: {
@@ -72,10 +72,15 @@ export default defineConfig({
       path: '/',
       redirect: '/home',
     },
+    {
+      path: '/noAccess',
+      component: './noAccess',
+    },
     {
       name: '首页',
       path: '/home',
       component: './Home',
+      wrappers: ['@/authWrapper'],
     },
     // {
     //   name: '静态测试',
@@ -94,26 +99,31 @@ export default defineConfig({
               name: '业务字典分类管理',
               path: '/setting/baseSetting/dicClassfication',
               component: './setting/baseSetting/dicClassfication',
+              wrappers: ['@/authWrapper'],
             },
             {
               name: '业务字典管理',
               path: '/setting/baseSetting/businessDicMana',
               component: './setting/baseSetting/businessDicMana',
+              wrappers: ['@/authWrapper'],
             },
             {
               name: '参数管理',
               path: '/setting/baseSetting/paramsMana',
               component: './setting/baseSetting/paramsMana',
+              wrappers: ['@/authWrapper'],
             },
             {
               name: '职类基础设定',
               path: '/setting/baseSetting/jobCateBaseSet',
               component: './setting/baseSetting/jobCateBaseSet',
+              wrappers: ['@/authWrapper'],
             },
             {
               name: '岗位等级系数设定',
               path: '/setting/baseSetting/positionLevelRateSet',
               component: './setting/baseSetting/positionLevelRateSet',
+              wrappers: ['@/authWrapper'],
             },
           ]
         },
@@ -125,46 +135,66 @@ export default defineConfig({
               name: '收费项目管理',
               path: '/setting/projectSetting/bilingProjectMana',
               component: './setting/projectSetting/bilingProjectMana',
+              wrappers: ['@/authWrapper'],
             },
             {
               name: '静态测试',
               path: '/setting/projectSetting/static/:pageCode',
               component: './setting/projectSetting/bilingProjectMana',
+              wrappers: ['@/authWrapper'],
             },
             {
               name: '指标项目管理',
               path: '/setting/projectSetting/indicProjectMana',
               component: './setting/projectSetting/indicProjectMana',
+              wrappers: ['@/authWrapper'],
             },
             {
               name: '二次分配项目管理',
               path: '/setting/projectSetting/secondaryProjectDistribute',
               component: './setting/projectSetting/secondaryProjectDistribute',
+              wrappers: ['@/authWrapper'],
             },
             {
               name: '非考核项目管理',
               path: '/setting/projectSetting/nonAssessmentProjectMana',
               component: './setting/projectSetting/nonAssessmentProjectMana',
+              wrappers: ['@/authWrapper'],
+            },
+            {
+              name: '保留考核项目管理',
+              path: '/setting/projectSetting/retentionAssessmentProjectMana',
+              component: './setting/projectSetting/retentionAssessmentProjectMana',
+              wrappers: ['@/authWrapper'],
+            },{
+              name: '职类保留考核设定',
+              path: '/setting/projectSetting/jobCateRetenSet',
+              component: './setting/projectSetting/jobCateRetenSet',
+              wrappers: ['@/authWrapper'],
             },
             {
               name: '职类二次分配设定',
               path: '/setting/projectSetting/occupationsSecondaryDistriSet',
               component: './setting/projectSetting/occupationsSecondaryDistriSet',
+              wrappers: ['@/authWrapper'],
             },
             {
               name: '核算单元项目设定',
               path: '/setting/projectSetting/checkUnitProjectSet',
               component: './setting/projectSetting/checkUnitProjectSet',
+              wrappers: ['@/authWrapper'],
             },
             {
               name: '成本收入项目设定',
               path: '/setting/projectSetting/costIncomeProjectSet',
               component: './setting/projectSetting/costIncomeProjectSet',
+              wrappers: ['@/authWrapper'],
             },
             {
               name: '二次分配分组设定',
               path: '/setting/projectSetting/secondaryDistriGroupSet',
               component: './setting/projectSetting/secondaryDistriGroupSet',
+              wrappers: ['@/authWrapper'],
             },
           ]
         },
@@ -176,21 +206,25 @@ export default defineConfig({
               name: '管理指标项目设定',
               path: '/setting/manaPerformanceSet/manaIndicItemSet',
               component: './setting/manaPerformanceSet/manaIndicItemSet',
+              wrappers: ['@/authWrapper'],
             },
             {
               name: '职类考核分级设定',
               path: '/setting/manaPerformanceSet/classAssessAndGradeSet',
               component: './setting/manaPerformanceSet/classAssessAndGradeSet',
+              wrappers: ['@/authWrapper'],
             },
             {
               name: '指标分组权重设定',
               path: '/setting/manaPerformanceSet/indicGroupWeightSet',
               component: './setting/manaPerformanceSet/indicGroupWeightSet',
+              wrappers: ['@/authWrapper'],
             },
             {
               name: '单元指标目标设定',
               path: '/setting/manaPerformanceSet/unitIndicTargetSet',
               component: './setting/manaPerformanceSet/unitIndicTargetSet',
+              wrappers: ['@/authWrapper'],
             },
           ]
         },
@@ -202,21 +236,25 @@ export default defineConfig({
               name: '报表列管理',
               path: '/setting/reportSet/reportListMana',
               component: './setting/reportSet/reportListMana',
+              wrappers: ['@/authWrapper'],
             },
             {
               name: '报表设置',
               path: '/setting/reportSet/reportSetting',
               component: './setting/reportSet/reportSetting',
+              wrappers: ['@/authWrapper'],
             },
             {
               name: '报表跳转管理',
               path: '/setting/reportSet/reportNavSet',
               component: './setting/reportSet/reportNavSet',
+              wrappers: ['@/authWrapper'],
             },
             {
               name: '自定义SQL管理',
               path: '/setting/reportSet/diySqlMana',
               component: './setting/reportSet/diySqlMana',
+              wrappers: ['@/authWrapper'],
             }
           ]
         },
@@ -228,31 +266,37 @@ export default defineConfig({
               name: '核算单元分类管理',
               path: '/setting/checkUnitSet/checkUnitClassMana',
               component: './setting/checkUnitSet/checkUnitClassMana',
+              wrappers: ['@/authWrapper'],
             },
             {
               name: '核算单元管理',
               path: '/setting/checkUnitSet/checkUnitMana',
               component: './setting/checkUnitSet/checkUnitMana',
+              wrappers: ['@/authWrapper'],
             },
             {
               name: '核算单元科室对照',
               path: '/setting/checkUnitSet/checkUnitDepMap',
               component: './setting/checkUnitSet/checkUnitDepMap',
+              wrappers: ['@/authWrapper'],
             },
             {
               name: '核算单元人员设定',
               path: '/setting/checkUnitSet/checkUnitEmpSet',
               component: './setting/checkUnitSet/checkUnitEmpSet',
+              wrappers: ['@/authWrapper'],
             },
             {
               name: '医疗组设定',
               path: '/setting/checkUnitSet/medicalGroupSet',
               component: './setting/checkUnitSet/medicalGroupSet',
+              wrappers: ['@/authWrapper'],
             },
             {
               name: '检视单元权限设定',
               path: '/setting/checkUnitSet/viewUnitPermSet',
               component: './setting/checkUnitSet/viewUnitPermSet',
+              wrappers: ['@/authWrapper'],
             }
           ]
         },
@@ -266,32 +310,38 @@ export default defineConfig({
         {
           name: '月度结转',
           path: '/budgetMana/monthlySet',
-          component: './budgetMana/monthlySet'
+          component: './budgetMana/monthlySet',
+          wrappers: ['@/authWrapper'],
         },
         {
           name: '月度设置信息核对',
           path: '/budgetMana/monthlyInfoCheck',
-          component: './budgetMana/monthlyInfoCheck'
+          component: './budgetMana/monthlyInfoCheck',
+          wrappers: ['@/authWrapper'],
         },
         {
           name: '月度绩效数据核对',
           path: '/budgetMana/monthlyDataCheck',
-          component: './budgetMana/monthlyDataCheck'
+          component: './budgetMana/monthlyDataCheck',
+          wrappers: ['@/authWrapper'],
         },
         {
           name: '人事薪酬预算',
           path: '/budgetMana/personnelSalaryBudget',
-          component: './budgetMana/personnelSalaryBudget'
+          component: './budgetMana/personnelSalaryBudget',
+          wrappers: ['@/authWrapper'],
         },
         {
           name: '一次分配',
           path: '/budgetMana/oneBatch',
-          component: './budgetMana/oneBatch'
+          component: './budgetMana/oneBatch',
+          wrappers: ['@/authWrapper'],
         },
         {
           name: '特殊数据导入',
           path: '/budgetMana/specialDataImport',
-          component: './budgetMana/specialDataImport'
+          component: './budgetMana/specialDataImport',
+          wrappers: ['@/authWrapper'],
         }
       ]
     },
@@ -302,6 +352,7 @@ export default defineConfig({
         {
           path: '/reportCheck/report/:reportCode',
           component: './reportCheck/report',
+          wrappers: ['@/authWrapper'],
         },
       ]
     },
@@ -313,26 +364,31 @@ export default defineConfig({
           name: '人员信息核对',
           path: '/secondaryDistribute/employeeInfoCheck',
           component: './secondaryDistribute/employeeInfoCheck',
+          wrappers: ['@/authWrapper'],
         },
         {
           name: '非考核项目核定',
           path: '/secondaryDistribute/nonCheckProjectApprove',
           component: './secondaryDistribute/nonCheckProjectApprove',
+          wrappers: ['@/authWrapper'],
         },
         {
           name: '单元考核项目评分',
           path: '/secondaryDistribute/unitCheckProjectScore',
           component: './secondaryDistribute/unitCheckProjectScore',
+          wrappers: ['@/authWrapper'],
         },
         {
           name: '二次分配计算',
           path: '/secondaryDistribute/secondaryDitriComputed',
           component: './secondaryDistribute/secondaryDitriComputed',
+          wrappers: ['@/authWrapper'],
         },
         {
           name: '二次分配审核',
           path: '/secondaryDistribute/secondaryDitriCheck',
           component: './secondaryDistribute/secondaryDitriComputed',
+          wrappers: ['@/authWrapper'],
         },
 
       ]
@@ -344,6 +400,7 @@ export default defineConfig({
         {
           name: '国考指标',
           path: '/crosstabReport/nationalIndicator',
+          // wrappers: ['@/authWrapper'],
           routes: [{}]
         },
         {
@@ -353,21 +410,25 @@ export default defineConfig({
             {
               name: '大屏-全院分析',
               path: '/crosstabReport/businessAnalysis/hospitalAnalysis',
+              // wrappers: ['@/authWrapper'],
               routes: [{}]
             },
             {
               name: '大屏-住院分析',
               path: '/crosstabReport/businessAnalysis/inpAnalysis',
+              // wrappers: ['@/authWrapper'],
               routes: [{}]
             },
             {
               name: '大屏-门诊分析',
               path: '/crosstabReport/businessAnalysis/opdAnalysis',
+              // wrappers: ['@/authWrapper'],
               routes: [{}]
             },
             {
               name: '收入分析',
               path: '/crosstabReport/businessAnalysis/incomeAnalysis',
+              // wrappers: ['@/authWrapper'],
               routes: [{}]
             },
           ]
@@ -375,11 +436,13 @@ export default defineConfig({
         {
           name: '成本分析',
           path: '/crosstabReport/costAnalysis',
+          // wrappers: ['@/authWrapper'],
           routes: [{}]
         },
         {
           name: '财务报表分析',
           path: '/crosstabReport/financialReportAnalysis',
+          // wrappers: ['@/authWrapper'],
           routes: [{}]
         },
       ]

+ 1 - 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=none PORT=8002 max dev",
     "format": "prettier --cache --write .",
     "postinstall": "max setup",
     "mock": "max dev",

+ 165 - 65
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-08-10 14:01:11
+ * @LastEditTime: 2024-07-17 14:56:56
  * @FilePath: /BudgetManaSystem/src/app.ts
  * @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
  */
@@ -14,8 +14,8 @@
 
 
 
-import { AxiosResponse } from '@umijs/max';
-import { message, notification } from 'antd';
+import { AxiosResponse, history } from '@umijs/max';
+import { message, notification, Menu } from 'antd';
 import type { RequestConfig } from 'umi';
 
 import iconEnum from './menuIcons.js';
@@ -28,11 +28,14 @@ import DevicePixelRatio from './utils/devicePixelRatio.js';
 
 import Icon, { createFromIconfontCN, FolderOutlined } from '@ant-design/icons';
 import { getPlatformMenu } from '@/services/auth';
-import { useState } from 'react';
+import { useEffect, useState } from 'react';
 
 
 import './utils/zhongtaiC'
-import { RuntimeAntdConfig } from '@umijs/max';
+
+import ResizableContainer from './components/ResizableContainer/index';
+
+
 
 
 const IconFont = createFromIconfontCN({
@@ -48,7 +51,7 @@ enum ErrorShowType {
   NOTIFICATION = 3,
   REDIRECT = 9,
 }
-// 与后端约定的响应数据格式
+// 与后端约定的响应数据格式  
 interface ResponseStructure {
   success?: boolean;
   code?: string;
@@ -68,13 +71,70 @@ interface UserData {
 
 
 
+interface TransformResult {
+  newTree: any[];
+  firstLeafNode: any | null;
+  firstLeafNodePath: any[];
+}
+
+function transformTree(tree: any[]): TransformResult {
+  let firstLeafNode: any | null = null;
+  let firstLeafNodePath: any[] = [];
+
+  function traverse(node: any, path: any[]): any {
+    const newNode: any = {
+      ...node,
+      label: node.name,
+    };
+
+    const newPath = [...path, newNode];
+
+    if (node.children && node.children.length > 0) {
+      newNode.children = node.children.map((child: any) => traverse(child, newPath));
+    } else {
+      if (!firstLeafNode) {
+        firstLeafNode = newNode;
+        firstLeafNodePath = newPath;
+      }
+    }
+
+    // Remove children property if it is an empty array
+    if (node.children && node.children.length === 0) {
+      newNode.children = null;
+    }
+
+    return newNode;
+  }
+
+  const newTree = tree.map(node => traverse(node, []));
+
+  return { newTree, firstLeafNode, firstLeafNodePath };
+}
+
+const findItemByKey: any = (tree: any[], key: string, keyName: string) => {
+  for (const node of tree) {
+    if (node[`${keyName}`] === key) {
+      return node;
+    }
+    if (node.children) {
+      const result = findItemByKey(node.children, key, keyName);
+      if (result) {
+        return result;
+      }
+    }
+  }
+  return null;
+}
+
+
+
 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 = {
@@ -110,6 +170,7 @@ export const request: RequestConfig = {
       if (error.name === 'BizError') {
         const errorInfo: ResponseStructure | undefined = error.info;
         if (errorInfo) {
+
           const { errorMessage, errorCode } = errorInfo;
           switch (errorInfo.showType) {
             case ErrorShowType.SILENT:
@@ -167,7 +228,7 @@ export const request: RequestConfig = {
     (response: AxiosResponse) => {
       // 拦截响应数据,进行个性化处理
 
-      const { status, data: { success, status: code, errorMessage, data: respData }, config: { method } } = response;
+      const { status, data: { success, status: code, errorMessage, msg, data: respData }, config: { method } } = response;
 
       try {
 
@@ -185,10 +246,12 @@ export const request: RequestConfig = {
 
             } else {
               notification.error({
-                message: '',
-                description: errorMessage,
+                top:72,
+                message: '提示',
+                description: errorMessage || msg,
                 placement: 'topRight',
-                icon: <></>
+                icon:<IconFont type="icon-jinggaotishi"  />,
+                style:{padding:16,margin:0,borderRadius:8}
               })
               return false
             }
@@ -203,10 +266,12 @@ export const request: RequestConfig = {
               } else {
 
                 notification.error({
-                  message: '',
-                  description: errorMessage,
+                  top:72,
+                  message: '提示',
+                  description: errorMessage || msg,
                   placement: 'topRight',
-                  icon: <></>
+                  icon:<IconFont type="icon-jinggaotishi"  />,
+                  style:{padding:16,margin:0,borderRadius:8}
                 })
                 return false;
               }
@@ -231,31 +296,31 @@ export const request: RequestConfig = {
 export function patchClientRoutes({ routes }: { routes: any }) {
 
   const treeLoop = (treeData: any) => {
-    // console.log({treeData});
-    if(treeData.routes){
+    // console.log({treeData,routes});
+    if (treeData.routes) {
       const paths = [...new Array(20).keys()].map((a, index) => ({
-        path: `${treeData.path == '/'?'':treeData.path}/reports/${index}`,
+        path: `${treeData.path == '/' ? '' : treeData.path}/reports/${index}`,
         exact: true,
         element: <Report />,
       }));
-  
+
       const lanhuPagePaths = [...new Array(20).keys()].map((a, index) => ({
-        path: `${treeData.path == '/'?'':treeData.path}/static/${index}`,
+        path: `${treeData.path == '/' ? '' : treeData.path}/static/${index}`,
         exact: true,
         element: <Static />,
       }));
-  
+
       //console.log({paths});
-  
+
       paths.forEach((a: any) => {
         treeData.routes.push(a);
       });
-  
+
       lanhuPagePaths.forEach((a: any) => {
         treeData.routes.push(a);
       });
     }
-    
+
 
     if (treeData.routes && treeData.routes.length > 0) {
       treeData.routes.forEach((a: any) => {
@@ -329,8 +394,7 @@ const crosstabReport = (params: 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[]>([]);
+
 
   const onCollapse = (isCollapsed: boolean): void => {
     setInitialState({ ...initialState, isCollapsed }).then();
@@ -338,29 +402,34 @@ export const layout = ({ initialState, setInitialState }: { initialState: any, s
 
 
   return {
-    menuHeaderRender: false,
-    token: {
-      sider: {
+    disableMobile: true,
+    menuRender: (props: any) => {
 
-        colorMenuBackground: '#fff',
-        colorTextMenuActive: '#3376FE',
-        colorTextMenuSelected: '#3376FE',
-        colorTextMenuTitle: '#17181A',
-        colorTextMenu: '#17181A',
-        //colorBgMenuItemHover:'##f0f2f5',
-        colorBgMenuItemSelected: '#F2F6FF',
 
-        // colorBgMenuItemCollapsedHover:'#f0f2f5',
-        // //colorBgMenuItemCollapsedSelected:'blue'
+      const { location } = props;
+      const [menu, set_menu] = useState<any[]>([]);
+
+      const [slectedKey, set_slectedKey] = useState<string[]>([]);
+      const [openKeys, set_openKeys] = useState<string[]>([]);
+
+      // const currentSelectedItem = findItemByKey(menuData,location.pathname,'path');
+
+      const menuClickHandle = (info: any) => {
+        const foundItem = findItemByKey(menu, info.key, 'key');
+        // console.log({foundItem,info});
+        if (foundItem) {
+          set_slectedKey([`${info.key}`]);
+          history.push(foundItem.path);
+
+        }
 
       }
 
-    },
-    siderWidth: 200,
+      const onOpenChangeHandle = (openKeys: string[]) => {
+        set_openKeys(openKeys);
+      }
 
-    menu: {
-      locale: false,
-      request: async () => {
+      const getMenu = async () => {
         const userData = localStorage.getItem('userData');
         const currentSelectedTab = localStorage.getItem('currentSelectedTab');
         if (currentSelectedTab) {
@@ -404,7 +473,7 @@ 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) }));
+            setInitialState((t: any) => ({ ...t, spacicalPageParamsType: _menu, memuData: data, userData: JSON.parse(userData as string) }));
 
             //return mappingIcon(data);
 
@@ -445,40 +514,71 @@ export const layout = ({ initialState, setInitialState }: { initialState: any, s
             }
 
             data.forEach((item: any) => {
-              addIconToPath(item, ['/home', '/budgetMana', '/setting', '/reportCheck', '/secondaryDistribute','/crosstabReport']);
+              addIconToPath(item, ['/home', '/budgetMana', '/setting', '/reportCheck', '/secondaryDistribute', '/crosstabReport']);
             });
 
-            return data
+            const { newTree, firstLeafNode, firstLeafNodePath } = transformTree(data);
+            // set_openKeys(firstLeafNodePath.map(a => `${a.key}`));
+            // set_slectedKey([`${firstLeafNode.key}`]);
+            set_menu(newTree);
+
           }
         }
+      }
+
+      useEffect(() => {
+
+        const foundItem = findItemByKey(menu, location.pathname, 'path');
+
+        if (foundItem) {
+          set_slectedKey(foundItem.key);
+
+        }
+
+      }, [menu]);
+
+      useEffect(() => {
+        getMenu();
+      }, [])
 
-      },
-    },
-    onPageChange: (location: Location) => {
-      // console.log({location});
-      // const {pathname} = location;
-      // localStorage.setItem('currentPath',pathname);
-    },
-    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={
-          () => {
-            onCollapse(isCollapsed ? false : true);
-          }
-        }><IconFont className='menuCollapseIcon' type={isCollapsed ? 'icon-celanzhankai' : 'icon-celanshouqi'} /></div>
-      )
+        <ResizableContainer width={isCollapsed?64:200} minWidth={0} maxWidth={600} height={'calc(100vh - 58px)'}>
+          <div style={{ height: '100%', background: '#fff', position: 'relative' }}>
+            <div className='menuWrapper' style={{height:'calc(100% - 40px)',overflowY: 'scroll',overflowX:'hidden'}}>
+              <Menu
+                
+                onClick={menuClickHandle}
+                onOpenChange={onOpenChangeHandle}
+                style={{ width: '100%' }}
+                openKeys={openKeys}
+                selectedKeys={slectedKey}
+                mode="inline"
+                items={menu}
+                inlineCollapsed={isCollapsed}
+              />
+            </div>
+
+            <div style={{
+              position: 'absolute', zIndex: 10, right: 17, bottom:9,
+              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>
+          </div>
+        </ResizableContainer>
+      );
     },
     collapsed: isCollapsed,
     contentStyle: {
       border: '16px solid #F7F9FC',
+      borderRight: '10px solid #F7F9FC',
       //height: '94.5vh',  //以去除顶部导航高度
       borderRadius: '22px',
       background: '#F7F9FC',
-      height:'calc(100vh - 48px)'
+      height: 'calc(100vh - 48px)'
       // overflow:'scroll',
       // margin:'20px 20px'
     },

+ 64 - 0
src/authWrapper.tsx

@@ -0,0 +1,64 @@
+/*
+ * @Author: code4eat awesomedema@gmail.com
+ * @Date: 2024-07-17 10:36:43
+ * @LastEditors: code4eat awesomedema@gmail.com
+ * @LastEditTime: 2024-07-18 16:41:08
+ * @FilePath: /BudgetManaSystem/src/authWrapper.tsx
+ * @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
+ */
+// src/components/AuthWrapper.tsx
+import React, { useEffect, useState } from 'react';
+import { history, useModel } from 'umi';
+import type { IRoute } from 'umi';
+import { Outlet } from '@umijs/max';
+
+// 权限检查函数
+function checkAccess(menu: IRoute[], pathname: string): boolean {
+    for (const item of menu) {
+      if (item.path === pathname) {
+        return true;
+      }
+      if (item.children) {
+        const hasAccess = checkAccess(item.children, pathname);
+        if (hasAccess) {
+          return true;
+        }
+      }
+    }
+    return false;
+}
+
+function removePrefix(input: string, prefix: string): string {
+    if (input.startsWith(prefix)) {
+        return input.slice(prefix.length);
+    }
+    return input;
+}
+
+// 权限包装组件
+const AuthWrapper: React.FC = (props) => {
+    const { initialState } = useModel('@@initialState');
+    const [loading, setLoading] = useState(true);
+    const [dataLoaded, setDataLoaded] = useState(false);
+  
+    useEffect(() => {
+      if (!dataLoaded) {
+        const menu = initialState?.memuData ?? [];
+        if (menu.length > 0) {
+            setDataLoaded(true);
+            const { pathname } = history.location;
+          const hasAccess = checkAccess(menu, removePrefix(pathname, '/budgetManaSystem'));
+        //   console.log({ hasAccess, menu, pathname });
+          if (!hasAccess) {
+            history.push('/noAccess');
+          }
+          setLoading(false);
+        }
+      }
+    }, [initialState,dataLoaded]);
+  
+    return loading ? <>loading...</> : <Outlet />;
+  };
+
+export default AuthWrapper;
+export { checkAccess };

+ 71 - 0
src/components/ResizableContainer/index.tsx

@@ -0,0 +1,71 @@
+/*
+ * @Author: code4eat awesomedema@gmail.com
+ * @Date: 2024-05-16 10:57:51
+ * @LastEditors: code4eat awesomedema@gmail.com
+ * @LastEditTime: 2024-06-07 17:16:04
+ * @FilePath: /BudgetManaSystem/src/components/ResizableContainer/index.tsx
+ * @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
+ */
+import React, { useState, useCallback, useEffect, useRef } from 'react';
+import './style.less';
+
+interface ResizableContainerProps {
+    width?: number;
+    minWidth?: number;
+    maxWidth?: number;
+    height?:string;
+}
+
+const ResizableContainer: React.FC<React.PropsWithChildren<ResizableContainerProps>> = ({
+    width = 250,
+    minWidth = 100,
+    maxWidth = 800,
+    height = '800px',
+    children,
+}) => {
+    const [containerWidth, setContainerWidth] = useState(width);
+    const isResizing = useRef(false);
+
+    const handleMouseMove = useCallback((e: MouseEvent) => {
+        if (isResizing.current) {
+            const newWidth = Math.min(Math.max(e.clientX, minWidth), maxWidth);
+            setContainerWidth(newWidth);
+        }
+    }, [minWidth, maxWidth]);
+
+    const handleMouseUp = useCallback(() => {
+        isResizing.current = false;
+        document.removeEventListener('mousemove', handleMouseMove);
+        document.removeEventListener('mouseup', handleMouseUp);
+        document.body.style.userSelect = 'auto'; // 恢复文本选择
+    }, [handleMouseMove]);
+
+    const handleMouseDown = useCallback(() => {
+        isResizing.current = true;
+        document.addEventListener('mousemove', handleMouseMove);
+        document.addEventListener('mouseup', handleMouseUp);
+        document.body.style.userSelect = 'none'; // 禁用文本选择
+    }, [handleMouseMove, handleMouseUp]);
+
+    useEffect(() => {
+        return () => {
+            document.removeEventListener('mousemove', handleMouseMove);
+            document.removeEventListener('mouseup', handleMouseUp);
+        };
+    }, [handleMouseMove, handleMouseUp]);
+
+    useEffect(() => {
+        setContainerWidth(width);
+    }, [width]);
+
+    return (
+        <div className="ResizableContainer">
+            <div className="resizable" style={{ width: `${containerWidth}px`, height: `${height}` }}>
+                {children}
+            </div>
+            <div className="resizer" onMouseDown={handleMouseDown} />
+        </div>
+    );
+};
+
+export default ResizableContainer;

+ 25 - 0
src/components/ResizableContainer/style.less

@@ -0,0 +1,25 @@
+
+
+.ResizableContainer {
+    position: relative;
+    height:100%;
+    
+    .resizable {
+        height: 100%;
+        overflow: hidden;
+    }
+    
+    .resizer {
+        position: absolute;
+        z-index: 999;
+        right: 0;
+        top:0;
+        width: 5px;
+        height:90vh;
+        cursor: ew-resize;
+        background-color:transparent;
+    }
+   
+}
+
+

+ 9 - 2
src/global.less

@@ -485,6 +485,8 @@ textarea {
     }
 }
 
+
+
 .bms-ant-table-wrapper {
     .bms-ant-table:not(.bms-ant-table-bordered) .bms-ant-table-tbody>tr.bms-ant-table-row.bms-ant-table-row-selected>td:first-child {
         border-radius: 0 !important;
@@ -531,7 +533,7 @@ textarea {
 //     color: #52c41a !important;
 // }
 .ant-message-success {
-    .anticon .anticon-check-circle {
+    .anticon .anticon-check-circle {                                                                                                                                                                                                                                                                                                             
         color: #52c41a !important;
     }
 }
@@ -541,7 +543,7 @@ textarea {
     Select
 **/
 .bms-ant-select {
-    .bms-ant-select-selector {
+    .bms-ant-select-selector {  
         height: 24px !important;
         padding: 0 8px !important;
         border-radius: 4px !important;
@@ -622,6 +624,11 @@ textarea {
 .bms-ant-pro-layout .bms-ant-pro-layout-container {
     flex: 1;
     background: #F7F9FC !important;
+    overflow-y: scroll;
+}
+
+.bms-ant-menu-inline {
+    border-right: none !important;
 }
 
 //页面全局底色

+ 0 - 0
src/global.ts


+ 0 - 21
src/pages/Access/index.tsx

@@ -1,21 +0,0 @@
-import { PageContainer } from '@ant-design/pro-components';
-import { Access, useAccess } from '@umijs/max';
-import { Button } from 'antd';
-
-const AccessPage: React.FC = () => {
-  const access = useAccess();
-  return (
-    <PageContainer
-      ghost
-      header={{
-        title: '权限示例',
-      }}
-    >
-      <Access accessible={access.canSeeAdmin}>
-        <Button>只有 Admin 可以看到这个按钮</Button>
-      </Access>
-    </PageContainer>
-  );
-};
-
-export default AccessPage;

+ 183 - 0
src/pages/budgetMana/personnelSalaryBudget/components/cardList/index.tsx

@@ -0,0 +1,183 @@
+/*
+ * @Author: code4eat awesomedema@gmail.com
+ * @Date: 2024-05-07 13:41:37
+ * @LastEditors: code4eat awesomedema@gmail.com
+ * @LastEditTime: 2024-06-21 14:54:24
+ * @FilePath: /BudgetManaSystem/src/pages/budgetMana/personnelSalaryBudget/components/cardList/index.tsx
+ * @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
+ */
+
+
+
+import { createFromIconfontCN } from '@ant-design/icons';
+import { useEffect, useState } from 'react';
+import { InputNumber,message,Modal } from 'antd'
+import './style.less'
+import { copyReq, getKpiRetainList, saveKpiRetain } from '../../service';
+
+
+const IconFont = createFromIconfontCN({
+    scriptUrl: '',
+});
+
+const formatValue = (value:number) => {
+    if (value !== undefined && value !== null) {
+      return parseFloat(value.toString().replace(/(\.\d*?[1-9])0+$/g, '$1').replace(/\.0*$/, ''));
+    }
+    return value;
+};
+
+function removeTrailingZeros(str:string) {
+    // 将数字转换为字符串,并去掉多余的0
+    return str.replace(/(\.\d*?[1-9])0+$/g, '$1').replace(/\.0*$/, '');
+  }
+
+export const CardList = ({ computeDate }: { computeDate: string }) => {
+
+    const [listData, set_listData] = useState<any[]>([]);
+    const [currentEdit, set_currentEdit] = useState<any>(undefined);
+    const [currentEditGroup, set_currentEditGroup] = useState<any>(undefined);
+    const [inputVal, set_inputVal] = useState(0);
+
+
+    const editHandle = (group: any, current: any) => {
+        if (currentEdit) {
+            const newArr = listData.map((a) => {
+                if (a.unitTypeCode === currentEditGroup.unitTypeCode) {
+                    const newRetainList = a.retainList.map((b:any) => {
+                        if (b.code === currentEdit.code) {
+                            return { ...b, value: inputVal };
+                        } else {
+                            return b;
+                        }
+                    });
+                    // 返回更新后的整个对象a,包括新的retainList和其他属性
+                    return { ...a, retainList: newRetainList };
+                } else {
+                    return a;
+                }
+            });
+          
+            set_listData([...newArr]);
+            savaData(newArr);
+            set_currentEditGroup(undefined);
+            set_currentEdit(undefined); return;
+        } else {
+            set_currentEdit({...current,flag:`${group.unitTypeCode}${current.code}`});
+            set_currentEditGroup(group);
+        }
+
+    }
+
+    const getData = async () => {
+        const resp = await getKpiRetainList(computeDate);
+        if (resp) {
+            set_listData(resp);
+        }
+    }
+
+    const onChangeHandle = (value: any) => {
+        if (value !== undefined && value !== null) {
+            // 将值转换为字符串并去掉无效的零
+            const formattedValue = parseFloat(value.toString().replace(/(\.\d*?[1-9])0+$/g, '$1').replace(/\.0*$/, ''));
+            set_inputVal(formattedValue);
+          } else {
+            set_inputVal(value);
+          }
+    }
+
+    const savaData = async (newData:any)=>{
+          const resp = await saveKpiRetain({
+            computeDate,
+            list:newData
+          });
+          if(resp){
+                message.success('操作成功!');
+          }
+    }
+
+    const copyReqHandle = async ()=>{
+          Modal.confirm({
+              title:'注意',
+              content:'复制操作会覆盖已有的保留系数设定,确定要继续操作?',
+              okText:'确定',
+              cancelText:'取消',
+              onOk:async (...args) => {
+                const resp = await copyReq(computeDate);
+                if(resp){
+                  message.success('复制成功!');
+                  getData();
+                }
+              },
+          })
+    }
+
+    useEffect(() => {
+        getData();
+        // set_listData([
+        //     {
+        //         unitTypeName:'临床医生',
+        //         retainList:[
+        //             {
+        //                 name:'季度考核',
+        //                 value:1,
+        //                 code:1
+        //             },
+        //             {
+        //                 name:'季度考核',
+        //                 value:1,
+        //                 code:2
+        //             }
+        //         ]
+        //     },
+        //     {
+        //         unitTypeName:'临床医生',
+        //         retainList:[
+        //             {
+        //                 name:'季度考核',
+        //                 value:1,
+        //                 code:3
+        //             },
+
+        //         ]
+        //     }
+        // ])
+    }, [])
+
+    return (
+        <div className="CardList">
+            <div className="header">
+                <span className="title">保留系数设定</span>
+                <a className='copyBtn' onClick={()=>copyReqHandle()}> <IconFont style={{ color: '#3377FF', paddingRight: 4,fontSize:16 }} type="iconfuzhi1" />复制上月</a>
+            </div>
+            <div className='content'>
+                {
+                    listData.map((a, index) => (
+                        <div className='group' key={index}>
+                            <div className='groupName'>{a.unitTypeName}</div>
+                            <div className='listWrap'>
+                                {
+                                    a.retainList.map((b: any, i: number) => (
+                                        <div className='list' key={i}>
+                                            <div className='name'>
+                                                <img src={require(`../../../../../../static/${b.type == 1 ? 'textImg_xi.png' : 'textImg_zhi.png'}`)} alt="" />
+                                                {b.name}
+                                            </div>
+                                            <div className='detail'>
+                                                {currentEdit?.flag != `${a.unitTypeCode}${b.code}` && <span className='num'>{b.type == 1?(removeTrailingZeros((b.value * 100).toFixed(4))+'%'):removeTrailingZeros((b.value).toFixed(2))}</span>}
+                                                {currentEdit?.flag == `${a.unitTypeCode}${b.code}` && <InputNumber precision={b.type == 1 ? 4 : 2} min={0} max={b.type == 1?1:10000000000} style={{ width: 160 }} autoFocus defaultValue={formatValue(b.value)} onChange={onChangeHandle} />}
+                                                <div className='actIcon'><IconFont style={{ color: '#17181A', cursor: 'pointer' }} onClick={() => editHandle(a, b)} type={currentEdit?.flag != `${a.unitTypeCode}${b.code}` ? 'icon-xiugai' : 'icon-queren'} /></div>
+                                            </div>
+                                        </div>
+                                    ))
+                                }
+                            </div>
+                        </div>
+                    ))
+                }
+            </div>
+        </div>
+    )
+
+
+}

+ 135 - 0
src/pages/budgetMana/personnelSalaryBudget/components/cardList/style.less

@@ -0,0 +1,135 @@
+.CardList {
+    background: #FFFFFF;
+    border-radius: 4px;
+    padding: 16px;
+
+    .header {
+        display: flex;
+        flex-direction: row;
+        justify-content: space-between;
+        align-items: center;
+        margin-bottom: 24px;
+
+        .title {
+            height: 16px;
+            line-height: 16px;
+            font-weight: 500;
+            font-size: 16px;
+            color: #17181A;
+        }
+
+        .copyBtn {
+            width: 100px;
+            height: 24px;
+            text-align: center;
+            line-height: 24px;
+            background: #FFFFFF;
+            border-radius: 4px;
+            font-weight: 400;
+            font-size: 14px;
+            color: #3376FE;
+            transition: all 0.1s ease-in;
+
+            &:hover {
+                 background: #F2F6FF;
+            }
+        }
+
+    }
+
+    .content {
+        max-height: calc(100vh - 217px);
+        overflow-y: scroll;
+        .group {
+            margin-bottom: 24px;
+            .groupName {
+                height: 15px;
+                line-height: 15px;
+                font-weight: 500;
+                font-size: 14px;
+                color: #17181A;
+                margin-bottom: 8px;
+
+                &::before {
+                    position: relative;
+                    display: inline-block;
+                    content: '';
+                    width: 8px;
+                    height: 8px;
+                    background: #CFD6E6;
+                    border-radius: 2px;
+                    margin-right: 4px;
+                }
+            }
+
+            .listWrap {
+                display: flex;
+                flex-direction: row;
+                justify-content: flex-start;
+                align-items: center;
+                .list {
+                    width: 19.2%;
+                    height: 74px;
+                    background: #FFFFFF;
+                    border-radius: 8px;
+                    padding: 12px;
+                    margin-right: 1%;
+                    border: 1px solid #E6EAF2;
+
+                    .name {
+                        height: 14px;
+                        line-height: 14px;
+                        font-weight: 400;
+                        font-size: 14px;
+                        color: #525866;
+                        margin-bottom: 12px;
+
+                        &>img {
+                            position: relative;
+                            top:-2px;
+                            width: 14px;
+                            height: 14px;
+                            margin-right: 4px;
+                        }
+                    }
+
+                    .detail {
+                        display: flex;
+                        flex-direction: row;
+                        justify-content: space-between;
+                        align-items: center;
+
+                        .num {
+                            height: 24px;
+                            line-height: 24px;
+                            font-weight: 500;
+                            font-size: 24px;
+                            color: #17181A;
+                        }
+                        .actIcon {
+                            display: flex;
+                            justify-content: center;
+                            align-items: center;
+                            width: 24px;
+                            height: 24px;
+                            border-radius: 4px;
+                            transition: all 0.1s ease-in;
+                            &:hover {
+                                background: #F5F7FA;
+                            }
+                        }
+
+                    }
+
+                    &:nth-child(5n){
+                        margin-right: 0;
+                    }
+                }
+            }
+
+            &:last-child {
+                margin-bottom: 0;
+            }
+        }
+    }
+}

+ 422 - 0
src/pages/budgetMana/personnelSalaryBudget/components/distribute/index.tsx

@@ -0,0 +1,422 @@
+import React, { useEffect, useRef, useState } from 'react';
+import { BMSTable } from '@/components/BMSTable';
+import { createFromIconfontCN } from '@ant-design/icons';
+import { ProColumns } from '@ant-design/pro-components';
+import { Tabs, InputNumber, message } from 'antd';
+import { debounce } from 'lodash';
+import { Key } from 'react';
+import { getUnitRetainListReq, getUnitTypes, saveUnitRetainReq } from '../../service';
+
+import './style.less';
+import TableSelecter from './tableSelector';
+
+const IconFont = createFromIconfontCN({
+    scriptUrl: '',
+});
+
+export const Distribute = ({ computeDate }: { computeDate: string }) => {
+    const [tabs, set_tabs] = useState<any[]>([]);
+    const [editable, set_editable] = useState(false);
+    const [dataSource, set_dataSource] = useState<any[]>([]);
+    const [currentTab, set_currentTab] = useState<undefined | string>(undefined);
+    const [tableColumns, set_tableColumns] = useState<ProColumns[]>([]);
+    const [titles, set_titles] = useState<any[]>([]);
+    const [tableSelecterVisible, set_tableSelecterVisible] = useState(false);
+    const [currentBlock, set_currentBlock] = useState<any>(undefined);
+    const [inputNum, set_inputNum] = useState(0);
+    const [currentEdit, set_currentEdit] = useState({ colCode: '', num: 0, rowCode: '' });
+    const [slectedRows, set_slectedRows] = useState<any[]>([]);
+    const [editMode, set_editMode] = useState(false);
+
+    const wrapContainerRef = useRef<HTMLDivElement>(null);
+    const tableBodyRef = useRef<HTMLDivElement>(null);
+
+    const getTab = async () => {
+        const resp = await getUnitTypes();
+        if (resp) {
+            const arr = resp.list.map((a: any, index: number) => ({
+                label: a.name,
+                key: a.code,
+            }));
+            set_tabs([...arr]);
+            if (arr.length > 0) set_currentTab(arr[0].key);
+        }
+    };
+
+    const onTabChange = (tabKey: string) => {
+        set_currentTab(tabKey);
+    };
+
+    const getTableData = async (unitType: string) => {
+        if (!unitType) return;
+        const resp = await getUnitRetainListReq(computeDate, unitType);
+        if (resp) {
+            const { title = [], remainData = [], data = [] } = resp;
+
+            set_titles(
+                title.map((a: any) => {
+                    const value = remainData.filter((b: any) => a.code == b.code)[0].value;
+                    return {
+                        ...a,
+                        value,
+                    };
+                })
+            );
+
+            const arr = title.map((a: any) => ({
+                title: a.name,
+                dataIndex: `${a.code}`,
+                key: `${a.code}`,
+                width: 230,
+                renderText(num: number, record: any) {
+                    const InputComponent = () => {
+                        const [inputNum, set_inputNum] = useState(0);
+                        const handleChange = (num: number | null) => {
+                            set_inputNum(num ? num : 0);
+                        };
+                        const onBlurhandle = () => {
+                            set_currentEdit({ colCode: a.code, num: inputNum, rowCode: record.unitCode });
+                        };
+
+                        return (
+                            <InputNumber
+                                style={{ width: '100%' }}
+                                disabled={!editMode || a.code == 'totalBonus'}
+                                precision={2}
+                                min={0}
+                                defaultValue={num}
+                                onBlur={onBlurhandle}
+                                onChange={handleChange}
+                            />
+                        );
+                    };
+
+                    return <InputComponent />;
+                },
+            }));
+
+            set_tableColumns([
+                {
+                    title: '核算单元名称',
+                    dataIndex: 'unitName',
+                    key: 'unitName',
+                    width: 182,
+                    ellipsis: true,
+                    fixed: 'left',
+                },
+                ...arr,
+            ]);
+
+            const tableData = data.map((a: any) => {
+                let obj: any = {};
+                let totalCount = 0;
+                title.forEach((b: any) => {
+                    const { data: rowData } = a;
+                    const needItem = rowData.filter((c: any) => c.code == b.code);
+                    obj[`${b.code}`] = needItem.length > 0 ? needItem[0].value : 0;
+                    if (b.code != 'totalBonus') {
+                        totalCount = totalCount + (needItem.length > 0 ? needItem[0].value : 0);
+                    }
+                });
+                return {
+                    ...a,
+                    ...obj,
+                    totalBonus: totalCount,
+                };
+            });
+            set_dataSource([...tableData]);
+        }
+    };
+
+    const tableSelecterCommit = (keys: Key[], rows: any[]) => {
+        set_slectedRows([...slectedRows, ...rows]);
+
+        if (currentBlock.code == 'totalBonus') {
+            const newTitles = titles.map((a) => {
+                if (a.code == currentBlock.code) {
+                    return {
+                        ...a,
+                        value: rows.reduce((prev: number, cur: any) => prev + cur.retainBonus, 0),
+                    };
+                } else {
+                    const needArr = rows.filter((b) => b.retainCode == a.code);
+
+                    return {
+                        ...a,
+                        value: needArr.length > 0 ? needArr.reduce((prev: number, cur: any) => prev + cur.retainBonus, 0) : 0,
+                    };
+                }
+            });
+            set_titles([...newTitles]);
+        } else {
+            const amount = rows.reduce((prev: number, cur: any) => prev + cur.retainBonus, 0);
+            const newTitles = titles.map((a) => {
+                if (a.code == currentBlock.code) {
+                    return {
+                        ...a,
+                        value: a.value + amount,
+                    };
+                } else {
+                    return a;
+                }
+            });
+            const titlesvalueCount = newTitles.reduce((prev: number, cur: any) => {
+                if (cur.code != 'totalBonus') {
+                    return prev + cur.value;
+                } else {
+                    return prev;
+                }
+            }, 0);
+
+            set_titles(
+                newTitles.map((a: any) => {
+                    if (a.code == 'totalBonus') {
+                        return { ...a, value: titlesvalueCount };
+                    } else {
+                        return a;
+                    }
+                })
+            );
+        }
+
+        set_tableSelecterVisible(false);
+    };
+
+    const saveHandle = async () => {
+        const reaultData = dataSource.map((a) => {
+            return {
+                unitCode: a.unitCode,
+                unitName: a.unitName,
+                data: titles.map((b) => ({ code: b.code, value: a[`${b.code}`] })),
+            };
+        });
+
+        const arr = titles.map((a) => {
+            const ids = slectedRows.filter((b) => b.retainCode == a.code).map((c) => c.id);
+
+            return {
+                retainCode: a.code,
+                retainIds: [...new Set(ids)],
+            };
+        });
+
+        const result = {
+            data: reaultData,
+            remainData: titles.map((a) => ({ code: a.code, value: a.value })),
+            historyRetain: arr,
+        };
+
+        const resp = await saveUnitRetainReq(result);
+        if (resp) {
+            message.success('保存成功!');
+
+            set_editMode(false);
+            set_slectedRows([]);
+        }
+    };
+
+    useEffect(() => {
+        getTableData(currentTab as string);
+    }, [editMode]);
+
+    useEffect(() => {
+        // 更新 titles
+        const newTitles = titles.map((b) => {
+            if (b.code === currentEdit.colCode) {
+                return { ...b, value: b.value - currentEdit.num };
+            } else {
+                return b;
+            }
+        });
+        const titlesvalueCount = newTitles.reduce((prev: number, cur: any) => {
+            if (cur.code != 'totalBonus') {
+                return prev + cur.value;
+            } else {
+                return prev;
+            }
+        }, 0);
+        set_titles(
+            newTitles.map((a: any) => {
+                if (a.code == 'totalBonus') {
+                    return { ...a, value: titlesvalueCount };
+                } else {
+                    return a;
+                }
+            })
+        );
+
+        // 基于更新后的 titles 同时更新 dataSource
+        const updatedDataSource = dataSource.map((item) => {
+            if (item.unitCode == currentEdit.rowCode) {
+                let total = 0;
+                titles.forEach((a) => {
+                    if (a.code != 'totalBonus') {
+                        if (a.code == currentEdit.colCode) {
+                            total = total + currentEdit.num;
+                        } else {
+                            total = total + item[`${a.code}`];
+                        }
+                    }
+                });
+                return { ...item, [`${currentEdit.colCode}`]: currentEdit.num, totalBonus: total };
+            } else {
+                return item;
+            }
+        });
+        set_dataSource(updatedDataSource);
+    }, [currentEdit]);
+
+    useEffect(() => {
+        if (currentTab) {
+            getTableData(currentTab);
+        }
+    }, [currentTab]);
+
+    useEffect(() => {
+        getTab();
+    }, []);
+
+    useEffect(() => {
+        if (titles.length > 0) {
+
+            const handleScroll = () => {
+                if (wrapContainerRef.current && tableBody) {
+                    wrapContainerRef.current.scrollLeft = tableBody.scrollLeft;
+                }
+            };
+
+            const tableBody = document.querySelector('.bms-ant-table-body') as HTMLElement;
+
+
+
+            // 获取 bms-ant-table-cell 元素
+            const td = document.querySelector('.bms-ant-table-cell') as HTMLElement;
+            const fixedTd = document.querySelector('.bms-ant-table-cell-fix-left') as HTMLElement;
+
+            if (td) {
+                // 获取元素的样式
+                const style = getComputedStyle(td);
+
+                // 获取元素的宽度,包含 padding 和 border
+                const paddingLeft = parseFloat(style.paddingLeft);
+                const paddingRight = parseFloat(style.paddingRight);
+                const borderLeftWidth = parseFloat(style.borderLeftWidth);
+                const borderRightWidth = parseFloat(style.borderRightWidth);
+
+                const tdWidth = td.clientWidth + paddingLeft + paddingRight + borderLeftWidth + borderRightWidth;
+                // console.log('bms-ant-table-cell 的实际宽度(包含 padding 和 border):', tdWidth);
+            } else {
+                // console.log('没有找到 .bms-ant-table-cell 元素');
+            }
+
+            if (fixedTd) {
+                // 获取元素的样式
+                const style = getComputedStyle(fixedTd);
+
+                // 获取元素的宽度,包含 padding 和 border
+                const paddingLeft = parseFloat(style.paddingLeft);
+                const paddingRight = parseFloat(style.paddingRight);
+                const borderLeftWidth = parseFloat(style.borderLeftWidth);
+                const borderRightWidth = parseFloat(style.borderRightWidth);
+
+                const fixedTdWidth = fixedTd.clientWidth + paddingLeft + paddingRight + borderLeftWidth + borderRightWidth;
+                // console.log('bms-ant-table-cell-fix-left 的实际宽度(包含 padding 和 border):', fixedTdWidth);
+            } else {
+                // console.log('没有找到 .bms-ant-table-cell-fix-left 元素');
+            }
+
+
+
+
+
+            if (tableBody) {
+                tableBody.addEventListener('scroll', handleScroll);
+            }
+
+            // 清理函数,用于移除事件监听器
+            return () => {
+                if (tableBody) {
+                    tableBody.removeEventListener('scroll', handleScroll);
+                }
+            };
+        }
+
+    }, [titles]);
+
+    return (
+        <div className="Distribute">
+            <TableSelecter
+                onVisibleChange={(bool) => set_tableSelecterVisible(bool)}
+                title="添加保留考核奖金"
+                rowKey={'id'}
+                defaultSelectedKeys={slectedRows.map((a: any) => a.id)}
+                record={{ ...currentBlock, currentTab }}
+                open={tableSelecterVisible}
+                onFinish={tableSelecterCommit}
+            />
+
+            <div className="Distribute_header">
+                <span className="title">保留奖金分配</span>
+                {!editMode && (
+                    <span className="editBtn" onClick={() => set_editMode(true)}>
+                        <IconFont style={{ color: '#3376FE', cursor: 'pointer', marginRight: 4, fontSize: 16 }} type={'iconbianji'} />
+                        开始分配
+                    </span>
+                )}
+                {editMode && (
+                    <div className="saveBtn">
+                        <span className="cancel" onClick={() => set_editMode(false)}>
+                            取消
+                        </span>
+                        <span className="save" onClick={() => saveHandle()}>
+                            保存
+                        </span>
+                    </div>
+                )}
+            </div>
+            {tabs.length > 0 && <Tabs defaultActiveKey={currentTab} onChange={onTabChange} items={tabs} />}
+            <div className="rowOne">
+                <div className="blockHeader">
+                    <img src={require('../../../../../../static/distribute.png')} alt="" />
+                    <div className="detail">
+                        <span className="name">可分配额</span>
+                        <span className="sub">对总额进行分配</span>
+                    </div>
+                </div>
+                <div className="wrap" ref={wrapContainerRef}>
+                    {titles.map((a, index) => (
+                        <div className="block" key={index}>
+                            <div className="left">
+                                <span className="label">{a.name}(元)</span>
+                                <span className="value">{a.value}</span>
+                            </div>
+                            <div className="icon">
+                                <IconFont
+                                    style={{ color: '#17181A', cursor: 'pointer' }}
+                                    type={'icon-zhuijia'}
+                                    onClick={() => {
+                                        set_currentBlock(a);
+                                        set_tableSelecterVisible(true);
+                                    }}
+                                />
+                            </div>
+                        </div>
+                    ))}
+                </div>
+            </div>
+            {titles.length > 0 && (
+                <div className="table-body">
+                    <BMSTable
+                        className="Distribute_table"
+                        dataSource={dataSource}
+                        scroll={{ x: titles.length * 220 + 192, y: editMode ? 440 : 470 }}
+                        pagination={false}
+                        rowKey="unitCode"
+
+                        columns={[...tableColumns]}
+                    />
+                </div>
+            )}
+        </div>
+    );
+};

+ 231 - 0
src/pages/budgetMana/personnelSalaryBudget/components/distribute/style.less

@@ -0,0 +1,231 @@
+.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;
+            }
+        }
+
+    }
+}
+
+.Distribute {
+    padding: 16px;
+    background: #FFFFFF;
+    border-radius: 4px;
+
+    // .BMSTable .bms-ant-table .bms-ant-table-tbody > tr.bms-ant-table-row .bms-ant-table-cell {
+    //     width: 220px !important;
+    //      padding: 8px 0 !important;
+    // }
+
+    .Distribute_header {
+        display: flex;
+        flex-direction: row;
+        justify-content: space-between;
+        align-items: center;
+
+        .title {
+            font-weight: 500;
+            height: 16px;
+            line-height: 16px;
+            font-size: 16px;
+            color: #17181A;
+        }
+
+        .editBtn {
+            display: inline-block;
+            position: relative;
+            width: 100px;
+            height: 24px;
+            text-align: center;
+            line-height: 24px;
+            top: 30px;
+            z-index: 999;
+            cursor: pointer;
+            font-weight: 400;
+            font-size: 14px;
+            border-radius: 4px;
+            color: #3376FE;
+            transition: all 0.1s ease-in;
+
+            &:hover {
+                background: #F2F6FF;
+            }
+        }
+        .saveBtn {
+            display: inline-block;
+            position: relative;
+            top: 30px;
+            z-index: 999;
+            &>span {
+                cursor: pointer;
+                display: inline-block;
+                width: 56px;
+                height: 24px;
+                background: #3377FF;
+                border-radius: 4px;
+                font-weight: 400;
+                font-size: 14px;
+                color: #FFFFFF;
+                text-align: center;
+                line-height: 24px;
+    
+                &.cancel {
+                    color: #17181A;
+                    background: #FAFCFF;
+                    border-radius: 4px;
+                    margin-right: 8px;
+                    border: 1px solid #DAE2F2;
+                }
+            }
+        }
+    }
+
+    .rowOne {
+        display: flex;
+
+        height: 72px;
+        flex-direction: row;
+        justify-content: flex-start;
+        align-items: center;
+        background: linear-gradient(180deg, #D9E9FF 0%, #F2F8FF 100%);
+        border-radius: 4px;
+        margin-bottom: 16px;
+
+        .blockHeader {
+            display: flex;
+            width: 184px;
+            flex-direction: row;
+            justify-content: flex-start;
+            align-items: center;
+
+            &>img {
+                width: 40px;
+                height: 40px;
+                margin-top:10px;
+                margin-left: 4px;
+            }
+
+            .detail {
+                display: flex;
+                margin-left: 10px;
+                flex-direction: column;
+                justify-content: flex-start;
+
+                .name {
+                    font-weight: 500;
+                    height: 16px;
+                    font-size: 16px;
+                    color: #17181A;
+                    line-height: 16px;
+                    margin-bottom: 4px;
+                }
+
+                .sub {
+                    font-weight: 400;
+                    height: 12px;
+                    font-size: 12px;
+                    color: #7A8599;
+                    line-height: 12px;
+                }
+            }
+        }
+
+        .wrap {
+            display: flex;
+            flex-direction: row;
+            justify-content: flex-start;
+            align-items: center;
+            width: calc(100% - 192px);
+            overflow-x: hidden;
+            overflow-y: hidden;
+            padding-left: 8px;
+
+            &::-webkit-scrollbar {
+                width: 2px !important;
+                height: 2px !important;
+                /**/
+            }
+
+            .block {
+                display: flex;
+                flex-shrink: 0;
+                flex-direction: row;
+                justify-content: space-between;
+                align-items: center;
+                width: 212px;
+                height: 56px;
+                padding: 0 8px;
+                background: #FFFFFF;
+                border-radius: 4px;
+                margin-right: 17px;
+
+                .left {
+                    display: flex;
+                    flex-direction: column;
+                    align-items: flex-start;
+
+                    .label {
+                        height: 12px;
+                        font-weight: 400;
+                        font-size: 12px;
+                        color: #515866;
+                        line-height: 12px;
+                        margin-bottom: 8px;
+                    }
+
+                    .value {
+                        font-weight: 500;
+                        height: 14px;
+                        font-size: 14px;
+                        color: #17181A;
+                        line-height: 14px;
+                    }
+                }
+
+                .icon {
+                    text-align: center;
+                    width: 24px;
+                    height: 24px;
+                    line-height: 24px;
+                    background: #F5F7FA;
+                    border-radius: 4px;
+
+                    &>img {
+                        width: 14px;
+                        height: 16px;
+                    }
+                }
+
+
+            }
+        }
+
+    }
+}

+ 197 - 0
src/pages/budgetMana/personnelSalaryBudget/components/distribute/tableSelector.tsx

@@ -0,0 +1,197 @@
+import React, { Key, useEffect, useState } from "react";
+import { Input, Select } from 'antd'
+
+import { createFromIconfontCN } from "@ant-design/icons";
+
+
+import { ModalForm, ProColumns, ProFormSelect, ProFormText } from "@ant-design/pro-components";
+
+import { set } from "lodash";
+import { BMSTable } from "@/components/BMSTable";
+import { getKpiRemainRetainList } from "../../service";
+
+
+// import './style.less';
+
+
+interface TableSelecterProps {
+    record: any
+}
+
+const IconFont = createFromIconfontCN({
+    scriptUrl: '',
+});
+
+
+
+
+const TableSelecter = ({ record, open, title, onVisibleChange, rowKey = 'id', request, onFinish, defaultSelectedKeys }: {
+    record: any, open: boolean, title: string, onVisibleChange: (bool: boolean) => void, defaultSelectedKeys: Key[],
+    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 [filter, set_filter] = useState<undefined | any>(undefined);
+        const [code, set_code] = useState<undefined | any>(undefined);
+        const [keyword, set_keyword] = useState<string | undefined>(undefined);
+
+
+        const columns: ProColumns[] = [
+            {
+                title: '保留月份',
+                dataIndex: 'computeDate',
+            },
+            {
+                title: '保留考核项目',
+                dataIndex: 'retainName',
+            },
+            {
+                title: '金额',
+                dataIndex: 'retainBonus',
+            },
+        ];
+
+
+        const onSelectChange = (newSelectedRowKeys: React.Key[], selectedRows: any) => {
+            setSelectedKeys([...newSelectedRowKeys]);
+            set_selectedRows([...selectedRows]);
+
+        };
+
+        const saveHandle = async () => {
+            const selectedRowkeys = selectedRows.map((a) => a[`${rowKey}`]);
+            const rows = datasource.filter((a) => selectedRowkeys.includes(a[`${rowKey}`]));
+            onFinish && onFinish(selectedKeys, rows);
+        }
+
+        const getTableData = async () => {
+            const resp = await getKpiRemainRetainList(record.currentTab,record.code);
+            
+
+            if (resp) {
+                let _defaultSelectedKeys:any[] = [];
+                let _defaultSelectedRow:any[] = [];
+
+                let tableData: any[] = [];
+
+                if(record.code == 'totalBonus'){
+                    resp.forEach((item:any) => {
+                        tableData =  tableData.concat(item.retains);
+                    });
+                }else{
+                    tableData = resp[0].retains;
+                }
+               
+                const data = tableData.map((a: any) => {
+                    if (defaultSelectedKeys.includes(a[`${rowKey}`])) {
+                       _defaultSelectedKeys.push(a[`${rowKey}`]);
+                       _defaultSelectedRow.push(a);
+                       return a
+                    } else {
+                        return a
+                    }
+                });
+
+                console.log({resp,defaultSelectedKeys,data})
+                setSelectedKeys(_defaultSelectedKeys);
+                set_selectedRows(_defaultSelectedRow);
+                set_showList(data);
+                set_datasource([...data]);
+            
+            }
+
+
+            return Promise.resolve([]);
+        }
+
+        useEffect(() => {
+            // console.log({code, keyword});
+            const result = datasource.filter((a) => {
+                 return a.retainName?a.retainName.indexOf(keyword) != -1:false
+            });
+
+            set_showList([...result]);
+        }, [keyword])
+
+        useEffect(() => {
+            getTableData()
+        }, [])
+
+
+        return (
+            <div >
+                <div className="filter" style={{ display: 'flex', flexDirection: 'row', marginBottom: 8 }}>
+                    <ProFormText noStyle placeholder={'请输入'}
+                        fieldProps={{
+                            suffix: <IconFont style={{ color: '#99A6BF' }} type="iconsousuo" />,
+                            onChange: (e) => {
+                                if (e.target.value.length != 0) {
+                                    set_keyword(e.target.value);
+                                } else {
+                                    set_keyword('');
+                                }
+                            }
+                        }}
+                    />
+                </div>
+
+                <BMSTable columns={columns}
+                    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: false,simple: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={400} submitter={{
+            render: false
+        }} open={open} modalProps={{
+            closable: false,
+        }}>
+            <Table
+                // ref={tableSelecterRef}
+                record={undefined}
+            ></Table>
+        </ModalForm>
+    )
+
+}
+
+
+
+
+export default TableSelecter

+ 249 - 107
src/pages/budgetMana/personnelSalaryBudget/index.tsx

@@ -2,7 +2,7 @@
  * @Author: code4eat awesomedema@gmail.com
  * @Date: 2023-01-03 14:20:22
  * @LastEditors: code4eat awesomedema@gmail.com
- * @LastEditTime: 2023-09-26 10:06:56
+ * @LastEditTime: 2024-07-22 11:39:34
  * @FilePath: /BudgetManaSystem/src/pages/budgetMana/personnelSalaryBudget/index.tsx
  * @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
  */
@@ -13,9 +13,9 @@ import { BMSTable } from '@/components/BMSTable';
 import { getComputeDate } from '@/pages/Home/service';
 import { createFromIconfontCN } from '@ant-design/icons';
 import { ActionType, ProColumns } from '@ant-design/pro-components';
-import { Input, InputNumber, message, Modal } from 'antd';
+import { Input, InputNumber, message, Modal, Steps } from 'antd';
 import React, { useEffect, useRef, useState } from 'react'
-import { caculate, checkRequest, editAssessmentBonus, generate, getCurrentCheckStatus, getData } from './service';
+import { auditRequest, caculate, checkRequest, checkRetainStatusReq, editAssessmentBonus, generate, getAllParameter, getCurrentCheckStatus, getData } from './service';
 
 import editIcon from '../../../../static/editIcon.png';
 import editIcon_gray from '../../../../static/editIcon_gray.png';
@@ -26,11 +26,31 @@ import '../../../utils/zhongtaiB'
 import { formatMoneyNumber } from '@/utils/format';
 import { getJiezhuanStatus } from '../monthlySet/service';
 import { getParams } from '../monthlyInfoCheck/service';
+import { CardList } from './components/cardList';
+import { Distribute } from './components/distribute';
 
 const IconFont = createFromIconfontCN({
     scriptUrl: '',
 });
 
+
+const steps = [
+    {
+        title: '保留系数设定',
+        content: 'First-content',
+    },
+    {
+        title: '人事薪酬预算',
+        content: 'Second-content',
+    },
+    {
+        title: '保留奖金分配',
+        content: 'Last-content',
+    },
+];
+
+const items = steps.map((item) => ({ key: item.title, title: item.title }));
+
 const PersonnelSalaryBudget = () => {
 
 
@@ -50,18 +70,20 @@ const PersonnelSalaryBudget = () => {
 
     const [ifBanAllAction, set_ifBanAllAction] = useState(true);  //是否掩藏所有操作
 
-    const [ifEditTable,set_ifEditTable] = useState(true);
+    const [ifEditTable, set_ifEditTable] = useState(true);
 
-    const [ifEditIncome,set_ifEditIncome] = useState(false);
-    const [ifEditCost,set_ifEditCost] = useState(false);
-    const [ifEditRate,set_ifEditRate] = useState(false);
+    const [ifEditIncome, set_ifEditIncome] = useState(false);
+    const [ifEditCost, set_ifEditCost] = useState(false);
+    const [ifEditRate, set_ifEditRate] = useState(false);
+    const [currentStep, set_currentStep] = useState(2);
+    const [pageType, set_pageType] = useState(1); //1旧版 2新版
 
     const tableColumn: ProColumns[] = [
         {
             title: '职类',
             dataIndex: 'unitTypeName',
             key: 'unitTypeName',
-            fixed:'left'
+            fixed: 'left'
 
         },
         {
@@ -74,10 +96,10 @@ const PersonnelSalaryBudget = () => {
             title: '合理人均薪酬系数',
             dataIndex: 'averageSalary',
             key: 'averageSalary',
-            ellipsis:true
+            ellipsis: true
         },
         {
-            title: ifEditTable?'绩效奖金占比':'人事成本占比',
+            title: ifEditTable ? '绩效奖金占比' : '人事成本占比',
             dataIndex: 'percent',
             key: 'percent',
         },
@@ -87,7 +109,7 @@ const PersonnelSalaryBudget = () => {
             key: 'effectiveRate',
         },
         {
-            title:ifEditTable?'绩效奖金':'人事成本',
+            title: ifEditTable ? '绩效奖金' : '人事成本',
             dataIndex: 'cost',
             key: 'cost',
             renderText(num, record, index, action) {
@@ -97,7 +119,7 @@ const PersonnelSalaryBudget = () => {
         {
             title: '固定工资',
             dataIndex: 'salary',
-            hideInTable:ifEditTable,
+            hideInTable: ifEditTable,
             key: 'salary',
             renderText(num, record, index, action) {
                 return formatMoneyNumber(num)
@@ -111,7 +133,7 @@ const PersonnelSalaryBudget = () => {
                 return formatMoneyNumber(num)
             },
         },
-       
+
         {
             title: '专项补助',
             dataIndex: 'subsidies',
@@ -143,8 +165,17 @@ const PersonnelSalaryBudget = () => {
             renderText(num, record, index, action) {
                 return formatMoneyNumber(num)
             },
+        },
+        {
+            title: '保留金额',
+            dataIndex: 'retainBonus',
+            key: 'retainBonus',
+            hideInTable: pageType == 1,
+            renderText(num, record, index, action) {
+                return formatMoneyNumber(num)
+            },
         }
-    ]
+    ];
 
     const getPageData = async (currentComputeDate: string, params: any, sort: any, filter: any) => {
         const resp = await getData(currentComputeDate);
@@ -166,10 +197,22 @@ const PersonnelSalaryBudget = () => {
 
     const checkHandle = async (type: string) => {
 
-        const resp = await checkRequest({
-            computeDate: currentComputeDate as string,
-            auditType: type == '0' ? '1' : '0',   //审核类型  1审核 0取消审核
-        });
+        let resp = undefined;
+
+        if(pageType == 1){
+            resp = await auditRequest({
+                computeDate: currentComputeDate as string,
+                auditType: type == '0' ? '1' : '0',   //审核类型  1审核 0取消审核
+            });
+        }else{
+            resp = await checkRequest({
+                computeDate: currentComputeDate as string,
+                auditType: type == '0' ? '1' : '0',   //审核类型  1审核 0取消审核
+                skipCheck: pageType == 1 ? '1' : '0'
+            });
+        }
+
+    
 
         if (resp) {
             if (type == '0') {
@@ -267,41 +310,90 @@ const PersonnelSalaryBudget = () => {
 
     }
 
-    const getJiezhuanStatusHandle =async () => {
+    const getJiezhuanStatusHandle = async () => {
         const resp = await getJiezhuanStatus(currentComputeDate as string);
-        if(resp == 2){
-              set_ifBanAllAction(true);
-        }else{
-              set_ifBanAllAction(false);
+        if (resp == 2) {
+            set_ifBanAllAction(true);
+        } else {
+            set_ifBanAllAction(false);
         }
     }
 
-   
+
 
     const setPageType = async () => {
         const resp = await getParams();
-        if(resp){
-            const needItems = resp.list.filter((a:any)=>a.code =='1686657454764597248' );
-            if(needItems.length>0){
-                  if(needItems[0].value == '2'){
-                       set_ifEditTable(false);
-                  }else{
+        if (resp) {
+            const needItems = resp.list.filter((a: any) => a.code == '1686657454764597248');
+            if (needItems.length > 0) {
+                if (needItems[0].value == '2') {
+                    set_ifEditTable(false);
+                } else {
                     set_ifEditTable(true);
-                  }
+                }
             }
         }
     }
 
-    useEffect(()=>{
-        if(!ifEditIncome&&!ifEditCost&&!ifEditRate){
-            if(ifEditTable&&pageData.income>0){
-                const result = (pageData.income - pageData.cost)*pageData.rate;
-                set_pageData({...pageData,personalCost:result})
-           }
+    const nextHandle = async () => {
+        if (currentStep > 2) return;
+        if (currentStep == 1) {
+            const canProceed = await checkRetainStatusHandle();
+            if (canProceed) {
+                set_currentStep(currentStep + 1);
+            }
+        } else {
+            set_currentStep(currentStep + 1);
         }
-    },[ifEditIncome,ifEditCost,ifEditRate]);
+    }
+
+    const checkRetainStatusHandle = async () => {
+        const resp = await checkRetainStatusReq(currentComputeDate as string);
+        let flag = false;
+        if (resp) {
+            // 使Modal操作成为决定flag的一部分
+            await new Promise((resolve, reject) => {
+                Modal.confirm({
+                    title: '注意',
+                    content: '保留系数设定的数据有调整,需重新进行计算操作',
+                    okText: '确定',
+                    cancelText: '取消',
+                });
+            });
+        } else {
+            flag = true;
+        }
+
+        return flag;
+    }
+
+
+    const prevHandle = () => {
+        set_currentStep(currentStep - 1)
+    }
+
+    const getAllParameterHandle = async () => {
+        const resp = await getAllParameter();
+        if (resp) {
+            const needItem = resp.list.filter((a: any) => a.code == '1788097421696442368');
+            if (needItem.length > 0) {
+                set_pageType(needItem[0].value == '1' ? 2 : 1)
+            }
+        }
+    }
+
+
+
+    useEffect(() => {
+        if (!ifEditIncome && !ifEditCost && !ifEditRate) {
+            if (ifEditTable && pageData.income > 0) {
+                const result = (pageData.income - pageData.cost) * pageData.rate;
+                set_pageData({ ...pageData, personalCost: result })
+            }
+        }
+    }, [ifEditIncome, ifEditCost, ifEditRate]);
+
 
-    
     useEffect(() => {
         currentComputeDate && getCheckStatus(currentComputeDate);
         currentComputeDate && getJiezhuanStatusHandle();
@@ -313,85 +405,135 @@ const PersonnelSalaryBudget = () => {
 
         getCurrentComputeDate();
         setPageType();
+        getAllParameterHandle();
 
     }, []);
 
 
     return (
-        <BMSPagecontainer className='PersonnelSalaryBudget' title={`核算年月:${currentComputeDate}`} ghost>
-            {!ifBanAllAction&&<div className='checkBtn' onClick={() => checkHandle(`${auditType}`)}>{auditType == '0' ? '审核' : '取消审核'}</div>}
-            <div className='paramsWrap'>
-
-                <div className='cardWrap'>
-                    <div className='card'>
-                        <span>{ifEditTable?'总收入':'收入'}</span>
-                        <div className='count'>
-                            <span onClick={()=>auditType == '0'?set_ifEditIncome(true):()=>{}}>{!ifEditIncome&&pageData.income}</span>
-                            {(ifEditIncome&&ifEditTable)&&<InputNumber precision={2} onChange={(num)=>{set_pageData({...pageData,income:num?num:0})}} style={{width:160}} autoFocus defaultValue={pageData.income} onBlur={()=>{
-                                set_ifEditIncome(false);
-                            }} />}
-                            {(!ifEditIncome&&ifEditTable&&auditType == '0')&&<div className='editBtn' onClick={()=>auditType == '0'?set_ifEditIncome(true):()=>{}}><img src={editIcon_gray} alt="" /></div>}
-                        </div>
-                    </div>
-                    <div className='card'>
-                        <span>{ifEditTable?'材料及药品收入':'成本'}</span>
-                        <div className='count'>
-                            <span onClick={()=>auditType == '0'?set_ifEditCost(true):()=>{}}>{!ifEditCost&&pageData.cost}</span>
-                            {(ifEditCost&&ifEditTable)&&<InputNumber precision={2} onChange={(num)=>{set_pageData({...pageData,cost:num?num:0})}} style={{width:160}} autoFocus defaultValue={pageData.cost} onBlur={()=>{
-                                set_ifEditCost(false);
-                            }} />}
-                            {(!ifEditCost&&ifEditTable&&auditType == '0')&&<div className='editBtn' onClick={()=>auditType == '0'?set_ifEditCost(true):()=>{}}><img src={editIcon_gray} alt="" /></div>}
-                        </div>
-                    </div>
-                    <div className='card'>
-                        <span>{ifEditTable?'绩效占有效收入比':'人事成本占比'}</span>
-                        <div className='count'>
-                            <span onClick={()=>auditType == '0'?set_ifEditRate(true):()=>{}}>{!ifEditRate&&pageData.rate}</span>
-                            {(ifEditRate&&ifEditTable)&&<InputNumber precision={4} onChange={(num)=>{set_pageData({...pageData,rate:num?num:0})}}  style={{width:160}} autoFocus defaultValue={pageData.rate} onBlur={()=>{
-                                set_ifEditRate(false);
-                            }} />}
-                            {(!ifEditRate&&ifEditTable&&auditType == '0')&&<div className='editBtn' onClick={()=>auditType == '0'?set_ifEditRate(true):()=>{}}><img src={editIcon_gray} alt="" /></div>}
-                        </div>
+        <BMSPagecontainer className='PersonnelSalaryBudget' title={false} ghost>
+            <div className='Pageheader'>
+                <div className='lineOne' style={{ marginBottom: pageType == 1 ? 0 : 24 }}>
+                    <div className='title'>{`核算年月:${currentComputeDate}`}</div>
+                    <div className='btns'>
+                        {(pageType == 2 && currentStep > 0) && <div className={'checkBtn prev'} style={{ right: currentStep == 2 ? 80 : 95 }} onClick={() => prevHandle()}>{'上一步'}</div>}
+                        {(pageType == 2 && currentStep < 2) && <div className={'checkBtn'} onClick={() => nextHandle()}>{'下一步'}</div>}
+                        {(!ifBanAllAction && (currentStep == 2) || pageType == 1) && <div className='checkBtn' onClick={() => checkHandle(`${auditType}`)}>{auditType == '0' ? '审核' : '取消审核'}</div>}
                     </div>
                 </div>
-
-                <div className='func'>
-                    <div className='title'>
-                        <span className='a'>{ifEditTable?'可分配奖金(D=(A-B)*C)':'全院人事成本(D=(A-B)*C)'}</span>
-                        {(!ifBanAllAction&&!ifEditTable)&&<span className='btn' onClick={() => generateFunc(1)}> <IconFont style={{ color: '#3376FE' }} type='iconzhongxin' /> 重新生成</span>}
-                    </div>
-                    <InputNumber className={ifEditTable?'input borderUpper':'input'} size='large' precision={2} min={-10000000} value={pageData.personalCost} placeholder='请输入'
-                        disabled={auditType == '1'}
-                        onChange={(value) => set_pageData({ ...pageData, personalCost: value as number })} />
-                </div>
-                {/* <div className='midLine'>
-                    <span>手动计算</span>
-                    根据手动填写的全院人事成本计算出各职系的
-                </div> */}
+                {
+                    pageType == 2 && (
+                        <div className='lineTwo'>
+                            <Steps current={currentStep} items={items} responsive={false} />
+                        </div>
+                    )
+                }
 
             </div>
-            {!ifBanAllAction&&<div className='countBtn' onClick={() => generateFunc(2)}>计算</div>}
-            <div className='b'>计算结果</div>
-            {currentComputeDate && <BMSTable actionRef={tableRef} pagination={false} rowKey='unitType'  scroll={{ x: 180 * 11 }} columns={[...tableColumn, {
-                title: '可分配考核奖金',
-                dataIndex: 'assessmentBonus',
-                key: 'assessmentBonus',
-                fixed: 'right',
-                render: (text: any, record: any) => {
-                    if(ifBanAllAction)return text
-                    return (
-                        <div style={{ display: 'flex', flexDirection: 'row', width: '100%', justifyContent:'flex-start', alignItems: 'center' }}>
-
-                            <div style={{ height: 16, display: 'flex', flexDirection: 'row', justifyContent: 'center', alignItems: 'center' }}>
-                                {
-                                    editTableCellId == record.id ? <Input size='small' defaultValue={record.assessmentBonus} width={80} placeholder='请输入' onChange={(e) => { set_inputSalaryNum(Number(e.target.value)) }} style={{ width: '80px' }} /> : <span>{formatMoneyNumber(text)}</span>
-                                }
+            {
+                currentStep == 0 && currentComputeDate && (
+                    <CardList computeDate={currentComputeDate} />
+                )
+            }
+            {
+                ((pageType == 2 && currentStep == 1) || pageType == 1) && (
+                    <>
+                        <div className='paramsWrap'>
+                            <div className='paramsWrapHeader'>
+                                <span className='title'>人事薪酬预算</span>
+                                {(!ifBanAllAction && !ifEditTable) && <span className='btn' onClick={() => generateFunc(1)}> <IconFont style={{ color: '#3376FE', fontSize: 16 }} type='iconzhongxin' /> 重新生成</span>}
                             </div>
-                            {auditType == '0' && <img onClick={() => editHandle(record)} style={{ width: 16, height: 16, cursor: 'pointer', marginLeft: 8 }} src={editTableCellId == record.id ? conformIcon : editIcon} alt="" />}
+                            <div className='cardWrap'>
+                                <div className='card'>
+                                    <span>{ifEditTable ? '总收入' : '收入'}</span>
+                                    <div className='count'>
+                                        <span onClick={() => auditType == '0' ? set_ifEditIncome(true) : () => { }}>{!ifEditIncome && pageData.income}</span>
+                                        {(ifEditIncome && ifEditTable) && <InputNumber precision={2} onChange={(num) => { set_pageData({ ...pageData, income: num ? num : 0 }) }} style={{ width: 160 }} autoFocus defaultValue={pageData.income} onBlur={() => {
+                                            set_ifEditIncome(false);
+                                        }} />}
+                                        {(!ifEditIncome && ifEditTable && auditType == '0') && <div className='editBtn' onClick={() => auditType == '0' ? set_ifEditIncome(true) : () => { }}><img src={editIcon_gray} alt="" /></div>}
+                                    </div>
+                                </div>
+                                <div className='card'>
+                                    <span>{ifEditTable ? '材料及药品收入' : '成本'}</span>
+                                    <div className='count'>
+                                        <span onClick={() => auditType == '0' ? set_ifEditCost(true) : () => { }}>{!ifEditCost && pageData.cost}</span>
+                                        {(ifEditCost && ifEditTable) && <InputNumber precision={2} onChange={(num) => { set_pageData({ ...pageData, cost: num ? num : 0 }) }} style={{ width: 160 }} autoFocus defaultValue={pageData.cost} onBlur={() => {
+                                            set_ifEditCost(false);
+                                        }} />}
+                                        {(!ifEditCost && ifEditTable && auditType == '0') && <div className='editBtn' onClick={() => auditType == '0' ? set_ifEditCost(true) : () => { }}><img src={editIcon_gray} alt="" /></div>}
+                                    </div>
+                                </div>
+                                <div className='card'>
+                                    <span>{ifEditTable ? '绩效占有效收入比' : '人事成本占比'}</span>
+                                    <div className='count'>
+                                        <span onClick={() => auditType == '0' ? set_ifEditRate(true) : () => { }}>{!ifEditRate && pageData.rate}</span>
+                                        {(ifEditRate && ifEditTable) && <InputNumber precision={4} onChange={(num) => { set_pageData({ ...pageData, rate: num ? num : 0 }) }} style={{ width: 160 }} autoFocus defaultValue={pageData.rate} onBlur={() => {
+                                            set_ifEditRate(false);
+                                        }} />}
+                                        {(!ifEditRate && ifEditTable && auditType == '0') && <div className='editBtn' onClick={() => auditType == '0' ? set_ifEditRate(true) : () => { }}><img src={editIcon_gray} alt="" /></div>}
+                                    </div>
+                                </div>
+                                <div className='card' style={{ background: '#F5F7FA', marginLeft: 8 }}>
+                                    <span>可分配奖金(D=(A-B)*C)</span>
+                                    <div className='count'>
+                                        <InputNumber className={ifEditTable ? 'input borderUpper' : 'input'} size='large' precision={2} min={-10000000} value={pageData.personalCost} placeholder='请输入'
+                                            disabled={auditType == '1'}
+                                            onChange={(value) => set_pageData({ ...pageData, personalCost: value as number })} />
+                                    </div>
+                                </div>
+                            </div>
+
+                            {/* <div className='func'>
+    <div className='title'>
+        <span className='a'>{ifEditTable?'可分配奖金(D=(A-B)*C)':'全院人事成本(D=(A-B)*C)'}</span>
+        {(!ifBanAllAction&&!ifEditTable)&&<span className='btn' onClick={() => generateFunc(1)}> <IconFont style={{ color: '#3376FE' }} type='iconzhongxin' /> 重新生成</span>}
+    </div>
+    <InputNumber className={ifEditTable?'input borderUpper':'input'} size='large' precision={2} min={-10000000} value={pageData.personalCost} placeholder='请输入'
+        disabled={auditType == '1'}
+        onChange={(value) => set_pageData({ ...pageData, personalCost: value as number })} />
+</div> */}
+                            {/* <div className='midLine'>
+    <span>手动计算</span>
+    根据手动填写的全院人事成本计算出各职系的
+</div> */}
+
                         </div>
-                    )
-                }
-            },]} request={(params, sort, filter) => getPageData(currentComputeDate, params, sort, filter)} />}
+                        {!ifBanAllAction && <div className='countBtn' onClick={() => generateFunc(2)}>开始计算</div>}
+
+                        <div className='tableWrap' style={{ padding: 16, background: '#fff' }}>
+                            <div className='b'>计算结果</div>
+                            {currentComputeDate && <BMSTable actionRef={tableRef} pagination={false} rowKey='unitType' scroll={{ x: 180 * 11 }} columns={[...tableColumn, {
+                                title: '可分配考核奖金',
+                                dataIndex: 'assessmentBonus',
+                                key: 'assessmentBonus',
+                                fixed: 'right',
+                                render: (text: any, record: any) => {
+                                    if (ifBanAllAction) return text
+                                    return (
+                                        <div style={{ display: 'flex', flexDirection: 'row', width: '100%', justifyContent: 'flex-start', alignItems: 'center' }}>
+
+                                            <div style={{ height: 16, display: 'flex', flexDirection: 'row', justifyContent: 'center', alignItems: 'center' }}>
+                                                {
+                                                    editTableCellId == record.id ? <Input size='small' defaultValue={record.assessmentBonus} width={80} placeholder='请输入' onChange={(e) => { set_inputSalaryNum(Number(e.target.value)) }} style={{ width: '80px' }} /> : <span>{formatMoneyNumber(text)}</span>
+                                                }
+                                            </div>
+                                            {auditType == '0' && <img onClick={() => editHandle(record)} style={{ width: 16, height: 16, cursor: 'pointer', marginLeft: 8 }} src={editTableCellId == record.id ? conformIcon : editIcon} alt="" />}
+                                        </div>
+                                    )
+                                }
+                            },]} request={(params, sort, filter) => getPageData(currentComputeDate, params, sort, filter)} />}
+                        </div>
+
+                    </>
+                )
+            }
+            {
+                currentStep == 2 && pageType == 2 && currentComputeDate && (
+                    <Distribute computeDate={currentComputeDate} />
+                )
+            }
+
         </BMSPagecontainer>
     )
 }

+ 84 - 4
src/pages/budgetMana/personnelSalaryBudget/service.ts

@@ -2,7 +2,7 @@
  * @Author: code4eat awesomedema@gmail.com
  * @Date: 2023-01-04 13:59:26
  * @LastEditors: code4eat awesomedema@gmail.com
- * @LastEditTime: 2023-06-08 10:40:40
+ * @LastEditTime: 2024-07-22 11:37:48
  * @FilePath: /BudgetManaSystem/src/pages/budgetMana/personnelSalaryBudget/service.ts
  * @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
  */
@@ -54,12 +54,28 @@ export const getCurrentCheckStatus = (computeDate:string)=>{
    })
 }
 
+//获取月度保留考核项目列表
+export const getKpiRetainList = (computeDate:string)=>{
+    return request('/performance/retain/kpiRetainList', {
+        method:'GET',
+        params:{computeDate}
+   })
+}
+
 //审核
-export const checkRequest = (data:{computeDate:string,auditType:string})=>{
+export const auditRequest = (data:{computeDate:string,auditType:string})=>{
     return request('/performance/bonus/audit', {
         method:'POST',
         params:data
-   })
+    })
+}
+
+//保留金审核
+export const checkRequest = (data:{computeDate:string,auditType:string,skipCheck:string})=>{
+    return request('/performance/retain/auditUnitRetain', {
+        method:'POST',
+        params:data
+    })
 }
 
 //生成
@@ -79,7 +95,6 @@ export const caculate = (data:any)=>{
 }
 
 //编辑
-
 export const editAssessmentBonus = (data:any)=>{
     return request('/performance/bonus/editAssessmentBonus', {
         method:'POST',
@@ -87,4 +102,69 @@ export const editAssessmentBonus = (data:any)=>{
    })
 }
 
+//保存保留系数设定
+export const saveKpiRetain = (data:any)=>{
+    return request('/performanece/retain/saveKpiRetain', {
+        method:'POST',
+        data
+   })
+}
+
+//校验保留系数是否有调整
+export const checkRetainStatusReq = (computeDate:string)=>{
+    return request('/performance/bonus/checkRetainStatus', {
+        method:'GET',
+        params:{computeDate}
+   })
+}
+
+//获取tab列表
+export const getUnitTypes = ()=>{
+    return request('/performance/dict/getDictDataList?current=1&pageSize=500&typeCode=1', {
+        method:'GET',
+   })
+}
+
+//获取月度职类保留奖金
+export const getUnitRetainListReq = (computeDate:string,unitType:string)=>{
+    return request('/performance/retain/getUnitRetainList', {
+        method:'GET',
+        params:{computeDate,unitType}
+   })
+}
+
+//获取可分配金额
+export const getKpiRemainRetainList = (unitType:string,retainCode:string)=>{
+    return request('/performance/retain/kpiRemainRetainList', {
+        method:'GET',
+        params:{retainCode:retainCode == 'totalBonus'?null:retainCode,unitType}
+   })
+}
+
+
+//保存月度职类保留奖金
+export const saveUnitRetainReq = (data:any)=>{
+    return request('/performance/retain/saveUnitRetain', {
+        method:'POST',
+        data
+   })
+}
+
+
+//获取中台参数
+export const getAllParameter = ()=>{
+    return request('/performance/parameter/getList?current=1&pageSize=500', {
+        method:'GET',
+   })
+}
+
+
+//复制上一个月
+export const copyReq = (computeDate:string)=>{
+    return request('/performance/retain/copyLastKpiRetain', {
+        method:'POST',
+        params:{computeDate}
+   })
+}
+
 

+ 175 - 88
src/pages/budgetMana/personnelSalaryBudget/style.less

@@ -1,11 +1,11 @@
 .PersonnelSalaryBudget {
     position: relative;
-    padding: 16px;
-    background: #fff;
+    // padding: 16px;
+    // background: #fff;
 
     .bms-ant-table-content {
         &::-webkit-scrollbar {
-             height: 3px;
+            height: 3px;
         }
     }
 
@@ -13,30 +13,101 @@
         color: #3376FE !important;
     }
 
-    .checkBtn {
-        position: absolute;
-        cursor: pointer;
-        top: 15px;
-        right: 16px;
-        padding: 0 14px;
-        height: 23px;
-        line-height: 23px;
-        background: #3376FE;
+    .Pageheader {
+        display: flex;
+        flex-direction: column;
+        width: 100%;
+        padding: 16px;
+        margin-bottom: 16px;
+        background: #fff;
         border-radius: 4px;
-        text-align: center;
-        font-size: 14px;
-        font-family: SourceHanSansCN-Normal, SourceHanSansCN;
-        font-weight: 400;
-        color: #FFF;
+
+        .lineOne {
+            display: flex;
+            flex-direction: row;
+            width: 100%;
+            justify-content: space-between;
+            align-items: center;
+            margin-bottom: 24px;
+
+            .title {
+                font-weight: 500;
+                font-size: 16px;
+                color: #17181A;
+            }
+
+            .btns {
+                .checkBtn {
+                    position: absolute;
+                    cursor: pointer;
+                    top: 15px;
+                    right: 16px;
+                    padding: 0 14px;
+                    height: 23px;
+                    line-height: 23px;
+                    background: #3376FE;
+                    border-radius: 4px;
+                    text-align: center;
+                    font-size: 14px;
+                    font-family: SourceHanSansCN-Normal, SourceHanSansCN;
+                    font-weight: 400;
+                    color: #FFF;
+
+                    &.prev {
+                        right:95px;
+                        color: #17181A;
+                        background: #FAFCFF;
+                        border: 1px solid #DAE2F2;
+                    }
+                }
+            }
+        }
+        .lineTwo {
+            padding: 0 24px;
+        }
     }
 
+
+
     .paramsWrap {
         border-radius: 4px;
         padding: 16px;
-        padding-bottom: 24px;
         margin-bottom: 16px;
         margin-top: 16px;
-        border: 1px solid #DAE2F2;
+        background: #fff;
+
+        .paramsWrapHeader {
+            display: flex;
+            flex-direction: row;
+            justify-content: space-between;
+            align-items: center;
+            margin-bottom: 12px;
+
+            .title {
+                height: 16px;
+                font-weight: 500;
+                font-size: 16px;
+                color: #17181A;
+                line-height: 16px;
+            }
+
+            .btn {
+                display: inline-block;
+                width: 100px;
+                height: 24px;
+                cursor: pointer;
+                text-align: center;
+                line-height: 24px;
+                border-radius: 4px;
+                font-weight: 400;
+                font-size: 14px;
+                color: #3376FE;
+
+                &:hover {
+                    background: #F2F6FF;
+                }
+            }
+        }
 
         .cardWrap {
             display: flex;
@@ -44,12 +115,11 @@
             justify-content: space-between;
             align-items: center;
             // margin-top: 16px;
-            margin-bottom: 32px;
 
             .card {
                 width: 100%;
                 height: 100px;
-                padding: 24px;
+                padding: 16px;
                 border-radius: 4px;
 
                 &>span {
@@ -122,7 +192,7 @@
                 &:nth-child(2) {
                     position: relative;
                     height: 100px;
-                    margin: 0 16px;
+                    margin: 0 8px;
                     background: linear-gradient(270deg, #D4F4FA 0%, #E8F9FC 100%);
 
                     .count {
@@ -165,66 +235,84 @@
                         background-size: cover;
                     }
                 }
-            }
-        }
-
-        .subTitle {
-            height: 17px;
-            font-size: 16px;
-            font-family: SourceHanSansCN-Medium, SourceHanSansCN;
-            font-weight: 500;
-            color: #17181A;
-            line-height: 17px;
-            margin-bottom: 24px;
-        }
-
-        .func {
-            display: flex;
-            flex-direction: column;
-            justify-content: center;
-            align-items: center;
-
-            .title {
-                display: flex;
-                width: 346px;
-                flex-direction: row;
-                justify-content: space-between;
-                align-items: center;
-                margin-bottom: 16px;
-
-                &>span {
-                    display: inline-block;
-                    height: 15px;
-                    font-size: 14px;
-                    font-family: SourceHanSansCN-Normal, SourceHanSansCN;
-                    font-weight: 400;
-                    color: #7A8599;
-                    line-height: 15px;
-                }
-
-                .btn {
-                    color: #3376FE;
-                    cursor: pointer;
-                }
-            }
 
-            .input {
-                display: flex;
-                flex-direction: row;
-                justify-content: flex-start;
-                align-items: center;
-                width: 346px;
-                height: 40px;
-                background: #FFF;
-                border-radius: 4px;
-                border: 2px solid #DAE2F2;
+                &:nth-child(4) {
+                    .input {
+                        display: flex;
+                        flex-direction: row;
+                        justify-content: flex-start;
+                        align-items: center;
+                        width: 100%;
+                        height: 32px;
+                        background: #FFF;
+                        border-radius: 4px;
+                        border: 2px solid #DAE2F2;
 
-                &.borderUpper {
-                    border: 2px solid #CFD6E6 !important;
+                        &.borderUpper {
+                            border: 2px solid #CFD6E6 !important;
+                        }
+                    }
                 }
             }
         }
 
+        // .subTitle {
+        //     height: 17px;
+        //     font-size: 16px;
+        //     font-family: SourceHanSansCN-Medium, SourceHanSansCN;
+        //     font-weight: 500;
+        //     color: #17181A;
+        //     line-height: 17px;
+        //     margin-bottom: 24px;
+        // }
+
+        // .func {
+        //     display: flex;
+        //     flex-direction: column;
+        //     justify-content: center;
+        //     align-items: center;
+
+        //     .title {
+        //         display: flex;
+        //         width: 346px;
+        //         flex-direction: row;
+        //         justify-content: space-between;
+        //         align-items: center;
+        //         margin-bottom: 16px;
+
+        //         &>span {
+        //             display: inline-block;
+        //             height: 15px;
+        //             font-size: 14px;
+        //             font-family: SourceHanSansCN-Normal, SourceHanSansCN;
+        //             font-weight: 400;
+        //             color: #7A8599;
+        //             line-height: 15px;
+        //         }
+
+        //         .btn {
+        //             color: #3376FE;
+        //             cursor: pointer;
+        //         }
+        //     }
+
+        //     .input {
+        //         display: flex;
+        //         flex-direction: row;
+        //         justify-content: flex-start;
+        //         align-items: center;
+        //         width: 346px;
+        //         height: 40px;
+        //         background: #FFF;
+        //         border-radius: 4px;
+        //         border: 2px solid #DAE2F2;
+
+        //         &.borderUpper {
+        //             border: 2px solid #CFD6E6 !important;
+        //         }
+        //     }
+        // }
+
         .midLine {
             display: flex;
             flex-direction: row;
@@ -264,18 +352,17 @@
         font-family: SourceHanSansCN-Normal, SourceHanSansCN;
         font-weight: 400;
         color: #fff;
-        margin-bottom: 24px;
-    }
-
-    .b {
-        height: 16px;
-        font-size: 16px;
-        font-family: SourceHanSansCN-Medium, SourceHanSansCN;
-        font-weight: 500;
-        color: #17181A;
-        line-height: 16px;
         margin-bottom: 16px;
     }
-
-
+    .tableWrap {
+        .b {
+            height: 16px;
+            font-size: 16px;
+            font-family: SourceHanSansCN-Medium, SourceHanSansCN;
+            font-weight: 500;
+            color: #17181A;
+            line-height: 16px;
+            margin-bottom: 16px;
+        }
+    }
 }

BIN
src/pages/noAccess/images/noAccess.png


+ 29 - 0
src/pages/noAccess/index.tsx

@@ -0,0 +1,29 @@
+/*
+ * @Author: code4eat awesomedema@gmail.com
+ * @Date: 2022-12-14 14:14:32
+ * @LastEditors: code4eat awesomedema@gmail.com
+ * @LastEditTime: 2024-07-17 15:27:23
+ * @FilePath: /BudgetManaSystem/src/pages/noAccess/index.tsx
+ * @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
+ */
+import BMSPagecontainer from '@/components/BMSPageContainer';
+import { history, Link } from 'umi';
+import './style.less';
+const NoAccessPage: React.FC = () => {
+
+  return (
+    <BMSPagecontainer
+      className='NoAccessPage'
+      title={false}
+    >
+         <div className='content'>
+              <img className='flagIcon' src={require('./images/noAccess.png')} alt="" />
+              <div className='title'>暂无权限</div>
+              <div className='subTitle'>当前功能暂无权限,请联系管理员分配权限</div>
+              <Link to='/home' replace ><div className='btn'>返回首页</div></Link>
+         </div>
+    </BMSPagecontainer>
+  );
+};
+
+export default NoAccessPage;

+ 49 - 0
src/pages/noAccess/style.less

@@ -0,0 +1,49 @@
+.NoAccessPage {
+    .content {
+        display: flex;
+        height: 91vh;
+        flex-direction: column;
+        justify-content:flex-start;
+        align-items: center;
+        background: #fff;
+        padding-top: 200px;
+        box-sizing: border-box;
+
+        .flagIcon {
+            width: 160px;
+            height: 140px;
+        }
+
+        .title {
+            font-weight: 500;
+            font-size: 24px;
+            height: 24px;
+            color: #17181A;
+            line-height: 24px;
+            margin-top: 24px;
+            margin-bottom: 16px;
+        }
+
+        .subTitle {
+            font-weight: 400;
+            font-size: 14px;
+            height: 14px;
+            color: #7A8599;
+            line-height: 14px;
+            margin-bottom: 24px;
+        }
+
+        .btn {
+            cursor: pointer;
+            width: 80px;
+            height: 24px;
+            line-height: 24px;
+            text-align: center;
+            background: #3377FF;
+            border-radius: 4px;
+            font-weight: 400;
+            font-size: 14px;
+            color: #FFFFFF;
+        }
+    }
+}

+ 150 - 0
src/pages/reportCheck/report/SetColWidComponent.tsx

@@ -0,0 +1,150 @@
+
+import React, { useState, useEffect } from 'react';
+import { Modal,Button } from 'antd';
+import { SortableContainer, SortableElement, SortableHandle } from 'react-sortable-hoc';
+
+import ProFormDigit from '@ant-design/pro-form/lib/components/Digit';
+import { getClolumnTableData } from '@/pages/setting/reportSet/reportSetting/service';
+import { arrayMoveImmutable } from 'array-move';
+import { ProColumns } from '@ant-design/pro-components';
+import { BMSTable } from '@/components/BMSTable';
+
+
+const SetColWidComponent = ({reportCode,onOkHandle}:{reportCode:string,onOkHandle?:(data:any[])=>void}) => {
+    const [dataSource, setDataSource] = useState<any[]>([]);
+    const [isModalVisible, setIsModalVisible] = useState(false);
+    const urlReportCode = reportCode; // Replace with actual report code
+  
+    useEffect(() => {
+      const fetchData = async () => {
+        const resp = await getClolumnTableData({ reportCode: urlReportCode });
+        if (resp) {
+          const dataSorted = resp.sort((prev: any, cur: any) => prev.sort - cur.sort);
+          setDataSource(dataSorted);
+        }
+      };
+      fetchData();
+    }, [urlReportCode]);
+  
+    const SortableItem = SortableElement((props: any) => <tr className="sortable-item" {...props} />);
+    const SortContainer = SortableContainer((props: any) => <tbody {...props} />);
+    const DragHandle = SortableHandle(() => <img width={16} style={{ cursor: 'pointer' }} src={require('../../../../static/tuozhuai_icon.png')} alt="" />);
+  
+    const onSortEnd = ({ oldIndex, newIndex }: { oldIndex: number; newIndex: number }) => {
+      if (oldIndex !== newIndex) {
+        const newData = arrayMoveImmutable([...dataSource], oldIndex, newIndex).filter((el) => !!el);
+        const updatedSortArr = newData.map((item: any, index: number) => ({ ...item, sort: index }));
+        setDataSource(updatedSortArr);
+      }
+    };
+  
+    const DraggableContainer = (props: any) => (
+      <SortContainer
+        useDragHandle
+        disableAutoscroll
+        helperClass="row-dragging"
+        onSortEnd={onSortEnd}
+        {...props}
+      />
+    );
+  
+    const DraggableBodyRow = (props: any) => {
+      const { className, style, ...restProps } = props;
+      const index = dataSource.findIndex((x) => x.id === restProps['data-row-key']);
+      return <SortableItem index={index} {...restProps} />;
+    };
+  
+    const onChangeHandle = (value:any, record: any) => {
+      const newDataSource = [...dataSource].map((a) => {
+        if (a.id === record.id) {
+          return {
+            ...a,
+            width: value
+          };
+        } else {
+          return a;
+        }
+      });
+      setDataSource(newDataSource);
+    };
+  
+    const modalTableColumns: ProColumns[] = [
+      {
+        title: '排序',
+        dataIndex: 'sort',
+        width: 60,
+        className: 'drag-visible',
+        render: () => <DragHandle />
+      },
+      {
+        title: '列标题',
+        ellipsis: true,
+        dataIndex: 'columnHeaderText',
+      },
+      {
+        title: '列名称',
+        ellipsis: true,
+        dataIndex: 'columnName',
+      },
+      {
+        title: '列宽',
+        dataIndex: 'width',
+        render: (num: any, record: any) => {
+          return (
+            <div style={{ display: 'flex', flexDirection: 'row', justifyContent: 'flex-start', alignItems: 'center' }}>
+              <ProFormDigit 
+                noStyle 
+                width={70} 
+                fieldProps={{
+                  defaultValue: num,
+                  onBlur: (value) => onChangeHandle(value, record)
+                }} 
+              />
+            </div>
+          );
+        }
+      }
+    ];
+  
+    const showModal = () => {
+      setIsModalVisible(true);
+    };
+  
+    const handleOk = () => {
+      setIsModalVisible(false);
+      onOkHandle&&onOkHandle(dataSource)
+    };
+  
+    const handleCancel = () => {
+      setIsModalVisible(false);
+    };
+  
+    return (
+      <div>
+        <div onClick={showModal}>设置列宽</div>
+        <Modal
+          title="设置列宽"
+          open={isModalVisible}
+          onOk={handleOk}
+          onCancel={handleCancel}
+          width={500}
+        >
+          <BMSTable
+            rowKey='id'
+            scroll={{ y: 450 }}
+            pagination={false}
+            columns={modalTableColumns}
+            dataSource={dataSource}
+            components={{
+              body: {
+                wrapper: DraggableContainer,
+                row: DraggableBodyRow,
+              },
+            }}
+          />
+        </Modal>
+      </div>
+    );
+  };
+  
+  export default SetColWidComponent;

+ 80 - 56
src/pages/reportCheck/report/index.tsx

@@ -2,7 +2,7 @@
  * @Author: code4eat awesomedema@gmail.com
  * @Date: 2023-01-04 14:12:31
  * @LastEditors: code4eat awesomedema@gmail.com
- * @LastEditTime: 2024-03-12 16:27:00
+ * @LastEditTime: 2024-07-19 15:49:54
  * @FilePath: /BudgetManaSystem/src/pages/budgetMana/oneBatch/index.tsx
  * @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
  */
@@ -11,8 +11,8 @@
 import BMSPagecontainer from '@/components/BMSPageContainer'
 import { BMSTable } from '@/components/BMSTable';
 import { getComputeDate } from '@/pages/Home/service';
-import { ActionType, ProColumns } from '@ant-design/pro-components';
-import { Space, Dropdown, MenuProps, DatePicker, Input } from 'antd';
+import { ActionType, ProColumns, ProFormDigit, useRefFunction } from '@ant-design/pro-components';
+import { Space, Dropdown, MenuProps, DatePicker, Input, Modal } from 'antd';
 import { useEffect, useRef, useState } from 'react';
 import { getData, getDataParamType, getRedirReportData } from './service';
 
@@ -21,7 +21,6 @@ import './style.less';
 
 import { useParams } from '@umijs/max';
 import { createFromIconfontCN, DownOutlined } from '@ant-design/icons';
-import { useLocation } from '@umijs/max';
 import exportTableToExcel from '@/utils/tableToExcel';
 
 
@@ -30,6 +29,10 @@ import locale from 'antd/es/date-picker/locale/zh_CN';
 import moment from 'moment';
 import { formatMoneyNumber } from '@/utils/format';
 import { number } from 'mathjs';
+import { editReportTbaleData, getClolumnTableData } from '@/pages/setting/reportSet/reportSetting/service';
+import { SortableContainer, SortableElement, SortableHandle } from 'react-sortable-hoc';
+import { arrayMoveImmutable } from 'array-move';
+import SetColWidComponent from './SetColWidComponent';
 
 const { RangePicker } = DatePicker;
 
@@ -55,16 +58,16 @@ function searchTableData(data: TableRecord[], query: string, searchableColumns:
 
 // const {search} = locatio
 let computeDate: any = undefined;
-let currentReportCode:any = undefined;
+let currentReportCode: any = undefined;
 
 const ReportTemplate = () => {
 
 
   const { reportCode: urlReportCode } = useParams();
 
-  
 
-  const [tableColumn, set_tableColumn] = useState<ProColumns[] | any[]>([]);
+
+  const [tableColumn, set_tableColumn] = useState<any[] | any[]>([]);
 
   const [tableData, set_tableData] = useState<any[]>([]);
 
@@ -88,7 +91,9 @@ const ReportTemplate = () => {
 
   const [tableDataSearchKeywords, set_tableDataSearchKeywords] = useState<string | undefined>(undefined);
   const [dataPikerType, set_dataPikerType] = useState(0);
-  const [dateComputeDateRange,set_dateComputeDateRange] = useState<string[]>([]);
+  const [dateComputeDateRange, set_dateComputeDateRange] = useState<string[] | undefined>(undefined);
+
+
 
   const reportJumphandle = (reportData: any) => {
 
@@ -125,8 +130,8 @@ const ReportTemplate = () => {
     set_tableDataFilterParams({
       ...tableDataFilterParams, reportCode: reportData.redirectReportCode, parameter: {
         compute_date: currentComputeDate,
-        start_date:dateComputeDateRange?dateComputeDateRange[0]:null,
-        end_date:dateComputeDateRange?dateComputeDateRange[1]:null,
+        start_date: dateComputeDateRange ? dateComputeDateRange[0] : null,
+        end_date: dateComputeDateRange ? dateComputeDateRange[1] : null,
         ...parameter
       }
     });
@@ -138,13 +143,13 @@ const ReportTemplate = () => {
 
     const { reportCode, parameter } = params;
     // console.log({parameter,params});
-    if(!parameter){
+    if (!parameter) {
       return {
-        data:[],
+        data: [],
         success: true
       }
-    } 
-    
+    }
+
     let resp: any = undefined;
 
     if (tableDataSearchKeywords != undefined && tableDataSearchKeywords.length > 0) {
@@ -165,7 +170,7 @@ const ReportTemplate = () => {
       //报表跳转
       resp = await getRedirReportData(
         reportCode,
-        { 
+        {
           ...parameter
         }
       );
@@ -175,7 +180,7 @@ const ReportTemplate = () => {
       //首次获取表格数据
       resp = await getData(
         reportCode,
-        { ...parameter}
+        { ...parameter }
       );
     }
 
@@ -192,7 +197,7 @@ const ReportTemplate = () => {
           title: item.name,
           dataIndex: `${item.columnName}`,
           key: `${item.columnName}`,
-          width: item.name.length * 35,
+          width: item.width?item.width:100,
           ellipsis: true,
           fixed: item.freeze ? true : false,
           hideInTable: item.hide,
@@ -264,7 +269,7 @@ const ReportTemplate = () => {
 
 
   const getCurrentComputeDate = async () => {
-    const resp = await getComputeDate();  
+    const resp = await getComputeDate();
     set_tableDataFilterParams({ ...tableDataFilterParams, parameter: { ...tableDataFilterParams.parameter, compute_date: resp } });
     set_currentComputeDate(resp);
     computeDate = resp;
@@ -284,6 +289,7 @@ const ReportTemplate = () => {
     }, 0)
   }
 
+
   const exportHandle = () => {
 
     //const headers = { name: '姓名', age: '年龄', city: '城市' };
@@ -323,50 +329,64 @@ const ReportTemplate = () => {
   const getDataPikerType = async (urlReportCode: string) => {
     const resp = await getDataParamType(urlReportCode);
     if (resp) {
-      set_dataPikerType(resp.dateType?resp.dateType:0);
-      if(resp.dateType != 0){
-          set_dateComputeDateRange(resp.dateType == 1?[computeDate,computeDate]:[`${computeDate}-01`,`${computeDate}-01`]);
-      }else{
-          set_dateComputeDateRange([]);
+      set_dataPikerType(resp.dateType ? resp.dateType : 0);
+      if (resp.dateType != 0) {
+        set_dateComputeDateRange(resp.dateType == 1 ? [computeDate, computeDate] : [`${computeDate}-01`, `${computeDate}-01`]);
+      } else {
+        set_dateComputeDateRange([]);
       }
-    }else{
+    } else {
       set_dataPikerType(0);
       set_dateComputeDateRange([]);
     }
   }
 
+  const updateTableCol = async (newData: any[]) => {
+    let newDataSource = newData.map((a: any, index: number) => {
+      //更新排序
+      return { ...a, sort: index + 1 }
+    })
+    const resp = await editReportTbaleData(newDataSource);
+    if (resp) {
+        tableRef.current?.reload();
+    }
+  }
+
 
   useEffect(() => {
     // 在这里处理路由参数变化的逻辑
-   
-    if(!currentComputeDate)return;
-    if(step == 0){
-      set_tableDataFilterParams({ 
-        reportCode: urlReportCode, 
-        parameter:{
+    if (!currentComputeDate) return;
+    if (step == 0) {
+      set_tableDataFilterParams({
+        reportCode: urlReportCode,
+        parameter: {
           compute_date: currentComputeDate,
-          start_date:dateComputeDateRange?dateComputeDateRange[0]:null,
-          end_date:dateComputeDateRange?dateComputeDateRange[1]:null
-        } 
+          start_date: dateComputeDateRange ? dateComputeDateRange[0] : null,
+          end_date: dateComputeDateRange ? dateComputeDateRange[1] : null
+        }
       });
       set_breadCrumbList([{
         name: '首页',
         index: 0,
-        params: { reportCode: urlReportCode, 
-          parameter: dateComputeDateRange?{ compute_date: currentComputeDate,start_date:dateComputeDateRange[0],
-          end_date:dateComputeDateRange[1] }:{compute_date: currentComputeDate} }
+        params: {
+          reportCode: urlReportCode,
+          parameter: dateComputeDateRange ? {
+            compute_date: currentComputeDate, start_date: dateComputeDateRange[0],
+            end_date: dateComputeDateRange[1]
+          } : { compute_date: currentComputeDate }
+        }
       }]);
-    }else{
-      set_tableDataFilterParams({ 
-        reportCode: currentReportCode, 
-        parameter:{
+    } else {
+      set_tableDataFilterParams({
+        reportCode: currentReportCode,
+        parameter: {
           compute_date: currentComputeDate,
-          start_date:dateComputeDateRange?dateComputeDateRange[0]:null,
-          end_date:dateComputeDateRange?dateComputeDateRange[1]:null
-        } 
+          start_date: dateComputeDateRange ? dateComputeDateRange[0] : null,
+          end_date: dateComputeDateRange ? dateComputeDateRange[1] : null
+        }
       });
     }
-    
+
     // set_dateComputeDateRange([]);
 
   }, [dateComputeDateRange]);
@@ -377,14 +397,14 @@ const ReportTemplate = () => {
 
 
   useEffect(() => {
-    if (urlReportCode){
+    if (urlReportCode) {
       getDataPikerType(urlReportCode);
       set_step(0);
-    } 
+    }
   }, [urlReportCode]);
 
 
-  
+
 
   useEffect(() => {
     getCurrentComputeDate();
@@ -422,6 +442,7 @@ const ReportTemplate = () => {
               <DatePicker
                 onChange={(data, dateString) => {
                   set_currentComputeDate(dateString);
+                  set_dateComputeDateRange([dateString, dateString]);
                   computeDate = dateString;
                 }}
                 allowClear={false}
@@ -434,10 +455,10 @@ const ReportTemplate = () => {
           {
             dataPikerType == 1 && (
               <RangePicker
-               onChange={(data, dateString) => {
-                set_dateComputeDateRange(dateString);
-                // computeDate = dateString;
-               }}
+                onChange={(data, dateString) => {
+                  set_dateComputeDateRange(dateString);
+                  // computeDate = dateString;
+                }}
                 picker="month"
                 allowClear={false}
                 locale={locale}
@@ -449,10 +470,10 @@ const ReportTemplate = () => {
           {
             dataPikerType == 2 && (
               <RangePicker
-               onChange={(data, dateString) => {
-                set_dateComputeDateRange(dateString);
-                // computeDate = dateString;
-               }}
+                onChange={(data, dateString) => {
+                  set_dateComputeDateRange(dateString);
+                  // computeDate = dateString;
+                }}
                 picker="date"
                 allowClear={false}
                 locale={locale}
@@ -482,12 +503,15 @@ const ReportTemplate = () => {
             </div>
           )
         }
+        {/* <div className='export' style={{ right: 80, width: 75 }}>
+          {urlReportCode && < SetColWidComponent reportCode={urlReportCode} onOkHandle={updateTableCol} />}
+        </div> */}
         <div className='export' onClick={() => exportHandle()}>导出</div>
 
         <div className='content'>
           {dataPikerType != undefined && <BMSTable actionRef={tableRef} rowKey='id' pagination={false} columns={tableColumn as ProColumns[]}
             params={tableDataFilterParams}
-            scroll={{ x: 100 * tableColumn.length, y: tableH }}
+            scroll={{ x: (tableColumn.reduce((prev:number,cur:any)=>prev + (cur.width?cur.width:100),0)), y: tableH }}
             request={(params, sort, filter) => getTableData(params, sort, filter)}
           />}
         </div>

+ 12 - 0
src/pages/reportCheck/report/style.less

@@ -1,3 +1,15 @@
+.row-dragging {
+ 
+    .bms-ant-table-cell {
+        width: 130px;
+    }
+}
+
+.sortable-item {
+    z-index: 1000;
+}
+
+
 .ReportTemplate {
     .breadcrumb {
         margin-top: 8px;

+ 19 - 3
src/pages/secondaryDistribute/employeeInfoCheck/index.tsx

@@ -4,7 +4,7 @@
  * @Author: code4eat awesomedema@gmail.com
  * @Date: 2022-12-16 09:42:52
  * @LastEditors: code4eat awesomedema@gmail.com
- * @LastEditTime: 2023-08-17 15:16:36
+ * @LastEditTime: 2024-07-22 15:06:18
  * @FilePath: /BudgetManaSystem/src/pages/budgetMana/monthlySet/index.tsx
  * @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
  */
@@ -45,6 +45,7 @@ import { getJiezhuanStatus } from '@/pages/budgetMana/monthlySet/service';
 
 
 
+
 const IconFont = createFromIconfontCN({
     scriptUrl: '',
 });
@@ -90,28 +91,40 @@ const EmployeeInfoCheck: React.FC = () => {
     const tableRef = useRef<ActionType>();
 
 
-    const column = [
+    const column:ProColumns[] = [
         {
             title: '工号',
             dataIndex: 'empNo',
             key: 'empNo',
+            sorter:(a,b)=>{
+                  return parseInt(a.empNo) - parseInt(b.empNo);
+            },
 
         },
         {
             title: '姓名',
             dataIndex: 'empName',
             key: 'empName',
+            sorter: (a, b) => {
+                return a.empName.localeCompare(b.empName);
+            },
 
         },
         {
             title: '职务',
             dataIndex: 'jobTitleName',
             key: 'jobTitleName',
+            sorter: (a, b) => {
+                return a.jobTitleName.localeCompare(b.jobTitleName);
+            },
 
         },
         {
             title: '职称',
             dataIndex: 'titleName',
+            sorter: (a, b) => {
+                return a.titleName.localeCompare(b.titleName);
+            },
         },
         {
             title: '职称系数',
@@ -186,6 +199,8 @@ const EmployeeInfoCheck: React.FC = () => {
         }
     }
 
+    
+
     const getTableData = async (type: 'PERSON' | 'DEP' | 'CHARGE', params: any, sort: any, filter: any) => {
         // console.log({ currentSelectedTreeNode });
         // console.log({ params, sort, filter });
@@ -631,7 +646,8 @@ const EmployeeInfoCheck: React.FC = () => {
                                     {currentComputeDate&&<DatePicker
                                         onChange={(data, dateString) => {
                                             if (dateString) {
-                                                set_empFilterParams({ ...empFilterParams, computeDate: dateString })
+                                                set_empFilterParams({ ...empFilterParams, computeDate: dateString });
+                                                set_currentComputeDate(dateString);
                                             } else {
                                                 set_empFilterParams({ ...empFilterParams, computeDate: currentComputeDate })
                                             }

+ 5 - 1
src/pages/secondaryDistribute/secondaryDitriComputed/index.tsx

@@ -4,7 +4,7 @@
  * @Author: code4eat awesomedema@gmail.com
  * @Date: 2022-12-16 09:42:52
  * @LastEditors: code4eat awesomedema@gmail.com
- * @LastEditTime: 2023-09-25 13:57:31
+ * @LastEditTime: 2024-07-19 15:27:13
  * @FilePath: /BudgetManaSystem/src/pages/budgetMana/monthlySet/index.tsx
  * @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
  */
@@ -161,6 +161,9 @@ const SecondaryDitriComputed: React.FC = () => {
         if (currentSelectedTreeNode) {
             const resp = await getCurrentUnitCheckStatusReq(currentComputeDate as string, currentSelectedTreeNode.code);
             set_currentUnitAuditType(`${resp}`);  //0 未审核 1 已审核
+            if(!ifCheckPage){
+                 set_auditType(`${resp}`);
+            }
         }
     }
 
@@ -591,6 +594,7 @@ const SecondaryDitriComputed: React.FC = () => {
 
             if (!ifCheckPage) {
                 getCurrentCommitStatus();
+                getCurrentUnitCheckStatus();
             }
             if (ifCheckPage) {
                 getCurrentUnitCheckStatus();

+ 3 - 1
src/pages/setting/checkUnitSet/checkUnitDepMap/index.tsx

@@ -4,7 +4,7 @@
  * @Author: code4eat awesomedema@gmail.com
  * @Date: 2022-12-16 09:42:52
  * @LastEditors: code4eat awesomedema@gmail.com
- * @LastEditTime: 2023-08-02 10:29:57
+ * @LastEditTime: 2024-05-09 14:06:33
  * @FilePath: /BudgetManaSystem/src/pages/budgetMana/monthlySet/index.tsx
  * @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
  */
@@ -138,6 +138,8 @@ const CheckUnitDepMap: React.FC = () => {
             const resp = await getCheckUnitDepMapTableDataByUnitClass({
                 ...params,
                 unitCode: currentSelectedTreeNode.code,
+                current:1,
+                pageSize:500
 
             });
             if (resp) {

+ 1 - 1
src/pages/setting/checkUnitSet/checkUnitEmpSet/index.tsx

@@ -169,7 +169,7 @@ const CheckUnitDepMap: React.FC = () => {
         },
     ];
 
-    const tableSelecterColumn = [
+    const tableSelecterColumn = [                                                                                                     
         {
             title: '核算单元名称',
             dataIndex: 'unitName',

+ 102 - 46
src/pages/setting/projectSetting/checkUnitProjectSet/index.tsx

@@ -4,7 +4,7 @@
  * @Author: code4eat awesomedema@gmail.com
  * @Date: 2022-12-16 09:42:52
  * @LastEditors: code4eat awesomedema@gmail.com
- * @LastEditTime: 2023-09-25 17:52:43
+ * @LastEditTime: 2024-06-05 15:38:03
  * @FilePath: /BudgetManaSystem/src/pages/budgetMana/monthlySet/index.tsx
  * @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
  */
@@ -738,66 +738,122 @@ const CheckUnitProjectSet: React.FC = () => {
 
 
         //获取单元
+        // const getFuncList = async () => {
+
+        //     if (currentSelectedTabKey == '1') {
+        //         const resp = await getAllCheckUnitProjectData();
+        //         if (resp) {
+
+        //             const resp1 = await getBillingItemTableData({
+        //                 unitCode: currentSelectedTreeNode.code,
+        //                 pageSize: 500, current: 1
+        //             });
+
+        //             if (resp1) {
+        //                 let allData = resp;
+        //                 set_targetData(resp1.list);
+        //                 const defaultSelctedkeys = (resp1.list).map((a: any) => a.itemPointCode);
+        //                 set_datasource(allData);
+        //                 setTargetKeys(defaultSelctedkeys);
+        //             }
+        //         }
+        //     }
+        //     if (currentSelectedTabKey == '2') {
+        //         const resp = await getIndicProjectTableData({ pageSize: 500, current: 1 });
+        //         if (resp) {
+
+        //             const resp1 = await getIndicTableData({
+        //                 unitCode: currentSelectedTreeNode.code,
+        //                 pageSize: 500, current: 1
+        //             });
+
+        //             if (resp1) {
+        //                 set_targetData(resp1.list);
+        //                 set_datasource(resp.list.map((a: any) => ({ ...a, itemName: a.indicatorName, itemType: a.indicatorTypeName })));
+
+        //                 const defaultSelctedkeys = (resp1.list).map((a: any) => a.indicatorCode);
+        //                 setTargetKeys(defaultSelctedkeys);
+        //             }
+        //         }
+        //     }
+        //     if (currentSelectedTabKey == '3') {
+        //         const resp = await getNonCheckProjectTableData({ pageSize: 500, current: 1 });
+        //         if (resp) {
+
+        //             const resp1 = await getNonCheckTableData({
+        //                 unitCode: currentSelectedTreeNode.code,
+        //                 pageSize: 1000000, current: 1
+        //             });
+
+        //             if (resp1) {
+        //                 set_targetData(resp1.list);
+        //                 set_datasource(resp.list.map((a: any) => ({ ...a, itemName: a.name, itemType: a.distributionTypeName })));
+
+        //                 const defaultSelctedkeys = (resp1.list).map((a: any) => a.nonAssessmentCode);
+        //                 setTargetKeys(defaultSelctedkeys);
+        //             }
+        //         }
+        //     }
+
+        //     set_loading(false);
+
+        // }
         const getFuncList = async () => {
-
+            const fetchData = async (fetchFunc: any, params: any, keyField: string, mapFunc: any) => {
+                let allData: any[] = [];
+                let page = 1;
+                let pageSize = params.pageSize;
+        
+                const fetchPage = async (page: number) => {
+                    const resp = await fetchFunc({ ...params, current: page });
+                    if (resp && resp.list.length > 0) {
+                        allData = allData.concat(resp.list);
+                        if (resp.list.length === pageSize) {
+                            await fetchPage(page + 1);
+                        }
+                    }
+                };
+        
+                await fetchPage(page);
+                return allData;
+            };
+        
             if (currentSelectedTabKey == '1') {
                 const resp = await getAllCheckUnitProjectData();
                 if (resp) {
-
-                    const resp1 = await getBillingItemTableData({
-                        unitCode: currentSelectedTreeNode.code,
-                        pageSize: 500, current: 1
-                    });
-
-                    if (resp1) {
-                        let allData = resp;
-                        set_targetData(resp1.list);
-                        const defaultSelctedkeys = (resp1.list).map((a: any) => a.itemPointCode);
-                        set_datasource(allData);
-                        setTargetKeys(defaultSelctedkeys);
-                    }
+                    const resp1 = await fetchData(getBillingItemTableData, { unitCode: currentSelectedTreeNode.code, pageSize: 500 }, 'itemPointCode', (a: any) => a.itemPointCode);
+                    set_targetData(resp1);
+                    const defaultSelctedkeys = resp1.map((a: any) => a.itemPointCode);
+                    set_datasource(resp);
+                    setTargetKeys(defaultSelctedkeys);
                 }
             }
+        
             if (currentSelectedTabKey == '2') {
-                const resp = await getIndicProjectTableData({ pageSize: 500, current: 1 });
+                const resp = await fetchData(getIndicProjectTableData, { pageSize: 500 }, 'indicatorCode', (a: any) => ({ ...a, itemName: a.indicatorName, itemType: a.indicatorTypeName }));
                 if (resp) {
-
-                    const resp1 = await getIndicTableData({
-                        unitCode: currentSelectedTreeNode.code,
-                        pageSize: 500, current: 1
-                    });
-
-                    if (resp1) {
-                        set_targetData(resp1.list);
-                        set_datasource(resp.list.map((a: any) => ({ ...a, itemName: a.indicatorName, itemType: a.indicatorTypeName })));
-
-                        const defaultSelctedkeys = (resp1.list).map((a: any) => a.indicatorCode);
-                        setTargetKeys(defaultSelctedkeys);
-                    }
+                    const resp1 = await fetchData(getIndicTableData, { unitCode: currentSelectedTreeNode.code, pageSize: 500 }, 'indicatorCode', (a: any) => a.indicatorCode);
+                    set_targetData(resp1);
+                    set_datasource(resp.map((a: any) => ({ ...a, itemName: a.indicatorName, itemType: a.indicatorTypeName })));
+                    const defaultSelctedkeys = resp1.map((a: any) => a.indicatorCode);
+                    setTargetKeys(defaultSelctedkeys);
                 }
             }
+        
             if (currentSelectedTabKey == '3') {
-                const resp = await getNonCheckProjectTableData({ pageSize: 500, current: 1 });
+                const resp = await fetchData(getNonCheckProjectTableData, { pageSize: 500 }, 'nonAssessmentCode', (a: any) => ({ ...a, itemName: a.name, itemType: a.distributionTypeName }));
                 if (resp) {
-
-                    const resp1 = await getNonCheckTableData({
-                        unitCode: currentSelectedTreeNode.code,
-                        pageSize: 1000000, current: 1
-                    });
-
-                    if (resp1) {
-                        set_targetData(resp1.list);
-                        set_datasource(resp.list.map((a: any) => ({ ...a, itemName: a.name, itemType: a.distributionTypeName })));
-
-                        const defaultSelctedkeys = (resp1.list).map((a: any) => a.nonAssessmentCode);
-                        setTargetKeys(defaultSelctedkeys);
-                    }
+                    const resp1 = await fetchData(getNonCheckTableData, { unitCode: currentSelectedTreeNode.code, pageSize: 1000000 }, 'nonAssessmentCode', (a: any) => a.nonAssessmentCode);
+                    set_targetData(resp1);
+                    set_datasource(resp.map((a: any) => ({ ...a, itemName: a.name, itemType: a.distributionTypeName })));
+                    const defaultSelctedkeys = resp1.map((a: any) => a.nonAssessmentCode);
+                    setTargetKeys(defaultSelctedkeys);
                 }
             }
-
+        
             set_loading(false);
-
-        }
+        };
+        
 
         const onChange = (nextTargetKeys: string[]) => {
             setTargetKeys(nextTargetKeys);

+ 370 - 0
src/pages/setting/projectSetting/jobCateRetenSet/UpDataActBtn.tsx

@@ -0,0 +1,370 @@
+import SQLEditor from "@/components/SQLEditor";
+import { ModalForm, ProFormDependency, ProFormRadio, ProFormSelect, ProFormText } from "@ant-design/pro-components";
+import { useEffect, useRef, useState } from "react";
+import { message } from 'antd';
+import { addData, editData, getManaIndic, getReportListTableData, getReportListTypeFromDic } from "./service";
+
+export const ReportListManaUpDataActBtn = ({ record, type, onFinishedhandle }: { record: any, type: 'EDIT' | 'ADD' | 'EDIT_CHILD', onFinishedhandle: () => void }) => {
+
+    const [expand, set_expand] = useState(false);
+    const [isModalVisible, setIsModalVisible] = useState(false);
+    const [initialValues, set_initialValues] = useState<any | undefined>(undefined);
+
+    const formRef = useRef();
+
+    const updateTable = async (formVal: any, type: 'EDIT' | "ADD" | 'EDIT_CHILD') => {
+        if (type == 'ADD') {
+            if (formVal.dataSource == 1) {
+                //
+                let result = {
+                    name: formVal.name,
+                    headerText: formVal.headerText,
+                    dataSource: formVal.dataSource,
+                    columnType: formVal.columnType,
+                    dataType: formVal.dataType,
+                    sql: '',
+                    indicatorCode: formVal.indicatorCode.key,
+                    indicatorCodeName: formVal.indicatorCode.label,
+                }
+
+                const resp = await addData({ ...result });
+                if (resp) {
+                    onFinishedhandle && onFinishedhandle()
+                }
+
+            }
+            if (formVal.dataSource == 2) {
+                //sql
+                let result = {
+                    name: formVal.name,
+                    headerText: formVal.headerText,
+                    dataSource: formVal.dataSource,
+                    columnType: formVal.columnType,
+                    dataType: formVal.dataType,
+                    sql: formVal.sql,
+                    indicatorCode: '',
+                    indicatorCodeName: ''
+                }
+
+                const resp = await addData({ ...result });
+                if (resp) {
+                    onFinishedhandle && onFinishedhandle()
+                }
+            }
+
+
+
+        }
+        if (type == 'EDIT') {
+
+            if (formVal.dataSource == 1) {
+                //指标
+                const result = {
+                    id: formVal.id,
+                    name: formVal.name,
+                    headerText: formVal.headerText,
+                    dataSource: formVal.dataSource,
+                    columnType: formVal.columnType,
+                    dataType: formVal.dataType,
+                    sql: '',
+                    indicatorCode: formVal.indicatorCode.key,
+                    indicatorCodeName: formVal.indicatorCode.label,
+                }
+                const resp = await editData({ ...result });
+                if (resp) {
+                    onFinishedhandle && onFinishedhandle()
+                }
+
+            }
+
+            if (formVal.dataSource == 2) {
+                //sql
+                const result = {
+                    id: formVal.id,
+                    name: formVal.name,
+                    headerText: formVal.headerText,
+                    dataSource: formVal.dataSource,
+                    columnType: formVal.columnType,
+                    dataType: formVal.dataType,
+                    sql: formVal.sql,
+                    indicatorCode: '',
+                    indicatorCodeName: ''
+                }
+                const resp = await editData({ ...result });
+                if (resp) {
+                    onFinishedhandle && onFinishedhandle()
+                }
+            }
+
+
+        }
+        return true;
+
+    }
+
+    const setFormInit = async (record: any) => {
+        const { columnHeaderText, columnCode } = record;
+        const resp = await getReportListTableData({ columnName: columnHeaderText, pageSize: 500 });
+        if (resp) {
+            const { list } = resp;
+            if (list.length > 1) {
+                const result = list.filter((a) => a.code == columnCode);
+                set_initialValues(result[0]);
+            } else {
+                set_initialValues(list[0]);
+            }
+        }
+
+    }
+
+    const onOpenChangeHandle = (visible: boolean) => {
+        setIsModalVisible(visible);
+    }
+
+    const openModal = async () => {
+        console.log('openModal')
+        if (record) {
+            const { request } = record;
+            if (request) {
+                await setFormInit(record);
+            } else {
+                set_initialValues(type == 'EDIT' ? { ...record, indicatorCode: { label: record.indicatorCodeName, value: record.indicatorCode } } : {});
+            }
+        }
+
+        setIsModalVisible(true);  // 在状态更新后显示模态框
+    };
+
+
+    useEffect(() => {
+        if (initialValues) {
+            set_expand(initialValues.dataSource == 2)
+        }
+    }, [initialValues]);
+
+    return (
+        <>
+            {type === 'EDIT_CHILD' ?
+                <a key="edit_child" onClick={openModal}>子列</a> :
+                type === 'EDIT' ?
+                    <a key="edit" onClick={openModal}>编辑</a> :
+                    <span className='add' onClick={openModal}>新增</span>
+            }
+            <ModalForm
+                title={`${type == 'EDIT' ? '编辑' : '新增'}报表列`}
+                width={expand ? 880 : 352}
+                formRef={formRef}
+                initialValues={initialValues}
+                open={isModalVisible}
+                onOpenChange={onOpenChangeHandle}
+                // trigger={
+                //     type == 'EDIT_CHILD' ? <a key="edit_child" >子列</a> : type == 'EDIT' ? <a key="edit" >编辑</a> : <span className='add'>新增</span>
+                // }
+                onFinish={(val) => {
+
+                    return updateTable(type == 'EDIT' ? {...initialValues,...val, sql: val.sql ? val.sql : '', } : { ...val }, type);
+                }}
+                colProps={{ span: 24 }}
+
+                modalProps={{
+                    destroyOnClose: true,
+
+                }}
+                grid
+            >
+                {
+                    (type != 'EDIT_CHILD') && (
+                        <div className='formContent' style={{ display: 'flex', flexDirection: 'row', alignItems: 'flex-start' }}>
+                            <div className='left' style={{ width: expand ? 240 : 328, marginRight: 16 }}>
+                                <ProFormText
+                                    name="name"
+                                    label="列名称:"
+                                    placeholder="请输入"
+                                    rules={[{ required: true, message: '列名称不能为空!' }]}
+                                />
+                                <ProFormText
+                                    name="headerText"
+                                    label="列标题:"
+                                    placeholder="请输入"
+                                    rules={[{ required: true, message: '列标题不能为空!' }]}
+                                />
+                                <ProFormSelect
+                                    name="columnType"
+                                    label="列类型:"
+                                    placeholder="请选择"
+                                    request={async () => {
+                                        const resp = await getReportListTypeFromDic();
+                                        if (resp) {
+                                            return resp.list.map((a: any) => ({ label: a.name, value: a.code }))
+                                        }
+                                    }}
+                                    rules={[{ required: true, message: '列类型不能为空!' }]}
+                                />
+
+
+                                <ProFormRadio.Group
+                                    name="dataType"
+                                    label="数据格式:"
+                                    options={[
+                                        {
+                                            label: '文本',
+                                            value: 1,
+                                        },
+                                        {
+                                            label: '金额',
+                                            value: 2,
+                                        },
+                                        {
+                                            label: '数值',
+                                            value: 3,
+                                        },
+                                        {
+                                            label: '百分比',
+                                            value: 4,
+                                        },
+                                    ]}
+                                    rules={[{ required: true, message: '取数格式不能为空!' }]}
+                                />
+
+
+                                <ProFormSelect
+                                    name="dataSource"
+                                    label="取数类型:"
+                                    placeholder="请选择"
+                                    fieldProps={{
+                                        onChange(value, option) {
+                                            set_expand(value == 2)
+                                        },
+                                    }}
+                                    options={[
+                                        { label: '指标', value: 1 },
+                                        { label: '自定义SQL', value: 2 }
+                                    ]}
+                                    rules={[{ required: true, message: '取数类型不能为空!' }]}
+                                />
+                                <ProFormDependency name={['dataSource']}>
+                                    {
+                                        ({ dataSource }) => dataSource == 1 && (
+                                            <ProFormSelect
+                                                name="indicatorCode"
+                                                label="取数来源:"
+                                                placeholder="请选择"
+                                                fieldProps={{
+                                                    labelInValue: true
+                                                }}
+                                                request={async () => {
+                                                    const resp = await getManaIndic();
+                                                    if (resp) {
+                                                        return resp.map((a: any) => ({ label: a.name, value: a.code }))
+                                                    }
+                                                }}
+                                                rules={[{ required: true, message: '数据来源不能为空!' }]}
+                                            />
+                                        )
+                                    }
+                                </ProFormDependency>
+                            </div>
+
+                            <ProFormDependency name={['dataSource']}>
+                                {
+                                    ({ dataSource }) => dataSource == 2 && (
+                                        <div className='right' style={{ width: 'calc(880px - 290px)' }}>
+                                            <SQLEditor
+                                                name="sql"
+                                                label="SQL:"
+                                                rules={[{ required: true, message: 'SQL不能为空!' }]}
+                                                form={formRef}
+                                                height={'260px'}
+                                            />
+                                        </div>
+
+                                    )
+                                }
+                            </ProFormDependency>
+                        </div>
+                    )
+                }
+                {/* {
+                (type == 'EDIT_CHILD') && (
+                    <>
+                        <ProFormText
+                            name="name"
+                            label="列名称:"
+                            placeholder="请输入"
+                            rules={[{ required: true, message: '列名称不能为空!' }]}
+                        />
+                        <ProFormText
+                            name="headerText"
+                            label="列标题:"
+                            placeholder="请输入"
+                            rules={[{ required: true, message: '列标题不能为空!' }]}
+                        />
+                        <ProFormSelect
+                            name="columnType"
+                            label="列类型:"
+                            placeholder="请选择"
+                            request={async () => {
+                                const resp = await getReportListType();
+                                if (resp) {
+                                    return resp.list.map((a: any) => ({ label: a.name, value: a.code }))
+                                }
+                            }}
+                            rules={[{ required: true, message: '列类型不能为空!' }]}
+                        />
+
+                        <ProFormCheckbox.Group
+                            name="checkbox"
+                            layout="vertical"
+                            label="取数格式:"
+                            options={['文本','金额','数值','百分比']}
+                        />
+
+                        <ProFormSelect
+                            name="dataSource"
+                            label="取数类型:"
+                            placeholder="请选择"
+                            options={[
+                                { label: '指标', value: 1 },
+                                { label: '自定义SQL', value: 2 }
+                            ]}
+                            rules={[{ required: true, message: '取数类型不能为空!' }]}
+                        />
+                        <ProFormDependency name={['dataSource']}>
+                            {
+                                ({ dataSource }) => dataSource == 1 && (
+                                    <ProFormSelect
+                                        name="indicatorCode"
+                                        label="取数来源:"
+                                        placeholder="请选择"
+                                        fieldProps={{
+                                            labelInValue: true
+                                        }}
+                                        request={async () => {
+                                            const resp = await getManaIndic();
+                                            if (resp) {
+                                                return resp.map((a: any) => ({ label: a.name, value: a.code }))
+                                            }
+                                        }}
+                                        rules={[{ required: true, message: '数据来源不能为空!' }]}
+                                    />
+                                )
+                            }
+                        </ProFormDependency>
+                        <ProFormDependency name={['dataSource']}>
+                            {
+                                ({ dataSource }) => dataSource == 2 && (
+                                    <ProFormTextArea name={'sql'} label='SQL:' rules={[{ required: true, message: 'SQL不能为空!' }]} />
+                                )
+                            }
+                        </ProFormDependency>
+                    </>
+                )
+            } */}
+
+
+
+            </ModalForm>
+        </>
+
+    )
+}

+ 441 - 0
src/pages/setting/projectSetting/jobCateRetenSet/index.tsx

@@ -0,0 +1,441 @@
+/*
+ * @Author: code4eat awesomedema@gmail.com
+ * @Date: 2023-03-03 11:30:33
+ * @LastEditors: code4eat awesomedema@gmail.com
+ * @LastEditTime: 2024-04-26 15:49:07
+ * @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 BMSPagecontainer from '@/components/BMSPageContainer';
+import { BMSTable } from '@/components/BMSTable';
+import { createFromIconfontCN, MenuOutlined } from '@ant-design/icons';
+
+import { ActionType, useRefFunction } from '@ant-design/pro-components';
+import { ModalForm, ProFormSelect } from '@ant-design/pro-form'
+import { ProColumns } from '@ant-design/pro-table';
+import { Input, message, Popconfirm,Tabs } from 'antd';
+import { Key, useEffect, useRef, useState } from 'react';
+
+import { delData, editData, getModalTableList, getJobCateReq, getReportListTableData} from './service';
+
+import './style.less';
+
+import '../../../../utils/zhongtaiB';
+import React from 'react';
+import SQLEditor from '@/components/SQLEditor';
+import { ReportListManaUpDataActBtn } from './UpDataActBtn';
+import { getDicDataBySysId } from '@/services/getDic';
+import { arrayMoveImmutable } from 'array-move';
+import { SortableContainer, SortableElement, SortableHandle } from 'react-sortable-hoc';
+const IconFont = createFromIconfontCN({
+    scriptUrl: '',
+});
+
+const DragHandle = SortableHandle(() => (
+    <MenuOutlined style={{ cursor: 'grab', color: '#999' }} />
+));
+
+export default function JobCateRetenSet() {
+
+    const [tableDataFilterParams, set_tableDataFilterParams] = useState<any | undefined>();
+    const [tableDataSearchKeywords, set_tableDataSearchKeywords] = useState<string>('');
+    const tableRef = useRef<ActionType>();
+    const [currentEditRow, set_currentEditRow] = useState<any | undefined>(undefined);
+    const [tabs,set_tabs] = useState<any[]>([]);
+    const [currentTabKey,set_currentTabKey] = useState<string|undefined>(undefined);
+    const [dataSource,set_dataSource] = useState<any[]>([]);
+    const SortableItem = SortableElement((props: any) => <tr {...props} />);
+    const SortContainer = SortableContainer((props: any) => <tbody {...props} />);
+    const [tableSelectedKeys,set_tableSelectedKeys] = useState<Key[]>([]);
+
+    const [ifShowModal, set_ifShowModal] = useState(false);
+
+    const ref = React.createRef<{ save: any; getSelectedKeys: any }>();
+
+    const columns: ProColumns[] = [
+        {
+            dataIndex: 'sort',
+            width: 50,
+            className: 'drag-visible',
+            render: () => <DragHandle />
+        },
+        {
+            title: '项目代码',
+            dataIndex: 'retainCode',
+            ellipsis: true
+        },
+        {
+            title: '项目名称',
+            dataIndex: 'retainName',
+            ellipsis: true
+        },
+        {
+            title: '项目类型',
+            dataIndex: 'retainTypeName',
+            ellipsis: true
+        },
+        {
+            title: '说明',
+            dataIndex: 'comment',
+            ellipsis: true
+        },
+        {
+            title: '操作',
+            key: 'option',
+            width: 120,
+            valueType: 'option',
+            render: (_: any, record: any) => {
+                return [
+                    <Popconfirm
+                        title="是否确认删除?"
+                        key="del"
+                        onConfirm={() => delTableData([record.id])}
+                    >
+                        <a>删除</a>
+                    </Popconfirm>
+                ]
+            },
+        },
+
+    ]
+
+    const tableSelecterColumn: ProColumns[] = [
+        {
+            title: '项目名称',
+            dataIndex: 'retainName',
+            ellipsis: true
+            // render:(_:any)=>_ == 1?'指标':'自定义SQL'
+        },
+        {
+            title: '数据格式',
+            dataIndex: 'retainTypeName',
+            ellipsis: true
+            // render:(_:any)=>_ == 1?'指标':'自定义SQL'
+        },
+    ]
+
+
+    const getTableData = async (params: any) => {
+        const {unitType,filter} = params;
+        if(!unitType) return []
+        const resp = await getReportListTableData(params);
+        if (resp) {
+            set_dataSource(resp);
+            if(filter){
+                
+                return {
+                     data:resp.filter((a:any)=>a.retainName.indexOf(filter) != -1),
+                     success: true,
+                }
+            }
+            return {
+                data: resp,
+                success: true,
+            }
+        }
+        return []
+    }
+
+    const delTableData = async (ids:Key[]) => {
+        const resp = await delData(ids);
+        if (resp) {
+            message.success('操作成功!');
+            tableRef.current?.reload();
+            // message.success('操作成功!');
+        }
+        set_tableSelectedKeys([]);
+    }
+
+
+
+    const tableDataSearchHandle = (paramName: string) => {
+
+        set_tableDataFilterParams({
+            ...tableDataFilterParams,
+            [`${paramName}`]: tableDataSearchKeywords
+        })
+
+    }
+
+    const getTabsHandle = async ()=>{
+          const resp = await getJobCateReq()
+          if(resp){
+               const arr = resp.list.map((a:any)=>({label:a.name,key:a.code}));
+               set_tabs([...arr]);
+               set_currentTabKey(arr[0].key);
+          }
+          
+    }
+
+
+    interface TableSelecterProps {
+        tableSelecterColumn: any[];
+        record: any
+    }
+
+    const TableSelecter = React.forwardRef(({ tableSelecterColumn, record }: 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 [tableParams, set_tableParams] = useState<any>({});
+        const [keywords, set_keywords] = useState('');
+    
+
+        //获取表格数据
+        const getFuncList = async () => {
+            const resp = await getModalTableList(currentTabKey as string);
+            if (resp) {
+                set_datasource(resp);
+                set_showList(resp);
+
+                // if (currentEditRow && currentEditRow.childColumnList) {
+                //     const defaultKeys = currentEditRow.childColumnList.map((a: any) => a.code);
+                //     setSelectedKeys(defaultKeys);
+                // }
+            }
+
+        }
+
+
+        const onSelectChange = (newSelectedRowKeys: React.Key[], selectedRows: any) => {
+            setSelectedKeys([...newSelectedRowKeys]);
+            set_selectedRows(selectedRows);
+
+        };
+
+        const save = async () => {
+            // console.log({selectedRows});
+            const arr = [...dataSource,...selectedRows];
+            const resp = await editData({
+                unitType:currentTabKey,
+                retainList:arr.map((a,index)=>({
+                    retainCode:a.retainCode,
+                    retainName:a.retainName,
+                    retainType:a.retainType,
+                    comment:a.comment,
+                    sort:index+1
+                }))
+            });
+            if (resp) {
+                set_ifShowModal(false);
+                tableRef.current?.reload();
+                message.success('操作成功!');
+            }
+        }
+
+        
+
+        useEffect(() => {
+            getFuncList();
+        }, [])
+
+        return (
+            <div className='TableSelecter'>
+                <Input placeholder={'请输入'} allowClear
+                    suffix={
+                        <IconFont type="iconsousuo" />
+                    }
+                    style={{ marginBottom: 8 }}
+                    onChange={(e) => {
+                        if (e.target.value.length != 0) {
+                            const result = datasource.filter(item => item.headerText.indexOf(e.target.value) != -1);
+                            set_showList(result);
+                        } else {
+                            set_showList(datasource);
+                        }
+                    }}
+                />
+                <div className='wrapper' style={{ maxHeight: 489, overflowY: 'scroll' }}>
+                    <BMSTable columns={tableSelecterColumn}
+                        options={{
+                            density: true,
+                            setting: {
+                                listsHeight: 100,
+                            },
+                        }}
+                        rowKey='retainCode'
+                        tableAlertRender={false}
+                        rowSelection={{
+                            // 自定义选择项参考: https://ant.design/components/table-cn/#components-table-demo-row-selection-custom
+                            // 注释该行则默认不显示下拉选项
+                            selectedRowKeys: selectedKeys,
+                            onChange: onSelectChange,
+                            checkStrictly: false
+                        }}
+                        dataSource={showList}
+                        pagination={false}
+
+                    />
+                </div>
+
+                <div className='footer'>
+                    <span className='cancel' onClick={() => set_ifShowModal(false)}>取消</span>
+                    <span className='ok' onClick={() => save()}>{`确认(${selectedKeys.length > 0 && selectedKeys.length})`}</span>
+                </div>
+            </div>
+
+        )
+    });
+
+
+    const updateTableSortHandle = async (data: any[]) => {
+        const resp = await editData({
+            unitType:currentTabKey,
+            retainList:data.map((a,index)=>({
+                retainCode:a.retainCode,
+                retainName:a.retainName,
+                retainType:a.retainType,
+                comment:a.comment,
+                sort:index+1
+            }))
+        });
+        if (resp) {
+            tableRef.current?.reload();
+        }
+    }
+
+    const onSortEnd = useRefFunction(
+        ({ oldIndex, newIndex }: { oldIndex: number; newIndex: number }) => {
+            if (oldIndex !== newIndex) {
+                const newData = arrayMoveImmutable([...dataSource], oldIndex, newIndex).filter((el) => !!el);
+
+                const updatedSortArr = newData.map((item: any, index: number) => ({ ...item, sort: index + 1 }))
+                set_dataSource([...updatedSortArr]);
+                updateTableSortHandle(updatedSortArr);
+            }
+        },
+    );
+
+    const DraggableContainer = (props: any) => (
+        <SortContainer
+            useDragHandle
+            disableAutoscroll
+            helperClass="row-dragging"
+            onSortEnd={onSortEnd}
+            {...props}
+        />
+    );
+
+    const DraggableBodyRow = (props: any) => {
+        const { className, style, ...restProps } = props;
+
+        // function findIndex base on Table rowKey props and should always be a right array index
+        const index = dataSource.findIndex((x) => x.id === restProps['data-row-key']);
+
+        return <SortableItem index={index} {...restProps} />;
+    };
+
+    const onTableSelectChange = (newSelectedRowKeys: React.Key[]) => {
+        set_tableSelectedKeys([...newSelectedRowKeys]);
+    };
+
+
+    useEffect(()=>{
+        if(currentTabKey){
+            set_tableDataFilterParams({...tableDataFilterParams,unitType:currentTabKey})
+        }
+    },[currentTabKey])
+
+    useEffect(() => {
+        getTabsHandle();
+    }, [])
+
+    return (
+        <BMSPagecontainer className='JobCateRetenSet' title={false}>
+            <ModalForm title={'添加保留项目'} width={400} submitter={{
+                render: (props, defaultDoms) => []
+            }} open={ifShowModal} modalProps={{
+                closable: false,
+            }}>
+                <TableSelecter
+                    ref={ref}
+                    record={undefined}
+                    tableSelecterColumn={tableSelecterColumn}
+                ></TableSelecter>
+            </ModalForm>
+            {
+                tabs.length > 0 && <Tabs
+                    defaultActiveKey={currentTabKey}
+                    onChange={(key)=>set_currentTabKey(key)}
+                    items={tabs}
+                />
+            }
+            <div className='toolBar'>
+                <div className='filter'>
+                    <div className='filterItem'>
+                        <span className='label' style={{ whiteSpace: 'nowrap' }}> 检索:</span>
+                        <Input placeholder={'请输入项目名'} allowClear width={160}
+                            suffix={
+                                <IconFont type="iconsousuo" 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'>
+                <a className='add' onClick={() => { set_ifShowModal(true) }}>添加</a>
+                </div>
+            </div>
+
+            <div style={{ marginTop: 16 }}>
+                <BMSTable columns={columns as ProColumns[]} actionRef={tableRef} rowKey='id' params={tableDataFilterParams} 
+                request={(params) => getTableData(params)} 
+                tableAlertRender={false}
+                rowSelection={ {
+                    onChange: onTableSelectChange,
+                }}
+                components={{
+                    body: {
+                        wrapper: DraggableContainer,
+                        row: DraggableBodyRow,
+                    },
+                }}
+                />
+            </div>
+
+            {
+                tableSelectedKeys.length > 0 && (
+                    <div className='bottomToolbar'>
+                        <span className='leftAccount'>已选择<a>{tableSelectedKeys.length}</a>项</span>
+                        <div className='btnGroup'>
+                            <span className='cancel' onClick={() => tableRef.current?.clearSelected && tableRef.current?.clearSelected()}>取消选择</span>
+                            <Popconfirm
+                                title="是否确认删除?"
+                                key="del"
+                                onConfirm={() => delTableData(tableSelectedKeys)}
+                            >
+                                <span className='batchDel'>批量删除</span>
+                            </Popconfirm>
+
+                        </div>
+                    </div>
+                )
+            }
+
+        </BMSPagecontainer>
+    )
+}

+ 74 - 0
src/pages/setting/projectSetting/jobCateRetenSet/service.ts

@@ -0,0 +1,74 @@
+/*
+ * @Author: code4eat awesomedema@gmail.com
+ * @Date: 2023-03-03 16:31:27
+ * @LastEditors: code4eat awesomedema@gmail.com
+ * @LastEditTime: 2024-04-26 15:42:53
+ * @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 { Key } from 'react';
+import { request } from 'umi';
+
+//获取table列表数据
+
+export const getReportListTableData = (params?: any) => {
+  return request('/performance/retain/unitTypeList', {
+    method: 'GET',
+    params: { ...params }
+  });
+};
+
+
+//获取职类
+
+export const getJobCateReq = () => {
+  return request('/performance/dict/getDictDataList', {
+    method: 'GET',
+    params:{
+      current:1,pageSize:100,typeCode:1
+    }
+  });
+};
+
+//编辑表格数据
+
+export const editData = (data: any) => {
+  return request('/performance/retain/unitTypeRetainSave', {
+    method: 'POST',
+    data
+  });
+};
+
+//删除表格操作
+export const delData = (ids:Key[]) => {
+  return request('/performance/retain/unitTypeRetainDelete', {
+    method: 'POST',
+    data:ids
+  });
+};
+
+
+
+
+//获取可添加保留项目
+
+export const getModalTableList = (unitType: string) => {
+  return request('/performance/retain/unitTypeRetainDict', {
+    method: 'GET',
+    params: { unitType }
+  });
+};
+
+
+
+
+
+
+
+
+
+
+

+ 134 - 0
src/pages/setting/projectSetting/jobCateRetenSet/style.less

@@ -0,0 +1,134 @@
+.TableSelecter {
+  .footer {
+       display: flex;
+       flex-direction:row;
+       justify-content: flex-end;
+       margin-bottom:-10px;
+       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;
+           }
+       }
+
+  }
+}
+
+.row-dragging {
+
+  .bms-ant-table-cell {
+    width: 130px;
+  }
+}
+
+
+.JobCateRetenSet {
+  padding: 16px;
+  padding-top: 0;
+  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;
+      }
+    }
+
+  }
+
+  .bottomToolbar {
+    display: flex;
+    flex-direction: row;
+    justify-content: space-between;
+    align-items: center;
+    position: fixed;
+    left: 200px;
+    bottom: 0;
+    padding: 0 16px;
+    width: calc(100% - 200px);
+    height: 48px;
+    background: #FFFFFF;
+    box-shadow: 0px -8px 16px 0px rgba(64, 85, 128, 0.1);
+
+    .leftAccount {
+      font-size: 14px;
+      font-family: SourceHanSansCN-Normal, SourceHanSansCN;
+      font-weight: 400;
+      color: #17181A;
+      line-height: 21px;
+
+    }
+
+    .btnGroup {
+      .cancel {
+        display: inline-block;
+        width: 80px;
+        height: 24px;
+        border-radius: 4px;
+        font-size: 14px;
+        text-align: center;
+        line-height: 23px;
+        margin-right: 8px;
+        cursor: pointer;
+        border: 1px solid #DAE2F2;
+      }
+
+      .batchDel {
+        display: inline-block;
+        width: 80px;
+        height: 24px;
+        border-radius: 4px;
+        font-size: 14px;
+        text-align: center;
+        line-height: 23px;
+        color: #FFFFFF;
+        cursor: pointer;
+        background: #3377FF;
+      }
+    }
+  }
+}

+ 237 - 0
src/pages/setting/projectSetting/retentionAssessmentProjectMana/index.tsx

@@ -0,0 +1,237 @@
+/*
+ * @Author: code4eat awesomedema@gmail.com
+ * @Date: 2023-03-03 11:30:33
+ * @LastEditors: code4eat awesomedema@gmail.com
+ * @LastEditTime: 2024-04-25 16:52:23
+ * @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 BMSPagecontainer from '@/components/BMSPageContainer';
+import { BMSTable } from '@/components/BMSTable';
+import { getComputeDate } from '@/pages/Home/service';
+import { getDataByKeyFromDic, getPubDicData } from '@/services/getDic';
+import { createFromIconfontCN } from '@ant-design/icons';
+
+import { ActionType, ProFormDependency, ProFormRadio } from '@ant-design/pro-components';
+import { ModalForm, ProFormCascader, ProFormDigit, ProFormSelect, ProFormText, ProFormTextArea } from '@ant-design/pro-form'
+import { ProColumns } from '@ant-design/pro-table';
+import { Form, Input, InputNumber, message, Popconfirm } from 'antd';
+import { useEffect, useRef, useState } from 'react'
+import { getSecondaryProjectDistriData } from '../secondaryProjectDistribute/service';
+
+
+import { addData, delData, editData, getProjectTableData } from './service';
+
+import './style.less';
+
+import '../../../../utils/zhongtaiB';
+import '../../../../utils/zhongtaiA'
+
+const IconFont = createFromIconfontCN({
+    scriptUrl: '',
+});
+
+const AddIconFont = createFromIconfontCN({
+    scriptUrl: '',
+});
+
+
+
+export default function RetentionAssessmentProjectMana() {
+
+    const [tableDataFilterParams, set_tableDataFilterParams] = useState<any | undefined>();
+    const [tableDataSearchKeywords, set_tableDataSearchKeywords] = useState<string>('');
+    const tableRef = useRef<ActionType>();
+
+
+
+
+    const columns: ProColumns[] = [
+
+        {
+            title: '项目代码',
+            dataIndex: 'code',
+        },
+        {
+            title: '项目名称',
+            dataIndex: 'name',
+        },
+        {
+            title: '项目类型',
+            dataIndex: 'typeName',
+        },
+        {
+            title: '说明',
+            dataIndex: 'comment',
+        },
+        {
+            title: '操作',
+            key: 'option',
+            width: 200,
+            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 getProjectTableData(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 result = {
+            name: formVal.name,
+            type: formVal.type,
+            comment: formVal.comment,
+        }
+
+        let flag = true;
+
+        if (type == 'ADD') {
+            const resp = await addData({ ...result });
+            if (resp) {
+                tableRef.current?.reload();
+                message.success('操作成功!');
+            }
+        }
+        if (type == 'EDIT') {
+            const resp = await editData({ id: formVal.id, ...result });
+            if (resp) {
+                tableRef.current?.reload();
+                message.success('操作成功!');
+            }
+        }
+
+        return flag;
+    }
+
+    const UpDataActBtn = ({ record, type }: { record: any, type: 'EDIT' | 'ADD'}) => {
+
+        return (
+            <ModalForm
+                title={`${type == 'EDIT' ? '编辑' : '新增'}`}
+                width={400}
+                initialValues={type == 'EDIT' ? { ...record } : {type:1}}
+                trigger={
+                    type == 'EDIT' ? <a key="edit" >编辑</a> : <span className='add'>新增</span>
+                }
+                onFinish={(val) => {
+                    return updateTable(type == 'EDIT' ? { ...record, ...val } : { ...val }, type);
+                }}
+                modalProps={{ destroyOnClose: true }}
+            >
+                <ProFormText
+                    name="name"
+                    label="项目名称:"
+                    placeholder="请输入"
+                    rules={[{ required: true, message: '名称不能为空!' }]}
+                />
+                <ProFormRadio.Group
+                    name="type"
+                    label="项目类型"
+                    options={[
+                        {
+                            label: '系数',
+                            value: 1,
+                        },
+                        {
+                            label: '数值',
+                            value: 2,
+                        }
+                    ]}
+                    rules={[{ required: true, message: '项目类型不能为空!' }]}
+                />
+                <ProFormTextArea
+                    name="comment"
+                    label="说明:"
+                    placeholder="请输入"
+                />
+            </ModalForm>
+        )
+    }
+
+    const tableDataSearchHandle = (paramName: string) => {
+
+        set_tableDataFilterParams({
+            ...tableDataFilterParams,
+            [`${paramName}`]: tableDataSearchKeywords
+        })
+    }
+
+    return (
+        <BMSPagecontainer className='RetentionAssessmentProjectMana' title={false}>
+            <div className='toolBar'>
+                <div className='filter'>
+                    <div className='filterItem'>
+                        <span className='label' style={{ whiteSpace: 'nowrap' }}> 检索:</span>
+                        <Input placeholder={'请输入项目名称'} allowClear
+                            suffix={
+                                <IconFont type="iconsousuo" 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'>
+                    {/* <span className='getDataBtn' onClick={() => getDataBySQL()}>获取</span> */}
+                    <UpDataActBtn record type='ADD' />
+                </div>
+            </div>
+            <div style={{ marginTop: 16 }}>
+                <BMSTable columns={columns as ProColumns[]} actionRef={tableRef} rowKey='id' params={tableDataFilterParams} request={(params) => getTableData(params)} />
+            </div>
+        </BMSPagecontainer>
+    )
+}

+ 74 - 0
src/pages/setting/projectSetting/retentionAssessmentProjectMana/service.ts

@@ -0,0 +1,74 @@
+/*
+ * @Author: code4eat awesomedema@gmail.com
+ * @Date: 2023-03-03 16:31:27
+ * @LastEditors: code4eat awesomedema@gmail.com
+ * @LastEditTime: 2024-04-25 16:48:33
+ * @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 getProjectTableData = (params?: any) => {
+  return request<{
+    current: number;
+    list: any[];
+    pageSize: number;
+    totalCount: number;
+    totalPage: number;
+  }>('/performance/retain/list', {
+    method: 'GET',
+    params: { ...params }
+  });
+};
+
+//新增表格数据
+export type AddTableDataType = {
+  "name": string, //名称
+  "type": number, //1系数2数值
+  "comment": string, 
+}
+export const addData = (data: AddTableDataType) => {
+  return request('/performance/retain/save', {
+    method: 'POST',
+    data
+  });
+};
+
+
+
+//编辑表格数据
+
+export const editData = (data: AddTableDataType & { id: number }) => {
+  return request('/performance/retain/update', {
+    method: 'POST',
+    data
+  });
+};
+
+
+
+//删除表格操作
+export const delData = (id: string) => {
+  return request('/performance/retain/delete', {
+    method: 'POST',
+    params: { id }
+  });
+};
+
+
+
+
+
+
+
+
+
+

+ 102 - 0
src/pages/setting/projectSetting/retentionAssessmentProjectMana/style.less

@@ -0,0 +1,102 @@
+
+.NonAssessmentProjectMana-ModalForm {
+  .item {
+    display: flex;
+    flex-direction: row;
+    justify-content: space-between;
+    align-items: center;
+
+    .delIcon {
+      position: relative;
+      top:3px;
+      cursor: pointer;
+      display:flex;
+      justify-content: center;
+      align-items: center;
+      width: 24px;
+      height: 24px;
+      background: #FAFCFF;
+      border-radius: 4px;
+      border: 1px solid #DAE2F2;
+      .iconshanchu {
+        color: #17181A !important;
+        svg {
+          color: #17181A !important;
+        }
+      }
+    }
+  }
+  .addBtn {
+    cursor: pointer;
+    display: flex;
+    flex-direction: row;
+    justify-content: center;
+    align-items: center;
+    width: 320px;
+    height: 40px;
+    background: #FFFFFF;
+    border-radius: 4px;
+    color: #3376FE;
+    border: 1px dashed #CFD6E6;
+
+    &>span {
+       margin-left: 8px;
+    }
+  }
+}
+
+.RetentionAssessmentProjectMana {
+  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 {
+      .getDataBtn {
+        display: inline-block;
+        text-align: center;
+        width: 56px;
+        height: 24px;
+        cursor: pointer;
+        line-height: 24px;
+        background: #FAFCFF;
+        border-radius: 4px;
+        margin-right: 8px;
+        border: 1px solid #DAE2F2;
+      }
+      .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;
+      }
+    }
+
+  }
+}

+ 2 - 2
src/pages/setting/projectSetting/secondaryDistriGroupSet/index.tsx

@@ -2,7 +2,7 @@
  * @Author: code4eat awesomedema@gmail.com
  * @Date: 2023-03-03 11:30:33
  * @LastEditors: code4eat awesomedema@gmail.com
- * @LastEditTime: 2024-04-01 16:54:11
+ * @LastEditTime: 2024-06-20 15:41:00
  * @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
  */
@@ -925,7 +925,6 @@ const SecondaryDistriGroupSet = () => {
                 <div style={{ marginTop: 16 }}>
                     {(currentSelectedType && currentTabKey) && <BMSTable actionRef={tableRef} columns={tableColumns}
                         rowKey='id'
-                        pagination={false}
                         tableAlertRender={false}
                         rowSelection={{
                             // 自定义选择项参考: https://ant.design/components/table-cn/#components-table-demo-row-selection-custom
@@ -948,6 +947,7 @@ const SecondaryDistriGroupSet = () => {
                                 </Space>
                             );
                         }}
+                        scroll={{y:`calc(100vh - 270px)`}}
                         params={tableDataFilterParams}
                         dataSource={dataSource}
                         //request={currentTabKey == '1'?(params, sort, filter) => getTableData(params, sort, filter):undefined}

+ 31 - 34
src/pages/setting/reportSet/diySqlMana/index.tsx

@@ -2,7 +2,7 @@
  * @Author: code4eat awesomedema@gmail.com
  * @Date: 2023-03-03 11:30:33
  * @LastEditors: code4eat awesomedema@gmail.com
- * @LastEditTime: 2024-04-02 10:54:37
+ * @LastEditTime: 2024-04-12 11:22:16
  * @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
  */
@@ -66,7 +66,7 @@ export default function DiySqlMana() {
         },
         {
             title: 'SQL代码',
-            width:150,
+            width: 150,
             ellipsis: true,
             dataIndex: 'sqlCode',
         },
@@ -83,7 +83,7 @@ export default function DiySqlMana() {
         },
         {
             title: '启用',
-            width:80,
+            width: 80,
             dataIndex: 'status',
             renderText: (_: any, record: any) => {
 
@@ -114,7 +114,7 @@ export default function DiySqlMana() {
 
 
     const getTableData = async (params: any) => {
-        const resp = await getSqlListTableData({...params});
+        const resp = await getSqlListTableData({ ...params });
         if (resp) {
             set_dataSource(resp.list);
             set_showList(resp.list);
@@ -171,27 +171,27 @@ export default function DiySqlMana() {
         }
 
         if (type == 'ADD_NEXT') {
-            const {sort} = formVal;
+            const { sort } = formVal;
 
             const temp = [...dataSource];
 
-            const index = temp.findIndex(a=>a.sort == sort);
+            const index = temp.findIndex(a => a.sort == sort);
 
-            const objectToInsert =  {
+            const objectToInsert = {
                 sqlCode: formVal.sqlCode,
                 sql: formVal.sql,
                 sqlDefinition: formVal.sqlDefinition,
-                sqlType:currentSelectedType.code,
-                status:1
+                sqlType: currentSelectedType.code,
+                status: 1
             }
-        
-            temp.splice(index+1,0, objectToInsert);
-            const arr = temp.map((a,index)=>({...a,sort:index+1}));  
+
+            temp.splice(index + 1, 0, objectToInsert);
+            const arr = temp.map((a, index) => ({ ...a, sort: index + 1 }));
             updateTableSortHandle(arr);
 
 
         }
-        
+
         if (type == 'EDIT') {
 
             const resp = await editData({
@@ -218,42 +218,39 @@ export default function DiySqlMana() {
         return (
             <ModalForm
                 title={`${type == 'EDIT' ? '编辑' : type == 'ADD_NEXT' ? '插入' : '新增'}自定义SQL`}
-                width={880}
+                width={1200}
                 formRef={formRef}
                 initialValues={type == 'EDIT' ? { ...record } : {}}
                 trigger={
-                    type == 'EDIT' ? <a key="edit" >编辑</a> :type == 'ADD_NEXT'?<a>插入</a>: <span className='add'>新增</span>
+                    type == 'EDIT' ? <a key="edit" >编辑</a> : type == 'ADD_NEXT' ? <a>插入</a> : <span className='add'>新增</span>
                 }
                 onFinish={(val) => {
-                    return updateTable(type == 'EDIT' ? { ...record, ...val } : type =='ADD_NEXT'?{...record, ...val}: { ...val }, type);
+                    return updateTable(type == 'EDIT' ? { ...record, ...val } : type == 'ADD_NEXT' ? { ...record, ...val } : { ...val }, type);
                 }}
                 modalProps={{ destroyOnClose: true }}
                 colProps={{ span: 24 }}
                 grid
             >
-                <div className='formContent' style={{ display: 'flex', flexDirection: 'row', alignContent: 'flex-start' }}>
-                    <div className='left' style={{ width: 240, marginRight: 16 }}>
-                        {
-                            type != 'EDIT' && (
-                                <ProFormText
-                                    name="sqlCode"
-                                    label="SQL代码:"
-                                    placeholder="请输入"
-                                    rules={[{ required: true, message: 'SQL代码不能为空!' }]}
-                                />
-                            )
-                        }
-                        <ProFormTextArea placeholder="请输入" name={'sqlDefinition'} label='SQL说明:' rules={[{ required: true, message: '说明不能为空!' }]} />
+                <div className='formContent' style={{ display: 'flex',width:'100%', flexDirection: 'row', alignContent: 'flex-start',overflow:'hidden'}}>
+                    <div className='left' style={{ width: 240,minWidth:240, marginRight: 16 }}>
+                        <ProFormText
+                            name="sqlCode"
+                            label="SQL代码:"
+                            placeholder="请输入"
+                            disabled={type == 'EDIT'}
+                            rules={[{ required: true, message: 'SQL代码不能为空!' }]}
+                        />
+                        <ProFormTextArea fieldProps={{rows:18}} placeholder="请输入" name={'sqlDefinition'} label='SQL说明:' rules={[{ required: true, message: '说明不能为空!' }]} />
 
                     </div>
-                    <div className='right' style={{ width: 'calc(880px - 288px)' }}>
+                    <div className='right' style={{ width: 'calc(1200px - 288px)',marginBottom:'-15px'}}>
                         {/* <ProFormTextArea placeholder="请输入" name={'sql'} label='SQL语句:' rules={[{ required: true, message: 'SQL不能为空!' }]} /> */}
                         <SQLEditor
                             name="sql"
                             label="SQL语句:"
                             rules={[{ required: true, message: 'SQL不能为空!' }]}
                             form={formRef}
-                            height={'230px'}
+                            height={'435px'}
                         />
                     </div>
                 </div>
@@ -261,8 +258,8 @@ export default function DiySqlMana() {
         )
     }
 
-    const tableDataSearchHandle = (key:string) => {
-        const result = dataSource.filter((a)=>a.sqlDefinition.indexOf(tableDataSearchKeywords) != -1);
+    const tableDataSearchHandle = (key: string) => {
+        const result = dataSource.filter((a) => a.sqlDefinition.indexOf(tableDataSearchKeywords) != -1);
         set_showList(result);
     }
 
@@ -361,7 +358,7 @@ export default function DiySqlMana() {
                                     set_tableDataSearchKeywords(e.target.value);
                                     if (e.target.value.length == 0) {
                                         set_tableDataSearchKeywords('');
-                                        getTableData({sqlType:currentSelectedType.code});
+                                        getTableData({ sqlType: currentSelectedType.code });
                                     }
                                 }}
 

+ 39 - 4
src/pages/setting/reportSet/reportSetting/index.tsx

@@ -2,7 +2,7 @@
  * @Author: code4eat awesomedema@gmail.com
  * @Date: 2023-03-03 11:30:33
  * @LastEditors: code4eat awesomedema@gmail.com
- * @LastEditTime: 2024-03-01 14:48:43
+ * @LastEditTime: 2024-07-19 15:53:18
  * @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
  */
@@ -15,7 +15,7 @@ import { BMSTable } from '@/components/BMSTable';
 
 import { createFromIconfontCN } from '@ant-design/icons';
 import { ActionType, useRefFunction } from '@ant-design/pro-components';
-import { ModalForm, ProFormRadio, ProFormSelect, ProFormText } from '@ant-design/pro-form';
+import { ModalForm, ProFormDigit, ProFormRadio, ProFormSelect, ProFormText } from '@ant-design/pro-form';
 import { ProColumns } from '@ant-design/pro-table';
 import { Dropdown, Input, MenuProps, message, Modal, Switch, Tooltip } from 'antd';
 import { ColumnsType, TableRowSelection } from 'antd/es/table/interface';
@@ -60,6 +60,7 @@ const ReportSetting = () => {
     const SortContainer = SortableContainer((props: any) => <tbody {...props} />);
 
     const [currentEditLeftData, set_currentEditLeftData] = useState<any | undefined>(undefined);
+    const [currentEditRow,set_currentEditRow] = useState<any>(undefined);
 
     const [ifEditTable, set_ifEditTable] = useState(false);
 
@@ -82,6 +83,28 @@ const ReportSetting = () => {
             dataIndex: 'columnHeaderText',
 
         },
+        {
+            title: '列宽',
+            width: 120,
+            dataIndex: 'width',
+            render: (num: any, record: any) => {
+                let inputNum:any = 0;
+    
+                return (
+                    <div style={{display:'flex',flexDirection:'row',justifyContent:'flex-start',alignItems:'center'}}>
+                        {currentEditRow&&currentEditRow.id == record.id&&<ProFormDigit noStyle width={70} fieldProps={{
+                            defaultValue:num,
+                            onChange(value) {
+                                inputNum = value?value:100
+                            },
+                        }}  />}
+                        {(!currentEditRow||(currentEditRow.id != record.id))&&<div style={{width:70}}>{num}</div>}
+                        
+                        {ifEditTable && <IconFont style={{color:'#17181A',cursor:'pointer',marginLeft:10}} type={(currentEditRow&&currentEditRow.id == record.id)?'icon-queren':'icon-xiugai'} onClick={()=>editColWid(record,inputNum)} />}
+                    </div>
+                )
+            }
+        },
         {
             title: '主键',
             width: 120,
@@ -138,7 +161,19 @@ const ReportSetting = () => {
 
     const [tableColumn, set_tableColumn] = useState<ProColumns[] | any[]>(column);
 
-
+    const editColWid = (record:any,num:number)=>{
+          set_currentEditRow(currentEditRow?undefined:record);
+          if(currentEditRow){
+            const newData = temp_dataSource.map((a: any) => {
+                if (a.id == record.id) {
+                    return { ...a, width:num}
+                }
+                return a
+            });
+    
+            setDataSource([...newData]);
+          }
+    }
 
 
     const getTableData = async (params: any) => {
@@ -604,7 +639,7 @@ const ReportSetting = () => {
         } else {
             set_tableColumn(column);
         }
-    }, [ifEditTable]);
+    }, [ifEditTable,currentEditRow]);
 
     useEffect(() => {
         temp_dataSource = [...dataSource];

+ 24 - 8
src/pages/static/index.tsx

@@ -1,7 +1,7 @@
 /*
  * @Author: your name
  * @Date: 2022-03-03 18:04:40
- * @LastEditTime: 2023-05-29 14:21:27
+ * @LastEditTime: 2024-05-10 17:23:52
  * @LastEditors: code4eat awesomedema@gmail.com
  * @Description: 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
  * @FilePath: /KC-MiddlePlatform/src/pages/platform/setting/reports/index.tsx
@@ -13,9 +13,12 @@ import { Skeleton } from 'antd';
 import BMSPagecontainer from '@/components/BMSPageContainer';
 import { useModel } from '@umijs/max';
 
+import { getPlatFormParams } from './service';
+import { getUrlParams } from '@/utils/tooljs';
+
 
 export default () => {
-  const { initialState, setInitialState } = useModel('@@initialState');
+  const { initialState } = useModel('@@initialState');
   const [specialPageUrl, setspecialPageUrl] = useState<string | undefined>(undefined);
   const [loading, setloading] = useState(false);
   const { pathname } = location;
@@ -27,25 +30,38 @@ export default () => {
     setloading(false);
   };
 
+  const getHost = async ()=>{
+       const resp = await getPlatFormParams('1547394914533380096','1788836132977512448');
+       if(resp){
+          return resp
+       }
+  }
+
   const loadPage = ()=>{
+
     if (initialState && initialState.spacicalPageParamsType && initialState.userData) {
       const {
         spacicalPageParamsType,
         userData: { youshuToken },
       } = initialState;
 
-      console.log({spacicalPageParamsType});
+  
       
       const spacialPage = spacicalPageParamsType.filter((t) => `/budgetManaSystem${t.path}` == pathname);
 
-      //console.log({pathname,spacialPage,spacicalPageParamsType,initialState});
-
+  
       if (spacialPage.length > 0) {
         //当前页面属于有数数据展示页面
-        console.log(`${spacialPage[0].url}`);
-
         const url = `${spacialPage[0].url}`;
-        setspecialPageUrl(url);
+
+        const {noMenu=undefined,noTopbar=undefined} = getUrlParams(`http://localhost:8000${url}`);
+        if(noMenu == 'true'&&noTopbar == 'true'){
+          getHost().then((val)=>{
+                setspecialPageUrl(`${val.value}${url}&userData=${localStorage.getItem('userData')}`);
+          })
+        }else{
+          setspecialPageUrl(url);
+        }
       }
     }
   }

+ 22 - 0
src/pages/static/service.ts

@@ -0,0 +1,22 @@
+/*
+ * @Author: code4eat awesomedema@gmail.com
+ * @Date: 2024-05-10 15:56:10
+ * @LastEditors: code4eat awesomedema@gmail.com
+ * @LastEditTime: 2024-05-10 16:24:53
+ * @FilePath: /BudgetManaSystem/src/pages/static/service.ts
+ * @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
+ */
+
+
+
+import { request } from '@@/plugin-request';
+
+
+//获取中台指定参数
+export const getPlatFormParams = (systemId:string,code: string) => {
+
+    return request('/centerSys/parameter/getParameterByCode', {
+        method: 'GET',
+        params: { systemId,parameterCode:code }
+    })
+}

+ 10 - 1
src/services/getDic.ts

@@ -2,7 +2,7 @@
  * @Author: code4eat awesomedema@gmail.com
  * @Date: 2023-04-20 14:06:17
  * @LastEditors: code4eat awesomedema@gmail.com
- * @LastEditTime: 2023-05-11 17:27:16
+ * @LastEditTime: 2024-04-25 18:00:49
  * @FilePath: /BudgetManaSystem/src/services/getDic.ts
  * @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
  */
@@ -24,6 +24,15 @@ export const getPubDicData =  () => {
     });    
 }
 
+//根据系统id+字典类型获取字典数据
+export const getDicDataBySysId =  (systemId:string,dictType?:string) => {
+    
+  return request('/centerSys/sysdictdata/getDictByDictType', {
+      method: 'GET',
+      params:{systemId,dictType}
+  });    
+}
+
 // 筛选指定的字典数据
 
 export const getDataByKeyFromDic = (dicData:any[],key: string) => {

+ 36 - 3
src/utils/tooljs.ts

@@ -2,11 +2,13 @@
  * @Author: code4eat awesomedema@gmail.com
  * @Date: 2023-02-20 14:31:06
  * @LastEditors: code4eat awesomedema@gmail.com
- * @LastEditTime: 2024-01-04 19:14:03
+ * @LastEditTime: 2024-05-16 16:22:16
  * @FilePath: /BudgetManaSystem/src/utils/tooljs.js
  * @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
  */
 
+import { string } from "mathjs";
+
 
 let parent:any = undefined;
 
@@ -162,12 +164,12 @@ export const  convertToColumns = (json: JsonStructure[]): Column[] => {
       dataIndex: item.code,
       key: item.code,
     };
-
+    
     if (item.expand === 1 && item.childTitle && item.childTitle.length > 0) {
       column.children = convertToColumns(item.childTitle);
     }
 
-    return column;
+    return column;                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                              
   });
 }
 
@@ -221,5 +223,36 @@ export const  copyToClipboard = (text:string) => {
 }
 
 
+export const getUrlParams = (url: string): { [key: string]: string | string[] } => {
+  // 创建一个URL对象(假设URL是有效的)
+  const urlObj = new URL(url);
+  // 获取所有查询字符串
+  const queryParams = new URLSearchParams(urlObj.search);
+  const params: { [key: string]: string | string[] } = {};  // 定义具有索引签名的对象
+
+  // 遍历查询字符串并将它们添加到params对象中
+  for (const [key, value] of queryParams) {
+      // 检查对象中是否已经存在这个键
+      if (params.hasOwnProperty(key)) {
+          // 如果这个键已经有值,将它转换为数组(如果尚未转换的话)
+          if (!Array.isArray(params[key])) {
+              params[key] = [params[key] as string];  // 作为 string 断言是安全的
+          }
+          // 添加新的值到数组
+          (params[key] as string[]).push(value);
+      } else {
+          // 如果这个键还没有值,直接添加
+          params[key] = value;
+      }
+  }
+
+  return params;
+}
+
+
+
+
+
+
 
 

Разлика између датотеке није приказан због своје велике величине
+ 2 - 2
src/utils/zhongtaiC.js


BIN
static/distribute.png


BIN
static/textImg_xi.png


BIN
static/textImg_zhi.png


BIN
绩效管理系统_2024_04_01.zip


Неке датотеке нису приказане због велике количине промена