Ver código fonte

first commit

张延森 4 anos atrás
commit
c2d88c678c
100 arquivos alterados com 4167 adições e 0 exclusões
  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.js
  10. 42
    0
      config/config.js
  11. 17
    0
      config/defaultSettings.js
  12. 36
    0
      config/proxy.js
  13. 214
    0
      config/routes.js
  14. 9
    0
      jest.config.js
  15. 10
    0
      jsconfig.json
  16. 178
    0
      mock/listTableList.js
  17. 103
    0
      mock/notices.js
  18. 7
    0
      mock/route.js
  19. 167
    0
      mock/user.js
  20. 112
    0
      package.json
  21. 1
    0
      public/CNAME
  22. BIN
      public/favicon.ico
  23. BIN
      public/home_bg.png
  24. BIN
      public/icons/icon-128x128.png
  25. BIN
      public/icons/icon-192x192.png
  26. BIN
      public/icons/icon-512x512.png
  27. 5
    0
      public/pro_icon.svg
  28. 1
    0
      src/assets/logo.svg
  29. 21
    0
      src/components/Authorized/Authorized.jsx
  30. 25
    0
      src/components/Authorized/AuthorizedRoute.jsx
  31. 73
    0
      src/components/Authorized/CheckPermissions.jsx
  32. 78
    0
      src/components/Authorized/PromiseRender.jsx
  33. 61
    0
      src/components/Authorized/Secured.jsx
  34. 9
    0
      src/components/Authorized/index.jsx
  35. 17
    0
      src/components/Authorized/renderAuthorize.js
  36. 12
    0
      src/components/Container/index.jsx
  37. 65
    0
      src/components/GlobalHeader/AvatarDropdown.jsx
  38. 153
    0
      src/components/GlobalHeader/NoticeIconView.jsx
  39. 25
    0
      src/components/GlobalHeader/RightContent.jsx
  40. 82
    0
      src/components/GlobalHeader/index.less
  41. 10
    0
      src/components/HeaderDropdown/index.jsx
  42. 16
    0
      src/components/HeaderDropdown/index.less
  43. 86
    0
      src/components/HeaderSearch/index.jsx
  44. 30
    0
      src/components/HeaderSearch/index.less
  45. 97
    0
      src/components/NoticeIcon/NoticeList.jsx
  46. 103
    0
      src/components/NoticeIcon/NoticeList.less
  47. 118
    0
      src/components/NoticeIcon/index.jsx
  48. 35
    0
      src/components/NoticeIcon/index.less
  49. 4
    0
      src/components/PageLoading/index.jsx
  50. 43
    0
      src/components/UploadImage/index.jsx
  51. 42
    0
      src/components/WangEditor/Preview.jsx
  52. 27
    0
      src/components/WangEditor/PreviewMenu.js
  53. 119
    0
      src/components/WangEditor/WangEditor.jsx
  54. 0
    0
      src/components/WangEditor/index.js
  55. 1
    0
      src/e2e/__mocks__/antd-pro-merge-less.js
  56. 57
    0
      src/e2e/baseLayout.e2e.js
  57. 107
    0
      src/global.jsx
  58. 59
    0
      src/global.less
  59. 173
    0
      src/layouts/BasicLayout.jsx
  60. 10
    0
      src/layouts/BlankLayout.jsx
  61. 49
    0
      src/layouts/SecurityLayout.jsx
  62. 65
    0
      src/layouts/UserLayout.jsx
  63. 71
    0
      src/layouts/UserLayout.less
  64. 23
    0
      src/locales/en-US.js
  65. 5
    0
      src/locales/en-US/component.js
  66. 17
    0
      src/locales/en-US/globalHeader.js
  67. 52
    0
      src/locales/en-US/menu.js
  68. 68
    0
      src/locales/en-US/pages.js
  69. 6
    0
      src/locales/en-US/pwa.js
  70. 31
    0
      src/locales/en-US/settingDrawer.js
  71. 60
    0
      src/locales/en-US/settings.js
  72. 24
    0
      src/locales/id-ID.js
  73. 5
    0
      src/locales/id-ID/component.js
  74. 17
    0
      src/locales/id-ID/globalHeader.js
  75. 52
    0
      src/locales/id-ID/menu.js
  76. 70
    0
      src/locales/id-ID/pages.js
  77. 7
    0
      src/locales/id-ID/pwa.js
  78. 32
    0
      src/locales/id-ID/settingDrawer.js
  79. 60
    0
      src/locales/id-ID/settings.js
  80. 23
    0
      src/locales/ja-JP.js
  81. 5
    0
      src/locales/ja-JP/component.js
  82. 17
    0
      src/locales/ja-JP/globalHeader.js
  83. 52
    0
      src/locales/ja-JP/menu.js
  84. 67
    0
      src/locales/ja-JP/pages.js
  85. 7
    0
      src/locales/ja-JP/pwa.js
  86. 31
    0
      src/locales/ja-JP/settingDrawer.js
  87. 59
    0
      src/locales/ja-JP/settings.js
  88. 19
    0
      src/locales/pt-BR.js
  89. 5
    0
      src/locales/pt-BR/component.js
  90. 18
    0
      src/locales/pt-BR/globalHeader.js
  91. 52
    0
      src/locales/pt-BR/menu.js
  92. 7
    0
      src/locales/pt-BR/pwa.js
  93. 32
    0
      src/locales/pt-BR/settingDrawer.js
  94. 60
    0
      src/locales/pt-BR/settings.js
  95. 23
    0
      src/locales/zh-CN.js
  96. 5
    0
      src/locales/zh-CN/component.js
  97. 17
    0
      src/locales/zh-CN/globalHeader.js
  98. 52
    0
      src/locales/zh-CN/menu.js
  99. 65
    0
      src/locales/zh-CN/pages.js
  100. 0
    0
      src/locales/zh-CN/pwa.js

+ 16
- 0
.editorconfig Ver arquivo

@@ -0,0 +1,16 @@
1
+# http://editorconfig.org
2
+root = true
3
+
4
+[*]
5
+indent_style = space
6
+indent_size = 2
7
+end_of_line = lf
8
+charset = utf-8
9
+trim_trailing_whitespace = true
10
+insert_final_newline = true
11
+
12
+[*.md]
13
+trim_trailing_whitespace = false
14
+
15
+[Makefile]
16
+indent_style = tab

+ 8
- 0
.eslintignore Ver arquivo

@@ -0,0 +1,8 @@
1
+/lambda/
2
+/scripts
3
+/config
4
+.history
5
+public
6
+dist
7
+.umi
8
+mock

+ 8
- 0
.eslintrc.js Ver arquivo

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

+ 40
- 0
.gitignore Ver arquivo

@@ -0,0 +1,40 @@
1
+# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2
+
3
+# dependencies
4
+**/node_modules
5
+# roadhog-api-doc ignore
6
+/src/utils/request-temp.js
7
+_roadhog-api-doc
8
+
9
+# production
10
+/dist
11
+/.vscode
12
+
13
+# misc
14
+.DS_Store
15
+npm-debug.log*
16
+yarn-error.log
17
+
18
+/coverage
19
+.idea
20
+yarn.lock
21
+package-lock.json
22
+*bak
23
+.vscode
24
+
25
+# visual studio code
26
+.history
27
+*.log
28
+functions/*
29
+.temp/**
30
+
31
+# umi
32
+.umi
33
+.umi-production
34
+
35
+# screenshot
36
+screenshot
37
+.firebase
38
+.eslintcache
39
+
40
+build

+ 23
- 0
.prettierignore Ver arquivo

@@ -0,0 +1,23 @@
1
+**/*.svg
2
+package.json
3
+.umi
4
+.umi-production
5
+/dist
6
+.dockerignore
7
+.DS_Store
8
+.eslintignore
9
+*.png
10
+*.toml
11
+docker
12
+.editorconfig
13
+Dockerfile*
14
+.gitignore
15
+.prettierignore
16
+LICENSE
17
+.eslintcache
18
+*.lock
19
+yarn-error.log
20
+.history
21
+CNAME
22
+/build
23
+/public

+ 5
- 0
.prettierrc.js Ver arquivo

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

+ 5
- 0
.stylelintrc.js Ver arquivo

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

+ 57
- 0
README.md Ver arquivo

@@ -0,0 +1,57 @@
1
+# Ant Design Pro
2
+
3
+This project is initialized with [Ant Design Pro](https://pro.ant.design). Follow is the quick guide for how to use.
4
+
5
+## Environment Prepare
6
+
7
+Install `node_modules`:
8
+
9
+```bash
10
+npm install
11
+```
12
+
13
+or
14
+
15
+```bash
16
+yarn
17
+```
18
+
19
+## Provided Scripts
20
+
21
+Ant Design Pro provides some useful script to help you quick start and build with web project, code style check and test.
22
+
23
+Scripts provided in `package.json`. It's safe to modify or add additional script:
24
+
25
+### Start project
26
+
27
+```bash
28
+npm start
29
+```
30
+
31
+### Build project
32
+
33
+```bash
34
+npm run build
35
+```
36
+
37
+### Check code style
38
+
39
+```bash
40
+npm run lint
41
+```
42
+
43
+You can also use script to auto fix some lint error:
44
+
45
+```bash
46
+npm run lint:fix
47
+```
48
+
49
+### Test code
50
+
51
+```bash
52
+npm test
53
+```
54
+
55
+## More
56
+
57
+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.js Ver arquivo

@@ -0,0 +1,15 @@
1
+// https://umijs.org/config/
2
+import { defineConfig } from 'umi';
3
+export default defineConfig({
4
+  plugins: [
5
+    // https://github.com/zthxxx/react-dev-inspector
6
+    'react-dev-inspector/plugins/umi/react-inspector',
7
+  ],
8
+  // https://github.com/zthxxx/react-dev-inspector#inspector-loader-props
9
+  inspectorConfig: {
10
+    exclude: [],
11
+    babelPlugins: [],
12
+    babelOptions: {},
13
+  },
14
+  webpack5: {},
15
+});

+ 42
- 0
config/config.js Ver arquivo

@@ -0,0 +1,42 @@
1
+// https://umijs.org/config/
2
+import { defineConfig } from 'umi';
3
+import defaultSettings from './defaultSettings';
4
+import proxy from './proxy';
5
+import routes from './routes';
6
+const { REACT_APP_ENV } = process.env;
7
+export default defineConfig({
8
+  hash: true,
9
+  antd: {},
10
+  dva: {
11
+    hmr: true,
12
+  },
13
+  history: {
14
+    type: 'browser',
15
+  },
16
+  locale: {
17
+    // default zh-CN
18
+    default: 'zh-CN',
19
+    antd: true,
20
+    // default true, when it is true, will use `navigator.language` overwrite default
21
+    baseNavigator: true,
22
+  },
23
+  dynamicImport: {
24
+    loading: '@/components/PageLoading/index',
25
+  },
26
+  targets: {
27
+    ie: 11,
28
+  },
29
+  // umi routes: https://umijs.org/docs/routing
30
+  routes,
31
+  // Theme for antd: https://ant.design/docs/react/customize-theme-cn
32
+  theme: {
33
+    'primary-color': defaultSettings.primaryColor,
34
+  },
35
+  title: false,
36
+  ignoreMomentLocale: true,
37
+  proxy: proxy[REACT_APP_ENV || 'dev'],
38
+  manifest: {
39
+    basePath: '/',
40
+  },
41
+  esbuild: {},
42
+});

+ 17
- 0
config/defaultSettings.js Ver arquivo

@@ -0,0 +1,17 @@
1
+const proSettings = {
2
+  navTheme: 'dark',
3
+  // 拂晓蓝
4
+  primaryColor: '#1890ff',
5
+  layout: 'side',
6
+  contentWidth: 'Fluid',
7
+  fixedHeader: false,
8
+  fixSiderbar: true,
9
+  colorWeak: false,
10
+  title: 'Ant Design Pro',
11
+  pwa: false,
12
+  iconfontUrl: '',
13
+  menu: {
14
+    disableLocal: true
15
+  },
16
+};
17
+export default proSettings;

+ 36
- 0
config/proxy.js Ver arquivo

@@ -0,0 +1,36 @@
1
+/**
2
+ * 在生产环境 代理是无法生效的,所以这里没有生产环境的配置
3
+ * The agent cannot take effect in the production environment
4
+ * so there is no configuration of the production environment
5
+ * For details, please see
6
+ * https://pro.ant.design/docs/deploy
7
+ */
8
+export default {
9
+  dev: {
10
+    '/api/': {
11
+      target: 'http://127.0.0.1:8080/',
12
+      changeOrigin: true,
13
+      pathRewrite: {
14
+        '^': '',
15
+      },
16
+    },
17
+  },
18
+  test: {
19
+    '/api/': {
20
+      target: 'https://preview.pro.ant.design',
21
+      changeOrigin: true,
22
+      pathRewrite: {
23
+        '^': '',
24
+      },
25
+    },
26
+  },
27
+  pre: {
28
+    '/api/': {
29
+      target: 'your pre url',
30
+      changeOrigin: true,
31
+      pathRewrite: {
32
+        '^': '',
33
+      },
34
+    },
35
+  },
36
+};

+ 214
- 0
config/routes.js Ver arquivo

@@ -0,0 +1,214 @@
1
+/**
2
+ * path: 一定要设置绝对路径
3
+ * menuCode: 服务端菜单编码, 用于权限校验
4
+ */
5
+
6
+export default [
7
+  {
8
+    path: '/',
9
+    component: '../layouts/BlankLayout',
10
+    routes: [
11
+      {
12
+        path: '/user',
13
+        component: '../layouts/UserLayout',
14
+        routes: [
15
+          {
16
+            name: 'login',
17
+            path: '/user/login',
18
+            component: './User/login',
19
+          },
20
+        ],
21
+      },
22
+      {
23
+        path: '/',
24
+        component: '../layouts/SecurityLayout',
25
+        routes: [
26
+          {
27
+            path: '/',
28
+            component: '../layouts/BasicLayout',
29
+            routes: [
30
+              {
31
+                path: '/',
32
+                redirect: '/welcome',
33
+              },
34
+              {
35
+                path: '/welcome',
36
+                name: 'welcome',
37
+                icon: 'smile',
38
+                component: './Welcome',
39
+              },
40
+              {
41
+                path: '/admin',
42
+                name: 'admin',
43
+                icon: 'crown',
44
+                component: './Admin',
45
+                routes: [
46
+                  {
47
+                    path: '/admin/sub-page',
48
+                    name: 'sub-page',
49
+                    icon: 'smile',
50
+                    component: './Welcome',
51
+                  },
52
+                ],
53
+              },
54
+              {
55
+                name: 'list.table-list',
56
+                icon: 'table',
57
+                path: '/list',
58
+                component: './TableList',
59
+              },
60
+              {
61
+                path: '/cms',
62
+                name: 'CMS管理',
63
+                icon: 'AppstoreOutlined',
64
+                menuCode: 'cms',
65
+                component: '../layouts/BlankLayout',
66
+                routes: [
67
+                  {
68
+                    path: '/cms/banner',
69
+                    name: 'Banner管理',
70
+                    menuCode: 'cms.banner',
71
+                    component: '../layouts/BlankLayout',
72
+                  },
73
+                  {
74
+                    path: '/cms/hot',
75
+                    name: '热门管理',
76
+                    menuCode: 'cms.hot',
77
+                    component: '../layouts/BlankLayout',
78
+                  },
79
+                ]
80
+              },
81
+              {
82
+                path: '/post',
83
+                name: '科普管理',
84
+                icon: 'ReadOutlined',
85
+                menuCode: 'post',
86
+                component: '../layouts/BlankLayout',
87
+                routes: [
88
+                  {
89
+                    path: '/post/list',
90
+                    name: '科普管理',
91
+                    menuCode: 'post.post',
92
+                    component: './Post/List',
93
+                  },
94
+                  {
95
+                    path: '/post/edit',
96
+                    name: '科普编辑',
97
+                    menuCode: 'post.post',
98
+                    component: './Post/Edit',
99
+                    hideInMenu: true,
100
+                  },
101
+                ]
102
+              },
103
+              {
104
+                path: '/student',
105
+                name: '学生管理',
106
+                icon: 'UserOutlined',
107
+                menuCode: 'student',
108
+                component: '../layouts/BlankLayout',
109
+                routes: [
110
+                  {
111
+                    path: '/student/student',
112
+                    name: '学生管理',
113
+                    menuCode: 'student.student',
114
+                    component: '../layouts/BlankLayout',
115
+                  },
116
+                ]
117
+              },
118
+              {
119
+                path: '/medical',
120
+                name: '就医管理',
121
+                icon: 'BranchesOutlined',
122
+                menuCode: 'medical',
123
+                component: '../layouts/BlankLayout',
124
+                routes: [
125
+                  {
126
+                    path: '/medical/visit',
127
+                    name: '就诊管理',
128
+                    menuCode: 'medical.visit',
129
+                    component: '../layouts/BlankLayout',
130
+                  },
131
+                  {
132
+                    path: '/medical/test',
133
+                    name: '体检管理',
134
+                    menuCode: 'medical.test',
135
+                    component: '../layouts/BlankLayout',
136
+                  },
137
+                ]
138
+              },
139
+              {
140
+                path: '/report',
141
+                name: '数据统计',
142
+                icon: 'BarChartOutlined',
143
+                menuCode: 'report',
144
+                component: '../layouts/BlankLayout',
145
+                routes: [
146
+                  {
147
+                    path: '/report/post',
148
+                    name: '科普统计',
149
+                    menuCode: 'report.post',
150
+                    component: '../layouts/BlankLayout',
151
+                  },
152
+                  {
153
+                    path: '/report/student',
154
+                    name: '阅读统计',
155
+                    menuCode: 'report.student',
156
+                    component: '../layouts/BlankLayout',
157
+                  },
158
+                ]
159
+              },
160
+              {
161
+                path: '/system',
162
+                name: '系统设置',
163
+                icon: 'SettingOutlined',
164
+                menuCode: 'system',
165
+                component: '../layouts/BlankLayout',
166
+                routes: [
167
+                  {
168
+                    path: '/system/user',
169
+                    name: '用户管理',
170
+                    menuCode: 'system.user',
171
+                    component: '../layouts/BlankLayout',
172
+                  },
173
+                  {
174
+                    path: '/system/role',
175
+                    name: '角色管理',
176
+                    menuCode: 'system.role',
177
+                    component: '../layouts/BlankLayout',
178
+                  },
179
+                  {
180
+                    path: '/system/params',
181
+                    name: '系统参数',
182
+                    menuCode: 'system.params',
183
+                    component: '../layouts/BlankLayout',
184
+                  },
185
+                  {
186
+                    path: '/system/school',
187
+                    name: '学校管理',
188
+                    menuCode: 'system.school',
189
+                    component: '../layouts/BlankLayout',
190
+                  },
191
+                  {
192
+                    path: '/system/clinic',
193
+                    name: '诊室管理',
194
+                    menuCode: 'system.clinic',
195
+                    component: '../layouts/BlankLayout',
196
+                  },
197
+                ]
198
+              },
199
+              {
200
+                component: './404',
201
+              },
202
+            ],
203
+          },
204
+          {
205
+            component: './404',
206
+          },
207
+        ],
208
+      },
209
+    ],
210
+  },
211
+  {
212
+    component: './404',
213
+  },
214
+];

+ 9
- 0
jest.config.js Ver arquivo

@@ -0,0 +1,9 @@
1
+module.exports = {
2
+  testURL: 'http://localhost:8000',
3
+  testEnvironment: './tests/PuppeteerEnvironment',
4
+  verbose: false,
5
+  globals: {
6
+    ANT_DESIGN_PRO_ONLY_DO_NOT_USE_IN_YOUR_PRODUCTION: false,
7
+    localStorage: null,
8
+  },
9
+};

+ 10
- 0
jsconfig.json Ver arquivo

@@ -0,0 +1,10 @@
1
+{
2
+  "compilerOptions": {
3
+    "emitDecoratorMetadata": true,
4
+    "experimentalDecorators": true,
5
+    "baseUrl": ".",
6
+    "paths": {
7
+      "@/*": ["./src/*"]
8
+    }
9
+  }
10
+}

+ 178
- 0
mock/listTableList.js Ver arquivo

@@ -0,0 +1,178 @@
1
+// eslint-disable-next-line import/no-extraneous-dependencies
2
+import { parse } from 'url';
3
+
4
+// mock tableListDataSource
5
+const genList = (current, pageSize) => {
6
+  const tableListDataSource = [];
7
+
8
+  for (let i = 0; i < pageSize; i += 1) {
9
+    const index = (current - 1) * 10 + i;
10
+    tableListDataSource.push({
11
+      key: index,
12
+      disabled: i % 6 === 0,
13
+      href: 'https://ant.design',
14
+      avatar: [
15
+        'https://gw.alipayobjects.com/zos/rmsportal/eeHMaZBwmTvLdIwMfBpg.png',
16
+        'https://gw.alipayobjects.com/zos/rmsportal/udxAbMEhpwthVVcjLXik.png',
17
+      ][i % 2],
18
+      name: `TradeCode ${index}`,
19
+      owner: '曲丽丽',
20
+      desc: '这是一段描述',
21
+      callNo: Math.floor(Math.random() * 1000),
22
+      status: Math.floor(Math.random() * 10) % 4,
23
+      updatedAt: new Date(),
24
+      createdAt: new Date(),
25
+      progress: Math.ceil(Math.random() * 100),
26
+    });
27
+  }
28
+
29
+  tableListDataSource.reverse();
30
+  return tableListDataSource;
31
+};
32
+
33
+let tableListDataSource = genList(1, 100);
34
+
35
+function getRule(req, res, u) {
36
+  let realUrl = u;
37
+
38
+  if (!realUrl || Object.prototype.toString.call(realUrl) !== '[object String]') {
39
+    realUrl = req.url;
40
+  }
41
+
42
+  const { current = 1, pageSize = 10 } = req.query;
43
+  const params = parse(realUrl, true).query;
44
+  let dataSource = [...tableListDataSource].slice((current - 1) * pageSize, current * pageSize);
45
+  const sorter = JSON.parse(params.sorter);
46
+
47
+  if (sorter) {
48
+    dataSource = dataSource.sort((prev, next) => {
49
+      let sortNumber = 0;
50
+      Object.keys(sorter).forEach((key) => {
51
+        if (sorter[key] === 'descend') {
52
+          if (prev[key] - next[key] > 0) {
53
+            sortNumber += -1;
54
+          } else {
55
+            sortNumber += 1;
56
+          }
57
+
58
+          return;
59
+        }
60
+
61
+        if (prev[key] - next[key] > 0) {
62
+          sortNumber += 1;
63
+        } else {
64
+          sortNumber += -1;
65
+        }
66
+      });
67
+      return sortNumber;
68
+    });
69
+  }
70
+
71
+  if (params.filter) {
72
+    const filter = JSON.parse(params.filter);
73
+
74
+    if (Object.keys(filter).length > 0) {
75
+      dataSource = dataSource.filter((item) => {
76
+        return Object.keys(filter).some((key) => {
77
+          if (!filter[key]) {
78
+            return true;
79
+          }
80
+
81
+          if (filter[key].includes(`${item[key]}`)) {
82
+            return true;
83
+          }
84
+
85
+          return false;
86
+        });
87
+      });
88
+    }
89
+  }
90
+
91
+  if (params.name) {
92
+    dataSource = dataSource.filter((data) => data.name.includes(params.name || ''));
93
+  }
94
+
95
+  const result = {
96
+    data: dataSource,
97
+    total: tableListDataSource.length,
98
+    success: true,
99
+    pageSize,
100
+    current: parseInt(`${params.currentPage}`, 10) || 1,
101
+  };
102
+  return res.json(result);
103
+}
104
+
105
+function postRule(req, res, u, b) {
106
+  let realUrl = u;
107
+
108
+  if (!realUrl || Object.prototype.toString.call(realUrl) !== '[object String]') {
109
+    realUrl = req.url;
110
+  }
111
+
112
+  const body = (b && b.body) || req.body;
113
+  const { method, name, desc, key } = body;
114
+
115
+  switch (method) {
116
+    /* eslint no-case-declarations:0 */
117
+    case 'delete':
118
+      tableListDataSource = tableListDataSource.filter((item) => key.indexOf(item.key) === -1);
119
+      break;
120
+
121
+    case 'post':
122
+      (() => {
123
+        const i = Math.ceil(Math.random() * 10000);
124
+        const newRule = {
125
+          key: tableListDataSource.length,
126
+          href: 'https://ant.design',
127
+          avatar: [
128
+            'https://gw.alipayobjects.com/zos/rmsportal/eeHMaZBwmTvLdIwMfBpg.png',
129
+            'https://gw.alipayobjects.com/zos/rmsportal/udxAbMEhpwthVVcjLXik.png',
130
+          ][i % 2],
131
+          name,
132
+          owner: '曲丽丽',
133
+          desc,
134
+          callNo: Math.floor(Math.random() * 1000),
135
+          status: Math.floor(Math.random() * 10) % 2,
136
+          updatedAt: new Date(),
137
+          createdAt: new Date(),
138
+          progress: Math.ceil(Math.random() * 100),
139
+        };
140
+        tableListDataSource.unshift(newRule);
141
+        return res.json(newRule);
142
+      })();
143
+
144
+      return;
145
+
146
+    case 'update':
147
+      (() => {
148
+        let newRule = {};
149
+        tableListDataSource = tableListDataSource.map((item) => {
150
+          if (item.key === key) {
151
+            newRule = { ...item, desc, name };
152
+            return { ...item, desc, name };
153
+          }
154
+
155
+          return item;
156
+        });
157
+        return res.json(newRule);
158
+      })();
159
+
160
+      return;
161
+
162
+    default:
163
+      break;
164
+  }
165
+
166
+  const result = {
167
+    list: tableListDataSource,
168
+    pagination: {
169
+      total: tableListDataSource.length,
170
+    },
171
+  };
172
+  res.json(result);
173
+}
174
+
175
+export default {
176
+  'GET /api/rule': getRule,
177
+  'POST /api/rule': postRule,
178
+};

+ 103
- 0
mock/notices.js Ver arquivo

@@ -0,0 +1,103 @@
1
+const getNotices = (req, res) => {
2
+  res.json([
3
+    {
4
+      id: '000000001',
5
+      avatar: 'https://gw.alipayobjects.com/zos/rmsportal/ThXAXghbEsBCCSDihZxY.png',
6
+      title: '你收到了 14 份新周报',
7
+      datetime: '2017-08-09',
8
+      type: 'notification',
9
+    },
10
+    {
11
+      id: '000000002',
12
+      avatar: 'https://gw.alipayobjects.com/zos/rmsportal/OKJXDXrmkNshAMvwtvhu.png',
13
+      title: '你推荐的 曲妮妮 已通过第三轮面试',
14
+      datetime: '2017-08-08',
15
+      type: 'notification',
16
+    },
17
+    {
18
+      id: '000000003',
19
+      avatar: 'https://gw.alipayobjects.com/zos/rmsportal/kISTdvpyTAhtGxpovNWd.png',
20
+      title: '这种模板可以区分多种通知类型',
21
+      datetime: '2017-08-07',
22
+      read: true,
23
+      type: 'notification',
24
+    },
25
+    {
26
+      id: '000000004',
27
+      avatar: 'https://gw.alipayobjects.com/zos/rmsportal/GvqBnKhFgObvnSGkDsje.png',
28
+      title: '左侧图标用于区分不同的类型',
29
+      datetime: '2017-08-07',
30
+      type: 'notification',
31
+    },
32
+    {
33
+      id: '000000005',
34
+      avatar: 'https://gw.alipayobjects.com/zos/rmsportal/ThXAXghbEsBCCSDihZxY.png',
35
+      title: '内容不要超过两行字,超出时自动截断',
36
+      datetime: '2017-08-07',
37
+      type: 'notification',
38
+    },
39
+    {
40
+      id: '000000006',
41
+      avatar: 'https://gw.alipayobjects.com/zos/rmsportal/fcHMVNCjPOsbUGdEduuv.jpeg',
42
+      title: '曲丽丽 评论了你',
43
+      description: '描述信息描述信息描述信息',
44
+      datetime: '2017-08-07',
45
+      type: 'message',
46
+      clickClose: true,
47
+    },
48
+    {
49
+      id: '000000007',
50
+      avatar: 'https://gw.alipayobjects.com/zos/rmsportal/fcHMVNCjPOsbUGdEduuv.jpeg',
51
+      title: '朱偏右 回复了你',
52
+      description: '这种模板用于提醒谁与你发生了互动,左侧放『谁』的头像',
53
+      datetime: '2017-08-07',
54
+      type: 'message',
55
+      clickClose: true,
56
+    },
57
+    {
58
+      id: '000000008',
59
+      avatar: 'https://gw.alipayobjects.com/zos/rmsportal/fcHMVNCjPOsbUGdEduuv.jpeg',
60
+      title: '标题',
61
+      description: '这种模板用于提醒谁与你发生了互动,左侧放『谁』的头像',
62
+      datetime: '2017-08-07',
63
+      type: 'message',
64
+      clickClose: true,
65
+    },
66
+    {
67
+      id: '000000009',
68
+      title: '任务名称',
69
+      description: '任务需要在 2017-01-12 20:00 前启动',
70
+      extra: '未开始',
71
+      status: 'todo',
72
+      type: 'event',
73
+    },
74
+    {
75
+      id: '000000010',
76
+      title: '第三方紧急代码变更',
77
+      description: '冠霖提交于 2017-01-06,需在 2017-01-07 前完成代码变更任务',
78
+      extra: '马上到期',
79
+      status: 'urgent',
80
+      type: 'event',
81
+    },
82
+    {
83
+      id: '000000011',
84
+      title: '信息安全考试',
85
+      description: '指派竹尔于 2017-01-09 前完成更新并发布',
86
+      extra: '已耗时 8 天',
87
+      status: 'doing',
88
+      type: 'event',
89
+    },
90
+    {
91
+      id: '000000012',
92
+      title: 'ABCD 版本发布',
93
+      description: '冠霖提交于 2017-01-06,需在 2017-01-07 前完成代码变更任务',
94
+      extra: '进行中',
95
+      status: 'processing',
96
+      type: 'event',
97
+    },
98
+  ]);
99
+};
100
+
101
+export default {
102
+  'GET /api/notices': getNotices,
103
+};

+ 7
- 0
mock/route.js Ver arquivo

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

+ 167
- 0
mock/user.js Ver arquivo

@@ -0,0 +1,167 @@
1
+const waitTime = (time = 100) => {
2
+  return new Promise((resolve) => {
3
+    setTimeout(() => {
4
+      resolve(true);
5
+    }, time);
6
+  });
7
+};
8
+
9
+async function getFakeCaptcha(req, res) {
10
+  await waitTime(2000);
11
+  return res.json('captcha-xxx');
12
+} // 代码中会兼容本地 service mock 以及部署站点的静态数据
13
+
14
+export default {
15
+  // 支持值为 Object 和 Array
16
+  'GET /api/currentUser': {
17
+    name: 'Serati Ma',
18
+    avatar: 'https://gw.alipayobjects.com/zos/antfincdn/XAosXuNZyF/BiazfanxmamNRoxxVxka.png',
19
+    userid: '00000001',
20
+    email: 'antdesign@alipay.com',
21
+    signature: '海纳百川,有容乃大',
22
+    title: '交互专家',
23
+    group: '蚂蚁集团-某某某事业群-某某平台部-某某技术部-UED',
24
+    tags: [
25
+      {
26
+        key: '0',
27
+        label: '很有想法的',
28
+      },
29
+      {
30
+        key: '1',
31
+        label: '专注设计',
32
+      },
33
+      {
34
+        key: '2',
35
+        label: '辣~',
36
+      },
37
+      {
38
+        key: '3',
39
+        label: '大长腿',
40
+      },
41
+      {
42
+        key: '4',
43
+        label: '川妹子',
44
+      },
45
+      {
46
+        key: '5',
47
+        label: '海纳百川',
48
+      },
49
+    ],
50
+    notifyCount: 12,
51
+    unreadCount: 11,
52
+    country: 'China',
53
+    geographic: {
54
+      province: {
55
+        label: '浙江省',
56
+        key: '330000',
57
+      },
58
+      city: {
59
+        label: '杭州市',
60
+        key: '330100',
61
+      },
62
+    },
63
+    address: '西湖区工专路 77 号',
64
+    phone: '0752-268888888',
65
+  },
66
+  // GET POST 可省略
67
+  'GET /api/users': [
68
+    {
69
+      key: '1',
70
+      name: 'John Brown',
71
+      age: 32,
72
+      address: 'New York No. 1 Lake Park',
73
+    },
74
+    {
75
+      key: '2',
76
+      name: 'Jim Green',
77
+      age: 42,
78
+      address: 'London No. 1 Lake Park',
79
+    },
80
+    {
81
+      key: '3',
82
+      name: 'Joe Black',
83
+      age: 32,
84
+      address: 'Sidney No. 1 Lake Park',
85
+    },
86
+  ],
87
+  'POST /api/login/account': async (req, res) => {
88
+    const { password, userName, type } = req.body;
89
+    await waitTime(2000);
90
+
91
+    if (password === 'ant.design' && userName === 'admin') {
92
+      res.send({
93
+        status: 'ok',
94
+        type,
95
+        currentAuthority: 'admin',
96
+      });
97
+      return;
98
+    }
99
+
100
+    if (password === 'ant.design' && userName === 'user') {
101
+      res.send({
102
+        status: 'ok',
103
+        type,
104
+        currentAuthority: 'user',
105
+      });
106
+      return;
107
+    }
108
+
109
+    if (type === 'mobile') {
110
+      res.send({
111
+        status: 'ok',
112
+        type,
113
+        currentAuthority: 'admin',
114
+      });
115
+      return;
116
+    }
117
+
118
+    res.send({
119
+      status: 'error',
120
+      type,
121
+      currentAuthority: 'guest',
122
+    });
123
+  },
124
+  'POST /api/register': (req, res) => {
125
+    res.send({
126
+      status: 'ok',
127
+      currentAuthority: 'user',
128
+    });
129
+  },
130
+  'GET /api/500': (req, res) => {
131
+    res.status(500).send({
132
+      timestamp: 1513932555104,
133
+      status: 500,
134
+      error: 'error',
135
+      message: 'error',
136
+      path: '/base/category/list',
137
+    });
138
+  },
139
+  'GET /api/404': (req, res) => {
140
+    res.status(404).send({
141
+      timestamp: 1513932643431,
142
+      status: 404,
143
+      error: 'Not Found',
144
+      message: 'No message available',
145
+      path: '/base/category/list/2121212',
146
+    });
147
+  },
148
+  'GET /api/403': (req, res) => {
149
+    res.status(403).send({
150
+      timestamp: 1513932555104,
151
+      status: 403,
152
+      error: 'Unauthorized',
153
+      message: 'Unauthorized',
154
+      path: '/base/category/list',
155
+    });
156
+  },
157
+  'GET /api/401': (req, res) => {
158
+    res.status(401).send({
159
+      timestamp: 1513932555104,
160
+      status: 401,
161
+      error: 'Unauthorized',
162
+      message: 'Unauthorized',
163
+      path: '/base/category/list',
164
+    });
165
+  },
166
+  'GET  /api/login/captcha': getFakeCaptcha,
167
+};

+ 112
- 0
package.json Ver arquivo

@@ -0,0 +1,112 @@
1
+{
2
+  "name": "ant-design-pro",
3
+  "version": "4.5.0",
4
+  "private": true,
5
+  "description": "An out-of-box UI solution for enterprise applications",
6
+  "scripts": {
7
+    "analyze": "cross-env ANALYZE=1 umi build",
8
+    "build": "umi build",
9
+    "deploy": "npm run site && npm run gh-pages",
10
+    "dev": "npm run start:dev",
11
+    "fetch:blocks": "pro fetch-blocks && npm run prettier",
12
+    "gh-pages": "gh-pages -d dist",
13
+    "i18n-remove": "pro i18n-remove --locale=zh-CN --write",
14
+    "postinstall": "umi g tmp",
15
+    "lint": "umi g tmp && npm run lint:js && npm run lint:style && npm run lint:prettier",
16
+    "lint-staged:js": "eslint --ext .js,.jsx,.ts,.tsx ",
17
+    "lint:fix": "eslint --fix --cache --ext .js,.jsx,.ts,.tsx --format=pretty ./src && npm run lint:style",
18
+    "lint:js": "eslint --cache --ext .js,.jsx,.ts,.tsx --format=pretty ./src",
19
+    "lint:prettier": "prettier --check \"src/**/*\" --end-of-line auto",
20
+    "lint:style": "stylelint --fix \"src/**/*.less\" --syntax less",
21
+    "precommit": "lint-staged",
22
+    "prettier": "prettier -c --write \"src/**/*\"",
23
+    "start": "cross-env UMI_ENV=dev umi dev",
24
+    "start:dev": "cross-env REACT_APP_ENV=dev MOCK=none UMI_ENV=dev umi dev",
25
+    "start:no-mock": "cross-env MOCK=none UMI_ENV=dev umi dev",
26
+    "start:no-ui": "cross-env UMI_UI=none UMI_ENV=dev umi dev",
27
+    "start:pre": "cross-env REACT_APP_ENV=pre UMI_ENV=dev umi dev",
28
+    "start:test": "cross-env REACT_APP_ENV=test MOCK=none UMI_ENV=dev umi dev",
29
+    "pretest": "node ./tests/beforeTest",
30
+    "test": "umi test",
31
+    "test:all": "node ./tests/run-tests.js",
32
+    "test:component": "umi test ./src/components",
33
+    "tsc": "tsc --noEmit"
34
+  },
35
+  "lint-staged": {
36
+    "**/*.less": "stylelint --syntax less",
37
+    "**/*.{js,jsx,ts,tsx}": "npm run lint-staged:js",
38
+    "**/*.{js,jsx,tsx,ts,less,md,json}": [
39
+      "prettier --write"
40
+    ]
41
+  },
42
+  "browserslist": [
43
+    "> 1%",
44
+    "last 2 versions",
45
+    "not ie <= 10"
46
+  ],
47
+  "dependencies": {
48
+    "@ant-design/icons": "^4.0.0",
49
+    "@ant-design/pro-descriptions": "^1.2.0",
50
+    "@ant-design/pro-form": "^1.3.0",
51
+    "@ant-design/pro-layout": "^6.9.0",
52
+    "@ant-design/pro-table": "^2.17.0",
53
+    "@umijs/route-utils": "^1.0.33",
54
+    "ali-oss": "^6.15.2",
55
+    "antd": "^4.12.0",
56
+    "classnames": "^2.2.6",
57
+    "lodash": "^4.17.11",
58
+    "md5": "^2.3.0",
59
+    "moment": "^2.25.3",
60
+    "omit.js": "^2.0.2",
61
+    "react": "^16.14.0",
62
+    "react-dev-inspector": "^1.1.1",
63
+    "react-dom": "^17.0.0",
64
+    "react-helmet-async": "^1.0.4",
65
+    "umi": "^3.4.1",
66
+    "umi-request": "^1.0.8",
67
+    "wangeditor": "^4.6.15"
68
+  },
69
+  "devDependencies": {
70
+    "@ant-design/pro-cli": "^1.0.28",
71
+    "@types/classnames": "^2.2.7",
72
+    "@types/express": "^4.17.0",
73
+    "@types/history": "^4.7.2",
74
+    "@types/jest": "^26.0.0",
75
+    "@types/lodash": "^4.14.144",
76
+    "@types/react": "^17.0.0",
77
+    "@types/react-dom": "^17.0.0",
78
+    "@types/react-helmet": "^6.1.0",
79
+    "@umijs/fabric": "^2.5.1",
80
+    "@umijs/plugin-blocks": "^2.0.5",
81
+    "@umijs/plugin-esbuild": "^1.0.1",
82
+    "@umijs/preset-ant-design-pro": "^1.2.0",
83
+    "@umijs/preset-react": "^1.4.8",
84
+    "@umijs/yorkie": "^2.0.3",
85
+    "carlo": "^0.9.46",
86
+    "chalk": "^4.0.0",
87
+    "cross-env": "^7.0.0",
88
+    "cross-port-killer": "^1.1.1",
89
+    "detect-installer": "^1.0.1",
90
+    "enzyme": "^3.11.0",
91
+    "eslint": "^7.1.0",
92
+    "express": "^4.17.1",
93
+    "gh-pages": "^3.0.0",
94
+    "jsdom-global": "^3.0.2",
95
+    "lint-staged": "^10.0.0",
96
+    "mockjs": "^1.0.1-beta3",
97
+    "prettier": "^2.0.1",
98
+    "puppeteer-core": "^8.0.0",
99
+    "stylelint": "^13.0.0",
100
+    "typescript": "^4.0.3"
101
+  },
102
+  "engines": {
103
+    "node": ">=10.0.0"
104
+  },
105
+  "checkFiles": [
106
+    "src/**/*.js*",
107
+    "src/**/*.ts*",
108
+    "src/**/*.less",
109
+    "config/**/*.js*",
110
+    "scripts/**/*.js"
111
+  ]
112
+}

+ 1
- 0
public/CNAME Ver arquivo

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

BIN
public/favicon.ico Ver arquivo


BIN
public/home_bg.png Ver arquivo


BIN
public/icons/icon-128x128.png Ver arquivo


BIN
public/icons/icon-192x192.png Ver arquivo


BIN
public/icons/icon-512x512.png Ver arquivo


+ 5
- 0
public/pro_icon.svg Ver arquivo

@@ -0,0 +1,5 @@
1
+<svg width="42" height="42" xmlns="http://www.w3.org/2000/svg">
2
+ <g>
3
+  <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"/>
4
+ </g>
5
+</svg>

+ 1
- 0
src/assets/logo.svg Ver arquivo

@@ -0,0 +1 @@
1
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200" version="1.1" viewBox="0 0 200 200"><title>Group 28 Copy 5</title><desc>Created with Sketch.</desc><defs><linearGradient id="linearGradient-1" x1="62.102%" x2="108.197%" y1="0%" y2="37.864%"><stop offset="0%" stop-color="#4285EB"/><stop offset="100%" stop-color="#2EC7FF"/></linearGradient><linearGradient id="linearGradient-2" x1="69.644%" x2="54.043%" y1="0%" y2="108.457%"><stop offset="0%" stop-color="#29CDFF"/><stop offset="37.86%" stop-color="#148EFF"/><stop offset="100%" stop-color="#0A60FF"/></linearGradient><linearGradient id="linearGradient-3" x1="69.691%" x2="16.723%" y1="-12.974%" y2="117.391%"><stop offset="0%" stop-color="#FA816E"/><stop offset="41.473%" stop-color="#F74A5C"/><stop offset="100%" stop-color="#F51D2C"/></linearGradient><linearGradient id="linearGradient-4" x1="68.128%" x2="30.44%" y1="-35.691%" y2="114.943%"><stop offset="0%" stop-color="#FA8E7D"/><stop offset="51.264%" stop-color="#F74A5C"/><stop offset="100%" stop-color="#F51D2C"/></linearGradient></defs><g id="Page-1" fill="none" fill-rule="evenodd" stroke="none" stroke-width="1"><g id="logo" transform="translate(-20.000000, -20.000000)"><g id="Group-28-Copy-5" transform="translate(20.000000, 20.000000)"><g id="Group-27-Copy-3"><g id="Group-25" fill-rule="nonzero"><g id="2"><path id="Shape" fill="url(#linearGradient-1)" d="M91.5880863,4.17652823 L4.17996544,91.5127728 C-0.519240605,96.2081146 -0.519240605,103.791885 4.17996544,108.487227 L91.5880863,195.823472 C96.2872923,200.518814 103.877304,200.518814 108.57651,195.823472 L145.225487,159.204632 C149.433969,154.999611 149.433969,148.181924 145.225487,143.976903 C141.017005,139.771881 134.193707,139.771881 129.985225,143.976903 L102.20193,171.737352 C101.032305,172.906015 99.2571609,172.906015 98.0875359,171.737352 L28.285908,101.993122 C27.1162831,100.824459 27.1162831,99.050775 28.285908,97.8821118 L98.0875359,28.1378823 C99.2571609,26.9692191 101.032305,26.9692191 102.20193,28.1378823 L129.985225,55.8983314 C134.193707,60.1033528 141.017005,60.1033528 145.225487,55.8983314 C149.433969,51.69331 149.433969,44.8756232 145.225487,40.6706018 L108.58055,4.05574592 C103.862049,-0.537986846 96.2692618,-0.500797906 91.5880863,4.17652823 Z"/><path id="Shape" fill="url(#linearGradient-2)" d="M91.5880863,4.17652823 L4.17996544,91.5127728 C-0.519240605,96.2081146 -0.519240605,103.791885 4.17996544,108.487227 L91.5880863,195.823472 C96.2872923,200.518814 103.877304,200.518814 108.57651,195.823472 L145.225487,159.204632 C149.433969,154.999611 149.433969,148.181924 145.225487,143.976903 C141.017005,139.771881 134.193707,139.771881 129.985225,143.976903 L102.20193,171.737352 C101.032305,172.906015 99.2571609,172.906015 98.0875359,171.737352 L28.285908,101.993122 C27.1162831,100.824459 27.1162831,99.050775 28.285908,97.8821118 L98.0875359,28.1378823 C100.999864,25.6271836 105.751642,20.541824 112.729652,19.3524487 C117.915585,18.4685261 123.585219,20.4140239 129.738554,25.1889424 C125.624663,21.0784292 118.571995,14.0340304 108.58055,4.05574592 C103.862049,-0.537986846 96.2692618,-0.500797906 91.5880863,4.17652823 Z"/></g><path id="Shape" fill="url(#linearGradient-3)" d="M153.685633,135.854579 C157.894115,140.0596 164.717412,140.0596 168.925894,135.854579 L195.959977,108.842726 C200.659183,104.147384 200.659183,96.5636133 195.960527,91.8688194 L168.690777,64.7181159 C164.472332,60.5180858 157.646868,60.5241425 153.435895,64.7316526 C149.227413,68.936674 149.227413,75.7543607 153.435895,79.9593821 L171.854035,98.3623765 C173.02366,99.5310396 173.02366,101.304724 171.854035,102.473387 L153.685633,120.626849 C149.47715,124.83187 149.47715,131.649557 153.685633,135.854579 Z"/></g><ellipse id="Combined-Shape" cx="100.519" cy="100.437" fill="url(#linearGradient-4)" rx="23.6" ry="23.581"/></g></g></g></g></svg>

+ 21
- 0
src/components/Authorized/Authorized.jsx Ver arquivo

@@ -0,0 +1,21 @@
1
+import React from 'react';
2
+import { Result } from 'antd';
3
+import check from './CheckPermissions';
4
+
5
+const Authorized = ({
6
+  children,
7
+  authority,
8
+  noMatch = (
9
+    <Result
10
+      status="403"
11
+      title="403"
12
+      subTitle="Sorry, you are not authorized to access this page."
13
+    />
14
+  ),
15
+}) => {
16
+  const childrenRender = typeof children === 'undefined' ? null : children;
17
+  const dom = check(authority, childrenRender, noMatch);
18
+  return <>{dom}</>;
19
+};
20
+
21
+export default Authorized;

+ 25
- 0
src/components/Authorized/AuthorizedRoute.jsx Ver arquivo

@@ -0,0 +1,25 @@
1
+import { Redirect, Route } from 'umi';
2
+import React from 'react';
3
+import Authorized from './Authorized';
4
+
5
+const AuthorizedRoute = ({ component: Component, render, authority, redirectPath, ...rest }) => (
6
+  <Authorized
7
+    authority={authority}
8
+    noMatch={
9
+      <Route
10
+        {...rest}
11
+        render={() => (
12
+          <Redirect
13
+            to={{
14
+              pathname: redirectPath,
15
+            }}
16
+          />
17
+        )}
18
+      />
19
+    }
20
+  >
21
+    <Route {...rest} render={(props) => (Component ? <Component {...props} /> : render(props))} />
22
+  </Authorized>
23
+);
24
+
25
+export default AuthorizedRoute;

+ 73
- 0
src/components/Authorized/CheckPermissions.jsx Ver arquivo

@@ -0,0 +1,73 @@
1
+import React from 'react';
2
+import { CURRENT } from './renderAuthorize'; // eslint-disable-next-line import/no-cycle
3
+
4
+import PromiseRender from './PromiseRender';
5
+
6
+/**
7
+ * 通用权限检查方法 Common check permissions method
8
+ *
9
+ * @param { 权限判定 | Permission judgment } authority
10
+ * @param { 你的权限 | Your permission description } currentAuthority
11
+ * @param { 通过的组件 | Passing components } target
12
+ * @param { 未通过的组件 | no pass components } Exception
13
+ */
14
+const checkPermissions = (authority, currentAuthority, target, Exception) => {
15
+  // 没有判定权限.默认查看所有
16
+  // Retirement authority, return target;
17
+  if (!authority) {
18
+    return target;
19
+  } // 数组处理
20
+
21
+  if (Array.isArray(authority)) {
22
+    if (Array.isArray(currentAuthority)) {
23
+      if (currentAuthority.some((item) => authority.includes(item))) {
24
+        return target;
25
+      }
26
+    } else if (authority.includes(currentAuthority)) {
27
+      return target;
28
+    }
29
+
30
+    return Exception;
31
+  } // string 处理
32
+
33
+  if (typeof authority === 'string') {
34
+    if (Array.isArray(currentAuthority)) {
35
+      if (currentAuthority.some((item) => authority === item)) {
36
+        return target;
37
+      }
38
+    } else if (authority === currentAuthority) {
39
+      return target;
40
+    }
41
+
42
+    return Exception;
43
+  } // Promise 处理
44
+
45
+  if (authority instanceof Promise) {
46
+    return <PromiseRender ok={target} error={Exception} promise={authority} />;
47
+  } // Function 处理
48
+
49
+  if (typeof authority === 'function') {
50
+    const bool = authority(currentAuthority); // 函数执行后返回值是 Promise
51
+
52
+    if (bool instanceof Promise) {
53
+      return <PromiseRender ok={target} error={Exception} promise={bool} />;
54
+    }
55
+
56
+    if (bool) {
57
+      return target;
58
+    }
59
+
60
+    return Exception;
61
+  }
62
+
63
+  throw new Error('unsupported parameters');
64
+};
65
+
66
+export { checkPermissions };
67
+
68
+function check(authority, target, Exception) {
69
+  const permit = typeof CURRENT === 'function' ? CURRENT() : CURRENT
70
+  return checkPermissions(authority, permit, target, Exception);
71
+}
72
+
73
+export default check;

+ 78
- 0
src/components/Authorized/PromiseRender.jsx Ver arquivo

@@ -0,0 +1,78 @@
1
+import React from 'react';
2
+import { Spin } from 'antd';
3
+import isEqual from 'lodash/isEqual';
4
+import { isComponentClass } from './Secured'; // eslint-disable-next-line import/no-cycle
5
+
6
+export default class PromiseRender extends React.Component {
7
+  state = {
8
+    component: () => null,
9
+  };
10
+
11
+  componentDidMount() {
12
+    this.setRenderComponent(this.props);
13
+  }
14
+
15
+  shouldComponentUpdate = (nextProps, nextState) => {
16
+    const { component } = this.state;
17
+
18
+    if (!isEqual(nextProps, this.props)) {
19
+      this.setRenderComponent(nextProps);
20
+    }
21
+
22
+    if (nextState.component !== component) return true;
23
+    return false;
24
+  }; // set render Component : ok or error
25
+
26
+  setRenderComponent(props) {
27
+    const ok = this.checkIsInstantiation(props.ok);
28
+    const error = this.checkIsInstantiation(props.error);
29
+    props.promise
30
+      .then(() => {
31
+        this.setState({
32
+          component: ok,
33
+        });
34
+        return true;
35
+      })
36
+      .catch(() => {
37
+        this.setState({
38
+          component: error,
39
+        });
40
+      });
41
+  } // Determine whether the incoming component has been instantiated
42
+  // AuthorizedRoute is already instantiated
43
+  // Authorized  render is already instantiated, children is no instantiated
44
+  // Secured is not instantiated
45
+
46
+  checkIsInstantiation = (target) => {
47
+    if (isComponentClass(target)) {
48
+      const Target = target;
49
+      return (props) => <Target {...props} />;
50
+    }
51
+
52
+    if (React.isValidElement(target)) {
53
+      return (props) => React.cloneElement(target, props);
54
+    }
55
+
56
+    return () => target;
57
+  };
58
+
59
+  render() {
60
+    const { component: Component } = this.state;
61
+    const { ok, error, promise, ...rest } = this.props;
62
+    return Component ? (
63
+      <Component {...rest} />
64
+    ) : (
65
+      <div
66
+        style={{
67
+          width: '100%',
68
+          height: '100%',
69
+          margin: 'auto',
70
+          paddingTop: 50,
71
+          textAlign: 'center',
72
+        }}
73
+      >
74
+        <Spin size="large" />
75
+      </div>
76
+    );
77
+  }
78
+}

+ 61
- 0
src/components/Authorized/Secured.jsx Ver arquivo

@@ -0,0 +1,61 @@
1
+import React from 'react';
2
+import CheckPermissions from './CheckPermissions';
3
+/** 默认不能访问任何页面 default is "NULL" */
4
+
5
+const Exception403 = () => 403;
6
+
7
+export const isComponentClass = (component) => {
8
+  if (!component) return false;
9
+  const proto = Object.getPrototypeOf(component);
10
+  if (proto === React.Component || proto === Function.prototype) return true;
11
+  return isComponentClass(proto);
12
+}; // Determine whether the incoming component has been instantiated
13
+// AuthorizedRoute is already instantiated
14
+// Authorized  render is already instantiated, children is no instantiated
15
+// Secured is not instantiated
16
+
17
+const checkIsInstantiation = (target) => {
18
+  if (isComponentClass(target)) {
19
+    const Target = target;
20
+    return (props) => <Target {...props} />;
21
+  }
22
+
23
+  if (React.isValidElement(target)) {
24
+    return (props) => React.cloneElement(target, props);
25
+  }
26
+
27
+  return () => target;
28
+};
29
+/**
30
+ * 用于判断是否拥有权限访问此 view 权限 authority 支持传入 string, () => boolean | Promise e.g. 'user' 只有 user 用户能访问
31
+ * e.g. 'user,admin' user 和 admin 都能访问 e.g. ()=>boolean 返回true能访问,返回false不能访问 e.g. Promise then 能访问
32
+ * catch不能访问 e.g. authority support incoming string, () => boolean | Promise e.g. 'user' only user
33
+ * user can access e.g. 'user, admin' user and admin can access e.g. () => boolean true to be able
34
+ * to visit, return false can not be accessed e.g. Promise then can not access the visit to catch
35
+ *
36
+ * @param {string | function | Promise} authority
37
+ * @param {ReactNode} error 非必需参数
38
+ */
39
+
40
+const authorize = (authority, error) => {
41
+  /**
42
+   * Conversion into a class 防止传入字符串时找不到staticContext造成报错 String parameters can cause staticContext
43
+   * not found error
44
+   */
45
+  let classError = false;
46
+
47
+  if (error) {
48
+    classError = () => error;
49
+  }
50
+
51
+  if (!authority) {
52
+    throw new Error('authority is required');
53
+  }
54
+
55
+  return function decideAuthority(target) {
56
+    const component = CheckPermissions(authority, target, classError || Exception403);
57
+    return checkIsInstantiation(component);
58
+  };
59
+};
60
+
61
+export default authorize;

+ 9
- 0
src/components/Authorized/index.jsx Ver arquivo

@@ -0,0 +1,9 @@
1
+import Authorized from './Authorized';
2
+import Secured from './Secured';
3
+import check from './CheckPermissions';
4
+import renderAuthorize from './renderAuthorize';
5
+
6
+Authorized.Secured = Secured;
7
+Authorized.check = check;
8
+const RenderAuthorize = renderAuthorize(Authorized);
9
+export default RenderAuthorize;

+ 17
- 0
src/components/Authorized/renderAuthorize.js Ver arquivo

@@ -0,0 +1,17 @@
1
+/* eslint-disable eslint-comments/disable-enable-pair */
2
+
3
+/* eslint-disable import/no-mutable-exports */
4
+let CURRENT;
5
+
6
+/**
7
+ * Use authority or getAuthority
8
+ *
9
+ * @param {string|()=>String} currentAuthority
10
+ */
11
+const renderAuthorize = (Authorized) => (currentAuthority) => {
12
+  CURRENT = currentAuthority
13
+  return Authorized;
14
+};
15
+
16
+export { CURRENT };
17
+export default (Authorized) => renderAuthorize(Authorized);

+ 12
- 0
src/components/Container/index.jsx Ver arquivo

@@ -0,0 +1,12 @@
1
+import React from 'react'
2
+import { Spin } from 'antd'
3
+
4
+export default (props) => {
5
+  const loading = typeof props.loading === 'boolean' ? props.loading : false
6
+
7
+  return (
8
+    <Spin spinning={loading} tip={props.loadingTip}>
9
+      <div style={{ background: '#fff', padding: '2em' }}>{props.children}</div>
10
+    </Spin>
11
+  )
12
+}

+ 65
- 0
src/components/GlobalHeader/AvatarDropdown.jsx Ver arquivo

@@ -0,0 +1,65 @@
1
+import { LogoutOutlined } from '@ant-design/icons';
2
+import { Avatar, Menu, Spin } from 'antd';
3
+import React from 'react';
4
+import { history, connect } from 'umi';
5
+import HeaderDropdown from '../HeaderDropdown';
6
+import styles from './index.less';
7
+
8
+class AvatarDropdown extends React.Component {
9
+  onMenuClick = (event) => {
10
+    const { key } = event;
11
+
12
+    if (key === 'logout') {
13
+      const { dispatch } = this.props;
14
+
15
+      if (dispatch) {
16
+        dispatch({
17
+          type: 'login/logout',
18
+        });
19
+      }
20
+
21
+      return;
22
+    }
23
+
24
+    history.push(`/account/${key}`);
25
+  };
26
+
27
+  render() {
28
+    const {
29
+      currentUser = {
30
+        avatar: '',
31
+        userName: '',
32
+      },
33
+    } = this.props;
34
+    const menuHeaderDropdown = (
35
+      <Menu className={styles.menu} selectedKeys={[]} onClick={this.onMenuClick}>
36
+        <Menu.Item key="logout">
37
+          <LogoutOutlined />
38
+          退出登录
39
+        </Menu.Item>
40
+      </Menu>
41
+    );
42
+    return currentUser && currentUser.userName ? (
43
+      <HeaderDropdown overlay={menuHeaderDropdown}>
44
+        <span className={`${styles.action} ${styles.account}`}>
45
+          <Avatar size="small" className={styles.avatar} src={currentUser.avatar || 'https://gw.alipayobjects.com/zos/antfincdn/XAosXuNZyF/BiazfanxmamNRoxxVxka.png'} alt="avatar" />
46
+          <span className={`${styles.name} anticon`}>{currentUser.userName}</span>
47
+        </span>
48
+      </HeaderDropdown>
49
+    ) : (
50
+      <span className={`${styles.action} ${styles.account}`}>
51
+        <Spin
52
+          size="small"
53
+          style={{
54
+            marginLeft: 8,
55
+            marginRight: 8,
56
+          }}
57
+        />
58
+      </span>
59
+    );
60
+  }
61
+}
62
+
63
+export default connect(({ user }) => ({
64
+  currentUser: user.currentUser,
65
+}))(AvatarDropdown);

+ 153
- 0
src/components/GlobalHeader/NoticeIconView.jsx Ver arquivo

@@ -0,0 +1,153 @@
1
+import { Component } from 'react';
2
+import { connect } from 'umi';
3
+import { Tag, message } from 'antd';
4
+import groupBy from 'lodash/groupBy';
5
+import moment from 'moment';
6
+import NoticeIcon from '../NoticeIcon';
7
+import styles from './index.less';
8
+
9
+class GlobalHeaderRight extends Component {
10
+  componentDidMount() {
11
+    const { dispatch } = this.props;
12
+
13
+    if (dispatch) {
14
+      dispatch({
15
+        type: 'global/fetchNotices',
16
+      });
17
+    }
18
+  }
19
+
20
+  changeReadState = (clickedItem) => {
21
+    const { id } = clickedItem;
22
+    const { dispatch } = this.props;
23
+
24
+    if (dispatch) {
25
+      dispatch({
26
+        type: 'global/changeNoticeReadState',
27
+        payload: id,
28
+      });
29
+    }
30
+  };
31
+  handleNoticeClear = (title, key) => {
32
+    const { dispatch } = this.props;
33
+    message.success(`${'清空了'} ${title}`);
34
+
35
+    if (dispatch) {
36
+      dispatch({
37
+        type: 'global/clearNotices',
38
+        payload: key,
39
+      });
40
+    }
41
+  };
42
+  getNoticeData = () => {
43
+    const { notices = [] } = this.props;
44
+
45
+    if (!notices || notices.length === 0 || !Array.isArray(notices)) {
46
+      return {};
47
+    }
48
+
49
+    const newNotices = notices.map((notice) => {
50
+      const newNotice = { ...notice };
51
+
52
+      if (newNotice.datetime) {
53
+        newNotice.datetime = moment(notice.datetime).fromNow();
54
+      }
55
+
56
+      if (newNotice.id) {
57
+        newNotice.key = newNotice.id;
58
+      }
59
+
60
+      if (newNotice.extra && newNotice.status) {
61
+        const color = {
62
+          todo: '',
63
+          processing: 'blue',
64
+          urgent: 'red',
65
+          doing: 'gold',
66
+        }[newNotice.status];
67
+        newNotice.extra = (
68
+          <Tag
69
+            color={color}
70
+            style={{
71
+              marginRight: 0,
72
+            }}
73
+          >
74
+            {newNotice.extra}
75
+          </Tag>
76
+        );
77
+      }
78
+
79
+      return newNotice;
80
+    });
81
+    return groupBy(newNotices, 'type');
82
+  };
83
+  getUnreadData = (noticeData) => {
84
+    const unreadMsg = {};
85
+    Object.keys(noticeData).forEach((key) => {
86
+      const value = noticeData[key];
87
+
88
+      if (!unreadMsg[key]) {
89
+        unreadMsg[key] = 0;
90
+      }
91
+
92
+      if (Array.isArray(value)) {
93
+        unreadMsg[key] = value.filter((item) => !item.read).length;
94
+      }
95
+    });
96
+    return unreadMsg;
97
+  };
98
+
99
+  render() {
100
+    const { currentUser, fetchingNotices, onNoticeVisibleChange } = this.props;
101
+    const noticeData = this.getNoticeData();
102
+    const unreadMsg = this.getUnreadData(noticeData);
103
+    return (
104
+      <NoticeIcon
105
+        className={styles.action}
106
+        count={currentUser && currentUser.unreadCount}
107
+        onItemClick={(item) => {
108
+          this.changeReadState(item);
109
+        }}
110
+        loading={fetchingNotices}
111
+        clearText="清空"
112
+        viewMoreText="查看更多"
113
+        onClear={this.handleNoticeClear}
114
+        onPopupVisibleChange={onNoticeVisibleChange}
115
+        onViewMore={() => message.info('Click on view more')}
116
+        clearClose
117
+      >
118
+        <NoticeIcon.Tab
119
+          tabKey="notification"
120
+          count={unreadMsg.notification}
121
+          list={noticeData.notification}
122
+          title="通知"
123
+          emptyText="你已查看所有通知"
124
+          showViewMore
125
+        />
126
+        <NoticeIcon.Tab
127
+          tabKey="message"
128
+          count={unreadMsg.message}
129
+          list={noticeData.message}
130
+          title="消息"
131
+          emptyText="您已读完所有消息"
132
+          showViewMore
133
+        />
134
+        <NoticeIcon.Tab
135
+          tabKey="event"
136
+          title="待办"
137
+          emptyText="你已完成所有待办"
138
+          count={unreadMsg.event}
139
+          list={noticeData.event}
140
+          showViewMore
141
+        />
142
+      </NoticeIcon>
143
+    );
144
+  }
145
+}
146
+
147
+export default connect(({ user, global, loading }) => ({
148
+  currentUser: user.currentUser,
149
+  collapsed: global.collapsed,
150
+  fetchingMoreNotices: loading.effects['global/fetchMoreNotices'],
151
+  fetchingNotices: loading.effects['global/fetchNotices'],
152
+  notices: global.notices,
153
+}))(GlobalHeaderRight);

+ 25
- 0
src/components/GlobalHeader/RightContent.jsx Ver arquivo

@@ -0,0 +1,25 @@
1
+import React from 'react';
2
+// import { connect, SelectLang } from 'umi';
3
+import { connect } from 'umi';
4
+import Avatar from './AvatarDropdown';
5
+import styles from './index.less';
6
+
7
+const GlobalHeaderRight = (props) => {
8
+  const { theme, layout } = props;
9
+  let className = styles.right;
10
+
11
+  if (theme === 'dark' && layout === 'top') {
12
+    className = `${styles.right}  ${styles.dark}`;
13
+  }
14
+
15
+  return (
16
+    <div className={className}>
17
+      <Avatar />
18
+    </div>
19
+  );
20
+};
21
+
22
+export default connect(({ settings }) => ({
23
+  theme: settings.navTheme,
24
+  layout: settings.layout,
25
+}))(GlobalHeaderRight);

+ 82
- 0
src/components/GlobalHeader/index.less Ver arquivo

@@ -0,0 +1,82 @@
1
+@import '~antd/es/style/themes/default.less';
2
+
3
+@pro-header-hover-bg: rgba(0, 0, 0, 0.025);
4
+
5
+.menu {
6
+  :global(.anticon) {
7
+    margin-right: 8px;
8
+  }
9
+  :global(.ant-dropdown-menu-item) {
10
+    min-width: 160px;
11
+  }
12
+}
13
+
14
+.right {
15
+  display: flex;
16
+  float: right;
17
+  height: 48px;
18
+  margin-left: auto;
19
+  overflow: hidden;
20
+  .action {
21
+    display: flex;
22
+    align-items: center;
23
+    height: 100%;
24
+    padding: 0 12px;
25
+    cursor: pointer;
26
+    transition: all 0.3s;
27
+    > span {
28
+      vertical-align: middle;
29
+    }
30
+    &:hover {
31
+      background: @pro-header-hover-bg;
32
+    }
33
+    &:global(.opened) {
34
+      background: @pro-header-hover-bg;
35
+    }
36
+  }
37
+  .search {
38
+    padding: 0 12px;
39
+    &:hover {
40
+      background: transparent;
41
+    }
42
+  }
43
+  .account {
44
+    .avatar {
45
+      margin: ~'calc((@{layout-header-height} - 24px) / 2)' 0;
46
+      margin-right: 8px;
47
+      color: @primary-color;
48
+      vertical-align: top;
49
+      background: rgba(255, 255, 255, 0.85);
50
+    }
51
+  }
52
+}
53
+
54
+.dark {
55
+  .action {
56
+    color: rgba(255, 255, 255, 0.85);
57
+    > span {
58
+      color: rgba(255, 255, 255, 0.85);
59
+    }
60
+    &:hover,
61
+    &:global(.opened) {
62
+      background: @primary-color;
63
+    }
64
+  }
65
+}
66
+
67
+:global(.ant-pro-global-header) {
68
+  .dark {
69
+    .action {
70
+      color: @text-color;
71
+      > span {
72
+        color: @text-color;
73
+      }
74
+      &:hover {
75
+        color: rgba(255, 255, 255, 0.85);
76
+        > span {
77
+          color: rgba(255, 255, 255, 0.85);
78
+        }
79
+      }
80
+    }
81
+  }
82
+}

+ 10
- 0
src/components/HeaderDropdown/index.jsx Ver arquivo

@@ -0,0 +1,10 @@
1
+import { Dropdown } from 'antd';
2
+import React from 'react';
3
+import classNames from 'classnames';
4
+import styles from './index.less';
5
+
6
+const HeaderDropdown = ({ overlayClassName: cls, ...restProps }) => (
7
+  <Dropdown overlayClassName={classNames(styles.container, cls)} {...restProps} />
8
+);
9
+
10
+export default HeaderDropdown;

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

@@ -0,0 +1,16 @@
1
+@import '~antd/es/style/themes/default.less';
2
+
3
+.container > * {
4
+  background-color: @popover-bg;
5
+  border-radius: 4px;
6
+  box-shadow: @shadow-1-down;
7
+}
8
+
9
+@media screen and (max-width: @screen-xs) {
10
+  .container {
11
+    width: 100% !important;
12
+  }
13
+  .container > * {
14
+    border-radius: 0 !important;
15
+  }
16
+}

+ 86
- 0
src/components/HeaderSearch/index.jsx Ver arquivo

@@ -0,0 +1,86 @@
1
+import { SearchOutlined } from '@ant-design/icons';
2
+import { AutoComplete, Input } from 'antd';
3
+import useMergedState from 'rc-util/es/hooks/useMergedState';
4
+import React, { useRef } from 'react';
5
+import classNames from 'classnames';
6
+import styles from './index.less';
7
+
8
+const HeaderSearch = (props) => {
9
+  const {
10
+    className,
11
+    defaultValue,
12
+    onVisibleChange,
13
+    placeholder,
14
+    open,
15
+    defaultOpen,
16
+    ...restProps
17
+  } = props;
18
+  const inputRef = useRef(null);
19
+  const [value, setValue] = useMergedState(defaultValue, {
20
+    value: props.value,
21
+    onChange: props.onChange,
22
+  });
23
+  const [searchMode, setSearchMode] = useMergedState(defaultOpen ?? false, {
24
+    value: props.open,
25
+    onChange: onVisibleChange,
26
+  });
27
+  const inputClass = classNames(styles.input, {
28
+    [styles.show]: searchMode,
29
+  });
30
+  return (
31
+    <div
32
+      className={classNames(className, styles.headerSearch)}
33
+      onClick={() => {
34
+        setSearchMode(true);
35
+
36
+        if (searchMode && inputRef.current) {
37
+          inputRef.current.focus();
38
+        }
39
+      }}
40
+      onTransitionEnd={({ propertyName }) => {
41
+        if (propertyName === 'width' && !searchMode) {
42
+          if (onVisibleChange) {
43
+            onVisibleChange(searchMode);
44
+          }
45
+        }
46
+      }}
47
+    >
48
+      <SearchOutlined
49
+        key="Icon"
50
+        style={{
51
+          cursor: 'pointer',
52
+        }}
53
+      />
54
+      <AutoComplete
55
+        key="AutoComplete"
56
+        className={inputClass}
57
+        value={value}
58
+        style={{
59
+          height: 28,
60
+          marginTop: -6,
61
+        }}
62
+        options={restProps.options}
63
+        onChange={setValue}
64
+      >
65
+        <Input
66
+          ref={inputRef}
67
+          defaultValue={defaultValue}
68
+          aria-label={placeholder}
69
+          placeholder={placeholder}
70
+          onKeyDown={(e) => {
71
+            if (e.key === 'Enter') {
72
+              if (restProps.onSearch) {
73
+                restProps.onSearch(value);
74
+              }
75
+            }
76
+          }}
77
+          onBlur={() => {
78
+            setSearchMode(false);
79
+          }}
80
+        />
81
+      </AutoComplete>
82
+    </div>
83
+  );
84
+};
85
+
86
+export default HeaderSearch;

+ 30
- 0
src/components/HeaderSearch/index.less Ver arquivo

@@ -0,0 +1,30 @@
1
+@import '~antd/es/style/themes/default.less';
2
+
3
+.headerSearch {
4
+  .input {
5
+    width: 0;
6
+    min-width: 0;
7
+    overflow: hidden;
8
+    background: transparent;
9
+    border-radius: 0;
10
+    transition: width 0.3s, margin-left 0.3s;
11
+    :global(.ant-select-selection) {
12
+      background: transparent;
13
+    }
14
+    input {
15
+      padding-right: 0;
16
+      padding-left: 0;
17
+      border: 0;
18
+      box-shadow: none !important;
19
+    }
20
+    &,
21
+    &:hover,
22
+    &:focus {
23
+      border-bottom: 1px solid @border-color-base;
24
+    }
25
+    &.show {
26
+      width: 210px;
27
+      margin-left: 8px;
28
+    }
29
+  }
30
+}

+ 97
- 0
src/components/NoticeIcon/NoticeList.jsx Ver arquivo

@@ -0,0 +1,97 @@
1
+import { Avatar, List } from 'antd';
2
+import React from 'react';
3
+import classNames from 'classnames';
4
+import styles from './NoticeList.less';
5
+
6
+const NoticeList = ({
7
+  data = [],
8
+  onClick,
9
+  onClear,
10
+  title,
11
+  onViewMore,
12
+  emptyText,
13
+  showClear = true,
14
+  clearText,
15
+  viewMoreText,
16
+  showViewMore = false,
17
+}) => {
18
+  if (!data || data.length === 0) {
19
+    return (
20
+      <div className={styles.notFound}>
21
+        <img
22
+          src="https://gw.alipayobjects.com/zos/rmsportal/sAuJeJzSKbUmHfBQRzmZ.svg"
23
+          alt="not found"
24
+        />
25
+        <div>{emptyText}</div>
26
+      </div>
27
+    );
28
+  }
29
+
30
+  return (
31
+    <div>
32
+      <List
33
+        className={styles.list}
34
+        dataSource={data}
35
+        renderItem={(item, i) => {
36
+          const itemCls = classNames(styles.item, {
37
+            [styles.read]: item.read,
38
+          }); // eslint-disable-next-line no-nested-ternary
39
+
40
+          const leftIcon = item.avatar ? (
41
+            typeof item.avatar === 'string' ? (
42
+              <Avatar className={styles.avatar} src={item.avatar} />
43
+            ) : (
44
+              <span className={styles.iconElement}>{item.avatar}</span>
45
+            )
46
+          ) : null;
47
+          return (
48
+            <List.Item
49
+              className={itemCls}
50
+              key={item.key || i}
51
+              onClick={() => {
52
+                onClick?.(item);
53
+              }}
54
+            >
55
+              <List.Item.Meta
56
+                className={styles.meta}
57
+                avatar={leftIcon}
58
+                title={
59
+                  <div className={styles.title}>
60
+                    {item.title}
61
+                    <div className={styles.extra}>{item.extra}</div>
62
+                  </div>
63
+                }
64
+                description={
65
+                  <div>
66
+                    <div className={styles.description}>{item.description}</div>
67
+                    <div className={styles.datetime}>{item.datetime}</div>
68
+                  </div>
69
+                }
70
+              />
71
+            </List.Item>
72
+          );
73
+        }}
74
+      />
75
+      <div className={styles.bottomBar}>
76
+        {showClear ? (
77
+          <div onClick={onClear}>
78
+            {clearText} {title}
79
+          </div>
80
+        ) : null}
81
+        {showViewMore ? (
82
+          <div
83
+            onClick={(e) => {
84
+              if (onViewMore) {
85
+                onViewMore(e);
86
+              }
87
+            }}
88
+          >
89
+            {viewMoreText}
90
+          </div>
91
+        ) : null}
92
+      </div>
93
+    </div>
94
+  );
95
+};
96
+
97
+export default NoticeList;

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

@@ -0,0 +1,103 @@
1
+@import '~antd/es/style/themes/default.less';
2
+
3
+.list {
4
+  max-height: 400px;
5
+  overflow: auto;
6
+  &::-webkit-scrollbar {
7
+    display: none;
8
+  }
9
+  .item {
10
+    padding-right: 24px;
11
+    padding-left: 24px;
12
+    overflow: hidden;
13
+    cursor: pointer;
14
+    transition: all 0.3s;
15
+
16
+    .meta {
17
+      width: 100%;
18
+    }
19
+
20
+    .avatar {
21
+      margin-top: 4px;
22
+      background: @component-background;
23
+    }
24
+    .iconElement {
25
+      font-size: 32px;
26
+    }
27
+
28
+    &.read {
29
+      opacity: 0.4;
30
+    }
31
+    &:last-child {
32
+      border-bottom: 0;
33
+    }
34
+    &:hover {
35
+      background: @primary-1;
36
+    }
37
+    .title {
38
+      margin-bottom: 8px;
39
+      font-weight: normal;
40
+    }
41
+    .description {
42
+      font-size: 12px;
43
+      line-height: @line-height-base;
44
+    }
45
+    .datetime {
46
+      margin-top: 4px;
47
+      font-size: 12px;
48
+      line-height: @line-height-base;
49
+    }
50
+    .extra {
51
+      float: right;
52
+      margin-top: -1.5px;
53
+      margin-right: 0;
54
+      color: @text-color-secondary;
55
+      font-weight: normal;
56
+    }
57
+  }
58
+  .loadMore {
59
+    padding: 8px 0;
60
+    color: @primary-6;
61
+    text-align: center;
62
+    cursor: pointer;
63
+    &.loadedAll {
64
+      color: rgba(0, 0, 0, 0.25);
65
+      cursor: unset;
66
+    }
67
+  }
68
+}
69
+
70
+.notFound {
71
+  padding: 73px 0 88px;
72
+  color: @text-color-secondary;
73
+  text-align: center;
74
+  img {
75
+    display: inline-block;
76
+    height: 76px;
77
+    margin-bottom: 16px;
78
+  }
79
+}
80
+
81
+.bottomBar {
82
+  height: 46px;
83
+  color: @text-color;
84
+  line-height: 46px;
85
+  text-align: center;
86
+  border-top: 1px solid @border-color-split;
87
+  border-radius: 0 0 @border-radius-base @border-radius-base;
88
+  transition: all 0.3s;
89
+  div {
90
+    display: inline-block;
91
+    width: 50%;
92
+    cursor: pointer;
93
+    transition: all 0.3s;
94
+    user-select: none;
95
+
96
+    &:only-child {
97
+      width: 100%;
98
+    }
99
+    &:not(:only-child):last-child {
100
+      border-left: 1px solid @border-color-split;
101
+    }
102
+  }
103
+}

+ 118
- 0
src/components/NoticeIcon/index.jsx Ver arquivo

@@ -0,0 +1,118 @@
1
+import { BellOutlined } from '@ant-design/icons';
2
+import { Badge, Spin, Tabs } from 'antd';
3
+import useMergedState from 'rc-util/es/hooks/useMergedState';
4
+import React from 'react';
5
+import classNames from 'classnames';
6
+import NoticeList from './NoticeList';
7
+import HeaderDropdown from '../HeaderDropdown';
8
+import styles from './index.less';
9
+const { TabPane } = Tabs;
10
+
11
+const NoticeIcon = (props) => {
12
+  const getNotificationBox = () => {
13
+    const {
14
+      children,
15
+      loading,
16
+      onClear,
17
+      onTabChange,
18
+      onItemClick,
19
+      onViewMore,
20
+      clearText,
21
+      viewMoreText,
22
+    } = props;
23
+
24
+    if (!children) {
25
+      return null;
26
+    }
27
+
28
+    const panes = [];
29
+    React.Children.forEach(children, (child) => {
30
+      if (!child) {
31
+        return;
32
+      }
33
+
34
+      const { list, title, count, tabKey, showClear, showViewMore } = child.props;
35
+      const len = list && list.length ? list.length : 0;
36
+      const msgCount = count || count === 0 ? count : len;
37
+      const tabTitle = msgCount > 0 ? `${title} (${msgCount})` : title;
38
+      panes.push(
39
+        <TabPane tab={tabTitle} key={tabKey}>
40
+          <NoticeList
41
+            {...child.props}
42
+            clearText={clearText}
43
+            viewMoreText={viewMoreText}
44
+            data={list}
45
+            onClear={() => {
46
+              onClear?.(title, tabKey);
47
+            }}
48
+            onClick={(item) => {
49
+              onItemClick?.(item, child.props);
50
+            }}
51
+            onViewMore={(event) => {
52
+              onViewMore?.(child.props, event);
53
+            }}
54
+            showClear={showClear}
55
+            showViewMore={showViewMore}
56
+            title={title}
57
+          />
58
+        </TabPane>,
59
+      );
60
+    });
61
+    return (
62
+      <Spin spinning={loading} delay={300}>
63
+        <Tabs className={styles.tabs} onChange={onTabChange}>
64
+          {panes}
65
+        </Tabs>
66
+      </Spin>
67
+    );
68
+  };
69
+
70
+  const { className, count, bell } = props;
71
+  const [visible, setVisible] = useMergedState(false, {
72
+    value: props.popupVisible,
73
+    onChange: props.onPopupVisibleChange,
74
+  });
75
+  const noticeButtonClass = classNames(className, styles.noticeButton);
76
+  const notificationBox = getNotificationBox();
77
+  const NoticeBellIcon = bell || <BellOutlined className={styles.icon} />;
78
+  const trigger = (
79
+    <span
80
+      className={classNames(noticeButtonClass, {
81
+        opened: visible,
82
+      })}
83
+    >
84
+      <Badge
85
+        count={count}
86
+        style={{
87
+          boxShadow: 'none',
88
+        }}
89
+        className={styles.badge}
90
+      >
91
+        {NoticeBellIcon}
92
+      </Badge>
93
+    </span>
94
+  );
95
+
96
+  if (!notificationBox) {
97
+    return trigger;
98
+  }
99
+
100
+  return (
101
+    <HeaderDropdown
102
+      placement="bottomRight"
103
+      overlay={notificationBox}
104
+      overlayClassName={styles.popover}
105
+      trigger={['click']}
106
+      visible={visible}
107
+      onVisibleChange={setVisible}
108
+    >
109
+      {trigger}
110
+    </HeaderDropdown>
111
+  );
112
+};
113
+
114
+NoticeIcon.defaultProps = {
115
+  emptyImage: 'https://gw.alipayobjects.com/zos/rmsportal/wAhyIChODzsoKIOBHcBk.svg',
116
+};
117
+NoticeIcon.Tab = NoticeList;
118
+export default NoticeIcon;

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

@@ -0,0 +1,35 @@
1
+@import '~antd/es/style/themes/default.less';
2
+
3
+.popover {
4
+  position: relative;
5
+  width: 336px;
6
+}
7
+
8
+.noticeButton {
9
+  display: inline-block;
10
+  cursor: pointer;
11
+  transition: all 0.3s;
12
+}
13
+.icon {
14
+  padding: 4px;
15
+  vertical-align: middle;
16
+}
17
+
18
+.badge {
19
+  font-size: 16px;
20
+}
21
+
22
+.tabs {
23
+  :global {
24
+    .ant-tabs-nav-list {
25
+      margin: auto;
26
+    }
27
+
28
+    .ant-tabs-nav-scroll {
29
+      text-align: center;
30
+    }
31
+    .ant-tabs-bar {
32
+      margin-bottom: 0;
33
+    }
34
+  }
35
+}

+ 4
- 0
src/components/PageLoading/index.jsx Ver arquivo

@@ -0,0 +1,4 @@
1
+import { PageLoading } from '@ant-design/pro-layout'; // loading components from code split
2
+// https://umijs.org/plugin/umi-plugin-react.html#dynamicimport
3
+
4
+export default PageLoading;

+ 43
- 0
src/components/UploadImage/index.jsx Ver arquivo

@@ -0,0 +1,43 @@
1
+import React, { useState, useCallback } from 'react'
2
+import { Upload, notification } from 'antd'
3
+import { LoadingOutlined, PlusOutlined } from '@ant-design/icons'
4
+import uploadFile from '@/utils/uploadFile'
5
+
6
+export default (props) => {
7
+  const [loading, setLoading] = useState(false)
8
+
9
+  const handleChange = useCallback(({ file }) => {
10
+    switch (file.status) {
11
+      case 'done':
12
+        setLoading(false)
13
+        props.onChange(file.response)
14
+        break
15
+      case 'error':
16
+        setLoading(false)
17
+        notification.error({ message: file.error })
18
+      break
19
+      default:
20
+        setLoading(true)
21
+    }
22
+  }, [props])
23
+
24
+  return (
25
+    <Upload
26
+      accept=".png, .jpg, .jpeg, .gif"
27
+      listType="picture-card"
28
+      className="avatar-uploader"
29
+      showUploadList={false}
30
+      customRequest={uploadFile}
31
+      onChange={handleChange}
32
+    >
33
+      {
34
+        props.value ?
35
+          <img src={props.value} width="100%" alt="" /> :
36
+          <div>
37
+            {loading ? <LoadingOutlined /> : <PlusOutlined />}
38
+            <div style={{ marginTop: 8 }}>上传</div>
39
+          </div>
40
+      }
41
+    </Upload>
42
+  )
43
+}

+ 42
- 0
src/components/WangEditor/Preview.jsx Ver arquivo

@@ -0,0 +1,42 @@
1
+import React, { useEffect, useRef } from 'react'
2
+import { Modal } from 'antd'
3
+
4
+export default props => {
5
+  const ref = useRef()
6
+
7
+  useEffect(() => {
8
+    let t = null
9
+    if (props.visible) {
10
+      // 防止 postMessage 的时候 iframe 内容还没有加载完成
11
+      t = setInterval(() => {
12
+        // window.preViewFrame.window.postMessage(props.html, '*')
13
+        if (ref.current) {
14
+          ref.current.contentWindow.postMessage(props.html, '*')
15
+        }
16
+      }, 800)
17
+    }
18
+
19
+    return () => t && clearInterval(t)
20
+  }, [props.visible, props.html])
21
+
22
+  return (
23
+    <Modal
24
+      visible={props.visible}
25
+      wrapClassName="editor-preivew-modal-content"
26
+      width={props.width}
27
+      bodyStyle={props.style}
28
+      footer={null}
29
+      closable={false}
30
+      title={null}
31
+      onCancel={props.onCancel}
32
+      >
33
+      <iframe
34
+        ref={ref}
35
+        style={{width: '100%', height: '100%'}}
36
+        src={`${window.location.origin}/preview-html/index.html`}
37
+        frameBorder={0}
38
+        name="preViewFrame"
39
+      ></iframe>
40
+    </Modal>
41
+  )
42
+}

+ 27
- 0
src/components/WangEditor/PreviewMenu.js Ver arquivo

@@ -0,0 +1,27 @@
1
+import E from 'wangeditor'
2
+
3
+const { BtnMenu } = E
4
+
5
+class PreViewMenu extends BtnMenu {
6
+  constructor(editor) {
7
+    // data-title属性表示当鼠标悬停在该按钮上时提示该按钮的功能简述
8
+      const $elem = E.$(
9
+          `<div class="w-e-menu" data-title="预览">
10
+            <i>预览</i>
11
+          </div>`
12
+      )
13
+      super($elem, editor)
14
+      this.editor = editor
15
+      // <i class="w-e-icon-fullscreen"></i>
16
+  }
17
+
18
+  static preview() {}
19
+
20
+  clickHandler() {
21
+    PreViewMenu.preview(this.editor.txt.html())
22
+  }
23
+
24
+  tryChangeActive(){}
25
+}
26
+
27
+export default PreViewMenu

+ 119
- 0
src/components/WangEditor/WangEditor.jsx Ver arquivo

@@ -0,0 +1,119 @@
1
+import React, { useState, useRef, useEffect, useCallback } from 'react';
2
+import E from 'wangeditor';
3
+import PreviewMenu from './PreviewMenu'
4
+import Preview from './Preview'
5
+import { fetch, apis } from '../../utils/request';
6
+
7
+export default props => {
8
+  const ref = useRef()
9
+  const editorRef = useRef()
10
+  const [preview, setPreview] = useState(false)
11
+  const [content, setContent] = useState()
12
+
13
+  // wangeditor 有bug, 初始先触发 onchange
14
+  const firstChanged = useRef(false)
15
+
16
+  const initEditor = useCallback(() => {
17
+    const editor = new E(ref.current)
18
+    editorRef.current = editor
19
+    
20
+    // 取消自动 focus
21
+    editor.config.focus = false
22
+
23
+    // 触发 change
24
+    editor.config.onchange = html => {
25
+      // 规避 bug
26
+      if (!firstChanged.current) {
27
+        firstChanged.current = true
28
+        return
29
+      }
30
+
31
+      setContent(html)
32
+
33
+      if (typeof props.onChange === 'function') {
34
+        props.onChange(html)
35
+      }
36
+    }
37
+
38
+    editor.config.zIndex = 100
39
+
40
+    // 自定义图片上传
41
+    editor.config.uploadImgMaxLength = 1
42
+    editor.config.customUploadImg = function (files, insert) {
43
+      if (!files.length) return
44
+      
45
+      const data = new FormData()
46
+      data.append('file', files[0])
47
+      fetch(apis.image.upload)({data}).then(insert)
48
+    }
49
+
50
+    // 扩展预览按钮
51
+    editor.menus.extend('previewMenu', PreviewMenu)
52
+    PreviewMenu.preview = (html) => setPreview(true)
53
+
54
+    // 配置菜单
55
+    editor.config.menus = [
56
+      'head',  // 标题
57
+      'bold',  // 粗体
58
+      'fontSize',  // 字号
59
+      'fontName',  // 字体
60
+      'italic',  // 斜体
61
+      'underline',  // 下划线
62
+      'strikeThrough',  // 删除线
63
+      'foreColor',  // 文字颜色
64
+      'backColor',  // 背景颜色
65
+      'list',  // 列表
66
+      'justify',  // 对齐方式
67
+      'quote',  // 引用
68
+      'image',  // 插入图片
69
+      'undo',  // 撤销
70
+      'redo',  // 重复
71
+      'previewMenu'
72
+    ]
73
+
74
+    // 过滤 word 字符
75
+    editor.config.pasteFilterStyle = false
76
+    editor.config.pasteTextHandle = ctt => {
77
+      const regs = [
78
+        /<!--\[if [\s\S]*?endif\]-->/ig,
79
+        /<[a-zA-Z0-9]+\:[^>]+>[^>]*<\/[a-zA-Z0-9]+\:[^>]+>/ig,
80
+        /<[a-zA-Z0-9]+\:[^>]+\/>/ig,
81
+        /<style>[\s\S]*?<\/style>/ig,
82
+        new RegExp('\u2029', 'ig'),     // 替换word分隔符 序号 8233
83
+      ]
84
+
85
+      return regs.reduce((acc, reg) => {
86
+        return acc.replace(reg, '')
87
+      }, ctt)
88
+    }
89
+
90
+    editor.create()
91
+    editor.$textElem.attr('contenteditable', props.contenteditable !== false)
92
+
93
+    return () => editor.destroy()
94
+  }, [props.contenteditable])
95
+
96
+  useEffect(() => {
97
+    initEditor()
98
+  }, [])
99
+  
100
+  useEffect(() => {
101
+    if (props.value !== content && editorRef.current) {
102
+      setContent(props.value)
103
+      editorRef.current.txt.html(props.value)
104
+    }
105
+  }, [props.value, content])
106
+
107
+  return (
108
+    <>
109
+      <div ref={ref} style={{ textAlign: 'left' }} />
110
+      <Preview
111
+        width={426}
112
+        style={{width: '426px', height: '863px', margin: 0, padding: 0}}
113
+        visible={preview}
114
+        html={props.value}
115
+        onCancel={() => setPreview(false)}
116
+      />
117
+    </>
118
+  )
119
+}

+ 0
- 0
src/components/WangEditor/index.js Ver arquivo


+ 1
- 0
src/e2e/__mocks__/antd-pro-merge-less.js Ver arquivo

@@ -0,0 +1 @@
1
+export default undefined;

+ 57
- 0
src/e2e/baseLayout.e2e.js Ver arquivo

@@ -0,0 +1,57 @@
1
+const { uniq } = require('lodash');
2
+const RouterConfig = require('../../config/config').default.routes;
3
+
4
+const BASE_URL = `http://localhost:${process.env.PORT || 8000}`;
5
+
6
+function formatter(routes, parentPath = '') {
7
+  const fixedParentPath = parentPath.replace(/\/{1,}/g, '/');
8
+  let result = [];
9
+  routes.forEach((item) => {
10
+    if (item.path) {
11
+      result.push(`${fixedParentPath}/${item.path}`.replace(/\/{1,}/g, '/'));
12
+    }
13
+    if (item.routes) {
14
+      result = result.concat(
15
+        formatter(item.routes, item.path ? `${fixedParentPath}/${item.path}` : parentPath),
16
+      );
17
+    }
18
+  });
19
+  return uniq(result.filter((item) => !!item));
20
+}
21
+
22
+beforeEach(async () => {
23
+  await page.goto(`${BASE_URL}`);
24
+  await page.evaluate(() => {
25
+    localStorage.setItem('antd-pro-authority', '["admin"]');
26
+  });
27
+});
28
+
29
+describe('Ant Design Pro E2E test', () => {
30
+  const testPage = (path) => async () => {
31
+    await page.goto(`${BASE_URL}${path}`);
32
+    await page.waitForSelector('footer', {
33
+      timeout: 2000,
34
+    });
35
+    const haveFooter = await page.evaluate(
36
+      () => document.getElementsByTagName('footer').length > 0,
37
+    );
38
+    expect(haveFooter).toBeTruthy();
39
+  };
40
+
41
+  const routers = formatter(RouterConfig);
42
+  routers.forEach((route) => {
43
+    it(`test pages ${route}`, testPage(route));
44
+  });
45
+
46
+  it('topmenu should have footer', async () => {
47
+    const params = '?navTheme=light&layout=topmenu';
48
+    await page.goto(`${BASE_URL}${params}`);
49
+    await page.waitForSelector('footer', {
50
+      timeout: 2000,
51
+    });
52
+    const haveFooter = await page.evaluate(
53
+      () => document.getElementsByTagName('footer').length > 0,
54
+    );
55
+    expect(haveFooter).toBeTruthy();
56
+  });
57
+});

+ 107
- 0
src/global.jsx Ver arquivo

@@ -0,0 +1,107 @@
1
+import { Button, message, notification } from 'antd';
2
+// import { useIntl } from 'umi';
3
+import defaultSettings from '../config/defaultSettings';
4
+
5
+const { pwa } = defaultSettings;
6
+const isHttps = document.location.protocol === 'https:'; // if pwa is true
7
+
8
+if (pwa) {
9
+  // Notify user if offline now
10
+  window.addEventListener('sw.offline', () => {
11
+    message.warning(
12
+      // useIntl().formatMessage({
13
+      //   id: 'app.pwa.offline',
14
+      // }),
15
+      "当前处于离线状态"
16
+    );
17
+  }); // Pop up a prompt on the page asking the user if they want to use the latest version
18
+
19
+  window.addEventListener('sw.updated', (event) => {
20
+    const e = event;
21
+
22
+    const reloadSW = async () => {
23
+      // Check if there is sw whose state is waiting in ServiceWorkerRegistration
24
+      // https://developer.mozilla.org/en-US/docs/Web/API/ServiceWorkerRegistration
25
+      const worker = e.detail && e.detail.waiting;
26
+
27
+      if (!worker) {
28
+        return true;
29
+      } // Send skip-waiting event to waiting SW with MessageChannel
30
+
31
+      await new Promise((resolve, reject) => {
32
+        const channel = new MessageChannel();
33
+
34
+        channel.port1.onmessage = (msgEvent) => {
35
+          if (msgEvent.data.error) {
36
+            reject(msgEvent.data.error);
37
+          } else {
38
+            resolve(msgEvent.data);
39
+          }
40
+        };
41
+
42
+        worker.postMessage(
43
+          {
44
+            type: 'skip-waiting',
45
+          },
46
+          [channel.port2],
47
+        );
48
+      }); // Refresh current page to use the updated HTML and other assets after SW has skiped waiting
49
+
50
+      window.location.reload(true);
51
+      return true;
52
+    };
53
+
54
+    const key = `open${Date.now()}`;
55
+    const btn = (
56
+      <Button
57
+        type="primary"
58
+        onClick={() => {
59
+          notification.close(key);
60
+          reloadSW();
61
+        }}
62
+      >
63
+        {/* {useIntl().formatMessage({
64
+          id: 'app.pwa.serviceworker.updated.ok',
65
+        })} */}
66
+        刷新
67
+      </Button>
68
+    );
69
+    notification.open({
70
+      // message: useIntl().formatMessage({
71
+      //   id: 'app.pwa.serviceworker.updated',
72
+      // }),
73
+      message: '有新内容',
74
+      // description: useIntl().formatMessage({
75
+      //   id: 'app.pwa.serviceworker.updated.hint',
76
+      // }),
77
+      description: '请点击',
78
+      btn,
79
+      key,
80
+      onClose: async () => null,
81
+    });
82
+  });
83
+} else if ('serviceWorker' in navigator && isHttps) {
84
+  // unregister service worker
85
+  const { serviceWorker } = navigator;
86
+
87
+  if (serviceWorker.getRegistrations) {
88
+    serviceWorker.getRegistrations().then((sws) => {
89
+      sws.forEach((sw) => {
90
+        sw.unregister();
91
+      });
92
+    });
93
+  }
94
+
95
+  serviceWorker.getRegistration().then((sw) => {
96
+    if (sw) sw.unregister();
97
+  }); // remove all caches
98
+  // @ts-ignore
99
+
100
+  if (window.caches && window.caches.keys) {
101
+    caches.keys().then((keys) => {
102
+      keys.forEach((key) => {
103
+        caches.delete(key);
104
+      });
105
+    });
106
+  }
107
+}

+ 59
- 0
src/global.less Ver arquivo

@@ -0,0 +1,59 @@
1
+@import '~antd/es/style/themes/default.less';
2
+
3
+html,
4
+body,
5
+#root {
6
+  height: 100%;
7
+}
8
+
9
+.colorWeak {
10
+  filter: invert(80%);
11
+}
12
+
13
+.ant-layout {
14
+  min-height: 100vh;
15
+}
16
+
17
+canvas {
18
+  display: block;
19
+}
20
+
21
+body {
22
+  text-rendering: optimizeLegibility;
23
+  -webkit-font-smoothing: antialiased;
24
+  -moz-osx-font-smoothing: grayscale;
25
+}
26
+
27
+ul,
28
+ol {
29
+  list-style: none;
30
+}
31
+
32
+@media (max-width: @screen-xs) {
33
+  .ant-table {
34
+    width: 100%;
35
+    overflow-x: auto;
36
+    &-thead > tr,
37
+    &-tbody > tr {
38
+      > th,
39
+      > td {
40
+        white-space: pre;
41
+        > span {
42
+          display: block;
43
+        }
44
+      }
45
+    }
46
+  }
47
+}
48
+
49
+// 兼容IE11
50
+@media screen and(-ms-high-contrast: active), (-ms-high-contrast: none) {
51
+  body .ant-design-pro > .ant-layout {
52
+    min-height: 100vh;
53
+  }
54
+}
55
+
56
+.avatar-uploader > .ant-upload {
57
+  width: 128px;
58
+  height: 128px;
59
+}

+ 173
- 0
src/layouts/BasicLayout.jsx Ver arquivo

@@ -0,0 +1,173 @@
1
+/**
2
+ * Ant Design Pro v4 use `@ant-design/pro-layout` to handle Layout.
3
+ *
4
+ * @see You can view component api by: https://github.com/ant-design/ant-design-pro-layout
5
+ */
6
+import ProLayout, { DefaultFooter } from '@ant-design/pro-layout';
7
+import React, { useEffect, useMemo } from 'react';
8
+// import { Link, useIntl, connect, history } from 'umi';
9
+import { Link, connect, history } from 'umi';
10
+import { GithubOutlined } from '@ant-design/icons';
11
+import { Result, Button } from 'antd';
12
+import Authorized from '@/utils/Authorized';
13
+import { getCurrentRoute } from '@/utils/utils'
14
+import RightContent from '@/components/GlobalHeader/RightContent';
15
+// import { getMatchMenu } from '@umijs/route-utils';
16
+import logo from '../assets/logo.svg';
17
+
18
+const noMatch = (
19
+  <Result
20
+    status={403}
21
+    title="403"
22
+    subTitle="Sorry, you are not authorized to access this page."
23
+    extra={
24
+      <Button type="primary">
25
+        <Link to="/user/login">Go Login</Link>
26
+      </Button>
27
+    }
28
+  />
29
+);
30
+
31
+/** Use Authorized check all menu item */
32
+const menuDataRender = (menuRoles) => (menuList) =>
33
+  menuList.map((item) => {
34
+    const needAuth = !!item.menuCode
35
+
36
+    const authority = (menuRoles.filter(x => x.menuCode === item.menuCode)[0] || {}).roleString || ''
37
+
38
+    const localItem = {
39
+      ...item,
40
+      children: item.children ? menuDataRender(menuRoles)(item.children) : undefined,
41
+    };
42
+
43
+    return Authorized.check(needAuth ? authority.split(',') : undefined, localItem, null);
44
+  });
45
+
46
+const defaultFooterDom = (
47
+  <DefaultFooter
48
+    copyright={`${new Date().getFullYear()} 云致科技`}
49
+    links={[
50
+      // {
51
+      //   key: 'Ant Design Pro',
52
+      //   title: 'Ant Design Pro',
53
+      //   href: 'https://pro.ant.design',
54
+      //   blankTarget: true,
55
+      // },
56
+      // {
57
+      //   key: 'github',
58
+      //   title: <GithubOutlined />,
59
+      //   href: 'https://github.com/ant-design/ant-design-pro',
60
+      //   blankTarget: true,
61
+      // },
62
+      // {
63
+      //   key: 'Ant Design',
64
+      //   title: 'Ant Design',
65
+      //   href: 'https://ant.design',
66
+      //   blankTarget: true,
67
+      // },
68
+    ]}
69
+  />
70
+);
71
+
72
+const BasicLayout = (props) => {
73
+  const {
74
+    // user,
75
+    menus,
76
+    dispatch,
77
+    children,
78
+    settings,
79
+    location = {
80
+      pathname: '/',
81
+    },
82
+  } = props;
83
+
84
+  const handleMenuCollapse = (payload) => {
85
+    if (dispatch) {
86
+      dispatch({
87
+        type: 'global/changeLayoutCollapsed',
88
+        payload,
89
+      });
90
+    }
91
+  }; // get children authority
92
+
93
+  const authorized = useMemo(
94
+    () => {
95
+      const routeItem = getCurrentRoute(location.pathname || '/')
96
+      if (!routeItem || !routeItem.menuCode) {
97
+        return { authority: undefined }
98
+      }
99
+
100
+      const authority = (menus.filter(x => x.menuCode === routeItem.menuCode)[0] || {}).roleString
101
+      return {
102
+        authority: authority ? authority.split(',') : ['any-string-for-no-right']
103
+      }
104
+    },
105
+    [location.pathname, menus],
106
+  );
107
+
108
+  // const { formatMessage } = useIntl();
109
+
110
+  return (
111
+    <ProLayout
112
+      logo={logo}
113
+      // formatMessage={formatMessage}
114
+      {...props}
115
+      {...settings}
116
+      onCollapse={handleMenuCollapse}
117
+      onMenuHeaderClick={() => history.push('/')}
118
+      menuItemRender={(menuItemProps, defaultDom) => {
119
+        if (
120
+          menuItemProps.isUrl ||
121
+          !menuItemProps.path ||
122
+          location.pathname === menuItemProps.path
123
+        ) {
124
+          return defaultDom;
125
+        }
126
+
127
+        return <Link to={menuItemProps.path}>{defaultDom}</Link>;
128
+      }}
129
+      breadcrumbRender={(routers = []) => [
130
+        {
131
+          path: '/',
132
+          breadcrumbName: '首页',
133
+          // breadcrumbName: formatMessage({
134
+          //   id: 'menu.home',
135
+          // }),
136
+        },
137
+        ...routers,
138
+      ]}
139
+      itemRender={(route, params, routes, paths) => {
140
+        const first = routes.indexOf(route) === 0;
141
+        return first ? (
142
+          <Link to={paths.join('/')}>{route.breadcrumbName}</Link>
143
+        ) : (
144
+          <span>{route.breadcrumbName}</span>
145
+        );
146
+      }}
147
+      footerRender={() => {
148
+        if (settings.footerRender || settings.footerRender === undefined) {
149
+          return defaultFooterDom;
150
+        }
151
+
152
+        return null;
153
+      }}
154
+      menuDataRender={menuDataRender(menus)}
155
+      rightContentRender={() => <RightContent />}
156
+      waterMarkProps={{
157
+        content: '云致科技',
158
+        fontColor: 'rgba(24,144,255,0.15)',
159
+      }}
160
+    >
161
+      <Authorized authority={authorized.authority} noMatch={noMatch}>
162
+        {children}
163
+      </Authorized>
164
+    </ProLayout>
165
+  );
166
+};
167
+
168
+export default connect(({ global, settings, user }) => ({
169
+  collapsed: global.collapsed,
170
+  settings,
171
+  user: user.currentUser,
172
+  menus: user.menus,
173
+}))(BasicLayout);

+ 10
- 0
src/layouts/BlankLayout.jsx Ver arquivo

@@ -0,0 +1,10 @@
1
+import React from 'react';
2
+import { Inspector } from 'react-dev-inspector';
3
+
4
+const InspectorWrapper = process.env.NODE_ENV === 'development' ? Inspector : React.Fragment;
5
+
6
+const Layout = ({ children }) => {
7
+  return <InspectorWrapper>{children}</InspectorWrapper>;
8
+};
9
+
10
+export default Layout;

+ 49
- 0
src/layouts/SecurityLayout.jsx Ver arquivo

@@ -0,0 +1,49 @@
1
+import React from 'react';
2
+import { PageLoading } from '@ant-design/pro-layout';
3
+import { Redirect, connect } from 'umi';
4
+import { stringify } from 'querystring';
5
+
6
+class SecurityLayout extends React.Component {
7
+  state = {
8
+    isReady: false,
9
+  };
10
+
11
+  componentDidMount() {
12
+    this.setState({
13
+      isReady: true,
14
+    });
15
+    const { dispatch } = this.props;
16
+
17
+    if (dispatch) {
18
+      dispatch({
19
+        type: 'user/fetchCurrent',
20
+      });
21
+    }
22
+  }
23
+
24
+  render() {
25
+    const { isReady } = this.state;
26
+    const { children, loading, currentUser } = this.props; // You can replace it to your authentication rule (such as check token exists)
27
+    // 你可以把它替换成你自己的登录认证规则(比如判断 token 是否存在)
28
+
29
+    const isLogin = currentUser && currentUser.userId;
30
+    const queryString = stringify({
31
+      redirect: window.location.href,
32
+    });
33
+
34
+    if ((!isLogin && loading) || !isReady) {
35
+      return <PageLoading />;
36
+    }
37
+
38
+    if (!isLogin && window.location.pathname !== '/user/login') {
39
+      return <Redirect to={`/user/login?${queryString}`} />;
40
+    }
41
+
42
+    return children;
43
+  }
44
+}
45
+
46
+export default connect(({ user, loading }) => ({
47
+  currentUser: user.currentUser,
48
+  loading: loading.models.user,
49
+}))(SecurityLayout);

+ 65
- 0
src/layouts/UserLayout.jsx Ver arquivo

@@ -0,0 +1,65 @@
1
+import { DefaultFooter, getMenuData, getPageTitle } from '@ant-design/pro-layout';
2
+import { Helmet, HelmetProvider } from 'react-helmet-async';
3
+// import { Link, SelectLang, useIntl, connect, FormattedMessage } from 'umi';
4
+import { Link, connect } from 'umi';
5
+import React from 'react';
6
+import logo from '../assets/logo.svg';
7
+import styles from './UserLayout.less';
8
+
9
+const UserLayout = (props) => {
10
+  const {
11
+    route = {
12
+      routes: [],
13
+    },
14
+  } = props;
15
+  const { routes = [] } = route;
16
+  const {
17
+    children,
18
+    location = {
19
+      pathname: '',
20
+    },
21
+  } = props;
22
+  // const { formatMessage } = useIntl();
23
+  const { breadcrumb } = getMenuData(routes);
24
+  const title = getPageTitle({
25
+    pathname: location.pathname,
26
+    formatMessage: x => x,
27
+    breadcrumb,
28
+    ...props,
29
+  });
30
+  return (
31
+    <HelmetProvider>
32
+      <Helmet>
33
+        <title>{title}</title>
34
+        <meta name="description" content={title} />
35
+      </Helmet>
36
+
37
+      <div className={styles.container}>
38
+        {/* <div className={styles.lang}>
39
+          <SelectLang />
40
+        </div> */}
41
+        <div className={styles.content}>
42
+          <div className={styles.top}>
43
+            <div className={styles.header}>
44
+              <Link to="/">
45
+                <img alt="logo" className={styles.logo} src={logo} />
46
+                <span className={styles.title}>Ant Design</span>
47
+              </Link>
48
+            </div>
49
+            <div className={styles.desc}>
50
+              {/* <FormattedMessage
51
+                id="pages.layouts.userLayout.title"
52
+                defaultMessage="Ant Design 是西湖区最具影响力的 Web 设计规范"
53
+              /> */}
54
+              Ant Design 是西湖区最具影响力的 Web 设计规范
55
+            </div>
56
+          </div>
57
+          {children}
58
+        </div>
59
+        <DefaultFooter />
60
+      </div>
61
+    </HelmetProvider>
62
+  );
63
+};
64
+
65
+export default connect(({ settings }) => ({ ...settings }))(UserLayout);

+ 71
- 0
src/layouts/UserLayout.less Ver arquivo

@@ -0,0 +1,71 @@
1
+@import '~antd/es/style/themes/default.less';
2
+
3
+.container {
4
+  display: flex;
5
+  flex-direction: column;
6
+  height: 100vh;
7
+  overflow: auto;
8
+  background: @layout-body-background;
9
+}
10
+
11
+.lang {
12
+  width: 100%;
13
+  height: 40px;
14
+  line-height: 44px;
15
+  text-align: right;
16
+  :global(.ant-dropdown-trigger) {
17
+    margin-right: 24px;
18
+  }
19
+}
20
+
21
+.content {
22
+  flex: 1;
23
+  padding: 32px 0;
24
+}
25
+
26
+@media (min-width: @screen-md-min) {
27
+  .container {
28
+    background-image: url('https://gw.alipayobjects.com/zos/rmsportal/TVYTbAXWheQpRcWDaDMu.svg');
29
+    background-repeat: no-repeat;
30
+    background-position: center 110px;
31
+    background-size: 100%;
32
+  }
33
+
34
+  .content {
35
+    padding: 32px 0 24px;
36
+  }
37
+}
38
+
39
+.top {
40
+  text-align: center;
41
+}
42
+
43
+.header {
44
+  height: 44px;
45
+  line-height: 44px;
46
+  a {
47
+    text-decoration: none;
48
+  }
49
+}
50
+
51
+.logo {
52
+  height: 44px;
53
+  margin-right: 16px;
54
+  vertical-align: top;
55
+}
56
+
57
+.title {
58
+  position: relative;
59
+  top: 2px;
60
+  color: @heading-color;
61
+  font-weight: 600;
62
+  font-size: 33px;
63
+  font-family: Avenir, 'Helvetica Neue', Arial, Helvetica, sans-serif;
64
+}
65
+
66
+.desc {
67
+  margin-top: 12px;
68
+  margin-bottom: 40px;
69
+  color: @text-color-secondary;
70
+  font-size: @font-size-base;
71
+}

+ 23
- 0
src/locales/en-US.js Ver arquivo

@@ -0,0 +1,23 @@
1
+import component from './en-US/component';
2
+import globalHeader from './en-US/globalHeader';
3
+import menu from './en-US/menu';
4
+import pwa from './en-US/pwa';
5
+import settingDrawer from './en-US/settingDrawer';
6
+import settings from './en-US/settings';
7
+import pages from './en-US/pages';
8
+export default {
9
+  'navBar.lang': 'Languages',
10
+  'layout.user.link.help': 'Help',
11
+  'layout.user.link.privacy': 'Privacy',
12
+  'layout.user.link.terms': 'Terms',
13
+  'app.preview.down.block': 'Download this page to your local project',
14
+  'app.welcome.link.fetch-blocks': 'Get all block',
15
+  'app.welcome.link.block-list': 'Quickly build standard, pages based on `block` development',
16
+  ...globalHeader,
17
+  ...menu,
18
+  ...settingDrawer,
19
+  ...settings,
20
+  ...pwa,
21
+  ...component,
22
+  ...pages,
23
+};

+ 5
- 0
src/locales/en-US/component.js Ver arquivo

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

+ 17
- 0
src/locales/en-US/globalHeader.js Ver arquivo

@@ -0,0 +1,17 @@
1
+export default {
2
+  'component.globalHeader.search': 'Search',
3
+  'component.globalHeader.search.example1': 'Search example 1',
4
+  'component.globalHeader.search.example2': 'Search example 2',
5
+  'component.globalHeader.search.example3': 'Search example 3',
6
+  'component.globalHeader.help': 'Help',
7
+  'component.globalHeader.notification': 'Notification',
8
+  'component.globalHeader.notification.empty': 'You have viewed all notifications.',
9
+  'component.globalHeader.message': 'Message',
10
+  'component.globalHeader.message.empty': 'You have viewed all messsages.',
11
+  'component.globalHeader.event': 'Event',
12
+  'component.globalHeader.event.empty': 'You have viewed all events.',
13
+  'component.noticeIcon.clear': 'Clear',
14
+  'component.noticeIcon.cleared': 'Cleared',
15
+  'component.noticeIcon.empty': 'No notifications',
16
+  'component.noticeIcon.view-more': 'View more',
17
+};

+ 52
- 0
src/locales/en-US/menu.js Ver arquivo

@@ -0,0 +1,52 @@
1
+export default {
2
+  'menu.welcome': 'Welcome',
3
+  'menu.more-blocks': 'More Blocks',
4
+  'menu.home': 'Home',
5
+  'menu.admin': 'Admin',
6
+  'menu.admin.sub-page': 'Sub-Page',
7
+  'menu.login': 'Login',
8
+  'menu.register': 'Register',
9
+  'menu.register.result': 'Register Result',
10
+  'menu.dashboard': 'Dashboard',
11
+  'menu.dashboard.analysis': 'Analysis',
12
+  'menu.dashboard.monitor': 'Monitor',
13
+  'menu.dashboard.workplace': 'Workplace',
14
+  'menu.exception.403': '403',
15
+  'menu.exception.404': '404',
16
+  'menu.exception.500': '500',
17
+  'menu.form': 'Form',
18
+  'menu.form.basic-form': 'Basic Form',
19
+  'menu.form.step-form': 'Step Form',
20
+  'menu.form.step-form.info': 'Step Form(write transfer information)',
21
+  'menu.form.step-form.confirm': 'Step Form(confirm transfer information)',
22
+  'menu.form.step-form.result': 'Step Form(finished)',
23
+  'menu.form.advanced-form': 'Advanced Form',
24
+  'menu.list': 'List',
25
+  'menu.list.table-list': 'Search Table',
26
+  'menu.list.basic-list': 'Basic List',
27
+  'menu.list.card-list': 'Card List',
28
+  'menu.list.search-list': 'Search List',
29
+  'menu.list.search-list.articles': 'Search List(articles)',
30
+  'menu.list.search-list.projects': 'Search List(projects)',
31
+  'menu.list.search-list.applications': 'Search List(applications)',
32
+  'menu.profile': 'Profile',
33
+  'menu.profile.basic': 'Basic Profile',
34
+  'menu.profile.advanced': 'Advanced Profile',
35
+  'menu.result': 'Result',
36
+  'menu.result.success': 'Success',
37
+  'menu.result.fail': 'Fail',
38
+  'menu.exception': 'Exception',
39
+  'menu.exception.not-permission': '403',
40
+  'menu.exception.not-find': '404',
41
+  'menu.exception.server-error': '500',
42
+  'menu.exception.trigger': 'Trigger',
43
+  'menu.account': 'Account',
44
+  'menu.account.center': 'Account Center',
45
+  'menu.account.settings': 'Account Settings',
46
+  'menu.account.trigger': 'Trigger Error',
47
+  'menu.account.logout': 'Logout',
48
+  'menu.editor': 'Graphic Editor',
49
+  'menu.editor.flow': 'Flow Editor',
50
+  'menu.editor.mind': 'Mind Editor',
51
+  'menu.editor.koni': 'Koni Editor',
52
+};

+ 68
- 0
src/locales/en-US/pages.js Ver arquivo

@@ -0,0 +1,68 @@
1
+export default {
2
+  'pages.layouts.userLayout.title':
3
+    'Ant Design is the most influential web design specification in Xihu district',
4
+  'pages.login.accountLogin.tab': 'Account Login',
5
+  'pages.login.accountLogin.errorMessage': 'Incorrect username/password(admin/ant.design)',
6
+  'pages.login.username.placeholder': 'Username: admin or user',
7
+  'pages.login.username.required': 'Please input your username!',
8
+  'pages.login.password.placeholder': 'Password: ant.design',
9
+  'pages.login.password.required': 'Please input your password!',
10
+  'pages.login.phoneLogin.tab': 'Phone Login',
11
+  'pages.login.phoneLogin.errorMessage': 'Verification Code Error',
12
+  'pages.login.phoneNumber.placeholder': 'Phone Number',
13
+  'pages.login.phoneNumber.required': 'Please input your phone number!',
14
+  'pages.login.phoneNumber.invalid': 'Phone number is invalid!',
15
+  'pages.login.captcha.placeholder': 'Verification Code',
16
+  'pages.login.captcha.required': 'Please input verification code!',
17
+  'pages.login.phoneLogin.getVerificationCode': 'Get Code',
18
+  'pages.getCaptchaSecondText': 'sec(s)',
19
+  'pages.login.rememberMe': 'Remember me',
20
+  'pages.login.forgotPassword': 'Forgot Password ?',
21
+  'pages.login.submit': 'Submit',
22
+  'pages.login.loginWith': 'Login with :',
23
+  'pages.login.registerAccount': 'Register Account',
24
+  'pages.welcome.advancedComponent': 'Advanced Component',
25
+  'pages.welcome.link': 'Welcome',
26
+  'pages.welcome.advancedLayout': 'Advanced Layout',
27
+  'pages.welcome.alertMessage': 'Faster and stronger heavy-duty components have been released.',
28
+  'pages.admin.subPage.title': 'This page can only be viewed by Admin',
29
+  'pages.admin.subPage.alertMessage':
30
+    'Umi ui is now released, welcome to use npm run ui to start the experience.',
31
+  'pages.searchTable.createForm.newRule': 'New Rule',
32
+  'pages.searchTable.updateForm.ruleConfig': 'Rule configuration',
33
+  'pages.searchTable.updateForm.basicConfig': 'Basic Information',
34
+  'pages.searchTable.updateForm.ruleName.nameLabel': 'Rule Name',
35
+  'pages.searchTable.updateForm.ruleName.nameRules': 'Please enter the rule name!',
36
+  'pages.searchTable.updateForm.ruleDesc.descLabel': 'Rule Description',
37
+  'pages.searchTable.updateForm.ruleDesc.descPlaceholder': 'Please enter at least five characters',
38
+  'pages.searchTable.updateForm.ruleDesc.descRules':
39
+    'Please enter a rule description of at least five characters!',
40
+  'pages.searchTable.updateForm.ruleProps.title': 'Configure Properties',
41
+  'pages.searchTable.updateForm.object': 'Monitoring Object',
42
+  'pages.searchTable.updateForm.ruleProps.templateLabel': 'Rule Template',
43
+  'pages.searchTable.updateForm.ruleProps.typeLabel': 'Rule Type',
44
+  'pages.searchTable.updateForm.schedulingPeriod.title': 'Set Scheduling Period',
45
+  'pages.searchTable.updateForm.schedulingPeriod.timeLabel': 'Starting Time',
46
+  'pages.searchTable.updateForm.schedulingPeriod.timeRules': 'Please choose a start time!',
47
+  'pages.searchTable.titleDesc': 'Description',
48
+  'pages.searchTable.ruleName': 'Rule name is required',
49
+  'pages.searchTable.titleCallNo': 'Number of Service Calls',
50
+  'pages.searchTable.titleStatus': 'Status',
51
+  'pages.searchTable.nameStatus.default': 'default',
52
+  'pages.searchTable.nameStatus.running': 'running',
53
+  'pages.searchTable.nameStatus.online': 'online',
54
+  'pages.searchTable.nameStatus.abnormal': 'abnormal',
55
+  'pages.searchTable.titleUpdatedAt': 'Last Scheduled at',
56
+  'pages.searchTable.exception': 'Please enter the reason for the exception!',
57
+  'pages.searchTable.titleOption': 'Option',
58
+  'pages.searchTable.config': 'Configuration',
59
+  'pages.searchTable.subscribeAlert': 'Subscribe to alerts',
60
+  'pages.searchTable.title': 'Enquiry Form',
61
+  'pages.searchTable.new': 'New',
62
+  'pages.searchTable.chosen': 'chosen',
63
+  'pages.searchTable.item': 'item',
64
+  'pages.searchTable.totalServiceCalls': 'Total Number of Service Calls',
65
+  'pages.searchTable.tenThousand': '0000',
66
+  'pages.searchTable.batchDeletion': 'bacth deletion',
67
+  'pages.searchTable.batchApproval': 'batch approval',
68
+};

+ 6
- 0
src/locales/en-US/pwa.js Ver arquivo

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

+ 31
- 0
src/locales/en-US/settingDrawer.js Ver arquivo

@@ -0,0 +1,31 @@
1
+export default {
2
+  'app.setting.pagestyle': 'Page style setting',
3
+  'app.setting.pagestyle.dark': 'Dark style',
4
+  'app.setting.pagestyle.light': 'Light style',
5
+  'app.setting.content-width': 'Content Width',
6
+  'app.setting.content-width.fixed': 'Fixed',
7
+  'app.setting.content-width.fluid': 'Fluid',
8
+  'app.setting.themecolor': 'Theme Color',
9
+  'app.setting.themecolor.dust': 'Dust Red',
10
+  'app.setting.themecolor.volcano': 'Volcano',
11
+  'app.setting.themecolor.sunset': 'Sunset Orange',
12
+  'app.setting.themecolor.cyan': 'Cyan',
13
+  'app.setting.themecolor.green': 'Polar Green',
14
+  'app.setting.themecolor.daybreak': 'Daybreak Blue (default)',
15
+  'app.setting.themecolor.geekblue': 'Geek Glue',
16
+  'app.setting.themecolor.purple': 'Golden Purple',
17
+  'app.setting.navigationmode': 'Navigation Mode',
18
+  'app.setting.sidemenu': 'Side Menu Layout',
19
+  'app.setting.topmenu': 'Top Menu Layout',
20
+  'app.setting.fixedheader': 'Fixed Header',
21
+  'app.setting.fixedsidebar': 'Fixed Sidebar',
22
+  'app.setting.fixedsidebar.hint': 'Works on Side Menu Layout',
23
+  'app.setting.hideheader': 'Hidden Header when scrolling',
24
+  'app.setting.hideheader.hint': 'Works when Hidden Header is enabled',
25
+  'app.setting.othersettings': 'Other Settings',
26
+  'app.setting.weakmode': 'Weak Mode',
27
+  'app.setting.copy': 'Copy Setting',
28
+  'app.setting.copyinfo': 'copy success,please replace defaultSettings in src/models/setting.js',
29
+  'app.setting.production.hint':
30
+    'Setting panel shows in development environment only, please manually modify',
31
+};

+ 60
- 0
src/locales/en-US/settings.js Ver arquivo

@@ -0,0 +1,60 @@
1
+export default {
2
+  'app.settings.menuMap.basic': 'Basic Settings',
3
+  'app.settings.menuMap.security': 'Security Settings',
4
+  'app.settings.menuMap.binding': 'Account Binding',
5
+  'app.settings.menuMap.notification': 'New Message Notification',
6
+  'app.settings.basic.avatar': 'Avatar',
7
+  'app.settings.basic.change-avatar': 'Change avatar',
8
+  'app.settings.basic.email': 'Email',
9
+  'app.settings.basic.email-message': 'Please input your email!',
10
+  'app.settings.basic.nickname': 'Nickname',
11
+  'app.settings.basic.nickname-message': 'Please input your Nickname!',
12
+  'app.settings.basic.profile': 'Personal profile',
13
+  'app.settings.basic.profile-message': 'Please input your personal profile!',
14
+  'app.settings.basic.profile-placeholder': 'Brief introduction to yourself',
15
+  'app.settings.basic.country': 'Country/Region',
16
+  'app.settings.basic.country-message': 'Please input your country!',
17
+  'app.settings.basic.geographic': 'Province or city',
18
+  'app.settings.basic.geographic-message': 'Please input your geographic info!',
19
+  'app.settings.basic.address': 'Street Address',
20
+  'app.settings.basic.address-message': 'Please input your address!',
21
+  'app.settings.basic.phone': 'Phone Number',
22
+  'app.settings.basic.phone-message': 'Please input your phone!',
23
+  'app.settings.basic.update': 'Update Information',
24
+  'app.settings.security.strong': 'Strong',
25
+  'app.settings.security.medium': 'Medium',
26
+  'app.settings.security.weak': 'Weak',
27
+  'app.settings.security.password': 'Account Password',
28
+  'app.settings.security.password-description': 'Current password strength',
29
+  'app.settings.security.phone': 'Security Phone',
30
+  'app.settings.security.phone-description': 'Bound phone',
31
+  'app.settings.security.question': 'Security Question',
32
+  'app.settings.security.question-description':
33
+    'The security question is not set, and the security policy can effectively protect the account security',
34
+  'app.settings.security.email': 'Backup Email',
35
+  'app.settings.security.email-description': 'Bound Email',
36
+  'app.settings.security.mfa': 'MFA Device',
37
+  'app.settings.security.mfa-description':
38
+    'Unbound MFA device, after binding, can be confirmed twice',
39
+  'app.settings.security.modify': 'Modify',
40
+  'app.settings.security.set': 'Set',
41
+  'app.settings.security.bind': 'Bind',
42
+  'app.settings.binding.taobao': 'Binding Taobao',
43
+  'app.settings.binding.taobao-description': 'Currently unbound Taobao account',
44
+  'app.settings.binding.alipay': 'Binding Alipay',
45
+  'app.settings.binding.alipay-description': 'Currently unbound Alipay account',
46
+  'app.settings.binding.dingding': 'Binding DingTalk',
47
+  'app.settings.binding.dingding-description': 'Currently unbound DingTalk account',
48
+  'app.settings.binding.bind': 'Bind',
49
+  'app.settings.notification.password': 'Account Password',
50
+  'app.settings.notification.password-description':
51
+    'Messages from other users will be notified in the form of a station letter',
52
+  'app.settings.notification.messages': 'System Messages',
53
+  'app.settings.notification.messages-description':
54
+    'System messages will be notified in the form of a station letter',
55
+  'app.settings.notification.todo': 'To-do Notification',
56
+  'app.settings.notification.todo-description':
57
+    'The to-do list will be notified in the form of a letter from the station',
58
+  'app.settings.open': 'Open',
59
+  'app.settings.close': 'Close',
60
+};

+ 24
- 0
src/locales/id-ID.js Ver arquivo

@@ -0,0 +1,24 @@
1
+import component from './id-ID/component';
2
+import globalHeader from './id-ID/globalHeader';
3
+import menu from './id-ID/menu';
4
+import pwa from './id-ID/pwa';
5
+import settingDrawer from './id-ID/settingDrawer';
6
+import settings from './id-ID/settings';
7
+import pages from './id-ID/pages';
8
+export default {
9
+  'navbar.lang': 'Bahasa',
10
+  'layout.user.link.help': 'Bantuan',
11
+  'layout.user.link.privacy': 'Privasi',
12
+  'layout.user.link.terms': 'Ketentuan',
13
+  'app.preview.down.block': 'Unduh halaman ini dalam projek lokal anda',
14
+  'app.welcome.link.fetch-blocks': 'Dapatkan semua blok',
15
+  'app.welcome.link.block-list':
16
+    'Buat standar dengan cepat, halaman-halaman berdasarkan pengembangan `block`',
17
+  ...globalHeader,
18
+  ...menu,
19
+  ...settingDrawer,
20
+  ...settings,
21
+  ...pwa,
22
+  ...component,
23
+  ...pages,
24
+};

+ 5
- 0
src/locales/id-ID/component.js Ver arquivo

@@ -0,0 +1,5 @@
1
+export default {
2
+  'component.tagSelect.expand': 'Perluas',
3
+  'component.tagSelect.collapse': 'Lipat',
4
+  'component.tagSelect.all': 'Semua',
5
+};

+ 17
- 0
src/locales/id-ID/globalHeader.js Ver arquivo

@@ -0,0 +1,17 @@
1
+export default {
2
+  'component.globalHeader.search': 'Pencarian',
3
+  'component.globalHeader.search.example1': 'Contoh 1 Pencarian',
4
+  'component.globalHeader.search.example2': 'Contoh 2 Pencarian',
5
+  'component.globalHeader.search.example3': 'Contoh 3 Pencarian',
6
+  'component.globalHeader.help': 'Bantuan',
7
+  'component.globalHeader.notification': 'Notifikasi',
8
+  'component.globalHeader.notification.empty': 'Anda telah membaca semua notifikasi',
9
+  'component.globalHeader.message': 'Pesan',
10
+  'component.globalHeader.message.empty': 'Anda telah membaca semua pesan.',
11
+  'component.globalHeader.event': 'Acara',
12
+  'component.globalHeader.event.empty': 'Anda telah melihat semua acara.',
13
+  'component.noticeIcon.clear': 'Kosongkan',
14
+  'component.noticeIcon.cleared': 'Berhasil dikosongkan',
15
+  'component.noticeIcon.empty': 'Tidak ada pemberitahuan',
16
+  'component.noticeIcon.view-more': 'Melihat lebih',
17
+};

+ 52
- 0
src/locales/id-ID/menu.js Ver arquivo

@@ -0,0 +1,52 @@
1
+export default {
2
+  'menu.welcome': 'Selamat Datang',
3
+  'menu.more-blocks': 'Blocks Lainnya',
4
+  'menu.home': 'Halaman Awal',
5
+  'menu.admin': 'Admin',
6
+  'menu.admin.sub-page': 'Sub-Halaman',
7
+  'menu.login': 'Masuk',
8
+  'menu.register': 'Pendaftaran',
9
+  'menu.register.result': 'Hasil Pendaftaran',
10
+  'menu.dashboard': 'Dasbor',
11
+  'menu.dashboard.analysis': 'Analisis',
12
+  'menu.dashboard.monitor': 'Monitor',
13
+  'menu.dashboard.workplace': 'Workplace',
14
+  'menu.exception.403': '403',
15
+  'menu.exception.404': '404',
16
+  'menu.exception.500': '500',
17
+  'menu.form': 'Form',
18
+  'menu.form.basic-form': 'Form Dasar',
19
+  'menu.form.step-form': 'Form Bertahap',
20
+  'menu.form.step-form.info': 'Form Bertahap(menulis informasi yang dibagikan)',
21
+  'menu.form.step-form.confirm': 'Form Bertahap(konfirmasi informasi yang dibagikan)',
22
+  'menu.form.step-form.result': 'Form Bertahap(selesai)',
23
+  'menu.form.advanced-form': 'Form Lanjutan',
24
+  'menu.list': 'Daftar',
25
+  'menu.list.table-list': 'Tabel Pencarian',
26
+  'menu.list.basic-list': 'Daftar Dasar',
27
+  'menu.list.card-list': 'Daftar Kartu',
28
+  'menu.list.search-list': 'Daftar Pencarian',
29
+  'menu.list.search-list.articles': 'Daftar Pencarian(artikel)',
30
+  'menu.list.search-list.projects': 'Daftar Pencarian(projek)',
31
+  'menu.list.search-list.applications': 'Daftar Pencarian(aplikasi)',
32
+  'menu.profile': 'Profil',
33
+  'menu.profile.basic': 'Profil Dasar',
34
+  'menu.profile.advanced': 'Profile Lanjutan',
35
+  'menu.result': 'Hasil',
36
+  'menu.result.success': 'Sukses',
37
+  'menu.result.fail': 'Gagal',
38
+  'menu.exception': 'Pengecualian',
39
+  'menu.exception.not-permission': '403',
40
+  'menu.exception.not-find': '404',
41
+  'menu.exception.server-error': '500',
42
+  'menu.exception.trigger': 'Jalankan',
43
+  'menu.account': 'Akun',
44
+  'menu.account.center': 'Detail Akun',
45
+  'menu.account.settings': 'Pengaturan Akun',
46
+  'menu.account.trigger': 'Mengaktivasi Error',
47
+  'menu.account.logout': 'Keluar',
48
+  'menu.editor': 'Penyusun Grafis',
49
+  'menu.editor.flow': 'Penyusun Alur',
50
+  'menu.editor.mind': 'Penyusun Mind',
51
+  'menu.editor.koni': 'Penyusun Koni',
52
+};

+ 70
- 0
src/locales/id-ID/pages.js Ver arquivo

@@ -0,0 +1,70 @@
1
+export default {
2
+  'pages.layouts.userLayout.title':
3
+    'Ant Design adalah spesifikasi desain Web yang paling berpengaruh di Kabupaten Xihu',
4
+  'pages.login.accountLogin.tab': 'Login dengan akun',
5
+  'pages.login.accountLogin.errorMessage': 'Nama pengguna dan kata sandi salah(admin/ant.design)',
6
+  'pages.login.username.placeholder': 'nama pengguna: admin atau user',
7
+  'pages.login.username.required': 'Nama pengguna harus diisi!',
8
+  'pages.login.password.placeholder': 'kata sandi: ant.design',
9
+  'pages.login.password.required': 'Kata sandi harus diisi!',
10
+  'pages.login.phoneLogin.tab': 'Login dengan ponsel',
11
+  'pages.login.phoneLogin.errorMessage': 'Kesalahan kode verifikasi',
12
+  'pages.login.phoneNumber.placeholder': 'masukkan nomor telepon',
13
+  'pages.login.phoneNumber.required': 'Nomor ponsel harus diisi!',
14
+  'pages.login.phoneNumber.invalid': 'Nomor ponsel tidak valid!',
15
+  'pages.login.captcha.placeholder': 'kode verifikasi',
16
+  'pages.login.captcha.required': 'Kode verifikasi diperlukan!',
17
+  'pages.login.phoneLogin.getVerificationCode': 'Dapatkan kode',
18
+  'pages.getCaptchaSecondText': 'detik tersisa',
19
+  'pages.login.rememberMe': 'Ingat saya',
20
+  'pages.login.forgotPassword': 'Lupa Kata Sandi?',
21
+  'pages.login.submit': 'Masuk',
22
+  'pages.login.loginWith': 'Masuk dengan :',
23
+  'pages.login.registerAccount': 'Daftar Akun',
24
+  'pages.welcome.advancedComponent': 'Formulir Lanjutan',
25
+  'pages.welcome.link': 'Selamat datang',
26
+  'pages.welcome.advancedLayout': 'Tata letak Lanjutan',
27
+  'pages.welcome.alertMessage':
28
+    'Komponen heavy-duty yang lebih cepat dan lebih kuat telah dirilis.',
29
+  'pages.admin.subPage.title': 'Halaman ini hanya dapat dilihat oleh admin',
30
+  'pages.admin.subPage.alertMessage':
31
+    'umi ui telah dirilis, silahkan gunakan npm run ui untuk memulai pengalaman.',
32
+  'pages.searchTable.createForm.newRule': 'Aturan baru',
33
+  'pages.searchTable.updateForm.ruleConfig': 'Konfigurasi aturan',
34
+  'pages.searchTable.updateForm.basicConfig': 'Informasi dasar',
35
+  'pages.searchTable.updateForm.ruleName.nameLabel': 'Nama aturan',
36
+  'pages.searchTable.updateForm.ruleName.nameRules': 'Harap masukkan nama aturan!',
37
+  'pages.searchTable.updateForm.ruleDesc.descLabel': 'Deskripsi aturan',
38
+  'pages.searchTable.updateForm.ruleDesc.descPlaceholder':
39
+    'Harap masukkan setidaknya lima karakter',
40
+  'pages.searchTable.updateForm.ruleDesc.descRules':
41
+    'Harap masukkan deskripsi aturan setidaknya lima karakter!',
42
+  'pages.searchTable.updateForm.ruleProps.title': 'Properti aturan',
43
+  'pages.searchTable.updateForm.object': 'Objek pemantauan',
44
+  'pages.searchTable.updateForm.ruleProps.templateLabel': 'Template aturan',
45
+  'pages.searchTable.updateForm.ruleProps.typeLabel': 'Jenis aturan',
46
+  'pages.searchTable.updateForm.schedulingPeriod.title': 'Periode penjadwalan',
47
+  'pages.searchTable.updateForm.schedulingPeriod.timeLabel': 'Waktu mulai',
48
+  'pages.searchTable.updateForm.schedulingPeriod.timeRules': 'Pilih waktu mulai!',
49
+  'pages.searchTable.titleDesc': 'deskripsi',
50
+  'pages.searchTable.ruleName': 'Nama aturan wajib diisi',
51
+  'pages.searchTable.titleCallNo': 'Jumlah panggilan',
52
+  'pages.searchTable.titleStatus': 'Status',
53
+  'pages.searchTable.nameStatus.default': 'default',
54
+  'pages.searchTable.nameStatus.running': 'menyala',
55
+  'pages.searchTable.nameStatus.online': 'online',
56
+  'pages.searchTable.nameStatus.abnormal': 'abnormal',
57
+  'pages.searchTable.titleUpdatedAt': 'Waktu terjadwal',
58
+  'pages.searchTable.exception': 'Harap masukkan alasan pengecualian!',
59
+  'pages.searchTable.titleOption': 'Pengoperasian',
60
+  'pages.searchTable.config': 'Konfigurasi',
61
+  'pages.searchTable.subscribeAlert': 'Berlangganan notifikasi',
62
+  'pages.searchTable.title': 'Formulir pertanyaan',
63
+  'pages.searchTable.new': 'Baru',
64
+  'pages.searchTable.chosen': 'Terpilih',
65
+  'pages.searchTable.item': 'item',
66
+  'pages.searchTable.totalServiceCalls': 'Jumlah total panggilan layanan',
67
+  'pages.searchTable.tenThousand': '0000',
68
+  'pages.searchTable.batchDeletion': 'Penghapusan batch',
69
+  'pages.searchTable.batchApproval': 'Persetujuan batch',
70
+};

+ 7
- 0
src/locales/id-ID/pwa.js Ver arquivo

@@ -0,0 +1,7 @@
1
+export default {
2
+  'app.pwa.offline': 'Koneksi anda terputus',
3
+  'app.pwa.serviceworker.updated': 'Konten baru sudah tersedia',
4
+  'app.pwa.serviceworker.updated.hint':
5
+    'Silahkan klik tombol "Refresh" untuk memuat ulang halaman ini',
6
+  'app.pwa.serviceworker.updated.ok': 'Memuat ulang',
7
+};

+ 32
- 0
src/locales/id-ID/settingDrawer.js Ver arquivo

@@ -0,0 +1,32 @@
1
+export default {
2
+  'app.setting.pagestyle': 'Pengaturan style Halaman',
3
+  'app.setting.pagestyle.dark': 'Style Gelap',
4
+  'app.setting.pagestyle.light': 'Style Cerah',
5
+  'app.setting.content-width': 'Lebar Konten',
6
+  'app.setting.content-width.fixed': 'Tetap',
7
+  'app.setting.content-width.fluid': 'Fluid',
8
+  'app.setting.themecolor': 'Theme Color',
9
+  'app.setting.themecolor.dust': 'Dust Red',
10
+  'app.setting.themecolor.volcano': 'Volcano',
11
+  'app.setting.themecolor.sunset': 'Sunset Orange',
12
+  'app.setting.themecolor.cyan': 'Cyan',
13
+  'app.setting.themecolor.green': 'Polar Green',
14
+  'app.setting.themecolor.daybreak': 'Daybreak Blue (bawaan)',
15
+  'app.setting.themecolor.geekblue': 'Geek Glue',
16
+  'app.setting.themecolor.purple': 'Golden Purple',
17
+  'app.setting.navigationmode': 'Mode Navigasi',
18
+  'app.setting.sidemenu': 'Susunan Menu Samping',
19
+  'app.setting.topmenu': 'Susunan Menu Atas',
20
+  'app.setting.fixedheader': 'Header Tetap',
21
+  'app.setting.fixedsidebar': 'Sidebar Tetap',
22
+  'app.setting.fixedsidebar.hint': 'Berjalan pada Susunan Menu Samping',
23
+  'app.setting.hideheader': 'Sembunyikan Header ketika gulir ke bawah',
24
+  'app.setting.hideheader.hint': 'Bekerja ketika Header tersembunyi dimunculkan',
25
+  'app.setting.othersettings': 'Pengaturan Lainnya',
26
+  'app.setting.weakmode': 'Mode Lemah',
27
+  'app.setting.copy': 'Salin Pengaturan',
28
+  'app.setting.copyinfo':
29
+    'Berhasil disalin,tolong ubah defaultSettings pada src/models/setting.js',
30
+  'app.setting.production.hint':
31
+    'Panel pengaturan hanya muncul pada lingkungan pengembangan, silahkan modifikasi secara menual',
32
+};

+ 60
- 0
src/locales/id-ID/settings.js Ver arquivo

@@ -0,0 +1,60 @@
1
+export default {
2
+  'app.settings.menuMap.basic': 'Pengaturan Dasar',
3
+  'app.settings.menuMap.security': 'Pengaturan Keamanan',
4
+  'app.settings.menuMap.binding': 'Pengikatan Akun',
5
+  'app.settings.menuMap.notification': 'Notifikasi Pesan Baru',
6
+  'app.settings.basic.avatar': 'Avatar',
7
+  'app.settings.basic.change-avatar': 'Ubah avatar',
8
+  'app.settings.basic.email': 'Email',
9
+  'app.settings.basic.email-message': 'Tolong masukkan email!',
10
+  'app.settings.basic.nickname': 'Nickname',
11
+  'app.settings.basic.nickname-message': 'Tolong masukkan Nickname!',
12
+  'app.settings.basic.profile': 'Profil Personal',
13
+  'app.settings.basic.profile-message': 'Tolong masukkan profil personal!',
14
+  'app.settings.basic.profile-placeholder': 'Perkenalan Singkat tentang Diri Anda',
15
+  'app.settings.basic.country': 'Negara/Wilayah',
16
+  'app.settings.basic.country-message': 'Tolong masukkan negara anda!',
17
+  'app.settings.basic.geographic': 'Provinsi atau kota',
18
+  'app.settings.basic.geographic-message': 'Tolong masukkan info geografis anda!',
19
+  'app.settings.basic.address': 'Alamat Jalan',
20
+  'app.settings.basic.address-message': 'Tolong masukkan Alamat Jalan anda!',
21
+  'app.settings.basic.phone': 'Nomor Ponsel',
22
+  'app.settings.basic.phone-message': 'Tolong masukkan Nomor Ponsel anda!',
23
+  'app.settings.basic.update': 'Perbarui Informasi',
24
+  'app.settings.security.strong': 'Kuat',
25
+  'app.settings.security.medium': 'Sedang',
26
+  'app.settings.security.weak': 'Lemah',
27
+  'app.settings.security.password': 'Kata Sandi Akun',
28
+  'app.settings.security.password-description': 'Kekuatan Kata Sandi saat ini',
29
+  'app.settings.security.phone': 'Keamanan Ponsel',
30
+  'app.settings.security.phone-description': 'Mengikat Ponsel',
31
+  'app.settings.security.question': 'Pertanyaan Keamanan',
32
+  'app.settings.security.question-description':
33
+    'Pertanyaan Keamanan belum diatur, dan kebijakan keamanan dapat melindungi akun secara efektif',
34
+  'app.settings.security.email': 'Email Cadangan',
35
+  'app.settings.security.email-description': 'Mengikat Email',
36
+  'app.settings.security.mfa': 'Perangka MFA',
37
+  'app.settings.security.mfa-description':
38
+    'Tidak mengikat Perangkat MFA, setelah diikat, dapat dikonfirmasi dua kali',
39
+  'app.settings.security.modify': 'Modifikasi',
40
+  'app.settings.security.set': 'Setel',
41
+  'app.settings.security.bind': 'Ikat',
42
+  'app.settings.binding.taobao': 'Mengikat Taobao',
43
+  'app.settings.binding.taobao-description': 'Tidak mengikat akun Taobao saat ini',
44
+  'app.settings.binding.alipay': 'Mengikat Alipay',
45
+  'app.settings.binding.alipay-description': 'Tidak mengikat akun Alipay saat ini',
46
+  'app.settings.binding.dingding': 'Mengikat DingTalk',
47
+  'app.settings.binding.dingding-description': 'Tidak mengikat akun DingTalk',
48
+  'app.settings.binding.bind': 'Ikat',
49
+  'app.settings.notification.password': 'Kata Sandi Akun',
50
+  'app.settings.notification.password-description':
51
+    'Pesan dari pengguna lain akan diberitahu dalam bentuk surat',
52
+  'app.settings.notification.messages': 'Pesan Sistem',
53
+  'app.settings.notification.messages-description':
54
+    'Pesan sistem akan diberitahu dalam bentuk surat',
55
+  'app.settings.notification.todo': 'Notifikasi daftar To-do',
56
+  'app.settings.notification.todo-description':
57
+    'Daftar to-do akan diberitahukan dalam bentuk surat dari stasiun',
58
+  'app.settings.open': 'Buka',
59
+  'app.settings.close': 'Tutup',
60
+};

+ 23
- 0
src/locales/ja-JP.js Ver arquivo

@@ -0,0 +1,23 @@
1
+import globalHeader from './ja-JP/globalHeader';
2
+import menu from './ja-JP/menu';
3
+import settingDrawer from './ja-JP/settingDrawer';
4
+import settings from './ja-JP/settings';
5
+import pwa from './ja-JP/pwa';
6
+import component from './ja-JP/component';
7
+import pages from './ja-JP/pages';
8
+export default {
9
+  'navBar.lang': '言語',
10
+  'layout.user.link.help': 'ヘルプ',
11
+  'layout.user.link.privacy': 'プライバシー',
12
+  'layout.user.link.terms': '利用規約',
13
+  'app.preview.down.block': 'このページをローカルプロジェクトにダウンロードしてください',
14
+  'app.welcome.link.fetch-blocks': '',
15
+  'app.welcome.link.block-list': '',
16
+  ...globalHeader,
17
+  ...menu,
18
+  ...settingDrawer,
19
+  ...settings,
20
+  ...pwa,
21
+  ...component,
22
+  ...pages,
23
+};

+ 5
- 0
src/locales/ja-JP/component.js Ver arquivo

@@ -0,0 +1,5 @@
1
+export default {
2
+  'component.tagSelect.expand': '展開',
3
+  'component.tagSelect.collapse': '折りたたむ',
4
+  'component.tagSelect.all': 'すべて',
5
+};

+ 17
- 0
src/locales/ja-JP/globalHeader.js Ver arquivo

@@ -0,0 +1,17 @@
1
+export default {
2
+  'component.globalHeader.search': '検索',
3
+  'component.globalHeader.search.example1': '検索例1',
4
+  'component.globalHeader.search.example2': '検索例2',
5
+  'component.globalHeader.search.example3': '検索例3',
6
+  'component.globalHeader.help': 'ヘルプ',
7
+  'component.globalHeader.notification': '通知',
8
+  'component.globalHeader.notification.empty': 'すべての通知を表示しました。',
9
+  'component.globalHeader.message': 'メッセージ',
10
+  'component.globalHeader.message.empty': 'すべてのメッセージを表示しました。',
11
+  'component.globalHeader.event': 'イベント',
12
+  'component.globalHeader.event.empty': 'すべてのイベントを表示しました。',
13
+  'component.noticeIcon.clear': 'クリア',
14
+  'component.noticeIcon.cleared': 'クリア済み',
15
+  'component.noticeIcon.empty': '通知なし',
16
+  'component.noticeIcon.view-more': 'もっと見る',
17
+};

+ 52
- 0
src/locales/ja-JP/menu.js Ver arquivo

@@ -0,0 +1,52 @@
1
+export default {
2
+  'menu.welcome': 'ようこそ',
3
+  'menu.more-blocks': 'その他のブロック',
4
+  'menu.home': 'ホーム',
5
+  'menu.admin': '管理者',
6
+  'menu.admin.sub-page': 'サブページ',
7
+  'menu.login': 'ログイン',
8
+  'menu.register': '登録',
9
+  'menu.register.result': '登録結果',
10
+  'menu.dashboard': 'ダッシュボード',
11
+  'menu.dashboard.analysis': '分析',
12
+  'menu.dashboard.monitor': 'モニター',
13
+  'menu.dashboard.workplace': '職場',
14
+  'menu.exception.403': '403',
15
+  'menu.exception.404': '404',
16
+  'menu.exception.500': '500',
17
+  'menu.form': 'フォーム',
18
+  'menu.form.basic-form': '基本フォーム',
19
+  'menu.form.step-form': 'ステップフォーム',
20
+  'menu.form.step-form.info': 'ステップフォーム(転送情報の書き込み)',
21
+  'menu.form.step-form.confirm': 'ステップフォーム(転送情報の確認)',
22
+  'menu.form.step-form.result': 'ステップフォーム(完成)',
23
+  'menu.form.advanced-form': '高度なフォーム',
24
+  'menu.list': 'リスト',
25
+  'menu.list.table-list': '検索テーブル',
26
+  'menu.list.basic-list': '基本リスト',
27
+  'menu.list.card-list': 'カードリスト',
28
+  'menu.list.search-list': '検索リスト',
29
+  'menu.list.search-list.articles': '検索リスト(記事)',
30
+  'menu.list.search-list.projects': '検索リスト(プロジェクト)',
31
+  'menu.list.search-list.applications': '検索リスト(アプリ)',
32
+  'menu.profile': 'プロフィール',
33
+  'menu.profile.basic': '基本プロフィール',
34
+  'menu.profile.advanced': '高度なプロフィール',
35
+  'menu.result': '結果',
36
+  'menu.result.success': '成功',
37
+  'menu.result.fail': '失敗',
38
+  'menu.exception': '例外',
39
+  'menu.exception.not-permission': '403',
40
+  'menu.exception.not-find': '404',
41
+  'menu.exception.server-error': '500',
42
+  'menu.exception.trigger': 'トリガー',
43
+  'menu.account': 'アカウント',
44
+  'menu.account.center': 'アカウントセンター',
45
+  'menu.account.settings': 'アカウント設定',
46
+  'menu.account.trigger': 'トリガーエラー',
47
+  'menu.account.logout': 'ログアウト',
48
+  'menu.editor': 'グラフィックエディタ',
49
+  'menu.editor.flow': 'フローエディタ',
50
+  'menu.editor.mind': 'マインドエディター',
51
+  'menu.editor.koni': 'コニエディター',
52
+};

+ 67
- 0
src/locales/ja-JP/pages.js Ver arquivo

@@ -0,0 +1,67 @@
1
+export default {
2
+  'pages.layouts.userLayout.title': 'Ant Designは、西湖区で最も影響力のあるWebデザイン仕様です。',
3
+  'pages.login.accountLogin.tab': 'アカウントログイン',
4
+  'pages.login.accountLogin.errorMessage':
5
+    'ユーザー名/パスワードが正しくありません(admin/ant.design)',
6
+  'pages.login.username.placeholder': 'ユーザー名:adminまたはuser',
7
+  'pages.login.username.required': 'ユーザー名を入力してください!',
8
+  'pages.login.password.placeholder': 'パスワード:ant.design',
9
+  'pages.login.password.required': 'パスワードを入力してください!',
10
+  'pages.login.phoneLogin.tab': '電話ログイン',
11
+  'pages.login.phoneLogin.errorMessage': '検証コードエラー',
12
+  'pages.login.phoneNumber.placeholder': '電話番号',
13
+  'pages.login.phoneNumber.required': '電話番号を入力してください!',
14
+  'pages.login.phoneNumber.invalid': '電話番号が無効です!',
15
+  'pages.login.captcha.placeholder': '確認コード',
16
+  'pages.login.captcha.required': '確認コードを入力してください!',
17
+  'pages.login.phoneLogin.getVerificationCode': '確認コードを取得',
18
+  'pages.getCaptchaSecondText': '秒',
19
+  'pages.login.rememberMe': 'Remember me',
20
+  'pages.login.forgotPassword': 'パスワードをお忘れですか?',
21
+  'pages.login.submit': 'ログイン',
22
+  'pages.login.loginWith': 'その他のログイン方法:',
23
+  'pages.login.registerAccount': 'アカウント登録',
24
+  'pages.welcome.advancedComponent': '高度なコンポーネント',
25
+  'pages.welcome.link': 'ようこそ',
26
+  'pages.welcome.advancedLayout': '高度なレイアウト',
27
+  'pages.welcome.alertMessage': 'より高速で強力な頑丈なコンポーネントがリリースされました。',
28
+  'pages.admin.subPage.title': 'このページは管理者のみが表示できます',
29
+  'pages.admin.subPage.alertMessage':
30
+    'Umi uiがリリースされました。npm run uiを使用して体験してください。',
31
+  'pages.searchTable.createForm.newRule': '新しいルール',
32
+  'pages.searchTable.updateForm.ruleConfig': 'ルール構成',
33
+  'pages.searchTable.updateForm.basicConfig': '基本情報',
34
+  'pages.searchTable.updateForm.ruleName.nameLabel': 'ルール名',
35
+  'pages.searchTable.updateForm.ruleName.nameRules': 'ルール名を入力してください!',
36
+  'pages.searchTable.updateForm.ruleDesc.descLabel': 'ルールの説明',
37
+  'pages.searchTable.updateForm.ruleDesc.descPlaceholder': '5文字以上入力してください',
38
+  'pages.searchTable.updateForm.ruleDesc.descRules': '5文字以上のルールの説明を入力してください!',
39
+  'pages.searchTable.updateForm.ruleProps.title': 'プロパティの構成',
40
+  'pages.searchTable.updateForm.object': '監視対象',
41
+  'pages.searchTable.updateForm.ruleProps.templateLabel': 'ルールテンプレート',
42
+  'pages.searchTable.updateForm.ruleProps.typeLabel': 'ルールタイプ',
43
+  'pages.searchTable.updateForm.schedulingPeriod.title': 'スケジュール期間の設定',
44
+  'pages.searchTable.updateForm.schedulingPeriod.timeLabel': '開始時間',
45
+  'pages.searchTable.updateForm.schedulingPeriod.timeRules': '開始時間を選択してください!',
46
+  'pages.searchTable.titleDesc': '説明',
47
+  'pages.searchTable.ruleName': 'ルール名が必要です',
48
+  'pages.searchTable.titleCallNo': 'サービスコール数',
49
+  'pages.searchTable.titleStatus': 'ステータス',
50
+  'pages.searchTable.nameStatus.default': 'デフォルト',
51
+  'pages.searchTable.nameStatus.running': '起動中',
52
+  'pages.searchTable.nameStatus.online': 'オンライン',
53
+  'pages.searchTable.nameStatus.abnormal': '異常',
54
+  'pages.searchTable.titleUpdatedAt': '最終スケジュール',
55
+  'pages.searchTable.exception': '例外の理由を入力してください!',
56
+  'pages.searchTable.titleOption': 'オプション',
57
+  'pages.searchTable.config': '構成',
58
+  'pages.searchTable.subscribeAlert': 'アラートを購読する',
59
+  'pages.searchTable.title': 'お問い合わせフォーム',
60
+  'pages.searchTable.new': '新しい',
61
+  'pages.searchTable.chosen': '選んだ項目',
62
+  'pages.searchTable.item': '項目',
63
+  'pages.searchTable.totalServiceCalls': 'サービスコールの総数',
64
+  'pages.searchTable.tenThousand': '万',
65
+  'pages.searchTable.batchDeletion': 'バッチ削除',
66
+  'pages.searchTable.batchApproval': 'バッチ承認',
67
+};

+ 7
- 0
src/locales/ja-JP/pwa.js Ver arquivo

@@ -0,0 +1,7 @@
1
+export default {
2
+  'app.pwa.offline': 'あなたは今オフラインです',
3
+  'app.pwa.serviceworker.updated': '新しいコンテンツが利用可能です',
4
+  'app.pwa.serviceworker.updated.hint':
5
+    '現在のページをリロードするには、「更新」ボタンを押してください',
6
+  'app.pwa.serviceworker.updated.ok': 'リフレッシュ',
7
+};

+ 31
- 0
src/locales/ja-JP/settingDrawer.js Ver arquivo

@@ -0,0 +1,31 @@
1
+export default {
2
+  'app.setting.pagestyle': 'ページスタイル設定',
3
+  'app.setting.pagestyle.dark': 'ダークスタイル',
4
+  'app.setting.pagestyle.light': 'ライトスタイル',
5
+  'app.setting.content-width': 'コンテンツの幅',
6
+  'app.setting.content-width.fixed': '固定',
7
+  'app.setting.content-width.fluid': '流体',
8
+  'app.setting.themecolor': 'テーマカラー',
9
+  'app.setting.themecolor.dust': 'ダストレッド',
10
+  'app.setting.themecolor.volcano': 'ボルケ-ノ',
11
+  'app.setting.themecolor.sunset': 'サンセットオレンジ',
12
+  'app.setting.themecolor.cyan': 'シアン',
13
+  'app.setting.themecolor.green': 'ポーラーグリーン',
14
+  'app.setting.themecolor.daybreak': '夜明けの青(デフォルト)',
15
+  'app.setting.themecolor.geekblue': 'ギーク ブルー',
16
+  'app.setting.themecolor.purple': 'ゴールデンパープル',
17
+  'app.setting.navigationmode': 'ナビゲーションモード',
18
+  'app.setting.sidemenu': 'サイドメニューのレイアウト',
19
+  'app.setting.topmenu': 'トップメニューのレイアウト',
20
+  'app.setting.fixedheader': '固定ヘッダー',
21
+  'app.setting.fixedsidebar': '固定サイドバー',
22
+  'app.setting.fixedsidebar.hint': 'サイドメニューのレイアウトで動作します',
23
+  'app.setting.hideheader': 'スクロール時の非表示ヘッダー',
24
+  'app.setting.hideheader.hint': '非表示ヘッダーが有効になっている場合に機能します',
25
+  'app.setting.othersettings': 'その他の設定',
26
+  'app.setting.weakmode': 'ウィークモード',
27
+  'app.setting.copy': 'コピー設定',
28
+  'app.setting.copyinfo':
29
+    'コピーが成功しました。src/models/setting.jsのdefaultSettingsを置き換えてください',
30
+  'app.setting.production.hint': '設定パネルは開発環境でのみ表示されます。手動で変更してください',
31
+};

+ 59
- 0
src/locales/ja-JP/settings.js Ver arquivo

@@ -0,0 +1,59 @@
1
+export default {
2
+  'app.settings.menuMap.basic': '基本設定',
3
+  'app.settings.menuMap.security': 'セキュリティ設定',
4
+  'app.settings.menuMap.binding': 'アカウントのバインド',
5
+  'app.settings.menuMap.notification': '新しいメッセージの通知',
6
+  'app.settings.basic.avatar': 'アバター',
7
+  'app.settings.basic.change-avatar': 'アバターを変更する',
8
+  'app.settings.basic.email': 'メール',
9
+  'app.settings.basic.email-message': 'メールアドレスを入力してください!',
10
+  'app.settings.basic.nickname': 'ニックネーム',
11
+  'app.settings.basic.nickname-message': 'ニックネームを入力してください!',
12
+  'app.settings.basic.profile': '個人プロフィール',
13
+  'app.settings.basic.profile-message': '個人プロフィールを入力してください!',
14
+  'app.settings.basic.profile-placeholder': '自己紹介',
15
+  'app.settings.basic.country': '国/地域',
16
+  'app.settings.basic.country-message': 'あなたの国を入力してください!',
17
+  'app.settings.basic.geographic': '州または市',
18
+  'app.settings.basic.geographic-message': '地理情報を入力してください!',
19
+  'app.settings.basic.address': '住所',
20
+  'app.settings.basic.address-message': '住所を入力してください!',
21
+  'app.settings.basic.phone': '電話番号',
22
+  'app.settings.basic.phone-message': '電話番号を入力してください!',
23
+  'app.settings.basic.update': '更新情報',
24
+  'app.settings.security.strong': '強い',
25
+  'app.settings.security.medium': 'ミディアム',
26
+  'app.settings.security.weak': '弱い',
27
+  'app.settings.security.password': 'アカウントパスワード',
28
+  'app.settings.security.password-description': '現在のパスワードの強度',
29
+  'app.settings.security.phone': 'セキュリティ電話番号',
30
+  'app.settings.security.phone-description': 'バインドされた電話番号',
31
+  'app.settings.security.question': '秘密の質問',
32
+  'app.settings.security.question-description':
33
+    'セキュリティの質問が設定されてません。セキュリティポリシーはアカウントのセキュリティを効果的に保護できます',
34
+  'app.settings.security.email': 'バックアップメール',
35
+  'app.settings.security.email-description': 'バインドされたメール',
36
+  'app.settings.security.mfa': '多要素認証デバイス',
37
+  'app.settings.security.mfa-description':
38
+    'バインドされていない多要素認証デバイスは、バインド後、2回確認できます',
39
+  'app.settings.security.modify': '変更する',
40
+  'app.settings.security.set': 'セットする',
41
+  'app.settings.security.bind': 'バインド',
42
+  'app.settings.binding.taobao': 'タオバオをバインドする',
43
+  'app.settings.binding.taobao-description': '現在バインドされていないタオバオアカウント',
44
+  'app.settings.binding.alipay': 'アリペイをバインドする',
45
+  'app.settings.binding.alipay-description': '現在バインドされていないアリペイアカウント',
46
+  'app.settings.binding.dingding': 'ディントークをバインドする',
47
+  'app.settings.binding.dingding-description': '現在バインドされていないディントークアカウント',
48
+  'app.settings.binding.bind': 'バインド',
49
+  'app.settings.notification.password': 'アカウントパスワード',
50
+  'app.settings.notification.password-description':
51
+    '他のユーザーからのメッセージは、ステーションレターの形式で通知されます',
52
+  'app.settings.notification.messages': 'システムメッセージ',
53
+  'app.settings.notification.messages-description':
54
+    'システムメッセージは、ステーションレターの形式で通知されます',
55
+  'app.settings.notification.todo': 'To Do(用事) 通知',
56
+  'app.settings.notification.todo-description': 'To Doタスクは、内部レターの形式で通知されます',
57
+  'app.settings.open': '開く',
58
+  'app.settings.close': '閉じる',
59
+};

+ 19
- 0
src/locales/pt-BR.js Ver arquivo

@@ -0,0 +1,19 @@
1
+import component from './pt-BR/component';
2
+import globalHeader from './pt-BR/globalHeader';
3
+import menu from './pt-BR/menu';
4
+import pwa from './pt-BR/pwa';
5
+import settingDrawer from './pt-BR/settingDrawer';
6
+import settings from './pt-BR/settings';
7
+export default {
8
+  'navBar.lang': 'Idiomas',
9
+  'layout.user.link.help': 'ajuda',
10
+  'layout.user.link.privacy': 'política de privacidade',
11
+  'layout.user.link.terms': 'termos de serviços',
12
+  'app.preview.down.block': 'Download this page to your local project',
13
+  ...globalHeader,
14
+  ...menu,
15
+  ...settingDrawer,
16
+  ...settings,
17
+  ...pwa,
18
+  ...component,
19
+};

+ 5
- 0
src/locales/pt-BR/component.js Ver arquivo

@@ -0,0 +1,5 @@
1
+export default {
2
+  'component.tagSelect.expand': 'Expandir',
3
+  'component.tagSelect.collapse': 'Diminuir',
4
+  'component.tagSelect.all': 'Todas',
5
+};

+ 18
- 0
src/locales/pt-BR/globalHeader.js Ver arquivo

@@ -0,0 +1,18 @@
1
+export default {
2
+  'component.globalHeader.search': 'Busca',
3
+  'component.globalHeader.search.example1': 'Exemplo de busca 1',
4
+  'component.globalHeader.search.example2': 'Exemplo de busca 2',
5
+  'component.globalHeader.search.example3': 'Exemplo de busca 3',
6
+  'component.globalHeader.help': 'Ajuda',
7
+  'component.globalHeader.notification': 'Notificação',
8
+  'component.globalHeader.notification.empty': 'Você visualizou todas as notificações.',
9
+  'component.globalHeader.message': 'Mensagem',
10
+  'component.globalHeader.message.empty': 'Você visualizou todas as mensagens.',
11
+  'component.globalHeader.event': 'Evento',
12
+  'component.globalHeader.event.empty': 'Você visualizou todos os eventos.',
13
+  'component.noticeIcon.clear': 'Limpar',
14
+  'component.noticeIcon.cleared': 'Limpo',
15
+  'component.noticeIcon.empty': 'Sem notificações',
16
+  'component.noticeIcon.loaded': 'Carregado',
17
+  'component.noticeIcon.view-more': 'Veja mais',
18
+};

+ 52
- 0
src/locales/pt-BR/menu.js Ver arquivo

@@ -0,0 +1,52 @@
1
+export default {
2
+  'menu.welcome': 'Welcome',
3
+  'menu.more-blocks': 'More Blocks',
4
+  'menu.home': 'Início',
5
+  'menu.login': 'Login',
6
+  'menu.admin': 'Admin',
7
+  'menu.admin.sub-page': 'Sub-Page',
8
+  'menu.register': 'Registro',
9
+  'menu.register.result': 'Resultado de registro',
10
+  'menu.dashboard': 'Dashboard',
11
+  'menu.dashboard.analysis': 'Análise',
12
+  'menu.dashboard.monitor': 'Monitor',
13
+  'menu.dashboard.workplace': 'Ambiente de Trabalho',
14
+  'menu.exception.403': '403',
15
+  'menu.exception.404': '404',
16
+  'menu.exception.500': '500',
17
+  'menu.form': 'Formulário',
18
+  'menu.form.basic-form': 'Formulário Básico',
19
+  'menu.form.step-form': 'Formulário Assistido',
20
+  'menu.form.step-form.info': 'Formulário Assistido(gravar informações de transferência)',
21
+  'menu.form.step-form.confirm': 'Formulário Assistido(confirmar informações de transferência)',
22
+  'menu.form.step-form.result': 'Formulário Assistido(finalizado)',
23
+  'menu.form.advanced-form': 'Formulário Avançado',
24
+  'menu.list': 'Lista',
25
+  'menu.list.table-list': 'Tabela de Busca',
26
+  'menu.list.basic-list': 'Lista Básica',
27
+  'menu.list.card-list': 'Lista de Card',
28
+  'menu.list.search-list': 'Lista de Busca',
29
+  'menu.list.search-list.articles': 'Lista de Busca(artigos)',
30
+  'menu.list.search-list.projects': 'Lista de Busca(projetos)',
31
+  'menu.list.search-list.applications': 'Lista de Busca(aplicações)',
32
+  'menu.profile': 'Perfil',
33
+  'menu.profile.basic': 'Perfil Básico',
34
+  'menu.profile.advanced': 'Perfil Avançado',
35
+  'menu.result': 'Resultado',
36
+  'menu.result.success': 'Sucesso',
37
+  'menu.result.fail': 'Falha',
38
+  'menu.exception': 'Exceção',
39
+  'menu.exception.not-permission': '403',
40
+  'menu.exception.not-find': '404',
41
+  'menu.exception.server-error': '500',
42
+  'menu.exception.trigger': 'Disparar',
43
+  'menu.account': 'Conta',
44
+  'menu.account.center': 'Central da Conta',
45
+  'menu.account.settings': 'Configurar Conta',
46
+  'menu.account.trigger': 'Disparar Erro',
47
+  'menu.account.logout': 'Sair',
48
+  'menu.editor': 'Graphic Editor',
49
+  'menu.editor.flow': 'Flow Editor',
50
+  'menu.editor.mind': 'Mind Editor',
51
+  'menu.editor.koni': 'Koni Editor',
52
+};

+ 7
- 0
src/locales/pt-BR/pwa.js Ver arquivo

@@ -0,0 +1,7 @@
1
+export default {
2
+  'app.pwa.offline': 'Você está offline agora',
3
+  'app.pwa.serviceworker.updated': 'Novo conteúdo está disponível',
4
+  'app.pwa.serviceworker.updated.hint':
5
+    'Por favor, pressione o botão "Atualizar" para recarregar a página atual',
6
+  'app.pwa.serviceworker.updated.ok': 'Atualizar',
7
+};

+ 32
- 0
src/locales/pt-BR/settingDrawer.js Ver arquivo

@@ -0,0 +1,32 @@
1
+export default {
2
+  'app.setting.pagestyle': 'Configuração de estilo da página',
3
+  'app.setting.pagestyle.dark': 'Dark style',
4
+  'app.setting.pagestyle.light': 'Light style',
5
+  'app.setting.content-width': 'Largura do conteúdo',
6
+  'app.setting.content-width.fixed': 'Fixo',
7
+  'app.setting.content-width.fluid': 'Fluido',
8
+  'app.setting.themecolor': 'Cor do Tema',
9
+  'app.setting.themecolor.dust': 'Dust Red',
10
+  'app.setting.themecolor.volcano': 'Volcano',
11
+  'app.setting.themecolor.sunset': 'Sunset Orange',
12
+  'app.setting.themecolor.cyan': 'Cyan',
13
+  'app.setting.themecolor.green': 'Polar Green',
14
+  'app.setting.themecolor.daybreak': 'Daybreak Blue (default)',
15
+  'app.setting.themecolor.geekblue': 'Geek Glue',
16
+  'app.setting.themecolor.purple': 'Golden Purple',
17
+  'app.setting.navigationmode': 'Modo de Navegação',
18
+  'app.setting.sidemenu': 'Layout do Menu Lateral',
19
+  'app.setting.topmenu': 'Layout do Menu Superior',
20
+  'app.setting.fixedheader': 'Cabeçalho fixo',
21
+  'app.setting.fixedsidebar': 'Barra lateral fixa',
22
+  'app.setting.fixedsidebar.hint': 'Funciona no layout do menu lateral',
23
+  'app.setting.hideheader': 'Esconder o cabeçalho quando rolar',
24
+  'app.setting.hideheader.hint': 'Funciona quando o esconder cabeçalho está abilitado',
25
+  'app.setting.othersettings': 'Outras configurações',
26
+  'app.setting.weakmode': 'Weak Mode',
27
+  'app.setting.copy': 'Copiar Configuração',
28
+  'app.setting.copyinfo':
29
+    'copiado com sucesso,por favor trocar o defaultSettings em src/models/setting.js',
30
+  'app.setting.production.hint':
31
+    'O painel de configuração apenas é exibido no ambiente de desenvolvimento, por favor modifique manualmente o',
32
+};

+ 60
- 0
src/locales/pt-BR/settings.js Ver arquivo

@@ -0,0 +1,60 @@
1
+export default {
2
+  'app.settings.menuMap.basic': 'Configurações Básicas',
3
+  'app.settings.menuMap.security': 'Configurações de Segurança',
4
+  'app.settings.menuMap.binding': 'Vinculação de Conta',
5
+  'app.settings.menuMap.notification': 'Mensagens de Notificação',
6
+  'app.settings.basic.avatar': 'Avatar',
7
+  'app.settings.basic.change-avatar': 'Alterar avatar',
8
+  'app.settings.basic.email': 'Email',
9
+  'app.settings.basic.email-message': 'Por favor insira seu email!',
10
+  'app.settings.basic.nickname': 'Nome de usuário',
11
+  'app.settings.basic.nickname-message': 'Por favor insira seu nome de usuário!',
12
+  'app.settings.basic.profile': 'Perfil pessoal',
13
+  'app.settings.basic.profile-message': 'Por favor insira seu perfil pessoal!',
14
+  'app.settings.basic.profile-placeholder': 'Breve introdução sua',
15
+  'app.settings.basic.country': 'País/Região',
16
+  'app.settings.basic.country-message': 'Por favor insira país!',
17
+  'app.settings.basic.geographic': 'Província, estado ou cidade',
18
+  'app.settings.basic.geographic-message': 'Por favor insira suas informações geográficas!',
19
+  'app.settings.basic.address': 'Endereço',
20
+  'app.settings.basic.address-message': 'Por favor insira seu endereço!',
21
+  'app.settings.basic.phone': 'Número de telefone',
22
+  'app.settings.basic.phone-message': 'Por favor insira seu número de telefone!',
23
+  'app.settings.basic.update': 'Atualizar Informações',
24
+  'app.settings.security.strong': 'Forte',
25
+  'app.settings.security.medium': 'Média',
26
+  'app.settings.security.weak': 'Fraca',
27
+  'app.settings.security.password': 'Senha da Conta',
28
+  'app.settings.security.password-description': 'Força da senha',
29
+  'app.settings.security.phone': 'Telefone de Seguraça',
30
+  'app.settings.security.phone-description': 'Telefone vinculado',
31
+  'app.settings.security.question': 'Pergunta de Segurança',
32
+  'app.settings.security.question-description':
33
+    'A pergunta de segurança não está definida e a política de segurança pode proteger efetivamente a segurança da conta',
34
+  'app.settings.security.email': 'Email de Backup',
35
+  'app.settings.security.email-description': 'Email vinculado',
36
+  'app.settings.security.mfa': 'Dispositivo MFA',
37
+  'app.settings.security.mfa-description':
38
+    'O dispositivo MFA não vinculado, após a vinculação, pode ser confirmado duas vezes',
39
+  'app.settings.security.modify': 'Modificar',
40
+  'app.settings.security.set': 'Atribuir',
41
+  'app.settings.security.bind': 'Vincular',
42
+  'app.settings.binding.taobao': 'Vincular Taobao',
43
+  'app.settings.binding.taobao-description': 'Atualmente não vinculado à conta Taobao',
44
+  'app.settings.binding.alipay': 'Vincular Alipay',
45
+  'app.settings.binding.alipay-description': 'Atualmente não vinculado à conta Alipay',
46
+  'app.settings.binding.dingding': 'Vincular DingTalk',
47
+  'app.settings.binding.dingding-description': 'Atualmente não vinculado à conta DingTalk',
48
+  'app.settings.binding.bind': 'Vincular',
49
+  'app.settings.notification.password': 'Senha da Conta',
50
+  'app.settings.notification.password-description':
51
+    'Mensagens de outros usuários serão notificadas na forma de uma estação de letra',
52
+  'app.settings.notification.messages': 'Mensagens de Sistema',
53
+  'app.settings.notification.messages-description':
54
+    'Mensagens de sistema serão notificadas na forma de uma estação de letra',
55
+  'app.settings.notification.todo': 'Notificação de To-do',
56
+  'app.settings.notification.todo-description':
57
+    'A lista de to-do será notificada na forma de uma estação de letra',
58
+  'app.settings.open': 'Aberto',
59
+  'app.settings.close': 'Fechado',
60
+};

+ 23
- 0
src/locales/zh-CN.js Ver arquivo

@@ -0,0 +1,23 @@
1
+import component from './zh-CN/component';
2
+import globalHeader from './zh-CN/globalHeader';
3
+import menu from './zh-CN/menu';
4
+import pwa from './zh-CN/pwa';
5
+import settingDrawer from './zh-CN/settingDrawer';
6
+import settings from './zh-CN/settings';
7
+import pages from './zh-CN/pages';
8
+export default {
9
+  'navBar.lang': '语言',
10
+  'layout.user.link.help': '帮助',
11
+  'layout.user.link.privacy': '隐私',
12
+  'layout.user.link.terms': '条款',
13
+  'app.preview.down.block': '下载此页面到本地项目',
14
+  'app.welcome.link.fetch-blocks': '获取全部区块',
15
+  'app.welcome.link.block-list': '基于 block 开发,快速构建标准页面',
16
+  ...pages,
17
+  ...globalHeader,
18
+  ...menu,
19
+  ...settingDrawer,
20
+  ...settings,
21
+  ...pwa,
22
+  ...component,
23
+};

+ 5
- 0
src/locales/zh-CN/component.js Ver arquivo

@@ -0,0 +1,5 @@
1
+export default {
2
+  'component.tagSelect.expand': '展开',
3
+  'component.tagSelect.collapse': '收起',
4
+  'component.tagSelect.all': '全部',
5
+};

+ 17
- 0
src/locales/zh-CN/globalHeader.js Ver arquivo

@@ -0,0 +1,17 @@
1
+export default {
2
+  'component.globalHeader.search': '站内搜索',
3
+  'component.globalHeader.search.example1': '搜索提示一',
4
+  'component.globalHeader.search.example2': '搜索提示二',
5
+  'component.globalHeader.search.example3': '搜索提示三',
6
+  'component.globalHeader.help': '使用文档',
7
+  'component.globalHeader.notification': '通知',
8
+  'component.globalHeader.notification.empty': '你已查看所有通知',
9
+  'component.globalHeader.message': '消息',
10
+  'component.globalHeader.message.empty': '您已读完所有消息',
11
+  'component.globalHeader.event': '待办',
12
+  'component.globalHeader.event.empty': '你已完成所有待办',
13
+  'component.noticeIcon.clear': '清空',
14
+  'component.noticeIcon.cleared': '清空了',
15
+  'component.noticeIcon.empty': '暂无数据',
16
+  'component.noticeIcon.view-more': '查看更多',
17
+};

+ 52
- 0
src/locales/zh-CN/menu.js Ver arquivo

@@ -0,0 +1,52 @@
1
+export default {
2
+  'menu.welcome': '欢迎',
3
+  'menu.more-blocks': '更多区块',
4
+  'menu.home': '首页',
5
+  'menu.admin': '管理页',
6
+  'menu.admin.sub-page': '二级管理页',
7
+  'menu.login': '登录',
8
+  'menu.register': '注册',
9
+  'menu.register.result': '注册结果',
10
+  'menu.dashboard': 'Dashboard',
11
+  'menu.dashboard.analysis': '分析页',
12
+  'menu.dashboard.monitor': '监控页',
13
+  'menu.dashboard.workplace': '工作台',
14
+  'menu.exception.403': '403',
15
+  'menu.exception.404': '404',
16
+  'menu.exception.500': '500',
17
+  'menu.form': '表单页',
18
+  'menu.form.basic-form': '基础表单',
19
+  'menu.form.step-form': '分步表单',
20
+  'menu.form.step-form.info': '分步表单(填写转账信息)',
21
+  'menu.form.step-form.confirm': '分步表单(确认转账信息)',
22
+  'menu.form.step-form.result': '分步表单(完成)',
23
+  'menu.form.advanced-form': '高级表单',
24
+  'menu.list': '列表页',
25
+  'menu.list.table-list': '查询表格',
26
+  'menu.list.basic-list': '标准列表',
27
+  'menu.list.card-list': '卡片列表',
28
+  'menu.list.search-list': '搜索列表',
29
+  'menu.list.search-list.articles': '搜索列表(文章)',
30
+  'menu.list.search-list.projects': '搜索列表(项目)',
31
+  'menu.list.search-list.applications': '搜索列表(应用)',
32
+  'menu.profile': '详情页',
33
+  'menu.profile.basic': '基础详情页',
34
+  'menu.profile.advanced': '高级详情页',
35
+  'menu.result': '结果页',
36
+  'menu.result.success': '成功页',
37
+  'menu.result.fail': '失败页',
38
+  'menu.exception': '异常页',
39
+  'menu.exception.not-permission': '403',
40
+  'menu.exception.not-find': '404',
41
+  'menu.exception.server-error': '500',
42
+  'menu.exception.trigger': '触发错误',
43
+  'menu.account': '个人页',
44
+  'menu.account.center': '个人中心',
45
+  'menu.account.settings': '个人设置',
46
+  'menu.account.trigger': '触发报错',
47
+  'menu.account.logout': '退出登录',
48
+  'menu.editor': '图形编辑器',
49
+  'menu.editor.flow': '流程编辑器',
50
+  'menu.editor.mind': '脑图编辑器',
51
+  'menu.editor.koni': '拓扑编辑器',
52
+};

+ 65
- 0
src/locales/zh-CN/pages.js Ver arquivo

@@ -0,0 +1,65 @@
1
+export default {
2
+  'pages.layouts.userLayout.title': 'Ant Design 是西湖区最具影响力的 Web 设计规范',
3
+  'pages.login.accountLogin.tab': '账户密码登录',
4
+  'pages.login.accountLogin.errorMessage': '错误的用户名和密码(admin/ant.design)',
5
+  'pages.login.username.placeholder': '用户名: admin or user',
6
+  'pages.login.username.required': '用户名是必填项!',
7
+  'pages.login.password.placeholder': '密码: ant.design',
8
+  'pages.login.password.required': '密码是必填项!',
9
+  'pages.login.phoneLogin.tab': '手机号登录',
10
+  'pages.login.phoneLogin.errorMessage': '验证码错误',
11
+  'pages.login.phoneNumber.placeholder': '请输入手机号!',
12
+  'pages.login.phoneNumber.required': '手机号是必填项!',
13
+  'pages.login.phoneNumber.invalid': '不合法的手机号!',
14
+  'pages.login.captcha.placeholder': '请输入验证码!',
15
+  'pages.login.captcha.required': '验证码是必填项!',
16
+  'pages.login.phoneLogin.getVerificationCode': '获取验证码',
17
+  'pages.getCaptchaSecondText': '秒后重新获取',
18
+  'pages.login.rememberMe': '自动登录',
19
+  'pages.login.forgotPassword': '忘记密码 ?',
20
+  'pages.login.submit': '提交',
21
+  'pages.login.loginWith': '其他登录方式 :',
22
+  'pages.login.registerAccount': '注册账户',
23
+  'pages.welcome.advancedComponent': '高级表格',
24
+  'pages.welcome.link': '欢迎使用',
25
+  'pages.welcome.advancedLayout': '高级布局',
26
+  'pages.welcome.alertMessage': '更快更强的重型组件,已经发布。',
27
+  'pages.admin.subPage.title': ' 这个页面只有 admin 权限才能查看',
28
+  'pages.admin.subPage.alertMessage': 'umi ui 现已发布,欢迎使用 npm run ui 启动体验。',
29
+  'pages.searchTable.createForm.newRule': '新建规则',
30
+  'pages.searchTable.updateForm.ruleConfig': '规则配置',
31
+  'pages.searchTable.updateForm.basicConfig': '基本信息',
32
+  'pages.searchTable.updateForm.ruleName.nameLabel': '规则名称',
33
+  'pages.searchTable.updateForm.ruleName.nameRules': '请输入规则名称!',
34
+  'pages.searchTable.updateForm.ruleDesc.descLabel': '规则描述',
35
+  'pages.searchTable.updateForm.ruleDesc.descPlaceholder': '请输入至少五个字符',
36
+  'pages.searchTable.updateForm.ruleDesc.descRules': '请输入至少五个字符的规则描述!',
37
+  'pages.searchTable.updateForm.ruleProps.title': '配置规则属性',
38
+  'pages.searchTable.updateForm.object': '监控对象',
39
+  'pages.searchTable.updateForm.ruleProps.templateLabel': '规则模板',
40
+  'pages.searchTable.updateForm.ruleProps.typeLabel': '规则类型',
41
+  'pages.searchTable.updateForm.schedulingPeriod.title': '设定调度周期',
42
+  'pages.searchTable.updateForm.schedulingPeriod.timeLabel': '开始时间',
43
+  'pages.searchTable.updateForm.schedulingPeriod.timeRules': '请选择开始时间!',
44
+  'pages.searchTable.titleDesc': '描述',
45
+  'pages.searchTable.ruleName': '规则名称为必填项',
46
+  'pages.searchTable.titleCallNo': '服务调用次数',
47
+  'pages.searchTable.titleStatus': '状态',
48
+  'pages.searchTable.nameStatus.default': '关闭',
49
+  'pages.searchTable.nameStatus.running': '运行中',
50
+  'pages.searchTable.nameStatus.online': '已上线',
51
+  'pages.searchTable.nameStatus.abnormal': '异常',
52
+  'pages.searchTable.titleUpdatedAt': '上次调度时间',
53
+  'pages.searchTable.exception': '请输入异常原因!',
54
+  'pages.searchTable.titleOption': '操作',
55
+  'pages.searchTable.config': '配置',
56
+  'pages.searchTable.subscribeAlert': '订阅警报',
57
+  'pages.searchTable.title': '查询表格',
58
+  'pages.searchTable.new': '新建',
59
+  'pages.searchTable.chosen': '已选择',
60
+  'pages.searchTable.item': '项',
61
+  'pages.searchTable.totalServiceCalls': '服务调用次数总计',
62
+  'pages.searchTable.tenThousand': '万',
63
+  'pages.searchTable.batchDeletion': '批量删除',
64
+  'pages.searchTable.batchApproval': '批量审批',
65
+};

+ 0
- 0
src/locales/zh-CN/pwa.js Ver arquivo


Alguns arquivos não foram mostrados porque muitos arquivos mudaram nesse diff