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

fix 修复部分bug

code4eat 3 éve
szülő
commit
d8d6fb743f
71 módosított fájl, 4938 hozzáadás és 644 törlés
  1. 0 43
      .umirc.ts
  2. 0 0
      KC-MiddlePlatform
  3. 17 0
      README.md
  4. 15 0
      config/config.dev.ts
  5. 92 0
      config/config.ts
  6. 35 0
      config/proxy.ts
  7. 7 5
      mock/login.ts
  8. 39 0
      mock/menu.ts
  9. 4 1
      package.json
  10. 168 61
      src/app.tsx
  11. 41 0
      src/components/KCModal/index.tsx
  12. 16 0
      src/components/KCModal/typing.d.ts
  13. 18 0
      src/components/KCProSelect/index.tsx
  14. 14 0
      src/components/KCProSelect/typings.d.ts
  15. 33 0
      src/components/KCUpload/index.tsx
  16. 5 0
      src/components/KCUpload/style.less
  17. 16 0
      src/components/KCUpload/typings.d.ts
  18. 15 16
      src/components/kc-select/index.tsx
  19. 10 0
      src/components/kc-select/style.less
  20. 49 0
      src/components/kcTable/index.tsx
  21. 2 0
      src/components/kcTable/style.less
  22. 17 0
      src/components/kcTable/typing.d.ts
  23. 99 34
      src/components/topBar/index.tsx
  24. 33 9
      src/components/topBar/style.less
  25. 17 0
      src/components/topBar/typings.d.ts
  26. 28 0
      src/constant.ts
  27. 5 1
      src/global.less
  28. 152 1
      src/layouts/index.tsx
  29. 50 51
      src/models/index.ts
  30. 50 0
      src/pages/index/index.less
  31. 33 105
      src/pages/index/index.tsx
  32. 150 132
      src/pages/login/index.tsx
  33. 3 2
      src/pages/login/login.d.ts
  34. 96 121
      src/pages/login/style.less
  35. 105 0
      src/pages/platform/_layout.tsx
  36. 209 0
      src/pages/platform/components/menuEditer/menu.tsx
  37. 19 0
      src/pages/platform/components/menuEditer/style.less
  38. 146 0
      src/pages/platform/components/usersEditer/index.tsx
  39. 24 0
      src/pages/platform/components/usersEditer/style.less
  40. 8 0
      src/pages/platform/index.less
  41. 43 0
      src/pages/platform/index/index.tsx
  42. 219 0
      src/pages/platform/setting/hospManage/index.tsx
  43. 208 0
      src/pages/platform/setting/hospManage/modals/modal.tsx
  44. 169 0
      src/pages/platform/setting/hospManage/model.ts
  45. 3 0
      src/pages/platform/setting/hospManage/style.less
  46. 27 0
      src/pages/platform/setting/hospManage/typings.d.ts
  47. 251 0
      src/pages/platform/setting/menuManage/index.tsx
  48. 17 0
      src/pages/platform/setting/menuManage/modals/menu.tsx
  49. 166 0
      src/pages/platform/setting/menuManage/modals/modal.tsx
  50. 160 0
      src/pages/platform/setting/menuManage/model.ts
  51. 3 0
      src/pages/platform/setting/menuManage/style.less
  52. 27 0
      src/pages/platform/setting/menuManage/typings.d.ts
  53. 220 0
      src/pages/platform/setting/roleManage/index.tsx
  54. 167 0
      src/pages/platform/setting/roleManage/modals/modal.tsx
  55. 204 0
      src/pages/platform/setting/roleManage/model.ts
  56. 3 0
      src/pages/platform/setting/roleManage/style.less
  57. 27 0
      src/pages/platform/setting/roleManage/typings.d.ts
  58. 233 0
      src/pages/platform/setting/userManage/index.tsx
  59. 159 0
      src/pages/platform/setting/userManage/modal.tsx
  60. 175 0
      src/pages/platform/setting/userManage/model.ts
  61. 3 0
      src/pages/platform/setting/userManage/style.less
  62. 25 0
      src/pages/platform/setting/userManage/typings.d.ts
  63. 42 31
      src/service/api.d.ts
  64. 116 0
      src/service/hospList.ts
  65. 31 20
      src/service/login.ts
  66. 111 0
      src/service/menu.ts
  67. 118 0
      src/service/role.ts
  68. 100 0
      src/service/user.ts
  69. 27 10
      src/typings.d.ts
  70. 42 0
      src/utils.ts
  71. 2 1
      tsconfig.json

+ 0 - 43
.umirc.ts

@@ -1,43 +0,0 @@
-/*
- * @Author: your name
- * @Date: 2021-11-09 11:20:18
- * @LastEditTime: 2022-01-06 11:20:55
- * @LastEditors: Please set LastEditors
- * @Description: 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
- * @FilePath: /KC-MiddlePlatform/.umirc.ts
- */
-import { defineConfig } from 'umi';
-
-export default defineConfig({
-  nodeModulesTransform: {
-    type: 'none',
-  },
-  dva: {
-    immer: true,
-    hmr: false,
-  },
-  theme: {},
-  routes: [
-    {
-      path: '/',
-      component: '@/pages/index/index',
-    },
-    { path: '/login', component: '@/pages/login/index' },
-  ],
-  proxy: {
-    // '/api': {
-    //   'target': 'http://112.124.59.133:8083/',
-    //   'changeOrigin': true,
-    //   'pathRewrite': { '^/api' : '' },
-    // },
-  },
-  fastRefresh: {},
-  mfsu: {},
-  // qiankun: {
-  //   master: {
-  //     sandbox: {
-  //       experimentalStyleIsolation: true, // 严格沙箱
-  //     },
-  //   },
-  // },
-});

+ 0 - 0
KC-MiddlePlatform


+ 17 - 0
README.md

@@ -93,7 +93,24 @@ npm run start:dev //开发环境,不使用mock,所有数据通过代理接口
     },[data]);
 ...
 
+
+//master前缀是主应用的代理,/api是子应用的代理
+dev: {
+      '/master': {
+        target: 'http://192.168.50.176:7000/',
+        changeOrigin: true,
+        pathRewrite: { '^/master': '' },
+      },
+      '/api': {
+        target: 'http://192.168.50.190:8083/',
+        changeOrigin: true,
+        pathRewrite: { '^/api': '' },
+      },
+},
+
 //项目数据共享使用的是umi 的model插件,每个page对应一个model.ts文件
+
+
 ```
 
 

+ 15 - 0
config/config.dev.ts

@@ -0,0 +1,15 @@
+// https://umijs.org/config/
+import { defineConfig } from 'umi';
+
+export default defineConfig({
+  plugins: [
+    // https://github.com/zthxxx/react-dev-inspector
+    'react-dev-inspector/plugins/umi/react-inspector',
+  ],
+  // https://github.com/zthxxx/react-dev-inspector#inspector-loader-props
+  inspectorConfig: {
+    exclude: [],
+    babelPlugins: [],
+    babelOptions: {},
+  },
+});

+ 92 - 0
config/config.ts

@@ -0,0 +1,92 @@
+/*
+ * @Author: your name
+ * @Date: 2022-01-07 10:04:20
+ * @LastEditTime: 2022-02-09 13:56:26
+ * @LastEditors: Please set LastEditors
+ * @Description: 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
+ * @FilePath: /KC-MiddlePlatform/config/config.ts
+ */
+
+import { defineConfig } from 'umi';
+
+import proxy from './proxy';
+
+const { REACT_APP_ENV } = process.env;
+
+export default defineConfig({
+  nodeModulesTransform: {
+    type: 'none',
+  },
+  dva: {
+    immer: true,
+    hmr: true,
+  },
+  antd: {
+    config: {
+      prefixCls: 'kcmp-ant',
+      // getPopupContainer:(triggerNode:HTMLElement)=>triggerNode
+    },
+  },
+  lessLoader: {
+    modifyVars: { '@ant-prefix': 'kcmp-ant' },
+  }, //
+  theme: {
+    // '@primary-color': '#1d4984',
+  },
+  routes: [
+    {
+      path: '/',
+      component: '@/layouts/index.tsx',
+      routes: [
+        {
+          path: '/app1',
+          microApp: 'app1',
+        },
+        {
+          path: '/index',
+          component: '@/pages/index/index.tsx',
+        },
+        {
+          path: '/platform',
+          component: '@/pages/platform/_layout.tsx',
+          routes: [
+            {
+              path: '/platform/setting/userManage',
+              component: '@/pages/platform/setting/userManage/index.tsx',
+            },
+            {
+              path: '/platform/setting/hospManage',
+              component: '@/pages/platform/setting/hospManage/index.tsx',
+            },
+            {
+              path: '/platform/setting/menuManage',
+              component: '@/pages/platform/setting/menuManage/index.tsx',
+            },
+            {
+              path: '/platform/setting/roleManage',
+              component: '@/pages/platform/setting/roleManage/index.tsx',
+            },
+          ],
+        },
+        { path: '/login', layout: false, component: '@/pages/login/index' },
+      ],
+    },
+  ],
+  proxy: proxy[REACT_APP_ENV || 'dev'],
+  manifest: {
+    basePath: '/',
+  },
+  fastRefresh: {},
+  layout: {
+    layout: 'top',
+  },
+  // mfsu: {},
+  qiankun: {
+    master: {
+      sandbox: {
+        // strictStyleIsolation:true,
+        // // experimentalStyleIsolation: true, // 试验性
+      },
+    },
+  },
+});

+ 35 - 0
config/proxy.ts

@@ -0,0 +1,35 @@
+/*
+ * @Author: your name
+ * @Date: 2022-01-07 10:00:52
+ * @LastEditTime: 2022-02-09 15:51:48
+ * @LastEditors: Please set LastEditors
+ * @Description: 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
+ * @FilePath: /KC-MiddlePlatform/config/proxy.ts
+ */
+
+//118.31.245.65   线上
+//112.124.59.133  测试
+
+const proxy: { [key: string]: any } = {
+  dev: {
+    '/master': {
+      target: 'http://192.168.50.176:7001/',
+      changeOrigin: true,
+      pathRewrite: { '^/master': '' },
+    },
+    '/api': {
+      target: 'http://192.168.50.176:8083/',
+      changeOrigin: true,
+      pathRewrite: { '^/api': '' },
+    },
+  },
+  mock: {
+    '/master/': {
+      target: 'http://localhost:8000',
+      changeOrigin: true,
+      pathRewrite: { '^': '' },
+    },
+  },
+};
+
+export default proxy;

+ 7 - 5
mock/login.ts

@@ -1,13 +1,12 @@
 /*
  * @Author: your name
  * @Date: 2021-11-11 11:46:42
- * @LastEditTime: 2021-12-20 11:48:56
+ * @LastEditTime: 2022-01-10 15:05:16
  * @LastEditors: Please set LastEditors
  * @Description: 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
  * @FilePath: /KC-MiddlePlatform/mock/login.ts
  */
 
-
 import { Request, Response } from 'express';
 
 const getList = (req: Request, res: Response, u: string) => {
@@ -43,7 +42,10 @@ const postData = (req: Request, res: Response, u: string) => {
   if (account == 'admin' && password == '123') {
     const result = {
       data: {
-        userName: 'hasaki',
+        name: '管理员',
+        token:
+          'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjEwMjgxNTIyOTc4LCJ1c2VySWQiOjMwNTd9.kr_TcZrhARPrCXzE5nhWd7V9B4rULRrFrLgMeEmTcR0',
+        userId: 3057,
       },
       success: true,
       status: 200,
@@ -72,13 +74,13 @@ const getHospSubSystemList = (req: Request, res: Response) => {
         icon: 'https://i.postimg.cc/J4fsWx1V/2x.png',
         id: 0,
         name: '平台管理',
-        url: '//localhost:2000',
+        url: '/platform/index',
       },
       {
         icon: 'https://i.postimg.cc/yNrSZ4pN/2x.png',
         id: 1,
         name: '智慧查检系统',
-        url: '//localhost:8804',
+        url: '/app1',
       },
     ],
     success: true,

+ 39 - 0
mock/menu.ts

@@ -0,0 +1,39 @@
+/*
+ * @Author: your name
+ * @Date: 2022-01-10 17:11:51
+ * @LastEditTime: 2022-01-10 17:16:43
+ * @LastEditors: Please set LastEditors
+ * @Description: 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
+ * @FilePath: /KC-MiddlePlatform/mock/menu.js
+ */
+
+import { Request, Response } from 'express';
+
+const getMenu = (req: Request, res: Response) => {
+  const result = {
+    data: [
+      {
+        path: '/paltform/userManage',
+        name: '用户管理',
+        icon: '',
+        url: '',
+        softUrl: '',
+      },
+      {
+        path: '/paltform/hospManage',
+        name: '院区管理',
+        icon: '',
+        url: '',
+        softUrl: '',
+      },
+    ],
+    success: true,
+    status: 200,
+  };
+
+  return res.json(result);
+};
+
+export default {
+  'GET /centerSys/user/getMenuNav': getMenu,
+};

+ 4 - 1
package.json

@@ -2,6 +2,7 @@
   "private": true,
   "scripts": {
     "start": "umi dev",
+    "start:dev": "cross-env REACT_APP_ENV=dev  UMI_ENV=dev umi dev",
     "build": "umi build",
     "postinstall": "umi generate tmp",
     "prettier": "prettier --write '**/*.{js,jsx,tsx,ts,less,md,json}'",
@@ -27,7 +28,9 @@
     "@ant-design/pro-form": "^1.18.3",
     "@ant-design/pro-layout": "^6.15.3",
     "@ant-design/pro-table": "^2.30.8",
+    "cross-env": "^7.0.3",
     "react": "16.x",
+    "react-dev-inspector": "^1.1.1",
     "react-dom": "16.x",
     "umi": "^3.5.20"
   },
@@ -36,7 +39,7 @@
     "@types/react": "^16.0.0",
     "@types/react-dom": "^16.0.0",
     "@umijs/plugin-qiankun": "^2.35.4",
-    "@umijs/preset-react": "1.x",
+    "@umijs/preset-react": "^2.1.1",
     "@umijs/test": "^3.5.20",
     "express": "^4.17.1",
     "lint-staged": "^10.0.7",

+ 168 - 61
src/app.tsx

@@ -1,45 +1,58 @@
 /*
  * @Author: your name
  * @Date: 2021-11-09 13:57:41
- * @LastEditTime: 2021-12-22 09:43:26
+ * @LastEditTime: 2022-02-09 14:09:24
  * @LastEditors: Please set LastEditors
  * @Description: 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
  * @FilePath: /KC-MiddlePlatform/src/app.ts
  */
 
-import React from 'react';
-import {
-  PageLoading,
-} from '@ant-design/pro-layout';
-import { notification, message } from 'antd';
+import React, { useState, useRef } from 'react';
+import { PageLoading } from '@ant-design/pro-layout';
+import { notification, Modal } from 'antd';
 import { RequestConfig, history } from 'umi';
-// import { createLogger } from 'redux-logger';
 import type { RequestOptionsInit } from 'umi-request';
-
 import { getHospSubSystemList } from '@/service/login';
 
+import {
+  BasicLayoutProps,
+  Settings as LayoutSettings,
+} from '@ant-design/pro-layout';
+
 const loginPath = '/login';
 
 let hospSign: string = ''; //医院标识
 
+if (history && history.location.query) {
+  hospSign = history.location.query.hospSign as string;
+  if (!hospSign) {
+    const localHospSign = localStorage.getItem('hospSign');
+    hospSign = localHospSign ? localHospSign : '';
+  }
+}
+
 /** 获取用户信息比较慢的时候会展示一个 loading */
 export const initialStateConfig = {
   loading: <PageLoading />,
 };
 
 type InitialStateType = {
-  userData?: any;
-  systemLists?: SystemListItem[]; //当前医院可选子系统列表
-  openedSysLists?: SystemListItem[]; //当前已打开的系统列表
-  currentSelectedSys?: SystemListItem; //当前选中的tab
+  userData?: {
+    name: string;
+    token: string;
+    userId: number;
+  };
+  systemLists?: userRelationInfo.OwnAppsItem[]; //当前医院可选子系统列表
+  openedSysLists?: userRelationInfo.OwnAppsItem[]; //当前已打开的系统列表
+  currentSelectedSys?: userRelationInfo.OwnAppsItem; //当前选中的tab
   logout?: () => Promise<boolean>;
+  getHospSubSystemListFunc?: () => Promise<any[]>;
 };
 
 export async function getInitialState(): Promise<InitialStateType> {
   const fetchUserInfo = async () => {
     try {
-      const userData = localStorage.getItem('KC-MiddlePlatformUserData');
-      // console.log({ userData });
+      const userData = localStorage.getItem('userData');
       if (userData) {
         return JSON.parse(userData);
       }
@@ -50,47 +63,66 @@ export async function getInitialState(): Promise<InitialStateType> {
     return undefined;
   };
 
+  //获取当前账号所有子应用列表
+  const getHospSubSystemListFunc = async () => {
+    const data = await getHospSubSystemList();
+    if (data) {
+      const _data = data.map((t) => ({
+        ...t,
+        icon:
+          t.name == '业务中台'
+            ? 'https://i.postimg.cc/J4fsWx1V/2x.png'
+            : 'https://i.postimg.cc/yNrSZ4pN/2x.png',
+        url: t.name == '业务中台' ? '/platform/setting/userManage' : '/app1',
+      }));
+
+      return _data;
+    }
+    return [];
+  };
+
   const logout = () => {
-    localStorage.removeItem('KC-MiddlePlatformUserData');
+    localStorage.removeItem('userData');
     history.replace(`${loginPath}?hospSign=${hospSign}`);
     return Promise.resolve(true);
   };
 
-  //获取当前账号所有子应用列表
-  const systemLists = await getHospSubSystemList();
-  
   const userData = await fetchUserInfo();
 
+  let systemLists: userRelationInfo.OwnAppsItem[] = [];
+
+  if (userData) {
+    systemLists = await getHospSubSystemListFunc();
+  }
+
+  const localInitData = localStorage.getItem('initialState');
+
   return {
-    userData,
-    openedSysLists: [],
     currentSelectedSys: undefined,
+    openedSysLists: [],
+    ...JSON.parse(localInitData ? localInitData : '{}'), //覆盖,恢复tab状态
+    userData,
     logout,
-    systemLists,
+    getHospSubSystemListFunc,
+    systemLists: systemLists,
   };
 }
 
-
-
-// export const dva = {
-//         config: {
-//           onAction: createLogger(),
-//           onError(e: Error) {
-//             message.error(e.message, 3);
-//           },
-//         },
-// };
-
-
-
-
 const requestInterceptorsHandle = (
   url: string,
   options: RequestOptionsInit,
 ) => {
+  const userData = localStorage.getItem('userData');
+  let authHeader = { token: '' };
+
+  if (userData) {
+    const { token } = JSON.parse(userData);
+    authHeader.token = token;
+  }
+
   return {
     url: `${url}`,
-    options: { ...options, interceptors: true },
+    options: { ...options, interceptors: true, headers: authHeader },
   };
 };
 
@@ -98,13 +130,28 @@ const responseInterceptorsHandle = async (
   response: Response,
   options: RequestOptionsInit,
 ) => {
-  const _response: { data: any; status: number; success: boolean } =
-    await response.clone().json();
-  // // console.log({_response});
-  if (_response.success) {
-    return _response.data;
+  const _response: {
+    data?: any;
+    status: number;
+    success?: boolean;
+    msg?: string;
+  } = await response.clone().json();
+
+  if (_response.status == 200) {
+    if (_response.data) {
+      return _response.data;
+    }
+    notification.success({
+      message: `${_response.msg}`,
+    });
+    return {
+      status: _response.status,
+      success: true,
+    };
   } else {
-    return _response;
+    return {
+      ..._response,
+    };
   }
 };
 
@@ -112,11 +159,11 @@ interface ErrorInfoStructure {
   success: boolean; // if request is success
   data?: any; // response data
   status?: number;
-  errorCode: string; // code for errorType
-  errorMessage: string; // message display to user
-  showType?: number; // error display type: 0 silent; 1 message.warn; 2 message.error; 4 notification; 9 page
-  traceId?: string; // Convenient for back-end Troubleshooting: unique request ID
-  host?: string; // Convenient for backend Troubleshooting: host of current access server
+  errorCode: string;
+  errorMessage: string;
+  showType?: number;
+  traceId?: string;
+  host?: string;
 }
 
 interface ResponseErr extends Error {
@@ -129,16 +176,22 @@ const errorHandlerFunc = (error: ResponseErr) => {
 
   try {
     const { info } = error;
-    const errortext = '';
-    const { status, errorMessage } = info;
-    notification.error({
-      message: ` ${status}: ${errorMessage}`,
-      description: errortext,
-    });
+    const { errorCode, errorMessage } = info;
+
+    if (errorMessage.length > 20) {
+      notification.error({
+        message: ` ${errorCode}:出现错误!`,
+        description: errorMessage,
+      });
+    } else {
+      notification.error({
+        message: ` ${errorCode}:${errorMessage}`,
+      });
+    }
   } catch (err) {
     console.log({ errorHandlerFunc: err });
     notification.error({
-      message: '登录遇到未知错误,查看控制台!',
+      message: '遇到未知错误,查看控制台!',
     });
   }
 };
@@ -147,18 +200,16 @@ export const request: RequestConfig = {
   timeout: 10000,
   errorConfig: {
     adaptor: (resData) => {
-      if (resData) {
+      if (!resData.success && resData.status != 200) {
+        // console.log({resData});
         return {
           ...resData,
-          success: resData.success,
-          errorMessage: resData.errorMessage,
+          // success:false,
         };
       } else {
         return {
-          success: false,
-          errorCode: 0,
-          status: 0,
-          errorMessage: '出现未知错误!',
+          success: true,
+          status: 200,
         };
       }
     },
@@ -179,3 +230,59 @@ export const request: RequestConfig = {
   requestInterceptors: [requestInterceptorsHandle],
   responseInterceptors: [responseInterceptorsHandle],
 };
+
+// 从接口中获取子应用配置,export 出的 qiankun 变量是一个 promise
+export const qiankun = fetch('/config').then(() => ({
+  // 注册子应用信息
+  apps: [
+    {
+      name: 'app1', // 唯一 id
+      entry: '//localhost:8804', // html entry
+    },
+  ],
+  // 完整生命周期钩子请看 https://qiankun.umijs.org/zh/api/#registermicroapps-apps-lifecycles
+  lifeCycles: {
+    afterMount: (props: any) => {
+      console.log(props);
+    },
+  },
+  // 支持更多的其他配置,详细看这里 https://qiankun.umijs.org/zh/api/#start-opts
+}));
+
+//向子应用透传
+export function useQiankunStateForSlave() {
+  const [masterState, setMasterState] = useState({});
+
+  return {
+    masterState,
+    setMasterState,
+  };
+}
+
+export const layout = ({
+  initialState: {
+    systemLists = [],
+    openedSysLists = [],
+    userData,
+    currentSelectedSys,
+  },
+}: {
+  initialState: InitialStateType;
+}): BasicLayoutProps => {
+  return {
+    // headerRender: () => (
+    //   <TopHoc systemLists={systemLists} openedSysLists={openedSysLists} currentSelectedSys={currentSelectedSys} />
+    // ),
+    headerRender: false,
+    rightContentRender: () => <>right</>,
+    footerRender: () => null,
+    onPageChange: () => {
+      //如果没有登录,重定向到 login
+      if (!userData && location.pathname !== '/login') {
+        history.push('/login');
+      }
+    },
+    menuHeaderRender: undefined,
+    // ...initialState?.settings,
+  };
+};

+ 41 - 0
src/components/KCModal/index.tsx

@@ -0,0 +1,41 @@
+/*
+ * @Author: your name
+ * @Date: 2022-01-12 15:26:43
+ * @LastEditTime: 2022-01-20 14:32:16
+ * @LastEditors: Please set LastEditors
+ * @Description: 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
+ * @FilePath: /KC-MiddlePlatform/src/components/KCModal/index.tsx
+ */
+
+import React, { useState, useEffect } from 'react';
+import { ModalForm } from '@ant-design/pro-form';
+import { KCModalType } from './typing';
+
+const KCModal: React.FC<KCModalType.KCModalProps> = ({
+  visible = false,
+  modalProps,
+  ...props
+}) => {
+  const [modalVisible, setModalVisible] = useState(false);
+
+  useEffect(() => {
+    setModalVisible(visible);
+  }, [visible]);
+
+  return (
+    <ModalForm
+      visible={modalVisible}
+      modalProps={{
+        destroyOnClose: true,
+        bodyStyle: {
+          maxHeight: '72vh',
+          overflowY: 'auto',
+        },
+        ...modalProps,
+      }}
+      {...props}
+    />
+  );
+};
+
+export default KCModal;

+ 16 - 0
src/components/KCModal/typing.d.ts

@@ -0,0 +1,16 @@
+/*
+ * @Author: your name
+ * @Date: 2022-01-12 15:29:17
+ * @LastEditTime: 2022-01-13 10:24:17
+ * @LastEditors: Please set LastEditors
+ * @Description: 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
+ * @FilePath: /KC-MiddlePlatform/src/components/KCModal/typing.d.ts
+ */
+
+import { ModalFormProps } from '@ant-design/pro-form';
+
+declare namespace KCModalType {
+  export interface KCModalProps extends ModalFormProps {
+    reload?: boolean;
+  }
+}

+ 18 - 0
src/components/KCProSelect/index.tsx

@@ -0,0 +1,18 @@
+/*
+ * @Author: your name
+ * @Date: 2022-01-12 17:42:25
+ * @LastEditTime: 2022-01-12 17:46:47
+ * @LastEditors: Please set LastEditors
+ * @Description: 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
+ * @FilePath: /KC-MiddlePlatform/src/components/KCProSelect/index.tsx
+ */
+
+import { ProFormSelect } from '@ant-design/pro-form';
+import React from 'react';
+import { KCProSelectType } from './typings';
+
+const KCProSelect: React.FC<KCProSelectType.KCProSelectProps> = (props) => {
+  return <ProFormSelect {...props} />;
+};
+
+export default KCProSelect;

+ 14 - 0
src/components/KCProSelect/typings.d.ts

@@ -0,0 +1,14 @@
+/*
+ * @Author: your name
+ * @Date: 2022-01-12 17:42:36
+ * @LastEditTime: 2022-01-12 17:45:06
+ * @LastEditors: Please set LastEditors
+ * @Description: 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
+ * @FilePath: /KC-MiddlePlatform/src/components/KCProSelect/typings.d.ts
+ */
+
+import { ProFormSelectProps } from '@ant-design/pro-form/lib/components/Select';
+
+declare namespace KCProSelectType {
+  export interface KCProSelectProps extends ProFormSelectProps {}
+}

+ 33 - 0
src/components/KCUpload/index.tsx

@@ -0,0 +1,33 @@
+/*
+ * @Author: your name
+ * @Date: 2022-01-18 09:15:26
+ * @LastEditTime: 2022-01-18 11:23:33
+ * @LastEditors: Please set LastEditors
+ * @Description: 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
+ * @FilePath: /KC-MiddlePlatform/src/components/KCUpload/index.tsx
+ */
+
+import { ProFormUploadButton } from '@ant-design/pro-form';
+import React from 'react';
+import { KCUploadType } from './typings';
+import './style.less';
+import { DownloadOutlined } from '@ant-design/icons';
+
+const KCUpload: React.FC<KCUploadType> = ({
+  title,
+  fieldProps,
+  ...restProps
+}) => {
+  return (
+    <div className="KCUpload">
+      <ProFormUploadButton
+        title={title}
+        fieldProps={fieldProps}
+        icon={<DownloadOutlined />}
+        {...restProps}
+      />
+    </div>
+  );
+};
+
+export default KCUpload;

+ 5 - 0
src/components/KCUpload/style.less

@@ -0,0 +1,5 @@
+.KCUpload {
+  .kcmp-ant-form-item {
+    margin-bottom: 0 !important;
+  }
+}

+ 16 - 0
src/components/KCUpload/typings.d.ts

@@ -0,0 +1,16 @@
+/*
+ * @Author: your name
+ * @Date: 2022-01-18 09:16:29
+ * @LastEditTime: 2022-01-18 11:33:23
+ * @LastEditors: Please set LastEditors
+ * @Description: 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
+ * @FilePath: /KC-MiddlePlatform/src/components/KCUpload/typings.d.ts
+ */
+
+import type { UploadProps, ButtonProps } from 'antd';
+import { FieldProps } from '@ant-design/pro-form/lib/interface';
+
+interface KCUploadType {
+  title: string;
+  fieldProps?: (FieldProps & UploadProps<any>) | undefined;
+}

+ 15 - 16
src/components/kc-select/index.tsx

@@ -1,38 +1,37 @@
 /*
  * @Author: your name
  * @Date: 2021-11-10 16:32:55
- * @LastEditTime: 2021-11-11 15:57:39
+ * @LastEditTime: 2022-01-18 17:32:26
  * @LastEditors: Please set LastEditors
  * @Description: 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
  * @FilePath: /KC-MiddlePlatform/src/components/kc-select/index.tsx
  */
-import React from 'react'
+import React from 'react';
+import { Select } from 'antd';
+import { SelectProps, SelectValue } from 'antd/lib/select';
+import './style.less';
 
-import Select, { SelectProps, SelectValue } from "antd/lib/select";
-
-
-interface KCSelectProps {
-
-}
+interface KCSelectProps {}
 
 class KCSelect<
-  ValueType extends SelectValue = SelectValue
+  ValueType extends SelectValue = SelectValue,
 > extends React.Component<SelectProps<ValueType>> {
-    
   static defaultProps = {
     showSearch: false,
     allowClear: true,
-    optionFilterProp: "children",
+    optionFilterProp: 'children',
     filterOption: (input: string, option: any) =>
-      option!.children.toLowerCase().indexOf(input.toLowerCase()) >=
-      0,
+      option!.children.toLowerCase().indexOf(input.toLowerCase()) >= 0,
   };
 
   public render() {
     const { children, ...restProps } = this.props;
-    return <Select {...restProps}>{children}</Select>;
+    return (
+      <div className="KCSelect">
+        <Select {...restProps}>{children}</Select>
+      </div>
+    );
   }
 }
 
-
-export default KCSelect
+export default KCSelect;

+ 10 - 0
src/components/kc-select/style.less

@@ -0,0 +1,10 @@
+.KCSelect {
+  .kcmp-ant-select-single
+    .kcmp-ant-select-selector
+    .kcmp-ant-select-selection-item,
+  .kcmp-ant-select-single
+    .kcmp-ant-select-selector
+    .kcmp-ant-select-selection-placeholder {
+    line-height: 35px;
+  }
+}

+ 49 - 0
src/components/kcTable/index.tsx

@@ -0,0 +1,49 @@
+/*
+ * @Author: your name
+ * @Date: 2022-01-11 16:03:24
+ * @LastEditTime: 2022-01-19 16:23:23
+ * @LastEditors: Please set LastEditors
+ * @Description: 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
+ * @FilePath: /KC-MiddlePlatform/src/components/kcTable/index.tsx
+ */
+
+import React, { useState, useEffect, useRef } from 'react';
+import ProTable, { ActionType, ProColumns } from '@ant-design/pro-table';
+import { KCTableType } from './typing';
+import './style.less';
+
+const KCTable: React.FC<KCTableType.KCTableProps<any, {}>> = ({
+  columns,
+  reload,
+  ...restProps
+}) => {
+  const [tableColumns, setTableColumns] = useState<ProColumns[]>([]);
+  const actionRef = useRef<ActionType>();
+
+  useEffect(() => {
+    setTableColumns(
+      columns.map((t) => ({
+        hideInSearch: true,
+        ...t,
+      })),
+    );
+  }, [columns]);
+
+  useEffect(() => {
+    if (reload) {
+      actionRef.current?.reload();
+    }
+  }, [reload]);
+
+  return (
+    <ProTable
+      className="KCTable"
+      actionRef={actionRef}
+      columns={tableColumns}
+      pagination={{ defaultPageSize: 10 }}
+      {...restProps}
+    />
+  );
+};
+
+export default KCTable;

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

@@ -0,0 +1,2 @@
+.KCTable {
+}

+ 17 - 0
src/components/kcTable/typing.d.ts

@@ -0,0 +1,17 @@
+/*
+ * @Author: your name
+ * @Date: 2022-01-11 16:24:23
+ * @LastEditTime: 2022-01-13 10:26:42
+ * @LastEditors: Please set LastEditors
+ * @Description: 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
+ * @FilePath: /KC-MiddlePlatform/src/components/kcTable/typing.d.ts
+ */
+
+import { ProColumns, ProTableProps } from '@ant-design/pro-table';
+
+declare namespace KCTableType {
+  export interface KCTableProps<T, U> extends ProTableProps<T, U> {
+    columns: ProColumns<T, 'text'>[];
+    reload?: boolean;
+  }
+}

+ 99 - 34
src/components/topBar/index.tsx

@@ -1,14 +1,15 @@
 /*
  * @Author: your name
  * @Date: 2021-11-16 09:12:37
- * @LastEditTime: 2021-12-17 10:59:01
+ * @LastEditTime: 2022-02-09 14:32:22
  * @LastEditors: Please set LastEditors
  * @Description: 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
  * @FilePath: /KC-MiddlePlatform/src/pages/index/components/topBar/index.tsx
  */
 
 import React, { useEffect, useState } from 'react';
-import './style.less';
+
+import styles from './style.less';
 
 import { Tooltip } from 'antd';
 import { LogoutOutlined, SettingOutlined } from '@ant-design/icons';
@@ -17,12 +18,17 @@ import logo from '../../../public/images/kc-logo.png';
 import platFormMenuIcon from '../../../public/images/platformMenu.png';
 import tabCloseIcon from '../../../public/images/tabCloseIcon.png';
 
+import { history } from 'umi';
+
 interface TopBarType {
-  onTabChange?: (data: SystemListItem) => void; //当tab切换时回调
-  sysList?: SystemListItem[]; //可选系统列表
-  openedTabs: SystemListItem[]; //已打开的列表
-  currentTab?: SystemListItem; //当前tab
-  userPannelTabClick?: (tag: 'SETTING' | 'LOGOUT') => void;
+  onTabChange?: (data: TopBar.Tab[]) => void; //当tab切换时回调
+  sysList: TopBar.Tab[]; //可选系统列表
+  openedTabs: TopBar.Tab[]; //已打开的列表
+  currentTab?: TopBar.Tab | undefined; //当前tab
+  userPannelTabClick?: (tag: 'SETTING' | 'LOGOUT' | 'SETUSERINFO') => void;
+  onCloseTab?: (data: TopBar.Tab) => void;
+  onTabClick?: (data: TopBar.Tab) => void;
+  userData?: { name: string; [key: string]: any };
 }
 
 const TopBar: React.FC<TopBarType> = (props) => {
@@ -32,22 +38,30 @@ const TopBar: React.FC<TopBarType> = (props) => {
     openedTabs = [],
     currentTab,
     userPannelTabClick,
+    onCloseTab,
+    onTabClick,
+    userData,
   } = props;
-  const [systemTabs, setSystemTabs] = useState<SystemListItem[]>([]); //已打开的tab
-  const [currentSelectedTab, setCurrentSelectedTab] =
-    useState<SystemListItem>();
+  const [systemTabs, setSystemTabs] = useState<TopBar.Tab[]>([]); //已打开的tab
+  const [currentSelectedTab, setCurrentSelectedTab] = useState<TopBar.Tab>();
   const [ifOpenPannel, setIfOpenPannel] = useState(false);
   const [arrowRotate, setArrowRotate] = useState(false);
+  // const [opendedTabs,setOpendedTabs] = useState<SystemListItem[]>([]);
 
-  const _systemTabClickHandle = (item: SystemListItem) => {
+  const _systemTabClickHandle = (item: TopBar.Tab) => {
     //导航栏tab点击
+    // console.log('tab click');
     setCurrentSelectedTab(item);
-    onTabChange && onTabChange(item);
+    onTabClick && onTabClick(item);
   };
 
-  const _systemListClickHandle = (data: SystemListItem) => {
+  const _systemListClickHandle = (data: TopBar.Tab) => {
     //导航栏系统菜单列表点击回调
-    onTabChange && onTabChange(data);
+    const findIndex = systemTabs.findIndex((t) => t.id == data.id);
+    if (findIndex != -1) return;
+    _systemTabClickHandle(data); //触发一次tab点击
+    setSystemTabs([...systemTabs, data]);
+    onTabChange && onTabChange([...systemTabs, data]);
   };
 
   const _userPannelTabClick = (tag: 'SETTING' | 'LOGOUT') => {
@@ -55,18 +69,39 @@ const TopBar: React.FC<TopBarType> = (props) => {
     userPannelTabClick && userPannelTabClick(tag);
   };
 
+  const closeTabHandle = (item: TopBar.Tab, e: React.MouseEvent) => {
+    e.stopPropagation();
+    let _systemTabs = [...systemTabs];
+    let delIndex = -1;
+
+    const filtered = _systemTabs.filter((t, index) => {
+      if (t.id == item.id) {
+        delIndex = index;
+      }
+      return t.id != item.id;
+    });
+
+    setSystemTabs([...filtered]);
+    if (delIndex != 0) {
+      _systemTabClickHandle(_systemTabs[delIndex - 1]); //自动切换为前一个tab
+    }
+
+    onTabChange && onTabChange(filtered);
+    onCloseTab && onCloseTab(item);
+  };
+
   const UserPannel = () => {
     return (
-      <div className="userPannel">
+      <div className={styles.userPannel}>
         <div
-          className="userPannelTab"
+          className={styles.userPannelTab}
           onClick={() => _userPannelTabClick('SETTING')}
         >
           <SettingOutlined />
           设置
         </div>
         <div
-          className="userPannelTab"
+          className={styles.userPannelTab}
           onClick={() => _userPannelTabClick('LOGOUT')}
         >
           <LogoutOutlined />
@@ -77,29 +112,46 @@ const TopBar: React.FC<TopBarType> = (props) => {
   };
 
   useEffect(() => {
+    if (openedTabs.length == 1) {
+      //当有且仅当只有一个的时候,默认激活
+      setCurrentSelectedTab(openedTabs[0]);
+    }
+    if (openedTabs.length == 0) {
+      //当所有tab都关闭时
+      setCurrentSelectedTab(undefined);
+      history.push('/index');
+    }
+  }, [openedTabs]);
+
+  useEffect(() => {
+    // console.log('Topbar props',props);
     setSystemTabs(openedTabs);
     currentTab && setCurrentSelectedTab(currentTab);
   }, [props]);
 
   return (
-    <div className="topBar">
-      <div className="logoWrap">
-        <img className="logo" src={logo} />
+    <div className={styles.topBar}>
+      <div className={styles.logoWrap}>
+        <img className={styles.logo} src={logo} />
       </div>
       <div
-        className={ifOpenPannel ? 'platformMenu on' : 'platformMenu'}
+        className={
+          ifOpenPannel
+            ? `${styles.platformMenu} ${styles.on}`
+            : `${styles.platformMenu}`
+        }
         onClick={() => setIfOpenPannel(!ifOpenPannel)}
       >
         <img src={platFormMenuIcon} />
-        <div className="systemPannel">
+        <div className={styles.systemPannel}>
           {sysList.map((item) => (
             <div
               key={item.id}
-              className="systemList"
+              className={styles.systemList}
               onClick={() => _systemListClickHandle(item)}
             >
               <img src={item.icon} alt="" />
-              <span className="name">{item.name}</span>
+              <div className={styles.systemListName}>{item.name}</div>
             </div>
           ))}
         </div>
@@ -107,21 +159,32 @@ const TopBar: React.FC<TopBarType> = (props) => {
       {/**
        * 已打开的tab
        */}
-      <div className="tabWrap">
+      <div className={styles.tabWrap}>
         {systemTabs.map((item) => (
           <div
             key={item.id}
-            className={currentSelectedTab?.id == item.id ? 'tab on' : 'tab'}
+            className={
+              currentSelectedTab?.id == item.id
+                ? `${styles.tab} ${styles.on}`
+                : `${styles.tab}`
+            }
             onClick={() => _systemTabClickHandle(item)}
           >
-            {item.name} <img src={tabCloseIcon} alt="close" />
+            <div className={styles.tabText}>{item.name} </div>
+            <div className={styles.closeIconWrap}>
+              <img
+                src={tabCloseIcon}
+                onClick={(e) => closeTabHandle(item, e)}
+                alt="close"
+              />
+            </div>
           </div>
         ))}
       </div>
-      <div className="userRelaInfoWrap">
-        <div className="notification">
+      <div className={styles.userRelaInfoWrap}>
+        <div className={styles.notification}>
           <img
-            className="notificationIcon"
+            className={styles.notificationIcon}
             src={require('../../../public/images/notificationIcon.png')}
           />
         </div>
@@ -130,14 +193,16 @@ const TopBar: React.FC<TopBarType> = (props) => {
           color="#fff"
           onVisibleChange={(visible) => setArrowRotate(visible)}
         >
-          <div className="user">
+          <div className={styles.user}>
             <img
-              className="avator"
+              className={styles.avator}
               src={require('../../../public/images/Mask.png')}
             />
-            <span className="name">Mr Lawrence</span>
+            <span className={styles.name}>{userData?.name}</span>
             <img
-              className={arrowRotate ? 'arrow on' : 'arrow'}
+              className={
+                arrowRotate ? `${styles.arrow} ${styles.on}` : `${styles.arrow}`
+              }
               src={require('../../../public/images/arrow_white.png')}
             />
           </div>

+ 33 - 9
src/components/topBar/style.less

@@ -25,6 +25,8 @@
 }
 
 .topBar {
+  position: relative;
+  z-index: 999;
   display: flex;
   flex-direction: row;
   justify-content: flex-start;
@@ -34,6 +36,10 @@
   padding-left: 16px;
   background: #3071f2;
 
+  * {
+    line-height: normal;
+  }
+
   .logoWrap {
     width: 200px;
 
@@ -84,11 +90,12 @@
         margin-right: 10px;
         border-radius: 8px;
 
-        .name {
+        .systemListName {
           font-size: 12px;
           font-family: SourceHanSansCN-Normal, SourceHanSansCN;
           font-weight: 400;
           color: #666e80;
+          line-height: normal;
         }
 
         & > img {
@@ -132,21 +139,38 @@
     align-items: center;
 
     .tab {
+      display: flex;
+      width: 92px;
+      flex-direction: row;
+      justify-content: space-between;
+      align-items: center;
       padding: 8px 9px;
+      padding-right: 0;
       border-radius: 4px;
-      font-size: 14px;
-      font-family: SourceHanSansCN-Medium, SourceHanSansCN;
-      font-weight: 500;
-      color: #ffffff;
       opacity: 0.8;
       margin-right: 16px;
       cursor: pointer;
       box-sizing: border-box;
 
-      & > img {
-        width: 8px;
-        height: 8px;
-        margin-left: 16px;
+      .tabText {
+        width: 56px;
+        font-size: 14px;
+        font-family: SourceHanSansCN-Medium, SourceHanSansCN;
+        font-weight: 500;
+        color: #ffffff;
+        white-space: nowrap;
+        overflow-x: hidden;
+        text-overflow: ellipsis;
+      }
+      .closeIconWrap {
+        display: flex;
+        justify-content: center;
+        align-items: center;
+        padding: 0px 9px;
+        img {
+          width: 8px;
+          height: 8px;
+        }
       }
 
       &:hover {

+ 17 - 0
src/components/topBar/typings.d.ts

@@ -0,0 +1,17 @@
+/*
+ * @Author: your name
+ * @Date: 2022-01-10 13:56:19
+ * @LastEditTime: 2022-01-10 14:41:51
+ * @LastEditors: Please set LastEditors
+ * @Description: 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
+ * @FilePath: /KC-MiddlePlatform/src/components/topBar/typings.d.ts
+ */
+
+declare namespace TopBar {
+  export type Tab = {
+    id: number;
+    name: string;
+    icon: string;
+    url: string;
+  };
+}

+ 28 - 0
src/constant.ts

@@ -0,0 +1,28 @@
+/*
+ * @Author: your name
+ * @Date: 2022-01-14 16:09:09
+ * @LastEditTime: 2022-01-14 16:13:10
+ * @LastEditors: your name
+ * @Description: 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
+ * @FilePath: /KC-MiddlePlatform/src/constant.ts
+ */
+
+//菜单所有类型
+export const MenuTypes = [
+  {
+    label: '目录',
+    value: 0,
+  },
+  {
+    label: '菜单',
+    value: 1,
+  },
+  {
+    label: '按钮',
+    value: 2,
+  },
+  {
+    label: '系统',
+    value: 3,
+  },
+];

+ 5 - 1
src/global.less

@@ -4,8 +4,12 @@ input {
 
 //overwrite antd-pro css
 
-.ant-pro-global-header {
+.kcmp-ant-pro-global-header {
   padding: 0 !important;
 }
 
+.kcmp-ant-pro-sider-light {
+  border-right: 1px solid #f5f3f3;
+}
+
 //--------------------

+ 152 - 1
src/layouts/index.tsx

@@ -1,8 +1,159 @@
 /*
  * @Author: your name
  * @Date: 2021-11-09 13:56:33
- * @LastEditTime: 2021-11-09 13:56:33
+ * @LastEditTime: 2022-02-10 08:53:19
  * @LastEditors: Please set LastEditors
  * @Description: 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
  * @FilePath: /KC-MiddlePlatform/src/layouts/index.tsx
  */
+
+import { IRouteComponentProps, useModel, history } from 'umi';
+import ProLayout from '@ant-design/pro-layout';
+import ProForm, { ProFormInstance, ProFormText } from '@ant-design/pro-form';
+import { useRef } from 'react';
+import { editUsers } from '@/service/user';
+import TopBar from '@/components/topBar';
+
+import { Modal } from 'antd';
+
+const TopHoc = ({
+  systemLists,
+  openedSysLists,
+  currentSelectedSys,
+}: {
+  systemLists: TopBar.Tab[];
+  openedSysLists: TopBar.Tab[];
+  currentSelectedSys: TopBar.Tab | undefined;
+}) => {
+  const formRef = useRef<ProFormInstance>();
+  const { initialState, setInitialState } = useModel('@@initialState');
+
+  localStorage.setItem('initialState', JSON.stringify(initialState)); //缓存tab状态,防止刷新丢失
+
+  const onTabChangeHandle = async (data: TopBar.Tab[]) => {
+    await setInitialState((s) => ({ ...s, openedSysLists: [...data] }));
+    if (data.length == 0) {
+      history.push('/');
+    }
+  };
+
+  const onTabClickHandle = async (data: TopBar.Tab) => {
+    await setInitialState((s) => ({ ...s, currentSelectedSys: data }));
+    history.push(data.url);
+  };
+
+  const userPannelTabClickhandle = (tag: string) => {
+    if (tag == 'LOGOUT') {
+      if (initialState) {
+        const { logout } = initialState;
+        logout && logout();
+      }
+    }
+    if (tag == 'SETTING') {
+      if (initialState) {
+        const { userData } = initialState;
+        const editUserInfoHandle = async () => {
+          const pswd = formRef.current?.getFieldValue('password');
+          if (userData) {
+            const resp = await editUsers({
+              id: userData.userId,
+              password: pswd,
+            });
+            console.log({ resp, pswd });
+          }
+        };
+
+        Modal.confirm({
+          title: '设置个人信息',
+          icon: false,
+          onOk: editUserInfoHandle,
+          width: 500,
+          okText: '确定',
+          cancelText: '取消',
+          content: (
+            <ProForm<{ password: string }>
+              formRef={formRef}
+              initialValues={{ password: null }}
+              submitter={{
+                resetButtonProps: {
+                  style: {
+                    display: 'none',
+                  },
+                },
+                submitButtonProps: {
+                  style: {
+                    display: 'none',
+                  },
+                },
+              }}
+            >
+              <ProFormText
+                width="md"
+                name="password"
+                label="修改密码"
+                tooltip="最长为10位"
+                placeholder="请输入密码"
+              />
+            </ProForm>
+          ),
+        });
+      }
+    }
+  };
+
+  return (
+    <TopBar
+      userData={initialState?.userData}
+      sysList={systemLists}
+      openedTabs={openedSysLists}
+      onTabChange={onTabChangeHandle}
+      onTabClick={onTabClickHandle}
+      currentTab={currentSelectedSys}
+      userPannelTabClick={userPannelTabClickhandle}
+    />
+  );
+};
+
+export default function Layout({
+  children,
+  location,
+  route,
+  history,
+  match,
+}: IRouteComponentProps) {
+  const { initialState, setInitialState } = useModel('@@initialState');
+
+  // console.log({children, location, route, history, match});
+  if (location.pathname == '/index' || location.pathname == '/login') {
+    return <div>{children}</div>;
+  }
+  // return <div style={{height:'calc(100vh - 48px)'}}>{children}</div>
+
+  return (
+    <ProLayout
+      layout="top"
+      contentStyle={{
+        margin: 0,
+      }}
+      headerRender={() =>
+        initialState && (
+          <TopHoc
+            systemLists={
+              initialState.systemLists ? initialState.systemLists : []
+            }
+            openedSysLists={
+              initialState.openedSysLists ? initialState.openedSysLists : []
+            }
+            currentSelectedSys={
+              initialState.currentSelectedSys
+                ? initialState.currentSelectedSys
+                : undefined
+            }
+          />
+        )
+      }
+    >
+      <div style={{ height: 'calc(100vh - 48px)' }}>{children}</div>
+    </ProLayout>
+  );
+}

+ 50 - 51
src/models/index.ts

@@ -7,60 +7,59 @@
  * @FilePath: /KC-MiddlePlatform/src/models/model.ts
  */
 
+// import { Effect, Reducer, Subscription } from 'umi';
 
-import { Effect, Reducer, Subscription } from 'umi';
+// export interface IndexModelState {
+//   name: string;
+// }
 
-export interface IndexModelState {
-  name: string;
-}
+// export interface IndexModelType {
+//   namespace: 'index';
+//   state: IndexModelState;
+//   effects: {
+//     query: Effect;
+//   };
+//   reducers: {
+//     save: Reducer<IndexModelState>;
+//     // 启用 immer 之后
+//     // save: ImmerReducer<IndexModelState>;
+//   };
+//   subscriptions: { setup: Subscription };
+// }
 
-export interface IndexModelType {
-  namespace: 'index';
-  state: IndexModelState;
-  effects: {
-    query: Effect;
-  };
-  reducers: {
-    save: Reducer<IndexModelState>;
-    // 启用 immer 之后
-    // save: ImmerReducer<IndexModelState>;
-  };
-  subscriptions: { setup: Subscription };
-}
+// const IndexModel: IndexModelType = {
+//   namespace: 'index',
 
-const IndexModel: IndexModelType = {
-  namespace: 'index',
+//   state: {
+//     name: '',
+//   },
 
-  state: {
-    name: '',
-  },
+//   effects: {
+//     *query({ payload }, { call, put }) {},
+//   },
+//   reducers: {
+//     save(state, action) {
+//       return {
+//         ...state,
+//         ...action.payload,
+//       };
+//     },
+//     // 启用 immer 之后
+//     // save(state, action) {
+//     //   state.name = action.payload;
+//     // },
+//   },
+//   subscriptions: {
+//     setup({ dispatch, history }) {
+//       return history.listen(({ pathname }) => {
+//         if (pathname === '/') {
+//           dispatch({
+//             type: 'query',
+//           });
+//         }
+//       });
+//     },
+//   },
+// };
 
-  effects: {
-    *query({ payload }, { call, put }) {},
-  },
-  reducers: {
-    save(state, action) {
-      return {
-        ...state,
-        ...action.payload,
-      };
-    },
-    // 启用 immer 之后
-    // save(state, action) {
-    //   state.name = action.payload;
-    // },
-  },
-  subscriptions: {
-    setup({ dispatch, history }) {
-      return history.listen(({ pathname }) => {
-        if (pathname === '/') {
-          dispatch({
-            type: 'query',
-          });
-        }
-      });
-    },
-  },
-};
-
-export default IndexModel;
+// export default IndexModel;

+ 50 - 0
src/pages/index/index.less

@@ -1,4 +1,54 @@
 .indexPage {
   font-size: 0;
   min-width: 1100px;
+  height: 100vh;
+
+  .block {
+    display: flex;
+    flex-direction: row;
+    justify-content: center;
+    align-items: center;
+    position: absolute;
+    z-index: 10;
+    width: 800px;
+    height: 400px;
+    left: 50%;
+    top: 50%;
+    margin-left: -400px;
+    margin-top: -200px;
+    background: #ffffff;
+    border-radius: 24px;
+    overflow: hidden;
+    .systemSelector {
+      display: flex;
+      flex-direction: row;
+      justify-content: center;
+      align-items: center;
+
+      .sysItem {
+        display: flex;
+        flex-direction: column;
+        justify-content: center;
+        align-items: center;
+        margin-right: 80px;
+        cursor: pointer;
+        .icon {
+          width: 64px;
+          height: 64px;
+          margin-bottom: 16px;
+        }
+        .sysName {
+          height: 12px;
+          font-size: 12px;
+          font-family: SourceHanSansCN-Normal, SourceHanSansCN;
+          font-weight: 400;
+          color: #334466;
+        }
+
+        &:last-child {
+          margin-right: 0;
+        }
+      }
+    }
+  }
 }

+ 33 - 105
src/pages/index/index.tsx

@@ -1,133 +1,61 @@
 /*
  * @Author: your name
  * @Date: 2021-11-10 09:33:30
- * @LastEditTime: 2021-12-22 08:59:34
+ * @LastEditTime: 2022-02-09 15:23:59
  * @LastEditors: Please set LastEditors
  * @Description: 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
  * @FilePath: /KC-MiddlePlatform/src/pages/index.tsx
  */
 
-import { useState, useEffect } from 'react';
-import TopBar from './components/topBar';
-import IframePage from './components/iframePage';
-import { useModel,history } from 'umi';
+import { useModel, history, Location } from 'umi';
 import './index.less';
+import LoginPage from '../login';
 
-const IndexPage = () => {
+export interface IndexPageType {
+  location: Location;
+}
+
+const IndexPage: React.FC<IndexPageType> = ({ location }) => {
   const {
     systemLists, //当前医院可选子系统列表
     setInitialState,
-    openedSysLists, //当前已打开的系统列表
-    loading,
-    currentSelectedSys, //当前选中的系统
-    logout,
   } = useModel('@@initialState', (model) => {
-    console.log({ model });
     return {
       systemLists: model.initialState?.systemLists,
       setInitialState: model.setInitialState,
-      openedSysLists: model.initialState?.openedSysLists,
-      loading: model.loading,
-      currentSelectedSys: model.initialState?.currentSelectedSys,
-      logout: model.initialState?.logout,
     };
   });
 
-  const [selectableSysList, setSelectableSysList] = useState<SystemListItem[]>(
-    [],
-  ); //可选的子应用
-  const [openedTabs, setOpenTabs] = useState<SystemListItem[]>(); //当前已开启的子应用集合
-  const [currentOpenedTab, setCurrentOpenedTab] = useState<SystemListItem>(); //当前激活的应用
-
-  const onCurrentSystemChanged = async (data: SystemListItem) => {
-    console.log({ 当前选中tab: data });
-    setCurrentOpenedTab(data);
-    const index = openedSysLists?.findIndex((t) => t.id == data.id);
-    if (index != -1) {
-      //现在打开的tab里已经存在
-      await setInitialState((s) => {
-        return {
-          ...s,
-          currentSelectedSys: data,
-        };
-      });
-    } else {
-      //新打开的tab
-      await setInitialState((s) => {
-        return {
-          ...s,
-          currentSelectedSys: data,
-          openedSysLists: openedSysLists?.concat(data),
-        };
-      });
-    }
-  };
-
-  const onIframePageLoad = ()=>{
-        console.log('onload');
-        window.postMessageToChild({
-            type:'LOGIN',
-            data:{
-               name: "管理员",
-               token: "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjEwMjgwMDU2MjAyLCJ1c2VySWQiOjMwNTd9.6nCb_0c4ZW4bgN1ogq6SRGeEhFXw_p2Rf22FLFEHtXc",
-               userId: 3057
-            }
-       });
-  }
+  const selectSysHandle = async (systemInfo: userRelationInfo.OwnAppsItem) => {
+    await setInitialState((s) => ({
+      ...s,
+      currentSelectedSys: systemInfo,
+      openedSysLists: [systemInfo],
+    }));
+    const { query } = history.location;
+    const { redirect } = query as { redirect: string };
 
-  const userPannelTabClick = async (tag: string) => {
-    if (tag == 'LOGOUT') {
-      if (logout) {
-        await logout();
-      }
-    }
+    history.push(redirect || systemInfo.url);
   };
 
-  useEffect(() => {
-    if (systemLists) setSelectableSysList(systemLists);
-  }, [systemLists]);
-
-  useEffect(() => {
-    if (openedSysLists) setOpenTabs(openedSysLists);
-    // console.log({openedSysLists});
-  }, [openedSysLists]);
-
-  useEffect(() => {
-    setCurrentOpenedTab(currentSelectedSys); //当前激活的tab
-  }, [currentSelectedSys]);
-
-  useEffect(() => {
-    if (systemLists) setSelectableSysList(systemLists);
-    if (openedSysLists) setOpenTabs(openedSysLists);
-
-    return ()=>{
-      window.removeEventListener('message',()=>{
-              console.log('remove message listener');
-      }); 
-    }
-  }, []);
-
-  if (loading) return <h1>Loading...</h1>;
-
-  
-
   return (
     <div className="indexPage">
-      <TopBar
-        openedTabs={openedTabs ? openedTabs : []}
-        onTabChange={(data) => {
-          onCurrentSystemChanged(data);
-        }}
-        sysList={selectableSysList}
-        currentTab={currentOpenedTab}
-        userPannelTabClick={(tag) => userPannelTabClick(tag)}
-      />
-
-      {/* <MicroApp name='MedicalWisdomCheckSys' /> */}
-
-      {openedTabs && (
-        <IframePage activedItem={currentOpenedTab} list={openedTabs} onload={onIframePageLoad} />
-      )}
+      <LoginPage location={location}>
+        <div className="block">
+          <div className="systemSelector">
+            {systemLists?.map((t) => (
+              <div
+                className="sysItem"
+                key={t.id}
+                onClick={() => selectSysHandle(t)}
+              >
+                <img className="icon" src={t.icon} alt="" />
+                <span className="sysName">{t.name}</span>
+              </div>
+            ))}
+          </div>
+        </div>
+      </LoginPage>
     </div>
   );
 };

+ 150 - 132
src/pages/login/index.tsx

@@ -1,30 +1,33 @@
 /*
  * @Author: your name
  * @Date: 2021-11-09 14:58:08
- * @LastEditTime: 2022-01-06 10:52:32
+ * @LastEditTime: 2022-02-10 16:52:48
  * @LastEditors: Please set LastEditors
  * @Description: 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
  * @FilePath: /KC-MiddlePlatform/src/pages/login/index.tsx
  */
 
-import React, { useRef, useEffect, useState } from 'react';
+import React, { useRef, useEffect, useState, Children } from 'react';
 import { Select, message } from 'antd';
 import './style.less';
 
-import { useModel, history } from 'umi';
+import { useModel, history, Location } from 'umi';
 
-import { Form, Input, Button, Checkbox } from 'antd';
+import { Form, Input, Button, Checkbox, notification } from 'antd';
 
 import logo from '../../../public/images/kc-logo.png';
 
 import KCSelect from '@/components/kc-select';
 
-import { getSubHosp, login, getHospSubSystemList } from '@/service/login';
-
+import { getHospConfigBySign, login } from '@/service/login';
 
 const { Option } = Select;
 
-const LoginPage: React.FC<any> = () => {
+export interface LoginPageType {
+  location: Location;
+}
+
+const LoginPage: React.FC<LoginPageType> = ({ location, children }) => {
   const [transitionObj, setTransitionObj] = useState<string>();
   const [transformObj, setTransformObj] = useState<string>();
   const loginPageRef = useRef<HTMLDivElement>(null);
@@ -33,8 +36,12 @@ const LoginPage: React.FC<any> = () => {
   >([]); //分院列表
   const [ifSelectSystem, setIfSelectSystem] = useState(false); //是否跳转到选择系统界面
   const [ifLoading, setIfLoading] = useState(false);
-  const [systemList, setSystemList] = useState<SystemListItem[]>([]); //可选平台列表
-  const { setInitialState } = useModel('@@initialState');
+  const [systemList, setSystemList] = useState<userRelationInfo.OwnAppsItem[]>(
+    [],
+  ); //可选平台列表
+  const [currentHospName, setCurrentHospName] =
+    useState('欢迎进入医务管理系统');
+  const { initialState, setInitialState } = useModel('@@initialState');
 
   const onMouseMoveHandle = (mouseEvent: any) => {
     //    console.log({mouseEvent});
@@ -64,63 +71,86 @@ const LoginPage: React.FC<any> = () => {
 
   //获取当前账号分院列表
   const getSubHospFunc = async () => {
-    const { list } = await getSubHosp();
-    setSubHospList(list);
-  };
-
-  //获取当前账号所有子应用列表
-  const getHospSubSystemListFunc = async (): Promise<SystemListItem[]> => {
-    const data = await getHospSubSystemList();
-    if (data) setSystemList(data);
-    return data;
+    const hospSign = history.location.query?.hospSign as string;
+    if (hospSign) {
+      const data = await getHospConfigBySign(hospSign);
+      if (!data) return;
+      setSubHospList(
+        data.map((t) => ({
+          name: t.name,
+          value: t.hospSign,
+        })),
+      );
+      setCurrentHospName(data[0].systemName);
+    }
   };
 
   const onFinish = async (values: LoginPageTypes.LoginInfo) => {
     setIfLoading(true);
-    const resp = await login(values);
+    const hospSign = history.location.query?.hospSign as string;
+    if (!hospSign) {
+      notification.error({
+        message: '网址标记缺失,请检查网址!',
+      });
+      setIfLoading(false);
+      return;
+    }
+    const resp = await login({
+      account: values.account,
+      password: values.password,
+      hospSign: hospSign,
+      // remember:false,
+    });
     setIfLoading(false);
     if (resp) {
-      // message.success('登录成功!');
-      const data = await getHospSubSystemListFunc();
-      await setInitialState((s) => ({
-        ...s,
-        userData:{},
-        systemLists: data,
-      }));
-
-      localStorage.setItem('KC-MiddlePlatformUserData', JSON.stringify(resp));
-
-      if (data.length > 1) {
-        //当前医院拥有多个子平台时,进入选择
-        setIfSelectSystem(true);
-      } else {
-        //否则直接进入首页
-        const { query } = history.location;
-        const { redirect } = query as { redirect: string };
-        history.replace(redirect || '/');
-        return;
+      localStorage.setItem('userData', JSON.stringify(resp));
+      localStorage.setItem('hospSign', hospSign);
+
+      if (initialState && initialState.getHospSubSystemListFunc) {
+        const data = await initialState.getHospSubSystemListFunc();
+        await setInitialState((s) => ({
+          ...s,
+          systemLists: data,
+          userData: resp,
+        }));
+
+        setSystemList(data);
+
+        if (data.length > 1) {
+          //当前医院拥有多个子平台时,进入选择
+          setIfSelectSystem(true);
+          history.replace('/index');
+        } else {
+          //否则直接进入首页
+          const { query } = history.location;
+          const { redirect } = query as { redirect: string };
+          history.replace(redirect || '/index');
+          return;
+        }
       }
+
       setIfLoading(false);
     }
   };
 
-  const selectSysHandle = async (systemInfo: SystemListItem) => {
-    await setInitialState((s) => ({
-      ...s,
-      currentSelectedSys: systemInfo,
-      openedSysLists: [systemInfo],
-    }));
-    const { query } = history.location;
-    const { redirect } = query as { redirect: string };
-    
-    history.push(redirect || '/');
-  };
-
   useEffect(() => {
     //根据hospSign获取分院信息
-    getSubHospFunc();
+    location.pathname == '/login' && getSubHospFunc();
+
+    // window.addEventListener('message',(e)=>{
+    //      console.log(e.origin)
+    //      if(e.origin == 'http://192.168.50.76:8902'){
+    //       console.log({e})
+    //      }
+    // }, false);
   }, []);
 
+  // return (
+  //   <div>
+  //       <iframe src="http://192.168.50.76:8902/TracerMethodology/?hospSign=IGlcn5nc4xBWc08C"></iframe>
+  //   </div>
+  // )
+
   return (
     <div
       className="loginPage"
@@ -137,90 +167,78 @@ const LoginPage: React.FC<any> = () => {
         className="circleLittleOne"
         style={{ transform: transformObj, transition: transitionObj }}
       ></div>
-      <div className="loginBlock">
-        {ifSelectSystem ? (
-          <div className="systemSelector">
-            {systemList.map((t) => (
-              <div
-                className="sysItem"
-                key={t.id}
-                onClick={() => selectSysHandle(t)}
-              >
-                <img className="icon" src={t.icon} alt="" />
-                <span className="sysName">{t.name}</span>
-              </div>
-            ))}
+      {location.pathname == '/login' ? (
+        <div className="loginBlock">
+          <div className="left">
+            <img
+              className="bannerImg"
+              src={require('../../../public/images/loginBannner.png')}
+            />
           </div>
-        ) : (
-          <>
-            <div className="left">
-              <img
-                className="bannerImg"
-                src={require('../../../public/images/loginBannner.png')}
-              />
-            </div>
-            <div className="rightLoginArea">
-              <div className="subHospSelector">
-                {subHospList.length > 0 && (
-                  <KCSelect
-                    allowClear={false}
-                    style={{ width: 200 }}
-                    defaultValue={subHospList[0].value}
-                    suffixIcon={
-                      <img
-                        style={{ width: '10px', height: '6px' }}
-                        src={require('../../../public/images/arrow.png')}
-                      />
-                    }
-                  >
-                    {subHospList.map((item) => {
-                      return (
-                        <Option value={item.value} key={item.value}>
-                          {item.name}
-                        </Option>
-                      );
-                    })}
-                  </KCSelect>
-                )}
-              </div>
-              <div className="systemName">欢迎进入医务管理系统</div>
-              <Form onFinish={onFinish}>
-                <Form.Item
-                  name="account"
-                  rules={[
-                    { required: true, message: 'Please input your username!' },
-                  ]}
+          <div className="rightLoginArea">
+            <div className="subHospSelector">
+              {subHospList.length > 0 && (
+                <KCSelect
+                  allowClear={false}
+                  style={{ width: 150 }}
+                  defaultValue={subHospList[0].value}
+                  suffixIcon={
+                    <img
+                      style={{ width: '10px', height: '6px' }}
+                      src={require('../../../public/images/arrow.png')}
+                    />
+                  }
                 >
-                  <Input className="input" />
-                </Form.Item>
-
-                <Form.Item
-                  name="password"
-                  rules={[
-                    { required: true, message: 'Please input your password!' },
-                  ]}
-                >
-                  <Input.Password className="input" />
-                </Form.Item>
-
-                <Form.Item>
-                  <Button
-                    className="loginBtn"
-                    type="primary"
-                    htmlType="submit"
-                    loading={ifLoading}
-                  >
-                    登录
-                  </Button>
-                </Form.Item>
-                <Form.Item name="remember" valuePropName="checked">
-                  <Checkbox className="checkBtn">记住密码</Checkbox>
-                </Form.Item>
-              </Form>
+                  {subHospList.map((item) => {
+                    return (
+                      <Option value={item.value} key={item.value}>
+                        {item.name}
+                      </Option>
+                    );
+                  })}
+                </KCSelect>
+              )}
             </div>
-          </>
-        )}
-      </div>
+            <div className="systemName">{currentHospName}</div>
+            <Form onFinish={onFinish}>
+              <Form.Item
+                name="account"
+                rules={[
+                  { required: true, message: 'Please input your username!' },
+                ]}
+              >
+                <Input className="input" />
+              </Form.Item>
+
+              <Form.Item
+                name="password"
+                rules={[
+                  { required: true, message: 'Please input your password!' },
+                ]}
+              >
+                <Input.Password className="input" />
+              </Form.Item>
+
+              <Form.Item>
+                <Button
+                  className="loginBtn"
+                  type="primary"
+                  htmlType="submit"
+                  loading={ifLoading}
+                >
+                  登录
+                </Button>
+              </Form.Item>
+              {/* <Form.Item name="remember" valuePropName="checked">
+                    <Checkbox className="checkBtn">记住密码</Checkbox>
+                  </Form.Item> */}
+            </Form>
+          </div>
+        </div>
+      ) : (
+        children
+      )}
+
       <div className="bottomLogo">
         <img className="logo" src={logo} />
         <div className="copyright">© 2021 康程智医(成都)技术部出品</div>

+ 3 - 2
src/pages/login/login.d.ts

@@ -1,7 +1,7 @@
 /*
  * @Author: your name
  * @Date: 2021-11-10 10:05:13
- * @LastEditTime: 2021-11-15 17:10:14
+ * @LastEditTime: 2022-01-10 14:54:26
  * @LastEditors: Please set LastEditors
  * @Description: 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
  * @FilePath: /KC-MiddlePlatform/src/pages/login/login.d.ts
@@ -18,7 +18,8 @@ declare namespace LoginPageTypes {
   //登录发送信息
   type LoginInfo = {
     account: string;
-    remember: boolean;
+    // remember: boolean;
     password: string;
+    hospSign: string;
   };
 }

+ 96 - 121
src/pages/login/style.less

@@ -3,7 +3,7 @@
   width: 100%;
   height: 100vh;
   min-width: 1100px;
-  background-image: linear-gradient(to right, #3264F2, #3B76F3);
+  background-image: linear-gradient(to right, #3264f2, #3b76f3);
   overflow: hidden;
 
   .circleBigOne {
@@ -13,7 +13,11 @@
     margin-left: 11%;
     z-index: 1;
     border-radius: 50%;
-    background: linear-gradient(126deg, #6699FF 0%, rgba(102, 153, 255, 0) 100%);
+    background: linear-gradient(
+      126deg,
+      #6699ff 0%,
+      rgba(102, 153, 255, 0) 100%
+    );
     opacity: 0.6;
     // transform: rotate(60deg);
     // background-image: linear-gradient(to right, #5083F9 ,#3C71F5);
@@ -27,7 +31,7 @@
     right: -120px;
     z-index: 1;
     transform: rotate(350deg);
-    background: linear-gradient(36deg, #5990FF 0%, rgba(88, 143, 252, 0) 100%);
+    background: linear-gradient(36deg, #5990ff 0%, rgba(88, 143, 252, 0) 100%);
     opacity: 0.6;
   }
 
@@ -42,7 +46,7 @@
     z-index: 1;
     // transform: rotate(60deg);
     // background-image: linear-gradient(to right, #5083F9 ,#3C71F5);
-    background: linear-gradient(126deg, #5990FF 0%, rgba(88, 143, 252, 0) 100%);
+    background: linear-gradient(126deg, #5990ff 0%, rgba(88, 143, 252, 0) 100%);
     opacity: 0.6;
 
     &::after {
@@ -52,7 +56,7 @@
       width: 80px;
       height: 22px;
       top: 88px;
-      background: #0D2980;
+      background: #0d2980;
       opacity: 0.1;
       border-radius: 50%;
       filter: blur(3px);
@@ -72,127 +76,98 @@
     top: 50%;
     margin-left: -400px;
     margin-top: -200px;
-    background: #FFFFFF;
+    background: #ffffff;
     border-radius: 24px;
     overflow: hidden;
 
-    .systemSelector {
-        display: flex;
-        flex-direction: row;
-        justify-content: center;
-        align-items: center;
-
-        .sysItem {
-            display: flex;
-            flex-direction: column;
-            justify-content: center;
-            align-items: center;
-            margin-right: 80px;
-            cursor: pointer;
-            .icon {
-                width: 64px;
-                height: 64px;
-                margin-bottom: 16px;
-            }
-            .sysName {
-                height: 12px;
-                font-size: 12px;
-                font-family: SourceHanSansCN-Normal, SourceHanSansCN;
-                font-weight: 400;
-                color: #334466;
-            }
-
-            &:last-child {
-                margin-right: 0;
-            }
-        }
-    }
     .left {
-        display: flex;
-        width: 50%;
-        height: 100%;
-        justify-content: center;
-        align-items: center;
-        text-align: center;
-        background: linear-gradient(135deg, #FAFCFF 0%, #F2F9FF 100%);
-        .bannerImg {
-            width: 72%;
-        }
+      display: flex;
+      width: 50%;
+      height: 100%;
+      justify-content: center;
+      align-items: center;
+      text-align: center;
+      background: linear-gradient(135deg, #fafcff 0%, #f2f9ff 100%);
+      .bannerImg {
+        width: 72%;
+      }
     }
     .rightLoginArea {
-        display: flex;
-        width: 50%;
-        height: 100%;
-        flex-direction: column;
-        justify-content:center;
-        align-items: flex-start;
-        padding-left: 60px;
-
-        .subHospSelector {
-            margin-bottom: 10px;
-        }
-        .systemName {
-            font-size: 24px;
-            height: 24px;
-            line-height: 24px;
-            font-family: SourceHanSansCN-Light, SourceHanSansCN;
-            font-weight: 300;
-            color: #1A2233;
-            margin-bottom: 32px;
-        }
-        .input {
-            width: 280px;
-            height: 40px;
-            background: #FFFFFF;
-            border-radius: 4px;
-            border: 1px solid #CFD7E6;
-        }
-
-        .loginBtn {
-            width: 280px;
-            height: 40px;
-            background: #3377FF;
-            border-radius: 4px;
-        }
-
-        .checkBtn {
-            .ant-checkbox-checked .ant-checkbox-inner {
-                background-color: #407FFF;
-                border-color: #407FFF;
-            }
-        }
-
-        .ant-select {
-            border:none;
-            outline: none;
-        }
-        .ant-select-focused {
-            border:none;
-            outline: none;
-        }
-        .ant-select-selector {
-            border:none;
-            outline: none;
-        }
-
-        .ant-select-focused:not(.ant-select-disabled).ant-select:not(.ant-select-customize-input) .ant-select-selector {
-            border-color: transparent;
-            box-shadow: none;
-            padding: 0;
-        }  
-        
-        .ant-select-single:not(.ant-select-customize-input) .ant-select-selector {
-            width: 100%;
-            height: 32px;
-            padding: 0;
-        }
-
-        .ant-select-selection-item {
-            font-size: 14px;
-            font-family: SourceHanSansCN-Normal, SourceHanSansCN;
-            font-weight: 400;
-            color: #666F80;
+      display: flex;
+      width: 50%;
+      height: 100%;
+      flex-direction: column;
+      justify-content: center;
+      align-items: flex-start;
+      padding-left: 60px;
+
+      .subHospSelector {
+        margin-bottom: 10px;
+      }
+      .systemName {
+        font-size: 24px;
+        height: 24px;
+        line-height: 24px;
+        font-family: SourceHanSansCN-Light, SourceHanSansCN;
+        font-weight: 300;
+        color: #1a2233;
+        margin-bottom: 32px;
+      }
+      .input {
+        width: 280px;
+        height: 40px;
+        background: #ffffff;
+        border-radius: 4px;
+        border: 1px solid #cfd7e6;
+      }
+
+      .loginBtn {
+        width: 280px;
+        height: 40px;
+        background: #3377ff;
+        border-radius: 4px;
+      }
+
+      .checkBtn {
+        .kcmp-ant-checkbox-checked .kcmp-ant-checkbox-inner {
+          background-color: #407fff;
+          border-color: #407fff;
         }
+      }
+
+      .kcmp-ant-select {
+        border: none;
+        outline: none;
+      }
+      .kcmp-ant-select-focused {
+        border: none;
+        outline: none;
+      }
+      .kcmp-ant-select-selector {
+        border: none;
+        outline: none;
+      }
+
+      .kcmp-ant-select-focused:not(.kcmp-ant-select-disabled).kcmp-ant-select:not(.kcmp-ant-select-customize-input)
+        .kcmp-ant-select-selector {
+        border-color: transparent;
+        box-shadow: none;
+        padding: 0;
+      }
+
+      .kcmp-ant-select-single:not(.kcmp-ant-select-customize-input)
+        .kcmp-ant-select-selector {
+        width: 100%;
+        height: 32px;
+        padding: 0;
+      }
+
+      .kcmp-ant-select-selection-item {
+        font-size: 14px;
+        font-family: SourceHanSansCN-Normal, SourceHanSansCN;
+        font-weight: 400;
+        color: #666f80;
+      }
     }
   }
 
@@ -211,14 +186,14 @@
     .logo {
       width: 144px;
       height: 24px;
-      margin-bottom:12px;
+      margin-bottom: 12px;
     }
 
     .copyright {
       font-size: 12px;
       font-family: SourceHanSansCN-Normal, SourceHanSansCN;
       font-weight: 400;
-      color: #FFFFFF;
+      color: #ffffff;
     }
   }
 }

+ 105 - 0
src/pages/platform/_layout.tsx

@@ -0,0 +1,105 @@
+/*
+ * @Author: your name
+ * @Date: 2022-01-06 15:25:39
+ * @LastEditTime: 2022-02-08 17:24:15
+ * @LastEditors: Please set LastEditors
+ * @Description: 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
+ * @FilePath: /KC-MiddlePlatform/src/pages/platform/_layout.tsx
+ */
+
+import { IRouteComponentProps, useModel } from 'umi';
+// import { CrownOutlined, UserOutlined, SmileOutlined } from '@ant-design/icons';
+import ProLayout, { PageContainer } from '@ant-design/pro-layout';
+
+import { getPlatformMenu } from '@/service/menu';
+import './index.less';
+
+export default function Layout({
+  children,
+  location,
+  route,
+  history,
+  match,
+}: IRouteComponentProps) {
+  const { initialState, setInitialState } = useModel('@@initialState');
+
+  // console.log({children, location, route, history, match});
+
+  return (
+    <ProLayout
+      style={{
+        height: 'calc(100vh - 50px)',
+      }}
+      logoStyle={{
+        display: 'none',
+      }}
+      contentStyle={{}}
+      disableContentMargin
+      menuItemRender={(item, dom) => (
+        <a
+          onClick={() => {
+            history.push(item.path || '/');
+          }}
+        >
+          {dom}
+        </a>
+      )}
+      menu={{
+        request: async () => {
+          if (initialState) {
+            const systemId = initialState.currentSelectedSys?.id;
+            if (systemId) {
+              const menuData = await getPlatformMenu(systemId);
+
+              // return menuData;
+              return [
+                {
+                  path: '/platform/setting',
+                  name: '系统设置',
+                  routes: [
+                    {
+                      path: '/platform/setting/userManage',
+                      name: '用户管理',
+                    },
+                    {
+                      path: '/platform/setting/hospManage',
+                      name: '院区管理',
+                    },
+                    {
+                      path: '/platform/setting/menuManage',
+                      name: '菜单管理',
+                    },
+                    {
+                      path: '/platform/setting/roleManage',
+                      name: '角色管理',
+                    },
+                  ],
+                },
+              ];
+            }
+            return [];
+          }
+          return [];
+        },
+      }}
+      onPageChange={(location) => {}}
+      layout="side"
+      headerRender={false}
+      navTheme="light"
+    >
+      <PageContainer
+        header={{}}
+        style={{
+          margin: 0,
+        }}
+      >
+        <div
+          className="page"
+          style={{ height: 'calc(100vh - 180px)', overflowY: 'scroll' }}
+        >
+          {children}
+        </div>
+      </PageContainer>
+    </ProLayout>
+  );
+}

+ 209 - 0
src/pages/platform/components/menuEditer/menu.tsx

@@ -0,0 +1,209 @@
+/*
+ * @Author: your name
+ * @Date: 2022-01-14 14:03:42
+ * @LastEditTime: 2022-02-08 10:28:11
+ * @LastEditors: Please set LastEditors
+ * @Description: 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
+ * @FilePath: /KC-MiddlePlatform/src/pages/platform/setting/hospManage/modals/menu.tsx
+ */
+
+import React, { Key, useEffect, useState } from 'react';
+import KCTable from '@/components/kcTable';
+import { Popconfirm } from 'antd';
+import { Input } from 'antd';
+import { getAllMenus } from '@/service/menu';
+import { ProColumns } from '@ant-design/pro-table';
+import type { MenuItemDataType } from '@/service/menu';
+
+import { DownOutlined, FileOutlined, RightOutlined } from '@ant-design/icons';
+import './style.less';
+import { MenuTypes } from '@/constant';
+import { getValsFromTree } from '@/utils';
+
+const { Search } = Input;
+
+export interface MenuEditerType {
+  value?: React.Key[] | React.Key[];
+  onChange?: (selectedRowKeys: React.Key[], selectedRows: any[]) => {};
+  noAction?: boolean;
+}
+
+const MenuEditer: React.FC<MenuEditerType> = ({
+  value = [],
+  onChange,
+  noAction = false,
+}) => {
+  const columns: ProColumns<MenuItemDataType>[] = [
+    {
+      title: 'ID',
+      dataIndex: 'menuId',
+      hideInTable: true,
+    },
+    {
+      title: 'parentId',
+      dataIndex: 'parentId',
+      hideInTable: true,
+    },
+    {
+      title: '菜单名称',
+      dataIndex: 'name',
+      hideInSearch: false,
+      render: (text, record) => {
+        if (record.children && record.children.length > 0) {
+          return text;
+        } else {
+          //叶子节点
+          return record.parentId != '0' ? (
+            //非第一级
+            <div>
+              <FileOutlined style={{ marginRight: 10, marginLeft: 30 }} />
+              {text}
+            </div>
+          ) : (
+            text
+          );
+        }
+      },
+    },
+    {
+      title: '类型',
+      dataIndex: 'isHospital',
+      render: (number, record) => {
+        const findType = MenuTypes.filter((t) => t.value == record.type);
+        return findType[0].label;
+      },
+    },
+    {
+      title: '操作',
+      width: 100,
+      key: 'option',
+      valueType: 'option',
+      render: (text, record) =>
+        record.parentId == '0'
+          ? [
+              <Popconfirm
+                title="是否初始化?"
+                onConfirm={() => {}}
+                // onCancel={cancel}
+                okText="确定"
+                cancelText="取消"
+                key="link2"
+              >
+                <a>初始化</a>
+              </Popconfirm>,
+            ]
+          : [],
+    },
+  ];
+
+  const getData = async () => {
+    const resp = await getAllMenus();
+    const data = resp.list ? resp.list : [];
+    setAllMenuData(data);
+    setDataSource(data);
+  };
+
+  const onChangeHandle = (
+    selectedRowKeys: React.Key[],
+    selectedRows: any[],
+  ) => {
+    setSelectedKeys(selectedRowKeys);
+    onChange && onChange(selectedRowKeys, selectedRows);
+  };
+
+  const [selectedKeys, setSelectedKeys] = useState<React.Key[]>([]);
+  const [dataSource, setDataSource] = useState<MenuItemDataType[]>([]);
+  const [allMenuData, setAllMenuData] = useState<MenuItemDataType[]>([]);
+  const [expandedRowKeys, setExpandedRowKeys] = useState<readonly Key[]>([]);
+
+  const deeper = (data: MenuItemDataType[], keyword: string) => {
+    let result: MenuItemDataType[] = [];
+
+    data.forEach((t, index) => {
+      if (t.name.includes(keyword)) {
+        result.push({ ...t, children: [] }); //不带出子集
+      } else {
+        if (t.children && t.children.length > 0) {
+          result.push({ ...t, children: deeper(t.children, keyword) });
+        }
+      }
+    });
+    return result;
+  };
+
+  const onSearchHandle = (value: string) => {
+    if (value.length == 0) {
+      setDataSource(allMenuData);
+      return;
+    }
+    const result = deeper(dataSource, value);
+    console.log({ result });
+    setDataSource(result);
+  };
+
+  const onExpandedRowsChangeHandle = (expandedRows: readonly Key[]) => {
+    setExpandedRowKeys(expandedRows);
+  };
+
+  useEffect(() => {
+    if (dataSource.length > 0) {
+      setSelectedKeys(value); //设置默认选中项
+      setExpandedRowKeys(value); //已选中的项默认展开
+    }
+    const openresult = getValsFromTree(dataSource, 'menuId');
+    setExpandedRowKeys(openresult);
+  }, [dataSource]);
+
+  useEffect(() => {
+    getData();
+  }, []);
+
+  return (
+    <div className="MenuEditer">
+      <div className="search">
+        <span className="label">搜索:</span>
+        <Search
+          className="valueArea"
+          onSearch={onSearchHandle}
+          allowClear={true}
+          placeholder="请输入"
+        />
+      </div>
+      <KCTable
+        rowKey="menuId"
+        columns={columns.filter((t) => (noAction ? t.title != '操作' : t))}
+        dataSource={dataSource}
+        search={false}
+        options={false}
+        pagination={false}
+        expandable={{
+          expandedRowKeys: expandedRowKeys,
+          expandIcon: ({ expanded, onExpand, record }) => {
+            // console.log({expanded, onExpand, record});
+            return expanded
+              ? record.children.length > 0 && (
+                  <DownOutlined
+                    onClick={(e) => onExpand(record, e)}
+                    style={{ marginRight: 10 }}
+                  />
+                )
+              : record.children.length > 0 && (
+                  <RightOutlined
+                    onClick={(e) => onExpand(record, e)}
+                    style={{ marginRight: 10 }}
+                  />
+                );
+          },
+          onExpandedRowsChange: onExpandedRowsChangeHandle,
+        }}
+        rowSelection={{
+          checkStrictly: false,
+          selectedRowKeys: selectedKeys,
+          onChange: onChangeHandle,
+        }}
+      />
+    </div>
+  );
+};
+
+export default MenuEditer;

+ 19 - 0
src/pages/platform/components/menuEditer/style.less

@@ -0,0 +1,19 @@
+.MenuEditer {
+  .kcmp-ant-card-body {
+    padding: 0;
+  }
+
+  .search {
+    display: flex;
+    flex-direction: row;
+    align-items: center;
+    margin-bottom: 24px;
+    .label {
+      white-space: nowrap;
+    }
+    .valueArea {
+      border-radius: 4px;
+      // border: 1px solid #CFD7E6;
+    }
+  }
+}

+ 146 - 0
src/pages/platform/components/usersEditer/index.tsx

@@ -0,0 +1,146 @@
+/*
+ * @Author: your name
+ * @Date: 2022-01-19 14:53:37
+ * @LastEditTime: 2022-01-20 10:28:32
+ * @LastEditors: Please set LastEditors
+ * @Description: 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
+ * @FilePath: /KC-MiddlePlatform/src/pages/platform/components/usersEditer/index.tsx
+ */
+
+import React, { useEffect, useState } from 'react';
+import KCTable from '@/components/kcTable';
+import { Input } from 'antd';
+import { getUsers } from '@/service/user';
+import type { EditUsersDataType } from '@/service/user';
+import { ProColumns } from '@ant-design/pro-table';
+import { TableRequestParamsType } from '@/typings';
+import './style.less';
+
+export interface UserEditerType {
+  value?: React.Key[] | React.Key[];
+  onChange?: (selectedRowKeys: React.Key[]) => {};
+  noAction?: boolean;
+}
+
+const UserEditer: React.FC<UserEditerType> = ({
+  value = [],
+  onChange,
+  noAction = false,
+}) => {
+  const columns: ProColumns<EditUsersDataType>[] = [
+    {
+      title: 'ID',
+      dataIndex: 'id',
+      hideInTable: true,
+    },
+    {
+      title: '姓名',
+      dataIndex: 'name',
+      hideInSearch: false,
+    },
+    {
+      title: '工号',
+      dataIndex: 'account',
+    },
+    {
+      title: '在职状态',
+      dataIndex: 'isOnService',
+      render: (number, record) => {
+        return record.isOnService == 0 ? '在职' : '离职';
+      },
+    },
+  ];
+
+  const getData = async (
+    params: { name?: string } & TableRequestParamsType,
+  ) => {
+    const { current = 1, pageSize = 10, keyword } = params;
+    const resp = await getUsers({
+      current,
+      pageSize,
+      keyword,
+    });
+    return {
+      data: resp.list ? resp.list : [],
+      success: true,
+      total: resp.totalCount,
+    };
+  };
+
+  const onChangeHandle = (
+    selectedRowKeys: React.Key[],
+    selectedRows: any[],
+  ) => {
+    if (selectedRows.length == 0) {
+      //取消选择时
+      setSelectedKeys([]);
+    }
+  };
+
+  const onSearchHandle = (
+    e: React.ChangeEvent & { target: { value: string } },
+  ) => {
+    setKeyword(e.target.value);
+  };
+
+  const [selectedKeys, setSelectedKeys] = useState<React.Key[]>([]);
+  const [keyword, setKeyword] = useState('');
+
+  //record: T, selected: boolean, selectedRows: T[], nativeEvent: Event
+  const onSelectHandle = (
+    record: EditUsersDataType,
+    selected: boolean,
+    selectedRows: EditUsersDataType[],
+    nativeEvent: Event,
+  ) => {
+    // console.log({record,selected,selectedRows,nativeEvent});
+    if (selected) {
+      //保证每次选择能够保留之前选中的
+      setSelectedKeys([...selectedKeys, record.id]);
+    } else {
+      const _selectedKeys = selectedKeys.filter((t) => t != record.id);
+      setSelectedKeys([..._selectedKeys]);
+    }
+  };
+
+  useEffect(() => {
+    onChange && onChange(selectedKeys);
+  }, [selectedKeys]);
+
+  useEffect(() => {
+    setTimeout(() => {
+      //让设置key的操作进入宏任务,等待微任务执行完毕
+      setSelectedKeys(value);
+    }, 500);
+  }, []);
+
+  return (
+    <div className="UserEditer">
+      <div className="search">
+        <span className="label">搜索人员:</span>
+        <Input
+          className="valueArea"
+          onChange={onSearchHandle}
+          allowClear={true}
+          placeholder="请输入姓名、工号"
+        />
+      </div>
+      <KCTable
+        rowKey="id"
+        columns={columns.filter((t) => (noAction ? t.title != '操作' : t))}
+        request={getData}
+        params={{ keyword: keyword }}
+        options={false}
+        search={false}
+        rowSelection={{
+          checkStrictly: false,
+          selectedRowKeys: selectedKeys,
+          onChange: onChangeHandle,
+          onSelect: onSelectHandle,
+        }}
+      />
+    </div>
+  );
+};
+
+export default UserEditer;

+ 24 - 0
src/pages/platform/components/usersEditer/style.less

@@ -0,0 +1,24 @@
+.UserEditer {
+  .kcmp-ant-form-item {
+    margin-bottom: 0;
+  }
+  .kcmp-ant-pro-table-search {
+    padding: 0;
+  }
+  .kcmp-ant-card-body {
+    padding: 0;
+  }
+  .search {
+    display: flex;
+    flex-direction: row;
+    align-items: center;
+    margin-bottom: 24px;
+    .label {
+      white-space: nowrap;
+    }
+    .valueArea {
+      border-radius: 4px;
+      border: 1px solid #cfd7e6;
+    }
+  }
+}

+ 8 - 0
src/pages/platform/index.less

@@ -0,0 +1,8 @@
+// .ant-pro-sider-light  {
+//     overflow: visible !important;
+//     // box-shadow: 2px 0px 6px 0px rgba(54, 61, 77, 0.1);
+//     box-shadow: 12px 12px 2px 1px rgba(0, 0, 255, .2);
+// }
+.ant-pro-sider-light .ant-menu-light {
+  border-right-color: rgba(54, 61, 77, 0.1);
+}

+ 43 - 0
src/pages/platform/index/index.tsx

@@ -0,0 +1,43 @@
+/*
+ * @Author: your name
+ * @Date: 2022-01-06 15:07:59
+ * @LastEditTime: 2022-01-06 15:09:45
+ * @LastEditors: Please set LastEditors
+ * @Description: 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
+ * @FilePath: /KC-MiddlePlatform/src/pages/platform/index.tsx
+ */
+
+/*
+ * @Author: your name
+ * @Date: 2021-11-10 09:33:30
+ * @LastEditTime: 2022-01-06 14:15:37
+ * @LastEditors: Please set LastEditors
+ * @Description: 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
+ * @FilePath: /KC-MiddlePlatform/src/pages/index.tsx
+ */
+
+import { useState, useEffect } from 'react';
+import { useModel, history } from 'umi';
+
+const IndexPage = () => {
+  return (
+    <div className="indexPage">
+      PLATFORM
+      {/* <TopBar
+        openedTabs={openedTabs ? openedTabs : []}
+        onTabChange={(data) => {
+          onCurrentSystemChanged(data);
+        }}
+        sysList={selectableSysList}
+        currentTab={currentOpenedTab}
+        userPannelTabClick={(tag) => userPannelTabClick(tag)}
+      /> */}
+      {/* <MicroApp name='MedicalWisdomCheckSys' /> */}
+      {/* {openedTabs && (
+        <IframePage activedItem={currentOpenedTab} list={openedTabs} onload={onIframePageLoad} />
+      )} */}
+    </div>
+  );
+};
+
+export default IndexPage;

+ 219 - 0
src/pages/platform/setting/hospManage/index.tsx

@@ -0,0 +1,219 @@
+/*
+ * @Author: your name
+ * @Date: 2022-01-13 15:22:48
+ * @LastEditTime: 2022-02-08 14:29:30
+ * @LastEditors: Please set LastEditors
+ * @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 { hospManageModelState, ConnectProps, Loading, connect } from 'umi';
+import { Button, Divider, Popconfirm } from 'antd';
+import KCTable from '@/components/kcTable';
+import type { ProColumns } from '@ant-design/pro-table';
+import { getAllHosp } from '@/service/hospList';
+import { TableRequestParamsType } from '@/typings';
+import ActModal from './modals/modal';
+import './style.less';
+import { TableListItem } from './typings';
+import { getSpacifyHospMenu } from '@/service/menu';
+
+export enum TableActType {
+  NOACT,
+  ADD,
+  DEL,
+  EDIT,
+  EDITMENU,
+}
+
+interface PageProps extends ConnectProps {
+  hospManageModel: hospManageModelState;
+  loading: boolean;
+}
+
+const HospManage: FC<PageProps> = ({ hospManageModel: state, dispatch }) => {
+  const { reloadTable } = state;
+
+  const columns: ProColumns<TableListItem>[] = [
+    {
+      title: 'ID',
+      dataIndex: 'id',
+      hideInTable: true,
+    },
+    {
+      title: '院区名称',
+      dataIndex: 'hospName',
+      hideInSearch: false,
+    },
+    {
+      title: '是否主院',
+      dataIndex: 'isHospital',
+      render: (text) => {
+        return text == 0 ? '是' : '否';
+      },
+    },
+    {
+      title: '医院标识',
+      dataIndex: 'hospSign',
+    },
+    {
+      title: '简称',
+      dataIndex: 'hospAbbreviation',
+    },
+    {
+      title: '主院名称',
+      dataIndex: 'mainHospName',
+    },
+    {
+      title: '系统名称',
+      dataIndex: 'systemName',
+    },
+    {
+      title: '是否互通',
+      dataIndex: 'dataShare',
+    },
+    {
+      title: '变更日期',
+      dataIndex: 'updateTime',
+    },
+    {
+      title: '操作',
+      width: 220,
+      key: 'option',
+      valueType: 'option',
+      render: (text, record) => [
+        <a key="link" onClick={() => editHandle(record)}>
+          编辑
+        </a>,
+        <Divider key="1" type="vertical" style={{ margin: '0 3px' }} />,
+        <Popconfirm
+          title="是否确定删除?"
+          onConfirm={() => delHandle(record)}
+          // onCancel={cancel}
+          okText="确定"
+          cancelText="取消"
+          key="link2"
+        >
+          <a>删除</a>
+        </Popconfirm>,
+        <Divider key="2" type="vertical" style={{ margin: '0 3px' }} />,
+        <a key="link3" onClick={() => editMenuBind(record)}>
+          菜单
+        </a>,
+      ],
+    },
+  ];
+
+  const getHospData = async (params: TableRequestParamsType) => {
+    const { current = 1, pageSize = 10, hospName: hospitalName } = params;
+    const resp = await getAllHosp({
+      current,
+      pageSize,
+      hospitalName: hospitalName ? hospitalName : null,
+    });
+
+    if (resp) {
+      const { list = [], totalCount = 0 } = resp;
+      //获取万最新数据取消重置reload
+      dispatch &&
+        dispatch({
+          type: 'hospManageModel/reloadTable',
+          payload: false,
+        });
+
+      return {
+        data: list,
+        success: true,
+        total: resp.totalCount,
+      };
+    } else {
+      return {
+        data: [],
+        success: false,
+        total: 0,
+      };
+    }
+  };
+
+  const addHandle = () => {
+    dispatch &&
+      dispatch({
+        type: 'hospManageModel/add',
+        payload: {
+          tableAct: TableActType.ADD,
+          isShowModal: true,
+        },
+      });
+  };
+
+  const editHandle = (record: TableListItem) => {
+    dispatch &&
+      dispatch({
+        type: 'hospManageModel/edit',
+        payload: {
+          data: record,
+          act: TableActType.EDIT,
+          isShowModal: true,
+        },
+      });
+  };
+
+  const delHandle = (record: TableListItem) => {
+    dispatch &&
+      dispatch({
+        type: 'hospManageModel/delRequest',
+        payload: [record.id],
+      });
+  };
+
+  const editMenuBind = async (record: TableListItem) => {
+    //编辑菜单绑定关系
+    const { selectedMenuIds } = await getSpacifyHospMenu({ hospId: record.id });
+    dispatch &&
+      dispatch({
+        type: 'hospManageModel/edit',
+        payload: {
+          data: { ...record, bindMenuIds: selectedMenuIds },
+          act: TableActType.EDITMENU,
+          isShowModal: true,
+        },
+      });
+  };
+
+  //  console.log({state});
+
+  return (
+    <div className="HospManage">
+      <ActModal {...state} dispatch={dispatch} />
+      <KCTable
+        rowKey="id"
+        headerTitle="查询表格"
+        columns={columns}
+        reload={reloadTable}
+        toolBarRender={() => [
+          <Button key="3" type="primary" onClick={addHandle}>
+            新增院区
+          </Button>,
+        ]}
+        request={(params) => getHospData(params)}
+      />
+    </div>
+  );
+};
+
+export default connect(
+  ({
+    hospManageModel,
+    loading,
+  }: {
+    hospManageModel: hospManageModelState;
+    loading: Loading;
+  }) => {
+    //  console.log({userManageModel});
+    return {
+      hospManageModel,
+      loading: loading.models.hospManageModel,
+    };
+  },
+)(HospManage);

+ 208 - 0
src/pages/platform/setting/hospManage/modals/modal.tsx

@@ -0,0 +1,208 @@
+/*
+ * @Author: your name
+ * @Date: 2022-01-12 17:11:11
+ * @LastEditTime: 2022-01-21 09:31:14
+ * @LastEditors: Please set LastEditors
+ * @Description: 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
+ * @FilePath: /KC-MiddlePlatform/src/pages/platform/setting/userManage/modal.tsx
+ */
+
+import React from 'react';
+import KCModal from '@/components/KCModal';
+import KCProSelect from '@/components/KCProSelect';
+import { hospManageModelState, Dispatch } from 'umi';
+
+import {
+  ProFormText,
+  ProFormRadio,
+  ProFormDependency,
+} from '@ant-design/pro-form';
+import { getHospList } from '@/service/hospList';
+import type { AddUsersDataType } from '@/service/user';
+import { randomString } from '@/utils';
+import { TableActType } from '..';
+import { Form } from 'antd';
+import MenuEditer from '@/pages/platform/components/menuEditer/menu';
+
+interface ActModalProps extends hospManageModelState {
+  dispatch: Dispatch | undefined;
+}
+
+const ActModal: React.FC<ActModalProps> = ({
+  dispatch,
+  isShowModal,
+  tableAct,
+  currentEdit,
+}) => {
+  const onVisibleChangeHandle = (bool: boolean) => {
+    // console.log({bool});
+    if (!bool) {
+      dispatch &&
+        dispatch({
+          type: 'hospManageModel/cancelTableAct',
+        });
+    }
+  };
+
+  const onFinishHandle = (data: any & AddUsersDataType) => {
+    if (tableAct == TableActType.ADD) {
+      dispatch &&
+        dispatch({
+          type: 'hospManageModel/postAddData',
+          payload: data,
+        });
+    }
+
+    if (tableAct == TableActType.EDIT || tableAct == TableActType.EDITMENU) {
+      dispatch &&
+        dispatch({
+          type: 'hospManageModel/postEditData',
+          payload: data,
+        });
+    }
+
+    return true;
+  };
+
+  const setModalTitle = () => {
+    if (tableAct == TableActType.EDIT) {
+      return '编辑院区';
+    }
+    if (tableAct == TableActType.ADD) {
+      return '新增院区';
+    }
+    if (tableAct == TableActType.EDITMENU) {
+      return '绑定菜单';
+    }
+  };
+
+  const setInitialValues = () => {
+    if (tableAct == TableActType.EDIT) {
+      return {
+        ...currentEdit,
+        isDataShare: currentEdit?.dataShare == '是' ? 1 : 0,
+      };
+    }
+    if (tableAct == TableActType.ADD) {
+      return { hospSign: randomString(16), isDataShare: 0 };
+    }
+    if (tableAct == TableActType.EDITMENU) {
+      return { ...currentEdit };
+    }
+  };
+
+  return (
+    <KCModal
+      visible={isShowModal}
+      onVisibleChange={onVisibleChangeHandle}
+      layout="horizontal"
+      width={600}
+      initialValues={setInitialValues()}
+      title={setModalTitle()}
+      labelCol={{
+        span: 4,
+      }}
+      onFinish={async (data) => onFinishHandle(data)}
+    >
+      {tableAct == TableActType.EDITMENU ? (
+        <Form.Item name="bindMenuIds">
+          <MenuEditer />
+        </Form.Item>
+      ) : (
+        <>
+          <ProFormText
+            width="md"
+            name="hospName"
+            label="医院名称"
+            placeholder="请输入"
+            rules={[
+              {
+                required: true,
+                message: '请输入医院名称!',
+              },
+            ]}
+          />
+          <KCProSelect
+            label="是否主院"
+            width="md"
+            name="isHospital"
+            options={[
+              {
+                value: 1,
+                label: '否',
+              },
+              {
+                value: 0,
+                label: '是',
+              },
+            ]}
+            rules={[
+              {
+                required: true,
+                message: '请选择!',
+              },
+            ]}
+          />
+          <ProFormDependency name={['isHospital']}>
+            {({ isHospital }) => {
+              if (isHospital && isHospital == 1) {
+                return (
+                  <KCProSelect
+                    label="选择主院"
+                    width="md"
+                    name="mainHospId"
+                    request={async () => {
+                      const hospList = await getHospList();
+                      if (hospList) {
+                        return hospList.map((t) => ({
+                          label: t.name,
+                          value: t.id,
+                        }));
+                      }
+                      return [];
+                    }}
+                    rules={[
+                      {
+                        required: true,
+                        message: '请选择院区!',
+                      },
+                    ]}
+                  />
+                );
+              }
+            }}
+          </ProFormDependency>
+          <ProFormText width="md" name="hospSign" label="医院标识" disabled />
+          <ProFormText
+            width="md"
+            name="hospAbbreviation"
+            label="简称"
+            placeholder="请输入"
+          />
+          <ProFormText
+            width="md"
+            name="systemName"
+            label="系统名称"
+            placeholder="请输入"
+          />
+          <ProFormRadio.Group
+            name="isDataShare"
+            label="是否数据分享"
+            options={[
+              {
+                label: '是',
+                value: 1,
+              },
+              {
+                label: '否',
+                value: 0,
+              },
+            ]}
+          />
+        </>
+      )}
+    </KCModal>
+  );
+};
+
+export default ActModal;

+ 169 - 0
src/pages/platform/setting/hospManage/model.ts

@@ -0,0 +1,169 @@
+/*
+ * @Author: your name
+ * @Date: 2022-01-12 10:12:55
+ * @LastEditTime: 2022-01-21 09:30:46
+ * @LastEditors: Please set LastEditors
+ * @Description: 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
+ * @FilePath: /KC-MiddlePlatform/src/pages/platform/setting/userManage/model.ts
+ */
+
+import { Effect, ImmerReducer, Reducer, Subscription } from 'umi';
+import { addHosp, delHosp, editHosp, hospBindMenu } from '@/service/hospList';
+import type { getUsersParams } from '@/service/user';
+import { HospTableItem as TableListItem } from '@/service/hospList';
+// import { TableActType } from '.';
+
+enum TableActType {
+  NOACT,
+  ADD,
+  DEL,
+  EDIT,
+  EDITMENU,
+}
+
+export interface hospManageModelState {
+  name: string;
+  tableAct: TableActType;
+  isShowModal: boolean;
+  reloadTable: boolean;
+  currentEdit: TableListItem | undefined;
+}
+
+export interface hospManageModelType {
+  namespace: 'hospManageModel';
+  state: hospManageModelState;
+  effects: {
+    query: Effect;
+    postAddData: Effect;
+    postEditData: Effect;
+    delRequest: Effect;
+  };
+  reducers: {
+    save: Reducer<hospManageModelState>;
+    add: Reducer<hospManageModelState>;
+    cancelTableAct: Reducer<hospManageModelState>;
+    reloadTable: Reducer<hospManageModelState>;
+    edit: Reducer<hospManageModelState>;
+    // 启用 immer 之后
+    // save: ImmerReducer<IndexModelState>;
+  };
+  subscriptions: { setup: Subscription };
+}
+
+const hospManageModel: hospManageModelType = {
+  namespace: 'hospManageModel',
+  state: {
+    name: '',
+    tableAct: TableActType.NOACT,
+    isShowModal: false,
+    reloadTable: false,
+    currentEdit: undefined,
+  },
+
+  effects: {
+    *query({ payload }, { call, put }) {},
+
+    *postAddData({ payload }, { call, put }) {
+      yield addHosp({ mainHospId: 0, ...payload });
+      yield put({
+        type: 'reloadTable',
+        payload: true,
+      });
+    },
+    *postEditData({ payload }, { call, put, select }) {
+      const {
+        tableAct,
+        currentEdit: currentEditOld,
+      }: { tableAct: TableActType; currentEdit: TableListItem } = yield select(
+        ({
+          hospManageModel: state,
+        }: {
+          hospManageModel: hospManageModelState;
+        }) => {
+          return {
+            tableAct: state.tableAct,
+            currentEdit: state.currentEdit,
+          };
+        },
+      );
+
+      if (tableAct == TableActType.EDITMENU) {
+        //绑定菜单
+        yield hospBindMenu({
+          hospId: currentEditOld.id,
+          menuIds: payload.bindMenuIds,
+        }); //menuId集合
+        yield put({
+          type: 'reloadTable',
+          payload: true,
+        });
+      } else {
+        //编辑院区信息
+        yield editHosp({ ...currentEditOld, ...payload });
+
+        yield put({
+          type: 'reloadTable',
+          payload: true,
+        });
+      }
+    },
+    *delRequest({ payload }, { put }) {
+      yield delHosp(payload);
+      yield put({
+        type: 'reloadTable',
+        payload: true,
+      });
+    },
+  },
+  reducers: {
+    reloadTable(state, action) {
+      return {
+        ...state,
+        reloadTable: action.payload,
+      };
+    },
+    save(state, action) {
+      return {
+        ...state,
+        ...action.payload,
+      };
+    },
+    add(state, action) {
+      return {
+        ...state,
+        ...action.payload,
+      };
+    },
+    cancelTableAct(state) {
+      return {
+        ...state,
+        isShowModal: false,
+      };
+    },
+    edit(state, action) {
+      return {
+        ...state,
+        tableAct: action.payload.act,
+        isShowModal: action.payload.isShowModal,
+        currentEdit: action.payload.data,
+      };
+    },
+    // 启用 immer 之后
+    // save(state, action) {
+    //   state.name = action.payload;
+    // },
+  },
+  subscriptions: {
+    setup({ dispatch, history }) {
+      return history.listen(({ pathname }) => {
+        if (pathname === '/') {
+          dispatch({
+            type: 'query',
+          });
+        }
+      });
+    },
+  },
+};
+
+export default hospManageModel;

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

@@ -0,0 +1,3 @@
+.HospManage {
+  height: 100%;
+}

+ 27 - 0
src/pages/platform/setting/hospManage/typings.d.ts

@@ -0,0 +1,27 @@
+/*
+ * @Author: your name
+ * @Date: 2022-01-13 11:10:58
+ * @LastEditTime: 2022-01-13 17:38:05
+ * @LastEditors: Please set LastEditors
+ * @Description: 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
+ * @FilePath: /KC-MiddlePlatform/src/pages/platform/setting/userManage/typings.d.ts
+ */
+
+export type TableListItem = {
+  id: number;
+  hospName: string;
+  hospSign: string;
+  hospAbbreviation: string;
+  mainHospId: number;
+  mainHospName: string;
+  updateTime: string;
+  systemName: string;
+  dataShare: number;
+};
+
+// export enum TableActType {
+//     NOACT,
+//     ADD,
+//     DEL,
+//     EDIT
+// }

+ 251 - 0
src/pages/platform/setting/menuManage/index.tsx

@@ -0,0 +1,251 @@
+/*
+ * @Author: your name
+ * @Date: 2022-01-13 15:22:48
+ * @LastEditTime: 2022-02-09 10:51:13
+ * @LastEditors: Please set LastEditors
+ * @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 { menuManageModelState, ConnectProps, Loading, connect } from 'umi';
+import { Button, Divider, Popconfirm } from 'antd';
+import KCTable from '@/components/kcTable';
+import type { ProColumns } from '@ant-design/pro-table';
+import { getAllMenus } from '@/service/menu';
+import { TableRequestParamsType } from '@/typings';
+import ActModal from './modals/modal';
+import './style.less';
+import { DownOutlined, FileOutlined, RightOutlined } from '@ant-design/icons';
+import type { MenuItemDataType } from '@/service/menu';
+import { MenuTypes } from '@/constant';
+
+export enum TableActType {
+  NOACT,
+  ADD,
+  DEL,
+  EDIT,
+}
+
+interface PageProps extends ConnectProps {
+  menuManageModel: menuManageModelState;
+  loading: boolean;
+}
+
+const MenuManage: FC<PageProps> = ({ menuManageModel: state, dispatch }) => {
+  const { reloadTable } = state;
+
+  const columns: ProColumns<MenuItemDataType>[] = [
+    {
+      title: 'ID',
+      dataIndex: 'menuId',
+      hideInTable: true,
+    },
+    {
+      title: 'parentId',
+      dataIndex: 'parentId',
+      hideInTable: true,
+    },
+    {
+      title: '菜单名称',
+      dataIndex: 'name',
+      hideInSearch: false,
+      render: (text, record) => {
+        if (record.children && record.children.length > 0) {
+          return text;
+        } else {
+          //叶子节点
+          return record.parentId != '0' ? (
+            //非第一级
+            <div>
+              <FileOutlined style={{ marginRight: 10 }} />
+              {text}
+            </div>
+          ) : (
+            text
+          );
+        }
+      },
+    },
+    {
+      title: '类型',
+      dataIndex: 'isHospital',
+      render: (number, record) => {
+        const findType = MenuTypes.filter((t) => t.value == record.type);
+        return findType[0].label;
+      },
+    },
+    {
+      title: 'URL',
+      dataIndex: 'url',
+    },
+    {
+      title: 'Path',
+      dataIndex: 'path',
+    },
+    // {
+    //   title: '变更人',
+    //   dataIndex: 'mainHospName',
+    // },
+    {
+      title: '变更日期',
+      dataIndex: 'modifyTime',
+    },
+    {
+      title: '操作',
+      width: 220,
+      key: 'option',
+      valueType: 'option',
+      render: (text, record) => [
+        <a key="link" onClick={() => editHandle(record)}>
+          编辑
+        </a>,
+        <Divider key="1" type="vertical" style={{ margin: '0 3px' }} />,
+        <Popconfirm
+          title="是否确定删除?"
+          onConfirm={() => delHandle(record)}
+          // onCancel={cancel}
+          okText="确定"
+          cancelText="取消"
+          key="link2"
+        >
+          <a>删除</a>
+        </Popconfirm>,
+        <Divider key="2" type="vertical" style={{ margin: '0 3px' }} />,
+        <a key="link3" onClick={() => addHandle(record.menuId, record)}>
+          添加
+        </a>,
+      ],
+    },
+  ];
+
+  const getData = async (
+    params: { name?: string } & TableRequestParamsType,
+  ) => {
+    const { current = 1, pageSize = 1000, name } = params;
+    const resp = await getAllMenus();
+
+    //获取万最新数据取消重置reload
+    dispatch &&
+      dispatch({
+        type: 'menuManageModel/reloadTable',
+        payload: false,
+      });
+
+    if (name) {
+      //搜索菜单
+      const deeper = (data: MenuItemDataType[], keyword: string) => {
+        let result: MenuItemDataType[] = [];
+
+        data.forEach((t, index) => {
+          if (t.name.includes(keyword)) {
+            result.push({ ...t, children: [] }); //不带出子集
+          } else {
+            if (t.children && t.children.length > 0) {
+              result.push({ ...t, children: deeper(t.children, keyword) });
+            }
+          }
+        });
+        return result;
+      };
+      return {
+        data: resp.list ? deeper(resp.list, name) : [],
+        success: true,
+        total: resp.totalCount,
+      };
+    }
+
+    return {
+      data: resp.list ? resp.list : [],
+      success: true,
+      total: resp.totalCount,
+    };
+  };
+
+  const addHandle = (parentId?: string, record?: MenuItemDataType) => {
+    dispatch &&
+      dispatch({
+        type: 'menuManageModel/add',
+        payload: {
+          tableAct: TableActType.ADD,
+          isShowModal: true,
+          parentId: parentId ? parentId : 0, //考虑新增子集菜单的情况
+          record: record ? record : null,
+        },
+      });
+  };
+
+  const editHandle = (record: MenuItemDataType) => {
+    dispatch &&
+      dispatch({
+        type: 'menuManageModel/edit',
+        payload: {
+          data: record,
+          act: TableActType.EDIT,
+          isShowModal: true,
+        },
+      });
+  };
+
+  const delHandle = (record: MenuItemDataType) => {
+    dispatch &&
+      dispatch({
+        type: 'menuManageModel/delRequest',
+        payload: [record.menuId],
+      });
+  };
+
+  //  console.log({state});
+
+  return (
+    <div className="MenuManage">
+      <ActModal {...state} dispatch={dispatch} />
+      <KCTable
+        rowKey="menuId"
+        headerTitle="查询表格"
+        columns={columns}
+        reload={reloadTable}
+        toolBarRender={() => [
+          <Button key="3" type="primary" onClick={() => addHandle()}>
+            新增主菜单
+          </Button>,
+        ]}
+        request={(params) => getData(params)}
+        expandable={{
+          expandIcon: ({ expanded, onExpand, record }) => {
+            // console.log({expanded, onExpand, record});
+            return expanded
+              ? record.children.length > 0 && (
+                  <DownOutlined
+                    onClick={(e) => onExpand(record, e)}
+                    style={{ marginRight: 10 }}
+                  />
+                )
+              : record.children.length > 0 && (
+                  <RightOutlined
+                    onClick={(e) => onExpand(record, e)}
+                    style={{ marginRight: 10 }}
+                  />
+                );
+          },
+        }}
+      />
+    </div>
+  );
+};
+
+export default connect(
+  ({
+    menuManageModel,
+    loading,
+  }: {
+    menuManageModel: menuManageModelState;
+    loading: Loading;
+  }) => {
+    //  console.log({userManageModel});
+    return {
+      menuManageModel,
+      loading: loading.models.menuManageModel,
+    };
+  },
+)(MenuManage);

+ 17 - 0
src/pages/platform/setting/menuManage/modals/menu.tsx

@@ -0,0 +1,17 @@
+/*
+ * @Author: your name
+ * @Date: 2022-01-14 14:03:42
+ * @LastEditTime: 2022-01-14 14:03:42
+ * @LastEditors: Please set LastEditors
+ * @Description: 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
+ * @FilePath: /KC-MiddlePlatform/src/pages/platform/setting/hospManage/modals/menu.tsx
+ */
+
+import React from 'react';
+import KCTable from '@/components/kcTable';
+
+export interface MenuEditerType {}
+
+const MenuEditer: React.FC<MenuEditerType> = () => {
+  return <KCTable columns={[]} />;
+};

+ 166 - 0
src/pages/platform/setting/menuManage/modals/modal.tsx

@@ -0,0 +1,166 @@
+/*
+ * @Author: your name
+ * @Date: 2022-01-12 17:11:11
+ * @LastEditTime: 2022-02-09 10:56:58
+ * @LastEditors: Please set LastEditors
+ * @Description: 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
+ * @FilePath: /KC-MiddlePlatform/src/pages/platform/setting/userManage/modal.tsx
+ */
+
+import React from 'react';
+import KCModal from '@/components/KCModal';
+import KCProSelect from '@/components/KCProSelect';
+import { menuManageModelState, Dispatch } from 'umi';
+
+import {
+  ProFormText,
+  ProFormRadio,
+  ProFormDependency,
+} from '@ant-design/pro-form';
+import { getHospList } from '@/service/hospList';
+import type { AddUsersDataType } from '@/service/user';
+import { randomString } from '@/utils';
+import { MenuTypes } from '@/constant';
+// import { TableActType } from "./typings";
+
+export enum TableActType { //表格操作类型枚举
+  NOACT,
+  ADD,
+  DEL,
+  EDIT,
+}
+
+interface ActModalProps extends menuManageModelState {
+  dispatch: Dispatch | undefined;
+}
+
+const ActModal: React.FC<ActModalProps> = ({
+  dispatch,
+  isShowModal,
+  tableAct,
+  currentEdit,
+  parentId,
+  record,
+}) => {
+  const onVisibleChangeHandle = (bool: boolean) => {
+    // console.log({bool});
+    if (!bool) {
+      dispatch &&
+        dispatch({
+          type: 'menuManageModel/cancelTableAct',
+        });
+    }
+  };
+
+  const onFinishHandle = (data: any & AddUsersDataType) => {
+    if (tableAct == TableActType.ADD) {
+      dispatch &&
+        dispatch({
+          type: 'menuManageModel/postAddData',
+          payload: data,
+        });
+    }
+
+    if (tableAct == TableActType.EDIT) {
+      dispatch &&
+        dispatch({
+          type: 'menuManageModel/postEditData',
+          payload: data,
+        });
+    }
+
+    return true;
+  };
+
+  const menuTypeOptionFiler = () => {
+    if (parentId == '0') {
+      //顶级目录
+      return MenuTypes.filter((t) => t.label == '系统');
+    }
+    if (record?.type == 3) {
+      //子系统
+      return MenuTypes.filter((t) => t.label == '菜单' || t.label == '目录');
+    }
+    if (record?.type == 1) {
+      //子系统菜单目录
+      return MenuTypes.filter((t) => t.label == '菜单');
+    }
+    return MenuTypes;
+  };
+
+  return (
+    <KCModal
+      visible={isShowModal}
+      onVisibleChange={onVisibleChangeHandle}
+      layout="horizontal"
+      width={600}
+      initialValues={
+        tableAct == TableActType.EDIT
+          ? { ...currentEdit }
+          : { hospSign: randomString(16), isDataShare: 0 }
+      }
+      title={tableAct == TableActType.EDIT ? '编辑菜单' : '新增菜单'}
+      labelCol={{
+        span: 4,
+      }}
+      onFinish={async (data) => onFinishHandle(data)}
+    >
+      <ProFormText
+        width="md"
+        name="name"
+        label="菜单名称"
+        placeholder="请输入"
+        rules={[
+          {
+            required: true,
+            message: '请输入菜单名称!',
+          },
+        ]}
+      />
+      <KCProSelect
+        label="类型"
+        width="md"
+        name="type"
+        options={menuTypeOptionFiler()}
+        rules={[
+          {
+            required: true,
+            message: '请选择!',
+          },
+        ]}
+      />
+      <ProFormText
+        width="md"
+        name="url"
+        label="URL"
+        placeholder="请输入"
+        rules={[
+          {
+            required: true,
+            message: '请输入!',
+          },
+        ]}
+      />
+      <ProFormText
+        width="md"
+        name="path"
+        label="PATH"
+        placeholder="请输入"
+        rules={[
+          {
+            required: true,
+            message: '请输入!',
+          },
+        ]}
+      />
+      <ProFormText
+        width="md"
+        name="icon"
+        label="菜单icon"
+        placeholder="请输入"
+      />
+    </KCModal>
+  );
+};
+
+export default ActModal;

+ 160 - 0
src/pages/platform/setting/menuManage/model.ts

@@ -0,0 +1,160 @@
+/*
+ * @Author: your name
+ * @Date: 2022-01-12 10:12:55
+ * @LastEditTime: 2022-02-09 10:45:10
+ * @LastEditors: Please set LastEditors
+ * @Description: 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
+ * @FilePath: /KC-MiddlePlatform/src/pages/platform/setting/userManage/model.ts
+ */
+
+import { Effect, ImmerReducer, Reducer, Subscription } from 'umi';
+
+import { addMenu, editMenu, delMenu } from '@/service/menu';
+
+import type { MenuItemDataType } from '@/service/menu';
+
+export enum TableActType {
+  NOACT,
+  ADD,
+  DEL,
+  EDIT,
+}
+
+export interface menuManageModelState {
+  name: string;
+  tableAct: TableActType;
+  isShowModal: boolean;
+  reloadTable: boolean;
+  currentEdit: MenuItemDataType | undefined;
+  parentId: string | undefined;
+  record: MenuItemDataType | undefined;
+}
+
+export interface menuManageModelType {
+  namespace: 'menuManageModel';
+  state: menuManageModelState;
+  effects: {
+    query: Effect;
+    postAddData: Effect;
+    postEditData: Effect;
+    delRequest: Effect;
+  };
+  reducers: {
+    add: Reducer<menuManageModelState>;
+    cancelTableAct: Reducer<menuManageModelState>;
+    reloadTable: Reducer<menuManageModelState>;
+    edit: Reducer<menuManageModelState>;
+    // 启用 immer 之后
+    // save: ImmerReducer<IndexModelState>;
+  };
+  subscriptions: { setup: Subscription };
+}
+
+const menuManageModel: menuManageModelType = {
+  namespace: 'menuManageModel',
+  state: {
+    name: '',
+    tableAct: TableActType.NOACT,
+    isShowModal: false,
+    reloadTable: false,
+    currentEdit: undefined,
+    parentId: undefined,
+    record: undefined, //当前操作基于项的数据
+  },
+
+  effects: {
+    *query({ payload }, { call, put }) {},
+    *postAddData({ payload }, { call, put, select }) {
+      const parentId: string = yield select(
+        ({
+          menuManageModel: state,
+        }: {
+          menuManageModel: menuManageModelState;
+        }) => {
+          return state.parentId;
+        },
+      );
+      yield addMenu({ parentId: parentId, ...payload });
+      yield put({
+        type: 'reloadTable',
+        payload: true,
+      });
+    },
+    *postEditData({ payload }, { call, put, select }) {
+      const currentEditOld: MenuItemDataType = yield select(
+        ({
+          menuManageModel: state,
+        }: {
+          menuManageModel: menuManageModelState;
+        }) => {
+          return state.currentEdit;
+        },
+      );
+
+      const _currentEditOld = JSON.parse(
+        JSON.stringify({
+          ...currentEditOld,
+          id: currentEditOld.menuId,
+          menuId: undefined,
+        }),
+      );
+      yield editMenu({ ..._currentEditOld, ...payload });
+      yield put({
+        type: 'reloadTable',
+        payload: true,
+      });
+    },
+    *delRequest({ payload }, { put }) {
+      yield delMenu(payload);
+      yield put({
+        type: 'reloadTable',
+        payload: true,
+      });
+    },
+  },
+  reducers: {
+    reloadTable(state, action) {
+      return {
+        ...state,
+        reloadTable: action.payload,
+      };
+    },
+    add(state, action) {
+      return {
+        ...state,
+        ...action.payload,
+      };
+    },
+    cancelTableAct(state) {
+      return {
+        ...state,
+        isShowModal: false,
+      };
+    },
+    edit(state, action) {
+      return {
+        ...state,
+        tableAct: action.payload.act,
+        isShowModal: action.payload.isShowModal,
+        currentEdit: action.payload.data,
+      };
+    },
+    // 启用 immer 之后
+    // save(state, action) {
+    //   state.name = action.payload;
+    // },
+  },
+  subscriptions: {
+    setup({ dispatch, history }) {
+      return history.listen(({ pathname }) => {
+        if (pathname === '/') {
+          dispatch({
+            type: 'query',
+          });
+        }
+      });
+    },
+  },
+};
+
+export default menuManageModel;

+ 3 - 0
src/pages/platform/setting/menuManage/style.less

@@ -0,0 +1,3 @@
+.HospManage {
+  height: 100%;
+}

+ 27 - 0
src/pages/platform/setting/menuManage/typings.d.ts

@@ -0,0 +1,27 @@
+/*
+ * @Author: your name
+ * @Date: 2022-01-13 11:10:58
+ * @LastEditTime: 2022-01-13 17:38:05
+ * @LastEditors: Please set LastEditors
+ * @Description: 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
+ * @FilePath: /KC-MiddlePlatform/src/pages/platform/setting/userManage/typings.d.ts
+ */
+
+export type TableListItem = {
+  id: number;
+  hospName: string;
+  hospSign: string;
+  hospAbbreviation: string;
+  mainHospId: number;
+  mainHospName: string;
+  updateTime: string;
+  systemName: string;
+  dataShare: number;
+};
+
+// export enum TableActType {
+//     NOACT,
+//     ADD,
+//     DEL,
+//     EDIT
+// }

+ 220 - 0
src/pages/platform/setting/roleManage/index.tsx

@@ -0,0 +1,220 @@
+/*
+ * @Author: your name
+ * @Date: 2022-01-13 15:22:48
+ * @LastEditTime: 2022-01-20 10:46:26
+ * @LastEditors: Please set LastEditors
+ * @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 { roleManageModelState, ConnectProps, Loading, connect } from 'umi';
+import { Button, Divider, Popconfirm } from 'antd';
+import KCTable from '@/components/kcTable';
+import type { ProColumns } from '@ant-design/pro-table';
+import {
+  getAllRoles,
+  getRoleHasBindMenus,
+  getRoleHasBindUsers,
+} from '@/service/role';
+import { TableRequestParamsType } from '@/typings';
+import ActModal from './modals/modal';
+import './style.less';
+import type { RoleItemType as TableListItem } from '@/service/role';
+
+export enum TableActType {
+  NOACT,
+  ADD,
+  DEL,
+  EDIT,
+  EDITMENU, //编辑菜单绑定
+  EDITRELAUSER, //编辑人员绑定
+}
+
+interface PageProps extends ConnectProps {
+  roleManageModel: roleManageModelState;
+  loading: boolean;
+}
+
+const RoleManage: FC<PageProps> = ({ roleManageModel: state, dispatch }) => {
+  const { reloadTable } = state;
+
+  const columns: ProColumns<TableListItem>[] = [
+    {
+      title: 'ID',
+      dataIndex: 'roleId',
+      hideInTable: true,
+    },
+    {
+      title: '院区名称',
+      dataIndex: 'hospName',
+    },
+    {
+      title: '角色名称',
+      dataIndex: 'roleName',
+      hideInSearch: false,
+    },
+    {
+      title: '备注',
+      dataIndex: 'remark',
+    },
+    {
+      title: '变更人',
+      dataIndex: 'hospAbbreviation',
+    },
+    {
+      title: '变更日期',
+      dataIndex: 'modifyTime',
+    },
+    {
+      title: '操作',
+      width: 240,
+      key: 'option',
+      valueType: 'option',
+      render: (text, record) => [
+        <a key="link4" onClick={() => editUserBind(record)}>
+          用户
+        </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>,
+        <Divider key="1" type="vertical" style={{ margin: '0 1px' }} />,
+        <Popconfirm
+          title="是否确定删除?"
+          onConfirm={() => delHandle(record)}
+          // onCancel={cancel}
+          okText="确定"
+          cancelText="取消"
+          key="link2"
+        >
+          <a>删除</a>
+        </Popconfirm>,
+      ],
+    },
+  ];
+
+  const getData = async (params: TableRequestParamsType) => {
+    const { current = 1, pageSize = 10, roleName } = params;
+    const resp = await getAllRoles({
+      current,
+      pageSize,
+      roleName: roleName ? roleName : null,
+    });
+
+    //获取万最新数据取消重置reload
+    dispatch &&
+      dispatch({
+        type: 'roleManageModel/reloadTable',
+        payload: false,
+      });
+
+    return {
+      data: resp.list ? resp.list : [],
+      success: true,
+      total: resp.totalCount,
+    };
+  };
+
+  const addHandle = () => {
+    dispatch &&
+      dispatch({
+        type: 'roleManageModel/addEffect',
+      });
+  };
+
+  const editHandle = (record: TableListItem) => {
+    dispatch &&
+      dispatch({
+        type: 'roleManageModel/edit',
+        payload: {
+          data: record,
+          act: TableActType.EDIT,
+          isShowModal: true,
+        },
+      });
+  };
+
+  const delHandle = (record: TableListItem) => {
+    dispatch &&
+      dispatch({
+        type: 'roleManageModel/delRequest',
+        payload: [record.roleId],
+      });
+  };
+
+  const editMenuBind = async (record: TableListItem) => {
+    //编辑菜单绑定关系
+    const { menuIds = [] } = await getRoleHasBindMenus({
+      hospId: record.hospId,
+      roleId: record.roleId,
+    });
+    dispatch &&
+      dispatch({
+        type: 'roleManageModel/edit',
+        payload: {
+          data: { ...record, bindMenuIds: menuIds },
+          act: TableActType.EDITMENU,
+          isShowModal: true,
+        },
+      });
+  };
+
+  const editUserBind = async (record: TableListItem) => {
+    //编辑角色与人员的绑定关系
+    const { userIds = [] } = await getRoleHasBindUsers({
+      hospId: record.hospId,
+      roleId: record.roleId,
+    });
+    dispatch &&
+      dispatch({
+        type: 'roleManageModel/edit',
+        payload: {
+          data: { ...record, bindUserIds: userIds },
+          act: TableActType.EDITRELAUSER,
+          isShowModal: true,
+        },
+      });
+  };
+
+  //  console.log({state});
+
+  return (
+    <div className="RoleManage">
+      <ActModal {...state} dispatch={dispatch} />
+
+      <KCTable
+        rowKey="roleId"
+        headerTitle="查询表格"
+        columns={columns}
+        reload={reloadTable}
+        toolBarRender={() => [
+          <Button key="3" type="primary" onClick={addHandle}>
+            新增角色
+          </Button>,
+        ]}
+        request={(params) => getData(params)}
+      />
+    </div>
+  );
+};
+
+export default connect(
+  ({
+    roleManageModel,
+    loading,
+  }: {
+    roleManageModel: roleManageModelState;
+    loading: Loading;
+  }) => {
+    //  console.log({userManageModel});
+    return {
+      roleManageModel,
+      loading: loading.models.roleManageModel,
+    };
+  },
+)(RoleManage);

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

@@ -0,0 +1,167 @@
+/*
+ * @Author: your name
+ * @Date: 2022-01-12 17:11:11
+ * @LastEditTime: 2022-02-09 10:09:22
+ * @LastEditors: Please set LastEditors
+ * @Description: 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
+ * @FilePath: /KC-MiddlePlatform/src/pages/platform/setting/userManage/modal.tsx
+ */
+
+import React from 'react';
+import KCModal from '@/components/KCModal';
+import KCProSelect from '@/components/KCProSelect';
+import { roleManageModelState, Dispatch } from 'umi';
+
+import { ProFormText, ProFormTextArea } from '@ant-design/pro-form';
+import { getHospList } from '@/service/hospList';
+import type { AddUsersDataType } from '@/service/user';
+
+import { TableActType } from '..';
+import { Form } from 'antd';
+import MenuEditer from '@/pages/platform/components/menuEditer/menu';
+import UserEditer from '@/pages/platform/components/usersEditer';
+
+interface ActModalProps extends roleManageModelState {
+  dispatch: Dispatch | undefined;
+}
+
+const ActModal: React.FC<ActModalProps> = ({
+  dispatch,
+  isShowModal,
+  tableAct,
+  currentEdit,
+  hospId,
+}) => {
+  const onVisibleChangeHandle = (bool: boolean) => {
+    // console.log({bool});
+    if (!bool) {
+      dispatch &&
+        dispatch({
+          type: 'roleManageModel/cancelTableAct',
+        });
+    }
+  };
+
+  const onFinishHandle = (data: any & AddUsersDataType) => {
+    if (tableAct == TableActType.ADD) {
+      dispatch &&
+        dispatch({
+          type: 'roleManageModel/postAddData',
+          payload: data,
+        });
+    }
+
+    if (
+      tableAct == TableActType.EDIT ||
+      tableAct == TableActType.EDITMENU ||
+      tableAct == TableActType.EDITRELAUSER
+    ) {
+      dispatch &&
+        dispatch({
+          type: 'roleManageModel/postEditData',
+          payload: data,
+        });
+    }
+
+    return true;
+  };
+
+  const setModalTitle = () => {
+    if (tableAct == TableActType.EDIT) {
+      return '编辑角色';
+    }
+    if (tableAct == TableActType.ADD) {
+      return '新增角色';
+    }
+    if (tableAct == TableActType.EDITMENU) {
+      return '绑定菜单';
+    }
+    if (tableAct == TableActType.EDITRELAUSER) {
+      return '绑定人员';
+    }
+  };
+
+  const setInitialValues = () => {
+    if (tableAct == TableActType.EDIT) {
+      return { ...currentEdit };
+    }
+    if (tableAct == TableActType.ADD) {
+      return { hospId: hospId };
+    }
+    if (
+      tableAct == TableActType.EDITMENU ||
+      tableAct == TableActType.EDITRELAUSER
+    ) {
+      return { ...currentEdit };
+    }
+  };
+
+  return (
+    <KCModal
+      visible={isShowModal}
+      onVisibleChange={onVisibleChangeHandle}
+      layout="horizontal"
+      width={600}
+      initialValues={setInitialValues()}
+      title={setModalTitle()}
+      labelCol={{
+        span: 4,
+      }}
+      onFinish={async (data) => onFinishHandle(data)}
+    >
+      {tableAct == TableActType.EDITMENU && (
+        <Form.Item name="bindMenuIds">
+          <MenuEditer noAction={true} />
+        </Form.Item>
+      )}
+
+      {tableAct == TableActType.EDITRELAUSER && (
+        <Form.Item name="bindUserIds">
+          <UserEditer noAction={true} />
+        </Form.Item>
+      )}
+
+      {(tableAct == TableActType.ADD || tableAct == TableActType.EDIT) && (
+        <>
+          <KCProSelect
+            label="选择院区"
+            width="md"
+            name="hospId"
+            disabled
+            request={async () => {
+              const hospList = await getHospList();
+              if (hospList) {
+                return hospList.map((t) => ({
+                  label: t.name,
+                  value: t.id,
+                }));
+              }
+              return [];
+            }}
+          />
+          <ProFormText
+            width="md"
+            name="roleName"
+            label="角色名称"
+            placeholder="请输入"
+            rules={[
+              {
+                required: true,
+                message: '请输入医院名称!',
+              },
+            ]}
+          />
+          <ProFormTextArea
+            name="remark"
+            label="备注"
+            placeholder="请输入名称"
+            width="md"
+            fieldProps={{}}
+          />
+        </>
+      )}
+    </KCModal>
+  );
+};
+
+export default ActModal;

+ 204 - 0
src/pages/platform/setting/roleManage/model.ts

@@ -0,0 +1,204 @@
+/*
+ * @Author: your name
+ * @Date: 2022-01-12 10:12:55
+ * @LastEditTime: 2022-01-19 17:28:09
+ * @LastEditors: Please set LastEditors
+ * @Description: 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
+ * @FilePath: /KC-MiddlePlatform/src/pages/platform/setting/userManage/model.ts
+ */
+
+import { Effect, ImmerReducer, Reducer, Subscription } from 'umi';
+import {
+  addRoles,
+  delRoles,
+  editRoles,
+  roleBindMenus,
+  roleBindUsers,
+} from '@/service/role';
+import { getMainHosp } from '@/service/hospList';
+import type { RoleItemType as TableListItem } from '@/service/role';
+
+enum TableActType {
+  NOACT,
+  ADD,
+  DEL,
+  EDIT,
+  EDITMENU, //编辑菜单绑定
+  EDITRELAUSER, //编辑人员绑定
+}
+
+export interface roleManageModelState {
+  name: string;
+  tableAct: TableActType;
+  isShowModal: boolean;
+  reloadTable: boolean;
+  currentEdit: TableListItem | undefined;
+  hospId: number | undefined;
+}
+
+export interface roleManageModelType {
+  namespace: 'roleManageModel';
+  state: roleManageModelState;
+  effects: {
+    query: Effect;
+    postAddData: Effect;
+    postEditData: Effect;
+    delRequest: Effect;
+    getMainHospHandle: Effect;
+    addEffect: Effect;
+  };
+  reducers: {
+    save: Reducer<roleManageModelState>;
+    add: Reducer<roleManageModelState>;
+    cancelTableAct: Reducer<roleManageModelState>;
+    reloadTable: Reducer<roleManageModelState>;
+    edit: Reducer<roleManageModelState>;
+    // 启用 immer 之后
+    // save: ImmerReducer<IndexModelState>;
+  };
+  subscriptions: { setup: Subscription };
+}
+
+const roleManageModel: roleManageModelType = {
+  namespace: 'roleManageModel',
+  state: {
+    name: '',
+    tableAct: TableActType.NOACT,
+    isShowModal: false,
+    reloadTable: false,
+    currentEdit: undefined,
+    hospId: undefined,
+  },
+
+  effects: {
+    *query({ payload }, { call, put }) {},
+
+    *postAddData({ payload }, { call, put }) {
+      yield addRoles({ ...payload });
+      yield put({
+        type: 'reloadTable',
+        payload: true,
+      });
+    },
+    *postEditData({ payload }, { call, put, select }) {
+      const {
+        tableAct,
+        currentEdit: currentEditOld,
+      }: { tableAct: TableActType; currentEdit: TableListItem } = yield select(
+        ({
+          roleManageModel: state,
+        }: {
+          roleManageModel: roleManageModelState;
+        }) => {
+          return {
+            tableAct: state.tableAct,
+            currentEdit: state.currentEdit,
+          };
+        },
+      );
+
+      if (tableAct == TableActType.EDITMENU) {
+        //角色绑定菜单
+        yield roleBindMenus({
+          hospId: Number(currentEditOld.hospId),
+          menuIds: payload.bindMenuIds,
+          roleId: Number(currentEditOld.roleId),
+        }); //menuId集合
+        yield put({
+          type: 'reloadTable',
+          payload: true,
+        });
+      } else if (tableAct == TableActType.EDITRELAUSER) {
+        //绑定角色和人员
+        yield roleBindUsers({
+          hospId: Number(currentEditOld.hospId),
+          userIds: payload.bindUserIds,
+          roleId: Number(currentEditOld.roleId),
+        }); //menuId集合
+        yield put({
+          type: 'reloadTable',
+          payload: true,
+        });
+      } else {
+        //编辑角色信息
+        yield editRoles({ ...currentEditOld, ...payload });
+
+        yield put({
+          type: 'reloadTable',
+          payload: true,
+        });
+      }
+    },
+    *delRequest({ payload }, { put }) {
+      yield delRoles(payload);
+      yield put({
+        type: 'reloadTable',
+        payload: true,
+      });
+    },
+    *getMainHospHandle() {
+      const { id } = yield getMainHosp();
+      return id;
+    },
+    *addEffect({ payload }, { put }) {
+      const { id } = yield getMainHosp();
+      yield put({
+        type: 'add',
+        payload: { hospId: id },
+      });
+    },
+  },
+  reducers: {
+    reloadTable(state, action) {
+      return {
+        ...state,
+        reloadTable: action.payload,
+      };
+    },
+    save(state, action) {
+      return {
+        ...state,
+        ...action.payload,
+      };
+    },
+    add(state, action) {
+      return {
+        ...state,
+        tableAct: TableActType.ADD,
+        isShowModal: true,
+        ...action.payload,
+      };
+    },
+    cancelTableAct(state) {
+      return {
+        ...state,
+        isShowModal: false,
+      };
+    },
+    edit(state, action) {
+      return {
+        ...state,
+        tableAct: action.payload.act,
+        isShowModal: action.payload.isShowModal,
+        currentEdit: action.payload.data,
+      };
+    },
+    // 启用 immer 之后
+    // save(state, action) {
+    //   state.name = action.payload;
+    // },
+  },
+  subscriptions: {
+    setup({ dispatch, history }) {
+      return history.listen(({ pathname }) => {
+        if (pathname === '/') {
+          dispatch({
+            type: 'query',
+          });
+        }
+      });
+    },
+  },
+};
+
+export default roleManageModel;

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

@@ -0,0 +1,3 @@
+.HospManage {
+  height: 100%;
+}

+ 27 - 0
src/pages/platform/setting/roleManage/typings.d.ts

@@ -0,0 +1,27 @@
+/*
+ * @Author: your name
+ * @Date: 2022-01-13 11:10:58
+ * @LastEditTime: 2022-01-13 17:38:05
+ * @LastEditors: Please set LastEditors
+ * @Description: 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
+ * @FilePath: /KC-MiddlePlatform/src/pages/platform/setting/userManage/typings.d.ts
+ */
+
+export type TableListItem = {
+  id: number;
+  hospName: string;
+  hospSign: string;
+  hospAbbreviation: string;
+  mainHospId: number;
+  mainHospName: string;
+  updateTime: string;
+  systemName: string;
+  dataShare: number;
+};
+
+// export enum TableActType {
+//     NOACT,
+//     ADD,
+//     DEL,
+//     EDIT
+// }

+ 233 - 0
src/pages/platform/setting/userManage/index.tsx

@@ -0,0 +1,233 @@
+/*
+ * @Author: your name
+ * @Date: 2022-01-11 09:43:18
+ * @LastEditTime: 2022-02-10 09:03:49
+ * @LastEditors: Please set LastEditors
+ * @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 { userManageModelState, ConnectProps, Loading, connect } from 'umi';
+import { Button, Divider, Popconfirm } from 'antd';
+import KCTable from '@/components/kcTable';
+import type { ProColumns } from '@ant-design/pro-table';
+import { getUsers } from '@/service/user';
+import { TableRequestParamsType } from '@/typings';
+import ActModal from './modal';
+import './style.less';
+import { TableListItem } from './typings';
+import { ProFormUploadButton } from '@ant-design/pro-form';
+import KCUpload from '@/components/KCUpload';
+import { RcFile } from 'antd/lib/upload/interface';
+
+export enum TableActType {
+  NOACT,
+  ADD,
+  DEL,
+  EDIT,
+}
+
+interface PageProps extends ConnectProps {
+  userManageModel: userManageModelState;
+  loading: boolean;
+}
+
+const UserManage: FC<PageProps> = ({ userManageModel: state, dispatch }) => {
+  const { reloadTable } = state;
+
+  const columns: ProColumns<TableListItem>[] = [
+    {
+      title: '院区',
+      dataIndex: 'hospName',
+    },
+    {
+      title: '姓名',
+      dataIndex: 'name',
+      hideInSearch: false,
+    },
+    {
+      title: '工号',
+      dataIndex: 'account',
+      hideInSearch: false,
+    },
+    {
+      title: '电话',
+      dataIndex: 'phoneNum',
+    },
+    {
+      title: '身份证号',
+      dataIndex: 'idCardNum',
+    },
+    {
+      title: '在职状态',
+      dataIndex: 'isOnService',
+      valueEnum: {
+        1: { text: '否', status: 'Default' },
+        0: { text: '是', status: 'Success' },
+      },
+    },
+    {
+      title: '操作',
+      width: 220,
+      key: 'option',
+      valueType: 'option',
+      render: (text, record) => [
+        <a key="link" onClick={() => editHandle(record)}>
+          编辑
+        </a>,
+        <Divider key="1" type="vertical" style={{ margin: '0 3px' }} />,
+        <Popconfirm
+          title="是否确定删除?"
+          onConfirm={() => delHandle(record)}
+          // onCancel={cancel}
+          okText="确定"
+          cancelText="取消"
+          key="link2"
+        >
+          <a>删除</a>
+        </Popconfirm>,
+        <Divider key="2" type="vertical" style={{ margin: '0 3px' }} />,
+        <a key="link3" onClick={() => resetUserPaswdHandle(record)}>
+          重置密码
+        </a>,
+      ],
+    },
+  ];
+
+  const getUserData = async (params: TableRequestParamsType) => {
+    const {
+      current = 1,
+      pageSize = 10,
+      name = undefined,
+      account = undefined,
+    } = params;
+
+    const resp = await getUsers({
+      current,
+      pageSize,
+      keyword: name ? name : account,
+    });
+
+    //获取万最新数据取消重置reload
+    dispatch &&
+      dispatch({
+        type: 'userManageModel/reloadTable',
+        payload: false,
+      });
+
+    return {
+      data: resp.list ? resp.list : [],
+      success: true,
+      total: resp.totalCount,
+    };
+  };
+
+  const addUserHandle = () => {
+    dispatch &&
+      dispatch({
+        type: 'userManageModel/addUser',
+        payload: {
+          tableAct: TableActType.ADD,
+          isShowModal: true,
+        },
+      });
+  };
+
+  const editHandle = (record: TableListItem) => {
+    dispatch &&
+      dispatch({
+        type: 'userManageModel/editUser',
+        payload: {
+          userData: record,
+          act: TableActType.EDIT,
+          isShowModal: true,
+        },
+      });
+  };
+
+  const delHandle = (record: TableListItem) => {
+    dispatch &&
+      dispatch({
+        type: 'userManageModel/delUsersRequest',
+        payload: [record.id],
+      });
+  };
+
+  const getUserTemplateHandle = () => {
+    dispatch &&
+      dispatch({
+        type: 'userManageModel/getUserTemplateReq',
+      });
+  };
+
+  const resetUserPaswdHandle = (record: TableListItem) => {
+    dispatch &&
+      dispatch({
+        type: 'userManageModel/resetUserPwd',
+        payload: {
+          id: record.id,
+        },
+      });
+  };
+
+  const importUserHandle = (file: RcFile | undefined) => {
+    if (file) {
+      dispatch &&
+        dispatch({
+          type: 'userManageModel/importUsers',
+          payload: file,
+        });
+    }
+  };
+
+  //  console.log({state});
+
+  return (
+    <div className="UserManage">
+      <ActModal {...state} dispatch={dispatch} />
+      <KCTable
+        rowKey="id"
+        headerTitle="查询表格"
+        columns={columns}
+        reload={reloadTable}
+        toolBarRender={() => [
+          <KCUpload
+            title="导入数据"
+            fieldProps={{
+              onChange: ({ file, fileList }) => {
+                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>
+  );
+};
+
+export default connect(
+  ({
+    userManageModel,
+    loading,
+  }: {
+    userManageModel: userManageModelState;
+    loading: Loading;
+  }) => {
+    //  console.log({userManageModel});
+    return {
+      userManageModel,
+      loading: loading.models.userManageModel,
+    };
+  },
+)(UserManage);

+ 159 - 0
src/pages/platform/setting/userManage/modal.tsx

@@ -0,0 +1,159 @@
+/*
+ * @Author: your name
+ * @Date: 2022-01-12 17:11:11
+ * @LastEditTime: 2022-01-13 16:42:17
+ * @LastEditors: Please set LastEditors
+ * @Description: 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
+ * @FilePath: /KC-MiddlePlatform/src/pages/platform/setting/userManage/modal.tsx
+ */
+
+import React from 'react';
+import KCModal from '@/components/KCModal';
+import KCProSelect from '@/components/KCProSelect';
+import { userManageModelState, Dispatch } from 'umi';
+import { ProFormText } from '@ant-design/pro-form';
+import { getHospList } from '@/service/hospList';
+import type { AddUsersDataType } from '@/service/user';
+// import { TableActType } from "./typings";
+
+export enum TableActType {
+  NOACT,
+  ADD,
+  DEL,
+  EDIT,
+}
+
+interface ActModalProps extends userManageModelState {
+  dispatch: Dispatch | undefined;
+}
+
+const ActModal: React.FC<ActModalProps> = ({
+  dispatch,
+  isShowModal,
+  tableAct,
+  currentEditUser,
+}) => {
+  const onVisibleChangeHandle = (bool: boolean) => {
+    // console.log({bool});
+    if (!bool) {
+      dispatch &&
+        dispatch({
+          type: 'userManageModel/cancelTableAct',
+        });
+    }
+  };
+
+  const onFinishHandle = (data: any & AddUsersDataType) => {
+    if (tableAct == TableActType.ADD) {
+      dispatch &&
+        dispatch({
+          type: 'userManageModel/postAddUserData',
+          payload: data,
+        });
+    }
+
+    if (tableAct == TableActType.EDIT) {
+      dispatch &&
+        dispatch({
+          type: 'userManageModel/postEditUserData',
+          payload: data,
+        });
+    }
+
+    return true;
+  };
+
+  // console.log({isShowModal,tableAct,currentEditUser})
+
+  return (
+    <KCModal
+      visible={isShowModal}
+      onVisibleChange={onVisibleChangeHandle}
+      layout="horizontal"
+      initialValues={
+        tableAct == TableActType.EDIT ? { ...currentEditUser } : undefined
+      }
+      title={tableAct == TableActType.EDIT ? '编辑用户' : '新增用户'}
+      labelCol={{
+        span: 4,
+      }}
+      width={600}
+      onFinish={async (data) => onFinishHandle(data)}
+    >
+      <KCProSelect
+        label="院区"
+        width="md"
+        name="hospId"
+        request={async () => {
+          const hospList = await getHospList();
+          if (hospList) {
+            return hospList.map((t) => ({
+              label: t.name,
+              value: t.id,
+            }));
+          }
+          return [];
+        }}
+        rules={[
+          {
+            required: true,
+            message: '请选择院区!',
+          },
+        ]}
+      />
+      <ProFormText
+        width="md"
+        name="name"
+        label="姓名"
+        placeholder="请输入"
+        rules={[
+          {
+            required: true,
+            message: '请输入姓名!',
+          },
+        ]}
+      />
+      <ProFormText
+        width="md"
+        name="account"
+        label="工号"
+        placeholder="请输入"
+        rules={[
+          {
+            required: true,
+            message: '请输入工号!',
+          },
+        ]}
+      />
+      <ProFormText
+        width="md"
+        name="phoneNum"
+        label="电话"
+        placeholder="请输入"
+      />
+      <ProFormText
+        width="md"
+        name="idCardNum"
+        label="身份证号码"
+        placeholder="请输入"
+      />
+      <KCProSelect
+        label="类型"
+        width="md"
+        name="isOnService"
+        options={[
+          {
+            value: 1,
+            label: '离职',
+          },
+          {
+            value: 0,
+            label: '在职',
+          },
+        ]}
+      />
+    </KCModal>
+  );
+};
+
+export default ActModal;

+ 175 - 0
src/pages/platform/setting/userManage/model.ts

@@ -0,0 +1,175 @@
+/*
+ * @Author: your name
+ * @Date: 2022-01-12 10:12:55
+ * @LastEditTime: 2022-02-10 09:02:58
+ * @LastEditors: Please set LastEditors
+ * @Description: 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
+ * @FilePath: /KC-MiddlePlatform/src/pages/platform/setting/userManage/model.ts
+ */
+
+import { Effect, ImmerReducer, Reducer, Subscription } from 'umi';
+import {
+  addUsers,
+  delUsers,
+  editUsers,
+  getUsertemplate,
+  importUserData,
+  resetUserPwd,
+} from '@/service/user';
+import { TableListItem } from './typings';
+
+export enum TableActType {
+  NOACT,
+  ADD,
+  DEL,
+  EDIT,
+}
+
+export interface userManageModelState {
+  name: string;
+  tableAct: TableActType;
+  isShowModal: boolean;
+  reloadTable: boolean;
+  currentEditUser: TableListItem | undefined;
+  record: TableListItem | undefined;
+}
+
+export interface userManageModelType {
+  namespace: 'userManageModel';
+  state: userManageModelState;
+  effects: {
+    query: Effect;
+    postAddUserData: Effect;
+    postEditUserData: Effect;
+    delUsersRequest: Effect;
+    getUserTemplateReq: Effect;
+    importUsers: Effect;
+    resetUserPwd: Effect;
+  };
+  reducers: {
+    save: Reducer<userManageModelState>;
+    addUser: Reducer<userManageModelState>;
+    cancelTableAct: Reducer<userManageModelState>;
+    reloadTable: Reducer<userManageModelState>;
+    editUser: Reducer<userManageModelState>;
+    // 启用 immer 之后
+    // save: ImmerReducer<IndexModelState>;
+  };
+  subscriptions: { setup: Subscription };
+}
+
+const userManageModel: userManageModelType = {
+  namespace: 'userManageModel',
+
+  state: {
+    name: '',
+    tableAct: TableActType.NOACT,
+    isShowModal: false,
+    reloadTable: false,
+    currentEditUser: undefined,
+    record: undefined, //当前操作对象
+  },
+
+  effects: {
+    *query({ payload }, { call, put }) {},
+
+    *postAddUserData({ payload }, { call, put }) {
+      yield addUsers(payload);
+      yield put({
+        type: 'reloadTable',
+        payload: true,
+      });
+    },
+    *resetUserPwd({ payload }) {
+      yield resetUserPwd({ userId: payload.id });
+    },
+    *postEditUserData({ payload }, { call, put, select }) {
+      const currentEditUserOld = yield select(
+        ({
+          userManageModel: state,
+        }: {
+          userManageModel: userManageModelState;
+        }) => {
+          return state.currentEditUser;
+        },
+      );
+
+      yield editUsers({ ...currentEditUserOld, ...payload });
+      yield put({
+        type: 'reloadTable',
+        payload: true,
+      });
+    },
+    *delUsersRequest({ payload }, { put }) {
+      yield delUsers(payload);
+      yield put({
+        type: 'reloadTable',
+        payload: true,
+      });
+    },
+    *getUserTemplateReq() {
+      const url = yield getUsertemplate();
+      window.open(url);
+    },
+    *importUsers({ payload }, { call, put, select }) {
+      const formData: any = new FormData();
+      formData.append('file', payload);
+      yield importUserData(formData);
+      yield put({
+        type: 'reloadTable',
+        payload: true,
+      });
+    },
+  },
+  reducers: {
+    reloadTable(state, action) {
+      return {
+        ...state,
+        reloadTable: action.payload,
+      };
+    },
+    save(state, action) {
+      return {
+        ...state,
+        ...action.payload,
+      };
+    },
+    addUser(state, action) {
+      return {
+        ...state,
+        ...action.payload,
+      };
+    },
+    cancelTableAct(state) {
+      return {
+        ...state,
+        isShowModal: false,
+      };
+    },
+    editUser(state, action) {
+      return {
+        ...state,
+        tableAct: action.payload.act,
+        isShowModal: action.payload.isShowModal,
+        currentEditUser: action.payload.userData,
+      };
+    },
+    // 启用 immer 之后
+    // save(state, action) {
+    //   state.name = action.payload;
+    // },
+  },
+  subscriptions: {
+    setup({ dispatch, history }) {
+      return history.listen(({ pathname }) => {
+        if (pathname === '/') {
+          dispatch({
+            type: 'query',
+          });
+        }
+      });
+    },
+  },
+};
+
+export default userManageModel;

+ 3 - 0
src/pages/platform/setting/userManage/style.less

@@ -0,0 +1,3 @@
+.UserManage {
+  height: 100%;
+}

+ 25 - 0
src/pages/platform/setting/userManage/typings.d.ts

@@ -0,0 +1,25 @@
+/*
+ * @Author: your name
+ * @Date: 2022-01-13 11:10:58
+ * @LastEditTime: 2022-01-13 11:43:54
+ * @LastEditors: Please set LastEditors
+ * @Description: 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
+ * @FilePath: /KC-MiddlePlatform/src/pages/platform/setting/userManage/typings.d.ts
+ */
+
+export type TableListItem = {
+  id: number;
+  name: string;
+  hospName: string;
+  account: number;
+  phoneNum: number;
+  idCardNum: number;
+  isOnService: number;
+};
+
+// export enum TableActType {
+//     NOACT,
+//     ADD,
+//     DEL,
+//     EDIT
+// }

+ 42 - 31
src/service/api.d.ts

@@ -1,40 +1,51 @@
 /*
  * @Author: your name
  * @Date: 2021-11-11 11:23:56
- * @LastEditTime: 2021-11-15 17:10:01
+ * @LastEditTime: 2022-01-12 10:05:06
  * @LastEditors: Please set LastEditors
  * @Description: 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
  * @FilePath: /KC-MiddlePlatform/src/service/api.d.ts
  */
 
-
 declare namespace LoginApi {
-      type LoginParams = {
-        account:string,
-        password:string,
-        remember:boolean
-      };
-      
-      type CurrentUserData = {
-          token:string,
-          name: string|number;
-          avatar?: string;
-          userid?: string|number;
-          [propsName:string]:any
-      };
-    
-      type LoginResult = {
-        name:string,
-        token:string,
-        [propsName:string]:any
-      }
-    
-      type EditUserInfoType = {
-        account?:string,
-        hospId?:number,
-        hospitalStatus?:number,
-        id?:number,
-        name?:string,
-        password?:string
-      }
-}
+  export type LoginParams = {
+    account: string;
+    password: string;
+    // remember:boolean,
+    hospSign: string;
+  };
+
+  export type CurrentUserData = {
+    token: string;
+    name: string | number;
+    avatar?: string;
+    userid?: string | number;
+    [propsName: string]: any;
+  };
+
+  export type LoginResult = {
+    name: string;
+    token: string;
+    [propsName: string]: any;
+  };
+
+  export type EditUserInfoType = {
+    account?: string;
+    hospId?: number;
+    hospitalStatus?: number;
+    id?: number;
+    name?: string;
+    password?: string;
+  };
+}
+
+declare namespace userRelationInfo {
+  export type subHospItem = {};
+
+  export type OwnAppsItem = {
+    id: number;
+    name: string;
+    icon: string;
+    url: string;
+  };
+}

+ 116 - 0
src/service/hospList.ts

@@ -0,0 +1,116 @@
+/*
+ * @Author: your name
+ * @Date: 2022-01-13 09:15:59
+ * @LastEditTime: 2022-02-08 14:25:50
+ * @LastEditors: Please set LastEditors
+ * @Description: 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
+ * @FilePath: /KC-MiddlePlatform/src/service/hospList.ts
+ */
+
+import { request } from 'umi';
+
+//获取当前登陆院区以及互通的院区列表
+export const getHospList = async () => {
+  return request<
+    {
+      id: number;
+      name: string;
+    }[]
+  >('/master/centerSys/hospital/currentAndShareHosp', {
+    method: 'GET',
+  });
+};
+
+//获取主院
+export const getMainHosp = async () => {
+  return request<{
+    id: number;
+    name: string;
+  }>('/master/centerSys/hospital/getMainList', {
+    method: 'GET',
+  });
+};
+
+export type HospTableItem = {
+  id: number;
+  hospName: string;
+  hospSign: string;
+  hospAbbreviation: string;
+  mainHospId: number;
+  mainHospName: string;
+  updateTime: string;
+  systemName: string;
+  dataShare: string;
+};
+
+//获取医院列表
+export const getAllHosp = async (params: {
+  current: number;
+  pageSize: number;
+  hospitalName: string;
+}) => {
+  return request<{
+    current: number;
+    pageSize: number;
+    totalCount: number;
+    totalPage: number;
+    list?: HospTableItem[];
+  }>('/master/centerSys/hospital/list', {
+    method: 'GET',
+    params: params,
+  });
+};
+
+export type AddHospDataType = {
+  hospAbbreviation: string; //医院简称
+  hospName: string;
+  hospSign: string;
+  isDataShare: number; //是否数据共享,0.默认,不共享,1.共享数据
+  mainHospId: number;
+  mainHospName: string;
+  systemName: string;
+};
+//添加院区
+export const addHosp = async (data: AddHospDataType) => {
+  return request('/master/centerSys/hospital/addHospital', {
+    method: 'POST',
+    data: data,
+  });
+};
+
+//院区管理删除院区
+
+export const delHosp = async (data: number[]) => {
+  return request('/master/centerSys/hospital/deleteHosp', {
+    method: 'POST',
+    data: data,
+  });
+};
+
+//院区管理编辑院区
+export type EditHospType = {
+  id: number;
+  hospAbbreviation: string;
+  hospName: string;
+  hospSign: string;
+  isDataShare: string;
+  systemName: string;
+};
+export const editHosp = async (data: EditHospType) => {
+  return request('/master/centerSys/hospital/editHosp', {
+    method: 'POST',
+    data: data,
+  });
+};
+
+//院区绑定菜单
+
+export const hospBindMenu = async (data: {
+  hospId: number;
+  menuIds: string[];
+}) => {
+  return request('/master/centerSys/hospital/addHospMenu', {
+    method: 'POST',
+    data: data,
+  });
+};

+ 31 - 20
src/service/login.ts

@@ -1,33 +1,44 @@
 /*
  * @Author: your name
  * @Date: 2021-11-11 10:35:56
- * @LastEditTime: 2021-11-15 16:56:50
+ * @LastEditTime: 2022-01-10 11:46:01
  * @LastEditors: Please set LastEditors
  * @Description: 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
  * @FilePath: /KC-MiddlePlatform/service/login.ts
  */
 
-
 import { request } from 'umi';
 
-export const getSubHosp = async ()=>{
-      return request('/api/getSubHosp',{
-        method: 'GET',
-      })
-}
-
+export const getHospConfigBySign = async (hospSign: string) => {
+  return request<
+    {
+      hospSign: string;
+      name: string;
+      systemName: string;
+    }[]
+  >('/master/centerSys/hospital/getHospConfigBySign', {
+    method: 'GET',
+    params: { hospSign },
+  });
+};
 
-export const getHospSubSystemList = async ()=>{
-    return request('/api/getHospSubSystemList',{
+//获取该账户拥有的子应用列表
+export const getHospSubSystemList = async () => {
+  return request<userRelationInfo.OwnAppsItem[]>(
+    '/master/centerSys/user/getSystemList',
+    {
       method: 'GET',
-    })
-}
-
-export const login  = async (data:LoginApi.LoginParams)=>{
-    
-    return request('/api/login', {
-        method: 'POST',
-        data:data
-    });
+    },
+  );
+};
 
-}
+export const login = async (data: LoginApi.LoginParams) => {
+  return request<{
+    name: string;
+    token: string;
+    userId: number;
+  }>('/master/centerSys/login', {
+    method: 'POST',
+    data: data,
+  });
+};

+ 111 - 0
src/service/menu.ts

@@ -0,0 +1,111 @@
+/*
+ * @Author: your name
+ * @Date: 2022-01-10 10:27:15
+ * @LastEditTime: 2022-01-20 14:00:10
+ * @LastEditors: Please set LastEditors
+ * @Description: 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
+ * @FilePath: /KC-MiddlePlatform/src/service/hospInfo.ts
+ */
+
+import { TableRequestParamsType, TableResponseDataType } from '@/typings';
+import { request } from 'umi';
+
+//获取平台菜单
+export const getPlatformMenu = async (systemId: number) => {
+  return request('/master/centerSys/user/getMenuNav', {
+    method: 'GET',
+    params: { systemId },
+  });
+};
+
+//菜单管理界面菜单列表
+export interface MenuItemDataType {
+  menuId: string;
+  icon?: any;
+  name: string;
+  orderNum?: number; //排序数字
+  parentId: string; //父级id,没有传0
+  path: string;
+  perms?: string;
+  softUrl?: string;
+  type: number;
+  url?: string;
+  modifyTime: string;
+  children: MenuItemDataType[];
+}
+
+export interface getAllMenusDataType extends TableResponseDataType {
+  list: MenuItemDataType[];
+}
+
+interface getAllMenusDataParamsType {
+  name?: string;
+}
+
+export const getAllMenus = async () => {
+  return request<getAllMenusDataType>('/master/centerSys/menu/list', {
+    method: 'GET',
+  });
+};
+
+//菜单管理-新增菜单
+
+export type AddMenuDataType = {
+  icon?: any;
+  name: string;
+  orderNum?: number; //排序数字
+  parentId: number; //父级id,没有传0
+  path: string;
+  perms?: string;
+  softUrl?: string;
+  type: number;
+  url?: string;
+};
+
+export const addMenu = async (data: AddMenuDataType) => {
+  return request('/master/centerSys/menu/addMenu', {
+    method: 'POST',
+    data: data,
+  });
+};
+
+//菜单管理- 编辑菜单
+
+export type EditMenuDataType = {
+  id?: string;
+  icon?: any;
+  name: string;
+  orderNum?: number; //排序数字
+  parentId: number; //父级id,没有传0
+  path: string;
+  perms?: string;
+  softUrl?: string;
+  type: number;
+  url?: string;
+};
+
+export const editMenu = async (data: EditMenuDataType) => {
+  return request('/master/centerSys/menu/editMenu', {
+    method: 'POST',
+    data: data,
+  });
+};
+
+//菜单管理-删除菜单
+export const delMenu = async (data: number[]) => {
+  return request('/master/centerSys/menu/removeMenu', {
+    method: 'POST',
+    data: data,
+  });
+};
+
+//获取指定院区菜单
+export const getSpacifyHospMenu = async (data: { hospId: number }) => {
+  return request<{
+    selectedMenuIds: string[];
+    allMenuVO: any[];
+  }>('/master/centerSys/hospital/getHospMenu', {
+    method: 'GET',
+    params: data,
+  });
+};

+ 118 - 0
src/service/role.ts

@@ -0,0 +1,118 @@
+/*
+ * @Author: your name
+ * @Date: 2022-01-18 14:56:29
+ * @LastEditTime: 2022-01-19 17:27:32
+ * @LastEditors: Please set LastEditors
+ * @Description: 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
+ * @FilePath: /KC-MiddlePlatform/src/service/role.ts
+ */
+
+import { TableRequestParamsType, TableResponseDataType } from '@/typings';
+import { request } from 'umi';
+
+//获取所有角色
+
+export type RoleItemType = {
+  roleId: number;
+  roleName: string;
+  hospName: string;
+  modifyTime: string;
+  remark: string;
+  hospId: number;
+};
+
+type GetAllRolesType = { list: RoleItemType[] } & TableResponseDataType;
+export const getAllRoles = async (
+  params: { roleName?: string } & TableRequestParamsType,
+) => {
+  return request<GetAllRolesType>('/master/centerSys/role/list', {
+    method: 'GET',
+    params: { ...params },
+  });
+};
+
+//新增角色
+export const addRoles = async (data: {
+  roleName: string;
+  remark?: string;
+  hospId: number;
+}) => {
+  return request('/master/centerSys/role/addRole', {
+    method: 'POST',
+    data: { ...data },
+  });
+};
+
+//删除角色
+export const delRoles = async (data: number[]) => {
+  return request('/master/centerSys/role/deleteRole', {
+    method: 'POST',
+    data: data,
+  });
+};
+
+//编辑角色
+export const editRoles = async (data: {
+  remark: string;
+  roleId: number;
+  roleName: string;
+}) => {
+  return request('/master/centerSys/role/editRole', {
+    method: 'POST',
+    data: data,
+  });
+};
+
+//根据角色获取一绑定的菜单
+export const getRoleHasBindMenus = async (params: {
+  hospId: number;
+  roleId: number;
+  name?: string;
+}) => {
+  return request<{
+    menuVOs: any[];
+    menuIds: number[];
+  }>('/master/centerSys/role/roleMenuList', {
+    method: 'GET',
+    params: params,
+  });
+};
+
+//角色绑定菜单
+export const roleBindMenus = async (data: {
+  hospId: number;
+  roleId: number;
+  menuIds: number[];
+}) => {
+  return request('/master/centerSys/role/editRoleMenu', {
+    method: 'POST',
+    data,
+  });
+};
+
+//根据角色获取已绑定的人员
+export const getRoleHasBindUsers = async (params: {
+  hospId: number;
+  roleId: number;
+  name?: string;
+}) => {
+  return request<{
+    users: any[];
+    userIds: number[];
+  }>('/master/centerSys/role/roleUserList', {
+    method: 'GET',
+    params: params,
+  });
+};
+
+//角色绑定人员
+export const roleBindUsers = async (data: {
+  hospId: number;
+  roleId: number;
+  userIds: number[];
+}) => {
+  return request('/master/centerSys/role/editUserRole', {
+    method: 'POST',
+    data,
+  });
+};

+ 100 - 0
src/service/user.ts

@@ -0,0 +1,100 @@
+/*
+ * @Author: your name
+ * @Date: 2022-01-12 09:55:49
+ * @LastEditTime: 2022-02-10 08:59:51
+ * @LastEditors: Please set LastEditors
+ * @Description: 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
+ * @FilePath: /KC-MiddlePlatform/src/service/user.ts
+ */
+
+import { request } from 'umi';
+
+//获取用户列表
+
+export type getUsersParams = {
+  current: number;
+  pageSize: number;
+  keyword?: string;
+};
+
+type paginationDefaultType = {
+  current: number;
+  pageSize: number;
+  totalCount: number;
+  totalPage: number;
+};
+
+export const getUsers = async (params: getUsersParams) => {
+  return request<
+    {
+      list: any[];
+    } & paginationDefaultType
+  >('/master/centerSys/user/list', {
+    method: 'GET',
+    params: { ...params },
+  });
+};
+
+export type AddUsersDataType = {
+  hospId: number;
+  name: string;
+  account: number;
+  phoneNum?: number;
+  idCardNum?: number;
+  isOnService?: number; //是否在职0.默认,1.离职
+};
+export const addUsers = async (data: AddUsersDataType) => {
+  return request('/master/centerSys/user/addUser', {
+    method: 'POST',
+    data: data,
+  });
+};
+
+export type EditUsersDataType = {
+  id: number;
+  hospId?: number;
+  name?: string;
+  account?: number;
+  password?: string;
+  phoneNum?: number;
+  idCardNum?: number;
+  isOnService?: number; //是否在职0.默认,1.离职
+};
+export const editUsers = async (data: EditUsersDataType) => {
+  return request('/master/centerSys/user/editUser', {
+    method: 'POST',
+    data: data,
+  });
+};
+
+export const delUsers = async (data: number[]) => {
+  return request('/master/centerSys/user/removeUser', {
+    method: 'POST',
+    data: data,
+  });
+};
+
+export const getUsertemplate = async () => {
+  return request<string>(
+    '/master/centerSys/pfmfiledata/getUserTemplateFileUrl',
+    {
+      method: 'GET',
+    },
+  );
+};
+
+//导入用户
+export const importUserData = async (formData: any) => {
+  return request('/master/centerSys/user/addAllUser', {
+    method: 'POST',
+    data: formData,
+  });
+};
+
+//重置用户密码
+export const resetUserPwd = async (data: { userId: number }) => {
+  return request('/master/centerSys/user/resetPwd', {
+    method: 'POST',
+    data: data,
+  });
+};

+ 27 - 10
src/typings.d.ts

@@ -1,7 +1,7 @@
 /*
  * @Author: your name
  * @Date: 2021-11-19 16:31:19
- * @LastEditTime: 2021-12-21 17:50:45
+ * @LastEditTime: 2022-01-14 15:25:24
  * @LastEditors: Please set LastEditors
  * @Description: 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
  * @FilePath: /KC-MiddlePlatform/src/typings.d.ts
@@ -13,19 +13,36 @@ type SystemListItem = {
   id: string;
   name: string;
   url: string;
+  path: string;
 };
 
+export type TableRequestParamsType = {
+  //KCtable组件request params
+  pageSize?: number | undefined;
+  current?: number | undefined;
+  keyword?: string | undefined;
+  [key: string]: any;
+};
+
+export type TableResponseDataType = {
+  //KCtable组件response data
+  totalCount?: number | undefined;
+  pageSize?: number | undefined;
+  totalPage?: string | undefined;
+  current?: string | undefined;
+  [key: string]: any;
+};
 
 declare interface PostMessageData {
-    type:'LOGOUT'|'REFRESH'|'DATA'|'LOGIN',
-    data?:any
+  type: 'LOGOUT' | 'REFRESH' | 'DATA' | 'LOGIN';
+  data?: any;
 }
-declare interface Window  {
-  stateFromTop: any,
-  postMessageToChild:(data:PostMessageData)=>Promise<any>,
-  removeListenner:(name:string)=>Promise<any>,
+declare interface Window {
+  stateFromTop: any;
+  postMessageToChild: (data: PostMessageData) => Promise<any>;
+  removeListenner: (name: string) => Promise<any>;
 }
 
-declare interface Iframe  {
-    contentWindow:any
-}
+declare interface Iframe {
+  contentWindow: any;
+}

+ 42 - 0
src/utils.ts

@@ -0,0 +1,42 @@
+/*
+ * @Author: your name
+ * @Date: 2022-01-13 17:09:05
+ * @LastEditTime: 2022-01-21 10:41:05
+ * @LastEditors: Please set LastEditors
+ * @Description: 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
+ * @FilePath: /KC-MiddlePlatform/src/utils.js
+ */
+
+export const randomString = (length: number) => {
+  const chars =
+    '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_=-';
+  let result = '';
+  for (let i = length; i > 0; --i) {
+    result += chars[Math.floor(Math.random() * chars.length)];
+  }
+  return result;
+};
+
+export interface TreeItemType {
+  children?: TreeItemType[];
+  [key: string]: any;
+}
+
+//获取树结构指定的值集合
+export const getValsFromTree = (data: TreeItemType[], key: string) => {
+  let result: any[] = [];
+  function looper(data: TreeItemType[], key: string) {
+    data.forEach((t) => {
+      if (t[key]) {
+        result.push(t[key]);
+      }
+      if (t.children && t.children.length > 0) {
+        looper(t.children, key);
+      }
+    });
+  }
+  looper(data, key);
+  return result;
+};
+
+export const searchTree = (treeData: TreeItemType[]) => {};

+ 2 - 1
tsconfig.json

@@ -21,7 +21,8 @@
     "src/**/*",
     "config/**/*",
     ".umirc.ts",
-    "typings.d.ts"
+    "typings.d.ts",
+    "**/*.ts"
   ],
   "exclude": [
     "node_modules",