code4eat 3 лет назад
Сommit
d776bc8518
100 измененных файлов с 5708 добавлено и 0 удалено
  1. 16 0
      .editorconfig
  2. 8 0
      .eslintignore
  3. 8 0
      .eslintrc.js
  4. 40 0
      .gitignore
  5. 23 0
      .prettierignore
  6. 5 0
      .prettierrc.js
  7. 5 0
      .stylelintrc.js
  8. 57 0
      README.md
  9. 15 0
      config/config.dev.ts
  10. 72 0
      config/config.ts
  11. 31 0
      config/defaultSettings.ts
  12. 593 0
      config/oneapi.json
  13. 46 0
      config/proxy.ts
  14. 61 0
      config/routes.ts
  15. 10 0
      jest.config.js
  16. 10 0
      jsconfig.json
  17. 104 0
      mock/accreditationDetail.ts
  18. 182 0
      mock/articleManagement.ts
  19. 175 0
      mock/listTableList.ts
  20. 107 0
      mock/notices.ts
  21. 5 0
      mock/route.ts
  22. 188 0
      mock/user.ts
  23. 113 0
      package.json
  24. 1 0
      public/CNAME
  25. BIN
      public/favicon.ico
  26. BIN
      public/icons/icon-128x128.png
  27. BIN
      public/icons/icon-192x192.png
  28. BIN
      public/icons/icon-512x512.png
  29. BIN
      public/logo.png
  30. 0 0
      public/logo.svg
  31. 5 0
      public/pro_icon.svg
  32. 9 0
      src/access.ts
  33. 189 0
      src/app.tsx
  34. 29 0
      src/components/Footer/index.tsx
  35. 16 0
      src/components/HeaderDropdown/index.less
  36. 17 0
      src/components/HeaderDropdown/index.tsx
  37. 25 0
      src/components/HeaderSearch/index.less
  38. 101 0
      src/components/HeaderSearch/index.tsx
  39. 33 0
      src/components/MccsClickableTabs/index.less
  40. 44 0
      src/components/MccsClickableTabs/index.tsx
  41. 21 0
      src/components/MccsClickableTabs/typings.d.ts
  42. 0 0
      src/components/MccsDrawerForm/index.less
  43. 32 0
      src/components/MccsDrawerForm/index.tsx
  44. 15 0
      src/components/MccsDrawerForm/typings.d.ts
  45. 12 0
      src/components/MccsFileTree/README.md
  46. 79 0
      src/components/MccsFileTree/components/DirectoryTree/index.less
  47. 116 0
      src/components/MccsFileTree/components/DirectoryTree/index.tsx
  48. BIN
      src/components/MccsFileTree/images/file.jpg
  49. BIN
      src/components/MccsFileTree/images/folder.jpg
  50. 59 0
      src/components/MccsFileTree/index.less
  51. 153 0
      src/components/MccsFileTree/index.tsx
  52. 22 0
      src/components/MccsFileTree/typing.d.ts
  53. 67 0
      src/components/MccsModal/index.tsx
  54. 17 0
      src/components/MccsModal/typings.d.ts
  55. 40 0
      src/components/MccsPageContainer/index.jsx
  56. 7 0
      src/components/MccsPageContainer/index.less
  57. 45 0
      src/components/MccsProCard/index.tsx
  58. BIN
      src/components/MccsRecordList/images/arrow.png
  59. 240 0
      src/components/MccsRecordList/index.less
  60. 209 0
      src/components/MccsRecordList/index.tsx
  61. 41 0
      src/components/MccsRecordList/typings.d.ts
  62. 34 0
      src/components/MccsScoreBanner/index.less
  63. 47 0
      src/components/MccsScoreBanner/index.tsx
  64. 8 0
      src/components/MccsTable/README.md
  65. 17 0
      src/components/MccsTable/index.less
  66. 70 0
      src/components/MccsTable/index.tsx
  67. 44 0
      src/components/MccsTable/typings.d.ts
  68. 126 0
      src/components/NoticeIcon/NoticeIcon.tsx
  69. 103 0
      src/components/NoticeIcon/NoticeList.less
  70. 113 0
      src/components/NoticeIcon/NoticeList.tsx
  71. 35 0
      src/components/NoticeIcon/index.less
  72. 153 0
      src/components/NoticeIcon/index.tsx
  73. 104 0
      src/components/RightContent/AvatarDropdown.tsx
  74. 84 0
      src/components/RightContent/index.less
  75. 103 0
      src/components/RightContent/index.tsx
  76. 33 0
      src/components/antdProOverwrite/MccsProForm/index.tsx
  77. 30 0
      src/components/antdProOverwrite/MccsProFormText/index.tsx
  78. 272 0
      src/components/index.md
  79. 61 0
      src/e2e/baseLayout.e2e.js
  80. 83 0
      src/global.less
  81. 91 0
      src/global.tsx
  82. 26 0
      src/locales/bn-BD.ts
  83. 5 0
      src/locales/bn-BD/component.ts
  84. 17 0
      src/locales/bn-BD/globalHeader.ts
  85. 52 0
      src/locales/bn-BD/menu.ts
  86. 70 0
      src/locales/bn-BD/pages.ts
  87. 7 0
      src/locales/bn-BD/pwa.ts
  88. 31 0
      src/locales/bn-BD/settingDrawer.ts
  89. 59 0
      src/locales/bn-BD/settings.ts
  90. 25 0
      src/locales/en-US.ts
  91. 5 0
      src/locales/en-US/component.ts
  92. 17 0
      src/locales/en-US/globalHeader.ts
  93. 52 0
      src/locales/en-US/menu.ts
  94. 70 0
      src/locales/en-US/pages.ts
  95. 6 0
      src/locales/en-US/pwa.ts
  96. 31 0
      src/locales/en-US/settingDrawer.ts
  97. 60 0
      src/locales/en-US/settings.ts
  98. 24 0
      src/locales/fa-IR.ts
  99. 5 0
      src/locales/fa-IR/component.ts
  100. 17 0
      src/locales/fa-IR/globalHeader.ts

+ 16 - 0
.editorconfig

@@ -0,0 +1,16 @@
+# http://editorconfig.org
+root = true
+
+[*]
+indent_style = space
+indent_size = 2
+end_of_line = lf
+charset = utf-8
+trim_trailing_whitespace = true
+insert_final_newline = true
+
+[*.md]
+trim_trailing_whitespace = false
+
+[Makefile]
+indent_style = tab

+ 8 - 0
.eslintignore

@@ -0,0 +1,8 @@
+/lambda/
+/scripts
+/config
+.history
+public
+dist
+.umi
+mock

+ 8 - 0
.eslintrc.js

@@ -0,0 +1,8 @@
+module.exports = {
+  extends: [require.resolve('@umijs/fabric/dist/eslint')],
+  globals: {
+    ANT_DESIGN_PRO_ONLY_DO_NOT_USE_IN_YOUR_PRODUCTION: true,
+    page: true,
+    REACT_APP_ENV: true,
+  },
+};

+ 40 - 0
.gitignore

@@ -0,0 +1,40 @@
+# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
+
+# dependencies
+**/node_modules
+# roadhog-api-doc ignore
+/src/utils/request-temp.js
+_roadhog-api-doc
+
+# production
+/dist
+/.vscode
+
+# misc
+.DS_Store
+npm-debug.log*
+yarn-error.log
+
+/coverage
+.idea
+yarn.lock
+package-lock.json
+*bak
+.vscode
+
+# visual studio code
+.history
+*.log
+functions/*
+.temp/**
+
+# umi
+.umi
+.umi-production
+
+# screenshot
+screenshot
+.firebase
+.eslintcache
+
+build

+ 23 - 0
.prettierignore

@@ -0,0 +1,23 @@
+**/*.svg
+package.json
+.umi
+.umi-production
+/dist
+.dockerignore
+.DS_Store
+.eslintignore
+*.png
+*.toml
+docker
+.editorconfig
+Dockerfile*
+.gitignore
+.prettierignore
+LICENSE
+.eslintcache
+*.lock
+yarn-error.log
+.history
+CNAME
+/build
+/public

+ 5 - 0
.prettierrc.js

@@ -0,0 +1,5 @@
+const fabric = require('@umijs/fabric');
+
+module.exports = {
+  ...fabric.prettier,
+};

+ 5 - 0
.stylelintrc.js

@@ -0,0 +1,5 @@
+const fabric = require('@umijs/fabric');
+
+module.exports = {
+  ...fabric.stylelint,
+};

+ 57 - 0
README.md

@@ -0,0 +1,57 @@
+# Ant Design Pro
+
+This project is initialized with [Ant Design Pro](https://pro.ant.design). Follow is the quick guide for how to use.
+
+## Environment Prepare
+
+Install `node_modules`:
+
+```bash
+npm install
+```
+
+or
+
+```bash
+yarn
+```
+
+## Provided Scripts
+
+Ant Design Pro provides some useful script to help you quick start and build with web project, code style check and test.
+
+Scripts provided in `package.json`. It's safe to modify or add additional script:
+
+### Start project
+
+```bash
+npm start
+```
+
+### Build project
+
+```bash
+npm run build
+```
+
+### Check code style
+
+```bash
+npm run lint
+```
+
+You can also use script to auto fix some lint error:
+
+```bash
+npm run lint:fix
+```
+
+### Test code
+
+```bash
+npm test
+```
+
+## More
+
+You can view full document on our [official website](https://pro.ant.design). And welcome any feedback in our [github](https://github.com/ant-design/ant-design-pro).

+ 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: {},
+  },
+});

+ 72 - 0
config/config.ts

@@ -0,0 +1,72 @@
+// https://umijs.org/config/
+import { defineConfig } from 'umi';
+import { join } from 'path';
+
+import defaultSettings from './defaultSettings';
+import proxy from './proxy';
+import routes from './routes';
+
+const { REACT_APP_ENV } = process.env;
+
+export default defineConfig({
+  hash: true,
+  antd: {},
+  dva: {
+    hmr: true,
+  },
+  layout: {
+    // https://umijs.org/zh-CN/plugins/plugin-layout
+    locale: false,
+    siderWidth: 208,
+    ...defaultSettings,
+  },
+  // https://umijs.org/zh-CN/plugins/plugin-locale
+  // locale: {
+  //   // default zh-CN
+  //   default: 'zh-CN',
+  //   antd: true,
+  //   // default true, when it is true, will use `navigator.language` overwrite default
+  //   baseNavigator: true,
+  // },
+  dynamicImport: {
+    loading: '@ant-design/pro-layout/es/PageLoading',
+  },
+  targets: {
+    ie: 11,
+  },
+  // umi routes: https://umijs.org/docs/routing
+  routes,
+  // Theme for antd: https://ant.design/docs/react/customize-theme-cn
+  theme: {
+    'primary-color': defaultSettings.primaryColor,
+  },
+  // esbuild is father build tools
+  // https://umijs.org/plugins/plugin-esbuild
+  esbuild: {},
+  title: false,
+  ignoreMomentLocale: true,
+  proxy: proxy[REACT_APP_ENV || 'dev'],
+  manifest: {
+    basePath: '/',
+  },
+  // Fast Refresh 热更新
+  fastRefresh: {},
+  openAPI: [
+    {
+      requestLibPath: "import { request } from 'umi'",
+      // 或者使用在线的版本
+      // schemaPath: "https://gw.alipayobjects.com/os/antfincdn/M%24jrzTTYJN/oneapi.json"
+      schemaPath: join(__dirname, 'oneapi.json'),
+      mock: false,
+    },
+    {
+      requestLibPath: "import { request } from 'umi'",
+      schemaPath: 'https://gw.alipayobjects.com/os/antfincdn/CA1dOm%2631B/openapi.json',
+      projectName: 'swagger',
+    },
+  ],
+  nodeModulesTransform: { type: 'none' },
+  mfsu: {},
+  webpack5: {},
+  exportStatic: {},
+});

+ 31 - 0
config/defaultSettings.ts

@@ -0,0 +1,31 @@
+/*
+ * @Author: your name
+ * @Date: 2021-09-03 14:28:27
+ * @LastEditTime: 2021-09-03 17:17:03
+ * @LastEditors: Please set LastEditors
+ * @Description: In User Settings Edit
+ * @FilePath: /MedicalWisdomCheckSys/config/defaultSettings.ts
+ */
+import { Settings as LayoutSettings } from '@ant-design/pro-layout';
+
+
+
+const Settings: LayoutSettings & {
+  pwa?: boolean;
+  logo?: string;
+} = {
+  navTheme: 'light',
+  // 拂晓蓝
+  primaryColor: '#00528E',
+  layout: 'mix',
+  contentWidth: 'Fluid',
+  fixedHeader: false,
+  fixSiderbar: true,
+  colorWeak: false,
+  title: '医院智慧查检系统',
+  pwa: false,
+  logo:'',
+  iconfontUrl: '',
+};
+
+export default Settings;

+ 593 - 0
config/oneapi.json

@@ -0,0 +1,593 @@
+{
+  "openapi": "3.0.1",
+  "info": {
+    "title": "Ant Design Pro",
+    "version": "1.0.0"
+  },
+  "servers": [
+    {
+      "url": "http://localhost:8000/"
+    },
+    {
+      "url": "https://localhost:8000/"
+    }
+  ],
+  "paths": {
+    "/api/currentUser": {
+      "get": {
+        "tags": ["api"],
+        "description": "获取当前的用户",
+        "operationId": "currentUser",
+        "responses": {
+          "200": {
+            "description": "Success",
+            "content": {
+              "application/json": {
+                "schema": {
+                  "$ref": "#/components/schemas/CurrentUser"
+                }
+              }
+            }
+          },
+          "401": {
+            "description": "Error",
+            "content": {
+              "application/json": {
+                "schema": {
+                  "$ref": "#/components/schemas/ErrorResponse"
+                }
+              }
+            }
+          }
+        }
+      },
+      "x-swagger-router-controller": "api"
+    },
+    "/api/login/captcha": {
+      "post": {
+        "description": "发送验证码",
+        "operationId": "getFakeCaptcha",
+        "tags": ["login"],
+        "parameters": [
+          {
+            "name": "phone",
+            "in": "query",
+            "description": "手机号",
+            "schema": {
+              "type": "string"
+            }
+          }
+        ],
+        "responses": {
+          "200": {
+            "description": "Success",
+            "content": {
+              "application/json": {
+                "schema": {
+                  "$ref": "#/components/schemas/FakeCaptcha"
+                }
+              }
+            }
+          }
+        }
+      }
+    },
+    "/api/login/outLogin": {
+      "post": {
+        "description": "登录接口",
+        "operationId": "outLogin",
+        "tags": ["login"],
+        "responses": {
+          "200": {
+            "description": "Success",
+            "content": {
+              "application/json": {
+                "schema": {
+                  "type": "object"
+                }
+              }
+            }
+          },
+          "401": {
+            "description": "Error",
+            "content": {
+              "application/json": {
+                "schema": {
+                  "$ref": "#/components/schemas/ErrorResponse"
+                }
+              }
+            }
+          }
+        }
+      },
+      "x-swagger-router-controller": "api"
+    },
+    "/api/login/account": {
+      "post": {
+        "tags": ["login"],
+        "description": "登录接口",
+        "operationId": "login",
+        "requestBody": {
+          "description": "登录系统",
+          "content": {
+            "application/json": {
+              "schema": {
+                "$ref": "#/components/schemas/LoginParams"
+              }
+            }
+          },
+          "required": true
+        },
+        "responses": {
+          "200": {
+            "description": "Success",
+            "content": {
+              "application/json": {
+                "schema": {
+                  "$ref": "#/components/schemas/LoginResult"
+                }
+              }
+            }
+          },
+          "401": {
+            "description": "Error",
+            "content": {
+              "application/json": {
+                "schema": {
+                  "$ref": "#/components/schemas/ErrorResponse"
+                }
+              }
+            }
+          }
+        },
+        "x-codegen-request-body-name": "body"
+      },
+      "x-swagger-router-controller": "api"
+    },
+    "/api/notices": {
+      "summary": "getNotices",
+      "description": "NoticeIconItem",
+      "get": {
+        "tags": ["api"],
+        "operationId": "getNotices",
+        "responses": {
+          "200": {
+            "description": "Success",
+            "content": {
+              "application/json": {
+                "schema": {
+                  "$ref": "#/components/schemas/NoticeIconList"
+                }
+              }
+            }
+          }
+        }
+      }
+    },
+    "/api/rule": {
+      "get": {
+        "tags": ["rule"],
+        "description": "获取规则列表",
+        "operationId": "rule",
+        "parameters": [
+          {
+            "name": "current",
+            "in": "query",
+            "description": "当前的页码",
+            "schema": {
+              "type": "number"
+            }
+          },
+          {
+            "name": "pageSize",
+            "in": "query",
+            "description": "页面的容量",
+            "schema": {
+              "type": "number"
+            }
+          }
+        ],
+        "responses": {
+          "200": {
+            "description": "Success",
+            "content": {
+              "application/json": {
+                "schema": {
+                  "$ref": "#/components/schemas/RuleList"
+                }
+              }
+            }
+          },
+          "401": {
+            "description": "Error",
+            "content": {
+              "application/json": {
+                "schema": {
+                  "$ref": "#/components/schemas/ErrorResponse"
+                }
+              }
+            }
+          }
+        }
+      },
+      "post": {
+        "tags": ["rule"],
+        "description": "新建规则",
+        "operationId": "addRule",
+        "responses": {
+          "200": {
+            "description": "Success",
+            "content": {
+              "application/json": {
+                "schema": {
+                  "$ref": "#/components/schemas/RuleListItem"
+                }
+              }
+            }
+          },
+          "401": {
+            "description": "Error",
+            "content": {
+              "application/json": {
+                "schema": {
+                  "$ref": "#/components/schemas/ErrorResponse"
+                }
+              }
+            }
+          }
+        }
+      },
+      "put": {
+        "tags": ["rule"],
+        "description": "新建规则",
+        "operationId": "updateRule",
+        "responses": {
+          "200": {
+            "description": "Success",
+            "content": {
+              "application/json": {
+                "schema": {
+                  "$ref": "#/components/schemas/RuleListItem"
+                }
+              }
+            }
+          },
+          "401": {
+            "description": "Error",
+            "content": {
+              "application/json": {
+                "schema": {
+                  "$ref": "#/components/schemas/ErrorResponse"
+                }
+              }
+            }
+          }
+        }
+      },
+      "delete": {
+        "tags": ["rule"],
+        "description": "删除规则",
+        "operationId": "removeRule",
+        "responses": {
+          "200": {
+            "description": "Success",
+            "content": {
+              "application/json": {
+                "schema": {
+                  "type": "object"
+                }
+              }
+            }
+          },
+          "401": {
+            "description": "Error",
+            "content": {
+              "application/json": {
+                "schema": {
+                  "$ref": "#/components/schemas/ErrorResponse"
+                }
+              }
+            }
+          }
+        }
+      },
+      "x-swagger-router-controller": "api"
+    },
+    "/swagger": {
+      "x-swagger-pipe": "swagger_raw"
+    }
+  },
+  "components": {
+    "schemas": {
+      "CurrentUser": {
+        "type": "object",
+        "properties": {
+          "name": {
+            "type": "string"
+          },
+          "avatar": {
+            "type": "string"
+          },
+          "userid": {
+            "type": "string"
+          },
+          "email": {
+            "type": "string"
+          },
+          "signature": {
+            "type": "string"
+          },
+          "title": {
+            "type": "string"
+          },
+          "group": {
+            "type": "string"
+          },
+          "tags": {
+            "type": "array",
+            "items": {
+              "type": "object",
+              "properties": {
+                "key": {
+                  "type": "string"
+                },
+                "label": {
+                  "type": "string"
+                }
+              }
+            }
+          },
+          "notifyCount": {
+            "type": "integer",
+            "format": "int32"
+          },
+          "unreadCount": {
+            "type": "integer",
+            "format": "int32"
+          },
+          "country": {
+            "type": "string"
+          },
+          "access": {
+            "type": "string"
+          },
+          "geographic": {
+            "type": "object",
+            "properties": {
+              "province": {
+                "type": "object",
+                "properties": {
+                  "label": {
+                    "type": "string"
+                  },
+                  "key": {
+                    "type": "string"
+                  }
+                }
+              },
+              "city": {
+                "type": "object",
+                "properties": {
+                  "label": {
+                    "type": "string"
+                  },
+                  "key": {
+                    "type": "string"
+                  }
+                }
+              }
+            }
+          },
+          "address": {
+            "type": "string"
+          },
+          "phone": {
+            "type": "string"
+          }
+        }
+      },
+      "LoginResult": {
+        "type": "object",
+        "properties": {
+          "status": {
+            "type": "string"
+          },
+          "type": {
+            "type": "string"
+          },
+          "currentAuthority": {
+            "type": "string"
+          }
+        }
+      },
+      "PageParams": {
+        "type": "object",
+        "properties": {
+          "current": {
+            "type": "number"
+          },
+          "pageSize": {
+            "type": "number"
+          }
+        }
+      },
+      "RuleListItem": {
+        "type": "object",
+        "properties": {
+          "key": {
+            "type": "integer",
+            "format": "int32"
+          },
+          "disabled": {
+            "type": "boolean"
+          },
+          "href": {
+            "type": "string"
+          },
+          "avatar": {
+            "type": "string"
+          },
+          "name": {
+            "type": "string"
+          },
+          "owner": {
+            "type": "string"
+          },
+          "desc": {
+            "type": "string"
+          },
+          "callNo": {
+            "type": "integer",
+            "format": "int32"
+          },
+          "status": {
+            "type": "integer",
+            "format": "int32"
+          },
+          "updatedAt": {
+            "type": "string",
+            "format": "datetime"
+          },
+          "createdAt": {
+            "type": "string",
+            "format": "datetime"
+          },
+          "progress": {
+            "type": "integer",
+            "format": "int32"
+          }
+        }
+      },
+      "RuleList": {
+        "type": "object",
+        "properties": {
+          "data": {
+            "type": "array",
+            "items": {
+              "$ref": "#/components/schemas/RuleListItem"
+            }
+          },
+          "total": {
+            "type": "integer",
+            "description": "列表的内容总数",
+            "format": "int32"
+          },
+          "success": {
+            "type": "boolean"
+          }
+        }
+      },
+      "FakeCaptcha": {
+        "type": "object",
+        "properties": {
+          "code": {
+            "type": "integer",
+            "format": "int32"
+          },
+          "status": {
+            "type": "string"
+          }
+        }
+      },
+      "LoginParams": {
+        "type": "object",
+        "properties": {
+          "username": {
+            "type": "string"
+          },
+          "password": {
+            "type": "string"
+          },
+          "autoLogin": {
+            "type": "boolean"
+          },
+          "type": {
+            "type": "string"
+          }
+        }
+      },
+      "ErrorResponse": {
+        "required": ["errorCode"],
+        "type": "object",
+        "properties": {
+          "errorCode": {
+            "type": "string",
+            "description": "业务约定的错误码"
+          },
+          "errorMessage": {
+            "type": "string",
+            "description": "业务上的错误信息"
+          },
+          "success": {
+            "type": "boolean",
+            "description": "业务上的请求是否成功"
+          }
+        }
+      },
+      "NoticeIconList": {
+        "type": "object",
+        "properties": {
+          "data": {
+            "type": "array",
+            "items": {
+              "$ref": "#/components/schemas/NoticeIconItem"
+            }
+          },
+          "total": {
+            "type": "integer",
+            "description": "列表的内容总数",
+            "format": "int32"
+          },
+          "success": {
+            "type": "boolean"
+          }
+        }
+      },
+      "NoticeIconItemType": {
+        "title": "NoticeIconItemType",
+        "description": "已读未读列表的枚举",
+        "type": "string",
+        "properties": {},
+        "enum": ["notification", "message", "event"]
+      },
+      "NoticeIconItem": {
+        "type": "object",
+        "properties": {
+          "id": {
+            "type": "string"
+          },
+          "extra": {
+            "type": "string",
+            "format": "any"
+          },
+          "key": { "type": "string" },
+          "read": {
+            "type": "boolean"
+          },
+          "avatar": {
+            "type": "string"
+          },
+          "title": {
+            "type": "string"
+          },
+          "status": {
+            "type": "string"
+          },
+          "datetime": {
+            "type": "string",
+            "format": "date"
+          },
+          "description": {
+            "type": "string"
+          },
+          "type": {
+            "extensions": {
+              "x-is-enum": true
+            },
+            "$ref": "#/components/schemas/NoticeIconItemType"
+          }
+        }
+      }
+    }
+  }
+}

+ 46 - 0
config/proxy.ts

@@ -0,0 +1,46 @@
+/*
+ * @Author: your name
+ * @Date: 2021-09-03 14:28:27
+ * @LastEditTime: 2021-09-13 11:37:54
+ * @LastEditors: Please set LastEditors
+ * @Description: In User Settings Edit
+ * @FilePath: /MedicalWisdomCheckSys/config/proxy.ts
+ */
+/**
+ * 在生产环境 代理是无法生效的,所以这里没有生产环境的配置
+ * -------------------------------
+ * The agent cannot take effect in the production environment
+ * so there is no configuration of the production environment
+ * For details, please see
+ * https://pro.ant.design/docs/deploy
+ */
+export default {
+  dev: {
+    '/api': {
+      target: 'http://112.124.59.133:8083/',
+      changeOrigin: true,
+      pathRewrite: { '^/api': '' },
+    },
+  },
+  mock: {
+    '/api/': {
+      target: 'http://localhost:8000',
+      changeOrigin: true,
+      pathRewrite: { '^': '' },
+    },
+  },
+  test: {
+    '/api/': {
+      target: 'https://preview.pro.ant.design',
+      changeOrigin: true,
+      pathRewrite: { '^': '' },
+    },
+  },
+  pre: {
+    '/api/': {
+      target: 'your pre url',
+      changeOrigin: true,
+      pathRewrite: { '^': '' },
+    },
+  },
+};

+ 61 - 0
config/routes.ts

@@ -0,0 +1,61 @@
+/*
+ * @Author: your name
+ * @Date: 2021-09-03 14:28:27
+ * @LastEditTime: 2021-09-14 10:27:17
+ * @LastEditors: Please set LastEditors
+ * @Description: In User Settings Edit
+ * @FilePath: /MedicalWisdomCheckSys/config/routes.ts
+ */
+export default [
+  {
+    path: '/user',
+    layout: false,
+    routes: [
+      {
+        path: '/user',
+        routes: [
+          {
+            name: 'login',
+            path: '/user/login',
+            component: './user/Login',
+          },
+        ],
+      },
+      {
+        component: './404',
+      },
+    ],
+  },
+  {
+    path: '/welcome',
+    name: 'welcome',
+    icon: 'smile',
+    component: './Welcome',
+  },
+  {
+    path: '/GradeHospitalAccreditation',
+    name: '等级医院评审',
+    icon: 'smile',
+    routes:[
+      {
+        path: '/GradeHospitalAccreditation/accreditationDetail',
+        name: '评审细则',
+        icon: 'smile',
+        component: './GradeHospitalAccreditation/accreditationDetail/index',
+      },
+      {
+        path: '/GradeHospitalAccreditation/articleManagement',
+        name: '条文管理',
+        icon: 'smile',
+        component: './GradeHospitalAccreditation/articleManagement/index',
+      },
+    ]
+  },
+  {
+    path: '/',
+    redirect: '/welcome',
+  },
+  {
+    component: './404',
+  },
+];

+ 10 - 0
jest.config.js

@@ -0,0 +1,10 @@
+module.exports = {
+  testURL: 'http://localhost:8000',
+  testEnvironment: './tests/PuppeteerEnvironment',
+  verbose: false,
+  extraSetupFiles: ['./tests/setupTests.js'],
+  globals: {
+    ANT_DESIGN_PRO_ONLY_DO_NOT_USE_IN_YOUR_PRODUCTION: false,
+    localStorage: null,
+  },
+};

+ 10 - 0
jsconfig.json

@@ -0,0 +1,10 @@
+{
+  "compilerOptions": {
+    "emitDecoratorMetadata": true,
+    "experimentalDecorators": true,
+    "baseUrl": ".",
+    "paths": {
+      "@/*": ["./src/*"]
+    }
+  }
+}

+ 104 - 0
mock/accreditationDetail.ts

@@ -0,0 +1,104 @@
+/*
+ * @Author: your name
+ * @Date: 2021-09-07 10:37:59
+ * @LastEditTime: 2021-09-13 19:13:28
+ * @LastEditors: Please set LastEditors
+ * @Description: In User Settings Edit
+ * @FilePath: /MedicalWisdomCheckSys/mock/accreditationDetail.ts
+ */
+
+
+// eslint-disable-next-line import/no-extraneous-dependencies
+import { Request, Response } from 'express';
+import moment from 'moment';
+
+
+
+
+function getList(req: Request, res: Response, u: string) {
+  const dataSource = [
+    {
+      title: 'parent 0',
+      id: '0-0',
+      code:'1.1.1',
+      isLeaf: false,
+      levelNum:1,
+      children: [
+        { 
+            title: 'leaf 0-0',
+            id: '0-0-0',
+            code:'1.1.1',
+            isLeaf: false,
+            levelNum:2,
+            children:[
+                { title: 'leaf 0-1-1-1', id: '0-0-1-1',code:'1.1.1', isLeaf: true,levelNum:3, },
+            ]
+        },
+        { title: 'leaf 0-1', id: '0-0-1',code:'1.1.1', isLeaf: true,levelNum:2, },
+      ],
+    },
+    {
+      title: 'parent 1',
+      id: '0-1',
+      code:'1.1.1',
+      isLeaf: false,
+      levelNum:1,
+      children: [
+        {title: 'leaf 1-0', id: '0-1-0',code:'1.1.1', isLeaf: true,levelNum:2,},
+        {title: 'leaf 1-1', id: '0-1-1',code:'1.1.1', isLeaf: true,levelNum:2, },
+      ],
+    },
+    {
+        title: 'parent 2',
+        id: '0-2',
+        code:'1.1.1',
+        isLeaf: true,
+        levelNum:1,
+    },
+  ];
+  const result = {
+    data:{
+        list:dataSource,
+        totalCount:dataSource.length,
+        current:1
+    },
+    success: true,
+    status:200,
+  };
+  setTimeout(()=>{
+    return res.json(result); 
+  },2000);
+  
+}
+
+const getRuleDetailList = (req: Request, res: Response, u: string)=>{
+  const result = {
+    data:{
+      leafDetails:[
+          {
+            evaluation:'A',
+            detail:[
+                {
+                  title:'aaaaaaaaaaaaaaaa'
+                },
+                {
+                  title:'bbbbbbbbbbbbbbb'
+                }
+            ]
+          }
+      ]
+    },
+    success: true,
+    status:200,
+  };
+  setTimeout(()=>{
+    return res.json(result); 
+  },2000);
+}
+
+
+
+export default {
+  'GET /api/pfm/reviewRules/getAllTree': getList,
+  'GET /api/pfm/reviewRules/getDetails': getRuleDetailList,
+};

+ 182 - 0
mock/articleManagement.ts

@@ -0,0 +1,182 @@
+/*
+ * @Author: your name
+ * @Date: 2021-09-14 14:40:58
+ * @LastEditTime: 2021-09-15 18:18:32
+ * @LastEditors: Please set LastEditors
+ * @Description: In User Settings Edit
+ * @FilePath: /MedicalWisdomCheckSys/mock/articleManagement.ts
+ */
+
+
+
+
+
+// eslint-disable-next-line import/no-extraneous-dependencies
+import { Request, Response } from 'express';
+
+
+
+function getList(req: Request, res: Response, u: string) {
+  const dataSource = [
+    {
+      title: 'parent 0',
+      id: '0-0',
+      code:'1.1.1',
+      isLeaf: false,
+      levelNum:1,
+      children: [
+        { 
+            title: 'leaf 0-0',
+            id: '0-0-0',
+            code:'1.1.1',
+            isLeaf: false,
+            levelNum:2,
+            children:[
+                { title: 'leaf 0-1-1-1', id: '0-0-1-1',code:'1.1.1', isLeaf: true,levelNum:3, },
+            ]
+        },
+        { title: 'leaf 0-1', id: '0-0-1',code:'1.1.1', isLeaf: true,levelNum:2, },
+      ],
+    },
+    {
+      title: 'parent 1',
+      id: '0-1',
+      code:'1.1.1',
+      isLeaf: false,
+      levelNum:1,
+      children: [
+        {title: 'leaf 1-0', id: '0-1-0',code:'1.1.1', isLeaf: true,levelNum:2,},
+        {title: 'leaf 1-1', id: '0-1-1',code:'1.1.1', isLeaf: true,levelNum:2, },
+      ],
+    },
+    {
+        title: 'parent 2',
+        id: '0-2',
+        code:'1.1.1',
+        isLeaf: true,
+        levelNum:1,
+    },
+  ];
+  const result = {
+    data:{
+        list:dataSource,
+        totalCount:dataSource.length,
+        current:1
+    },
+    success: true,
+    status:200,
+  };
+  setTimeout(()=>{
+    return res.json(result); 
+  },2000);
+  
+}
+
+const getRuleDetailList = (req: Request, res: Response, u: string)=>{
+
+  const result = {
+    data:{
+      ifLeaf:true,
+      page:{
+        pageSize:1,
+        totalCount:2,
+        totalPage:1,
+        current:1,
+        list:[
+            {
+              id:1,
+              numStr:'1.1.1.1',
+              name:'医院的功能、任务和定位明确,保持适度规模',
+              responsibilityDepartmentName:'单位',
+              responsibilityUserName:'张三',
+              selfEvaluation:'A',
+              targetEvaluation:'B',
+              systemEvaluation:'C',
+              siteEvaluation:'D'
+            },
+            {
+                id:2,
+                numStr:'1.1.1.2',
+                name:'医院的功能、任务和定位明确,保持适度规模',
+                responsibilityDepartmentName:'单位',
+                responsibilityUserName:'张三',
+                selfEvaluation:'A',
+                targetEvaluation:'B',
+                systemEvaluation:'C',
+                siteEvaluation:'D'
+              }
+        ]
+      },
+      leafData:{
+        id:2,
+        numStr:'四码',
+        name:'名称',
+        responsibilityDepartmentName:'责任部门',
+        responsibilityUserName:'责任人',
+        selfEvaluation:'A',
+        targetEvaluation:'B',
+        systemEvaluation:'C',
+        siteEvaluation:'D',
+        records:[
+            {
+                evaluation:'E',
+                details:[
+                    {
+                        userName:'用户名',
+                        fileName:'文件名',
+                        fileUrl:'文件链接',
+                        createTime:'2021-09-14',
+                        recordTypeName:'台帐类型'
+                    }
+                ]
+            }
+        ]
+      }
+    },
+    success: true,
+    status:200,
+  };
+  return res.json(result); 
+}
+
+
+const getDepartmentList = (req: Request, res: Response, u: string)=>{
+    const result = {
+        data:{
+            totalCount:0,
+            pageSize:1,
+            current:1,
+            list:[
+                {
+                    id:1,
+                    name:'单位名称',
+                    deptManagerId:'单位负责人的Id1',
+                    deptManagerName:'单位负责人的名字',
+                    delFlag:1,
+                    hiId:0,
+                    sort:1,
+                },
+                {
+                    id:1,
+                    name:'单位名称',
+                    deptManagerId:'单位负责人的Id2',
+                    deptManagerName:'单位负责人的名字',
+                    delFlag:1,
+                    hiId:0,
+                    sort:1,
+                }
+            ]
+        },
+        success: true,
+        status:200,
+      };
+      return res.json(result); 
+}
+
+
+
+export default {
+  'GET /api/pfm/reviewRules/getAllTree': getList,
+  'GET /api/pfm/reviewArticle/list': getRuleDetailList,
+  'GET /api/pfm/sysdepartment/getSysDepartmentList': getDepartmentList,
+};

+ 175 - 0
mock/listTableList.ts

@@ -0,0 +1,175 @@
+// eslint-disable-next-line import/no-extraneous-dependencies
+import { Request, Response } from 'express';
+import moment from 'moment';
+import { parse } from 'url';
+
+// mock tableListDataSource
+const genList = (current: number, pageSize: number) => {
+  const tableListDataSource: API.RuleListItem[] = [];
+
+  for (let i = 0; i < pageSize; i += 1) {
+    const index = (current - 1) * 10 + i;
+    tableListDataSource.push({
+      key: index,
+      disabled: i % 6 === 0,
+      href: 'https://ant.design',
+      avatar: [
+        'https://gw.alipayobjects.com/zos/rmsportal/eeHMaZBwmTvLdIwMfBpg.png',
+        'https://gw.alipayobjects.com/zos/rmsportal/udxAbMEhpwthVVcjLXik.png',
+      ][i % 2],
+      name: `TradeCode ${index}`,
+      owner: '曲丽丽',
+      desc: '这是一段描述',
+      callNo: Math.floor(Math.random() * 1000),
+      status: Math.floor(Math.random() * 10) % 4,
+      updatedAt: moment().format('YYYY-MM-DD'),
+      createdAt: moment().format('YYYY-MM-DD'),
+      progress: Math.ceil(Math.random() * 100),
+    });
+  }
+  tableListDataSource.reverse();
+  return tableListDataSource;
+};
+
+let tableListDataSource = genList(1, 100);
+
+function getRule(req: Request, res: Response, u: string) {
+  let realUrl = u;
+  if (!realUrl || Object.prototype.toString.call(realUrl) !== '[object String]') {
+    realUrl = req.url;
+  }
+  const { current = 1, pageSize = 10 } = req.query;
+  const params = parse(realUrl, true).query as unknown as API.PageParams &
+    API.RuleListItem & {
+      sorter: any;
+      filter: any;
+    };
+
+  let dataSource = [...tableListDataSource].slice(
+    ((current as number) - 1) * (pageSize as number),
+    (current as number) * (pageSize as number),
+  );
+  if (params.sorter) {
+    const sorter = JSON.parse(params.sorter);
+    dataSource = dataSource.sort((prev, next) => {
+      let sortNumber = 0;
+      Object.keys(sorter).forEach((key) => {
+        if (sorter[key] === 'descend') {
+          if (prev[key] - next[key] > 0) {
+            sortNumber += -1;
+          } else {
+            sortNumber += 1;
+          }
+          return;
+        }
+        if (prev[key] - next[key] > 0) {
+          sortNumber += 1;
+        } else {
+          sortNumber += -1;
+        }
+      });
+      return sortNumber;
+    });
+  }
+  if (params.filter) {
+    const filter = JSON.parse(params.filter as any) as {
+      [key: string]: string[];
+    };
+    if (Object.keys(filter).length > 0) {
+      dataSource = dataSource.filter((item) => {
+        return Object.keys(filter).some((key) => {
+          if (!filter[key]) {
+            return true;
+          }
+          if (filter[key].includes(`${item[key]}`)) {
+            return true;
+          }
+          return false;
+        });
+      });
+    }
+  }
+
+  if (params.name) {
+    dataSource = dataSource.filter((data) => data?.name?.includes(params.name || ''));
+  }
+  const result = {
+    data: dataSource,
+    total: tableListDataSource.length,
+    success: true,
+    pageSize,
+    current: parseInt(`${params.current}`, 10) || 1,
+  };
+
+  return res.json(result);
+}
+
+function postRule(req: Request, res: Response, u: string, b: Request) {
+  let realUrl = u;
+  if (!realUrl || Object.prototype.toString.call(realUrl) !== '[object String]') {
+    realUrl = req.url;
+  }
+
+  const body = (b && b.body) || req.body;
+  const { method, name, desc, key } = body;
+
+  switch (method) {
+    /* eslint no-case-declarations:0 */
+    case 'delete':
+      tableListDataSource = tableListDataSource.filter((item) => key.indexOf(item.key) === -1);
+      break;
+    case 'post':
+      (() => {
+        const i = Math.ceil(Math.random() * 10000);
+        const newRule: API.RuleListItem = {
+          key: tableListDataSource.length,
+          href: 'https://ant.design',
+          avatar: [
+            'https://gw.alipayobjects.com/zos/rmsportal/eeHMaZBwmTvLdIwMfBpg.png',
+            'https://gw.alipayobjects.com/zos/rmsportal/udxAbMEhpwthVVcjLXik.png',
+          ][i % 2],
+          name,
+          owner: '曲丽丽',
+          desc,
+          callNo: Math.floor(Math.random() * 1000),
+          status: Math.floor(Math.random() * 10) % 2,
+          updatedAt: moment().format('YYYY-MM-DD'),
+          createdAt: moment().format('YYYY-MM-DD'),
+          progress: Math.ceil(Math.random() * 100),
+        };
+        tableListDataSource.unshift(newRule);
+        return res.json(newRule);
+      })();
+      return;
+
+    case 'update':
+      (() => {
+        let newRule = {};
+        tableListDataSource = tableListDataSource.map((item) => {
+          if (item.key === key) {
+            newRule = { ...item, desc, name };
+            return { ...item, desc, name };
+          }
+          return item;
+        });
+        return res.json(newRule);
+      })();
+      return;
+    default:
+      break;
+  }
+
+  const result = {
+    list: tableListDataSource,
+    pagination: {
+      total: tableListDataSource.length,
+    },
+  };
+
+  res.json(result);
+}
+
+export default {
+  'GET /api/rule': getRule,
+  'POST /api/rule': postRule,
+};

+ 107 - 0
mock/notices.ts

@@ -0,0 +1,107 @@
+import { Request, Response } from 'express';
+
+const getNotices = (req: Request, res: Response) => {
+  res.json({
+    data: [
+      {
+        id: '000000001',
+        avatar: 'https://gw.alipayobjects.com/zos/rmsportal/ThXAXghbEsBCCSDihZxY.png',
+        title: '你收到了 14 份新周报',
+        datetime: '2017-08-09',
+        type: 'notification',
+      },
+      {
+        id: '000000002',
+        avatar: 'https://gw.alipayobjects.com/zos/rmsportal/OKJXDXrmkNshAMvwtvhu.png',
+        title: '你推荐的 曲妮妮 已通过第三轮面试',
+        datetime: '2017-08-08',
+        type: 'notification',
+      },
+      {
+        id: '000000003',
+        avatar: 'https://gw.alipayobjects.com/zos/rmsportal/kISTdvpyTAhtGxpovNWd.png',
+        title: '这种模板可以区分多种通知类型',
+        datetime: '2017-08-07',
+        read: true,
+        type: 'notification',
+      },
+      {
+        id: '000000004',
+        avatar: 'https://gw.alipayobjects.com/zos/rmsportal/GvqBnKhFgObvnSGkDsje.png',
+        title: '左侧图标用于区分不同的类型',
+        datetime: '2017-08-07',
+        type: 'notification',
+      },
+      {
+        id: '000000005',
+        avatar: 'https://gw.alipayobjects.com/zos/rmsportal/ThXAXghbEsBCCSDihZxY.png',
+        title: '内容不要超过两行字,超出时自动截断',
+        datetime: '2017-08-07',
+        type: 'notification',
+      },
+      {
+        id: '000000006',
+        avatar: 'https://gw.alipayobjects.com/zos/rmsportal/fcHMVNCjPOsbUGdEduuv.jpeg',
+        title: '曲丽丽 评论了你',
+        description: '描述信息描述信息描述信息',
+        datetime: '2017-08-07',
+        type: 'message',
+        clickClose: true,
+      },
+      {
+        id: '000000007',
+        avatar: 'https://gw.alipayobjects.com/zos/rmsportal/fcHMVNCjPOsbUGdEduuv.jpeg',
+        title: '朱偏右 回复了你',
+        description: '这种模板用于提醒谁与你发生了互动,左侧放『谁』的头像',
+        datetime: '2017-08-07',
+        type: 'message',
+        clickClose: true,
+      },
+      {
+        id: '000000008',
+        avatar: 'https://gw.alipayobjects.com/zos/rmsportal/fcHMVNCjPOsbUGdEduuv.jpeg',
+        title: '标题',
+        description: '这种模板用于提醒谁与你发生了互动,左侧放『谁』的头像',
+        datetime: '2017-08-07',
+        type: 'message',
+        clickClose: true,
+      },
+      {
+        id: '000000009',
+        title: '任务名称',
+        description: '任务需要在 2017-01-12 20:00 前启动',
+        extra: '未开始',
+        status: 'todo',
+        type: 'event',
+      },
+      {
+        id: '000000010',
+        title: '第三方紧急代码变更',
+        description: '冠霖提交于 2017-01-06,需在 2017-01-07 前完成代码变更任务',
+        extra: '马上到期',
+        status: 'urgent',
+        type: 'event',
+      },
+      {
+        id: '000000011',
+        title: '信息安全考试',
+        description: '指派竹尔于 2017-01-09 前完成更新并发布',
+        extra: '已耗时 8 天',
+        status: 'doing',
+        type: 'event',
+      },
+      {
+        id: '000000012',
+        title: 'ABCD 版本发布',
+        description: '冠霖提交于 2017-01-06,需在 2017-01-07 前完成代码变更任务',
+        extra: '进行中',
+        status: 'processing',
+        type: 'event',
+      },
+    ],
+  });
+};
+
+export default {
+  'GET /api/notices': getNotices,
+};

+ 5 - 0
mock/route.ts

@@ -0,0 +1,5 @@
+export default {
+  '/api/auth_routes': {
+    '/form/advanced-form': { authority: ['admin', 'user'] },
+  },
+};

+ 188 - 0
mock/user.ts

@@ -0,0 +1,188 @@
+import { Request, Response } from 'express';
+
+const waitTime = (time: number = 100) => {
+  return new Promise((resolve) => {
+    setTimeout(() => {
+      resolve(true);
+    }, time);
+  });
+};
+
+async function getFakeCaptcha(req: Request, res: Response) {
+  await waitTime(2000);
+  return res.json('captcha-xxx');
+}
+
+const { ANT_DESIGN_PRO_ONLY_DO_NOT_USE_IN_YOUR_PRODUCTION } = process.env;
+
+/**
+ * 当前用户的权限,如果为空代表没登录
+ * current user access, if is '', user need login
+ * 如果是 pro 的预览,默认是有权限的
+ */
+let access = ANT_DESIGN_PRO_ONLY_DO_NOT_USE_IN_YOUR_PRODUCTION === 'site' ? 'admin' : '';
+
+const getAccess = () => {
+  return access;
+};
+
+// 代码中会兼容本地 service mock 以及部署站点的静态数据
+export default {
+  // 支持值为 Object 和 Array
+  'GET /api/currentUser': (req: Request, res: Response) => {
+    if (!getAccess()) {
+      res.status(401).send({
+        data: {
+          isLogin: false,
+        },
+        errorCode: '401',
+        errorMessage: '请先登录!',
+        success: true,
+      });
+      return;
+    }
+    res.send({
+      success: true,
+      data: {
+        name: 'Serati Ma',
+        avatar: 'https://gw.alipayobjects.com/zos/antfincdn/XAosXuNZyF/BiazfanxmamNRoxxVxka.png',
+        userid: '00000001',
+        email: 'antdesign@alipay.com',
+        signature: '海纳百川,有容乃大',
+        title: '交互专家',
+        group: '蚂蚁金服-某某某事业群-某某平台部-某某技术部-UED',
+        tags: [
+          {
+            key: '0',
+            label: '很有想法的',
+          },
+          {
+            key: '1',
+            label: '专注设计',
+          },
+          {
+            key: '2',
+            label: '辣~',
+          },
+          {
+            key: '3',
+            label: '大长腿',
+          },
+          {
+            key: '4',
+            label: '川妹子',
+          },
+          {
+            key: '5',
+            label: '海纳百川',
+          },
+        ],
+        notifyCount: 12,
+        unreadCount: 11,
+        country: 'China',
+        access: getAccess(),
+        geographic: {
+          province: {
+            label: '浙江省',
+            key: '330000',
+          },
+          city: {
+            label: '杭州市',
+            key: '330100',
+          },
+        },
+        address: '西湖区工专路 77 号',
+        phone: '0752-268888888',
+      },
+    });
+  },
+  // GET POST 可省略
+  'GET /api/users': [
+    {
+      key: '1',
+      name: 'John Brown',
+      age: 32,
+      address: 'New York No. 1 Lake Park',
+    },
+    {
+      key: '2',
+      name: 'Jim Green',
+      age: 42,
+      address: 'London No. 1 Lake Park',
+    },
+    {
+      key: '3',
+      name: 'Joe Black',
+      age: 32,
+      address: 'Sidney No. 1 Lake Park',
+    },
+  ],
+  'POST /api/login': async (req: Request, res: Response) => {
+    const { password, username } = req.body;
+    await waitTime(2000);
+    if (password === '123' && username === 'admin') {
+      res.send({
+        status:200,
+        msg:'',
+        data:{
+          token:'mock',
+          name:'admin',
+          avatar:'https://gw.alipayobjects.com/zos/antfincdn/XAosXuNZyF/BiazfanxmamNRoxxVxka.png',
+          userId:27
+        }
+      });
+      return;
+    }
+
+    res.send({
+      status:300,
+      msg:'账号/密码错误!',
+      data:{}
+    });
+  },
+  'POST /api/login/outLogin': (req: Request, res: Response) => {
+    access = '';
+    res.send({ data: {}, success: true });
+  },
+  'POST /api/register': (req: Request, res: Response) => {
+    res.send({ status: 'ok', currentAuthority: 'user', success: true });
+  },
+  'GET /api/500': (req: Request, res: Response) => {
+    res.status(500).send({
+      timestamp: 1513932555104,
+      status: 500,
+      error: 'error',
+      message: 'error',
+      path: '/base/category/list',
+    });
+  },
+  'GET /api/404': (req: Request, res: Response) => {
+    res.status(404).send({
+      timestamp: 1513932643431,
+      status: 404,
+      error: 'Not Found',
+      message: 'No message available',
+      path: '/base/category/list/2121212',
+    });
+  },
+  'GET /api/403': (req: Request, res: Response) => {
+    res.status(403).send({
+      timestamp: 1513932555104,
+      status: 403,
+      error: 'Forbidden',
+      message: 'Forbidden',
+      path: '/base/category/list',
+    });
+  },
+  'GET /api/401': (req: Request, res: Response) => {
+    res.status(401).send({
+      timestamp: 1513932555104,
+      status: 401,
+      error: 'Unauthorized',
+      message: 'Unauthorized',
+      path: '/base/category/list',
+    });
+  },
+
+  'GET  /api/login/captcha': getFakeCaptcha,
+};

+ 113 - 0
package.json

@@ -0,0 +1,113 @@
+{
+  "name": "MedicalWisdomCheckSys",
+  "version": "0.0.1",
+  "private": true,
+  "author": {
+    "name": "xieyunhui"
+  },
+  "description": "",
+  "scripts": {
+    "analyze": "cross-env ANALYZE=1 umi build",
+    "build": "umi build",
+    "deploy": "npm run build && npm run gh-pages",
+    "dev": "npm run start:dev",
+    "gh-pages": "gh-pages -d dist",
+    "i18n-remove": "pro i18n-remove --locale=zh-CN --write",
+    "postinstall": "umi g tmp",
+    "lint": "umi g tmp && npm run lint:js && npm run lint:style && npm run lint:prettier && npm run tsc",
+    "lint-staged": "lint-staged",
+    "lint-staged:js": "eslint --ext .js,.jsx,.ts,.tsx ",
+    "lint:fix": "eslint --fix --cache --ext .js,.jsx,.ts,.tsx --format=pretty ./src && npm run lint:style",
+    "lint:js": "eslint --cache --ext .js,.jsx,.ts,.tsx --format=pretty ./src",
+    "lint:prettier": "prettier -c --write \"src/**/*\" --end-of-line auto",
+    "lint:style": "stylelint --fix \"src/**/*.less\" --syntax less",
+    "openapi": "umi openapi",
+    "precommit": "lint-staged",
+    "prettier": "prettier -c --write \"src/**/*\"",
+    "start": "cross-env UMI_ENV=dev umi dev",
+    "start:dev": "cross-env REACT_APP_ENV=dev MOCK=none UMI_ENV=dev umi dev",
+    "start:mock": "cross-env REACT_APP_ENV=mock UMI_ENV=dev umi dev",
+    "start:no-mock": "cross-env MOCK=none UMI_ENV=dev umi dev",
+    "start:no-ui": "cross-env UMI_UI=none UMI_ENV=dev umi dev",
+    "start:pre": "cross-env REACT_APP_ENV=pre UMI_ENV=dev umi dev",
+    "start:test": "cross-env REACT_APP_ENV=test MOCK=none UMI_ENV=dev umi dev",
+    "pretest": "node ./tests/beforeTest",
+    "test": "umi test",
+    "test:all": "node ./tests/run-tests.js",
+    "test:component": "umi test ./src/components",
+    "serve": "umi-serve",
+    "tsc": "tsc --noEmit"
+  },
+  "lint-staged": {
+    "**/*.less": "stylelint --syntax less",
+    "**/*.{js,jsx,ts,tsx}": "npm run lint-staged:js",
+    "**/*.{js,jsx,tsx,ts,less,md,json}": [
+      "prettier --write"
+    ]
+  },
+  "browserslist": [
+    "> 1%",
+    "last 2 versions",
+    "not ie <= 10"
+  ],
+  "dependencies": {
+    "@ant-design/icons": "^4.5.0",
+    "@ant-design/pro-card": "^1.14.17",
+    "@ant-design/pro-descriptions": "^1.6.8",
+    "@ant-design/pro-form": "^1.18.3",
+    "@ant-design/pro-layout": "^6.15.3",
+    "@ant-design/pro-table": "^2.30.8",
+    "@umijs/route-utils": "^1.0.36",
+    "antd": "^4.14.0",
+    "classnames": "^2.2.6",
+    "lodash": "^4.17.11",
+    "moment": "^2.25.3",
+    "omit.js": "^2.0.2",
+    "react": "^17.0.0",
+    "react-dev-inspector": "^1.1.1",
+    "react-dom": "^17.0.0",
+    "react-helmet-async": "^1.0.4",
+    "umi": "^3.5.0",
+    "umi-serve": "^1.9.10"
+  },
+  "devDependencies": {
+    "@ant-design/pro-cli": "^2.0.2",
+    "@types/express": "^4.17.0",
+    "@types/history": "^4.7.2",
+    "@types/jest": "^26.0.0",
+    "@types/lodash": "^4.14.144",
+    "@types/react": "^17.0.0",
+    "@types/react-dom": "^17.0.0",
+    "@types/react-helmet": "^6.1.0",
+    "@umijs/fabric": "^2.6.2",
+    "@umijs/openapi": "^1.1.14",
+    "@umijs/plugin-blocks": "^2.0.5",
+    "@umijs/plugin-esbuild": "^1.0.1",
+    "@umijs/plugin-openapi": "^1.2.0",
+    "@umijs/preset-ant-design-pro": "^1.2.0",
+    "@umijs/preset-dumi": "^1.1.7",
+    "@umijs/preset-react": "^1.8.17",
+    "@umijs/yorkie": "^2.0.3",
+    "carlo": "^0.9.46",
+    "cross-env": "^7.0.0",
+    "cross-port-killer": "^1.1.1",
+    "detect-installer": "^1.0.1",
+    "enzyme": "^3.11.0",
+    "eslint": "^7.1.0",
+    "express": "^4.17.1",
+    "gh-pages": "^3.0.0",
+    "jsdom-global": "^3.0.2",
+    "lint-staged": "^10.0.0",
+    "mockjs": "^1.0.1-beta3",
+    "prettier": "^2.3.2",
+    "puppeteer-core": "^8.0.0",
+    "stylelint": "^13.0.0",
+    "typescript": "^4.2.2"
+  },
+  "engines": {
+    "node": ">=10.0.0"
+  },
+  "gitHooks": {
+    "commit-msg": "fabric verify-commit"
+  }
+}

+ 1 - 0
public/CNAME

@@ -0,0 +1 @@
+preview.pro.ant.design

BIN
public/favicon.ico


BIN
public/icons/icon-128x128.png


BIN
public/icons/icon-192x192.png


BIN
public/icons/icon-512x512.png


BIN
public/logo.png


Разница между файлами не показана из-за своего большого размера
+ 0 - 0
public/logo.svg


+ 5 - 0
public/pro_icon.svg

@@ -0,0 +1,5 @@
+<svg width="42" height="42" xmlns="http://www.w3.org/2000/svg">
+ <g>
+  <path fill="#070707" d="m6.717392,13.773912l5.6,0c2.8,0 4.7,1.9 4.7,4.7c0,2.8 -2,4.7 -4.9,4.7l-2.5,0l0,4.3l-2.9,0l0,-13.7zm2.9,2.2l0,4.9l1.9,0c1.6,0 2.6,-0.9 2.6,-2.4c0,-1.6 -0.9,-2.4 -2.6,-2.4l-1.9,0l0,-0.1zm8.9,11.5l2.7,0l0,-5.7c0,-1.4 0.8,-2.3 2.2,-2.3c0.4,0 0.8,0.1 1,0.2l0,-2.4c-0.2,-0.1 -0.5,-0.1 -0.8,-0.1c-1.2,0 -2.1,0.7 -2.4,2l-0.1,0l0,-1.9l-2.7,0l0,10.2l0.1,0zm11.7,0.1c-3.1,0 -5,-2 -5,-5.3c0,-3.3 2,-5.3 5,-5.3s5,2 5,5.3c0,3.4 -1.9,5.3 -5,5.3zm0,-2.1c1.4,0 2.2,-1.1 2.2,-3.2c0,-2 -0.8,-3.2 -2.2,-3.2c-1.4,0 -2.2,1.2 -2.2,3.2c0,2.1 0.8,3.2 2.2,3.2z" class="st0" id="Ant-Design-Pro"/>
+ </g>
+</svg>

+ 9 - 0
src/access.ts

@@ -0,0 +1,9 @@
+/**
+ * @see https://umijs.org/zh-CN/plugins/plugin-access
+ * */
+export default function access(initialState: { currentUser?: API.CurrentUser | undefined }) {
+  const { currentUser } = initialState || {};
+  return {
+    canAdmin: currentUser && currentUser.access === 'admin',
+  };
+}

+ 189 - 0
src/app.tsx

@@ -0,0 +1,189 @@
+/*
+ * @Author: your name
+ * @Date: 2021-09-03 14:28:27
+ * @LastEditTime: 2021-09-14 09:40:44
+ * @LastEditors: Please set LastEditors
+ * @Description: In User Settings Edit
+ * @FilePath: /MedicalWisdomCheckSys/src/app.tsx
+ */
+import type { Settings as LayoutSettings } from '@ant-design/pro-layout';
+import { PageLoading } from '@ant-design/pro-layout';
+import type { RunTimeLayoutConfig } from 'umi';
+import { history, Link } from 'umi';
+import { notification, Modal, message } from 'antd';
+
+import RightContent from '@/components/RightContent';
+// import Footer from '@/components/Footer';
+// import { currentUser as queryCurrentUser } from './services/ant-design-pro/api';
+import { BookOutlined, LinkOutlined } from '@ant-design/icons';
+
+import logoIcon from '../public/logo.png';
+
+const isDev = process.env.NODE_ENV === 'development';
+
+const loginPath = '/user/login';
+
+
+/** 获取用户信息比较慢的时候会展示一个 loading */
+export const initialStateConfig = {
+  loading: <PageLoading />,
+};
+
+/**
+ * @see  https://umijs.org/zh-CN/plugins/plugin-initial-state
+ * */
+export async function getInitialState(): Promise<{
+  settings?: Partial<LayoutSettings>;
+  currentUser?: API.CurrentUserData;
+  logo?:String;
+  clearUserData?:() => Promise<boolean>;  //清空用户数据
+  fetchUserInfo?: () => Promise<API.CurrentUser | undefined>;
+}> {
+  const fetchUserInfo = async () => {
+    try {
+      const userData = localStorage.getItem('userData');
+      if(userData){
+        return JSON.parse(userData);
+      }
+      throw Error;
+    } catch (error) {
+      history.push(loginPath);
+    }
+    return undefined;
+  };
+
+  const clearUserData = ()=>{
+      localStorage.removeItem('userData');
+      return Promise.resolve(true);
+  };
+  // 如果是登录页面,不执行
+  if (history.location.pathname !== loginPath) {
+    const currentUser = await fetchUserInfo();
+    return {
+      fetchUserInfo,
+      currentUser,
+      logo:logoIcon,
+      clearUserData,
+      settings: {
+         navTheme:'dark',
+         layout:'side'
+      },
+    };
+  }
+
+  return {
+    fetchUserInfo,
+    settings: {},
+  };
+}
+
+
+type RequestOptionsInit = {
+     [propsName:string]:any
+}
+
+const authHeaderInterceptor = (url: string, options: RequestOptionsInit) => {
+  
+  const userData = localStorage.getItem('userData');
+  let authHeader = {token:''};
+  
+
+  if(userData){
+       const {token}:API.CurrentUserData = JSON.parse(userData);
+       authHeader.token = token;
+  }
+  return {
+    url: `${url}`,
+    options: { ...options, interceptors: true, headers: authHeader },
+  };
+
+};
+
+
+const responseInterceptors = async (response: Response, options: RequestOptionsInit) => {
+  console.log({response});
+  try {
+    const {status} = response;
+    if(status == 200){
+        //网络请求成功
+        const _response = await response.clone().json();
+        const { status: dataStatus,errorMessage,data} = _response;
+        if(dataStatus == 200){
+             //接口请求成功
+             if(data){
+              return data;
+             }
+             
+             return true;
+        }else {
+             //接口请求不成功
+             notification.error({
+              message:errorMessage
+             });
+        }
+        
+    }else {
+      //网络请求失败
+       throw Error;
+    }
+  }catch(error){
+      console.log({error});    
+  }
+  
+  return response;
+};
+
+
+type RequestConfig = {
+  [propsName:string]:any
+}
+
+export const request: RequestConfig = {
+  // 新增自动添加AccessToken的请求前拦截器
+  errorHandler: (error:any) => {
+    console.log({ error });
+    throw error;
+  },
+  requestInterceptors: [authHeaderInterceptor],
+  responseInterceptors: [responseInterceptors],
+};
+
+
+
+
+
+// ProLayout 支持的api https://procomponents.ant.design/components/layout
+export const layout: RunTimeLayoutConfig = ({ initialState }) => {
+  return {
+    logo:initialState?.logo,
+    rightContentRender: () => <RightContent />,
+    disableContentMargin: false,
+    waterMarkProps: {
+      // content: initialState?.currentUser?.name,
+    },
+    // footerRender: () => <Footer />,
+    onPageChange: () => {
+      const { location } = history;
+      // 如果没有登录,重定向到 login
+      if (!initialState?.currentUser && location.pathname !== loginPath) {
+        history.push(loginPath);
+      }
+    },
+    links: isDev
+      ? [
+          <Link to="/umi/plugin/openapi" target="_blank">
+            <LinkOutlined />
+            <span>OpenAPI 文档</span>
+          </Link>,
+          <Link to="/~docs">
+            <BookOutlined />
+            <span>业务组件文档</span>
+          </Link>,
+        ]
+      : [],
+    menuHeaderRender: undefined,
+    // 自定义 403 页面
+    // unAccessible: <div>unAccessible</div>,
+    ...initialState?.settings,
+  };
+};

+ 29 - 0
src/components/Footer/index.tsx

@@ -0,0 +1,29 @@
+/*
+ * @Author: your name
+ * @Date: 2021-09-03 14:28:27
+ * @LastEditTime: 2021-09-03 14:51:18
+ * @LastEditors: Please set LastEditors
+ * @Description: In User Settings Edit
+ * @FilePath: /MedicalWisdomCheckSys/src/components/Footer/index.tsx
+ */
+
+import { DefaultFooter } from '@ant-design/pro-layout';
+
+export default () => {
+
+  const currentYear = new Date().getFullYear();
+
+  return (
+    <DefaultFooter
+      copyright={`${currentYear} 康程智医技术出品`}
+      links={[
+        {
+          key: '',
+          title: 'v0.0.1',
+          href: '',
+          blankTarget: true,
+        }
+      ]}
+    />
+  );
+};

+ 16 - 0
src/components/HeaderDropdown/index.less

@@ -0,0 +1,16 @@
+@import '~antd/es/style/themes/default.less';
+
+.container > * {
+  background-color: @popover-bg;
+  border-radius: 4px;
+  box-shadow: @shadow-1-down;
+}
+
+@media screen and (max-width: @screen-xs) {
+  .container {
+    width: 100% !important;
+  }
+  .container > * {
+    border-radius: 0 !important;
+  }
+}

+ 17 - 0
src/components/HeaderDropdown/index.tsx

@@ -0,0 +1,17 @@
+import type { DropDownProps } from 'antd/es/dropdown';
+import { Dropdown } from 'antd';
+import React from 'react';
+import classNames from 'classnames';
+import styles from './index.less';
+
+export type HeaderDropdownProps = {
+  overlayClassName?: string;
+  overlay: React.ReactNode | (() => React.ReactNode) | any;
+  placement?: 'bottomLeft' | 'bottomRight' | 'topLeft' | 'topCenter' | 'topRight' | 'bottomCenter';
+} & Omit<DropDownProps, 'overlay'>;
+
+const HeaderDropdown: React.FC<HeaderDropdownProps> = ({ overlayClassName: cls, ...restProps }) => (
+  <Dropdown overlayClassName={classNames(styles.container, cls)} {...restProps} />
+);
+
+export default HeaderDropdown;

+ 25 - 0
src/components/HeaderSearch/index.less

@@ -0,0 +1,25 @@
+@import '~antd/es/style/themes/default.less';
+
+.headerSearch {
+  display: inline-flex;
+  align-items: center;
+  .input {
+    width: 0;
+    min-width: 0;
+    overflow: hidden;
+    background: transparent;
+    border-radius: 0;
+    transition: width 0.3s, margin-left 0.3s;
+    :global(.ant-select-selection) {
+      background: transparent;
+    }
+    input {
+      box-shadow: none !important;
+    }
+
+    &.show {
+      width: 210px;
+      margin-left: 8px;
+    }
+  }
+}

+ 101 - 0
src/components/HeaderSearch/index.tsx

@@ -0,0 +1,101 @@
+import { SearchOutlined } from '@ant-design/icons';
+import { AutoComplete, Input } from 'antd';
+import useMergedState from 'rc-util/es/hooks/useMergedState';
+import type { AutoCompleteProps } from 'antd/es/auto-complete';
+import React, { useRef } from 'react';
+
+import classNames from 'classnames';
+import styles from './index.less';
+
+export type HeaderSearchProps = {
+  onSearch?: (value?: string) => void;
+  onChange?: (value?: string) => void;
+  onVisibleChange?: (b: boolean) => void;
+  className?: string;
+  placeholder?: string;
+  options: AutoCompleteProps['options'];
+  defaultVisible?: boolean;
+  visible?: boolean;
+  defaultValue?: string;
+  value?: string;
+};
+
+const HeaderSearch: React.FC<HeaderSearchProps> = (props) => {
+  const {
+    className,
+    defaultValue,
+    onVisibleChange,
+    placeholder,
+    visible,
+    defaultVisible,
+    ...restProps
+  } = props;
+
+  const inputRef = useRef<Input | null>(null);
+
+  const [value, setValue] = useMergedState<string | undefined>(defaultValue, {
+    value: props.value,
+    onChange: props.onChange,
+  });
+
+  const [searchMode, setSearchMode] = useMergedState(defaultVisible ?? false, {
+    value: props.visible,
+    onChange: onVisibleChange,
+  });
+
+  const inputClass = classNames(styles.input, {
+    [styles.show]: searchMode,
+  });
+  return (
+    <div
+      className={classNames(className, styles.headerSearch)}
+      onClick={() => {
+        setSearchMode(true);
+        if (searchMode && inputRef.current) {
+          inputRef.current.focus();
+        }
+      }}
+      onTransitionEnd={({ propertyName }) => {
+        if (propertyName === 'width' && !searchMode) {
+          if (onVisibleChange) {
+            onVisibleChange(searchMode);
+          }
+        }
+      }}
+    >
+      <SearchOutlined
+        key="Icon"
+        style={{
+          cursor: 'pointer',
+        }}
+      />
+      <AutoComplete
+        key="AutoComplete"
+        className={inputClass}
+        value={value}
+        options={restProps.options}
+        onChange={setValue}
+      >
+        <Input
+          size="small"
+          ref={inputRef}
+          defaultValue={defaultValue}
+          aria-label={placeholder}
+          placeholder={placeholder}
+          onKeyDown={(e) => {
+            if (e.key === 'Enter') {
+              if (restProps.onSearch) {
+                restProps.onSearch(value);
+              }
+            }
+          }}
+          onBlur={() => {
+            setSearchMode(false);
+          }}
+        />
+      </AutoComplete>
+    </div>
+  );
+};
+
+export default HeaderSearch;

+ 33 - 0
src/components/MccsClickableTabs/index.less

@@ -0,0 +1,33 @@
+
+
+
+.wrap {
+    display: flex;
+    width: 100%;
+    flex-direction: row;
+    justify-content:space-evenly;
+    flex-wrap: wrap;
+    align-items: center;
+    .tab {
+        width: 140px;
+        height: 28px;
+        cursor: pointer;
+        text-align: center;
+        line-height: 28px;
+        background: #F7F8FA;
+        border-radius: 14px;
+        font-size: 14px;
+        font-family: SourceHanSansCN-Normal, SourceHanSansCN;
+        font-weight: 400;
+        color: #525866;
+        // margin-right:16px;
+        margin-bottom: 16px;
+
+        &.on {
+            color: #3377FF;
+            background: #EBF1FF;
+        }
+
+        
+    }
+}

+ 44 - 0
src/components/MccsClickableTabs/index.tsx

@@ -0,0 +1,44 @@
+/*
+ * @Author: your name
+ * @Date: 2021-09-15 18:38:41
+ * @LastEditTime: 2021-09-15 19:20:22
+ * @LastEditors: Please set LastEditors
+ * @Description: In User Settings Edit
+ * @FilePath: /MedicalWisdomCheckSys/src/components/MccsClickableTabs/index.tsx
+ */
+
+
+
+import React,{useState,useEffect} from 'react'
+
+import './index.less';
+
+const MccsClickableTabs:React.FC<MccsClickableTabs.propsType> = (props)=>{
+    const [list,setList] = useState<MccsClickableTabs.TabType[]>([]);
+    const [activedIndex,setActivedIndex] = useState(-1);
+    const {tabClickHandle} = props;
+
+    const clickHandle = (item:MccsClickableTabs.TabType,index:number)=>{
+        tabClickHandle&&tabClickHandle(item);
+        setActivedIndex(index);
+    }
+
+    useEffect(()=>{
+        const {data=[]} = props;
+        setList(data);
+    },[props]);
+
+    return (
+        <div className='wrap'>
+                {
+                   list.map((item,index)=>{
+                        return (
+                            <div className={activedIndex==index?'tab on':'tab'} key={index} onClick={()=>clickHandle(item,index)}>{item.label}</div>
+                        )
+                   })
+                }
+        </div>
+    )
+}
+
+export default MccsClickableTabs

+ 21 - 0
src/components/MccsClickableTabs/typings.d.ts

@@ -0,0 +1,21 @@
+/*
+ * @Author: your name
+ * @Date: 2021-09-15 18:39:49
+ * @LastEditTime: 2021-09-15 19:03:12
+ * @LastEditors: Please set LastEditors
+ * @Description: In User Settings Edit
+ * @FilePath: /MedicalWisdomCheckSys/src/components/MccsClickableTabs/typings.d.ts
+ */
+
+
+declare namespace MccsClickableTabs {
+      type propsType = {
+        data:TabType[],
+        tabClickHandle?:(data:TabType)=>{}
+      }
+
+      type TabType = {
+          label:string,
+          value:string|number
+      }
+}

+ 0 - 0
src/components/MccsDrawerForm/index.less


+ 32 - 0
src/components/MccsDrawerForm/index.tsx

@@ -0,0 +1,32 @@
+/*
+ * @Author: your name
+ * @Date: 2021-09-15 16:25:53
+ * @LastEditTime: 2021-09-15 16:59:06
+ * @LastEditors: Please set LastEditors
+ * @Description: In User Settings Edit
+ * @FilePath: /MedicalWisdomCheckSys/src/components/MccsDrawerForm/index.tsx
+ */
+
+
+
+import React,{} from 'react'
+import {
+    DrawerForm,
+  } from '@ant-design/pro-form';
+
+const MccsDrawerForm:React.FC<MccsDrawerForm.propsType> = (props)=>{
+    const {children,...rest} = props;
+    return (
+        <DrawerForm
+             drawerProps={{
+                 
+                destroyOnClose:true
+             }}
+             {...rest}
+        >
+            {children}
+        </DrawerForm>
+    )
+}
+
+export default MccsDrawerForm

+ 15 - 0
src/components/MccsDrawerForm/typings.d.ts

@@ -0,0 +1,15 @@
+/*
+ * @Author: your name
+ * @Date: 2021-09-15 16:26:10
+ * @LastEditTime: 2021-09-15 16:32:02
+ * @LastEditors: Please set LastEditors
+ * @Description: In User Settings Edit
+ * @FilePath: /MedicalWisdomCheckSys/src/components/MccsDrawerForm/typings.d.ts
+ */
+
+
+declare namespace MccsDrawerForm {
+     type propsType = {
+          [propsName:string]:any
+     }
+}

+ 12 - 0
src/components/MccsFileTree/README.md

@@ -0,0 +1,12 @@
+<!--
+ * @Author: your name
+ * @Date: 2021-09-14 11:03:43
+ * @LastEditTime: 2021-09-14 11:03:43
+ * @LastEditors: Please set LastEditors
+ * @Description: In User Settings Edit
+ * @FilePath: /MedicalWisdomCheckSys/src/components/MccsFileTree/README.md
+-->
+
+
+
+####更新中。。。。

+ 79 - 0
src/components/MccsFileTree/components/DirectoryTree/index.less

@@ -0,0 +1,79 @@
+
+/* the tree node's style */
+.treeView {
+    overflow-y: hidden;
+  }
+  
+  .treeViewItem {
+     display: flex;
+     flex-direction: row;
+     justify-content: flex-start;
+     align-items: center;
+     height:32px;
+     cursor: pointer;
+     padding: 0 16px;
+     padding-right: 0;
+     border-radius: 4px;
+     .inner {
+        display: flex;
+        flex-direction: row;
+        justify-content: flex-start;
+        align-items: center;
+        width: 92%;
+        .node {
+            width: 90%;
+            overflow:hidden; //超出的文本隐藏
+            text-overflow:ellipsis; //溢出用省略号显示
+            white-space:nowrap; //溢出不换行
+        }
+     }
+     &.actived {
+         color: @primary-color;
+         background-color:rgba(0, 82, 142,0.2);
+     }
+  }
+  
+  /* style for the children nodes container */
+  .treeViewChildren {
+    margin-left: 16px;
+  }
+  
+  .treeViewChildrenCollapsed {
+    height: 0px;
+  }
+  
+  .treeViewArrow {
+    cursor: pointer;
+    height:18px;
+    margin-right: 6px;
+    display: inline-block;
+    -webkit-user-select: none;
+    -moz-user-select: none;
+    -ms-user-select: none;
+    user-select: none;
+    font-size: 0;
+  }
+  
+  .treeViewArrow:after {
+    content: '';
+    display: inline-block;
+    background: url('https://i.postimg.cc/DfLBgJng/image.png') no-repeat;/*兼容没测*/
+    background-size: 100% 100%;
+    width: 18px;
+    height:18px;
+    line-height: 18px;
+    position: relative;
+    // top:4px;
+  }
+  
+  /* rotate the triangle to close it */
+  .treeViewArrowCollapsed:after {
+    content: '';
+    display: inline-block;
+    background: url('https://i.postimg.cc/gkp4Bz0P/image.png') no-repeat;/*兼容没测*/
+    background-size: 100% 100%;
+    width: 18px;
+    height:18px;
+    position: relative;
+    // top:4px;
+  }

+ 116 - 0
src/components/MccsFileTree/components/DirectoryTree/index.tsx

@@ -0,0 +1,116 @@
+/*
+ * @Author: your name
+ * @Date: 2021-09-07 16:40:33
+ * @LastEditTime: 2021-09-13 14:52:24
+ * @LastEditors: Please set LastEditors
+ * @Description: In User Settings Edit
+ * @FilePath: /MedicalWisdomCheckSys/src/components/MccsFileTree/components/directoryTree.tsx
+ */
+import React, { useState,useEffect } from 'react'
+import { ReactNode } from 'react';
+import './index.less'
+
+
+
+
+type DirectoryTreeProps = {
+
+    defaultCollapsed?:boolean,
+    nodeLabel:ReactNode,
+    className?:string,
+    itemClassName?:string,
+    childrenClassName?:string,
+    treeViewClassName?:string,
+    onClick?:(args:object)=>void,
+    [propName: string]: any
+}
+
+type childTree = {
+    title: string,
+    id: string,
+    isLeaf:boolean,
+    code:string,
+    children?: childTree[],
+    [propName: string]: any;
+}
+
+const DirectoryTree: React.FC<DirectoryTreeProps> = (props) => {
+
+    
+    const {
+        className = '',
+        itemClassName = '',
+        treeViewClassName = '',
+        childrenClassName = '',
+        nodeLabel,
+        children,
+        defaultCollapsed,
+        currentActivedIndex,
+        setcurrentActivedIndex,
+        isLeaf,
+        action,
+        ...rest
+    } = props;
+    
+    //是否展开   true合上反之展开
+    const [collapsed, setCollapsed] = useState(defaultCollapsed);
+    
+
+    let arrowClassName = 'treeViewArrow';
+
+    let containerClassName = 'treeViewChildren';
+
+    if (collapsed) {
+        arrowClassName += ' treeViewArrowCollapsed';
+        containerClassName += ' treeViewChildrenCollapsed';
+    }
+
+
+    const handleClick = (args:childTree)=>{
+        setcurrentActivedIndex(args.id);
+        setCollapsed(!collapsed)
+        if (rest.onClick) {
+            rest.onClick(args);
+        }
+    }
+    
+    const arrow = (
+        <div
+            className={className + ' ' + arrowClassName}
+        />
+    );
+    
+    useEffect(() => {
+        //当设置默认展开项时触发
+        if(props.currentActivedIndex==props.id){
+            const {title,id,isLeaf=false,code} = props;
+            handleClick({title,id,isLeaf,code});
+        }
+    }, [])
+
+    return (
+        <div className={'treeView ' + treeViewClassName}>
+            <div className={props.currentActivedIndex==props.id?'treeViewItem actived':'treeViewItem'} onClick={()=>handleClick({
+                  title:props.title,
+                  id:props.id,
+                  isLeaf:false,
+                  code:props.code,
+            })}>
+                <div className='inner'>
+                    {arrow}
+                    {nodeLabel}
+                </div>
+                
+                {/**
+                 *  点击时展示
+                 */}
+                { props.currentActivedIndex==props.id&&action}
+            </div>
+            <div className={containerClassName + ' ' + childrenClassName}>
+                {collapsed ? null : children}
+            </div>
+        </div>
+    )
+}
+
+export default DirectoryTree;

BIN
src/components/MccsFileTree/images/file.jpg


BIN
src/components/MccsFileTree/images/folder.jpg


+ 59 - 0
src/components/MccsFileTree/index.less

@@ -0,0 +1,59 @@
+.searchBar {
+  display: flex;
+  flex-direction: row;
+  margin-bottom: 18px;
+  .add {
+    display: flex;
+    justify-content: center;
+    align-items: center;
+    width: 32px;
+    height: 32px;
+    background: #FFFFFF;
+    border-radius: 4px;
+    margin-right: 6px;
+    cursor: pointer;
+    border: 1px solid #DADEE6;
+  }
+  .inputArea {
+    border-radius: 4px;
+  }
+}
+
+.spinWrap {
+  display: flex;
+  width: 100%;
+  padding-top: 200px;
+  justify-content: center;
+  align-items: center;
+}
+
+.action {
+    display: flex;
+    flex-direction: row;
+    justify-content: center;
+    align-items: center;
+    margin-right: 5px;
+}
+
+.treeNode {
+    display: flex;
+    width: 100%;
+    height:32px;
+    flex-direction: row;
+    justify-content: flex-start;
+    align-items: center;
+    cursor: pointer;
+    padding: 0 16px;
+    padding-right: 0;
+    border-radius: 4px;
+    .treeNodeInner {
+      width: 88%;
+      overflow:hidden; //超出的文本隐藏
+      text-overflow:ellipsis; //溢出用省略号显示
+      white-space:nowrap; //溢出不换行
+    }
+    &.actived {
+      color: @primary-color;
+      background-color:rgba(0, 82, 142,0.3);
+    }
+}

+ 153 - 0
src/components/MccsFileTree/index.tsx

@@ -0,0 +1,153 @@
+/*
+ * @Author: your name
+ * @Date: 2021-09-06 10:28:12
+ * @LastEditTime: 2021-09-14 16:36:03
+ * @LastEditors: Please set LastEditors
+ * @Description: In User Settings Edit
+ * @FilePath: /MedicalWisdomCheckSys/src/components/MccsFileTree/index.tsx
+ */
+
+
+import React, { ReactNode, useEffect, useState,MouseEvent} from 'react'
+import { Input, Spin,Image } from 'antd';
+import { PlusOutlined } from '@ant-design/icons';
+import DirectoryTree from './components/DirectoryTree';
+import './index.less'
+
+
+// const { DirectoryTree } = Tree;
+const { Search } = Input;
+
+//树形文件组件
+
+
+
+enum Types {'add','del','edit','search'};
+
+type TypeVals = keyof typeof Types;
+
+type MccsFileTreeProps = {
+  onSelectHandle?: (data:MccsFileTree.childTree) => void, //选中回调
+  actionHandle?:({type,data}:{type:string,data:MccsFileTree.childTree}) => void, //操作回调,包括编辑/删除/新增/搜索
+  searchHandle?:(val:any)=>void,  
+  switcherIcon?: ReactNode,
+  treeData: MccsFileTree.childTree[]|[],
+  defaultSelected?:string,  //传id
+  formContent?:ReactNode,
+  editable?:boolean
+}
+
+  const MccsFileTree: React.FC<MccsFileTreeProps> = (props) => {
+  const {treeData,onSelectHandle,actionHandle,defaultSelected,searchHandle,editable} = props;
+  const [isLoading, setIsLoading] = useState(true);
+  //当前选中的
+  const [currentActivedIndex, setcurrentActivedIndex] = useState(defaultSelected?defaultSelected:'');
+
+  const actionFunc = (event:MouseEvent,type:TypeVals,data:MccsFileTree.childTree)=>{
+       event?.stopPropagation();
+       actionHandle&&actionHandle({type,data:data});
+  }
+
+  //操作
+  const Action =(props:MccsFileTree.childTree)=>{
+       const {isLeaf} = props;
+       return (
+        <div className='action'>
+              {
+                !isLeaf&&(  //非叶子结点才可以新增
+                  <>
+                  <Image width={15} preview={false} onClick={(e)=>actionFunc(e,'add',props)} src={'https://i.postimg.cc/13HdKg0T/image.png'} />
+                  <div style={{width:5}}></div>
+                  </>
+                )
+              }
+              <Image width={15} preview={false} onClick={(e)=>actionFunc(e,'edit',props)} src={'https://i.postimg.cc/13HdKg0T/image.png'} />
+              <div style={{width:5}}></div>
+              <Image width={15} preview={false} onClick={(e)=>actionFunc(e,'del',props)} src={'https://i.postimg.cc/HLkhvpBL/image.png'} />
+        </div>
+       )
+  }
+
+  //叶子结点结构
+  const TreeNode = (nodeProps:MccsFileTree.childTree)=>{
+       const {title,id} = nodeProps;
+       return (
+            <div className={currentActivedIndex==id?'treeNode actived':'treeNode'} onClick={onSelectHandle?()=>{
+              setcurrentActivedIndex(id);
+              onSelectHandle(nodeProps)
+            }:()=>{setcurrentActivedIndex(id);}}>
+              <div  style={{display:'flex',marginRight:5,justifyContent:'center',alignItems:'center'}} ><Image width={15} preview={false} src={'https://i.postimg.cc/vHvhJJ1x/image.png'} /> </div>
+                  <div className='treeNodeInner'>{title}</div>
+                  
+                  {(currentActivedIndex==id&&editable)&&<Action {...nodeProps} />  /*点击展示操作项*/}
+              </div>
+       )
+  }
+  //递归树形结构
+  const loop = (data:MccsFileTree.childTree,i:number) => {
+     
+       const {title,children=[],...restProps} = data;
+
+       const label = <div className="node">{title}</div>;
+
+       if(data.isLeaf){
+           return <TreeNode key={data.id} currentActivedIndex={currentActivedIndex} setcurrentActivedIndex={setcurrentActivedIndex}  title={title} {...restProps} />
+       }
+       return (
+        <DirectoryTree key={data.id} currentActivedIndex={currentActivedIndex} setcurrentActivedIndex={setcurrentActivedIndex} 
+          nodeLabel={label}  {...data}  onClick={()=>onSelectHandle?onSelectHandle(data):()=>{}}
+          action={editable&&<Action  {...data}/>}  defaultCollapsed={true}
+        >
+               {
+                 children.map((item,index)=>{
+                      if(item.isLeaf){
+                          return <TreeNode currentActivedIndex={currentActivedIndex} setcurrentActivedIndex={setcurrentActivedIndex}  key={index}   {...item} />
+                      }else{
+                         return loop(item,index);
+                      }
+                 })
+               }
+        </DirectoryTree>
+       )
+  }
+
+  useEffect(() => {
+    if (treeData.length > 0) {
+      setIsLoading(false);
+    }
+  }, [treeData]);
+
+  return (
+    <React.Fragment>
+      <div className="searchBar">
+        {
+          editable&&(
+            <div className="add" onClick={e=>actionFunc(e,'add',{
+              title:'',
+              id:`0`,
+              isLeaf:false,
+              code:`${new Date().getTime()}`,
+              children:[]
+            })}><PlusOutlined /></div>
+          )
+        }
+        <Search placeholder='请输入' className="inputArea" allowClear onSearch={(val,e)=>searchHandle&&searchHandle(val)} />
+      </div>
+
+      {
+        isLoading ? <div className='spinWrap'><Spin delay={500} /></div> : (
+          <>
+            {treeData.map((node, i) => {
+              return loop(node,i);
+            })}
+          </>
+        )
+      }
+    </React.Fragment>
+  );
+};
+
+
+
+export default MccsFileTree
+

+ 22 - 0
src/components/MccsFileTree/typing.d.ts

@@ -0,0 +1,22 @@
+/*
+ * @Author: your name
+ * @Date: 2021-09-14 16:34:18
+ * @LastEditTime: 2021-09-14 16:35:21
+ * @LastEditors: Please set LastEditors
+ * @Description: In User Settings Edit
+ * @FilePath: /MedicalWisdomCheckSys/src/components/MccsFileTree/typing.d.ts
+ */
+
+
+
+declare namespace MccsFileTree {
+       
+    type childTree = {
+        title: string,
+        id: string,
+        isLeaf:boolean,
+        code:string,
+        [propName: string]: any;
+        children?: childTree[]
+    }
+}

+ 67 - 0
src/components/MccsModal/index.tsx

@@ -0,0 +1,67 @@
+/*
+ * @Author: your name
+ * @Date: 2021-09-08 17:09:34
+ * @LastEditTime: 2021-09-10 16:51:34
+ * @LastEditors: Please set LastEditors
+ * @Description: In User Settings Edit
+ * @FilePath: /MedicalWisdomCheckSys/src/components/MccsModal/index.tsx
+ */
+
+
+/*
+ * @Author: your name
+ * @Date: 2021-09-06 11:32:58
+ * @LastEditTime: 2021-09-06 16:04:15
+ * @LastEditors: Please set LastEditors
+ * @Description: In User Settings Edit
+ * @FilePath: /MedicalWisdomCheckSys/src/components/MccsProCard/index.tsx
+ */
+
+
+
+import React,{useRef,createContext} from 'react'
+import {
+    ModalForm,
+  } from '@ant-design/pro-form';
+  import type { ProFormInstance } from '@ant-design/pro-form';
+  
+
+
+
+//卡片组件,使用参考antD pro ModalForm
+
+type MccsModalProps = {
+    title:string,
+    visible:boolean,
+    [propName: string]: any;
+}
+
+// const MccsModalContext = React.createContext({});
+
+
+const MccsModalContext: React.Context<MccsModal.MccsModalContextT> = createContext({});
+
+
+
+const MccsModal: React.FC<MccsModalProps> = (props) => {
+  const { title,visible,children,...restProps } = props;
+  const formRef = useRef<ProFormInstance>();
+
+  return (
+    <ModalForm
+      title={title}
+      visible={visible}
+      formRef={formRef}
+      {...restProps}
+    >
+      <MccsModalContext.Provider value={{formRef:formRef}}>
+         {children}
+      </MccsModalContext.Provider> 
+   </ModalForm>
+  );
+};
+
+
+export {MccsModal,MccsModalContext} 
+
+

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

@@ -0,0 +1,17 @@
+/*
+ * @Author: your name
+ * @Date: 2021-09-10 16:43:58
+ * @LastEditTime: 2021-09-10 17:12:59
+ * @LastEditors: Please set LastEditors
+ * @Description: In User Settings Edit
+ * @FilePath: /MedicalWisdomCheckSys/src/components/MccsModal/typings.d.ts
+ */
+
+
+
+
+declare namespace MccsModal {
+    type MccsModalContextT = {
+         [propsName:string]:any
+    }
+}

+ 40 - 0
src/components/MccsPageContainer/index.jsx

@@ -0,0 +1,40 @@
+
+
+import React from 'react'
+import PropTypes from 'prop-types'
+
+import { Button, Dropdown, Menu } from 'antd';
+import { PageContainer } from '@ant-design/pro-layout';
+
+import './index.less';
+
+
+
+function MccsPageContainer(props) {
+    /**
+     * @param {*} children 
+     * @param {Object} config  //组件配置信息,参考ProComponent ProContainer
+     * @returns 
+     */
+    const {children,config} = props;
+    return (
+        <PageContainer
+           {
+               ...config
+           } 
+           style={{padding:'16px'}} 
+        >
+             {
+                children
+             }
+        </PageContainer>
+    )
+}
+
+MccsPageContainer.propTypes = {
+    config:PropTypes.object,
+    children:PropTypes.node
+}
+
+export default MccsPageContainer
+

+ 7 - 0
src/components/MccsPageContainer/index.less

@@ -0,0 +1,7 @@
+
+
+.ant-pro-page-container-children-content {
+    padding: 0 !important;
+    margin:0;
+    margin-top: 16px;
+}

+ 45 - 0
src/components/MccsProCard/index.tsx

@@ -0,0 +1,45 @@
+/*
+ * @Author: your name
+ * @Date: 2021-09-06 11:32:58
+ * @LastEditTime: 2021-09-06 16:04:15
+ * @LastEditors: Please set LastEditors
+ * @Description: In User Settings Edit
+ * @FilePath: /MedicalWisdomCheckSys/src/components/MccsProCard/index.tsx
+ */
+
+
+
+import React from 'react'
+import ProCard from '@ant-design/pro-card';
+
+
+//卡片组件,参考antD ProCard
+
+type MccsProCardProps = {
+    gutter?:number,
+    ghost?:boolean,
+    isProCard?: boolean,
+    colSpan?:number | string,
+    direction?:"column" | "row" | undefined,
+    [propName: string]: any;
+}
+
+
+const MccsProCard: React.FC<MccsProCardProps> = (props) => {
+  const { children,...restProps } = props;
+
+  return (
+    <ProCard
+      {...restProps}
+    >
+       {children}
+   </ProCard>
+  );
+};
+
+
+//参考 源码:https://github.com/ant-design/pro-components/blob/master/packages/card/src/ProCard.tsx
+MccsProCard.isProCard = true;
+
+export default MccsProCard
+

BIN
src/components/MccsRecordList/images/arrow.png


+ 240 - 0
src/components/MccsRecordList/index.less

@@ -0,0 +1,240 @@
+.mccsRecordList {
+  .mccsRecordListTitle {
+    font-size: 16px;
+    font-family: SourceHanSansCN-Normal, SourceHanSansCN;
+    font-weight: 400;
+    color: #7A8599;
+    margin-bottom: 16px;
+  }
+
+  .container {
+    .list {
+      margin-bottom: 24px;
+
+      .listInner {
+        display: flex;
+        width: 100%;
+        flex-direction: row;
+        justify-content: flex-start;
+        align-items: flex-start;
+
+        .header {
+          text-align: center;
+          width: 32px;
+          height: 32px;
+          line-height: 32px;
+          font-size: 20px;
+          font-family: SourceHanSansCN-Bold, SourceHanSansCN;
+          font-weight: bold;
+          color: #FFC080;
+          margin-right: 16px;
+          border-radius: 100%;
+          background: #FFF5EB;
+        }
+
+        .innerWrap {
+          width: 95%;
+          padding-top: 5px;
+
+          .item {
+            display: flex;
+            flex-direction: column;
+            margin-bottom: 24px;
+            width: 100%;
+
+            .itemInner {
+              display: flex;
+              width: 100%;
+              flex-direction: row;
+              justify-content: space-between;
+              align-items: center;
+              margin-bottom: 15px;
+
+              .itemHeader {
+                display: flex;
+                width: 60%;
+                flex-direction: row;
+                align-items: center;
+
+                .type {
+                  height: 16px;
+                  line-height: 16px;
+                  background: #A3B1CC;
+                  border-radius: 2px;
+                  font-size: 10px;
+                  font-family: SourceHanSansCN-Normal, SourceHanSansCN;
+                  font-weight: 400;
+                  margin-right: 8px;
+                  color: #FFFFFF;
+                  padding: 0 5px;
+                }
+
+                .bolder {
+                  font-size: 14px;
+                  font-family: SourceHanSansCN-Bold, SourceHanSansCN;
+                  font-weight: 600;
+                  color: #292C33;
+                }
+
+                &:last-child {
+                    margin-bottom: 0;
+                }
+              }
+
+              .time {
+                display: flex;
+                flex-direction: row;
+                align-items: center;
+                font-size: 12px;
+                font-family: SourceHanSansCN-Normal, SourceHanSansCN;
+                font-weight: 400;
+                color: #525866;
+
+                .history {
+                  display: flex;
+                  flex-direction: row;
+                  align-items: center;
+                  cursor: pointer;
+
+                  &::before {
+                    position: relative;
+                    top: -1px;
+                    display: inline-block;
+                    content: '';
+                    width: 15px;
+                    height:15px;
+                    background-image:url('./images/arrow.png');
+                    background-size: contain;
+                    margin-right: 5px;
+                    transform: rotate(180deg);
+                  }
+
+                  &.on {
+                    &::before {
+                      position: relative;
+                      top: -1px;
+                      display: inline-block;
+                      content: '';
+                      width:15px;
+                      height:15px;
+                      background-image:url('./images/arrow.png');
+                      background-size: contain;
+                      margin-right: 5px;
+                      transform: rotate(360deg);
+                    }
+                  }
+                }
+
+                .data {}
+              }
+
+              .action {
+                cursor: pointer;
+
+                span {
+                  font-size: 12px;
+                  font-family: SourceHanSansCN-Normal, SourceHanSansCN;
+                  font-weight: 400;
+                  color: @primary-color;
+                }
+              }
+
+              &:last-child {
+                  margin-bottom: 0;
+              }
+            }
+
+            .subListWrap {
+              padding: 16px;
+              background: #F7F8FA;
+              border-radius: 4px;
+
+              .ant-timeline {
+                .ant-timeline-item {
+                  .ant-timeline-item-tail {
+                    top: 18px;
+                  }
+
+                  .ant-timeline-item-head {
+                    top: 6px;
+                  }
+
+                  .ant-timeline-item-head-custom {
+                    top: 11px;
+                    background-color: transparent;
+
+                    .timeLineDot {
+                      display: inline-block;
+                      width: 10px;
+                      height: 10px;
+                      border-radius: 100%;
+                      background: @primary-color;
+                    }
+                  }
+
+                  .ant-timeline-item-content {
+                    top: 0;
+                  }
+
+                  &:last-child {
+                    padding-bottom: 0;
+                  }
+                }
+
+                .ant-timeline-item-last>.ant-timeline-item-content {
+                  min-height: 0;
+                  padding-bottom: 0;
+                }
+              }
+
+
+              .timeLineNode {
+                display: flex;
+                width: 100%;
+                flex-direction: row;
+                justify-content: space-between;
+                align-items: center;
+
+                .action {
+                  display: flex;
+                  justify-content: center;
+                  align-items: center;
+                  cursor: pointer;
+                  width: 15px;
+                  height: 15px;
+                  border-radius: 100%;
+                //   transform: rotate(45deg);
+                  background: #8F9BB3;
+                  &::after {
+                      position: relative;
+                      display: inline-block;
+                      content: '+';
+                      top:-0.5px;
+                      left:1px;
+                      color: white;
+                      transform: rotate(45deg);
+                  }
+                }
+
+                &.on {
+                  .version {
+                    color: @primary-color;
+                  }
+                }
+              }
+            }
+
+            &:last-child {
+              margin-bottom: 0;
+            }
+          }
+        }
+      }
+
+      &:last-child {
+        margin-bottom: 0;
+      }
+    }
+  }
+
+}

+ 209 - 0
src/components/MccsRecordList/index.tsx

@@ -0,0 +1,209 @@
+/*
+ * @Author: your name
+ * @Date: 2021-09-15 09:17:28
+ * @LastEditTime: 2021-09-15 19:16:17
+ * @LastEditors: Please set LastEditors
+ * @Description: In User Settings Edit
+ * @FilePath: /MedicalWisdomCheckSys/src/components/MccsRecordList/index.tsx
+ */
+
+
+
+import React, { useState, useEffect } from 'react'
+import { Divider, Timeline, Empty } from 'antd';
+
+import './index.less';
+
+
+
+const MccsRecordList: React.FC<MccsRecordList.propsType> = (props) => {
+
+    const { title, list = [], actionFunc} = props;
+    const [currentOpen, setCurrentOpen] = useState<[number, number] | []>([]);
+    const [data, setData] = useState<MccsRecordList.MccsRecordListType>([]);
+
+    const historyHandle = (supIndex: number, subIndex: number) => {
+        if (currentOpen[0] == supIndex && currentOpen[1] == subIndex) {
+            setCurrentOpen([-1, -1]);
+        } else {
+            setCurrentOpen([supIndex, subIndex]);
+        }
+    }
+
+    const actionHandle = (supIndex: number,subIndex:number,lowerIndex:number) => {
+        let flag = actionFunc&&actionFunc();  //返回true表示服务器删除成功!
+        if(flag){
+            let _data = data;
+            _data[supIndex].details[subIndex].historyRecords.splice(lowerIndex,1);
+            setData([..._data]);
+        }
+    }
+
+
+    useEffect(() => {
+        // if (list) {
+        //     setData(list);
+        // }
+        setData([
+            {
+                evaluation: 'C',
+                details: [
+                    {
+                        name: '张三',
+                        fileName: '文件名',
+                        fileUrl: '',
+                        createTime: '2021-05-02 18:27',
+                        recordTypeName: '自查',
+                        historyRecords: [
+                            {
+                                versionName: '当前版本',
+                                name: '麻子',
+                                time: '2021-05-02 18:27',
+                            }
+                        ]
+                    },
+                    {
+                        name: '张三',
+                        fileName: '文件名',
+                        fileUrl: '',
+                        createTime: '2021-05-02 18:27',
+                        recordTypeName: '自查',
+                        historyRecords: []
+                    }
+                ]
+            },
+            {
+                evaluation: 'C',
+                details: [
+                    {
+                        name: '张三',
+                        fileName: '文件名',
+                        fileUrl: '',
+                        createTime: '2021-05-02 18:27',
+                        recordTypeName: '自查',
+                        historyRecords: []
+                    },
+                    {
+                        name: '张三',
+                        fileName: '文件名',
+                        fileUrl: '',
+                        createTime: '2021-05-02 18:27',
+                        recordTypeName: '自查',
+                        historyRecords: []
+                    }
+                ]
+            }
+        ])
+    }, [props]);
+
+    return (
+        <div className='mccsRecordList'>
+            <div className="mccsRecordListTitle">{title ? title : '标题'}</div>
+            {
+                data.length == 0 && (
+                    //空数据
+                    <Empty image={Empty.PRESENTED_IMAGE_SIMPLE}></Empty>
+                )
+            }
+            {
+                data.length > 0 && (
+                    <div className='container'>
+                        {
+                            data.map((item, index) => {
+                                return (
+                                    <div className='list' key={index}>
+                                        <div className='listInner'>
+                                            <div className='header'>{item.evaluation}</div>
+                                            <div className='innerWrap'>
+                                                {
+                                                    item.details.map((detailsList, detailListIndex) => {
+                                                        return (
+                                                            <div className='item' key={detailListIndex}>
+                                                                <div className='itemInner'>
+                                                                    <div className='itemHeader'>
+                                                                        <span className='type'>{detailsList.recordTypeName}</span>
+                                                                        <div className='recordContent'>
+                                                                            <span className='bolder'>{detailsList.name}</span>
+                                                                            上传了台帐
+                                                                            <span className='bolder'>{detailsList.fileName}</span>
+                                                                        </div>
+                                                                    </div>
+                                                                    <div className='time'>
+                                                                        {
+                                                                            detailsList.historyRecords.length > 0 && (
+                                                                                <>
+                                                                                <div className={(currentOpen[0] == index && currentOpen[1] == detailListIndex) ? 'history on' : 'history'} onClick={() => historyHandle(index, detailListIndex)}>历史记录</div>
+                                                                                <Divider type="vertical" />
+                                                                                </>
+                                                                            )
+                                                                        }
+                                                                        <span className='date'>{detailsList.createTime}</span>
+                                                                    </div>
+                                                                    <div className='action'>
+                                                                        <a href={detailsList.fileUrl}>下载文件</a>
+                                                                    </div>
+                                                                </div>
+                                                                {
+                                                                    (currentOpen[0] == index && currentOpen[1] == detailListIndex&&detailsList.historyRecords.length > 0) && (
+                                                                        <div className='subListWrap'>
+            
+                                                                            <Timeline pending={false}>
+                                                                                {
+                                                                                    item.details[detailListIndex].historyRecords.map((val, i) => {
+                            
+                                                                                        return (
+                                                                                            <React.Fragment key={i}>
+                                                                                                {
+                                                                                                    i == 0 && ( //当前版本
+                                                                                                        <Timeline.Item dot={<span className='timeLineDot'></span>} >
+                                                                                                            <div className='timeLineNode on'>
+                                                                                                                <div className='version'>{val.versionName}</div>
+                                                                                                                <div className='actionPeople'>{`上传人:${val.name}`}</div>
+                                                                                                                <div className='time'>{val.time}</div>
+                                                                                                                <span className='action' onClick={() => actionHandle(index,detailListIndex,i)}></span>
+                                                                                                            </div>
+                                                                                                        </Timeline.Item >
+                                                                                                    )
+                                                                                                }
+                                                                                                {
+                                                                                                    i > 0 && (
+                                                                                                        <Timeline.Item color='gray' >
+                                                                                                            <div className='timeLineNode'>
+                                                                                                                <div className='version'>{val.versionName}</div>
+                                                                                                                <div className='actionPeople'>{`上传人:${val.name}`}</div>
+                                                                                                                <div className='time'>{val.time}</div>
+                                                                                                                <span className='action' onClick={() => actionHandle(index,detailListIndex,i)}></span>
+                                                                                                            </div>
+                                                                                                        </Timeline.Item >
+                                                                                                    )
+                                                                                                }
+                                                                                            </React.Fragment>
+                                                                                        )
+                                                                                    })
+                                                                                }
+
+                                                                            </Timeline>
+                                                                        </div>
+                                                                    )
+                                                                }
+
+                                                            </div>
+                                                        )
+                                                    })
+                                                }
+                                            </div>
+                                        </div>
+                                    </div>
+                                )
+                            })
+                        }
+
+                    </div>
+                )
+            }
+
+        </div>
+    )
+}
+
+export default MccsRecordList

+ 41 - 0
src/components/MccsRecordList/typings.d.ts

@@ -0,0 +1,41 @@
+/*
+ * @Author: your name
+ * @Date: 2021-09-15 09:17:43
+ * @LastEditTime: 2021-09-15 15:58:59
+ * @LastEditors: Please set LastEditors
+ * @Description: In User Settings Edit
+ * @FilePath: /MedicalWisdomCheckSys/src/components/MccsRecordList/typings.d.ts
+ */
+
+
+
+
+declare namespace MccsRecordList {
+
+      type propsType = {
+        title:string,
+        list?:MccsRecordListType|[],
+        actionFunc?:()=>boolean,
+      }
+
+      type historyRecordsItem = {
+        versionName:string,
+        name:string,
+        time:string,
+      }
+
+      type MccsRecordListItem = {
+          evaluation:string,
+          details:{
+            name:string,
+            fileName:string,
+            fileUrl:string,
+            createTime:string,
+            recordTypeName:string,
+            historyRecords:historyRecordsItem[]|[]
+          }[]
+      }
+
+      type MccsRecordListType = MccsRecordListItem[]
+
+}

+ 34 - 0
src/components/MccsScoreBanner/index.less

@@ -0,0 +1,34 @@
+
+
+.MccsScoreBanner {
+    display: flex;
+    flex-direction: row;
+    justify-content: space-around;
+    align-items: center;
+    background: hsl(220, 23%, 97%);
+    border-radius: 4px; 
+    padding: 20px 0;
+    .scoreList {
+          text-align: center;
+          .label {
+            font-size: 12px;
+            font-family: SourceHanSansCN-Normal, SourceHanSansCN;
+            font-weight: 400;
+            color: #525866;
+            line-height: 18px;
+            margin-bottom: 8px;
+          }
+          .value {
+            font-size: 20px;
+            font-family: SourceHanSansCN-Medium, SourceHanSansCN;
+            font-weight: 500;
+            color: #292C33;
+            line-height: 30px;
+          }
+          .dvider {
+            width: 1px;
+            height: 24px;
+            background: #DADEE6;
+          }
+    }
+}

+ 47 - 0
src/components/MccsScoreBanner/index.tsx

@@ -0,0 +1,47 @@
+/*
+ * @Author: your name
+ * @Date: 2021-09-14 18:15:35
+ * @LastEditTime: 2021-09-15 19:21:56
+ * @LastEditors: Please set LastEditors
+ * @Description: In User Settings Edit
+ * @FilePath: /MedicalWisdomCheckSys/src/components/MccsScoreBanner/index.tsx
+ */
+
+
+
+import React from 'react'
+import { Divider } from 'antd';
+
+import './index.less'
+
+
+type ScoreListItem = {
+     label:string,
+     value:string
+}
+type MccsScoreBannerProps = {
+     list:ScoreListItem[]
+}
+
+const MccsScoreBanner:React.FC<MccsScoreBannerProps>=(props)=>{
+    const {list=[]} = props;
+    return (
+        <div className='MccsScoreBanner'>
+             {
+                list.map((item,index)=>{
+                    return (
+                        <React.Fragment key={index}>
+                        <div className='scoreList' >
+                             <div className='label'>{item.label}</div>
+                             <div className='value'>{item.value}</div>
+                        </div> 
+                        {index!==list.length-1&&<Divider type="vertical" className='dvider' style={{height:'24px'}} />}
+                        </React.Fragment>
+                    )
+                })
+             }
+        </div>
+    )
+}
+
+export default MccsScoreBanner

+ 8 - 0
src/components/MccsTable/README.md

@@ -0,0 +1,8 @@
+<!--
+ * @Author: your name
+ * @Date: 2021-09-14 17:55:18
+ * @LastEditTime: 2021-09-14 17:55:19
+ * @LastEditors: Please set LastEditors
+ * @Description: In User Settings Edit
+ * @FilePath: /MedicalWisdomCheckSys/src/components/MccsTable/README.md
+-->

+ 17 - 0
src/components/MccsTable/index.less

@@ -0,0 +1,17 @@
+
+
+.ant-card-body {
+    padding: 0;
+}
+
+
+.ant-pro-table-search {
+    padding: 0;
+    .ant-form-item {
+        margin-bottom: 0;
+    }
+}
+
+.formGroup {
+    margin-bottom: 20px;
+}

+ 70 - 0
src/components/MccsTable/index.tsx

@@ -0,0 +1,70 @@
+/*
+ * @Author: your name
+ * @Date: 2021-09-14 11:14:06
+ * @LastEditTime: 2021-09-14 17:34:00
+ * @LastEditors: Please set LastEditors
+ * @Description: In User Settings Edit
+ * @FilePath: /MedicalWisdomCheckSys/src/components/MccsTable/index.tsx
+ */
+
+
+import React,{useImperativeHandle,useRef} from 'react'
+import { Button, Tag, Space, Menu, Dropdown } from 'antd';
+import ProTable, { TableDropdown } from '@ant-design/pro-table';
+import type { ProColumns, ActionType } from '@ant-design/pro-table';
+import type { ProFormInstance } from '@ant-design/pro-form';
+
+import './index.less';
+
+
+
+
+const MccsTable=React.forwardRef<MccsTable.MccsTableRef,MccsTable.MccsTable>((props,ref)=>{
+
+    const {columns=[],serach,request,searchNode,...rest} = props;
+    const formRef = useRef<ProFormInstance>();
+    const tableRef =useRef<ActionType>(); 
+
+    const processColumns = (colums:MccsTable.Columns[])=>{
+         return colums.map((item,index)=>{
+                return {
+                     ...item,
+                     ellipsis:true
+                }
+         })
+    }
+
+    useImperativeHandle(ref,()=>{
+        // getFormRef 就是暴露给父组件的方法
+        const getFormRef = ()=>{
+            return formRef;
+        }
+        const getTableRef = ()=>{
+            return tableRef;
+        }
+        return {getFormRef,getTableRef}
+    });
+
+    return (
+        <React.Fragment >
+            <div className='container'>
+                 {/* <div className='formGroup'>
+                {
+                    searchNode
+                }
+            </div> */}
+            <ProTable
+                  columns={processColumns(columns)}
+                  toolBarRender={false}
+                  formRef={formRef}
+                  actionRef={tableRef}
+                  request={request}
+                  {...rest}
+            />
+            </div>
+        </React.Fragment>
+    )
+})
+
+export default MccsTable
+

+ 44 - 0
src/components/MccsTable/typings.d.ts

@@ -0,0 +1,44 @@
+/*
+ * @Author: your name
+ * @Date: 2021-09-14 11:16:08
+ * @LastEditTime: 2021-09-14 17:43:42
+ * @LastEditors: Please set LastEditors
+ * @Description: In User Settings Edit
+ * @FilePath: /MedicalWisdomCheckSys/src/components/MccsTable/typings.d.ts
+ */
+
+
+
+declare namespace MccsTable {
+
+    type GithubIssueItem = {
+        url: string;
+        id: number;
+        number: number;
+        title: string;
+        labels: {
+          name: string;
+          color: string;
+        }[];
+        state: string;
+        comments: number;
+        created_at: string;
+        updated_at: string;
+        closed_at?: string;
+    };
+
+    type Columns = {
+        title?:string,
+        key?:string,
+        [propsName:string]:any
+    }
+
+    type MccsTable = {
+        columns:MccsTable.Columns[],
+        [propsName:string]:any
+    }
+    type MccsTableRef = {
+        getFormRef:()=>any,
+        getTableRef:()=>any
+    }|undefined
+}

+ 126 - 0
src/components/NoticeIcon/NoticeIcon.tsx

@@ -0,0 +1,126 @@
+import { BellOutlined } from '@ant-design/icons';
+import { Badge, Spin, Tabs } from 'antd';
+import useMergedState from 'rc-util/es/hooks/useMergedState';
+import React from 'react';
+import classNames from 'classnames';
+import type { NoticeIconTabProps } from './NoticeList';
+import NoticeList from './NoticeList';
+import HeaderDropdown from '../HeaderDropdown';
+import styles from './index.less';
+
+const { TabPane } = Tabs;
+
+export type NoticeIconProps = {
+  count?: number;
+  bell?: React.ReactNode;
+  className?: string;
+  loading?: boolean;
+  onClear?: (tabName: string, tabKey: string) => void;
+  onItemClick?: (item: API.NoticeIconItem, tabProps: NoticeIconTabProps) => void;
+  onViewMore?: (tabProps: NoticeIconTabProps, e: MouseEvent) => void;
+  onTabChange?: (tabTile: string) => void;
+  style?: React.CSSProperties;
+  onPopupVisibleChange?: (visible: boolean) => void;
+  popupVisible?: boolean;
+  clearText?: string;
+  viewMoreText?: string;
+  clearClose?: boolean;
+  emptyImage?: string;
+  children?: React.ReactElement<NoticeIconTabProps>[];
+};
+
+const NoticeIcon: React.FC<NoticeIconProps> & {
+  Tab: typeof NoticeList;
+} = (props) => {
+  const getNotificationBox = (): React.ReactNode => {
+    const {
+      children,
+      loading,
+      onClear,
+      onTabChange,
+      onItemClick,
+      onViewMore,
+      clearText,
+      viewMoreText,
+    } = props;
+    if (!children) {
+      return null;
+    }
+    const panes: React.ReactNode[] = [];
+    React.Children.forEach(children, (child: React.ReactElement<NoticeIconTabProps>): void => {
+      if (!child) {
+        return;
+      }
+      const { list, title, count, tabKey, showClear, showViewMore } = child.props;
+      const len = list && list.length ? list.length : 0;
+      const msgCount = count || count === 0 ? count : len;
+      const tabTitle: string = msgCount > 0 ? `${title} (${msgCount})` : title;
+      panes.push(
+        <TabPane tab={tabTitle} key={tabKey}>
+          <NoticeList
+            clearText={clearText}
+            viewMoreText={viewMoreText}
+            list={list}
+            tabKey={tabKey}
+            onClear={(): void => onClear && onClear(title, tabKey)}
+            onClick={(item): void => onItemClick && onItemClick(item, child.props)}
+            onViewMore={(event): void => onViewMore && onViewMore(child.props, event)}
+            showClear={showClear}
+            showViewMore={showViewMore}
+            title={title}
+          />
+        </TabPane>,
+      );
+    });
+    return (
+      <>
+        <Spin spinning={loading} delay={300}>
+          <Tabs className={styles.tabs} onChange={onTabChange}>
+            {panes}
+          </Tabs>
+        </Spin>
+      </>
+    );
+  };
+
+  const { className, count, bell } = props;
+
+  const [visible, setVisible] = useMergedState<boolean>(false, {
+    value: props.popupVisible,
+    onChange: props.onPopupVisibleChange,
+  });
+  const noticeButtonClass = classNames(className, styles.noticeButton);
+  const notificationBox = getNotificationBox();
+  const NoticeBellIcon = bell || <BellOutlined className={styles.icon} />;
+  const trigger = (
+    <span className={classNames(noticeButtonClass, { opened: visible })}>
+      <Badge count={count} style={{ boxShadow: 'none' }} className={styles.badge}>
+        {NoticeBellIcon}
+      </Badge>
+    </span>
+  );
+  if (!notificationBox) {
+    return trigger;
+  }
+
+  return (
+    <HeaderDropdown
+      placement="bottomRight"
+      overlay={notificationBox}
+      overlayClassName={styles.popover}
+      trigger={['click']}
+      visible={visible}
+      onVisibleChange={setVisible}
+    >
+      {trigger}
+    </HeaderDropdown>
+  );
+};
+
+NoticeIcon.defaultProps = {
+  emptyImage: 'https://gw.alipayobjects.com/zos/rmsportal/wAhyIChODzsoKIOBHcBk.svg',
+};
+
+NoticeIcon.Tab = NoticeList;
+
+export default NoticeIcon;

+ 103 - 0
src/components/NoticeIcon/NoticeList.less

@@ -0,0 +1,103 @@
+@import '~antd/es/style/themes/default.less';
+
+.list {
+  max-height: 400px;
+  overflow: auto;
+  &::-webkit-scrollbar {
+    display: none;
+  }
+  .item {
+    padding-right: 24px;
+    padding-left: 24px;
+    overflow: hidden;
+    cursor: pointer;
+    transition: all 0.3s;
+
+    .meta {
+      width: 100%;
+    }
+
+    .avatar {
+      margin-top: 4px;
+      background: @component-background;
+    }
+    .iconElement {
+      font-size: 32px;
+    }
+
+    &.read {
+      opacity: 0.4;
+    }
+    &:last-child {
+      border-bottom: 0;
+    }
+    &:hover {
+      background: @primary-1;
+    }
+    .title {
+      margin-bottom: 8px;
+      font-weight: normal;
+    }
+    .description {
+      font-size: 12px;
+      line-height: @line-height-base;
+    }
+    .datetime {
+      margin-top: 4px;
+      font-size: 12px;
+      line-height: @line-height-base;
+    }
+    .extra {
+      float: right;
+      margin-top: -1.5px;
+      margin-right: 0;
+      color: @text-color-secondary;
+      font-weight: normal;
+    }
+  }
+  .loadMore {
+    padding: 8px 0;
+    color: @primary-6;
+    text-align: center;
+    cursor: pointer;
+    &.loadedAll {
+      color: rgba(0, 0, 0, 0.25);
+      cursor: unset;
+    }
+  }
+}
+
+.notFound {
+  padding: 73px 0 88px;
+  color: @text-color-secondary;
+  text-align: center;
+  img {
+    display: inline-block;
+    height: 76px;
+    margin-bottom: 16px;
+  }
+}
+
+.bottomBar {
+  height: 46px;
+  color: @text-color;
+  line-height: 46px;
+  text-align: center;
+  border-top: 1px solid @border-color-split;
+  border-radius: 0 0 @border-radius-base @border-radius-base;
+  transition: all 0.3s;
+  div {
+    display: inline-block;
+    width: 50%;
+    cursor: pointer;
+    transition: all 0.3s;
+    user-select: none;
+
+    &:only-child {
+      width: 100%;
+    }
+    &:not(:only-child):last-child {
+      border-left: 1px solid @border-color-split;
+    }
+  }
+}

+ 113 - 0
src/components/NoticeIcon/NoticeList.tsx

@@ -0,0 +1,113 @@
+import { Avatar, List } from 'antd';
+
+import React from 'react';
+import classNames from 'classnames';
+import styles from './NoticeList.less';
+
+export type NoticeIconTabProps = {
+  count?: number;
+  showClear?: boolean;
+  showViewMore?: boolean;
+  style?: React.CSSProperties;
+  title: string;
+  tabKey: API.NoticeIconItemType;
+  onClick?: (item: API.NoticeIconItem) => void;
+  onClear?: () => void;
+  emptyText?: string;
+  clearText?: string;
+  viewMoreText?: string;
+  list: API.NoticeIconItem[];
+  onViewMore?: (e: any) => void;
+};
+const NoticeList: React.FC<NoticeIconTabProps> = ({
+  list = [],
+  onClick,
+  onClear,
+  title,
+  onViewMore,
+  emptyText,
+  showClear = true,
+  clearText,
+  viewMoreText,
+  showViewMore = false,
+}) => {
+  if (!list || list.length === 0) {
+    return (
+      <div className={styles.notFound}>
+        <img
+          src="https://gw.alipayobjects.com/zos/rmsportal/sAuJeJzSKbUmHfBQRzmZ.svg"
+          alt="not found"
+        />
+        <div>{emptyText}</div>
+      </div>
+    );
+  }
+  return (
+    <div>
+      <List<API.NoticeIconItem>
+        className={styles.list}
+        dataSource={list}
+        renderItem={(item, i) => {
+          const itemCls = classNames(styles.item, {
+            [styles.read]: item.read,
+          });
+          // eslint-disable-next-line no-nested-ternary
+          const leftIcon = item.avatar ? (
+            typeof item.avatar === 'string' ? (
+              <Avatar className={styles.avatar} src={item.avatar} />
+            ) : (
+              <span className={styles.iconElement}>{item.avatar}</span>
+            )
+          ) : null;
+
+          return (
+            <List.Item
+              className={itemCls}
+              key={item.key || i}
+              onClick={() => {
+                onClick?.(item);
+              }}
+            >
+              <List.Item.Meta
+                className={styles.meta}
+                avatar={leftIcon}
+                title={
+                  <div className={styles.title}>
+                    {item.title}
+                    <div className={styles.extra}>{item.extra}</div>
+                  </div>
+                }
+                description={
+                  <div>
+                    <div className={styles.description}>{item.description}</div>
+                    <div className={styles.datetime}>{item.datetime}</div>
+                  </div>
+                }
+              />
+            </List.Item>
+          );
+        }}
+      />
+      <div className={styles.bottomBar}>
+        {showClear ? (
+          <div onClick={onClear}>
+            {clearText} {title}
+          </div>
+        ) : null}
+        {showViewMore ? (
+          <div
+            onClick={(e) => {
+              if (onViewMore) {
+                onViewMore(e);
+              }
+            }}
+          >
+            {viewMoreText}
+          </div>
+        ) : null}
+      </div>
+    </div>
+  );
+};
+
+export default NoticeList;

+ 35 - 0
src/components/NoticeIcon/index.less

@@ -0,0 +1,35 @@
+@import '~antd/es/style/themes/default.less';
+
+.popover {
+  position: relative;
+  width: 336px;
+}
+
+.noticeButton {
+  display: inline-block;
+  cursor: pointer;
+  transition: all 0.3s;
+}
+.icon {
+  padding: 4px;
+  vertical-align: middle;
+}
+
+.badge {
+  font-size: 16px;
+}
+
+.tabs {
+  :global {
+    .ant-tabs-nav-list {
+      margin: auto;
+    }
+
+    .ant-tabs-nav-scroll {
+      text-align: center;
+    }
+    .ant-tabs-bar {
+      margin-bottom: 0;
+    }
+  }
+}

+ 153 - 0
src/components/NoticeIcon/index.tsx

@@ -0,0 +1,153 @@
+import { useEffect, useState } from 'react';
+import { Tag, message } from 'antd';
+import { groupBy } from 'lodash';
+import moment from 'moment';
+import { useModel, useRequest } from 'umi';
+import { getNotices } from '@/services/ant-design-pro/api';
+
+import NoticeIcon from './NoticeIcon';
+import styles from './index.less';
+
+export type GlobalHeaderRightProps = {
+  fetchingNotices?: boolean;
+  onNoticeVisibleChange?: (visible: boolean) => void;
+  onNoticeClear?: (tabName?: string) => void;
+};
+
+const getNoticeData = (notices: API.NoticeIconItem[]): Record<string, API.NoticeIconItem[]> => {
+  if (!notices || notices.length === 0 || !Array.isArray(notices)) {
+    return {};
+  }
+
+  const newNotices = notices.map((notice) => {
+    const newNotice = { ...notice };
+
+    if (newNotice.datetime) {
+      newNotice.datetime = moment(notice.datetime as string).fromNow();
+    }
+
+    if (newNotice.id) {
+      newNotice.key = newNotice.id;
+    }
+
+    if (newNotice.extra && newNotice.status) {
+      const color = {
+        todo: '',
+        processing: 'blue',
+        urgent: 'red',
+        doing: 'gold',
+      }[newNotice.status];
+      newNotice.extra = (
+        <Tag
+          color={color}
+          style={{
+            marginRight: 0,
+          }}
+        >
+          {newNotice.extra}
+        </Tag>
+      ) as any;
+    }
+
+    return newNotice;
+  });
+  return groupBy(newNotices, 'type');
+};
+
+const getUnreadData = (noticeData: Record<string, API.NoticeIconItem[]>) => {
+  const unreadMsg: Record<string, number> = {};
+  Object.keys(noticeData).forEach((key) => {
+    const value = noticeData[key];
+
+    if (!unreadMsg[key]) {
+      unreadMsg[key] = 0;
+    }
+
+    if (Array.isArray(value)) {
+      unreadMsg[key] = value.filter((item) => !item.read).length;
+    }
+  });
+  return unreadMsg;
+};
+
+const NoticeIconView = () => {
+  const { initialState } = useModel('@@initialState');
+  const { currentUser } = initialState || {};
+  const [notices, setNotices] = useState<API.NoticeIconItem[]>([]);
+  const { data } = useRequest(getNotices);
+
+  useEffect(() => {
+    setNotices(data || []);
+  }, [data]);
+
+  const noticeData = getNoticeData(notices);
+  const unreadMsg = getUnreadData(noticeData || {});
+
+  const changeReadState = (id: string) => {
+    setNotices(
+      notices.map((item) => {
+        const notice = { ...item };
+        if (notice.id === id) {
+          notice.read = true;
+        }
+        return notice;
+      }),
+    );
+  };
+
+  const clearReadState = (title: string, key: string) => {
+    setNotices(
+      notices.map((item) => {
+        const notice = { ...item };
+        if (notice.type === key) {
+          notice.read = true;
+        }
+        return notice;
+      }),
+    );
+    message.success(`${'清空了'} ${title}`);
+  };
+
+  return (
+    <NoticeIcon
+      className={styles.action}
+      count={currentUser && currentUser.unreadCount}
+      onItemClick={(item) => {
+        changeReadState(item.id!);
+      }}
+      onClear={(title: string, key: string) => clearReadState(title, key)}
+      loading={false}
+      clearText="清空"
+      viewMoreText="查看更多"
+      onViewMore={() => message.info('Click on view more')}
+      clearClose
+    >
+      <NoticeIcon.Tab
+        tabKey="notification"
+        count={unreadMsg.notification}
+        list={noticeData.notification}
+        title="通知"
+        emptyText="你已查看所有通知"
+        showViewMore
+      />
+      <NoticeIcon.Tab
+        tabKey="message"
+        count={unreadMsg.message}
+        list={noticeData.message}
+        title="消息"
+        emptyText="您已读完所有消息"
+        showViewMore
+      />
+      <NoticeIcon.Tab
+        tabKey="event"
+        title="待办"
+        emptyText="你已完成所有待办"
+        count={unreadMsg.event}
+        list={noticeData.event}
+        showViewMore
+      />
+    </NoticeIcon>
+  );
+};
+
+export default NoticeIconView;

+ 104 - 0
src/components/RightContent/AvatarDropdown.tsx

@@ -0,0 +1,104 @@
+import React, { useCallback } from 'react';
+import { LogoutOutlined, SettingOutlined, UserOutlined } from '@ant-design/icons';
+import { Avatar, Menu, Spin } from 'antd';
+import { history, useModel } from 'umi';
+import { stringify } from 'querystring';
+import HeaderDropdown from '../HeaderDropdown';
+import styles from './index.less';
+import { outLogin } from '@/services/ant-design-pro/api';
+import type { MenuInfo } from 'rc-menu/lib/interface';
+
+export type GlobalHeaderRightProps = {
+  menu?: boolean;
+};
+
+/**
+ * 退出登录,并且将当前的 url 保存
+ */
+const loginOut = async () => {
+  await outLogin();
+  const { query = {}, pathname } = history.location;
+  const { redirect } = query;
+  // Note: There may be security issues, please note
+  if (window.location.pathname !== '/user/login' && !redirect) {
+    history.replace({
+      pathname: '/user/login',
+      search: stringify({
+        redirect: pathname,
+      }),
+    });
+  }
+};
+
+const AvatarDropdown: React.FC<GlobalHeaderRightProps> = ({ menu }) => {
+  const { initialState, setInitialState } = useModel('@@initialState');
+
+  const onMenuClick = useCallback(
+    (event: MenuInfo) => {
+      const { key } = event;
+      if (key === 'logout') {
+        setInitialState((s) => ({ ...s, currentUser: undefined }));
+        localStorage.removeItem('userData');
+        loginOut();
+        return;
+      }
+      history.push(`/account/${key}`);
+    },
+    [setInitialState],
+  );
+
+  const loading = (
+    <span className={`${styles.action} ${styles.account}`}>
+      <Spin
+        size="small"
+        style={{
+          marginLeft: 8,
+          marginRight: 8,
+        }}
+      />
+    </span>
+  );
+
+  if (!initialState) {
+    return loading;
+  }
+
+  const { currentUser } = initialState;
+
+  if (!currentUser || !currentUser.name) {
+    return loading;
+  }
+
+  const menuHeaderDropdown = (
+    <Menu className={styles.menu} selectedKeys={[]} onClick={onMenuClick}>
+      {menu && (
+        <Menu.Item key="center">
+          <UserOutlined />
+          个人中心
+        </Menu.Item>
+      )}
+      {menu && (
+        <Menu.Item key="settings">
+          <SettingOutlined />
+          个人设置
+        </Menu.Item>
+      )}
+      {menu && <Menu.Divider />}
+
+      <Menu.Item key="logout">
+        <LogoutOutlined />
+        退出登录
+      </Menu.Item>
+    </Menu>
+  );
+  return (
+    <HeaderDropdown overlay={menuHeaderDropdown}>
+      <span className={`${styles.action} ${styles.account}`}>
+        <Avatar size="small" className={styles.avatar} src={currentUser.avatar} alt="avatar" />
+        <span className={`${styles.name} anticon`}>{currentUser.name}</span>
+      </span>
+    </HeaderDropdown>
+  );
+};
+
+export default AvatarDropdown;

+ 84 - 0
src/components/RightContent/index.less

@@ -0,0 +1,84 @@
+@import '~antd/es/style/themes/default.less';
+
+@pro-header-hover-bg: rgba(0, 0, 0, 0.025);
+
+.menu {
+  :global(.anticon) {
+    margin-right: 8px;
+  }
+  :global(.ant-dropdown-menu-item) {
+    min-width: 160px;
+  }
+}
+
+.right {
+  display: flex;
+  float: right;
+  height: 48px;
+  margin-left: auto;
+  overflow: hidden;
+  .action {
+    display: flex;
+    align-items: center;
+    height: 48px;
+    padding: 0 12px;
+    cursor: pointer;
+    transition: all 0.3s;
+    > span {
+      vertical-align: middle;
+    }
+    &:hover {
+      background: @pro-header-hover-bg;
+    }
+    &:global(.opened) {
+      background: @pro-header-hover-bg;
+    }
+  }
+  .search {
+    padding: 0 12px;
+    &:hover {
+      background: transparent;
+    }
+  }
+  .account {
+    .avatar {
+      margin-right: 8px;
+      color: @primary-color;
+      vertical-align: top;
+      background: rgba(255, 255, 255, 0.85);
+    }
+  }
+}
+
+.dark {
+  .action {
+    &:hover {
+      background: #252a3d;
+    }
+    &:global(.opened) {
+      background: #252a3d;
+    }
+  }
+}
+
+@media only screen and (max-width: @screen-md) {
+  :global(.ant-divider-vertical) {
+    vertical-align: unset;
+  }
+  .name {
+    display: none;
+  }
+  .right {
+    position: absolute;
+    top: 0;
+    right: 12px;
+    .account {
+      .avatar {
+        margin-right: 0;
+      }
+    }
+    .search {
+      display: none;
+    }
+  }
+}

+ 103 - 0
src/components/RightContent/index.tsx

@@ -0,0 +1,103 @@
+/*
+ * @Author: your name
+ * @Date: 2021-09-03 14:28:27
+ * @LastEditTime: 2021-09-03 15:43:20
+ * @LastEditors: Please set LastEditors
+ * @Description: In User Settings Edit
+ * @FilePath: /MedicalWisdomCheckSys/src/components/RightContent/index.tsx
+ */
+import { Space,message } from 'antd';
+
+import React from 'react';
+import { useModel } from 'umi';
+import Avatar from './AvatarDropdown';
+
+import styles from './index.less';
+import NoticeIcon from '@/components/NoticeIcon/NoticeIcon';
+
+export type SiderTheme = 'light' | 'dark';
+
+
+const Notice = () => {
+  const list:API.NoticeIconItem[] = [
+    {
+      id: '000000001',
+      avatar: 'https://gw.alipayobjects.com/zos/rmsportal/ThXAXghbEsBCCSDihZxY.png',
+      title: '你收到了 14 份新周报',
+      datetime: '2017-08-09',
+      type: 'notification',
+    },
+    {
+      id: '000000002',
+      avatar: 'https://gw.alipayobjects.com/zos/rmsportal/OKJXDXrmkNshAMvwtvhu.png',
+      title: '你推荐的 曲妮妮 已通过第三轮面试',
+      datetime: '2017-08-08',
+      type: 'notification',
+    },
+  ];
+  return (
+    <NoticeIcon
+      count={10}
+      onItemClick={(item) => {
+        message.info(`${item.title} 被点击了`);
+      }}
+      onClear={(title: string, key: string) => message.info('点击了清空更多')}
+      loading={false}
+      clearText="清空"
+      viewMoreText="查看更多"
+      onViewMore={() => message.info('点击了查看更多')}
+      clearClose
+    >
+      <NoticeIcon.Tab
+        tabKey="notification"
+        count={2}
+        list={list}
+        title="通知"
+        emptyText="你已查看所有通知"
+        showViewMore
+      />
+      <NoticeIcon.Tab
+        tabKey="message"
+        count={2}
+        list={list}
+        title="消息"
+        emptyText="您已读完所有消息"
+        showViewMore
+      />
+      <NoticeIcon.Tab
+        tabKey="event"
+        title="待办"
+        emptyText="你已完成所有待办"
+        count={2}
+        list={list}
+        showViewMore
+      />
+    </NoticeIcon>
+  );
+};
+
+
+
+const GlobalHeaderRight: React.FC = () => {
+
+  const { initialState } = useModel('@@initialState');
+
+  if (!initialState || !initialState.settings) {
+    return null;
+  }
+
+  const { navTheme, layout } = initialState.settings;
+
+  let className = styles.right;
+
+  if ((navTheme === 'dark' && layout === 'top') || layout === 'mix') {
+    className = `${styles.right}  ${styles.dark}`;
+  }
+  return (
+    <Space className={className}>
+      <Notice />
+      <Avatar menu={true} />
+    </Space>
+  );
+};
+export default GlobalHeaderRight;

+ 33 - 0
src/components/antdProOverwrite/MccsProForm/index.tsx

@@ -0,0 +1,33 @@
+/*
+ * @Author: your name
+ * @Date: 2021-09-10 11:24:57
+ * @LastEditTime: 2021-09-14 15:21:24
+ * @LastEditors: Please set LastEditors
+ * @Description: In User Settings Edit
+ * @FilePath: /MedicalWisdomCheckSys/src/components/antdProOverwrite/MccsProForm/index.tsx
+ */
+
+
+import React from 'react'
+import ProForm from '@ant-design/pro-form';
+
+
+type MccsProFormProps = {
+    [propsName:string]:any
+}
+//使用方式参考 antd pro
+const MccsProForm:React.FC<MccsProFormProps> =(props)=>{
+    const {children} = props;
+    return (
+        <ProForm 
+           {...props} 
+           submitter={{
+            render:()=>null  
+           }}
+        >
+               {children}
+        </ProForm>
+    )
+}
+
+export default MccsProForm

+ 30 - 0
src/components/antdProOverwrite/MccsProFormText/index.tsx

@@ -0,0 +1,30 @@
+/*
+ * @Author: your name
+ * @Date: 2021-09-10 09:40:50
+ * @LastEditTime: 2021-09-10 09:50:43
+ * @LastEditors: Please set LastEditors
+ * @Description: In User Settings Edit
+ * @FilePath: /MedicalWisdomCheckSys/src/components/antdProOverwrite/MccsProFormText/index.tsx
+ */
+
+
+import React from 'react'
+import {
+    ProFormText,
+  } from '@ant-design/pro-form';
+
+
+type MccsProFormTextProps = {
+    [propsName:string]:any
+}
+//使用方式参考 antd pro
+const MccsProFormText:React.FC<MccsProFormTextProps> =(props)=>{
+
+    return (
+        <ProFormText {...props} />
+    )
+}
+
+export default MccsProFormText
+
+

+ 272 - 0
src/components/index.md

@@ -0,0 +1,272 @@
+---
+title: 业务组件
+sidemenu: false
+---
+
+> 此功能由[dumi](https://d.umijs.org/zh-CN/guide/advanced#umi-%E9%A1%B9%E7%9B%AE%E9%9B%86%E6%88%90%E6%A8%A1%E5%BC%8F)提供,dumi 是一个 📖 为组件开发场景而生的文档工具,用过的都说好。
+
+# 业务组件
+
+这里列举了 Pro 中所有用到的组件,这些组件不适合作为组件库,但是在业务中却真实需要。所以我们准备了这个文档,来指导大家是否需要使用这个组件。
+
+## Footer 页脚组件
+
+这个组件自带了一些 Pro 的配置,你一般都需要改掉它的信息。
+
+```tsx
+/**
+ * background: '#f0f2f5'
+ */
+import React from 'react';
+import Footer from '@/components/Footer';
+
+export default () => <Footer />;
+```
+
+## HeaderDropdown 头部下拉列表
+
+HeaderDropdown 是 antd Dropdown 的封装,但是增加了移动端的特殊处理,用法也是相同的。
+
+```tsx
+/**
+ * background: '#f0f2f5'
+ */
+import { Button, Menu } from 'antd';
+import React from 'react';
+import HeaderDropdown from '@/components/HeaderDropdown';
+
+export default () => {
+  const menuHeaderDropdown = (
+    <Menu selectedKeys={[]}>
+      <Menu.Item key="center">个人中心</Menu.Item>
+      <Menu.Item key="settings">个人设置</Menu.Item>
+      <Menu.Divider />
+      <Menu.Item key="logout">退出登录</Menu.Item>
+    </Menu>
+  );
+  return (
+    <HeaderDropdown overlay={menuHeaderDropdown}>
+      <Button>hover 展示菜单</Button>
+    </HeaderDropdown>
+  );
+};
+```
+
+## HeaderSearch 头部搜索框
+
+一个带补全数据的输入框,支持收起和展开 Input
+
+```tsx
+/**
+ * background: '#f0f2f5'
+ */
+import { Button, Menu } from 'antd';
+import React from 'react';
+import HeaderSearch from '@/components/HeaderSearch';
+
+export default () => {
+  return (
+    <HeaderSearch
+      placeholder="站内搜索"
+      defaultValue="umi ui"
+      options={[
+        { label: 'Ant Design Pro', value: 'Ant Design Pro' },
+        {
+          label: 'Ant Design',
+          value: 'Ant Design',
+        },
+        {
+          label: 'Pro Table',
+          value: 'Pro Table',
+        },
+        {
+          label: 'Pro Layout',
+          value: 'Pro Layout',
+        },
+      ]}
+      onSearch={(value) => {
+        console.log('input', value);
+      }}
+    />
+  );
+};
+```
+
+### API
+
+| 参数            | 说明                               | 类型                         | 默认值 |
+| --------------- | ---------------------------------- | ---------------------------- | ------ |
+| value           | 输入框的值                         | `string`                     | -      |
+| onChange        | 值修改后触发                       | `(value?: string) => void`   | -      |
+| onSearch        | 查询后触发                         | `(value?: string) => void`   | -      |
+| options         | 选项菜单的的列表                   | `{label,value}[]`            | -      |
+| defaultVisible  | 输入框默认是否显示,只有第一次生效 | `boolean`                    | -      |
+| visible         | 输入框是否显示                     | `boolean`                    | -      |
+| onVisibleChange | 输入框显示隐藏的回调函数           | `(visible: boolean) => void` | -      |
+
+## NoticeIcon 通知工具
+
+通知工具提供一个展示多种通知信息的界面。
+
+```tsx
+/**
+ * background: '#f0f2f5'
+ */
+import { message } from 'antd';
+import React from 'react';
+import NoticeIcon from '@/components/NoticeIcon/NoticeIcon';
+
+export default () => {
+  const list = [
+    {
+      id: '000000001',
+      avatar: 'https://gw.alipayobjects.com/zos/rmsportal/ThXAXghbEsBCCSDihZxY.png',
+      title: '你收到了 14 份新周报',
+      datetime: '2017-08-09',
+      type: 'notification',
+    },
+    {
+      id: '000000002',
+      avatar: 'https://gw.alipayobjects.com/zos/rmsportal/OKJXDXrmkNshAMvwtvhu.png',
+      title: '你推荐的 曲妮妮 已通过第三轮面试',
+      datetime: '2017-08-08',
+      type: 'notification',
+    },
+  ];
+  return (
+    <NoticeIcon
+      count={10}
+      onItemClick={(item) => {
+        message.info(`${item.title} 被点击了`);
+      }}
+      onClear={(title: string, key: string) => message.info('点击了清空更多')}
+      loading={false}
+      clearText="清空"
+      viewMoreText="查看更多"
+      onViewMore={() => message.info('点击了查看更多')}
+      clearClose
+    >
+      <NoticeIcon.Tab
+        tabKey="notification"
+        count={2}
+        list={list}
+        title="通知"
+        emptyText="你已查看所有通知"
+        showViewMore
+      />
+      <NoticeIcon.Tab
+        tabKey="message"
+        count={2}
+        list={list}
+        title="消息"
+        emptyText="您已读完所有消息"
+        showViewMore
+      />
+      <NoticeIcon.Tab
+        tabKey="event"
+        title="待办"
+        emptyText="你已完成所有待办"
+        count={2}
+        list={list}
+        showViewMore
+      />
+    </NoticeIcon>
+  );
+};
+```
+
+### NoticeIcon API
+
+| 参数 | 说明 | 类型 | 默认值 |
+| --- | --- | --- | --- |
+| count | 有多少未读通知 | `number` | - |
+| bell | 铃铛的图表 | `ReactNode` | - |
+| onClear | 点击清空数据按钮 | `(tabName: string, tabKey: string) => void` | - |
+| onItemClick | 未读消息列被点击 | `(item: API.NoticeIconData, tabProps: NoticeIconTabProps) => void` | - |
+| onViewMore | 查看更多的按钮点击 | `(tabProps: NoticeIconTabProps, e: MouseEvent) => void` | - |
+| onTabChange | 通知 Tab 的切换 | `(tabTile: string) => void;` | - |
+| popupVisible | 通知显示是否展示 | `boolean` | - |
+| onPopupVisibleChange | 通知信息显示隐藏的回调函数 | `(visible: boolean) => void` | - |
+| clearText | 清空按钮的文字 | `string` | - |
+| viewMoreText | 查看更多的按钮文字 | `string` | - |
+| clearClose | 展示清空按钮 | `boolean` | - |
+| emptyImage | 列表为空时的兜底展示 | `ReactNode` | - |
+
+### NoticeIcon.Tab API
+
+| 参数         | 说明               | 类型                                 | 默认值 |
+| ------------ | ------------------ | ------------------------------------ | ------ |
+| count        | 有多少未读通知     | `number`                             | -      |
+| title        | 通知 Tab 的标题    | `ReactNode`                          | -      |
+| showClear    | 展示清除按钮       | `boolean`                            | `true` |
+| showViewMore | 展示加载更         | `boolean`                            | `true` |
+| tabKey       | Tab 的唯一 key     | `string`                             | -      |
+| onClick      | 子项的单击事件     | `(item: API.NoticeIconData) => void` | -      |
+| onClear      | 清楚按钮的点击     | `()=>void`                           | -      |
+| emptyText    | 为空的时候测试     | `()=>void`                           | -      |
+| viewMoreText | 查看更多的按钮文字 | `string`                             | -      |
+| onViewMore   | 查看更多的按钮点击 | `( e: MouseEvent) => void`           | -      |
+| list         | 通知信息的列表     | `API.NoticeIconData`                 | -      |
+
+### NoticeIconData
+
+```tsx | pure
+export interface NoticeIconData {
+  id: string;
+  key: string;
+  avatar: string;
+  title: string;
+  datetime: string;
+  type: string;
+  read?: boolean;
+  description: string;
+  clickClose?: boolean;
+  extra: any;
+  status: string;
+}
+```
+
+## RightContent
+
+RightContent 是以上几个组件的组合,同时新增了 plugins 的 `SelectLang` 插件。
+
+```tsx | pure
+<Space>
+  <HeaderSearch
+    placeholder="站内搜索"
+    defaultValue="umi ui"
+    options={[
+      { label: <a href="https://umijs.org/zh/guide/umi-ui.html">umi ui</a>, value: 'umi ui' },
+      {
+        label: <a href="next.ant.design">Ant Design</a>,
+        value: 'Ant Design',
+      },
+      {
+        label: <a href="https://protable.ant.design/">Pro Table</a>,
+        value: 'Pro Table',
+      },
+      {
+        label: <a href="https://prolayout.ant.design/">Pro Layout</a>,
+        value: 'Pro Layout',
+      },
+    ]}
+  />
+  <Tooltip title="使用文档">
+    <span
+      className={styles.action}
+      onClick={() => {
+        window.location.href = 'https://pro.ant.design/docs/getting-started';
+      }}
+    >
+      <QuestionCircleOutlined />
+    </span>
+  </Tooltip>
+  <Avatar />
+  {REACT_APP_ENV && (
+    <span>
+      <Tag color={ENVTagColor[REACT_APP_ENV]}>{REACT_APP_ENV}</Tag>
+    </span>
+  )}
+  <SelectLang className={styles.action} />
+</Space>
+```

+ 61 - 0
src/e2e/baseLayout.e2e.js

@@ -0,0 +1,61 @@
+const { uniq } = require('lodash');
+const RouterConfig = require('../../config/config').default.routes;
+
+const BASE_URL = `http://localhost:${process.env.PORT || 8001}`;
+
+function formatter(routes, parentPath = '') {
+  const fixedParentPath = parentPath.replace(/\/{1,}/g, '/');
+  let result = [];
+  routes.forEach((item) => {
+    if (item.path && !item.path.startsWith('/')) {
+      result.push(`${fixedParentPath}/${item.path}`.replace(/\/{1,}/g, '/'));
+    }
+    if (item.path && item.path.startsWith('/')) {
+      result.push(`${item.path}`.replace(/\/{1,}/g, '/'));
+    }
+    if (item.routes) {
+      result = result.concat(
+        formatter(item.routes, item.path ? `${fixedParentPath}/${item.path}` : parentPath),
+      );
+    }
+  });
+  return uniq(result.filter((item) => !!item));
+}
+
+beforeEach(async () => {
+  await page.goto(`${BASE_URL}`);
+  await page.evaluate(() => {
+    localStorage.setItem('antd-pro-authority', '["admin"]');
+  });
+});
+
+describe('Ant Design Pro E2E test', () => {
+  const testPage = (path) => async () => {
+    await page.goto(`${BASE_URL}${path}`);
+    await page.waitForSelector('footer', {
+      timeout: 2000,
+    });
+    const haveFooter = await page.evaluate(
+      () => document.getElementsByTagName('footer').length > 0,
+    );
+    expect(haveFooter).toBeTruthy();
+  };
+
+  const routers = formatter(RouterConfig);
+  routers.forEach((route) => {
+    it(`test pages ${route}`, testPage(route));
+  });
+
+  it('topmenu should have footer', async () => {
+    const params = '?navTheme=light&layout=topmenu';
+    await page.goto(`${BASE_URL}${params}`);
+    await page.waitForSelector('footer', {
+      timeout: 2000,
+    });
+
+    const haveFooter = await page.evaluate(
+      () => document.getElementsByTagName('footer').length > 0,
+    );
+    expect(haveFooter).toBeTruthy();
+  });
+});

+ 83 - 0
src/global.less

@@ -0,0 +1,83 @@
+@import '~antd/es/style/themes/default.less';
+
+html,
+body,
+#root {
+  height: 100%;
+}
+
+//---------------custom------------
+
+@primary-color: #00528E !important;
+
+@primary-color-rgba:rgba(0,82,142,0.3);
+
+.ant-pro-sider-logo {
+   h1 {
+    font-size: 16px;
+   }
+}
+.ant-menu-submenu-selected {
+    //  color: @primary-color !important;
+     .ant-menu-item-selected {
+      // color: @primary-color !important;
+     }
+}
+
+.ant-tree-checkbox-checked .ant-tree-checkbox-inner {
+  background-color: #00528E;
+  border-color: #00528E;
+}
+
+
+//---------------custom------------
+
+.colorWeak {
+  filter: invert(80%);
+}
+
+.ant-layout {
+  min-height: 100vh;
+}
+.ant-pro-sider.ant-layout-sider.ant-pro-sider-fixed {
+  left: unset;
+}
+
+canvas {
+  display: block;
+}
+
+body {
+  text-rendering: optimizeLegibility;
+  -webkit-font-smoothing: antialiased;
+  -moz-osx-font-smoothing: grayscale;
+}
+
+ul,
+ol {
+  list-style: none;
+}
+
+@media (max-width: @screen-xs) {
+  .ant-table {
+    width: 100%;
+    overflow-x: auto;
+    &-thead > tr,
+    &-tbody > tr {
+      > th,
+      > td {
+        white-space: pre;
+        > span {
+          display: block;
+        }
+      }
+    }
+  }
+}
+
+// Compatible with IE11
+@media screen and(-ms-high-contrast: active), (-ms-high-contrast: none) {
+  body .ant-design-pro > .ant-layout {
+    min-height: 100vh;
+  }
+}

+ 91 - 0
src/global.tsx

@@ -0,0 +1,91 @@
+/*
+ * @Author: your name
+ * @Date: 2021-09-03 14:28:27
+ * @LastEditTime: 2021-09-03 16:22:34
+ * @LastEditors: your name
+ * @Description: In User Settings Edit
+ * @FilePath: /MedicalWisdomCheckSys/src/global.tsx
+ */
+import { Button, message, notification } from 'antd';
+
+import defaultSettings from '../config/defaultSettings';
+
+const { pwa } = defaultSettings;
+const isHttps = document.location.protocol === 'https:';
+
+// if pwa is true
+if (pwa) {
+  // Notify user if offline now
+  window.addEventListener('sw.offline', () => {
+    message.warning('app.pwa.offline');
+  });
+
+  // Pop up a prompt on the page asking the user if they want to use the latest version
+  window.addEventListener('sw.updated', (event: Event) => {
+    const e = event as CustomEvent;
+    const reloadSW = async () => {
+      // Check if there is sw whose state is waiting in ServiceWorkerRegistration
+      // https://developer.mozilla.org/en-US/docs/Web/API/ServiceWorkerRegistration
+      const worker = e.detail && e.detail.waiting;
+      if (!worker) {
+        return true;
+      }
+      // Send skip-waiting event to waiting SW with MessageChannel
+      await new Promise((resolve, reject) => {
+        const channel = new MessageChannel();
+        channel.port1.onmessage = (msgEvent) => {
+          if (msgEvent.data.error) {
+            reject(msgEvent.data.error);
+          } else {
+            resolve(msgEvent.data);
+          }
+        };
+        worker.postMessage({ type: 'skip-waiting' }, [channel.port2]);
+      });
+      // Refresh current page to use the updated HTML and other assets after SW has skiped waiting
+      window.location.reload(true);
+      return true;
+    };
+    const key = `open${Date.now()}`;
+    const btn = (
+      <Button
+        type="primary"
+        onClick={() => {
+          notification.close(key);
+          reloadSW();
+        }}
+      >
+        app.pwa.serviceworker.updated.ok
+      </Button>
+    );
+    notification.open({
+      message: 'app.pwa.serviceworker.updated',
+      description: 'pp.pwa.serviceworker.updated.hint',
+      btn,
+      key,
+      onClose: async () => null,
+    });
+  });
+} else if ('serviceWorker' in navigator && isHttps) {
+  // unregister service worker
+  const { serviceWorker } = navigator;
+  if (serviceWorker.getRegistrations) {
+    serviceWorker.getRegistrations().then((sws) => {
+      sws.forEach((sw) => {
+        sw.unregister();
+      });
+    });
+  }
+  serviceWorker.getRegistration().then((sw) => {
+    if (sw) sw.unregister();
+  });
+
+  // remove all caches
+  if (window.caches && window.caches.keys()) {
+    caches.keys().then((keys) => {
+      keys.forEach((key) => {
+        caches.delete(key);
+      });
+    });
+  }
+}

+ 26 - 0
src/locales/bn-BD.ts

@@ -0,0 +1,26 @@
+import component from './bn-BD/component';
+import globalHeader from './bn-BD/globalHeader';
+import menu from './bn-BD/menu';
+import pages from './bn-BD/pages';
+import pwa from './bn-BD/pwa';
+import settingDrawer from './bn-BD/settingDrawer';
+import settings from './bn-BD/settings';
+
+export default {
+  'navBar.lang': 'ভাষা',
+  'layout.user.link.help': 'সহায়তা',
+  'layout.user.link.privacy': 'গোপনীয়তা',
+  'layout.user.link.terms': 'শর্তাদি',
+  'app.copyright.produced': 'প্রযোজনা করেছেন অ্যান্ট ফিনান্সিয়াল এক্সপেরিয়েন্স ডিপার্টমেন্ট',
+  'app.preview.down.block': 'আপনার স্থানীয় প্রকল্পে এই পৃষ্ঠাটি ডাউনলোড করুন',
+  'app.welcome.link.fetch-blocks': 'সমস্ত ব্লক পান',
+  'app.welcome.link.block-list':
+    '`block` ডেভেলপমেন্ট এর উপর ভিত্তি করে দ্রুত স্ট্যান্ডার্ড, পৃষ্ঠাসমূহ তৈরি করুন।',
+  ...globalHeader,
+  ...menu,
+  ...settingDrawer,
+  ...settings,
+  ...pwa,
+  ...component,
+  ...pages,
+};

+ 5 - 0
src/locales/bn-BD/component.ts

@@ -0,0 +1,5 @@
+export default {
+  'component.tagSelect.expand': 'বিস্তৃত',
+  'component.tagSelect.collapse': 'সঙ্কুচিত',
+  'component.tagSelect.all': 'সব',
+};

+ 17 - 0
src/locales/bn-BD/globalHeader.ts

@@ -0,0 +1,17 @@
+export default {
+  'component.globalHeader.search': 'অনুসন্ধান করুন',
+  'component.globalHeader.search.example1': 'অনুসন্ধান উদাহরণ ১',
+  'component.globalHeader.search.example2': 'অনুসন্ধান উদাহরণ ২',
+  'component.globalHeader.search.example3': 'অনুসন্ধান উদাহরণ ৩',
+  'component.globalHeader.help': 'সহায়তা',
+  'component.globalHeader.notification': 'বিজ্ঞপ্তি',
+  'component.globalHeader.notification.empty': 'আপনি সমস্ত বিজ্ঞপ্তি দেখেছেন।',
+  'component.globalHeader.message': 'বার্তা',
+  'component.globalHeader.message.empty': 'আপনি সমস্ত বার্তা দেখেছেন।',
+  'component.globalHeader.event': 'ঘটনা',
+  'component.globalHeader.event.empty': 'আপনি সমস্ত ইভেন্ট দেখেছেন।',
+  'component.noticeIcon.clear': 'সাফ',
+  'component.noticeIcon.cleared': 'সাফ করা হয়েছে',
+  'component.noticeIcon.empty': 'বিজ্ঞপ্তি নেই',
+  'component.noticeIcon.view-more': 'আরো দেখুন',
+};

+ 52 - 0
src/locales/bn-BD/menu.ts

@@ -0,0 +1,52 @@
+export default {
+  'menu.welcome': 'স্বাগতম',
+  'menu.more-blocks': 'আরও ব্লক',
+  'menu.home': 'নীড়',
+  'menu.admin': 'অ্যাডমিন',
+  'menu.admin.sub-page': 'উপ-পৃষ্ঠা',
+  'menu.login': 'প্রবেশ',
+  'menu.register': 'নিবন্ধন',
+  'menu.register-result': 'নিবন্ধনে ফলাফল',
+  'menu.dashboard': 'ড্যাশবোর্ড',
+  'menu.dashboard.analysis': 'বিশ্লেষণ',
+  'menu.dashboard.monitor': 'নিরীক্ষণ',
+  'menu.dashboard.workplace': 'কর্মক্ষেত্র',
+  'menu.exception.403': '403',
+  'menu.exception.404': '404',
+  'menu.exception.500': '500',
+  'menu.form': 'ফর্ম',
+  'menu.form.basic-form': 'বেসিক ফর্ম',
+  'menu.form.step-form': 'পদক্ষেপ ফর্ম',
+  'menu.form.step-form.info': 'পদক্ষেপ ফর্ম (স্থানান্তর তথ্য লিখুন)',
+  'menu.form.step-form.confirm': 'পদক্ষেপ ফর্ম (স্থানান্তর তথ্য নিশ্চিত করুন)',
+  'menu.form.step-form.result': 'পদক্ষেপ ফর্ম (সমাপ্ত)',
+  'menu.form.advanced-form': 'উন্নত ফর্ম',
+  'menu.list': 'তালিকা',
+  'menu.list.table-list': 'অনুসন্ধানের টেবিল',
+  'menu.list.basic-list': 'বেসিক তালিকা',
+  'menu.list.card-list': 'কার্ডের তালিকা',
+  'menu.list.search-list': 'অনুসন্ধানের তালিকা',
+  'menu.list.search-list.articles': 'অনুসন্ধানের তালিকা (নিবন্ধসমূহ)',
+  'menu.list.search-list.projects': 'অনুসন্ধানের তালিকা (প্রকল্পগুলি)',
+  'menu.list.search-list.applications': 'অনুসন্ধানের তালিকা (অ্যাপ্লিকেশন)',
+  'menu.profile': 'প্রোফাইল',
+  'menu.profile.basic': 'বেসিক প্রোফাইল',
+  'menu.profile.advanced': 'উন্নত প্রোফাইল',
+  'menu.result': 'ফলাফল',
+  'menu.result.success': 'সাফল্য',
+  'menu.result.fail': 'ব্যর্থ',
+  'menu.exception': 'ব্যতিক্রম',
+  'menu.exception.not-permission': '403',
+  'menu.exception.not-find': '404',
+  'menu.exception.server-error': '500',
+  'menu.exception.trigger': 'ট্রিগার',
+  'menu.account': 'হিসাব',
+  'menu.account.center': 'অ্যাকাউন্ট কেন্দ্র',
+  'menu.account.settings': 'অ্যাকাউন্ট সেটিংস',
+  'menu.account.trigger': 'ট্রিগার ত্রুটি',
+  'menu.account.logout': 'প্রস্থান',
+  'menu.editor': 'গ্রাফিক সম্পাদক',
+  'menu.editor.flow': 'ফ্লো এডিটর',
+  'menu.editor.mind': 'মাইন্ড এডিটর',
+  'menu.editor.koni': 'কোনি সম্পাদক',
+};

+ 70 - 0
src/locales/bn-BD/pages.ts

@@ -0,0 +1,70 @@
+export default {
+  'pages.layouts.userLayout.title':
+    'পিঁপড়া ডিজাইন হচ্ছে সিহু জেলার সবচেয়ে প্রভাবশালী ওয়েব ডিজাইনের স্পেসিফিকেশন',
+  'pages.login.accountLogin.tab': 'অ্যাকাউন্টে লগইন',
+  'pages.login.accountLogin.errorMessage': 'ভুল ব্যবহারকারীর নাম/পাসওয়ার্ড(admin/ant.design)',
+  'pages.login.failure': 'লগইন ব্যর্থ হয়েছে। আবার চেষ্টা করুন!',
+  'pages.login.success': 'সফল লগইন!',
+  'pages.login.username.placeholder': 'ব্যবহারকারীর নাম: admin or user',
+  'pages.login.username.required': 'আপনার ব্যবহারকারীর নাম ইনপুট করুন!',
+  'pages.login.password.placeholder': 'পাসওয়ার্ড: ant.design',
+  'pages.login.password.required': 'আপনার পাসওয়ার্ড ইনপুট করুন!',
+  'pages.login.phoneLogin.tab': 'ফোন লগইন',
+  'pages.login.phoneLogin.errorMessage': 'যাচাইকরণ কোড ত্রুটি',
+  'pages.login.phoneNumber.placeholder': 'ফোন নম্বর',
+  'pages.login.phoneNumber.required': 'আপনার ফোন নম্বর ইনপুট করুন!',
+  'pages.login.phoneNumber.invalid': 'ফোন নম্বরটি সঠিক নয়!',
+  'pages.login.captcha.placeholder': 'যাচাইকরণের কোড',
+  'pages.login.captcha.required': 'দয়া করে ভেরিফিকেশন কোডটি ইনপুট করুন!',
+  'pages.login.phoneLogin.getVerificationCode': 'কোড পান',
+  'pages.getCaptchaSecondText': 'সেকেন্ড',
+  'pages.login.rememberMe': 'আমাকে মনে রাখুন',
+  'pages.login.forgotPassword': 'পাসওয়ার্ড ভুলে গেছেন?',
+  'pages.login.submit': 'প্রবেশ করুন',
+  'pages.login.loginWith': 'লগইন করতে পারেন:',
+  'pages.login.registerAccount': 'অ্যাকাউন্ট নিবন্ধন করুন',
+  'pages.welcome.advancedComponent': 'অ্যাডভান্সড কম্পোনেন্ট',
+  'pages.welcome.link': 'স্বাগতম',
+  'pages.welcome.advancedLayout': 'অ্যাডভান্সড লেআউট',
+  'pages.welcome.alertMessage': 'দ্রুত এবং শক্তিশালী ভারী শুল্ক উপাদান প্রকাশ করা হয়েছে।',
+  'pages.admin.subPage.title': 'এই পৃষ্ঠাটি কেবল অ্যাডমিন দ্বারা দেখা যাবে',
+  'pages.admin.subPage.alertMessage':
+    'UMI UI এখন প্রকাশিত হয়েছে, অভিজ্ঞতা শুরু করতে npm run ui ব্যবহার করতে স্বাগতম।',
+  'pages.searchTable.createForm.newRule': 'নতুন বিধি',
+  'pages.searchTable.updateForm.ruleConfig': 'বিধি কনফিগারেশন',
+  'pages.searchTable.updateForm.basicConfig': 'মৌলিক তথ্য',
+  'pages.searchTable.updateForm.ruleName.nameLabel': 'বিধি নাম',
+  'pages.searchTable.updateForm.ruleName.nameRules': 'বিধির নাম লিখুন!',
+  'pages.searchTable.updateForm.ruleDesc.descLabel': 'বিধির বিবরণ',
+  'pages.searchTable.updateForm.ruleDesc.descPlaceholder': 'কমপক্ষে পাঁচটি অক্ষর লিখুন',
+  'pages.searchTable.updateForm.ruleDesc.descRules':
+    'কমপক্ষে পাঁচটি অক্ষরের একটি বিধান বিবরণ লিখুন!',
+  'pages.searchTable.updateForm.ruleProps.title': 'বৈশিষ্ট্য কনফিগার করুন',
+  'pages.searchTable.updateForm.object': 'নিরীক্ষণ অবজেক্ট',
+  'pages.searchTable.updateForm.ruleProps.templateLabel': 'বিধি টেম্পলেট',
+  'pages.searchTable.updateForm.ruleProps.typeLabel': 'বিধি প্রকার',
+  'pages.searchTable.updateForm.schedulingPeriod.title': 'সময়সূচী নির্ধারণ করুন',
+  'pages.searchTable.updateForm.schedulingPeriod.timeLabel': 'শুরুর সময়',
+  'pages.searchTable.updateForm.schedulingPeriod.timeRules': 'একটি শুরুর সময় চয়ন করুন!',
+  'pages.searchTable.titleDesc': 'বর্ণনা',
+  'pages.searchTable.ruleName': 'বিধি নাম প্রয়োজন',
+  'pages.searchTable.titleCallNo': 'পরিষেবা কল সংখ্যা',
+  'pages.searchTable.titleStatus': 'অবস্থা',
+  'pages.searchTable.nameStatus.default': 'ডিফল্ট',
+  'pages.searchTable.nameStatus.running': 'চলমান',
+  'pages.searchTable.nameStatus.online': 'অনলাইন',
+  'pages.searchTable.nameStatus.abnormal': 'অস্বাভাবিক',
+  'pages.searchTable.titleUpdatedAt': 'সর্বশেষ নির্ধারিত',
+  'pages.searchTable.exception': 'ব্যতিক্রম জন্য কারণ লিখুন!',
+  'pages.searchTable.titleOption': 'অপশন',
+  'pages.searchTable.config': 'কনফিগারেশন',
+  'pages.searchTable.subscribeAlert': 'সতর্কতা সাবস্ক্রাইব করুন',
+  'pages.searchTable.title': 'ইনকয়েরি ফরম',
+  'pages.searchTable.new': 'নতুন',
+  'pages.searchTable.chosen': 'নির্বাচিত',
+  'pages.searchTable.item': 'আইটেম',
+  'pages.searchTable.totalServiceCalls': 'পরিষেবা কলগুলির মোট সংখ্যা',
+  'pages.searchTable.tenThousand': '000',
+  'pages.searchTable.batchDeletion': 'একসাখে ডিলিট',
+  'pages.searchTable.batchApproval': 'একসাখে অনুমোদন',
+};

+ 7 - 0
src/locales/bn-BD/pwa.ts

@@ -0,0 +1,7 @@
+export default {
+  'app.pwa.offline': 'আপনি এখন অফলাইন',
+  'app.pwa.serviceworker.updated': 'নতুন সামগ্রী উপলব্ধ',
+  'app.pwa.serviceworker.updated.hint':
+    'বর্তমান পৃষ্ঠাটি পুনরায় লোড করতে দয়া করে "রিফ্রেশ" বোতাম টিপুন',
+  'app.pwa.serviceworker.updated.ok': 'রিফ্রেশ',
+};

+ 31 - 0
src/locales/bn-BD/settingDrawer.ts

@@ -0,0 +1,31 @@
+export default {
+  'app.setting.pagestyle': 'পৃষ্ঠা স্টাইল সেটিং',
+  'app.setting.pagestyle.dark': 'ডার্ক স্টাইল',
+  'app.setting.pagestyle.light': 'লাইট স্টাইল',
+  'app.setting.content-width': 'সামগ্রীর প্রস্থ',
+  'app.setting.content-width.fixed': 'স্থির',
+  'app.setting.content-width.fluid': 'প্রবাহী',
+  'app.setting.themecolor': 'থিম রঙ',
+  'app.setting.themecolor.dust': 'ডাস্ট রেড',
+  'app.setting.themecolor.volcano': 'আগ্নেয়গিরি',
+  'app.setting.themecolor.sunset': 'সানসেট কমলা',
+  'app.setting.themecolor.cyan': 'সবুজাভ নীল',
+  'app.setting.themecolor.green': 'পোলার সবুজ',
+  'app.setting.themecolor.daybreak': 'দিবস ব্রেক ব্লু (ডিফল্ট)',
+  'app.setting.themecolor.geekblue': 'গিক আঠালো',
+  'app.setting.themecolor.purple': 'গোল্ডেন বেগুনি',
+  'app.setting.navigationmode': 'নেভিগেশন মোড',
+  'app.setting.sidemenu': 'সাইড মেনু লেআউট',
+  'app.setting.topmenu': 'টপ মেনু লেআউট',
+  'app.setting.fixedheader': 'স্থির হেডার',
+  'app.setting.fixedsidebar': 'স্থির সাইডবার',
+  'app.setting.fixedsidebar.hint': 'সাইড মেনু বিন্যাসে কাজ করে',
+  'app.setting.hideheader': 'স্ক্রোল করার সময় হেডার লুকানো',
+  'app.setting.hideheader.hint': 'লুকানো হেডার সক্ষম থাকলে কাজ করে',
+  'app.setting.othersettings': 'অন্যান্য সেটিংস্',
+  'app.setting.weakmode': 'দুর্বল মোড',
+  'app.setting.copy': 'সেটিং কপি করুন',
+  'app.setting.copyinfo': 'সাফল্যের অনুলিপি করুন - প্রতিস্থাপন করুন: src/models/setting.js',
+  'app.setting.production.hint':
+    'কেবল বিকাশের পরিবেশে প্যানেল শো সেট করা হচ্ছে, দয়া করে ম্যানুয়ালি সংশোধন করুন',
+};

+ 59 - 0
src/locales/bn-BD/settings.ts

@@ -0,0 +1,59 @@
+export default {
+  'app.settings.menuMap.basic': 'মৌলিক বৈশিষ্ট্যসহ',
+  'app.settings.menuMap.security': 'নিরাপত্তা বিন্যাস',
+  'app.settings.menuMap.binding': 'অ্যাকাউন্ট বাঁধাই',
+  'app.settings.menuMap.notification': 'নতুন বার্তা বিজ্ঞপ্তি',
+  'app.settings.basic.avatar': 'অবতার',
+  'app.settings.basic.change-avatar': 'অবতার পরিবর্তন করুন',
+  'app.settings.basic.email': 'ইমেইল',
+  'app.settings.basic.email-message': 'আপনার ইমেইল ইনপুট করুন!',
+  'app.settings.basic.nickname': 'ডাক নাম',
+  'app.settings.basic.nickname-message': 'আপনার ডাকনামটি ইনপুট করুন!',
+  'app.settings.basic.profile': 'ব্যক্তিগত প্রোফাইল',
+  'app.settings.basic.profile-message': 'আপনার ব্যক্তিগত প্রোফাইল ইনপুট করুন!',
+  'app.settings.basic.profile-placeholder': 'নিজের সাথে সংক্ষিপ্ত পরিচয়',
+  'app.settings.basic.country': 'দেশ/অঞ্চল',
+  'app.settings.basic.country-message': 'আপনার দেশ ইনপুট করুন!',
+  'app.settings.basic.geographic': 'প্রদেশ বা শহর',
+  'app.settings.basic.geographic-message': 'আপনার ভৌগলিক তথ্য ইনপুট করুন!',
+  'app.settings.basic.address': 'রাস্তার ঠিকানা',
+  'app.settings.basic.address-message': 'দয়া করে আপনার ঠিকানা ইনপুট করুন!',
+  'app.settings.basic.phone': 'ফোন নম্বর',
+  'app.settings.basic.phone-message': 'আপনার ফোন ইনপুট করুন!',
+  'app.settings.basic.update': 'তথ্য হালনাগাদ',
+  'app.settings.security.strong': 'শক্তিশালী',
+  'app.settings.security.medium': 'মধ্যম',
+  'app.settings.security.weak': 'দুর্বল',
+  'app.settings.security.password': 'অ্যাকাউন্টের পাসওয়ার্ড',
+  'app.settings.security.password-description': 'বর্তমান পাসওয়ার্ড শক্তি',
+  'app.settings.security.phone': 'সুরক্ষা ফোন',
+  'app.settings.security.phone-description': 'আবদ্ধ ফোন',
+  'app.settings.security.question': 'নিরাপত্তা প্রশ্ন',
+  'app.settings.security.question-description':
+    'সুরক্ষা প্রশ্ন সেট করা নেই, এবং সুরক্ষা নীতি কার্যকরভাবে অ্যাকাউন্ট সুরক্ষা রক্ষা করতে পারে',
+  'app.settings.security.email': 'ব্যাকআপ ইমেইল',
+  'app.settings.security.email-description': 'বাউন্ড ইমেইল',
+  'app.settings.security.mfa': 'MFA ডিভাইস',
+  'app.settings.security.mfa-description':
+    "আনবাউন্ড এমএফএ ডিভাইস, বাঁধাইয়ের পরে, দু'বার নিশ্চিত করা যায়",
+  'app.settings.security.modify': 'পরিবর্তন করুন',
+  'app.settings.security.set': 'সেট',
+  'app.settings.security.bind': 'বাঁধাই',
+  'app.settings.binding.taobao': 'বাঁধাই তাওবাও',
+  'app.settings.binding.taobao-description': 'বর্তমানে আনবাউন্ড তাওবাও অ্যাকাউন্ট',
+  'app.settings.binding.alipay': 'বাইন্ডিং আলিপে',
+  'app.settings.binding.alipay-description': 'বর্তমানে আনবাউন্ড আলিপে অ্যাকাউন্ট',
+  'app.settings.binding.dingding': 'বাঁধাই ডিঙ্গটালক',
+  'app.settings.binding.dingding-description': 'বর্তমানে আনবাউন্ড ডিঙ্গটাল অ্যাকাউন্ট',
+  'app.settings.binding.bind': 'বাঁধাই',
+  'app.settings.notification.password': 'অ্যাকাউন্টের পাসওয়ার্ড',
+  'app.settings.notification.password-description':
+    'অন্যান্য ব্যবহারকারীর বার্তাগুলি স্টেশন চিঠি আকারে জানানো হবে',
+  'app.settings.notification.messages': 'সিস্টেম বার্তা',
+  'app.settings.notification.messages-description':
+    'সিস্টেম বার্তাগুলি স্টেশন চিঠির আকারে জানানো হবে',
+  'app.settings.notification.todo': 'করণীয় বিজ্ঞপ্তি',
+  'app.settings.notification.todo-description': 'করণীয় তালিকাটি স্টেশন থেকে চিঠি আকারে জানানো হবে',
+  'app.settings.open': 'খোলা',
+  'app.settings.close': 'বন্ধ',
+};

+ 25 - 0
src/locales/en-US.ts

@@ -0,0 +1,25 @@
+import component from './en-US/component';
+import globalHeader from './en-US/globalHeader';
+import menu from './en-US/menu';
+import pages from './en-US/pages';
+import pwa from './en-US/pwa';
+import settingDrawer from './en-US/settingDrawer';
+import settings from './en-US/settings';
+
+export default {
+  'navBar.lang': 'Languages',
+  'layout.user.link.help': 'Help',
+  'layout.user.link.privacy': 'Privacy',
+  'layout.user.link.terms': 'Terms',
+  'app.copyright.produced': 'Produced by Ant Financial Experience Department',
+  'app.preview.down.block': 'Download this page to your local project',
+  'app.welcome.link.fetch-blocks': 'Get all block',
+  'app.welcome.link.block-list': 'Quickly build standard, pages based on `block` development',
+  ...globalHeader,
+  ...menu,
+  ...settingDrawer,
+  ...settings,
+  ...pwa,
+  ...component,
+  ...pages,
+};

+ 5 - 0
src/locales/en-US/component.ts

@@ -0,0 +1,5 @@
+export default {
+  'component.tagSelect.expand': 'Expand',
+  'component.tagSelect.collapse': 'Collapse',
+  'component.tagSelect.all': 'All',
+};

+ 17 - 0
src/locales/en-US/globalHeader.ts

@@ -0,0 +1,17 @@
+export default {
+  'component.globalHeader.search': 'Search',
+  'component.globalHeader.search.example1': 'Search example 1',
+  'component.globalHeader.search.example2': 'Search example 2',
+  'component.globalHeader.search.example3': 'Search example 3',
+  'component.globalHeader.help': 'Help',
+  'component.globalHeader.notification': 'Notification',
+  'component.globalHeader.notification.empty': 'You have viewed all notifications.',
+  'component.globalHeader.message': 'Message',
+  'component.globalHeader.message.empty': 'You have viewed all messsages.',
+  'component.globalHeader.event': 'Event',
+  'component.globalHeader.event.empty': 'You have viewed all events.',
+  'component.noticeIcon.clear': 'Clear',
+  'component.noticeIcon.cleared': 'Cleared',
+  'component.noticeIcon.empty': 'No notifications',
+  'component.noticeIcon.view-more': 'View more',
+};

+ 52 - 0
src/locales/en-US/menu.ts

@@ -0,0 +1,52 @@
+export default {
+  'menu.welcome': 'Welcome',
+  'menu.more-blocks': 'More Blocks',
+  'menu.home': 'Home',
+  'menu.admin': 'Admin',
+  'menu.admin.sub-page': 'Sub-Page',
+  'menu.login': 'Login',
+  'menu.register': 'Register',
+  'menu.register-result': 'Register Result',
+  'menu.dashboard': 'Dashboard',
+  'menu.dashboard.analysis': 'Analysis',
+  'menu.dashboard.monitor': 'Monitor',
+  'menu.dashboard.workplace': 'Workplace',
+  'menu.exception.403': '403',
+  'menu.exception.404': '404',
+  'menu.exception.500': '500',
+  'menu.form': 'Form',
+  'menu.form.basic-form': 'Basic Form',
+  'menu.form.step-form': 'Step Form',
+  'menu.form.step-form.info': 'Step Form(write transfer information)',
+  'menu.form.step-form.confirm': 'Step Form(confirm transfer information)',
+  'menu.form.step-form.result': 'Step Form(finished)',
+  'menu.form.advanced-form': 'Advanced Form',
+  'menu.list': 'List',
+  'menu.list.table-list': 'Search Table',
+  'menu.list.basic-list': 'Basic List',
+  'menu.list.card-list': 'Card List',
+  'menu.list.search-list': 'Search List',
+  'menu.list.search-list.articles': 'Search List(articles)',
+  'menu.list.search-list.projects': 'Search List(projects)',
+  'menu.list.search-list.applications': 'Search List(applications)',
+  'menu.profile': 'Profile',
+  'menu.profile.basic': 'Basic Profile',
+  'menu.profile.advanced': 'Advanced Profile',
+  'menu.result': 'Result',
+  'menu.result.success': 'Success',
+  'menu.result.fail': 'Fail',
+  'menu.exception': 'Exception',
+  'menu.exception.not-permission': '403',
+  'menu.exception.not-find': '404',
+  'menu.exception.server-error': '500',
+  'menu.exception.trigger': 'Trigger',
+  'menu.account': 'Account',
+  'menu.account.center': 'Account Center',
+  'menu.account.settings': 'Account Settings',
+  'menu.account.trigger': 'Trigger Error',
+  'menu.account.logout': 'Logout',
+  'menu.editor': 'Graphic Editor',
+  'menu.editor.flow': 'Flow Editor',
+  'menu.editor.mind': 'Mind Editor',
+  'menu.editor.koni': 'Koni Editor',
+};

+ 70 - 0
src/locales/en-US/pages.ts

@@ -0,0 +1,70 @@
+export default {
+  'pages.layouts.userLayout.title':
+    'Ant Design is the most influential web design specification in Xihu district',
+  'pages.login.accountLogin.tab': 'Account Login',
+  'pages.login.accountLogin.errorMessage': 'Incorrect username/password(admin/ant.design)',
+  'pages.login.failure': 'Login failed, please try again!',
+  'pages.login.success': 'Login successful!',
+  'pages.login.username.placeholder': 'Username: admin or user',
+  'pages.login.username.required': 'Please input your username!',
+  'pages.login.password.placeholder': 'Password: ant.design',
+  'pages.login.password.required': 'Please input your password!',
+  'pages.login.phoneLogin.tab': 'Phone Login',
+  'pages.login.phoneLogin.errorMessage': 'Verification Code Error',
+  'pages.login.phoneNumber.placeholder': 'Phone Number',
+  'pages.login.phoneNumber.required': 'Please input your phone number!',
+  'pages.login.phoneNumber.invalid': 'Phone number is invalid!',
+  'pages.login.captcha.placeholder': 'Verification Code',
+  'pages.login.captcha.required': 'Please input verification code!',
+  'pages.login.phoneLogin.getVerificationCode': 'Get Code',
+  'pages.getCaptchaSecondText': 'sec(s)',
+  'pages.login.rememberMe': 'Remember me',
+  'pages.login.forgotPassword': 'Forgot Password ?',
+  'pages.login.submit': 'Login',
+  'pages.login.loginWith': 'Login with :',
+  'pages.login.registerAccount': 'Register Account',
+  'pages.welcome.advancedComponent': 'Advanced Component',
+  'pages.welcome.link': 'Welcome',
+  'pages.welcome.advancedLayout': 'Advanced Layout',
+  'pages.welcome.alertMessage': 'Faster and stronger heavy-duty components have been released.',
+  'pages.admin.subPage.title': 'This page can only be viewed by Admin',
+  'pages.admin.subPage.alertMessage':
+    'Umi ui is now released, welcome to use npm run ui to start the experience.',
+  'pages.searchTable.createForm.newRule': 'New Rule',
+  'pages.searchTable.updateForm.ruleConfig': 'Rule configuration',
+  'pages.searchTable.updateForm.basicConfig': 'Basic Information',
+  'pages.searchTable.updateForm.ruleName.nameLabel': 'Rule Name',
+  'pages.searchTable.updateForm.ruleName.nameRules': 'Please enter the rule name!',
+  'pages.searchTable.updateForm.ruleDesc.descLabel': 'Rule Description',
+  'pages.searchTable.updateForm.ruleDesc.descPlaceholder': 'Please enter at least five characters',
+  'pages.searchTable.updateForm.ruleDesc.descRules':
+    'Please enter a rule description of at least five characters!',
+  'pages.searchTable.updateForm.ruleProps.title': 'Configure Properties',
+  'pages.searchTable.updateForm.object': 'Monitoring Object',
+  'pages.searchTable.updateForm.ruleProps.templateLabel': 'Rule Template',
+  'pages.searchTable.updateForm.ruleProps.typeLabel': 'Rule Type',
+  'pages.searchTable.updateForm.schedulingPeriod.title': 'Set Scheduling Period',
+  'pages.searchTable.updateForm.schedulingPeriod.timeLabel': 'Starting Time',
+  'pages.searchTable.updateForm.schedulingPeriod.timeRules': 'Please choose a start time!',
+  'pages.searchTable.titleDesc': 'Description',
+  'pages.searchTable.ruleName': 'Rule name is required',
+  'pages.searchTable.titleCallNo': 'Number of Service Calls',
+  'pages.searchTable.titleStatus': 'Status',
+  'pages.searchTable.nameStatus.default': 'default',
+  'pages.searchTable.nameStatus.running': 'running',
+  'pages.searchTable.nameStatus.online': 'online',
+  'pages.searchTable.nameStatus.abnormal': 'abnormal',
+  'pages.searchTable.titleUpdatedAt': 'Last Scheduled at',
+  'pages.searchTable.exception': 'Please enter the reason for the exception!',
+  'pages.searchTable.titleOption': 'Option',
+  'pages.searchTable.config': 'Configuration',
+  'pages.searchTable.subscribeAlert': 'Subscribe to alerts',
+  'pages.searchTable.title': 'Enquiry Form',
+  'pages.searchTable.new': 'New',
+  'pages.searchTable.chosen': 'chosen',
+  'pages.searchTable.item': 'item',
+  'pages.searchTable.totalServiceCalls': 'Total Number of Service Calls',
+  'pages.searchTable.tenThousand': '0000',
+  'pages.searchTable.batchDeletion': 'bacth deletion',
+  'pages.searchTable.batchApproval': 'batch approval',
+};

+ 6 - 0
src/locales/en-US/pwa.ts

@@ -0,0 +1,6 @@
+export default {
+  'app.pwa.offline': 'You are offline now',
+  'app.pwa.serviceworker.updated': 'New content is available',
+  'app.pwa.serviceworker.updated.hint': 'Please press the "Refresh" button to reload current page',
+  'app.pwa.serviceworker.updated.ok': 'Refresh',
+};

+ 31 - 0
src/locales/en-US/settingDrawer.ts

@@ -0,0 +1,31 @@
+export default {
+  'app.setting.pagestyle': 'Page style setting',
+  'app.setting.pagestyle.dark': 'Dark style',
+  'app.setting.pagestyle.light': 'Light style',
+  'app.setting.content-width': 'Content Width',
+  'app.setting.content-width.fixed': 'Fixed',
+  'app.setting.content-width.fluid': 'Fluid',
+  'app.setting.themecolor': 'Theme Color',
+  'app.setting.themecolor.dust': 'Dust Red',
+  'app.setting.themecolor.volcano': 'Volcano',
+  'app.setting.themecolor.sunset': 'Sunset Orange',
+  'app.setting.themecolor.cyan': 'Cyan',
+  'app.setting.themecolor.green': 'Polar Green',
+  'app.setting.themecolor.daybreak': 'Daybreak Blue (default)',
+  'app.setting.themecolor.geekblue': 'Geek Glue',
+  'app.setting.themecolor.purple': 'Golden Purple',
+  'app.setting.navigationmode': 'Navigation Mode',
+  'app.setting.sidemenu': 'Side Menu Layout',
+  'app.setting.topmenu': 'Top Menu Layout',
+  'app.setting.fixedheader': 'Fixed Header',
+  'app.setting.fixedsidebar': 'Fixed Sidebar',
+  'app.setting.fixedsidebar.hint': 'Works on Side Menu Layout',
+  'app.setting.hideheader': 'Hidden Header when scrolling',
+  'app.setting.hideheader.hint': 'Works when Hidden Header is enabled',
+  'app.setting.othersettings': 'Other Settings',
+  'app.setting.weakmode': 'Weak Mode',
+  'app.setting.copy': 'Copy Setting',
+  'app.setting.copyinfo': 'copy success,please replace defaultSettings in src/models/setting.js',
+  'app.setting.production.hint':
+    'Setting panel shows in development environment only, please manually modify',
+};

+ 60 - 0
src/locales/en-US/settings.ts

@@ -0,0 +1,60 @@
+export default {
+  'app.settings.menuMap.basic': 'Basic Settings',
+  'app.settings.menuMap.security': 'Security Settings',
+  'app.settings.menuMap.binding': 'Account Binding',
+  'app.settings.menuMap.notification': 'New Message Notification',
+  'app.settings.basic.avatar': 'Avatar',
+  'app.settings.basic.change-avatar': 'Change avatar',
+  'app.settings.basic.email': 'Email',
+  'app.settings.basic.email-message': 'Please input your email!',
+  'app.settings.basic.nickname': 'Nickname',
+  'app.settings.basic.nickname-message': 'Please input your Nickname!',
+  'app.settings.basic.profile': 'Personal profile',
+  'app.settings.basic.profile-message': 'Please input your personal profile!',
+  'app.settings.basic.profile-placeholder': 'Brief introduction to yourself',
+  'app.settings.basic.country': 'Country/Region',
+  'app.settings.basic.country-message': 'Please input your country!',
+  'app.settings.basic.geographic': 'Province or city',
+  'app.settings.basic.geographic-message': 'Please input your geographic info!',
+  'app.settings.basic.address': 'Street Address',
+  'app.settings.basic.address-message': 'Please input your address!',
+  'app.settings.basic.phone': 'Phone Number',
+  'app.settings.basic.phone-message': 'Please input your phone!',
+  'app.settings.basic.update': 'Update Information',
+  'app.settings.security.strong': 'Strong',
+  'app.settings.security.medium': 'Medium',
+  'app.settings.security.weak': 'Weak',
+  'app.settings.security.password': 'Account Password',
+  'app.settings.security.password-description': 'Current password strength',
+  'app.settings.security.phone': 'Security Phone',
+  'app.settings.security.phone-description': 'Bound phone',
+  'app.settings.security.question': 'Security Question',
+  'app.settings.security.question-description':
+    'The security question is not set, and the security policy can effectively protect the account security',
+  'app.settings.security.email': 'Backup Email',
+  'app.settings.security.email-description': 'Bound Email',
+  'app.settings.security.mfa': 'MFA Device',
+  'app.settings.security.mfa-description':
+    'Unbound MFA device, after binding, can be confirmed twice',
+  'app.settings.security.modify': 'Modify',
+  'app.settings.security.set': 'Set',
+  'app.settings.security.bind': 'Bind',
+  'app.settings.binding.taobao': 'Binding Taobao',
+  'app.settings.binding.taobao-description': 'Currently unbound Taobao account',
+  'app.settings.binding.alipay': 'Binding Alipay',
+  'app.settings.binding.alipay-description': 'Currently unbound Alipay account',
+  'app.settings.binding.dingding': 'Binding DingTalk',
+  'app.settings.binding.dingding-description': 'Currently unbound DingTalk account',
+  'app.settings.binding.bind': 'Bind',
+  'app.settings.notification.password': 'Account Password',
+  'app.settings.notification.password-description':
+    'Messages from other users will be notified in the form of a station letter',
+  'app.settings.notification.messages': 'System Messages',
+  'app.settings.notification.messages-description':
+    'System messages will be notified in the form of a station letter',
+  'app.settings.notification.todo': 'To-do Notification',
+  'app.settings.notification.todo-description':
+    'The to-do list will be notified in the form of a letter from the station',
+  'app.settings.open': 'Open',
+  'app.settings.close': 'Close',
+};

+ 24 - 0
src/locales/fa-IR.ts

@@ -0,0 +1,24 @@
+import component from './fa-IR/component';
+import globalHeader from './fa-IR/globalHeader';
+import menu from './fa-IR/menu';
+import pwa from './fa-IR/pwa';
+import settingDrawer from './fa-IR/settingDrawer';
+import settings from './fa-IR/settings';
+import pages from './fa-IR/pages';
+
+export default {
+  'navBar.lang': 'زبان ها  ',
+  'layout.user.link.help': 'کمک',
+  'layout.user.link.privacy': 'حریم خصوصی',
+  'layout.user.link.terms': 'مقررات',
+  'app.preview.down.block': 'این صفحه را در پروژه محلی خود بارگیری کنید',
+  'app.welcome.link.fetch-blocks': 'دریافت تمام بلوک',
+  'app.welcome.link.block-list': 'به سرعت صفحات استاندارد مبتنی بر توسعه "بلوک" را بسازید',
+  ...globalHeader,
+  ...menu,
+  ...settingDrawer,
+  ...settings,
+  ...pwa,
+  ...component,
+  ...pages,
+};

+ 5 - 0
src/locales/fa-IR/component.ts

@@ -0,0 +1,5 @@
+export default {
+  'component.tagSelect.expand': 'باز',
+  'component.tagSelect.collapse': 'بسته ',
+  'component.tagSelect.all': 'همه',
+};

+ 17 - 0
src/locales/fa-IR/globalHeader.ts

@@ -0,0 +1,17 @@
+export default {
+  'component.globalHeader.search': 'جستجو ',
+  'component.globalHeader.search.example1': 'مثال 1 را جستجو کنید',
+  'component.globalHeader.search.example2': 'مثال 2 را جستجو کنید',
+  'component.globalHeader.search.example3': 'مثال 3 را جستجو کنید',
+  'component.globalHeader.help': 'کمک',
+  'component.globalHeader.notification': 'اعلان',
+  'component.globalHeader.notification.empty': 'شما همه اعلان ها را مشاهده کرده اید.',
+  'component.globalHeader.message': 'پیام',
+  'component.globalHeader.message.empty': 'شما همه پیام ها را مشاهده کرده اید.',
+  'component.globalHeader.event': 'رویداد',
+  'component.globalHeader.event.empty': 'شما همه رویدادها را مشاهده کرده اید.',
+  'component.noticeIcon.clear': 'پاک کردن',
+  'component.noticeIcon.cleared': 'پاک شد',
+  'component.noticeIcon.empty': 'بدون اعلان',
+  'component.noticeIcon.view-more': 'نمایش بیشتر',
+};

Некоторые файлы не были показаны из-за большого количества измененных файлов