Forráskód Böngészése

添加V1.2相关界面

code4eat 2 éve
szülő
commit
e63f0e3393

+ 12 - 1
config/config.ts

@@ -1,7 +1,7 @@
 /*
  * @Author: your name
  * @Date: 2022-01-07 10:04:20
- * @LastEditTime: 2023-03-09 09:54:36
+ * @LastEditTime: 2023-03-21 09:31:27
  * @LastEditors: code4eat awesomedema@gmail.com
  * @Description: 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
  * @FilePath: /KC-MiddlePlatform/config/config.ts
@@ -120,6 +120,17 @@ export default defineConfig({
               path: '/platform/setting/paramsMana',
               component: '@/pages/platform/setting/paramsMana/index.tsx',
             },
+            {
+              path: '/platform/setting/systemNavMana',
+              component: '@/pages/platform/setting/systemNavMana/index.tsx',
+            },
+            {
+              path: '/platform/setting/notificationTemplate',
+              component: '@/pages/platform/setting/notificationTemplate/index.tsx',
+            },
+
+
+
             {
               path: '/platform/setting/reports/0',
               component: '@/pages/platform/setting/reports/index.tsx',

+ 1 - 0
package.json

@@ -32,6 +32,7 @@
     "antd": "^4.21.6",
     "axios": "^1.3.4",
     "cross-env": "^7.0.3",
+    "lodash": "^4.17.21",
     "password-quality-calculator": "^1.0.4",
     "react": "16.x",
     "react-dev-inspector": "^1.1.1",

BIN
public/images/treenode_collapse.png


BIN
public/images/treenode_open.png


+ 22 - 0
src/components/kcTable/style.less

@@ -9,7 +9,12 @@
   }
   .kcmp-ant-table-thead>tr>th {
     background: #EEF3FA;
+    padding: 9px 8px !important;
     border-bottom: 1px solid #DAE2F2;
+
+    &:first-child {
+         padding-left: 16px !important;
+    }
   }
 
   .kcmp-ant-table-thead > tr > th:not(:last-child):not(.kcmp-ant-table-selection-column):not(.kcmp-ant-table-row-expand-icon-cell):not([colspan])::before {
@@ -19,12 +24,29 @@
 
   .kcmp-ant-table-tbody>tr>td {
     border-bottom: 1px solid #DAE2F2;
+    padding: 9px 8px !important;
+    &:first-child {
+      padding-left: 16px !important;
+    }
+
+    
   }
 
   .kcmp-ant-table-tbody > tr.kcmp-ant-table-row:hover > td, .kcmp-ant-table-tbody > tr > td.kcmp-ant-table-cell-row-hover {
         background: #F5F7FA;
   }
 
+  .kcmp-ant-table-tbody {
+       .kcmp-ant-table-expanded-row {
+            &>td {
+                padding: 0 !important;
+                &:first-child {
+                    padding-left: 0 !important;
+                }
+            }
+       }
+  }
+
   .kcmp-ant-table-row {
        .kcmp-ant-space {
         gap: 8px !important;

+ 154 - 3
src/global.less

@@ -107,11 +107,14 @@ input {
     .kcmp-ant-input-number-handler {
       border-left: 1px solid #CFD7E6;
     }
+
     .kcmp-ant-input-number-handler-down {
       border-top: 1px solid #CFD7E6;
     }
-    .kcmp-ant-input-number-handler-up-inner, .kcmp-ant-input-number-handler-down-inner {
-         color: #99A6BF;
+
+    .kcmp-ant-input-number-handler-up-inner,
+    .kcmp-ant-input-number-handler-down-inner {
+      color: #99A6BF;
     }
   }
 
@@ -130,6 +133,15 @@ input {
 }
 
 
+.kcmp-ant-table-thead>tr>th {
+  border-bottom: none;
+
+  &::before {
+    display: none;
+  }
+}
+
+
 /**
     Select
 **/
@@ -180,6 +192,24 @@ input {
        ModalForm
 **/
 
+.kcmp-ant-modal-confirm-body .kcmp-ant-modal-confirm-content {
+  margin-top: 16px;
+
+  .kcmp-ant-transfer {
+    .kcmp-ant-transfer-list {
+      border: 1px solid #DAE2F2;
+      border-radius: 4px;
+
+      .kcmp-ant-transfer-list-header {
+        border-bottom: 1px solid #DAE2F2;
+      }
+    }
+  }
+}
+
+
+
+
 .kcmp-ant-modal {
   padding-bottom: 0;
   background: #FFFFFF;
@@ -201,9 +231,24 @@ input {
     }
 
     .kcmp-ant-modal-body {
-      padding: 0;
+      padding: 16px;
       padding-left: 16px;
       padding-right: 16px;
+
+      .kcmp-ant-modal-confirm-body-wrapper {
+        .kcmp-ant-modal-confirm-btns {
+          .kcmp-ant-btn {
+            height: 24px;
+            line-height: 13px;
+            border-radius: 4px;
+            padding: 5px 14px;
+            font-size: 14px;
+            font-family: SourceHanSansCN-Normal, SourceHanSansCN;
+            font-weight: 400;
+            box-sizing: border-box;
+          }
+        }
+      }
     }
 
     .kcmp-ant-modal-footer {
@@ -258,3 +303,109 @@ div::-webkit-scrollbar-corner {
 
 
 //--------------------
+
+
+//Transfer
+
+
+.kcmp-ant-modal-body {
+  padding-top: 0 !important;
+  .TableTransfer {
+    .kcmp-ant-transfer-operation {
+      align-self: self-end;
+      margin-bottom: 30px;
+
+      button {
+        //color: #17181A;
+        width: 24px;
+        height: 40px !important;
+        // background: #FAFCFF;
+        border-radius: 4px;
+        border: 1px solid #DAE2F2;
+      }
+    }
+
+    .kcmp-ant-transfer-list-header {
+      .anticon {
+        color: #99A6BF;
+      }
+
+      .kcmp-ant-transfer-list-header-selected {
+        color: #515866;
+      }
+    }
+
+    .kcmp-ant-transfer-list-body {
+      .kcmp-ant-transfer-list-body-search-wrapper {
+        padding: 8px;
+
+        .kcmp-ant-input-prefix {
+          .anticon-search {
+            color: #99A6BF;
+          }
+        }
+      }
+    }
+
+    .kcmp-ant-table {
+      .kcmp-ant-table-container {
+        .kcmp-ant-table-content {
+          .kcmp-ant-table-thead {
+
+            &>tr>th {
+              font-size: 14px;
+              font-family: SourceHanSansCN-Medium, SourceHanSansCN;
+              font-weight: 500;
+              color: #17181A;
+              background: #EEF3FA;
+              padding: 4px 8px !important;
+            }
+          }
+        }
+      }
+    }
+  }
+
+}
+
+
+//checkbox
+
+.kcmp-ant-checkbox {
+  .kcmp-ant-checkbox-inner {
+    border: 1px solid #DAE2F2;
+    border-radius: 4px;
+  }
+}
+
+.kcmp-ant-checkbox+span {
+  padding-left: 4px;
+  padding-right: 4px;
+}
+
+
+//drawer
+
+
+.kcmp-ant-drawer-body {
+  padding: 16px;
+}
+
+
+//Picker
+
+.kcmp-ant-picker {
+  padding: 0px 11px !important;
+  border-radius: 4px;
+  border: 1px solid #CFD7E6;
+  .kcmp-ant-picker-input {
+         input {
+             &::placeholder {
+              color: #99A6BF;
+             }
+         }
+  }
+  .kcmp-ant-picker-suffix {
+    color: #99A6BF;
+  }
+}

+ 559 - 14
src/pages/platform/setting/hospManage/index.tsx

@@ -1,24 +1,33 @@
 /*
  * @Author: your name
  * @Date: 2022-01-13 15:22:48
- * @LastEditTime: 2023-03-10 14:59:09
+ * @LastEditTime: 2023-03-17 16:01:54
  * @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/hospManage/index.tsx
  */
 
-import { FC, useState } from 'react';
+import { FC, Key, useEffect, useMemo, useState } from 'react';
 import { hospManageModelState, ConnectProps, Loading, connect } from 'umi';
-import { Button, Divider, Popconfirm } from 'antd';
+import { Button, Checkbox, Divider, Drawer, Dropdown, Input, Popconfirm, Switch, Table, Tree, TreeProps } from 'antd';
 import KCTable from '@/components/kcTable';
 import type { ProColumns } from '@ant-design/pro-table';
-import { getAllHosp, getHospYoushuAccounts, hospInit } from '@/service/hospList';
+import { getAllHosp, getHospYoushuAccounts, getMenuRelaPerm, hospInit, saveHospMenuApiPerm, setReadOnly } from '@/service/hospList';
 import { TableRequestParamsType } from '@/typings';
 import ActModal from './modals/modal';
 import './style.less';
 import { TableListItem } from './typings';
 import { getSpacifyHospMenu } from '@/service/menu';
 import { KCInput } from '@/components/KCInput';
+import { createFromIconfontCN, DownOutlined } from '@ant-design/icons';
+import { DrawerForm } from '@ant-design/pro-form';
+import { DataNode } from 'antd/es/tree';
+import { getTableDataRequest, getTreeData, getTreeDataRespType } from '../systemNavMana/service';
+
+import expandedIcon from '../../../../../public/images/treenode_open.png';
+import closeIcon from '../../../../../public/images/treenode_collapse.png';
+import { getDeepestTreeData } from '@/utils';
+import { CheckboxValueType } from 'antd/lib/checkbox/Group';
 
 export enum TableActType {
   NOACT,
@@ -29,6 +38,12 @@ export enum TableActType {
   BINDACCOUNT, //绑定有数账号
 }
 
+const { DirectoryTree } = Tree;
+
+const SearchIcon = createFromIconfontCN({
+  scriptUrl: '//at.alicdn.com/t/c/font_1927152_g1njmm1kh7b.js',
+});
+
 interface PageProps extends ConnectProps {
   hospManageModel: hospManageModelState;
   loading: boolean;
@@ -40,6 +55,10 @@ const HospManage: FC<PageProps> = ({ hospManageModel: state, dispatch }) => {
   const [tableDataFilterParams, set_tableDataFilterParams] = useState<any | undefined>();
   const [tableDataSearchKeywords, set_tableDataSearchKeywords] = useState<string>('');
 
+  const [drawerVisible, set_drawerVisible] = useState(false);
+
+
+
   const columns: ProColumns<TableListItem>[] = [
     {
       title: 'ID',
@@ -100,7 +119,7 @@ const HospManage: FC<PageProps> = ({ hospManageModel: state, dispatch }) => {
     // },
     {
       title: '操作',
-      width: 300,
+      width: 230,
       key: 'option',
       valueType: 'option',
       render: (text, record) => [
@@ -123,16 +142,529 @@ const HospManage: FC<PageProps> = ({ hospManageModel: state, dispatch }) => {
           菜单
         </a>,
         <Divider key="3" type="vertical" style={{ margin: '0 1px' }} />,
-        <a key="link4" onClick={() => youshuAccountBind(record)}>
-          报告
-        </a>,
-        <a key="link5" onClick={() => initHospData(record)}>
-          初始化
-        </a>,
+        <Dropdown key='4' menu={{
+          items: [
+            { key: '1', label: <a key="link4" onClick={() => youshuAccountBind(record)}>报告</a> },
+            { key: '2', label: <DrawerActBtn record={record} /> },
+            { key: '3', label: <a key="link5" onClick={() => initHospData(record)}>初始化</a> },
+            { key: '4', label: <a key="link6" onClick={() => setOnlyRead(record)}>初始化只读</a> },
+          ]
+        }}>
+          <a>
+            更多 <DownOutlined />
+          </a>
+        </Dropdown>,
       ],
     },
   ];
 
+
+
+  const DrawerActBtn = ({ record }: { record: any }) => {
+
+    const [treeData, set_treeData] = useState<getTreeDataRespType[]>([]);
+    const [currentSelectedTreeNode, set_currentSelectedTreeNode] = useState<any | undefined>(undefined);
+    const [drawerTablereload, set_drawerTablereload] = useState(false);
+    const [expandedKeys, setExpandedKeys] = useState<React.Key[]>([]);
+    const [searchValue, setSearchValue] = useState('');
+    const [autoExpandParent, setAutoExpandParent] = useState(true);
+    const [drawerTableDataFilterParams, set_drawerTableDataFilterParams] = useState<any | undefined>();
+
+    const [checkBoxCodes, set_checkBoxCodes] = useState<any[]>([]);
+
+
+    const [drawerTableDataSearchKeywords, set_drawerTableDataSearchKeywords] = useState<string>('');
+
+    const [checkedTableMenuIds, set_checkedTableMenuIds] = useState<any[]>([]);
+
+    const columnsData: ProColumns<TableListItem>[] = [
+      {
+        title: '名称',
+        dataIndex: 'name',
+        width: 300,
+        ellipsis: true
+      },
+      {
+        title: '类型',
+        width: 50,
+        dataIndex: 'contentType',
+        key: 'contentType',
+        render: (_: any, record: any) => {
+          //console.log({ record });
+          switch (record.type) {
+            case 0:
+              return '目录'
+            case 1:
+              return '菜单'
+            case 2:
+              return 'api'
+            case 3:
+              return '系统'
+            case 4:
+              return '有数bi'
+            case 5:
+              return '体系'
+            case 6:
+              return '中心层'
+            case 7:
+              return '平台层'
+            default:
+              break;
+          }
+        },
+      },
+      {
+        title: '功能',
+        dataIndex: 'deptName',
+        key: 'deptName',
+        render: (_: any, record: any) => {
+
+          if (record.functionList) {
+            const options = record.functionList.map((item: any, index: number) => ({ label: item.name, value: item.code }))
+
+            const needItem = checkBoxCodes.filter(a => a.menuId == record.menuId);
+
+            const codes = needItem && needItem.length > 0 ? needItem[0].function.map((a: any) => a.code) : [];
+
+            const onCheckGroupChange = (checkedValue: CheckboxValueType[]) => {
+
+              if (checkedValue.length > 0) {
+                const _temp = checkBoxCodes;
+                const index = checkBoxCodes.findIndex((item) => item.menuId == record.menuId);
+
+
+                const needed = options.filter((item: any) => checkedValue.includes(item.value));
+                const transfered = needed.map((item: any) => ({ name: item.label, code: item.value }));
+
+                if (index >= 0) {
+                  //先去除旧的的对象
+                  _temp.splice(index, 1);
+                }
+
+                _temp.push({
+                  menuId: record.menuId,
+                  function: transfered
+                });
+
+                const menuIdsArr = _temp.map((item: any) => item.menuId);
+
+                set_checkedTableMenuIds([...menuIdsArr])
+
+                set_checkBoxCodes([..._temp]);
+              } else {
+                //取消选择
+                const _temp = checkBoxCodes;
+                const index = checkBoxCodes.findIndex((item) => item.menuId == record.menuId);
+
+                _temp.splice(index, 1);
+
+                const menuIdsArr = _temp.map((item: any) => item.menuId);
+                set_checkedTableMenuIds([...menuIdsArr])
+                set_checkBoxCodes([..._temp]);
+
+              }
+
+
+            }
+
+            return (
+              <Checkbox.Group
+                options={options}
+                value={codes}
+                onChange={checkedValue => onCheckGroupChange(checkedValue)}
+              />
+            )
+          }
+          return '-'
+        }
+
+      },
+    ];
+
+    const getTreeReqFunc = async (name?: string) => {
+      const resp = await getTreeData();
+      set_treeData(resp);
+    }
+
+    const onSelect: TreeProps['onSelect'] = (selectedKeys, info) => {
+      //console.log('selected', selectedKeys, info);
+      const { node } = info;
+      set_currentSelectedTreeNode(node);
+    };
+
+    const dataList: any[] = [];
+
+    const getParentKey = (key: React.Key, tree: any[]): React.Key => {
+      let parentKey: React.Key;
+      for (let i = 0; i < tree.length; i++) {
+        const node = tree[i];
+        if (node.children) {
+          if (node.children.some((item: { code: React.Key; }) => item.code === key)) {
+            parentKey = node.code;
+          } else if (getParentKey(key, node.children)) {
+            parentKey = getParentKey(key, node.children);
+          }
+        }
+      }
+      return parentKey!;
+    };
+
+
+    const onTreeSearchKeyChange = (e: React.ChangeEvent<HTMLInputElement>) => {
+
+      const { value } = e.target;
+      const newExpandedKeys = dataList
+        .map((item) => {
+          if (item.name.indexOf(value) > -1) {
+            return getParentKey(item.code, treeData);
+          }
+          return null;
+        });
+
+      const b = newExpandedKeys.filter((item, i, self) => item && self.indexOf(item) === i);
+
+      setExpandedKeys(newExpandedKeys as React.Key[]);
+      setSearchValue(value);
+      setAutoExpandParent(true);
+    }
+
+
+    const onExpand = (newExpandedKeys: React.Key[]) => {
+      setExpandedKeys(newExpandedKeys);
+      setAutoExpandParent(false);
+    };
+
+    const generateList = (data: getTreeDataRespType[]) => {
+      for (let i = 0; i < data.length; i++) {
+        const node = data[i];
+        const { code, name } = node;
+        dataList.push({ code, name: name });
+        if (node.children) {
+          generateList(node.children);
+        }
+      }
+    };
+    generateList(treeData as any);
+
+
+    const getTableData = async (params: any, sort: any, filter: any) => {
+      set_drawerTablereload(false);
+      if (currentSelectedTreeNode) {
+        const resp = await getMenuRelaPerm({ ...params, hospId: record.id });
+        if (resp) {
+          let temp: { menuId: any; function: any; }[] = [];
+          const setTreeRecursion = (data: any) => {
+            data.map((item: any, index: number) => {
+
+
+              if (item.type == 1 && item.functionCheckList) {
+                //菜单
+                temp.push({
+                  menuId: item.menuId,
+                  function: item.functionCheckList
+                });
+              }
+
+              if (item.children && item.children.length != 0) {
+                setTreeRecursion(item.children);
+              }
+
+            });
+
+            set_checkBoxCodes(temp);
+            set_checkedTableMenuIds(temp.map((a: any) => a.menuId))
+
+
+            return data;
+          }
+
+          setTreeRecursion(resp)
+
+
+
+          return {
+            data: resp,
+            success: true,
+          }
+        }
+        return {
+          data: [],
+          success: true
+        }
+      }
+
+      return []
+    }
+
+    const saveResult = async () => {
+      const result = checkBoxCodes.map((item: any) => ({ ...item, hospId: record.id }))
+      const resp = await saveHospMenuApiPerm(result);
+      if (resp) {
+        set_drawerTablereload(true);
+      }
+
+    }
+
+    const onCancel = () => {
+      set_drawerVisible(false);
+    }
+
+    const drawerTableDataSearchHandle = (paramName: string) => {
+
+
+      set_drawerTableDataFilterParams({
+        ...drawerTableDataFilterParams,
+        [`${paramName}`]: drawerTableDataSearchKeywords
+      })
+    }
+
+
+    const oneKeySetReadOnly = (bool:boolean)=>{
+       
+        if(bool){
+          const _temp = checkBoxCodes.map((item:any)=>{
+                const needed = item.function.filter((a:any)=>a.code == 'search');
+                return {...item,function:needed}
+          })
+          set_checkBoxCodes([..._temp]);
+        }
+
+    }
+
+
+    useEffect(() => {
+      if (currentSelectedTreeNode) {
+        set_drawerTableDataFilterParams({ ...tableDataFilterParams, systemId: currentSelectedTreeNode.code })
+      }
+
+      //切换系统清空数据
+      set_checkBoxCodes([]);
+      set_checkedTableMenuIds([]);
+
+    }, [currentSelectedTreeNode]);
+
+    useEffect(() => {
+      //初始化左侧树结构数据后
+
+      if (treeData?.length > 0) {
+
+        if (treeData[0].children && treeData[0].children.length > 0) {
+          const [node, nodeParent] = getDeepestTreeData(treeData[0], 'children');
+
+          set_currentSelectedTreeNode(node);
+          setExpandedKeys([nodeParent.code]);
+        }
+      }
+    }, [treeData]);
+
+
+
+    useEffect(() => {
+      getTreeReqFunc();
+
+    }, []);
+
+
+    return (
+      <DrawerForm
+        trigger={
+          <a key="link3" onClick={(e) => { set_drawerVisible(true); }}>接口</a>
+        }
+        width={908}
+        // visible={drawerVisible}
+        drawerProps={{
+          open: drawerVisible,
+          closable: false,
+          destroyOnClose: true,
+          extra: <div>anniu</div>,
+        }}
+        submitter={false}
+      >
+        <div className='setApiPermDrawer'>
+          <div className='topbar'>
+            <div className='title'>{`院区功能权限设置(${record.hospName})`}</div>
+            <div className='btnGroup'>
+              <span className='cancel' onClick={() => onCancel()}>取消</span>
+              <span className='save' onClick={() => saveResult()}>保存</span>
+            </div>
+          </div>
+          <div className='content'>
+            <div className='leftTree'>
+              <div className='search'>
+                <Input
+                  className='searchInput'
+                  placeholder="请输入"
+                  size='small'
+                  allowClear
+
+                  style={{ marginBottom: 16 }}
+                  onChange={onTreeSearchKeyChange}
+                  suffix={
+                    <SearchIcon type='iconsousuo' />
+                  }
+                />
+              </div>
+              {
+                treeData && treeData.length > 0 && (
+                  <DirectoryTree
+                    fieldNames={{ title: 'name', key: 'code' }}
+                    rootStyle={{ height: '100%', paddingBottom: 50, overflowY: 'scroll', overflowX: 'hidden' }}
+                    onSelect={onSelect}
+                    onExpand={onExpand}
+                    expandedKeys={expandedKeys}
+                    autoExpandParent={autoExpandParent}
+                    selectedKeys={currentSelectedTreeNode ? [currentSelectedTreeNode.code] : []}
+                    blockNode={true}
+                    icon={() => null}
+                    titleRender={
+                      (nodeData: any) => {
+
+                        const strTitle = nodeData.name as string;
+                        const index = strTitle.indexOf(searchValue);
+                        const beforeStr = strTitle.substring(0, index);
+                        const afterStr = strTitle.slice(index + searchValue.length);
+                        const title =
+                          index > -1 ? (
+                            <span>
+                              {beforeStr}
+                              <span className="site-tree-search-value" style={{ color: 'red', overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>{searchValue}</span>
+                              {afterStr}
+                            </span>
+                          ) : (
+                            <span className='strTitle'>{strTitle}</span>
+                          );
+                        return <div style={{
+                          display: 'flex', flexDirection: 'row',
+                          width: '100%',
+                          justifyContent: 'flex-start', alignItems: 'center', height: 32,
+                          borderRadius: '4px',
+                          overflow: 'hidden',
+                          color: '#17181A',
+                          textOverflow: 'ellipsis',
+                          whiteSpace: 'nowrap'
+
+                        }}>{title}</div>
+                      }
+                    }
+                    defaultSelectedKeys={[treeData[0].children[0].code]}
+                    treeData={treeData as unknown as DataNode[]}
+                    switcherIcon={(props: any) => {
+                      const { expanded } = props;
+                      //return <button className='site-table-row-expand-icon site-table-row-expand-icon-expanded'></button>
+                      return !expanded ? <img style={{ width: 20, height: 20, position: 'relative', top: 5 }} src={expandedIcon} /> : <img style={{ width: 20, height: 20, position: 'relative', top: 5 }} src={closeIcon} />
+                    }}
+                  />
+                )
+              }
+            </div>
+            {/* <div style={{width:16,height:'92vh',background:'#F5F7FA'}}></div> */}
+            <div className='rightContent'>
+              <div className='tableToolbar'>
+                <div className='filter'>
+                  <div className='filterItem'>
+                    <span className='label'>检索:</span>
+                    <KCInput placeholder={'请输入'} style={{ width: 160 }} search allowClear
+                      onChange={(e) => {
+                        set_drawerTableDataSearchKeywords(e.target.value);
+                        if (e.target.value.length == 0) {
+                          set_drawerTableDataFilterParams({
+                            ...drawerTableDataFilterParams,
+                            name: ''
+                          });
+                        }
+                      }}
+                      onSearch={() => drawerTableDataSearchHandle('name')}
+
+                    />
+                  </div>
+                </div>
+
+                <div className={'btnGroup'}>
+                     {/* <a style={{color: '#17181A'}}><Switch style={{position:'relative',marginRight:4}} size='small'  onChange={(bool)=>oneKeySetReadOnly(bool)} />只读</a> */}
+                  {/* <UpDataActBtn key={'act'} record={undefined} type='ADD' /> */}
+                </div>
+
+              </div>
+              {currentSelectedTreeNode && <KCTable
+                rowSelection={{
+                  // 自定义选择项参考: https://ant.design/components/table-cn/#components-table-demo-row-selection-custom
+                  // 注释该行则默认不显示下拉选项
+                  checkStrictly: false,
+                  onChange(selectedRowKeys, selectedRows, info) {
+                    //console.log({selectedRowKeys, selectedRows, info});
+                    if (selectedRowKeys.length == 0) {
+                      set_checkedTableMenuIds([]);
+                    }
+                    if (selectedRows.length == 0) {
+                      set_checkBoxCodes([]);
+                    }
+                  },
+                  selectedRowKeys: checkedTableMenuIds,
+                  selections: [Table.SELECTION_ALL, Table.SELECTION_INVERT],
+                  onSelect: (record, selected, selectedRows, nativeEvent) => {
+                    //console.log({ record, selected, selectedRows, nativeEvent });
+                    let checkedData = checkBoxCodes;
+                    if (selected) {
+                      //选中
+                      selectedRows.forEach(a => {
+                        if (a.functionList) {
+                          checkedData.push({
+                            menuId: a.menuId,
+                            function: a.functionList
+                          })
+                        }
+                      });
+                      //更新表格row选中状态
+                      if (record.type == 0) {
+                        //选中的是目录
+                        const ids = record.children.map((item: any) => item.menuId);
+                        set_checkedTableMenuIds([...checkedTableMenuIds, ...ids]);
+                      }
+                      if (record.type == 1) {
+                        //菜单
+                        set_checkedTableMenuIds([...checkedTableMenuIds, record.menuId]);
+
+                      }
+                    } else {
+                      if (record.type == 0) {
+                        //目录
+                        if (record.children) {
+                          record.children.forEach((element: any) => {
+                            const index = checkedData.findIndex((item) => item.menuId == record.menuId);
+                            checkedData.splice(index, 1);
+                          });
+                        }
+
+                        const ids = record.children.map((item: any) => item.menuId);
+                        const leftIds = checkedTableMenuIds.filter((id: number) => !ids.includes(id)); //筛选出剩余选中项
+                        set_checkedTableMenuIds([...leftIds]);
+
+                      } else if (record.type == 1) {
+                        //菜单
+                        const temp = checkedTableMenuIds;
+                        const index = checkedData.findIndex((item) => item.menuId == record.menuId);
+                        const menuIdIndex = checkedTableMenuIds.findIndex((item) => item == record.menuId);
+                        checkedData.splice(index, 1);
+                        temp.splice(menuIdIndex, 1);
+                        set_checkedTableMenuIds([...temp]);
+                      }
+                    }
+
+                    //console.log({ checkedData });
+
+                    set_checkBoxCodes([...checkedData]);
+                  }
+                }}
+                pagination={false} reload={drawerTablereload} newVer params={drawerTableDataFilterParams} rowKey='menuId' columns={columnsData as ProColumns[]} request={(params: any, sort: any, filter: any) => getTableData(params, sort, filter)} />}
+
+            </div>
+          </div>
+        </div>
+
+      </DrawerForm>
+    )
+  }
+
   const getHospData = async (params: TableRequestParamsType) => {
     const { current = 1, pageSize = 10, hospName: hospitalName } = params;
     const resp = await getAllHosp({
@@ -227,6 +759,19 @@ const HospManage: FC<PageProps> = ({ hospManageModel: state, dispatch }) => {
     }
   };
 
+  const setOnlyRead = async (record: any) => {
+    const resp = await setReadOnly(record.id);
+    if (resp) {
+      dispatch &&
+        dispatch({
+          type: 'hospManageModel/reloadTable',
+        });
+    }
+  }
+
+
+
+
   const initHospData = async (record: TableListItem) => {
     const resp = await hospInit(record.id);
   }
@@ -235,10 +780,10 @@ const HospManage: FC<PageProps> = ({ hospManageModel: state, dispatch }) => {
 
 
     set_tableDataFilterParams({
-        ...tableDataFilterParams,
-        [`${paramName}`]: tableDataSearchKeywords
+      ...tableDataFilterParams,
+      [`${paramName}`]: tableDataSearchKeywords
     })
-}
+  }
 
   return (
     <div className="HospManage">

+ 183 - 0
src/pages/platform/setting/hospManage/style.less

@@ -1,8 +1,191 @@
+.setApiPermDrawer {
+  .topbar {
+    display: flex;
+    flex-direction: row;
+    justify-content: space-between;
+    align-items: center;
+    margin-bottom: 16px;
+
+    .title {
+      height: 16px;
+      font-size: 16px;
+      font-family: SourceHanSansCN-Medium, SourceHanSansCN;
+      font-weight: 500;
+      color: #17181A;
+      line-height:16px;
+    }
+
+    .btnGroup {
+      .cancel {
+        cursor: pointer;
+        display: inline-block;
+        text-align: center;
+        width: 56px;
+        height: 24px;
+        line-height: 23px;
+        background: #FAFCFF;
+        border-radius: 4px;
+        border: 1px solid #DAE2F2;
+        font-size: 14px;
+        font-family: SourceHanSansCN-Normal, SourceHanSansCN;
+        font-weight: 400;
+        color: #17181A;
+        margin-right: 8px;
+      }
+
+      .save {
+        cursor: pointer;
+        display: inline-block;
+        width: 56px;
+        text-align: center;
+        height: 24px;
+        line-height: 23px;
+        background: #3377FF;
+        border-radius: 4px;
+        border: 1px solid #DAE2F2;
+        font-size: 14px;
+        font-family: SourceHanSansCN-Normal, SourceHanSansCN;
+        font-weight: 400;
+        color: #fff;
+      }
+    }
+  }
+
+  .content {
+    overflow: hidden;
+    .leftTree {
+      width: 220px;
+      height:calc(100vh - 72px);
+      float: left;
+      background: #FFF;
+      border-radius: 4px;
+      overflow: hidden;
+      margin-right: 16px;
+      padding: 16px;
+      border: 1px solid #DAE2F2;
+   
+      .searchInput {
+        border: 1px solid #CFD7E6;
+      }
+
+
+      .kcmp-ant-tree.kcmp-ant-tree-directory .kcmp-ant-tree-treenode-selected:hover::before,
+      .kcmp-ant-tree.kcmp-ant-tree-directory .kcmp-ant-tree-treenode-selected::before {
+        border-radius: 4px;
+        background: #F0F2F5;
+      }
+
+      .kcmp-ant-tree.kcmp-ant-tree-directory .kcmp-ant-tree-treenode .kcmp-ant-tree-node-content-wrapper.kcmp-ant-tree-node-selected {
+        font-weight: bold;
+      }
+
+    }
+
+    .rightContent {
+      position: relative;
+      border-radius: 4px;
+      width: calc(100% - 236px);
+      // padding: 16px;
+      margin-left: 236px;
+      background: #FFF;
+
+      .checkBtn {
+        position: absolute;
+        cursor: pointer;
+        top: 0;
+        right: 0;
+        padding: 0 14px;
+        height: 24px;
+        line-height: 24px;
+        background: #3376FE;
+        border-radius: 4px;
+        text-align: center;
+        font-size: 14px;
+        font-family: SourceHanSansCN-Normal, SourceHanSansCN;
+        font-weight: 400;
+        color: #FFF;
+      }
+
+      .midLine {
+        display: flex;
+        flex-direction: row;
+        justify-content: center;
+        align-items: center;
+        height: 24px;
+        line-height: 24px;
+        background: #F0FCFC;
+        font-size: 12px;
+        color: #515866;
+        font-family: SourceHanSansCN-Normal, SourceHanSansCN;
+        font-weight: 400;
+        margin-top: 12px;
+        margin-bottom: 15px;
+
+        &>span {
+          cursor: pointer;
+          font-size: 12px;
+          font-family: SourceHanSansCN-Medium, SourceHanSansCN;
+          font-weight: 500;
+          color: #00B3B3;
+          padding-right: 4px;
+        }
+
+      }
+
+      .kcmp-ant-tabs-nav {
+        &::before {
+          border-bottom: none !important;
+        }
+      }
+
+      .tableToolbar {
+        display: flex;
+        flex-direction: row;
+        justify-content: space-between;
+        align-items: center;
+        margin-bottom: 16px;
+
+        .filter {
+          display: flex;
+          flex-direction: row;
+          justify-content: flex-start;
+          align-items: center;
+
+          .filterItem {
+            display: flex;
+            flex-direction: row;
+            justify-content: center;
+            align-items: center;
+          }
+        }
+
+        .btnGroup {
+          &>span {
+            display: inline-block;
+            text-align: center;
+            width: 56px;
+            height: 24px;
+            line-height: 24px;
+            background: #3377FF;
+            color: #FFF;
+            border-radius: 4px;
+            cursor: pointer;
+          }
+        }
+      }
+    }
+  }
+}
+
+
+
 .HospManage {
   height: auto;
   padding: 16px;
   background: #fff;
 
+
+
   .toolBar {
     display: flex;
     flex-direction: row;

+ 397 - 0
src/pages/platform/setting/notificationTemplate/index.tsx

@@ -0,0 +1,397 @@
+/*
+ * @Author: code4eat awesomedema@gmail.com
+ * @Date: 2023-03-03 11:30:33
+ * @LastEditors: code4eat awesomedema@gmail.com
+ * @LastEditTime: 2023-03-21 11:27:51
+ * @FilePath: /KC-MiddlePlatform/src/pages/platform/setting/pubDicTypeMana/index.tsx
+ * @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
+ */
+
+
+
+import { KCInput } from '@/components/KCInput';
+import KCTable from '@/components/kcTable';
+import { getAllHosp } from '@/service/hospList';
+import { getUserRelaSeletData } from '@/service/user';
+import { ModalForm, ProFormCascader, ProFormSelect, ProFormText, ProFormTextArea } from '@ant-design/pro-form'
+import { ProColumns } from '@ant-design/pro-table';
+import { Popconfirm } from 'antd';
+import { useEffect, useState } from 'react'
+import { getSysLists } from '../paramsMana/service';
+import { addData, delData, editData, getData } from './service';
+
+import './style.less';
+
+
+
+export default function PubDicTypeMana() {
+
+    const [tableDataFilterParams, set_tableDataFilterParams] = useState<any | undefined>();
+    const [tableDataSearchKeywords, set_tableDataSearchKeywords] = useState<string>('');
+    const [reload, set_reload] = useState(false);
+    const [currentEdit, set_currentEdit] = useState<any>(undefined);
+
+    //字典
+    const [dirData, set_dirData] = useState<any[]>([]);
+
+
+
+
+    const columns = [
+        {
+            title: '主题',
+            dataIndex: 'title',
+
+        },
+        {
+            title: '消息内容',
+            dataIndex: 'content',
+        },
+        {
+            title: '院区',
+            dataIndex: 'hospName',
+        },
+        {
+            title: '系统',
+            dataIndex: 'systemName',
+        },
+        {
+            title: '接收类型',
+            dataIndex: 'receiveTypeName',
+        },
+        {
+            title: '接收对象',
+            dataIndex: 'receiveTargetName',
+        },
+        {
+            title: '消息级别',
+            dataIndex: 'receiveLevelName',
+        },
+        {
+            title: '处理链接',
+            dataIndex: 'resolvePath',
+        },
+        {
+            title: '备注',
+            dataIndex: 'remark',
+        },
+        {
+            title: '操作',
+            key: 'option',
+            width: 120,
+            valueType: 'option',
+            render: (_: any, record: any) => {
+                return [
+                    <UpDataActBtn key={'act'} record={record} type='EDIT' />,
+                    <Popconfirm
+                        title="是否确认删除?"
+                        key="del"
+                        onConfirm={() => delTableData(record)}
+                    >
+                        <a>删除</a>
+                    </Popconfirm>
+                ]
+            },
+        },
+
+    ]
+
+
+    const getTableData = async (params: any) => {
+        const resp = await getData(params);
+        set_reload(false);
+        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) {
+            set_reload(true);
+            // message.success('操作成功!');
+        }
+    }
+
+
+    const getDirecData = async (key?: string) => {
+
+        const data = await getUserRelaSeletData(key);
+        if (data) {
+          set_dirData(data);
+        }
+    }
+
+
+    const setSelectorData = (key: string) => {
+
+        let result = dirData.filter((t: any) => (t.code == key));
+
+        if (result.length > 0) {
+
+            let dataArr = result[0].dataVoList;
+
+            let defaultValue = dataArr.filter((t: any) => t.defaultValue == 1);
+
+            dataArr.sort((prev: any, next: any) => {
+                return prev.sort - next.sort;
+            });
+
+            return {
+                defaultvalue: defaultValue[0] ? defaultValue[0].value : '',
+                list: dataArr.map((t: any) => ({ label: t.code, value: t.value }))
+            }
+        }
+
+        return {
+            defaultvalue: '',
+            list: []
+        };
+    }
+
+
+
+
+    const updateTable = async (formVal: any, type: 'EDIT' | "ADD") => {
+
+        if (type == 'ADD') {
+            const resp = await addData({ ...formVal, hospId: 0 });
+            if (resp) {
+                set_reload(true);
+            }
+        }
+        if (type == 'EDIT') {
+            const { dictId } = currentEdit;
+            const resp = await editData({ ...formVal, dictId, hospId: 0 });
+            if (resp) {
+                set_reload(true);
+            }
+        }
+
+
+    }
+
+    const UpDataActBtn = ({ record, type }: { record: any, type: 'EDIT' | 'ADD' }) => {
+
+        return (
+            <ModalForm
+                title={`${type == 'EDIT' ? '编辑' : '新增'}消息模板`}
+                width={688}
+                initialValues={type == 'EDIT' ? { ...record } : {}}
+                trigger={
+                    type == 'EDIT' ? <a key="edit" onClick={() => set_currentEdit(record)}>编辑</a> : <span className='add'>新增</span>
+                }
+                onFinish={(val) => {
+                    return updateTable(val, type);
+                }}
+                colProps={{span:12}}
+                grid
+            >
+
+                <ProFormText
+                    name="title"
+                    label="消息主题:"
+                    placeholder="请输入"
+                    rules={[{ required: true, message: '主题不能为空!' }]}
+                />
+                <ProFormText
+                    name="content"
+                    label="消息内容:"
+                    placeholder="请输入"
+                    rules={[{ required: true, message: '消息内容不能为空!' }]}
+                />
+                <ProFormSelect
+                    name="hospId"
+                    label="院区:"
+                    disabled={type == 'EDIT'}
+                    placeholder="请选择院区"
+                    rules={[{ required: true, message: '院区不能为空!' }]}
+                    request={async () => {
+                        const resp = await getAllHosp({ pageSize: 200, current: 1 });
+                        if (resp) {
+                            const data: any = resp.list?.map((a) => ({
+                                label: a.hospName,
+                                value: a.id
+                            }));
+
+                            return [{
+                                  label:'所有院区',
+                                  value:0
+                            },...data];
+                        }
+                        return []
+
+                    }}
+
+                    fieldProps={{
+
+                    }}
+                />
+
+                <ProFormCascader
+                    label='系统:'
+                    name='systemId'
+                    placeholder="请选择"
+                    request={async () => {
+                        const resp = await getSysLists();
+                        if (resp) {
+                            return resp
+                        }
+                        return []
+
+                    }}
+                    fieldProps={{
+                        fieldNames: {
+                            label: 'name',
+                            value: 'code'
+                        },
+                    }}
+                    rules={[{ required: true, message: '系统不能为空!' }]}
+                />
+
+                <ProFormSelect
+                    name="receiveTypeName"
+                    label="接收类型:"
+                    options={setSelectorData('RECEIVE_TYPE').list}
+                    placeholder="请选择"
+                    rules={[{ required: true, message: '请选择!' }]}
+                />
+                <ProFormSelect
+                    name="receiveTarget"
+                    label="接收对象:"
+                    options={setSelectorData('RECEIVE_TARGET').list}
+                    placeholder="请选择"
+                    rules={[{ required: true, message: '请选择!' }]}
+                />
+                <ProFormSelect
+                    name="receiveLevel"
+                    label="消息级别:"
+                    options={setSelectorData('MESSAGE_LEVEL').list}
+                    placeholder="请选择"
+                    rules={[{ required: true, message: '请选择!' }]}
+                />
+                <ProFormText
+                    name="resolvePath"
+                    label="处理链接:"
+                    placeholder="请输入"
+                />
+                <ProFormTextArea
+                    colProps={{span:24}}
+                    name="remark"
+                    label="备注:"
+                    placeholder="请输入"
+
+                />
+            </ModalForm>
+        )
+    }
+
+    const tableDataSearchHandle = (paramName: string) => {
+
+
+        set_tableDataFilterParams({
+            ...tableDataFilterParams,
+            [`${paramName}`]: tableDataSearchKeywords
+        })
+    }
+
+
+    useEffect(()=>{
+        getDirecData();
+    },[])
+
+    return (
+        <div className='PubDicTypeMana'>
+            <div className='toolBar'>
+                <div className='filter'>
+                    <div className='filterItem'>
+                        <span className='label'>院区:</span>
+                        <ProFormSelect
+                            noStyle
+                            allowClear
+                            placeholder="请选择"
+                            style={{ width: 160, marginRight: 16 }}
+                            request={async () => {
+                                const resp = await getAllHosp({ pageSize: 200, current: 1 });
+                                if (resp) {
+                                    const data: any = resp.list?.map((a) => ({
+                                        label: a.hospName,
+                                        value: a.id
+                                    }));
+
+                                    return [
+                                        {
+                                            label: '所有院区', value: '0'
+                                        }, ...data
+                                    ];
+                                }
+                                return []
+
+                            }}
+                            fieldProps={{
+                                onChange(value, option) {
+                                    set_tableDataFilterParams({ ...tableDataFilterParams, hospId: value })
+                                },
+                            }}
+                        />
+                    </div>
+                    <div className='filterItem' style={{ marginRight: 16 }}>
+                        <span className='label'>系统名称:</span>
+                        <ProFormCascader
+                            noStyle
+                            allowClear
+                            width={160}
+                            placeholder="请选择"
+                            request={async () => {
+                                const resp = await getSysLists();
+                                if (resp) {
+                                    return resp
+                                }
+                                return []
+
+                            }}
+                            fieldProps={{
+                                fieldNames: {
+                                    label: 'name',
+                                    value: 'code'
+                                },
+                                onChange(value: any, option: any) {
+                                    set_tableDataFilterParams({ ...tableDataFilterParams, systemId: value ? value[value.length - 1] : '' })
+                                },
+                            }}
+                        />
+                    </div>
+                    <div className='filterItem'>
+                        <span className='label'>检索:</span>
+                        <KCInput placeholder={'请输入主题名称'} style={{ width: 160 }} search allowClear
+                            onChange={(e) => {
+                                set_tableDataSearchKeywords(e.target.value);
+                                if (e.target.value.length == 0) {
+                                    set_tableDataFilterParams({
+                                        ...tableDataFilterParams,
+                                        title: ''
+                                    });
+                                }
+                            }}
+                            onSearch={() => tableDataSearchHandle('title')}
+
+                        />
+                    </div>
+                </div>
+                <div className='btnGroup'>
+                    <UpDataActBtn record type='ADD' />
+                </div>
+            </div>
+            <div style={{ marginTop: 16 }}>
+                <KCTable columns={columns as ProColumns[]} reload={reload} rowKey='id' newVer params={tableDataFilterParams} request={(params) => getTableData(params)} />
+            </div>
+        </div>
+    )
+}

+ 96 - 0
src/pages/platform/setting/notificationTemplate/service.ts

@@ -0,0 +1,96 @@
+/*
+ * @Author: code4eat awesomedema@gmail.com
+ * @Date: 2023-03-03 16:31:27
+ * @LastEditors: code4eat awesomedema@gmail.com
+ * @LastEditTime: 2023-03-21 11:27:38
+ * @FilePath: /KC-MiddlePlatform/src/pages/platform/setting/pubDicTypeMana/service.ts
+ * @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
+ */
+
+
+
+import { request } from 'umi';
+
+//获取table列表数据
+
+export type NotificationTemplateType = {
+  id:number;
+  hospId:string;
+  title:string;
+  content:string;
+  systemPath:string;
+  systemId:string;
+  receiveType:string;
+  receiveTypeName:string;
+  receiveTarget:string;
+  receiveTargetName:string;
+  receiveLevel:string;
+  receiveLevelName:string;
+  resolvePath:string;
+  remark:string;
+}
+
+
+export const getData = (params?:any) => {
+  return request<{
+       current:number;
+       list:NotificationTemplateType[];
+       pageSize:number;
+       totalCount:number;
+       totalPage:number;
+  }>('/centerSys/message/list', {
+    method: 'GET',
+    params:{...params}
+  });
+};
+
+
+//新增表格数据
+export type AddTableDataType = {
+  hospId:string;
+  title:string;
+  content:string;
+  systemPath:string;
+  systemId:string;
+  receiveType:string;
+  receiveTypeName:string;
+  receiveTarget:string;
+  receiveTargetName:string;
+  receiveLevel:string;
+  receiveLevelName:string;
+  resolvePath:string;
+  remark:string;
+}
+export const addData = (data:AddTableDataType) => {
+  return request('/centerSys/message/add', {
+    method: 'POST',
+    data
+  });
+};
+
+
+
+//编辑表格数据
+
+export const editData = (data:NotificationTemplateType) => {
+  return request('/centerSys/message/edit', {
+    method: 'POST',
+    data
+  });
+};
+
+//删除表格操作
+export const delData = (id:string) => {
+  return request('/centerSys/message/delete', {
+    method: 'POST',
+    params:{id}
+  });
+};
+
+
+
+
+
+
+
+

+ 42 - 0
src/pages/platform/setting/notificationTemplate/style.less

@@ -0,0 +1,42 @@
+.PubDicTypeMana {
+  padding: 16px;
+  background: #FFFFFF;
+  border-radius: 4px;
+
+  .toolBar {
+    display: flex;
+    flex-direction: row;
+    justify-content: space-between;
+    align-items: center;
+
+    .filter {
+      display: flex;
+      flex-direction: row;
+      justify-content: flex-start;
+      align-items: center;
+
+      .filterItem {
+        display: flex;
+        flex-direction: row;
+        justify-content: center;
+        align-items: center;
+      }
+    }
+
+    .btnGroup {
+      .add {
+        cursor: pointer;
+        display: inline-block;
+        font-size: 14px;
+        font-family: SourceHanSansCN-Normal, SourceHanSansCN;
+        font-weight: 400;
+        color: #FFFFFF;
+        line-height: 24px;
+        padding: 0 14px;
+        background: #3377FF;
+        border-radius: 4px;
+      }
+    }
+
+  }
+}

+ 1 - 2
src/pages/platform/setting/pubDicMana/index.tsx

@@ -2,7 +2,7 @@
  * @Author: code4eat awesomedema@gmail.com
  * @Date: 2023-03-03 11:30:33
  * @LastEditors: code4eat awesomedema@gmail.com
- * @LastEditTime: 2023-03-10 17:31:00
+ * @LastEditTime: 2023-03-10 17:36:08
  * @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
  */
@@ -159,7 +159,6 @@ const PubDicMana = () => {
                     name="name"
                     label="项目名称:"
                     placeholder="请输入"
-                    disabled={type == 'EDIT'}
                     rules={[{ required: true, message: '项目名称不能为空!' }]}
                 />
                 <ProFormText

+ 598 - 18
src/pages/platform/setting/roleManage/index.tsx

@@ -1,22 +1,34 @@
 /*
  * @Author: your name
  * @Date: 2022-01-13 15:22:48
- * @LastEditTime: 2022-03-03 14:27:58
- * @LastEditors: Please set LastEditors
+ * @LastEditTime: 2023-03-21 11:23:55
+ * @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/hospManage/index.tsx
  */
 
-import { FC } from 'react';
+import { FC, useEffect, useState } from 'react';
 import { roleManageModelState, ConnectProps, Loading, connect } from 'umi';
-import { Button, Divider, Popconfirm } from 'antd';
+import {Checkbox, Divider, Dropdown, Input, Popconfirm, Table, TreeProps } from 'antd';
 import KCTable from '@/components/kcTable';
 import type { ProColumns } from '@ant-design/pro-table';
-import { getAllRoles, getRoleHasBindMenus, getRoleHasBindUsers } from '@/service/role';
+import { getAllRoles, getRoleHasBindMenus, getRoleHasBindUsers, getRolePermRelaMenu, initRoleFunc, saveRoleRelaApiPerm } from '@/service/role';
 import { TableRequestParamsType } from '@/typings';
 import ActModal from './modals/modal';
 import './style.less';
 import type { RoleItemType as TableListItem } from '@/service/role';
+import { KCInput } from '@/components/KCInput';
+import { createFromIconfontCN, DownOutlined } from '@ant-design/icons';
+import { getMenuRelaPerm, saveHospMenuApiPerm } from '@/service/hospList';
+import { getTreeData, getTreeDataRespType } from '../systemNavMana/service';
+import { CheckboxValueType } from 'antd/lib/checkbox/Group';
+import { getDeepestTreeData } from '@/utils';
+import { DrawerForm } from '@ant-design/pro-form';
+import { DataNode } from 'antd/es/tree';
+
+import expandedIcon from '../../../../../public/images/treenode_open.png';
+import closeIcon from '../../../../../public/images/treenode_collapse.png';
+import DirectoryTree from 'antd/es/tree/DirectoryTree';
 
 export enum TableActType {
   NOACT,
@@ -32,9 +44,17 @@ interface PageProps extends ConnectProps {
   loading: boolean;
 }
 
+const SearchIcon = createFromIconfontCN({
+  scriptUrl: '//at.alicdn.com/t/c/font_1927152_g1njmm1kh7b.js',
+});
+
 const RoleManage: FC<PageProps> = ({ roleManageModel: state, dispatch }) => {
   const { reloadTable } = state;
 
+  const [tableDataFilterParams, set_tableDataFilterParams] = useState<any | undefined>();
+  const [tableDataSearchKeywords, set_tableDataSearchKeywords] = useState<string>('');
+  const [drawerVisible, set_drawerVisible] = useState(false);
+
   const columns: ProColumns<TableListItem>[] = [
     {
       title: 'ID',
@@ -76,10 +96,6 @@ const RoleManage: FC<PageProps> = ({ roleManageModel: state, dispatch }) => {
           用户
         </a>,
         <Divider key="3" type="vertical" style={{ margin: '0 1px' }} />,
-        <a key="link3" onClick={() => editMenuBind(record)}>
-          菜单
-        </a>,
-        <Divider key="2" type="vertical" style={{ margin: '0 1px' }} />,
         <a key="link" onClick={() => editHandle(record)}>
           编辑
         </a>,
@@ -94,10 +110,527 @@ const RoleManage: FC<PageProps> = ({ roleManageModel: state, dispatch }) => {
         >
           <a>删除</a>
         </Popconfirm>,
+         <Divider key="9" type="vertical" style={{ margin: '0 1px' }} />,
+        <Dropdown key='4' menu={{
+          items: [
+            { key:'0',label: <a key="link3" onClick={() => editMenuBind(record)}>菜单</a>},
+            { key: '5', label: <DrawerActBtn key="link7" record={record} /> },
+            { key: '2', label: <a key="link4" onClick={() => initRoleData('func',record)} >初始化功能</a> },
+            //{ key: '6', label: <a key="link6" onClick={() => initRoleData('read',record)}>初始化只读</a> },
+          ]
+        }}>
+          <a>
+            更多 <DownOutlined />
+          </a>
+        </Dropdown>
       ],
     },
+
   ];
 
+  const DrawerActBtn = ({ record }: { record: any }) => {
+
+    
+    const [treeData, set_treeData] = useState<getTreeDataRespType[]>([]);
+    const [currentSelectedTreeNode, set_currentSelectedTreeNode] = useState<any | undefined>(undefined);
+    const [drawerTablereload, set_drawerTablereload] = useState(false);
+    const [expandedKeys, setExpandedKeys] = useState<React.Key[]>([]);
+    const [searchValue, setSearchValue] = useState('');
+    const [autoExpandParent, setAutoExpandParent] = useState(true);
+    const [drawerTableDataFilterParams, set_drawerTableDataFilterParams] = useState<any | undefined>();
+
+    const [checkBoxCodes, set_checkBoxCodes] = useState<any[]>([]);
+
+
+    const [drawerTableDataSearchKeywords, set_drawerTableDataSearchKeywords] = useState<string>('');
+
+    const [checkedTableMenuIds, set_checkedTableMenuIds] = useState<any[]>([]);
+
+    const columnsData: ProColumns<TableListItem>[] = [
+      {
+        title: '名称',
+        dataIndex: 'name',
+        width: 300,
+        ellipsis: true
+      },
+      {
+        title: '类型',
+        width: 50,
+        dataIndex: 'contentType',
+        key: 'contentType',
+        render: (_: any, record: any) => {
+          //console.log({ record });
+          switch (record.type) {
+            case 0:
+              return '目录'
+            case 1:
+              return '菜单'
+            case 2:
+              return 'api'
+            case 3:
+              return '系统'
+            case 4:
+              return '有数bi'
+            case 5:
+              return '体系'
+            case 6:
+              return '中心层'
+            case 7:
+              return '平台层'
+            default:
+              break;
+          }
+        },
+      },
+      {
+        title: '功能',
+        dataIndex: 'deptName',
+        key: 'deptName',
+        render: (_: any, record: any) => {
+
+          if (record.functionList) {
+            const options = record.functionList.map((item: any, index: number) => ({ label: item.name, value: item.code }))
+
+            const needItem = checkBoxCodes.filter(a => a.menuId == record.menuId);
+
+            const codes = needItem && needItem.length > 0 ? needItem[0].function.map((a: any) => a.code) : [];
+
+            const onCheckGroupChange = (checkedValue: CheckboxValueType[]) => {
+
+              if (checkedValue.length > 0) {
+                const _temp = checkBoxCodes;
+                const index = checkBoxCodes.findIndex((item) => item.menuId == record.menuId);
+
+
+                const needed = options.filter((item: any) => checkedValue.includes(item.value));
+                const transfered = needed.map((item: any) => ({ name: item.label, code: item.value }));
+
+                if (index >= 0) {
+                  //先去除旧的的对象
+                  _temp.splice(index, 1);
+                }
+
+                _temp.push({
+                  menuId: record.menuId,
+                  function: transfered
+                });
+
+                const menuIdsArr = _temp.map((item: any) => item.menuId);
+
+                set_checkedTableMenuIds([...menuIdsArr])
+
+                set_checkBoxCodes([..._temp]);
+              } else {
+                //取消选择
+                const _temp = checkBoxCodes;
+                const index = checkBoxCodes.findIndex((item) => item.menuId == record.menuId);
+
+                _temp.splice(index, 1);
+
+                const menuIdsArr = _temp.map((item: any) => item.menuId);
+                set_checkedTableMenuIds([...menuIdsArr])
+                set_checkBoxCodes([..._temp]);
+
+              }
+
+
+            }
+
+            return (
+              <Checkbox.Group
+                options={options}
+                value={codes}
+                onChange={(checkedValue: CheckboxValueType[]) => onCheckGroupChange(checkedValue)}
+              />
+            )
+          }
+          return '-'
+        }
+
+      },
+    ];
+
+    const getTreeReqFunc = async (name?: string) => {
+      const resp = await getTreeData();
+      set_treeData(resp);
+    }
+
+    const onSelect: TreeProps['onSelect'] = (selectedKeys, info) => {
+      //console.log('selected', selectedKeys, info);
+      const { node } = info;
+      set_currentSelectedTreeNode(node);
+    };
+
+    const dataList: any[] = [];
+
+    const getParentKey = (key: React.Key, tree: any[]): React.Key => {
+      let parentKey: React.Key;
+      for (let i = 0; i < tree.length; i++) {
+        const node = tree[i];
+        if (node.children) {
+          if (node.children.some((item: { code: React.Key; }) => item.code === key)) {
+            parentKey = node.code;
+          } else if (getParentKey(key, node.children)) {
+            parentKey = getParentKey(key, node.children);
+          }
+        }
+      }
+      return parentKey!;
+    };
+
+
+    const onTreeSearchKeyChange = (e: React.ChangeEvent<HTMLInputElement>) => {
+
+      const { value } = e.target;
+      const newExpandedKeys = dataList
+        .map((item) => {
+          if (item.name.indexOf(value) > -1) {
+            return getParentKey(item.code, treeData);
+          }
+          return null;
+        });
+
+      const b = newExpandedKeys.filter((item, i, self) => item && self.indexOf(item) === i);
+
+      setExpandedKeys(newExpandedKeys as React.Key[]);
+      setSearchValue(value);
+      setAutoExpandParent(true);
+    }
+
+
+    const onExpand = (newExpandedKeys: React.Key[]) => {
+      setExpandedKeys(newExpandedKeys);
+      setAutoExpandParent(false);
+    };
+
+    const generateList = (data: getTreeDataRespType[]) => {
+      for (let i = 0; i < data.length; i++) {
+        const node = data[i];
+        const { code, name } = node;
+        dataList.push({ code, name: name });
+        if (node.children) {
+          generateList(node.children);
+        }
+      }
+    };
+    generateList(treeData as any);
+
+
+    const getTableData = async (params: any, sort: any, filter: any) => {
+
+      set_drawerTablereload(false);
+      if (currentSelectedTreeNode) {
+        // console.log({params,record});
+        const resp = await getRolePermRelaMenu({ ...params, hospId: record.hospId,roleId:record.roleId});
+        if (resp) {
+          let temp: { menuId: any; function: any; }[] = [];
+          const setTreeRecursion = (data: any) => {
+            data.map((item: any, index: number) => {
+
+
+              if (item.type == 1 && item.functionCheckList) {
+                //菜单
+                temp.push({
+                  menuId: item.menuId,
+                  function: item.functionCheckList
+                });
+              }
+
+              if (item.children && item.children.length != 0) {
+                setTreeRecursion(item.children);
+              }
+
+            });
+
+            set_checkBoxCodes(temp);
+            set_checkedTableMenuIds(temp.map((a: any) => a.menuId));
+            return data;
+          }
+
+          setTreeRecursion(resp)
+
+          return {
+            data: resp,
+            success: true,
+          }
+        }
+        return {
+          data: [],
+          success: true
+        }
+      }
+      return []
+    }
+
+    const saveResult = async () => {
+      const result = checkBoxCodes.map((item: any) => ({ ...item, hospId: record.hospId,roleId:record.roleId }))
+      const resp = await saveRoleRelaApiPerm(result);
+      if (resp) {
+        set_drawerTablereload(true);
+      }
+
+    }
+
+    const onCancel = () => {
+      set_drawerVisible(false);
+    }
+
+    const drawerTableDataSearchHandle = (paramName: string) => {
+
+
+      set_drawerTableDataFilterParams({
+        ...drawerTableDataFilterParams,
+        [`${paramName}`]: drawerTableDataSearchKeywords
+      })
+    }
+
+
+    const oneKeySetReadOnly = (bool:boolean)=>{
+       
+        if(bool){
+          const _temp = checkBoxCodes.map((item:any)=>{
+                const needed = item.function.filter((a:any)=>a.code == 'search');
+                return {...item,function:needed}
+          })
+          set_checkBoxCodes([..._temp]);
+        }
+
+    }
+
+
+    useEffect(() => {
+      if (currentSelectedTreeNode) {
+        set_drawerTableDataFilterParams({ ...tableDataFilterParams, systemId: currentSelectedTreeNode.code })
+      }
+
+      //切换系统清空数据
+      set_checkBoxCodes([]);
+      set_checkedTableMenuIds([]);
+
+    }, [currentSelectedTreeNode]);
+
+    useEffect(() => {
+      //初始化左侧树结构数据后
+
+      if (treeData?.length > 0) {
+
+        if (treeData[0].children && treeData[0].children.length > 0) {
+          const [node, nodeParent] = getDeepestTreeData(treeData[0], 'children');
+
+          set_currentSelectedTreeNode(node);
+          setExpandedKeys([nodeParent.code]);
+        }
+      }
+    }, [treeData]);
+
+
+
+    useEffect(() => {
+      getTreeReqFunc();
+
+    }, []);
+
+
+    return (
+      <DrawerForm
+        trigger={
+          <a key="link3" onClick={(e) => { set_drawerVisible(true); }}>功能</a>
+        }
+        width={908}
+        // visible={drawerVisible}
+        drawerProps={{
+          open: drawerVisible,
+          closable: false,
+          destroyOnClose: true,
+          extra: <div>anniu</div>,
+        }}
+        submitter={false}
+      >
+        <div className='setApiPermDrawer'>
+          <div className='topbar'>
+            <div className='title'>{`院区功能权限设置(${record.hospName})`}</div>
+            <div className='btnGroup'>
+              <span className='cancel' onClick={() => onCancel()}>取消</span>
+              <span className='save' onClick={() => saveResult()}>保存</span>
+            </div>
+          </div>
+          <div className='content'>
+            <div className='leftTree'>
+              <div className='search'>
+                <Input
+                  className='searchInput'
+                  placeholder="请输入"
+                  size='small'
+                  allowClear
+
+                  style={{ marginBottom: 16 }}
+                  onChange={onTreeSearchKeyChange}
+                  suffix={
+                    <SearchIcon type='iconsousuo' />
+                  }
+                />
+              </div>
+              {
+                treeData && treeData.length > 0 && (
+                  <DirectoryTree
+                    fieldNames={{ title: 'name', key: 'code' }}
+                    rootStyle={{ height: '100%', paddingBottom: 50, overflowY: 'scroll', overflowX: 'hidden' }}
+                    onSelect={onSelect}
+                    onExpand={onExpand}
+                    expandedKeys={expandedKeys}
+                    autoExpandParent={autoExpandParent}
+                    selectedKeys={currentSelectedTreeNode ? [currentSelectedTreeNode.code] : []}
+                    blockNode={true}
+                    icon={() => null}
+                    titleRender={
+                      (nodeData: any) => {
+
+                        const strTitle = nodeData.name as string;
+                        const index = strTitle.indexOf(searchValue);
+                        const beforeStr = strTitle.substring(0, index);
+                        const afterStr = strTitle.slice(index + searchValue.length);
+                        const title =
+                          index > -1 ? (
+                            <span>
+                              {beforeStr}
+                              <span className="site-tree-search-value" style={{ color: 'red', overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>{searchValue}</span>
+                              {afterStr}
+                            </span>
+                          ) : (
+                            <span className='strTitle'>{strTitle}</span>
+                          );
+                        return <div style={{
+                          display: 'flex', flexDirection: 'row',
+                          width: '100%',
+                          justifyContent: 'flex-start', alignItems: 'center', height: 32,
+                          borderRadius: '4px',
+                          overflow: 'hidden',
+                          color: '#17181A',
+                          textOverflow: 'ellipsis',
+                          whiteSpace: 'nowrap'
+
+                        }}>{title}</div>
+                      }
+                    }
+                    defaultSelectedKeys={[treeData[0].children[0].code]}
+                    treeData={treeData as unknown as DataNode[]}
+                    switcherIcon={(props: any) => {
+                      const { expanded } = props;
+                      //return <button className='site-table-row-expand-icon site-table-row-expand-icon-expanded'></button>
+                      return !expanded ? <img style={{ width: 20, height: 20, position: 'relative', top: 5 }} src={expandedIcon} /> : <img style={{ width: 20, height: 20, position: 'relative', top: 5 }} src={closeIcon} />
+                    }}
+                  />
+                )
+              }
+            </div>
+            {/* <div style={{width:16,height:'92vh',background:'#F5F7FA'}}></div> */}
+            <div className='rightContent'>
+              <div className='tableToolbar'>
+                <div className='filter'>
+                  <div className='filterItem'>
+                    <span className='label'>检索:</span>
+                    <KCInput placeholder={'请输入'} style={{ width: 160 }} search allowClear
+                      onChange={(e) => {
+                        set_drawerTableDataSearchKeywords(e.target.value);
+                        if (e.target.value.length == 0) {
+                          set_drawerTableDataFilterParams({
+                            ...drawerTableDataFilterParams,
+                            name: ''
+                          });
+                        }
+                      }}
+                      onSearch={() => drawerTableDataSearchHandle('name')}
+
+                    />
+                  </div>
+                </div>
+
+                <div className={'btnGroup'}>
+                     {/* <a style={{color: '#17181A'}}><Switch style={{position:'relative',marginRight:4}} size='small'  onChange={(bool)=>oneKeySetReadOnly(bool)} />只读</a> */}
+                  {/* <UpDataActBtn key={'act'} record={undefined} type='ADD' /> */}
+                </div>
+
+              </div>
+              {currentSelectedTreeNode && <KCTable
+                rowSelection={{
+                  // 自定义选择项参考: https://ant.design/components/table-cn/#components-table-demo-row-selection-custom
+                  // 注释该行则默认不显示下拉选项
+                  checkStrictly: false,
+                  onChange(selectedRowKeys, selectedRows, info) {
+                    //console.log({selectedRowKeys, selectedRows, info});
+                    if (selectedRowKeys.length == 0) {
+                      set_checkedTableMenuIds([]);
+                    }
+                    if (selectedRows.length == 0) {
+                      set_checkBoxCodes([]);
+                    }
+                  },
+                  selectedRowKeys: checkedTableMenuIds,
+                  selections: [Table.SELECTION_ALL, Table.SELECTION_INVERT],
+                  onSelect: (record, selected, selectedRows, nativeEvent) => {
+                    //console.log({ record, selected, selectedRows, nativeEvent });
+                    let checkedData = checkBoxCodes;
+                    if (selected) {
+                      //选中
+                      selectedRows.forEach(a => {
+                        if (a.functionList) {
+                          checkedData.push({
+                            menuId: a.menuId,
+                            function: a.functionList
+                          })
+                        }
+                      });
+                      //更新表格row选中状态
+                      if (record.type == 0) {
+                        //选中的是目录
+                        const ids = record.children.map((item: any) => item.menuId);
+                        set_checkedTableMenuIds([...checkedTableMenuIds, ...ids]);
+                      }
+                      if (record.type == 1) {
+                        //菜单
+                        set_checkedTableMenuIds([...checkedTableMenuIds, record.menuId]);
+
+                      }
+                    } else {
+                      if (record.type == 0) {
+                        //目录
+                        if (record.children) {
+                          record.children.forEach((element: any) => {
+                            const index = checkedData.findIndex((item) => item.menuId == record.menuId);
+                            checkedData.splice(index, 1);
+                          });
+                        }
+
+                        const ids = record.children.map((item: any) => item.menuId);
+                        const leftIds = checkedTableMenuIds.filter((id: number) => !ids.includes(id)); //筛选出剩余选中项
+                        set_checkedTableMenuIds([...leftIds]);
+
+                      } else if (record.type == 1) {
+                        //菜单
+                        const temp = checkedTableMenuIds;
+                        const index = checkedData.findIndex((item) => item.menuId == record.menuId);
+                        const menuIdIndex = checkedTableMenuIds.findIndex((item) => item == record.menuId);
+                        checkedData.splice(index, 1);
+                        temp.splice(menuIdIndex, 1);
+                        set_checkedTableMenuIds([...temp]);
+                      }
+                    }
+
+                    //console.log({ checkedData });
+
+                    set_checkBoxCodes([...checkedData]);
+                  }
+                }}
+                pagination={false} reload={drawerTablereload} newVer params={drawerTableDataFilterParams} rowKey='menuId' columns={columnsData as ProColumns[]} request={(params: any, sort: any, filter: any) => getTableData(params, sort, filter)} />}
+
+            </div>
+          </div>
+        </div>
+
+      </DrawerForm>
+    )
+  }
+
   const getData = async (params: TableRequestParamsType) => {
     const { current = 1, pageSize = 10, roleName } = params;
     const resp = await getAllRoles({
@@ -164,39 +697,86 @@ const RoleManage: FC<PageProps> = ({ roleManageModel: state, dispatch }) => {
       });
   };
 
+  const tableDataSearchHandle = (paramName: string) => {
+
+
+    set_tableDataFilterParams({
+      ...tableDataFilterParams,
+      [`${paramName}`]: tableDataSearchKeywords
+    })
+  }
+
   const editUserBind = async (record: TableListItem) => {
     //编辑角色与人员的绑定关系
-    const { userIds = [],users=[] } = await getRoleHasBindUsers({
+    const { userIds = [], users = [] } = await getRoleHasBindUsers({
       hospId: record.hospId,
       roleId: record.roleId,
     });
     dispatch &&
       dispatch({
-        type: 'roleManageModel/edit',
+        type: 'roleManageModel/reloadTable',
         payload: {
-          data: { ...record, bindUserIds: userIds,allUsers:users },
+          data: { ...record, bindUserIds: userIds, allUsers: users },
           act: TableActType.EDITRELAUSER,
           isShowModal: true,
         },
       });
   };
 
+  const initRoleData =async (type:string,record:any) => {
+        if(type == 'func'){
+              const resp = await initRoleFunc({roleId:record.roleId,hospId:record.hospId});
+              if(resp){
+                dispatch &&
+                dispatch({
+                  type: 'roleManageModel/reloadTable',
+                });
+              }
+        }
+  }
+  
+
   //  console.log({state});
 
   return (
     <div className="RoleManage">
       <ActModal {...state} dispatch={dispatch} />
 
+      <div className='toolBar'>
+        <div className='filter'>
+          <div className='filterItem'>
+            <span className='label'>检索:</span>
+            <KCInput placeholder={'请输入角色名称'} style={{ width: 160 }} search allowClear
+              onChange={(e) => {
+                set_tableDataSearchKeywords(e.target.value);
+                if (e.target.value.length == 0) {
+                  set_tableDataFilterParams({
+                    ...tableDataFilterParams,
+                    hospName: ''
+                  });
+                }
+              }}
+              onSearch={() => tableDataSearchHandle('hospName')}
+
+            />
+          </div>
+        </div>
+        <div className='btnGroup'>
+          <span className='add' onClick={addHandle}>新增</span>
+        </div>
+      </div>
+      
       <KCTable
         rowKey="roleId"
-        headerTitle="查询表格"
         columns={columns}
+        options={false}
+        newVer
         reload={reloadTable}
-        toolBarRender={() => [
-          <Button key="3" type="primary" onClick={addHandle}>
-            新增角色
-          </Button>,
-        ]}
+        // toolBarRender={() => [
+        //   <Button key="3" type="primary" onClick={addHandle}>
+        //     新增角色
+        //   </Button>,
+        // ]}
         request={(params) => getData(params)}
       />
     </div>

+ 2 - 2
src/pages/platform/setting/roleManage/modals/modal.tsx

@@ -1,7 +1,7 @@
 /*
  * @Author: your name
  * @Date: 2022-01-12 17:11:11
- * @LastEditTime: 2022-07-07 09:47:36
+ * @LastEditTime: 2023-03-17 16:33:16
  * @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/userManage/modal.tsx
@@ -88,7 +88,7 @@ const ActModal: React.FC<ActModalProps> = ({ dispatch, isShowModal, tableAct, cu
       visible={isShowModal}
       onVisibleChange={onVisibleChangeHandle}
       layout="horizontal"
-      width={600}
+      width={500}
       initialValues={setInitialValues()}
       title={setModalTitle()}
       labelCol={{

+ 43 - 0
src/pages/platform/setting/roleManage/style.less

@@ -0,0 +1,43 @@
+.RoleManage {
+  height: auto;
+  padding: 16px;
+  background: #fff;
+
+  .toolBar {
+    display: flex;
+    flex-direction: row;
+    justify-content: space-between;
+    align-items: center;
+    margin-bottom: 16px;
+
+    .filter {
+      display: flex;
+      flex-direction: row;
+      justify-content: flex-start;
+      align-items: center;
+
+      .filterItem {
+        display: flex;
+        flex-direction: row;
+        justify-content: 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;
+      }
+    }
+
+  }
+}

+ 777 - 0
src/pages/platform/setting/systemNavMana/index.tsx

@@ -0,0 +1,777 @@
+
+
+/*
+ * @Author: code4eat awesomedema@gmail.com
+ * @Date: 2022-12-16 09:42:52
+ * @LastEditors: code4eat awesomedema@gmail.com
+ * @LastEditTime: 2023-03-21 10:17:27
+ * @FilePath: /BudgetManaSystem/src/pages/budgetMana/monthlySet/index.tsx
+ * @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
+ */
+
+
+
+
+
+import { useEffect, useImperativeHandle, useRef, useState } from 'react';
+import './style.less';
+
+import { TreeProps, Input, Transfer, Popconfirm, Modal, TransferProps, Table } from 'antd';
+import { DataNode } from 'antd/es/tree';
+
+import expandedIcon from '../../../../../public/images/treenode_open.png';
+import closeIcon from '../../../../../public/images/treenode_collapse.png';
+import type { ColumnsType, TableRowSelection } from 'antd/es/table/interface';
+
+import { createFromIconfontCN } from '@ant-design/icons';
+import { addData, delData, editData, getMenuRelaActDic, getTableDataRequest, getTreeData, getTreeDataRespType, updateFuncDic } from './service';
+import { TransferDirection, TransferItem } from 'antd/es/transfer';
+
+import difference from 'lodash/difference';
+
+import 'dayjs/locale/zh-cn';
+import locale from 'antd/es/date-picker/locale/zh_CN';
+
+import React from 'react';
+import DirectoryTree from 'antd/es/tree/DirectoryTree';
+import { ActionType, ProColumns } from '@ant-design/pro-table';
+import KCTable from '@/components/kcTable';
+import { getDeepestTreeData } from '@/utils';
+import ProForm, { ModalForm, ProFormDependency, ProFormDigit, ProFormItem, ProFormRadio, ProFormSelect, ProFormText, ProFormTextArea } from '@ant-design/pro-form';
+import { KCInput } from '@/components/KCInput';
+import { UserRelaSeletDataListType } from '@/service/user';
+
+
+
+const IconFont = createFromIconfontCN({
+    scriptUrl: '//at.alicdn.com/t/c/font_1927152_4nm5kxbv4m3.js',
+});
+
+const SearchIcon = createFromIconfontCN({
+    scriptUrl: '//at.alicdn.com/t/c/font_1927152_g1njmm1kh7b.js',
+});
+
+
+
+
+
+export type TableListItem = {
+    key: number;
+    name: string;
+};
+
+
+const MonthlyInfoCheck: React.FC = () => {
+
+    const [treeData, set_treeData] = useState<getTreeDataRespType[]>([]);
+    const [currentSelectedTreeNode, set_currentSelectedTreeNode] = useState<any | undefined>();
+
+    const [reload, set_reload] = useState(false);
+
+    const [tableDataFilterParams, set_tableDataFilterParams] = useState<any | undefined>();
+
+    const [expandedKeys, setExpandedKeys] = useState<React.Key[]>([]);
+    const [searchValue, setSearchValue] = useState('');
+    const [autoExpandParent, setAutoExpandParent] = useState(true);
+    const [tableDataSearchKeywords, set_tableDataSearchKeywords] = useState<string>('');
+
+    const [currentOperateRow, set_currentOperateRow] = useState<any | undefined>(undefined);  //当前操作的表格行数据
+
+
+    const tableRef = useRef<ActionType>();
+
+
+    const transferTableColumn = [
+        {
+            title: '功能名称',
+            dataIndex: 'name',
+            key: 'name',
+
+        },
+        {
+            title: '功能代码',
+            dataIndex: 'code',
+            key: 'code',
+
+        },
+    ];
+
+
+    const columns = [
+        {
+            title: '名称',
+            dataIndex: 'name',
+            key: 'name',
+
+        },
+        {
+            title: '类型',
+            dataIndex: 'contentType',
+            key: 'contentType',
+            render: (_: any, record: any) => {
+                //console.log({ record });
+                switch (record.type) {
+                    case 0:
+                        return '目录'
+                    case 1:
+                        return '菜单'
+                    case 2:
+                        return 'api'
+                    case 3:
+                        return '系统'
+                    case 4:
+                        return '有数bi'
+                    case 5:
+                        return '体系'
+                    case 6:
+                        return '中心层'
+                    case 7:
+                        return '平台层'
+                    default:
+                        break;
+                }
+            },
+        },
+        {
+            title: 'Path',
+            dataIndex: 'path',
+            key: 'path',
+
+        },
+        {
+            title: '内容',
+            dataIndex: 'contentType',
+            key: 'contentType',
+            valueEnum: {
+                0: '一般',
+                1: '报告',
+                2: '大屏',
+                3: '填报',
+                4: '空白'
+            }
+
+        },
+        {
+            title: '功能',
+            dataIndex: 'deptName',
+            key: 'deptName',
+            render: (_: any, record: any) => {
+                if (record.functionList) {
+                    return record.functionList.reduce((prev: any, cur: any) => {
+                        return `${prev ? prev + ' | ' : prev}${cur.name}`
+                    }, '')
+                }
+                return '-'
+            }
+
+        },
+        {
+            title: '操作',
+            key: 'option',
+            width: 120,
+            fixed:'right',
+            valueType: 'option',
+            render: (_: any, record: any) => {
+                return record.type != 1 && record.type != 2 && record.type != 4 ? [
+                    <UpDataActBtn key={'add'} record={record} type='ADD' />,
+                    <UpDataActBtn key={'act'} record={record} type='EDIT' />,
+                    <Popconfirm
+                        title="是否确认删除?"
+                        key="del"
+                        onConfirm={() => delMenuHandle(record)}
+                    >
+                        <a>删除</a>
+                    </Popconfirm>
+                ] : [
+                    <a key={'fuc'} onClick={() => addFuncHandle(record)}>功能</a>,
+                    <UpDataActBtn key={'act'} record={record} type='EDIT' />,
+                    <Popconfirm
+                        title="是否确认删除?"
+                        key="del"
+                        onConfirm={() => delMenuHandle(record)}
+                    >
+                        <a>删除</a>
+                    </Popconfirm>
+                ]
+            },
+        },
+    ]
+
+
+    const onSelect: TreeProps['onSelect'] = (selectedKeys, info) => {
+        //console.log('selected', selectedKeys, info);
+        const { node } = info;
+        set_currentSelectedTreeNode(node);
+    };
+
+
+    const getTableData = async (params: any, sort: any, filter: any) => {
+        set_reload(false);
+        if (currentSelectedTreeNode) {
+            const resp = await getTableDataRequest(params);
+            if (resp) {
+                return {
+                    data: resp,
+                    success: true,
+                }
+            }
+            return {
+                data: [],
+                success: true
+            }
+        }
+
+        return []
+    }
+
+    const addFuncHandle = (record: any) => {
+        set_currentOperateRow(record);
+        const ref = React.createRef<{ save: any; }>();
+
+        Modal.confirm({
+            title: '菜单功能设置(核算单元管理)',
+            icon: '',
+            width: 672,
+            content: <TableTransfer
+                ref={ref}
+                record={record}
+                leftColumns={transferTableColumn}
+                rightColumns={transferTableColumn} dataSource={[]}
+            ></TableTransfer>,
+            onOk: () => {
+                return ref.current && ref.current.save();
+            }
+        })
+    }
+
+
+    const delMenuHandle = async (record:any) => {
+         const resp = await delData(record.menuId);
+         resp&&set_reload(true);
+    }
+
+    interface DataType {
+        key: string;
+        title: string;
+        description: string;
+        disabled: boolean;
+        tag: string;
+    }
+
+    interface TableTransferProps extends TransferProps<TransferItem> {
+        dataSource: DataType[];
+        leftColumns: ColumnsType<DataType>;
+        rightColumns: ColumnsType<DataType>;
+        record: any
+    }
+
+    const TableTransfer = React.forwardRef(({ leftColumns, rightColumns, record, ...restProps }: TableTransferProps, ref) => {
+
+        const [_data, _set_data] = useState<any>();
+        const [targetKeys, setTargetKeys] = useState<string[]>([]);
+        const [datasource, set_datasource] = useState<any[]>([]);
+        const [selectedKeys, setSelectedKeys] = useState<string[]>([]);
+
+        //获取页面权限字典
+        const getFuncList = async () => {
+            const resp = await getMenuRelaActDic();
+            if (resp) {
+
+                set_datasource(resp);
+              
+                if (record && record.functionList) {
+
+                    const defaultSelctedkeys = record.functionList.map((item: any) => item.code);
+                   
+                    setTargetKeys(defaultSelctedkeys);
+                }
+
+            }
+        }
+
+        const onChange = (nextTargetKeys: string[]) => {
+            setTargetKeys(nextTargetKeys);
+        };
+
+        const onSelectChange = (sourceSelectedKeys: string[], targetSelectedKeys: string[]) => {
+            //console.log('sourceSelectedKeys:', sourceSelectedKeys,'targetSelectedKeys:',targetSelectedKeys);
+            setSelectedKeys([...sourceSelectedKeys, ...targetSelectedKeys]);
+        };
+
+        useImperativeHandle(ref, () => ({
+            save: async () => {
+
+                const data = datasource.filter(item=>targetKeys.includes(item.code));
+             
+                const resp = await updateFuncDic({
+                       menuId:record.menuId,
+                       function:data
+                });
+                if (resp) {
+                    set_reload(true);
+                }
+            }
+        }));
+
+        useEffect(() => {
+            getFuncList();
+        }, [])
+
+        return (
+            <Transfer className='TableTransfer' showSearch
+                titles={['待选项目', '已选项目']}
+                locale={{
+                    itemUnit: '项',
+                    itemsUnit: '项',
+                    searchPlaceholder: '请输入'
+                }}
+                onChange={onChange}
+                onSelectChange={onSelectChange}
+                dataSource={datasource}
+                rowKey={record => record.code}
+                targetKeys={targetKeys}
+                selectedKeys={selectedKeys}
+                filterOption={(inputValue, item) => {
+                   return  item.name!.indexOf(inputValue) !== -1
+                
+                }}
+            >
+                {({
+                    direction,
+                    filteredItems,
+                    onItemSelectAll,
+                    onItemSelect,
+                    selectedKeys: listSelectedKeys,
+                    disabled: listDisabled,
+                }) => {
+
+                    // console.log({ filteredItems, listSelectedKeys,direction });
+                    const columns = direction === 'left' ? leftColumns : rightColumns;
+
+                    const rowSelection: TableRowSelection<TransferItem> = {
+                        getCheckboxProps: (item) => ({ disabled: listDisabled || item.disabled }),
+                        onSelectAll(selected, selectedRows) {
+                            const treeSelectedKeys = selectedRows.map(({ code }) => code);
+                            const diffKeys = selected
+                                ? difference(treeSelectedKeys, listSelectedKeys)
+                                : difference(listSelectedKeys, treeSelectedKeys);
+                            onItemSelectAll(diffKeys as string[], selected);
+                        },
+                        onSelect({ code }, selected) {
+                            onItemSelect(code as string, selected);
+                        },
+                        selectedRowKeys: listSelectedKeys,
+                    };
+
+                    return (
+                        <Table
+                            rowSelection={rowSelection}
+                            columns={columns as TransferItem[]}
+                            dataSource={filteredItems}
+                            size="small"
+                            rowKey={'code'}
+                            style={{ pointerEvents: listDisabled ? 'none' : undefined }}
+                            onRow={({ code, disabled: itemDisabled }) => ({
+                                onClick: () => {
+                                    if (itemDisabled || listDisabled) return;
+                                    onItemSelect(code as string, !listSelectedKeys.includes(code as string));
+                                },
+                            })}
+                        />
+                    );
+                }}
+            </Transfer>
+        )
+    })
+
+
+    
+
+    const dataList: any[] = [];
+
+    const getParentKey = (key: React.Key, tree: any[]): React.Key => {
+        let parentKey: React.Key;
+        for (let i = 0; i < tree.length; i++) {
+            const node = tree[i];
+            if (node.children) {
+                if (node.children.some((item: { code: React.Key; }) => item.code === key)) {
+                    parentKey = node.code;
+                } else if (getParentKey(key, node.children)) {
+                    parentKey = getParentKey(key, node.children);
+                }
+            }
+        }
+        return parentKey!;
+    };
+
+
+    const onTreeSearchKeyChange = (e: React.ChangeEvent<HTMLInputElement>) => {
+
+        const { value } = e.target;
+        const newExpandedKeys = dataList
+            .map((item) => {
+                if (item.name.indexOf(value) > -1) {
+                    return getParentKey(item.code, treeData);
+                }
+                return null;
+            });
+
+        const b = newExpandedKeys.filter((item, i, self) => item && self.indexOf(item) === i);
+
+        setExpandedKeys(newExpandedKeys as React.Key[]);
+        setSearchValue(value);
+        setAutoExpandParent(true);
+    }
+
+
+    const onExpand = (newExpandedKeys: React.Key[]) => {
+        setExpandedKeys(newExpandedKeys);
+        setAutoExpandParent(false);
+    };
+
+    const generateList = (data: getTreeDataRespType[]) => {
+        for (let i = 0; i < data.length; i++) {
+            const node = data[i];
+            const { code, name } = node;
+            dataList.push({ code, name: name });
+            if (node.children) {
+                generateList(node.children);
+            }
+        }
+    };
+    generateList(treeData as any);
+
+
+    const getTreeReqFunc = async (name?: string) => {
+        const resp = await getTreeData();
+        set_treeData(resp);
+    }
+
+    const tableDataSearchHandle = (paramName: string) => {
+
+
+        set_tableDataFilterParams({
+            ...tableDataFilterParams,
+            [`${paramName}`]: tableDataSearchKeywords
+        })
+    }
+
+
+    const updateTable = async (type: 'EDIT' | "ADD",formVal: any,record:any) => {
+
+
+
+        if (type == 'ADD') {
+            let data = undefined;
+            if(record){
+                  //添加操作
+                  data = {...formVal,parentId:record.menuId,systemId:record.systemId}
+            }else{
+                 //新增
+                 data = {...formVal,parentId:currentSelectedTreeNode.code,systemId:currentSelectedTreeNode.code}
+            }
+
+            const resp = await addData({ ...data});
+
+            if (resp) {
+                set_reload(true);
+            }
+        }
+        if (type == 'EDIT') {
+            
+            const resp = await editData({...record,...formVal});
+            if (resp) {
+                set_reload(true);
+            }
+        }
+
+        set_currentOperateRow(undefined);
+
+        return true
+    }
+
+
+    const UpDataActBtn = ({ record, type }: { record: any, type: 'EDIT' | 'ADD' }) => {
+
+        return (
+
+            <ModalForm
+                className='systemNavManaForm'
+                title={`${type == 'EDIT' ? '编辑' : '新增'}菜单`}
+                width={688}
+                initialValues={type == 'EDIT' ? { ...record } : { isHomepage: 0 }}
+                trigger={
+                    type == 'EDIT' ? <a key="edit" >编辑</a> : record ? <a className='add'>添加</a> : <span>新增</span>
+                }
+                grid={true}
+
+                onFinish={(val) => {
+                    set_currentOperateRow(record);
+
+                    return updateTable(type,val,record)
+                       
+                }}
+            >
+                <ProFormText
+                    name="name"
+                    label="名称:"
+                    placeholder="请输入"
+                    colProps={{ span: 12 }}
+                    rules={[{ required: true, message: '名称不能为空!' }]}
+                />
+                <ProFormSelect
+                    name="type"
+                    label="类型:"
+                    placeholder="请输入"
+                    colProps={{ span: 12 }}
+                    options={[
+                        {
+                            label: '目录',
+                            value: 0
+                        },
+                        {
+                            label: '菜单',
+                            value: 2
+                        },
+                        {
+                            label: '有数bi',
+                            value: 4
+                        }
+                    ]}
+                    rules={[{ required: true, message: '类型不能为空!' }]}
+                />
+                <ProFormText
+                    name="path"
+                    label="Path:"
+                    placeholder="请输入"
+                    colProps={{ span: 12 }}
+                    rules={[{ required: true, message: 'Path不能为空!' }]}
+                />
+                <ProFormText
+                    name="icon"
+                    label="icon:"
+                    placeholder="请输入"
+                    colProps={{ span: 12 }}
+                //rules={[{ required: true, message: '类型代码不能为空!' }]}
+                />
+                <ProFormDigit
+                    name="orderNum"
+                    label="顺序号:"
+                    width={'xs'}
+                    placeholder="请输入"
+                    colProps={{ span: 12 }}
+                //rules={[{ required: true, message: '类型代码不能为空!' }]}
+                />
+                <ProFormRadio.Group
+                    name="isHomepage"
+                    label="是否首页:"
+                    width={'xs'}
+                    placeholder="请选择"
+                    colProps={{ span: 12 }}
+                    rules={[{ required: true, message: '是否为首页不能为空!' }]}
+                    options={[
+                        {
+                            label: '是',
+                            value: 1,
+                        },
+                        {
+                            label: '否',
+                            value: 0,
+                        },
+                    ]}
+                //rules={[{ required: true, message: '类型代码不能为空!' }]}
+                />
+                <ProForm.Group colProps={{ span: 12 }} align='start'>
+                    <ProFormSelect
+                        name="contentType"
+                        label="内容类型:"
+                        placeholder="请输入"
+                        width={100}
+                        colProps={{ span: 8 }}
+                        options={[
+                            { label: '一般', value: 0 },
+                            { label: '报告', value: 1 },
+                            { label: '大屏', value: 2 },
+                            { label: '填报', value: 3 },
+                            { label: '空白', value: 4 },
+                        ]}
+                        rules={[{ required: true, message: '类型不能为空!' }]}
+                    />
+                    <ProFormDependency name={['contentType']}>
+                        {
+                            ({ contentType }) => {
+                                return contentType == 1 && (
+                                    <div style={{ position: 'relative', top: 24 }}>
+                                        <ProFormText
+                                            noStyle
+                                            width={210}
+                                            className='reportId'
+                                            name="reportId"
+                                            placeholder="请输入报告Id"
+                                            colProps={{ span: 16 }}
+                                        //rules={[{ required: true, message: '类型代码不能为空!' }]}
+                                        />
+                                    </div>
+                                )
+                            }
+                        }
+                    </ProFormDependency>
+                </ProForm.Group>
+                <ProFormText
+                    name="url"
+                    label="URL:"
+                    placeholder="请输入"
+                    colProps={{ span: 12 }}
+                //rules={[{ required: true, message: '类型代码不能为空!' }]}
+                />
+                <ProFormTextArea
+                    name="description"
+                    label="描述:"
+                    placeholder="请输入"
+                    colProps={{ span: 24 }}
+
+                />
+
+            </ModalForm>
+        )
+    }
+
+
+
+    useEffect(() => {
+        if (currentSelectedTreeNode) {
+            set_tableDataFilterParams({ ...tableDataFilterParams, systemId: currentSelectedTreeNode.code })
+        }
+       
+    }, [currentSelectedTreeNode]);
+
+
+    useEffect(() => {
+        //初始化左侧树结构数据后
+
+        if (treeData?.length > 0) {
+
+            if (treeData[0].children && treeData[0].children.length > 0) {
+                const [node, nodeParent] = getDeepestTreeData(treeData[0], 'children');
+
+                set_currentSelectedTreeNode(node);
+                setExpandedKeys([nodeParent.code]);
+            }
+        }
+    }, [treeData]);
+
+
+
+
+    useEffect(() => {
+        getTreeReqFunc();
+    }, []);
+
+
+    return (
+        <div className='SystemNavMana'>
+            <div className='leftTree'>
+                <div className='search'>
+                    <Input
+                        className='searchInput'
+                        placeholder="请输入"
+                        size='small'
+                        allowClear
+
+                        style={{ marginBottom: 16 }}
+                        onChange={onTreeSearchKeyChange}
+                        suffix={
+                            <SearchIcon type='iconsousuo' />
+                        }
+                    />
+                </div>
+                {
+                    treeData && treeData.length > 0 && (
+                        <DirectoryTree
+                            fieldNames={{ title: 'name', key: 'code' }}
+                            rootStyle={{ height: '100%', paddingBottom: 50, overflowY: 'scroll', overflowX: 'hidden' }}
+                            onSelect={onSelect}
+                            onExpand={onExpand}
+                            expandedKeys={expandedKeys}
+                            autoExpandParent={autoExpandParent}
+                            selectedKeys={currentSelectedTreeNode ? [currentSelectedTreeNode.code] : []}
+                            blockNode={true}
+                            icon={() => null}
+                            titleRender={
+                                (nodeData: any) => {
+
+                                    const strTitle = nodeData.name as string;
+                                    const index = strTitle.indexOf(searchValue);
+                                    const beforeStr = strTitle.substring(0, index);
+                                    const afterStr = strTitle.slice(index + searchValue.length);
+                                    const title =
+                                        index > -1 ? (
+                                            <span>
+                                                {beforeStr}
+                                                <span className="site-tree-search-value" style={{ color: 'red', overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>{searchValue}</span>
+                                                {afterStr}
+                                            </span>
+                                        ) : (
+                                            <span className='strTitle'>{strTitle}</span>
+                                        );
+                                    return <div style={{
+                                        display: 'flex', flexDirection: 'row',
+                                        width: '100%',
+                                        justifyContent: 'flex-start', alignItems: 'center', height: 32,
+                                        borderRadius: '4px',
+                                        overflow: 'hidden',
+                                        color: '#17181A',
+                                        textOverflow: 'ellipsis',
+                                        whiteSpace: 'nowrap'
+
+                                    }}>{title}</div>
+                                }
+                            }
+                            defaultSelectedKeys={[treeData[0].children[0].code]}
+                            treeData={treeData as unknown as DataNode[]}
+                            // treeData={treeDataNew}
+                            switcherIcon={(props: any) => {
+                                const { expanded } = props;
+                                //return <button className='site-table-row-expand-icon site-table-row-expand-icon-expanded'></button>
+                                return !expanded ? <img style={{ width: 20, height: 20, position: 'relative', top: 5 }} src={expandedIcon} /> : <img style={{ width: 20, height: 20, position: 'relative', top: 5 }} src={closeIcon} />
+                            }}
+                        />
+                    )
+                }
+            </div>
+            {/* <div style={{width:16,height:'92vh',background:'#F5F7FA'}}></div> */}
+            <div className='rightContent'>
+                <div className='tableToolbar'>
+                    <div className='filter'>
+                        <div className='filterItem'>
+                            <span className='label'>检索:</span>
+                            <KCInput placeholder={'请输入菜单名称'} style={{ width: 160 }} search allowClear
+                                onChange={(e) => {
+                                    set_tableDataSearchKeywords(e.target.value);
+                                    if (e.target.value.length == 0) {
+                                        set_tableDataFilterParams({
+                                            ...tableDataFilterParams,
+                                            name: ''
+                                        });
+                                    }
+                                }}
+                            onSearch={() => tableDataSearchHandle('name')}
+
+                            />
+                        </div>
+                    </div>
+
+                    <div className={'btnGroup'}>
+                        <UpDataActBtn key={'act'} record={undefined} type='ADD' />
+                    </div>
+
+                </div>
+                {currentSelectedTreeNode && <KCTable pagination={false} reload={reload} newVer params={tableDataFilterParams}  rowKey='menuId' columns={columns as ProColumns[]} request={(params: any, sort: any, filter: any) => getTableData(params, sort, filter)} />}
+
+            </div>
+        </div>
+    );
+
+};
+
+export default MonthlyInfoCheck;

+ 172 - 0
src/pages/platform/setting/systemNavMana/service.ts

@@ -0,0 +1,172 @@
+/*
+ * @Author: code4eat awesomedema@gmail.com
+ * @Date: 2022-12-21 11:13:51
+ * @LastEditors: code4eat awesomedema@gmail.com
+ * @LastEditTime: 2023-03-15 16:40:17
+ * @FilePath: /BudgetManaSystem/src/pages/budgetMana/monthlyInfoCheck/service.ts
+ * @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
+ */
+
+
+
+
+import { request } from 'umi';
+
+
+
+
+//获取树结构数据
+
+export type getTreeDataRespType = {
+    code: number;
+    name: string;
+    children: getTreeDataRespType[]
+}
+
+export const getTreeData = (name?:string) => {
+
+    return request<getTreeDataRespType[]>('/centerSys/navigation/getSystemList', {
+        method: 'GET',
+        params: { name }
+    })
+}
+
+
+
+
+//获取表格数据
+
+export type SystemRelaMenuDataType = {
+    menuId:string;
+    parentId:string;
+    name:string;
+    path:string;
+    url:string;
+    softUrl:string;
+    perms:string;
+    type:number;
+    icon:string;
+    orderNum:number;
+    reportId:string;
+    contentType:number;
+    isHomepage:number;
+    children:SystemRelaMenuDataType[]
+}
+
+export const getTableDataRequest = (params:{systemId:string;name?:string}) => {
+
+    return request<SystemRelaMenuDataType[]>('/centerSys/navigation/getMenuList', {
+        method: 'GET',
+        params: { ...params }
+    })
+
+}
+
+
+//新增菜单
+
+export type AddMenuDataType = {
+    parentId:number;
+    name:string;
+    path:string;
+    url:string;
+    softUrl:string;
+    perms:string;
+    type:string;
+    icon:string;
+    orderNum:number;
+    reportId:string;
+    contentType:string;
+    isHomepage:number;
+    systemId:number;
+}
+
+export const addData = (data:AddMenuDataType) => {
+
+    return request('/centerSys/navigation/addMenu', {
+        method: 'POST',
+        data
+    })
+
+}
+
+
+//编辑菜单
+
+
+export type EditMenuDataType = {
+    parentId:number;
+    name:string;
+    path:string;
+    url:string;
+    softUrl:string;
+    perms:string;
+    type:string;
+    icon:string;
+    orderNum:number;
+    reportId:string;
+    contentType:string;
+    isHomepage:number;
+    systemId:number;
+    menuId:number;
+}
+
+export const editData = (data:EditMenuDataType) => {
+
+    return request('/centerSys/navigation/editMenu', {
+        method: 'POST',
+        data
+    })
+
+}
+
+
+//删除菜单
+export const delData = (menuId:number) => {
+
+    return request('/centerSys/navigation/deleteMenu', {
+        method: 'POST',
+        params:{menuId}
+    })
+
+}
+
+
+
+
+//获取功能字典
+
+export type  MenuRelaActDicItemType = {
+    code:string;
+    value:string;
+    name:string;
+}
+
+export const getMenuRelaActDic = () => {
+
+    return request<MenuRelaActDicItemType[]>('/centerSys/navigation/getFunctionDict', {
+        method: 'GET',
+    })
+
+}
+
+
+//保存功能字典
+
+
+export const updateFuncDic = (data:{
+    menuId:number;
+    function:{code:string;name:string}[]
+}) => {
+
+    return request('/centerSys/navigation/addFunction', {
+        method: 'POST',
+        data
+    })
+
+}
+
+
+
+
+

+ 135 - 0
src/pages/platform/setting/systemNavMana/style.less

@@ -0,0 +1,135 @@
+.SystemNavMana {
+  display: flex;
+  flex-direction: row;
+  // height: 100%;
+  background: #F5F7FA;
+
+
+  
+
+
+  .systemNavManaForm {}
+
+  .leftTree {
+    position: fixed;
+    top: 64px;
+    width: 220px;
+    height: calc(100vh - 80px);
+    background: #FFF;
+    border-radius: 4px;
+    padding: 16px;
+    overflow: hidden;
+    margin-right: 16px;
+    // border-right:16px solid #F5F7FA;
+
+    .searchInput {
+      border: 1px solid #CFD7E6;
+    }
+
+
+    .kcmp-ant-tree.kcmp-ant-tree-directory .kcmp-ant-tree-treenode-selected:hover::before,
+    .kcmp-ant-tree.kcmp-ant-tree-directory .kcmp-ant-tree-treenode-selected::before {
+      border-radius: 4px;
+      background: #F0F2F5;
+    }
+
+    .kcmp-ant-tree.kcmp-ant-tree-directory .kcmp-ant-tree-treenode .kcmp-ant-tree-node-content-wrapper.kcmp-ant-tree-node-selected {
+      font-weight: bold;
+    }
+
+  }
+
+  .rightContent {
+    position: relative;
+    border-radius: 4px;
+    width: calc(100% - 220px);
+    padding: 16px;
+    margin-left: 236px;
+    background: #FFF;
+
+    .checkBtn {
+      position: absolute;
+      cursor: pointer;
+      top: 0;
+      right: 0;
+      padding: 0 14px;
+      height: 24px;
+      line-height: 24px;
+      background: #3376FE;
+      border-radius: 4px;
+      text-align: center;
+      font-size: 14px;
+      font-family: SourceHanSansCN-Normal, SourceHanSansCN;
+      font-weight: 400;
+      color: #FFF;
+    }
+
+    .midLine {
+      display: flex;
+      flex-direction: row;
+      justify-content: center;
+      align-items: center;
+      height: 24px;
+      line-height: 24px;
+      background: #F0FCFC;
+      font-size: 12px;
+      color: #515866;
+      font-family: SourceHanSansCN-Normal, SourceHanSansCN;
+      font-weight: 400;
+      margin-top: 12px;
+      margin-bottom: 15px;
+
+      &>span {
+        cursor: pointer;
+        font-size: 12px;
+        font-family: SourceHanSansCN-Medium, SourceHanSansCN;
+        font-weight: 500;
+        color: #00B3B3;
+        padding-right: 4px;
+      }
+
+    }
+
+    .kcmp-ant-tabs-nav {
+      &::before {
+        border-bottom: none !important;
+      }
+    }
+
+    .tableToolbar {
+      display: flex;
+      flex-direction: row;
+      justify-content: space-between;
+      align-items: center;
+      margin-bottom: 16px;
+
+      .filter {
+        display: flex;
+        flex-direction: row;
+        justify-content: flex-start;
+        align-items: center;
+
+        .filterItem {
+          display: flex;
+          flex-direction: row;
+          justify-content: center;
+          align-items: center;
+        }
+      }
+
+      .btnGroup {
+        &>span {
+          display: inline-block;
+          text-align: center;
+          width: 56px;
+          height: 24px;
+          line-height: 24px;
+          background: #3377FF;
+          color: #FFF;
+          border-radius: 4px;
+          cursor: pointer;
+        }
+      }
+    }
+  }
+}

+ 128 - 22
src/pages/platform/setting/userManage/index.tsx

@@ -1,13 +1,13 @@
 /*
  * @Author: your name
  * @Date: 2022-01-11 09:43:18
- * @LastEditTime: 2022-07-14 08:59:21
+ * @LastEditTime: 2023-03-20 11:36:12
  * @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/userManage/index.tsx
  */
 
-import { FC } from 'react';
+import { FC, useState } from 'react';
 import { userManageModelState, ConnectProps, Loading, connect } from 'umi';
 import { Button, Divider, Popconfirm } from 'antd';
 import KCTable from '@/components/kcTable';
@@ -17,9 +17,11 @@ import { TableRequestParamsType } from '@/typings';
 import ActModal from './modal';
 import './style.less';
 import { TableListItem } from './typings';
-import { ProFormUploadButton } from '@ant-design/pro-form';
+import { ModalForm, ProFormSelect, ProFormUploadButton } from '@ant-design/pro-form';
 import KCUpload from '@/components/KCUpload';
 import { RcFile } from 'antd/lib/upload/interface';
+import { KCInput } from '@/components/KCInput';
+import FormItem from 'antd/es/form/FormItem';
 
 export enum TableActType {
   NOACT,
@@ -36,6 +38,10 @@ interface PageProps extends ConnectProps {
 const UserManage: FC<PageProps> = ({ userManageModel: state, dispatch }) => {
   const { reloadTable } = state;
 
+  const [tableDataFilterParams, set_tableDataFilterParams] = useState<any | undefined>();
+  const [tableDataSearchKeywords, set_tableDataSearchKeywords] = useState<string>('');
+
+
   const columns: ProColumns<TableListItem>[] = [
     {
       title: '院区',
@@ -176,36 +182,136 @@ const UserManage: FC<PageProps> = ({ userManageModel: state, dispatch }) => {
     }
   };
 
+  const tableDataSearchHandle = (paramName: string) => {
+
+
+    set_tableDataFilterParams({
+      ...tableDataFilterParams,
+      [`${paramName}`]: tableDataSearchKeywords
+    })
+  }
+
+  const importData = (name: string) => {
+
+    return (
+      <ModalForm
+        title={`导入${name}数据`}
+        width={352}
+        trigger={
+          <span key="3">导入</span>
+        }
+        onFinish={async (values:any) => {
+
+          const { importFile: { fileList } } = values;
+
+          let formData = new FormData();
+          formData.append('file', fileList[0].originFileObj);
+
+          dispatch &&
+          dispatch({
+            type: 'userManageModel/importUsers',
+            payload: formData,
+          });
+        }}
+      >
+        <FormItem name={'importFile'}>
+          <KCUpload downloadTemplateFile={() => getUserTemplateHandle()} />
+        </FormItem>
+
+      </ModalForm>
+    )
+  }
+
+
   //  console.log({state});
 
   return (
     <div className="UserManage">
       <ActModal  {...state} dispatch={dispatch} />
 
+      <div className='toolBar'>
+        <div className='filter'>
+          <div className='filterItem' style={{ marginRight: 16 }}>
+            <span className='label'>科室:</span>
+            <KCInput placeholder={'请输入'} style={{ width: 160 }} search allowClear
+              onChange={(e) => {
+                set_tableDataSearchKeywords(e.target.value);
+                if (e.target.value.length == 0) {
+                  set_tableDataFilterParams({
+                    ...tableDataFilterParams,
+                    account: ''
+                  });
+                }
+              }}
+              onSearch={() => tableDataSearchHandle('account')}
+
+            />
+          </div>
+          <div className='filterItem'>
+            <span className='label'>检索:</span>
+            <KCInput placeholder={'请输入工号'} style={{ width: 160 }} search allowClear
+              onChange={(e) => {
+                set_tableDataSearchKeywords(e.target.value);
+                if (e.target.value.length == 0) {
+                  set_tableDataFilterParams({
+                    ...tableDataFilterParams,
+                    name: ''
+                  });
+                }
+              }}
+              onSearch={() => tableDataSearchHandle('name')}
+
+            />
+          </div>
+        </div>
+        <div className='btnGroup'>
+          {importData('员工')}
+          <span className='add' onClick={addUserHandle}>新增</span>
+        </div>
+      </div>
+
       <KCTable
         rowKey="id"
         headerTitle="查询表格"
         columns={columns}
         reload={reloadTable}
-        toolBarRender={() => [
-          <KCUpload
-            title="导入数据"
-            fieldProps={{
-              onChange: ({ file, fileList }:{file:any,fileList:any}) => {
-                if (file.status == 'done') {
-                  importUserHandle(file.originFileObj);
-                }
-              },
-              showUploadList: false,
-            }}
-          />,
-          <Button key="2" onClick={() => getUserTemplateHandle()}>
-            模板下载
-          </Button>,
-          <Button key="3" onClick={addUserHandle}>
-            新增用户
-          </Button>,
-        ]}
+        newVer
+        expandable={{
+          expandedRowRender: record => (
+            <div className='userExpandInfo'>
+              <span>进院时间:</span>
+              <span>手机号:{record.phoneNumber}</span>
+              <span>所学专业:{record.major}</span>
+              <span>资格证号:{record.qualificationCertificateNo}</span>
+              <span>医师级别:{record.doctorLevel}</span>
+              <span>执业证号:{record.practiceCertificateNo}</span>
+              <span>执业类别:{record.practiceSubject}</span>
+              <span>执业科目:{record.practiceCate}</span>
+              <span>执业状态:{record.practiceStatus}</span>
+              <span>备注:{record.remark}</span>
+            </div>
+          ),
+          rowExpandable: () => true,
+        }}
+        // toolBarRender={() => [
+        //   <KCUpload
+        //     title="导入数据"
+        //     fieldProps={{
+        //       onChange: ({ file, fileList }:{file:any,fileList:any}) => {
+        //         if (file.status == 'done') {
+        //           importUserHandle(file.originFileObj);
+        //         }
+        //       },
+        //       showUploadList: false,
+        //     }}
+        //   />,
+        //   <Button key="2" onClick={() => getUserTemplateHandle()}>
+        //     模板下载
+        //   </Button>,
+        //   <Button key="3" onClick={addUserHandle}>
+        //     新增用户
+        //   </Button>,
+        // ]}
         request={(params) => getUserData(params)}
       />
     </div>

+ 187 - 107
src/pages/platform/setting/userManage/modal.tsx

@@ -1,7 +1,7 @@
 /*
  * @Author: your name
  * @Date: 2022-01-12 17:11:11
- * @LastEditTime: 2022-07-14 16:27:52
+ * @LastEditTime: 2023-03-20 17:28:48
  * @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/userManage/modal.tsx
@@ -10,12 +10,12 @@
 import React, { useEffect, useState } from 'react';
 import KCModal from '@/components/KCModal';
 import KCProSelect from '@/components/KCProSelect';
-import { message, Button, Spin, Upload } from 'antd';
+import { message, Button, Spin, Upload, UploadProps } from 'antd';
 import { userManageModelState, Dispatch } from 'umi';
-import { ProFormDateTimePicker, ProFormSelect, ProFormText, ProFormTextArea } from '@ant-design/pro-form';
+import { ProFormDateTimePicker, ProFormDependency, ProFormRadio, ProFormSelect, ProFormText, ProFormTextArea } from '@ant-design/pro-form';
 import { getShareHospList } from '@/service/hospList';
 import { AddUsersDataType, getUserRelaSeletData, UserRelaSeletDataListType, UserRelaSeletDataType } from '@/service/user';
-import { LoadingOutlined, UploadOutlined } from '@ant-design/icons';
+import { LoadingOutlined, PlusOutlined, UploadOutlined } from '@ant-design/icons';
 // import { TableActType } from "./typings";
 
 import './style.less';
@@ -40,20 +40,17 @@ const ActModal: React.FC<ActModalProps> = ({
   currentEditUser,
 }) => {
 
-
   const [avatarUrl, setAvatarUrl] = useState<string>('');
   const [loadAvatar, setLoadAvatar] = useState(false);
 
   //字典
   const [dirData, set_dirData] = useState<UserRelaSeletDataType[]>([]);
 
-  // let dirData: UserRelaSeletDataType[] = []
-
-  const props = {
+  const props: UploadProps = {
     name: 'file',
     maxCount: 1,
     showUploadList: false,
-    onChange(info: { file: { originFileObj: File, status: string; name: any; }; fileList: any; }) {
+    onChange(info: { file: any; fileList: any; }) {
 
       setLoadAvatar(true);
 
@@ -117,53 +114,62 @@ const ActModal: React.FC<ActModalProps> = ({
     let result = dirData.filter((t: any) => (t.code == key));
 
     if (result.length > 0) {
-      
+
       let dataArr = result[0].dataVoList;
 
-      let defaultValue = dataArr.filter(t=>t.defaultValue == 1);
+      let defaultValue = dataArr.filter(t => t.defaultValue == 1);
 
-      dataArr.sort((prev:UserRelaSeletDataListType,next:UserRelaSeletDataListType)=>{
+      dataArr.sort((prev: UserRelaSeletDataListType, next: UserRelaSeletDataListType) => {
         return prev.sort - next.sort;
       });
 
       // console.log({dataArr,defaultValue});
 
       return {
-           defaultvalue:defaultValue[0]?defaultValue[0].value:'',
-           list:dataArr.map(t=>({label:t.code,value:t.value}))
+        defaultvalue: defaultValue[0] ? defaultValue[0].value : '',
+        list: dataArr.map(t => ({ label: t.code, value: t.value }))
       }
     }
+
     return {
-      defaultvalue:'',
-      list:[]
+      defaultvalue: '',
+      list: []
     };
   }
 
+  const uploadButton = (
+    <div>
+      {loadAvatar ? <LoadingOutlined /> : <PlusOutlined />}
+      <div style={{ marginTop: 8 }}>Upload</div>
+    </div>
+  );
+
   useEffect(() => {
     getDirecData();
   }, [])
 
   return (
     <KCModal
+      width={720}
       visible={isShowModal}
       onVisibleChange={onVisibleChangeHandle}
-      layout="horizontal"
+      layout='vertical'
       className='ProfileModal'
       initialValues={
         tableAct == TableActType.EDIT ? { ...currentEditUser } : {
-               //设置当新增时的默认值
-               major:(setSelectorData('PROFESSIONAL_TYPE')).defaultvalue,
-               jobTitle:(setSelectorData('POSITION_TYPE')).defaultvalue,
-               doctorLevel:(setSelectorData('PHYSICIAN_TYPE')).defaultvalue,
-               practiceSubject:setSelectorData('PROCESSIONAL_SUBJECTS_TYPE').defaultvalue,
-               title:setSelectorData('TITLE_TYPE').defaultvalue,
-               practiceCate:setSelectorData('JOB_TYPE').defaultvalue,
-               practiceStatus:setSelectorData('PROFESSIONAL_STATUS_TYPE').defaultvalue
+          //设置当新增时的默认值
+          major: (setSelectorData('PROFESSIONAL_TYPE')).defaultvalue,
+          jobTitle: (setSelectorData('POSITION_TYPE')).defaultvalue,
+          doctorLevel: (setSelectorData('PHYSICIAN_TYPE')).defaultvalue,
+          practiceSubject: setSelectorData('PROCESSIONAL_SUBJECTS_TYPE').defaultvalue,
+          title: setSelectorData('TITLE_TYPE').defaultvalue,
+          practiceCate: setSelectorData('JOB_TYPE').defaultvalue,
+          practiceStatus: setSelectorData('PROFESSIONAL_STATUS_TYPE').defaultvalue
         }
       }
       title={tableAct == TableActType.EDIT ? '编辑用户' : '新增用户'}
-      labelCol={{ span: 5 }}
-      wrapperCol={{ span: 18 }}
+      // labelCol={{ span: 5 }}
+      // wrapperCol={{ span:8 }}
       onFinish={async (data) => onFinishHandle(data)}
     >
 
@@ -172,76 +178,48 @@ const ActModal: React.FC<ActModalProps> = ({
           <div>
             <div className='wraper'>
               <div className='left'>
+                <span style={{ fontSize: 14, color: '#17181A', marginBottom: 3, display: 'block' }}>照片:</span>
                 <div className='avatarContainer'>
-                  <span>个人照片:</span>
-                  <div className='avatar'>
-                    {avatarUrl && <img src={avatarUrl} alt="avatar" />}
-                    {loadAvatar && <Spin className='spin' indicator={antIcon} />}
-                  </div>
-                  <Upload {...props} className='UploadBtn'>
-                    <Button icon={<UploadOutlined />}>上传照片</Button>
+                  <Upload
+                    // name="avatar"
+                    listType="picture-card"
+                    showUploadList={false}
+                    {...props}
+                  >
+                    {avatarUrl ? <img src={avatarUrl} alt="avatar" style={{ width: '100%' }} /> : uploadButton}
                   </Upload>
                 </div>
+
                 <div className='formItem'>
-                  <KCProSelect
-                    label="院区"
-                    width="md"
-                    name="hospId"
-                    request={async () => {
-                      const hospList = await getShareHospList();
-                      if (hospList) {
-                        return hospList.map((t) => ({
-                          label: t.name,
-                          value: t.id,
-                        }));
-                      }
-                      return [];
-                    }}
-                    rules={[
+                  <ProFormRadio.Group
+                    name="isOnService"
+                    label='在职:'
+                    options={[
                       {
-                        required: true,
-                        message: '请选择院区!',
+                        label: '是',
+                        value: 0,
+                      },
+                      {
+                        label: '否',
+                        value: 1,
                       },
                     ]}
-                  />
-                </div>
-                <div className='formItem'>
-                  <ProFormSelect
-                    name="major"
-                    label="所学专业:"
-                    
-                    options={(setSelectorData('PROFESSIONAL_TYPE')).list}
-                    placeholder="请选择"
                     rules={[{ required: true, message: '请选择专业!' }]}
                   />
                 </div>
+
                 <div className='formItem'>
                   <ProFormSelect
-                    name="jobTitle"
-                    label="职务:"
-                    options={setSelectorData('POSITION_TYPE').list}
-                    placeholder="请选择"
-                    rules={[{ required: true, message: '请选择职务!' }]}
-                  />
-                </div>
-                <div className='formItem'>
-                  <ProFormText name="idCardNum" label="身份证号:" placeholder="请输入" />
-                </div>
-                <div className='formItem'>
-                  <ProFormText name="phoneNumber" label="手机号:" placeholder="请输入" />
-                </div>
-                <div className='formItem'>
-                  <ProFormText name="qualificationCertificateNo" label="资格证号:" placeholder="请输入" />
-                </div>
-                <div className='formItem'>
-                  <ProFormSelect
-                    name="doctorLevel"
-                    label="医师级别:"
-                    options={setSelectorData('PHYSICIAN_TYPE').list}
+                    name="position"
+                    label="岗位:"
+                    options={setSelectorData('POSITION').list}
                     placeholder="请选择"
+                    rules={[{ required: true, message: '请选择!' }]}
                   />
                 </div>
-                
+
+
+
               </div>
               <div className='right'>
                 <div className='formItem'>
@@ -262,68 +240,170 @@ const ActModal: React.FC<ActModalProps> = ({
                     rules={[{ required: true, message: '请选择性别!' }]}
                   />
                 </div>
-                {/* <div className='formItem'>
-                  <ProFormText name="departmentId" label="科室:" placeholder="请输入" rules={[{ required: true, message: '请选择!' }]} />
-                </div> */}
+                <div className='formItem'>
+                  <KCProSelect
+                    label="所属院区:"
+                    width="md"
+                    name="hospId"
+                    request={async () => {
+                      const hospList = await getShareHospList();
+                      if (hospList) {
+                        return hospList.map((t) => ({
+                          label: t.name,
+                          value: t.id,
+                        }));
+                      }
+                      return [];
+                    }}
+                    rules={[
+                      {
+                        required: true,
+                        message: '请选择院区!',
+                      },
+                    ]}
+                  />
+                </div>
                 <div className='formItem'>
                   <ProFormSelect
-                    name="title"
-                    label="职称:"
-                    options={setSelectorData('TITLE_TYPE').list}
+                    name="departmentId"
+                    label="科室:"
+                    options={setSelectorData('PRACTICE_DEPARTMENT_TYPE').list}
                     placeholder="请选择"
-                    rules={[{ required: true, message: '请选择职称!' }]}
                   />
                 </div>
                 <div className='formItem'>
-                  <ProFormDateTimePicker name="entryTime" width={282} label="入职时间" placeholder={'请选择日期'} />
+                  <ProFormSelect
+                    name="userCate"
+                    label="人员类别:"
+                    options={setSelectorData('PERSONNEL_CATEGORY').list}
+                    placeholder="请选择"
+                  />
                 </div>
+
                 <div className='formItem'>
-                  <ProFormText name="practiceCertificateNo" label="执业证号:" placeholder="请输入" />
+                  <ProFormDateTimePicker name="entryTime" label="入职时间" placeholder={'请选择日期'} rules={[{ required: true, message: '请选择日期!' }]} />
                 </div>
                 <div className='formItem'>
                   <ProFormSelect
-                    name="practiceCate"
-                    label="执业类别:"
-                    options={setSelectorData('JOB_TYPE').list}
+                    name="title"
+                    label="职称:"
+                    options={setSelectorData('TITLE_TYPE').list}
                     placeholder="请选择"
+                    rules={[{ required: true, message: '请选择职称!' }]}
                   />
                 </div>
                 <div className='formItem'>
                   <ProFormSelect
-                    name="practiceStatus"
-                    label="执业状态:"
-                    options={setSelectorData('PROFESSIONAL_STATUS_TYPE').list}
+                    name="jobTitle"
+                    label="职务:"
+                    options={setSelectorData('POSITION_TYPE').list}
                     placeholder="请选择"
+                    rules={[{ required: true, message: '请选择职务!' }]}
                   />
                 </div>
+                <ProFormDependency name={['isOnService']}>
+                  {({ isOnService }) => (
+                    <div className='formItem'>
+                      <ProFormDateTimePicker disabled={isOnService == 1} label='离职日期:' name="outTime" placeholder={'请选择离职时间'} />
+                    </div>
+                  )}
+                </ProFormDependency>
+
                 <div className='formItem'>
                   <ProFormSelect
-                    name="departmentId"
-                    label="科室:"
-                    options={setSelectorData('PRACTICE_DEPARTMENT_TYPE').list}
+                    name="title"
+                    label="学历:"
+                    options={setSelectorData('TITLE_TYPE').list}
                     placeholder="请选择"
+                    rules={[{ required: true, message: '请选择学历!' }]}
                   />
                 </div>
-                <div className='formItem'>
+
+                <div className='formItem' >
                   <ProFormSelect
-                    name="practiceSubject"
-                    label="执业科目:"
-                    options={setSelectorData('PROCESSIONAL_SUBJECTS_TYPE').list}
+                    name="practiceCate"
+                    label="执业类别:"
+                    options={setSelectorData('JOB_TYPE').list}
                     placeholder="请选择"
                   />
                 </div>
+
+                <div className='formItem'>
+                  <ProFormText name="phoneNumber" label="手机号:" placeholder="请输入" />
+                </div>
+
+                <ProFormDependency name={['userCate']}>
+                  {({ userCate }) => (userCate == 6 || userCate == 5) && (
+                    <div className='formItem'>
+                      <ProFormText name="idCardNum" label="身份证号:" placeholder="请输入" rules={[{ required: true, message: '请选择!' }]} />
+                    </div>
+                  )}
+                </ProFormDependency>
+
+
+                <ProFormDependency name={['userCate']}>
+                  {({ userCate }) => (userCate == 6 || userCate == 5) && (
+                    <div className='formItem'>
+                      <ProFormSelect
+                        name="practiceCate"
+                        label="执业科目:"
+                        options={setSelectorData('PROCESSIONAL_SUBJECTS_TYPE').list}
+                        placeholder="请选择"
+                        rules={[{ required: true, message: '请选择!' }]}
+                      />
+                    </div>
+                  )}
+                </ProFormDependency>
+
+                <ProFormDependency name={['userCate']}>
+                  {({ userCate }) => (userCate == 6 || userCate == 5) && (
+                    <div className='formItem' style={{position:'relative',left:-175}}>
+                      <ProFormText name="practiceCertificateNo" label="执业证号:" placeholder="请输入" rules={[{ required: true, message: '请选择!' }]} />
+                    </div>
+                  )}
+                </ProFormDependency>
+
+                <ProFormDependency name={['userCate']}>
+                  {({ userCate }) => (userCate == 6 || userCate == 5) && (
+                    <div className='formItem' style={{position:'relative',left:-175}}>
+                      <ProFormSelect
+                        name="practiceStatus"
+                        label="执业状态:"
+                        options={setSelectorData('PROFESSIONAL_STATUS_TYPE').list}
+                        placeholder="请选择"
+                        rules={[{ required: true, message: '请选择!' }]}
+                      />
+                    </div>
+                  )}
+                </ProFormDependency>
+
+                <ProFormDependency name={['userCate']}>
+                  {({ userCate }) => (userCate == 6 || userCate == 5) && (
+                    <div className='formItem' style={{position:'relative',left:-175}}>
+                      <ProFormSelect
+                        name="doctorLevel"
+                        label="医师级别:"
+                        options={setSelectorData('PHYSICIAN_TYPE').list}
+                        placeholder="请选择"
+                        rules={[{ required: true, message: '请选择!' }]}
+                      />
+                    </div>
+                  )}
+                </ProFormDependency>
+
               </div>
             </div>
 
-            <div style={{display:'flex',flexDirection:'row',width:'100%'}}>
-              <div style={{paddingLeft:36,whiteSpace:'nowrap'}}>备注:</div>
+            <div style={{ display: 'flex', flexDirection: 'row', width: '100%' }}>
               <ProFormTextArea
+                label='备注'
                 name="remark"
-                width={877}
+                width={688}
                 placeholder="请输入备注"
               />
             </div>
 
+
           </div>
         ) : ''
       }

+ 6 - 4
src/pages/platform/setting/userManage/model.ts

@@ -1,8 +1,8 @@
 /*
  * @Author: your name
  * @Date: 2022-01-12 10:12:55
- * @LastEditTime: 2022-03-07 14:44:53
- * @LastEditors: Please set LastEditors
+ * @LastEditTime: 2023-03-20 13:29:04
+ * @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/userManage/model.ts
  */
@@ -83,7 +83,7 @@ const userManageModel: userManageModelType = {
     *resetUserPwd({ payload }) {
       yield resetUserPwd({ userId: payload.id });
     },
-    *postEditUserData({ payload }, { call, put, select }) {
+    *postEditUserData({ payload }:any, { call, put, select }:any) {
       const currentEditUserOld = yield select(
         ({ userManageModel: state }: { userManageModel: userManageModelState }) => {
           return state.currentEditUser;
@@ -105,7 +105,9 @@ const userManageModel: userManageModelType = {
     },
     *getUserTemplateReq() {
       const url = yield getUsertemplate();
-      window.open(url);
+      if(url){
+        window.open(url);
+      }
     },
     *importUsers({ payload }, { call, put, select }) {
       const formData: any = new FormData();

+ 105 - 52
src/pages/platform/setting/userManage/style.less

@@ -1,95 +1,148 @@
 .UserManage {
-  height: 100%;
+  padding: 16px;
+  background-color: #fff;
+
+  .userExpandInfo {
+    display: flex;
+    flex-direction: row;
+    background: #F7F8FA;
+    padding: 20px 24px;
+    flex-wrap: wrap;
+    padding-bottom: 0;
+
+    span {
+      display: block;
+      width: 20%;
+      color: #525866;
+      margin-bottom: 16px;
+    }
+  }
+
+  .toolBar {
+    display: flex;
+    flex-direction: row;
+    justify-content: space-between;
+    align-items: center;
+    margin-bottom: 16px;
+
+    .filter {
+      display: flex;
+      flex-direction: row;
+      justify-content: flex-start;
+      align-items: center;
+
+      .filterItem {
+        display: flex;
+        flex-direction: row;
+        justify-content: center;
+        align-items: center;
+      }
+    }
+
+    .btnGroup {
+      span {
+        box-sizing: border-box;
+        cursor: pointer;
+        display: inline-block;
+        font-size: 14px;
+        font-family: SourceHanSansCN-Normal, SourceHanSansCN;
+        font-weight: 400;
+        color: #17181A;
+        border: 1px solid #DAE2F2;
+        line-height: 24px;
+        padding: 0 14px;
+        background: #FAFCFF;
+        border-radius: 4px;
+      }
+
+      .add {
+        color: #FFFFFF;
+        background: #3377FF;
+        border: 1px solid #3377FF;
+        margin-left: 8px;
+        border: none;
+      }
+    }
+
+  }
 }
 
 .ProfileModal {
+
+
   .wraper {
     display: flex;
     flex-direction: row;
 
     .left {
-      width: 50%;
+      width: 160px;
 
       .avatarContainer {
         display: flex;
-        flex-direction: row;
-        margin-bottom: 42px;
-        margin-left: 11px;
-
-        .avatar {
-          position: relative;
-          display: flex;
-          justify-content: center;
-          align-items: center;
-          width: 96px;
-          height: 128px;
-          border-radius: 4px;
-          background-image: url(./assets/avatar_empty.png);
-          background-size: cover;
-
-          .spin {
-            position: absolute;
-            top: 45%;
-            left: 0;
-            bottom: 0;
-            right: 0;
-          }
-
-          &>img {
-            width: 100%;
-          }
-        }
-
-        .UploadBtn {
-          margin-left: 18px;
-          margin-top: 45px;
-        }
-
-        &>span {
-          height: 14px;
-          font-size: 14px;
-          font-family: SourceHanSansCN-Normal, SourceHanSansCN;
-          font-weight: 400;
-          color: #17181A;
-          line-height: 14px;
-          margin-top: 5px;
-          margin-right: 10px;
+        justify-content: center;
+        align-items: center;
+        width: 148px;
+        height: 148px;
+        display: flex;
+        margin-bottom: 11px;
+        background: #F7F8FA;
+        border-radius: 4px;
+        padding-left: 20px;
+        padding-top: 10px;
+        border: 1px dashed #CFD6E6;
+        .kcmp-ant-upload-picture-card-wrapper {
+             .kcmp-ant-upload-select-picture-card {
+                border: none;
+                background-color:#F7F8FA;
+             }
         }
       }
 
       .formItem {
-
-        // margin-bottom: 16px;
-        .mq-ant-form-item {
-          // margin-bottom: 0;
-        }
+          margin-bottom: -4px;
       }
     }
 
     .right {
-      width: 50%;
+      display: flex;
+      flex-direction: row;
+      width: calc(100% - 160px);
+      padding-left: 16px;
+      justify-content: space-between;
+      align-content: flex-start;
+      flex-wrap: wrap;
 
       .formItem {
+        width: 160px;
+        height: 50px;
+        margin-bottom: 12px;
 
-        // margin-bottom: 16px;
         .mq-ant-form-item {
           .mq-ant-picker {
             width: 100%;
           }
         }
+
+        &:nth-child(3n) {
+          margin-right: 0;
+        }
       }
     }
   }
+
   .remarkArea {
     .mq-ant-col-5 {
       flex: 0 0 10%;
     }
+
     .mq-ant-col-18 {
       max-width: 100%;
+
       .pro-field-xl {
-          width: 662px;
+        width: 662px;
       }
     }
+
     .pro-field-xl {
       width: 100%;
     }

+ 29 - 1
src/service/hospList.ts

@@ -1,7 +1,7 @@
 /*
  * @Author: your name
  * @Date: 2022-01-13 09:15:59
- * @LastEditTime: 2023-03-06 13:47:16
+ * @LastEditTime: 2023-03-17 11:21:54
  * @LastEditors: code4eat awesomedema@gmail.com
  * @Description: 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
  * @FilePath: /KC-MiddlePlatform/src/service/hospList.ts
@@ -175,3 +175,31 @@ export const hospInit = async (hospId:Key) => {
     params:{hospId},
   });
 };
+
+
+//获取院区对应的菜单列表
+
+export const getMenuRelaPerm = async (params:{systemId:string;hospId:string;name?:string}) => {
+  return request('/centerSys/hospital/getMenuList', {
+    method: 'GET',
+    params
+  });   
+
+}
+
+//保存院区菜单绑定的api权限
+export const saveHospMenuApiPerm = async (data:any) => {
+  return request('/centerSys/navigation/addBatchFunction', {
+    method: 'POST',
+    data
+  });
+};
+
+
+
+//初始化只读
+export const setReadOnly = async (hospId:string) => {
+  return request(`/centerSys/role/initReadOnly?hospId=${hospId}`, {
+    method: 'POST'
+  });
+};

+ 6 - 1
src/service/menu.ts

@@ -1,7 +1,7 @@
 /*
  * @Author: your name
  * @Date: 2022-01-10 10:27:15
- * @LastEditTime: 2023-01-10 13:18:28
+ * @LastEditTime: 2023-03-16 19:11:06
  * @LastEditors: code4eat awesomedema@gmail.com
  * @Description: 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
  * @FilePath: /KC-MiddlePlatform/src/service/hospInfo.ts
@@ -211,3 +211,8 @@ export const getSpecifyMenuDetail = async (menuId:Key) => {
   });   
 
 }
+
+
+
+
+

+ 39 - 1
src/service/role.ts

@@ -1,7 +1,7 @@
 /*
  * @Author: your name
  * @Date: 2022-01-18 14:56:29
- * @LastEditTime: 2023-01-10 13:19:00
+ * @LastEditTime: 2023-03-21 11:18:57
  * @LastEditors: code4eat awesomedema@gmail.com
  * @Description: 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
  * @FilePath: /KC-MiddlePlatform/src/service/role.ts
@@ -112,3 +112,41 @@ export const roleBindUsers = async (data: {
     data,
   });
 };
+
+
+
+//角色绑定api权限菜单列表
+
+export const getRolePermRelaMenu = async (params: {
+  hospId: number;
+  roleId: string;
+  systemId:number,
+  name?:string;
+}) => {
+  return request('/centerSys/role/getMenuList', {
+    method: 'GET',
+    params,
+  });
+};
+
+
+//保存角色与菜单api权限关系
+export const saveRoleRelaApiPerm = async (data:any) => {
+  return request('/centerSys/navigation/addBatchRoleFunction', {
+    method: 'POST',
+    data,
+  });
+};
+
+
+//初始化角色
+export const initRoleFunc = async (params:{hospId:number;roleId:number}) => {
+  return request('/centerSys/role/initRole', {
+    method: 'POST',
+    params,
+  });
+};
+
+
+
+

+ 1 - 1
src/service/user.ts

@@ -1,7 +1,7 @@
 /*
  * @Author: your name
  * @Date: 2022-01-12 09:55:49
- * @LastEditTime: 2023-01-10 13:19:41
+ * @LastEditTime: 2023-03-20 10:47:52
  * @LastEditors: code4eat awesomedema@gmail.com
  * @Description: 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
  * @FilePath: /KC-MiddlePlatform/src/service/user.ts

+ 16 - 1
src/utils.ts

@@ -1,7 +1,7 @@
 /*
  * @Author: your name
  * @Date: 2022-01-13 17:09:05
- * @LastEditTime: 2023-03-09 15:44:36
+ * @LastEditTime: 2023-03-14 10:08:32
  * @LastEditors: code4eat awesomedema@gmail.com
  * @Description: 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
  * @FilePath: /KC-MiddlePlatform/src/utils.js
@@ -81,5 +81,20 @@ export const downloadTemplateReq = (path:string) => {
 
 
 
+let parent:any = undefined;
+
+export const getDeepestTreeData:any= (tree:any,childrenName:string)=>{
+     
+      if(tree[`${childrenName}`]&&tree[`${childrenName}`].length>0){
+           parent = tree;
+           return getDeepestTreeData(tree[`${childrenName}`][0],childrenName)    
+      }
+
+      return  [tree,parent] 
+}
+
+
+
+
 
 

+ 9 - 0
typings.d.ts

@@ -1,6 +1,15 @@
+/*
+ * @Author: code4eat awesomedema@gmail.com
+ * @Date: 2021-11-09 11:20:18
+ * @LastEditors: code4eat awesomedema@gmail.com
+ * @LastEditTime: 2023-03-14 18:27:04
+ * @FilePath: /KC-MiddlePlatform/typings.d.ts
+ * @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
+ */
 declare module '*.css';
 declare module '*.less';
 declare module '*.png';
+declare module 'lodash/difference';
 declare module '*.svg' {
   export function ReactComponent(
     props: React.SVGProps<SVGSVGElement>,