├── .editorconfig
├── .env
├── .eslintignore
├── .eslintrc.js
├── .gitignore
├── .prettierignore
├── .prettierrc.js
├── LICENSE
├── README.md
├── config
├── config.ts
├── defaultSettings.ts
├── oneapi.json
├── proxy.ts
└── routes.ts
├── docs
├── account.png
├── account_add.png
├── account_info.png
├── login.png
├── task.png
└── welcome.png
├── jest.config.ts
├── jsconfig.json
├── mock
├── listTableList.ts
├── notices.ts
├── requestRecord.mock.js
├── route.ts
└── user.ts
├── package.json
├── pnpm-lock.yaml
├── public
├── CNAME
├── favicon.ico
├── icons
│ ├── icon-128x128.png
│ ├── icon-192x192.png
│ └── icon-512x512.png
├── logo.svg
├── pro_icon.svg
└── scripts
│ └── loading.js
├── src
├── access.ts
├── app.tsx
├── components
│ ├── Footer
│ │ └── index.tsx
│ ├── HeaderDropdown
│ │ └── index.tsx
│ └── RightContent
│ │ ├── AvatarDropdown.tsx
│ │ └── index.tsx
├── global.less
├── global.tsx
├── locales
│ ├── en-US.ts
│ ├── en-US
│ │ ├── component.ts
│ │ ├── globalHeader.ts
│ │ ├── menu.ts
│ │ ├── pages.ts
│ │ ├── pwa.ts
│ │ ├── settingDrawer.ts
│ │ └── settings.ts
│ ├── zh-CN.ts
│ └── zh-CN
│ │ ├── component.ts
│ │ ├── globalHeader.ts
│ │ ├── menu.ts
│ │ ├── pages.ts
│ │ ├── pwa.ts
│ │ ├── settingDrawer.ts
│ │ └── settings.ts
├── manifest.json
├── pages
│ ├── 404.tsx
│ ├── AccountList
│ │ ├── components
│ │ │ ├── ColumnBuilder.tsx
│ │ │ ├── Modal.tsx
│ │ │ ├── button
│ │ │ │ ├── DelButton.tsx
│ │ │ │ └── SyncButton.tsx
│ │ │ └── contents
│ │ │ │ ├── AddContent .tsx
│ │ │ │ ├── EditContent .tsx
│ │ │ │ └── MoreContent .tsx
│ │ ├── index.less
│ │ └── index.tsx
│ ├── Task
│ │ └── List
│ │ │ ├── index.less
│ │ │ └── index.tsx
│ ├── User
│ │ └── Login
│ │ │ ├── __snapshots__
│ │ │ └── login.test.tsx.snap
│ │ │ ├── index.tsx
│ │ │ └── login.test.tsx
│ └── Welcome.tsx
├── requestErrorConfig.ts
├── service-worker.js
├── services
│ ├── ant-design-pro
│ │ ├── api.ts
│ │ ├── index.ts
│ │ ├── login.ts
│ │ └── typings.d.ts
│ └── swagger
│ │ ├── index.ts
│ │ ├── pet.ts
│ │ ├── store.ts
│ │ ├── typings.d.ts
│ │ └── user.ts
└── typings.d.ts
├── tests
└── setupTests.jsx
├── tsconfig.json
└── types
├── cache
├── cache.json
├── login.cache.json
└── mock
│ ├── login.mock.cache.js
│ └── mock.cache.js
└── index.d.ts
/.editorconfig:
--------------------------------------------------------------------------------
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
17 |
--------------------------------------------------------------------------------
/.env:
--------------------------------------------------------------------------------
1 | # 账户名
2 | ADMIN_NAME=admin
3 | # 密码
4 | PASS_WORD=123456
5 | # MJ-SERVER
6 | MJ_SERVER=http://127.0.0.1:8080
7 | # mj.api-secret
8 | UMI_APP_MJ_API_SECRET=homoloadmin
--------------------------------------------------------------------------------
/.eslintignore:
--------------------------------------------------------------------------------
1 | /lambda/
2 | /scripts
3 | /config
4 | .history
5 | public
6 | dist
7 | .umi
8 | mock
--------------------------------------------------------------------------------
/.eslintrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | extends: [require.resolve('@umijs/lint/dist/config/eslint')],
3 | globals: {
4 | page: true,
5 | REACT_APP_ENV: true,
6 | },
7 | };
8 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
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 |
12 | # misc
13 | .DS_Store
14 | npm-debug.log*
15 | yarn-error.log
16 |
17 | /coverage
18 | .idea
19 | yarn.lock
20 | package-lock.json
21 | *bak
22 | .vscode
23 |
24 |
25 | # visual studio code
26 | .history
27 | *.log
28 | functions/*
29 | .temp/**
30 |
31 | # umi
32 | .umi
33 | .umi-production
34 | .umi-test
35 |
36 | # screenshot
37 | screenshot
38 | .firebase
39 | .eslintcache
40 |
41 | build
42 |
--------------------------------------------------------------------------------
/.prettierignore:
--------------------------------------------------------------------------------
1 | **/*.svg
2 | .umi
3 | .umi-production
4 | /dist
5 | .dockerignore
6 | .DS_Store
7 | .eslintignore
8 | *.png
9 | *.toml
10 | docker
11 | .editorconfig
12 | Dockerfile*
13 | .gitignore
14 | .prettierignore
15 | LICENSE
16 | .eslintcache
17 | *.lock
18 | yarn-error.log
19 | .history
20 | CNAME
21 | /build
22 | /public
23 |
--------------------------------------------------------------------------------
/.prettierrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | singleQuote: true,
3 | trailingComma: 'all',
4 | printWidth: 100,
5 | proseWrap: 'never',
6 | endOfLine: 'lf',
7 | overrides: [
8 | {
9 | files: '.prettierrc',
10 | options: {
11 | parser: 'json',
12 | },
13 | },
14 | {
15 | files: 'document.ejs',
16 | options: {
17 | parser: 'html',
18 | },
19 | },
20 | ],
21 | };
22 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Apache License
2 | Version 2.0, January 2004
3 | http://www.apache.org/licenses/
4 |
5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
6 |
7 | 1. Definitions.
8 |
9 | "License" shall mean the terms and conditions for use, reproduction,
10 | and distribution as defined by Sections 1 through 9 of this document.
11 |
12 | "Licensor" shall mean the copyright owner or entity authorized by
13 | the copyright owner that is granting the License.
14 |
15 | "Legal Entity" shall mean the union of the acting entity and all
16 | other entities that control, are controlled by, or are under common
17 | control with that entity. For the purposes of this definition,
18 | "control" means (i) the power, direct or indirect, to cause the
19 | direction or management of such entity, whether by contract or
20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the
21 | outstanding shares, or (iii) beneficial ownership of such entity.
22 |
23 | "You" (or "Your") shall mean an individual or Legal Entity
24 | exercising permissions granted by this License.
25 |
26 | "Source" form shall mean the preferred form for making modifications,
27 | including but not limited to software source code, documentation
28 | source, and configuration files.
29 |
30 | "Object" form shall mean any form resulting from mechanical
31 | transformation or translation of a Source form, including but
32 | not limited to compiled object code, generated documentation,
33 | and conversions to other media types.
34 |
35 | "Work" shall mean the work of authorship, whether in Source or
36 | Object form, made available under the License, as indicated by a
37 | copyright notice that is included in or attached to the work
38 | (an example is provided in the Appendix below).
39 |
40 | "Derivative Works" shall mean any work, whether in Source or Object
41 | form, that is based on (or derived from) the Work and for which the
42 | editorial revisions, annotations, elaborations, or other modifications
43 | represent, as a whole, an original work of authorship. For the purposes
44 | of this License, Derivative Works shall not include works that remain
45 | separable from, or merely link (or bind by name) to the interfaces of,
46 | the Work and Derivative Works thereof.
47 |
48 | "Contribution" shall mean any work of authorship, including
49 | the original version of the Work and any modifications or additions
50 | to that Work or Derivative Works thereof, that is intentionally
51 | submitted to Licensor for inclusion in the Work by the copyright owner
52 | or by an individual or Legal Entity authorized to submit on behalf of
53 | the copyright owner. For the purposes of this definition, "submitted"
54 | means any form of electronic, verbal, or written communication sent
55 | to the Licensor or its representatives, including but not limited to
56 | communication on electronic mailing lists, source code control systems,
57 | and issue tracking systems that are managed by, or on behalf of, the
58 | Licensor for the purpose of discussing and improving the Work, but
59 | excluding communication that is conspicuously marked or otherwise
60 | designated in writing by the copyright owner as "Not a Contribution."
61 |
62 | "Contributor" shall mean Licensor and any individual or Legal Entity
63 | on behalf of whom a Contribution has been received by Licensor and
64 | subsequently incorporated within the Work.
65 |
66 | 2. Grant of Copyright License. Subject to the terms and conditions of
67 | this License, each Contributor hereby grants to You a perpetual,
68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
69 | copyright license to reproduce, prepare Derivative Works of,
70 | publicly display, publicly perform, sublicense, and distribute the
71 | Work and such Derivative Works in Source or Object form.
72 |
73 | 3. Grant of Patent License. Subject to the terms and conditions of
74 | this License, each Contributor hereby grants to You a perpetual,
75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
76 | (except as stated in this section) patent license to make, have made,
77 | use, offer to sell, sell, import, and otherwise transfer the Work,
78 | where such license applies only to those patent claims licensable
79 | by such Contributor that are necessarily infringed by their
80 | Contribution(s) alone or by combination of their Contribution(s)
81 | with the Work to which such Contribution(s) was submitted. If You
82 | institute patent litigation against any entity (including a
83 | cross-claim or counterclaim in a lawsuit) alleging that the Work
84 | or a Contribution incorporated within the Work constitutes direct
85 | or contributory patent infringement, then any patent licenses
86 | granted to You under this License for that Work shall terminate
87 | as of the date such litigation is filed.
88 |
89 | 4. Redistribution. You may reproduce and distribute copies of the
90 | Work or Derivative Works thereof in any medium, with or without
91 | modifications, and in Source or Object form, provided that You
92 | meet the following conditions:
93 |
94 | (a) You must give any other recipients of the Work or
95 | Derivative Works a copy of this License; and
96 |
97 | (b) You must cause any modified files to carry prominent notices
98 | stating that You changed the files; and
99 |
100 | (c) You must retain, in the Source form of any Derivative Works
101 | that You distribute, all copyright, patent, trademark, and
102 | attribution notices from the Source form of the Work,
103 | excluding those notices that do not pertain to any part of
104 | the Derivative Works; and
105 |
106 | (d) If the Work includes a "NOTICE" text file as part of its
107 | distribution, then any Derivative Works that You distribute must
108 | include a readable copy of the attribution notices contained
109 | within such NOTICE file, excluding those notices that do not
110 | pertain to any part of the Derivative Works, in at least one
111 | of the following places: within a NOTICE text file distributed
112 | as part of the Derivative Works; within the Source form or
113 | documentation, if provided along with the Derivative Works; or,
114 | within a display generated by the Derivative Works, if and
115 | wherever such third-party notices normally appear. The contents
116 | of the NOTICE file are for informational purposes only and
117 | do not modify the License. You may add Your own attribution
118 | notices within Derivative Works that You distribute, alongside
119 | or as an addendum to the NOTICE text from the Work, provided
120 | that such additional attribution notices cannot be construed
121 | as modifying the License.
122 |
123 | You may add Your own copyright statement to Your modifications and
124 | may provide additional or different license terms and conditions
125 | for use, reproduction, or distribution of Your modifications, or
126 | for any such Derivative Works as a whole, provided Your use,
127 | reproduction, and distribution of the Work otherwise complies with
128 | the conditions stated in this License.
129 |
130 | 5. Submission of Contributions. Unless You explicitly state otherwise,
131 | any Contribution intentionally submitted for inclusion in the Work
132 | by You to the Licensor shall be under the terms and conditions of
133 | this License, without any additional terms or conditions.
134 | Notwithstanding the above, nothing herein shall supersede or modify
135 | the terms of any separate license agreement you may have executed
136 | with Licensor regarding such Contributions.
137 |
138 | 6. Trademarks. This License does not grant permission to use the trade
139 | names, trademarks, service marks, or product names of the Licensor,
140 | except as required for reasonable and customary use in describing the
141 | origin of the Work and reproducing the content of the NOTICE file.
142 |
143 | 7. Disclaimer of Warranty. Unless required by applicable law or
144 | agreed to in writing, Licensor provides the Work (and each
145 | Contributor provides its Contributions) on an "AS IS" BASIS,
146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
147 | implied, including, without limitation, any warranties or conditions
148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
149 | PARTICULAR PURPOSE. You are solely responsible for determining the
150 | appropriateness of using or redistributing the Work and assume any
151 | risks associated with Your exercise of permissions under this License.
152 |
153 | 8. Limitation of Liability. In no event and under no legal theory,
154 | whether in tort (including negligence), contract, or otherwise,
155 | unless required by applicable law (such as deliberate and grossly
156 | negligent acts) or agreed to in writing, shall any Contributor be
157 | liable to You for damages, including any direct, indirect, special,
158 | incidental, or consequential damages of any character arising as a
159 | result of this License or out of the use or inability to use the
160 | Work (including but not limited to damages for loss of goodwill,
161 | work stoppage, computer failure or malfunction, or any and all
162 | other commercial damages or losses), even if such Contributor
163 | has been advised of the possibility of such damages.
164 |
165 | 9. Accepting Warranty or Additional Liability. While redistributing
166 | the Work or Derivative Works thereof, You may choose to offer,
167 | and charge a fee for, acceptance of support, warranty, indemnity,
168 | or other liability obligations and/or rights consistent with this
169 | License. However, in accepting such obligations, You may act only
170 | on Your own behalf and on Your sole responsibility, not on behalf
171 | of any other Contributor, and only if You agree to indemnify,
172 | defend, and hold each Contributor harmless for any liability
173 | incurred by, or claims asserted against, such Contributor by reason
174 | of your accepting any such warranty or additional liability.
175 |
176 | END OF TERMS AND CONDITIONS
177 |
178 | APPENDIX: How to apply the Apache License to your work.
179 |
180 | To apply the Apache License to your work, attach the following
181 | boilerplate notice, with the fields enclosed by brackets "[]"
182 | replaced with your own identifying information. (Don't include
183 | the brackets!) The text should be enclosed in the appropriate
184 | comment syntax for the file format. We also recommend that a
185 | file or class name and description of purpose be included on the
186 | same "printed page" as the copyright notice for easier
187 | identification within third-party archives.
188 |
189 | Copyright [yyyy] [name of copyright owner]
190 |
191 | Licensed under the Apache License, Version 2.0 (the "License");
192 | you may not use this file except in compliance with the License.
193 | You may obtain a copy of the License at
194 |
195 | http://www.apache.org/licenses/LICENSE-2.0
196 |
197 | Unless required by applicable law or agreed to in writing, software
198 | distributed under the License is distributed on an "AS IS" BASIS,
199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
200 | See the License for the specific language governing permissions and
201 | limitations under the License.
202 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # midjourney-proxy-admin
2 | [midjourney-proxy-plus](https://github.com/litter-coder/midjourney-proxy-plus) 的管理后台
3 |
4 | # 主要功能
5 |
6 | - [x] 支持MJ账户的增删改查功能
7 | - [x] 支持MJ账户的详细信息查询和账户同步操作
8 | - [x] 支持MJ账户的并发队列设置
9 | - [x] 支持MJ的任务查询
10 |
11 | # 后续计划
12 |
13 | - [ ] 任务查询功能优化
14 | - [ ] 支持MJ的账户settings修改
15 | - [ ] 支持MJ的队列内容查询
16 | - [ ] ...
17 |
18 | # 使用示例
19 |
20 | ①登录页
21 |
22 |
23 |
24 | ②欢迎页
25 |
26 |
27 |
28 | ③账户管理
29 |
30 |
31 |
32 | ④添加账户
33 |
34 |
35 |
36 | ⑤账户详情
37 |
38 |
39 |
40 | ⑥任务列表
41 |
42 |
43 |
44 | # 部署方式
45 |
46 | ## 1.运行环境
47 |
48 | 支持 Linux、MacOS、Windows 系统(可在Linux服务器上长期运行),同时需安装 `node18`。
49 |
50 | **(1) 克隆项目代码:**
51 |
52 | ```bash
53 | git clone https://github.com/litter-coder/midjourney-proxy-admin
54 | cd midjourney-proxy-admin/
55 | ```
56 |
57 | **(2) 安装依赖 :**
58 |
59 | ```bash
60 | npm install
61 | ```
62 |
63 | ## 2.配置
64 |
65 | 配置文件在根目录的`.env`中:
66 |
67 | ```shell
68 | # 账户名
69 | ADMIN_NAME=admin
70 | # 密码
71 | PASS_WORD=123456
72 | # MJ-SERVER
73 | MJ_SERVER=http://127.0.0.1:8080
74 | # mj.api-secret
75 | UMI_APP_MJ_API_SECRET=123456
76 | ```
77 |
78 | ## 3.运行
79 |
80 | 使用nohup命令在后台运行程序:
81 |
82 | ```
83 | nohup npm run start > out.log 2>&1 & disown
84 | # 在后台运行程序
85 | ```
86 |
87 | ## 4.其他
88 |
89 | ### 1.查看进程
90 |
91 | ```shell
92 | ps -ef | grep npm
93 | ```
94 |
95 | ### 2.结束进程
96 |
97 | ```sh
98 | kill -9 [进程id]
99 | ```
100 |
101 | # 联系我们
102 |
103 | 问题咨询和商务合作可联系
104 |
105 |
106 |
107 |
--------------------------------------------------------------------------------
/config/config.ts:
--------------------------------------------------------------------------------
1 | // https://umijs.org/config/
2 | import { defineConfig } from '@umijs/max';
3 | import { join } from 'path';
4 | import defaultSettings from './defaultSettings';
5 | import proxy from './proxy';
6 | import routes from './routes';
7 |
8 | const { REACT_APP_ENV = 'dev' } = process.env;
9 |
10 | export default defineConfig({
11 | /**
12 | * @name 开启 hash 模式
13 | * @description 让 build 之后的产物包含 hash 后缀。通常用于增量发布和避免浏览器加载缓存。
14 | * @doc https://umijs.org/docs/api/config#hash
15 | */
16 | hash: true,
17 |
18 | /**
19 | * @name 兼容性设置
20 | * @description 设置 ie11 不一定完美兼容,需要检查自己使用的所有依赖
21 | * @doc https://umijs.org/docs/api/config#targets
22 | */
23 | // targets: {
24 | // ie: 11,
25 | // },
26 | /**
27 | * @name 路由的配置,不在路由中引入的文件不会编译
28 | * @description 只支持 path,component,routes,redirect,wrappers,title 的配置
29 | * @doc https://umijs.org/docs/guides/routes
30 | */
31 | // umi routes: https://umijs.org/docs/routing
32 | routes,
33 | /**
34 | * @name 主题的配置
35 | * @description 虽然叫主题,但是其实只是 less 的变量设置
36 | * @doc antd的主题设置 https://ant.design/docs/react/customize-theme-cn
37 | * @doc umi 的theme 配置 https://umijs.org/docs/api/config#theme
38 | */
39 | theme: {
40 | // 如果不想要 configProvide 动态设置主题需要把这个设置为 default
41 | // 只有设置为 variable, 才能使用 configProvide 动态设置主色调
42 | 'root-entry-name': 'variable',
43 | },
44 | /**
45 | * @name moment 的国际化配置
46 | * @description 如果对国际化没有要求,打开之后能减少js的包大小
47 | * @doc https://umijs.org/docs/api/config#ignoremomentlocale
48 | */
49 | ignoreMomentLocale: true,
50 | /**
51 | * @name 代理配置
52 | * @description 可以让你的本地服务器代理到你的服务器上,这样你就可以访问服务器的数据了
53 | * @see 要注意以下 代理只能在本地开发时使用,build 之后就无法使用了。
54 | * @doc 代理介绍 https://umijs.org/docs/guides/proxy
55 | * @doc 代理配置 https://umijs.org/docs/api/config#proxy
56 | */
57 | proxy: proxy[REACT_APP_ENV as keyof typeof proxy],
58 | /**
59 | * @name 快速热更新配置
60 | * @description 一个不错的热更新组件,更新时可以保留 state
61 | */
62 | fastRefresh: true,
63 | //============== 以下都是max的插件配置 ===============
64 | /**
65 | * @name 数据流插件
66 | * @@doc https://umijs.org/docs/max/data-flow
67 | */
68 | model: {},
69 | /**
70 | * 一个全局的初始数据流,可以用它在插件之间共享数据
71 | * @description 可以用来存放一些全局的数据,比如用户信息,或者一些全局的状态,全局初始状态在整个 Umi 项目的最开始创建。
72 | * @doc https://umijs.org/docs/max/data-flow#%E5%85%A8%E5%B1%80%E5%88%9D%E5%A7%8B%E7%8A%B6%E6%80%81
73 | */
74 | initialState: {},
75 | /**
76 | * @name layout 插件
77 | * @doc https://umijs.org/docs/max/layout-menu
78 | */
79 | title: 'Ant Design Pro',
80 | layout: {
81 | locale: true,
82 | ...defaultSettings,
83 | },
84 | /**
85 | * @name moment2dayjs 插件
86 | * @description 将项目中的 moment 替换为 dayjs
87 | * @doc https://umijs.org/docs/max/moment2dayjs
88 | */
89 | moment2dayjs: {
90 | preset: 'antd',
91 | plugins: ['duration'],
92 | },
93 | /**
94 | * @name 国际化插件
95 | * @doc https://umijs.org/docs/max/i18n
96 | */
97 | locale: {
98 | // default zh-CN
99 | default: 'zh-CN',
100 | antd: true,
101 | // default true, when it is true, will use `navigator.language` overwrite default
102 | baseNavigator: true,
103 | },
104 | /**
105 | * @name antd 插件
106 | * @description 内置了 babel import 插件
107 | * @doc https://umijs.org/docs/max/antd#antd
108 | */
109 | antd: {},
110 | /**
111 | * @name 网络请求配置
112 | * @description 它基于 axios 和 ahooks 的 useRequest 提供了一套统一的网络请求和错误处理方案。
113 | * @doc https://umijs.org/docs/max/request
114 | */
115 | request: {},
116 | /**
117 | * @name 权限插件
118 | * @description 基于 initialState 的权限插件,必须先打开 initialState
119 | * @doc https://umijs.org/docs/max/access
120 | */
121 | access: {},
122 | /**
123 | * @name
中额外的 script
124 | * @description 配置 中额外的 script
125 | */
126 | headScripts: [
127 | // 解决首次加载时白屏的问题
128 | { src: '/scripts/loading.js', async: true },
129 | ],
130 | //================ pro 插件配置 =================
131 | presets: ['umi-presets-pro'],
132 | /**
133 | * @name openAPI 插件的配置
134 | * @description 基于 openapi 的规范生成serve 和mock,能减少很多样板代码
135 | * @doc https://pro.ant.design/zh-cn/docs/openapi/
136 | */
137 | openAPI: [
138 | {
139 | requestLibPath: "import { request } from '@umijs/max'",
140 | // 或者使用在线的版本
141 | // schemaPath: "https://gw.alipayobjects.com/os/antfincdn/M%24jrzTTYJN/oneapi.json"
142 | schemaPath: join(__dirname, 'oneapi.json'),
143 | mock: false,
144 | },
145 | {
146 | requestLibPath: "import { request } from '@umijs/max'",
147 | schemaPath: 'https://gw.alipayobjects.com/os/antfincdn/CA1dOm%2631B/openapi.json',
148 | projectName: 'swagger',
149 | },
150 | ],
151 | mfsu: {
152 | strategy: 'normal',
153 | },
154 | requestRecord: {},
155 | });
156 |
--------------------------------------------------------------------------------
/config/defaultSettings.ts:
--------------------------------------------------------------------------------
1 | import { ProLayoutProps } from '@ant-design/pro-components';
2 |
3 | /**
4 | * @name
5 | */
6 | const Settings: ProLayoutProps & {
7 | pwa?: boolean;
8 | logo?: string;
9 | } = {
10 | navTheme: 'light',
11 | // 拂晓蓝
12 | colorPrimary: '#1890ff',
13 | layout: 'mix',
14 | contentWidth: 'Fluid',
15 | fixedHeader: false,
16 | fixSiderbar: true,
17 | colorWeak: false,
18 | title: 'Midjourney Proxy Admin',
19 | pwa: true,
20 | logo: 'https://gw.alipayobjects.com/zos/rmsportal/KDpgvguMpGfqaHPjicRK.svg',
21 | iconfontUrl: '',
22 | token: {
23 | // 参见ts声明,demo 见文档,通过token 修改样式
24 | //https://procomponents.ant.design/components/layout#%E9%80%9A%E8%BF%87-token-%E4%BF%AE%E6%94%B9%E6%A0%B7%E5%BC%8F
25 | },
26 | };
27 |
28 | export default Settings;
29 |
--------------------------------------------------------------------------------
/config/oneapi.json:
--------------------------------------------------------------------------------
1 | {
2 | "openapi": "3.0.1",
3 | "info": {
4 | "title": "Ant Design Pro",
5 | "version": "1.0.0"
6 | },
7 | "servers": [
8 | {
9 | "url": "http://localhost:8000/"
10 | },
11 | {
12 | "url": "https://localhost:8000/"
13 | }
14 | ],
15 | "paths": {
16 | "/api/currentUser": {
17 | "get": {
18 | "tags": ["api"],
19 | "description": "获取当前的用户",
20 | "operationId": "currentUser",
21 | "responses": {
22 | "200": {
23 | "description": "Success",
24 | "content": {
25 | "application/json": {
26 | "schema": {
27 | "$ref": "#/components/schemas/CurrentUser"
28 | }
29 | }
30 | }
31 | },
32 | "401": {
33 | "description": "Error",
34 | "content": {
35 | "application/json": {
36 | "schema": {
37 | "$ref": "#/components/schemas/ErrorResponse"
38 | }
39 | }
40 | }
41 | }
42 | }
43 | },
44 | "x-swagger-router-controller": "api"
45 | },
46 | "/api/login/captcha": {
47 | "post": {
48 | "description": "发送验证码",
49 | "operationId": "getFakeCaptcha",
50 | "tags": ["login"],
51 | "parameters": [
52 | {
53 | "name": "phone",
54 | "in": "query",
55 | "description": "手机号",
56 | "schema": {
57 | "type": "string"
58 | }
59 | }
60 | ],
61 | "responses": {
62 | "200": {
63 | "description": "Success",
64 | "content": {
65 | "application/json": {
66 | "schema": {
67 | "$ref": "#/components/schemas/FakeCaptcha"
68 | }
69 | }
70 | }
71 | }
72 | }
73 | }
74 | },
75 | "/api/login/outLogin": {
76 | "post": {
77 | "description": "登录接口",
78 | "operationId": "outLogin",
79 | "tags": ["login"],
80 | "responses": {
81 | "200": {
82 | "description": "Success",
83 | "content": {
84 | "application/json": {
85 | "schema": {
86 | "type": "object"
87 | }
88 | }
89 | }
90 | },
91 | "401": {
92 | "description": "Error",
93 | "content": {
94 | "application/json": {
95 | "schema": {
96 | "$ref": "#/components/schemas/ErrorResponse"
97 | }
98 | }
99 | }
100 | }
101 | }
102 | },
103 | "x-swagger-router-controller": "api"
104 | },
105 | "/api/login/account": {
106 | "post": {
107 | "tags": ["login"],
108 | "description": "登录接口",
109 | "operationId": "login",
110 | "requestBody": {
111 | "description": "登录系统",
112 | "content": {
113 | "application/json": {
114 | "schema": {
115 | "$ref": "#/components/schemas/LoginParams"
116 | }
117 | }
118 | },
119 | "required": true
120 | },
121 | "responses": {
122 | "200": {
123 | "description": "Success",
124 | "content": {
125 | "application/json": {
126 | "schema": {
127 | "$ref": "#/components/schemas/LoginResult"
128 | }
129 | }
130 | }
131 | },
132 | "401": {
133 | "description": "Error",
134 | "content": {
135 | "application/json": {
136 | "schema": {
137 | "$ref": "#/components/schemas/ErrorResponse"
138 | }
139 | }
140 | }
141 | }
142 | },
143 | "x-codegen-request-body-name": "body"
144 | },
145 | "x-swagger-router-controller": "api"
146 | },
147 | "/api/notices": {
148 | "summary": "getNotices",
149 | "description": "NoticeIconItem",
150 | "get": {
151 | "tags": ["api"],
152 | "operationId": "getNotices",
153 | "responses": {
154 | "200": {
155 | "description": "Success",
156 | "content": {
157 | "application/json": {
158 | "schema": {
159 | "$ref": "#/components/schemas/NoticeIconList"
160 | }
161 | }
162 | }
163 | }
164 | }
165 | }
166 | },
167 | "/api/rule": {
168 | "get": {
169 | "tags": ["rule"],
170 | "description": "获取规则列表",
171 | "operationId": "rule",
172 | "parameters": [
173 | {
174 | "name": "current",
175 | "in": "query",
176 | "description": "当前的页码",
177 | "schema": {
178 | "type": "number"
179 | }
180 | },
181 | {
182 | "name": "pageSize",
183 | "in": "query",
184 | "description": "页面的容量",
185 | "schema": {
186 | "type": "number"
187 | }
188 | }
189 | ],
190 | "responses": {
191 | "200": {
192 | "description": "Success",
193 | "content": {
194 | "application/json": {
195 | "schema": {
196 | "$ref": "#/components/schemas/RuleList"
197 | }
198 | }
199 | }
200 | },
201 | "401": {
202 | "description": "Error",
203 | "content": {
204 | "application/json": {
205 | "schema": {
206 | "$ref": "#/components/schemas/ErrorResponse"
207 | }
208 | }
209 | }
210 | }
211 | }
212 | },
213 | "post": {
214 | "tags": ["rule"],
215 | "description": "新建规则",
216 | "operationId": "addRule",
217 | "responses": {
218 | "200": {
219 | "description": "Success",
220 | "content": {
221 | "application/json": {
222 | "schema": {
223 | "$ref": "#/components/schemas/RuleListItem"
224 | }
225 | }
226 | }
227 | },
228 | "401": {
229 | "description": "Error",
230 | "content": {
231 | "application/json": {
232 | "schema": {
233 | "$ref": "#/components/schemas/ErrorResponse"
234 | }
235 | }
236 | }
237 | }
238 | }
239 | },
240 | "put": {
241 | "tags": ["rule"],
242 | "description": "新建规则",
243 | "operationId": "updateRule",
244 | "responses": {
245 | "200": {
246 | "description": "Success",
247 | "content": {
248 | "application/json": {
249 | "schema": {
250 | "$ref": "#/components/schemas/RuleListItem"
251 | }
252 | }
253 | }
254 | },
255 | "401": {
256 | "description": "Error",
257 | "content": {
258 | "application/json": {
259 | "schema": {
260 | "$ref": "#/components/schemas/ErrorResponse"
261 | }
262 | }
263 | }
264 | }
265 | }
266 | },
267 | "delete": {
268 | "tags": ["rule"],
269 | "description": "删除规则",
270 | "operationId": "removeRule",
271 | "responses": {
272 | "200": {
273 | "description": "Success",
274 | "content": {
275 | "application/json": {
276 | "schema": {
277 | "type": "object"
278 | }
279 | }
280 | }
281 | },
282 | "401": {
283 | "description": "Error",
284 | "content": {
285 | "application/json": {
286 | "schema": {
287 | "$ref": "#/components/schemas/ErrorResponse"
288 | }
289 | }
290 | }
291 | }
292 | }
293 | },
294 | "x-swagger-router-controller": "api"
295 | },
296 | "/swagger": {
297 | "x-swagger-pipe": "swagger_raw"
298 | }
299 | },
300 | "components": {
301 | "schemas": {
302 | "CurrentUser": {
303 | "type": "object",
304 | "properties": {
305 | "name": {
306 | "type": "string"
307 | },
308 | "avatar": {
309 | "type": "string"
310 | },
311 | "userid": {
312 | "type": "string"
313 | },
314 | "email": {
315 | "type": "string"
316 | },
317 | "signature": {
318 | "type": "string"
319 | },
320 | "title": {
321 | "type": "string"
322 | },
323 | "group": {
324 | "type": "string"
325 | },
326 | "tags": {
327 | "type": "array",
328 | "items": {
329 | "type": "object",
330 | "properties": {
331 | "key": {
332 | "type": "string"
333 | },
334 | "label": {
335 | "type": "string"
336 | }
337 | }
338 | }
339 | },
340 | "notifyCount": {
341 | "type": "integer",
342 | "format": "int32"
343 | },
344 | "unreadCount": {
345 | "type": "integer",
346 | "format": "int32"
347 | },
348 | "country": {
349 | "type": "string"
350 | },
351 | "access": {
352 | "type": "string"
353 | },
354 | "geographic": {
355 | "type": "object",
356 | "properties": {
357 | "province": {
358 | "type": "object",
359 | "properties": {
360 | "label": {
361 | "type": "string"
362 | },
363 | "key": {
364 | "type": "string"
365 | }
366 | }
367 | },
368 | "city": {
369 | "type": "object",
370 | "properties": {
371 | "label": {
372 | "type": "string"
373 | },
374 | "key": {
375 | "type": "string"
376 | }
377 | }
378 | }
379 | }
380 | },
381 | "address": {
382 | "type": "string"
383 | },
384 | "phone": {
385 | "type": "string"
386 | }
387 | }
388 | },
389 | "LoginResult": {
390 | "type": "object",
391 | "properties": {
392 | "status": {
393 | "type": "string"
394 | },
395 | "type": {
396 | "type": "string"
397 | },
398 | "currentAuthority": {
399 | "type": "string"
400 | }
401 | }
402 | },
403 | "PageParams": {
404 | "type": "object",
405 | "properties": {
406 | "current": {
407 | "type": "number"
408 | },
409 | "pageSize": {
410 | "type": "number"
411 | }
412 | }
413 | },
414 | "RuleListItem": {
415 | "type": "object",
416 | "properties": {
417 | "key": {
418 | "type": "integer",
419 | "format": "int32"
420 | },
421 | "disabled": {
422 | "type": "boolean"
423 | },
424 | "href": {
425 | "type": "string"
426 | },
427 | "avatar": {
428 | "type": "string"
429 | },
430 | "name": {
431 | "type": "string"
432 | },
433 | "owner": {
434 | "type": "string"
435 | },
436 | "desc": {
437 | "type": "string"
438 | },
439 | "callNo": {
440 | "type": "integer",
441 | "format": "int32"
442 | },
443 | "status": {
444 | "type": "integer",
445 | "format": "int32"
446 | },
447 | "updatedAt": {
448 | "type": "string",
449 | "format": "datetime"
450 | },
451 | "createdAt": {
452 | "type": "string",
453 | "format": "datetime"
454 | },
455 | "progress": {
456 | "type": "integer",
457 | "format": "int32"
458 | }
459 | }
460 | },
461 | "RuleList": {
462 | "type": "object",
463 | "properties": {
464 | "data": {
465 | "type": "array",
466 | "items": {
467 | "$ref": "#/components/schemas/RuleListItem"
468 | }
469 | },
470 | "total": {
471 | "type": "integer",
472 | "description": "列表的内容总数",
473 | "format": "int32"
474 | },
475 | "success": {
476 | "type": "boolean"
477 | }
478 | }
479 | },
480 | "FakeCaptcha": {
481 | "type": "object",
482 | "properties": {
483 | "code": {
484 | "type": "integer",
485 | "format": "int32"
486 | },
487 | "status": {
488 | "type": "string"
489 | }
490 | }
491 | },
492 | "LoginParams": {
493 | "type": "object",
494 | "properties": {
495 | "username": {
496 | "type": "string"
497 | },
498 | "password": {
499 | "type": "string"
500 | },
501 | "autoLogin": {
502 | "type": "boolean"
503 | },
504 | "type": {
505 | "type": "string"
506 | }
507 | }
508 | },
509 | "ErrorResponse": {
510 | "required": ["errorCode"],
511 | "type": "object",
512 | "properties": {
513 | "errorCode": {
514 | "type": "string",
515 | "description": "业务约定的错误码"
516 | },
517 | "errorMessage": {
518 | "type": "string",
519 | "description": "业务上的错误信息"
520 | },
521 | "success": {
522 | "type": "boolean",
523 | "description": "业务上的请求是否成功"
524 | }
525 | }
526 | },
527 | "NoticeIconList": {
528 | "type": "object",
529 | "properties": {
530 | "data": {
531 | "type": "array",
532 | "items": {
533 | "$ref": "#/components/schemas/NoticeIconItem"
534 | }
535 | },
536 | "total": {
537 | "type": "integer",
538 | "description": "列表的内容总数",
539 | "format": "int32"
540 | },
541 | "success": {
542 | "type": "boolean"
543 | }
544 | }
545 | },
546 | "NoticeIconItemType": {
547 | "title": "NoticeIconItemType",
548 | "description": "已读未读列表的枚举",
549 | "type": "string",
550 | "properties": {},
551 | "enum": ["notification", "message", "event"]
552 | },
553 | "NoticeIconItem": {
554 | "type": "object",
555 | "properties": {
556 | "id": {
557 | "type": "string"
558 | },
559 | "extra": {
560 | "type": "string",
561 | "format": "any"
562 | },
563 | "key": { "type": "string" },
564 | "read": {
565 | "type": "boolean"
566 | },
567 | "avatar": {
568 | "type": "string"
569 | },
570 | "title": {
571 | "type": "string"
572 | },
573 | "status": {
574 | "type": "string"
575 | },
576 | "datetime": {
577 | "type": "string",
578 | "format": "date"
579 | },
580 | "description": {
581 | "type": "string"
582 | },
583 | "type": {
584 | "extensions": {
585 | "x-is-enum": true
586 | },
587 | "$ref": "#/components/schemas/NoticeIconItemType"
588 | }
589 | }
590 | }
591 | }
592 | }
593 | }
594 |
--------------------------------------------------------------------------------
/config/proxy.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * @name 代理的配置
3 | * @see 在生产环境 代理是无法生效的,所以这里没有生产环境的配置
4 | * -------------------------------
5 | * The agent cannot take effect in the production environment
6 | * so there is no configuration of the production environment
7 | * For details, please see
8 | * https://pro.ant.design/docs/deploy
9 | *
10 | * @doc https://umijs.org/docs/guides/proxy
11 | */
12 |
13 | const MJ_SERVER = process.env.MJ_SERVER;
14 |
15 | export default {
16 | // 如果需要自定义本地开发服务器 请取消注释按需调整
17 | dev: {
18 | // localhost:8000/api/** -> https://preview.pro.ant.design/api/**
19 | '/mj/': {
20 | // 要代理的地址
21 | target: MJ_SERVER,
22 | // 配置了这个可以从 http 代理到 https
23 | // 依赖 origin 的功能可能需要这个,比如 cookie
24 | changeOrigin: true,
25 | },
26 | },
27 |
28 | /**
29 | * @name 详细的代理配置
30 | * @doc https://github.com/chimurai/http-proxy-middleware
31 | */
32 | test: {
33 | // localhost:8000/api/** -> https://preview.pro.ant.design/api/**
34 | '/api/': {
35 | target: 'https://proapi.azurewebsites.net',
36 | changeOrigin: true,
37 | pathRewrite: { '^': '' },
38 | },
39 | },
40 | pre: {
41 | '/api/': {
42 | target: 'your pre url',
43 | changeOrigin: true,
44 | pathRewrite: { '^': '' },
45 | },
46 | },
47 | };
48 |
--------------------------------------------------------------------------------
/config/routes.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * @name umi 的路由配置
3 | * @description 只支持 path,component,routes,redirect,wrappers,name,icon 的配置
4 | * @param path path 只支持两种占位符配置,第一种是动态参数 :id 的形式,第二种是 * 通配符,通配符只能出现路由字符串的最后。
5 | * @param component 配置 location 和 path 匹配后用于渲染的 React 组件路径。可以是绝对路径,也可以是相对路径,如果是相对路径,会从 src/pages 开始找起。
6 | * @param routes 配置子路由,通常在需要为多个路径增加 layout 组件时使用。
7 | * @param redirect 配置路由跳转
8 | * @param wrappers 配置路由组件的包装组件,通过包装组件可以为当前的路由组件组合进更多的功能。 比如,可以用于路由级别的权限校验
9 | * @param name 配置路由的标题,默认读取国际化文件 menu.ts 中 menu.xxxx 的值,如配置 name 为 login,则读取 menu.ts 中 menu.login 的取值作为标题
10 | * @param icon 配置路由的图标,取值参考 https://ant.design/components/icon-cn, 注意去除风格后缀和大小写,如想要配置图标为 则取值应为 stepBackward 或 StepBackward,如想要配置图标为 则取值应为 user 或者 User
11 | * @doc https://umijs.org/docs/guides/routes
12 | */
13 | export default [
14 | {
15 | path: '/user',
16 | layout: false,
17 | routes: [
18 | {
19 | name: 'login',
20 | path: '/user/login',
21 | component: './User/Login',
22 | },
23 | ],
24 | },
25 | {
26 | path: '/welcome',
27 | name: 'welcome',
28 | icon: 'smile',
29 | component: './Welcome',
30 | },
31 | {
32 | name: 'list.account-list',
33 | icon: 'crown',
34 | path: '/account',
35 | component: './AccountList',
36 | },
37 | {
38 | name: 'task-list',
39 | icon: 'bars',
40 | path: '/task/list',
41 | component: './Task/List',
42 | },
43 | {
44 | path: '/',
45 | redirect: '/welcome',
46 | },
47 | {
48 | path: '*',
49 | layout: false,
50 | component: './404',
51 | },
52 | ];
53 |
--------------------------------------------------------------------------------
/docs/account.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rancytech/mj-proxy-admin/969850bfbc599cefbf51ee9d701cca3cb38e66e7/docs/account.png
--------------------------------------------------------------------------------
/docs/account_add.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rancytech/mj-proxy-admin/969850bfbc599cefbf51ee9d701cca3cb38e66e7/docs/account_add.png
--------------------------------------------------------------------------------
/docs/account_info.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rancytech/mj-proxy-admin/969850bfbc599cefbf51ee9d701cca3cb38e66e7/docs/account_info.png
--------------------------------------------------------------------------------
/docs/login.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rancytech/mj-proxy-admin/969850bfbc599cefbf51ee9d701cca3cb38e66e7/docs/login.png
--------------------------------------------------------------------------------
/docs/task.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rancytech/mj-proxy-admin/969850bfbc599cefbf51ee9d701cca3cb38e66e7/docs/task.png
--------------------------------------------------------------------------------
/docs/welcome.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rancytech/mj-proxy-admin/969850bfbc599cefbf51ee9d701cca3cb38e66e7/docs/welcome.png
--------------------------------------------------------------------------------
/jest.config.ts:
--------------------------------------------------------------------------------
1 | import { configUmiAlias, createConfig } from '@umijs/max/test';
2 |
3 | export default async () => {
4 | const config = await configUmiAlias({
5 | ...createConfig({
6 | target: 'browser',
7 | }),
8 | });
9 |
10 | console.log();
11 | return {
12 | ...config,
13 | testEnvironmentOptions: {
14 | ...(config?.testEnvironmentOptions || {}),
15 | url: 'http://localhost:8000',
16 | },
17 | setupFiles: [...(config.setupFiles || []), './tests/setupTests.jsx'],
18 | globals: {
19 | ...config.globals,
20 | localStorage: null,
21 | },
22 | };
23 | };
24 |
--------------------------------------------------------------------------------
/jsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "jsx": "react-jsx",
4 | "emitDecoratorMetadata": true,
5 | "experimentalDecorators": true,
6 | "baseUrl": ".",
7 | "paths": {
8 | "@/*": ["./src/*"]
9 | }
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/mock/listTableList.ts:
--------------------------------------------------------------------------------
1 | import { Request, Response } from 'express';
2 | import moment from 'moment';
3 | import { parse } from 'url';
4 |
5 | // mock tableListDataSource
6 | const genList = (current: number, pageSize: number) => {
7 | const tableListDataSource: API.RuleListItem[] = [];
8 |
9 | for (let i = 0; i < pageSize; i += 1) {
10 | const index = (current - 1) * 10 + i;
11 | tableListDataSource.push({
12 | key: index,
13 | disabled: i % 6 === 0,
14 | href: 'https://ant.design',
15 | avatar: [
16 | 'https://gw.alipayobjects.com/zos/rmsportal/eeHMaZBwmTvLdIwMfBpg.png',
17 | 'https://gw.alipayobjects.com/zos/rmsportal/udxAbMEhpwthVVcjLXik.png',
18 | ][i % 2],
19 | name: `TradeCode ${index}`,
20 | owner: '曲丽丽',
21 | desc: '这是一段描述',
22 | callNo: Math.floor(Math.random() * 1000),
23 | status: Math.floor(Math.random() * 10) % 4,
24 | updatedAt: moment().format('YYYY-MM-DD'),
25 | createdAt: moment().format('YYYY-MM-DD'),
26 | progress: Math.ceil(Math.random() * 100),
27 | });
28 | }
29 | tableListDataSource.reverse();
30 | return tableListDataSource;
31 | };
32 |
33 | let tableListDataSource = genList(1, 100);
34 |
35 | function getRule(req: Request, res: Response, u: string) {
36 | let realUrl = u;
37 | if (!realUrl || Object.prototype.toString.call(realUrl) !== '[object String]') {
38 | realUrl = req.url;
39 | }
40 | const { current = 1, pageSize = 10 } = req.query;
41 | const params = parse(realUrl, true).query as unknown as API.PageParams &
42 | API.RuleListItem & {
43 | sorter: any;
44 | filter: any;
45 | };
46 |
47 | let dataSource = [...tableListDataSource].slice(
48 | ((current as number) - 1) * (pageSize as number),
49 | (current as number) * (pageSize as number),
50 | );
51 | if (params.sorter) {
52 | const sorter = JSON.parse(params.sorter);
53 | dataSource = dataSource.sort((prev, next) => {
54 | let sortNumber = 0;
55 | (Object.keys(sorter) as Array).forEach((key) => {
56 | let nextSort = next?.[key] as number;
57 | let preSort = prev?.[key] as number;
58 | if (sorter[key] === 'descend') {
59 | if (preSort - nextSort > 0) {
60 | sortNumber += -1;
61 | } else {
62 | sortNumber += 1;
63 | }
64 | return;
65 | }
66 | if (preSort - nextSort > 0) {
67 | sortNumber += 1;
68 | } else {
69 | sortNumber += -1;
70 | }
71 | });
72 | return sortNumber;
73 | });
74 | }
75 | if (params.filter) {
76 | const filter = JSON.parse(params.filter as any) as {
77 | [key: string]: string[];
78 | };
79 | if (Object.keys(filter).length > 0) {
80 | dataSource = dataSource.filter((item) => {
81 | return (Object.keys(filter) as Array).some((key) => {
82 | if (!filter[key]) {
83 | return true;
84 | }
85 | if (filter[key].includes(`${item[key]}`)) {
86 | return true;
87 | }
88 | return false;
89 | });
90 | });
91 | }
92 | }
93 |
94 | if (params.name) {
95 | dataSource = dataSource.filter((data) => data?.name?.includes(params.name || ''));
96 | }
97 | const result = {
98 | data: dataSource,
99 | total: tableListDataSource.length,
100 | success: true,
101 | pageSize,
102 | current: parseInt(`${params.current}`, 10) || 1,
103 | };
104 |
105 | return res.json(result);
106 | }
107 |
108 | function postRule(req: Request, res: Response, u: string, b: Request) {
109 | let realUrl = u;
110 | if (!realUrl || Object.prototype.toString.call(realUrl) !== '[object String]') {
111 | realUrl = req.url;
112 | }
113 |
114 | const body = (b && b.body) || req.body;
115 | const { method, name, desc, key } = body;
116 |
117 | switch (method) {
118 | /* eslint no-case-declarations:0 */
119 | case 'delete':
120 | tableListDataSource = tableListDataSource.filter((item) => key.indexOf(item.key) === -1);
121 | break;
122 | case 'post':
123 | (() => {
124 | const i = Math.ceil(Math.random() * 10000);
125 | const newRule: API.RuleListItem = {
126 | key: tableListDataSource.length,
127 | href: 'https://ant.design',
128 | avatar: [
129 | 'https://gw.alipayobjects.com/zos/rmsportal/eeHMaZBwmTvLdIwMfBpg.png',
130 | 'https://gw.alipayobjects.com/zos/rmsportal/udxAbMEhpwthVVcjLXik.png',
131 | ][i % 2],
132 | name,
133 | owner: '曲丽丽',
134 | desc,
135 | callNo: Math.floor(Math.random() * 1000),
136 | status: Math.floor(Math.random() * 10) % 2,
137 | updatedAt: moment().format('YYYY-MM-DD'),
138 | createdAt: moment().format('YYYY-MM-DD'),
139 | progress: Math.ceil(Math.random() * 100),
140 | };
141 | tableListDataSource.unshift(newRule);
142 | return res.json(newRule);
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 | return item;
155 | });
156 | return res.json(newRule);
157 | })();
158 | return;
159 | default:
160 | break;
161 | }
162 |
163 | const result = {
164 | list: tableListDataSource,
165 | pagination: {
166 | total: tableListDataSource.length,
167 | },
168 | };
169 |
170 | res.json(result);
171 | }
172 |
173 | export default {
174 | 'GET /api/rule': getRule,
175 | 'POST /api/rule': postRule,
176 | };
177 |
--------------------------------------------------------------------------------
/mock/notices.ts:
--------------------------------------------------------------------------------
1 | import { Request, Response } from 'express';
2 |
3 | const getNotices = (req: Request, res: Response) => {
4 | res.json({
5 | data: [
6 | {
7 | id: '000000001',
8 | avatar:
9 | 'https://mdn.alipayobjects.com/yuyan_qk0oxh/afts/img/MSbDR4FR2MUAAAAAAAAAAAAAFl94AQBr',
10 | title: '你收到了 14 份新周报',
11 | datetime: '2017-08-09',
12 | type: 'notification',
13 | },
14 | {
15 | id: '000000002',
16 | avatar:
17 | 'https://mdn.alipayobjects.com/yuyan_qk0oxh/afts/img/hX-PTavYIq4AAAAAAAAAAAAAFl94AQBr',
18 | title: '你推荐的 曲妮妮 已通过第三轮面试',
19 | datetime: '2017-08-08',
20 | type: 'notification',
21 | },
22 | {
23 | id: '000000003',
24 | avatar:
25 | 'https://mdn.alipayobjects.com/yuyan_qk0oxh/afts/img/jHX5R5l3QjQAAAAAAAAAAAAAFl94AQBr',
26 | title: '这种模板可以区分多种通知类型',
27 | datetime: '2017-08-07',
28 | read: true,
29 | type: 'notification',
30 | },
31 | {
32 | id: '000000004',
33 | avatar:
34 | 'https://mdn.alipayobjects.com/yuyan_qk0oxh/afts/img/Wr4mQqx6jfwAAAAAAAAAAAAAFl94AQBr',
35 | title: '左侧图标用于区分不同的类型',
36 | datetime: '2017-08-07',
37 | type: 'notification',
38 | },
39 | {
40 | id: '000000005',
41 | avatar:
42 | 'https://mdn.alipayobjects.com/yuyan_qk0oxh/afts/img/Mzj_TbcWUj4AAAAAAAAAAAAAFl94AQBr',
43 | title: '内容不要超过两行字,超出时自动截断',
44 | datetime: '2017-08-07',
45 | type: 'notification',
46 | },
47 | {
48 | id: '000000006',
49 | avatar:
50 | 'https://mdn.alipayobjects.com/yuyan_qk0oxh/afts/img/eXLzRbPqQE4AAAAAAAAAAAAAFl94AQBr',
51 | title: '曲丽丽 评论了你',
52 | description: '描述信息描述信息描述信息',
53 | datetime: '2017-08-07',
54 | type: 'message',
55 | clickClose: true,
56 | },
57 | {
58 | id: '000000007',
59 | avatar:
60 | 'https://mdn.alipayobjects.com/yuyan_qk0oxh/afts/img/w5mRQY2AmEEAAAAAAAAAAAAAFl94AQBr',
61 | title: '朱偏右 回复了你',
62 | description: '这种模板用于提醒谁与你发生了互动,左侧放『谁』的头像',
63 | datetime: '2017-08-07',
64 | type: 'message',
65 | clickClose: true,
66 | },
67 | {
68 | id: '000000008',
69 | avatar:
70 | 'https://mdn.alipayobjects.com/yuyan_qk0oxh/afts/img/wPadR5M9918AAAAAAAAAAAAAFl94AQBr',
71 | title: '标题',
72 | description: '这种模板用于提醒谁与你发生了互动,左侧放『谁』的头像',
73 | datetime: '2017-08-07',
74 | type: 'message',
75 | clickClose: true,
76 | },
77 | {
78 | id: '000000009',
79 | title: '任务名称',
80 | description: '任务需要在 2017-01-12 20:00 前启动',
81 | extra: '未开始',
82 | status: 'todo',
83 | type: 'event',
84 | },
85 | {
86 | id: '000000010',
87 | title: '第三方紧急代码变更',
88 | description: '冠霖提交于 2017-01-06,需在 2017-01-07 前完成代码变更任务',
89 | extra: '马上到期',
90 | status: 'urgent',
91 | type: 'event',
92 | },
93 | {
94 | id: '000000011',
95 | title: '信息安全考试',
96 | description: '指派竹尔于 2017-01-09 前完成更新并发布',
97 | extra: '已耗时 8 天',
98 | status: 'doing',
99 | type: 'event',
100 | },
101 | {
102 | id: '000000012',
103 | title: 'ABCD 版本发布',
104 | description: '冠霖提交于 2017-01-06,需在 2017-01-07 前完成代码变更任务',
105 | extra: '进行中',
106 | status: 'processing',
107 | type: 'event',
108 | },
109 | ],
110 | });
111 | };
112 |
113 | export default {
114 | 'GET /api/notices': getNotices,
115 | };
116 |
--------------------------------------------------------------------------------
/mock/requestRecord.mock.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | 'GET /api/currentUser': {
3 | data: {
4 | name: 'Serati Ma',
5 | avatar: 'https://gw.alipayobjects.com/zos/rmsportal/BiazfanxmamNRoxxVxka.png',
6 | userid: '00000001',
7 | email: 'antdesign@alipay.com',
8 | signature: '海纳百川,有容乃大',
9 | title: '交互专家',
10 | group: '蚂蚁金服-某某某事业群-某某平台部-某某技术部-UED',
11 | tags: [
12 | { key: '0', label: '很有想法的' },
13 | { key: '1', label: '专注设计' },
14 | { key: '2', label: '辣~' },
15 | { key: '3', label: '大长腿' },
16 | { key: '4', label: '川妹子' },
17 | { key: '5', label: '海纳百川' },
18 | ],
19 | notifyCount: 12,
20 | unreadCount: 11,
21 | country: 'China',
22 | geographic: {
23 | province: { label: '浙江省', key: '330000' },
24 | city: { label: '杭州市', key: '330100' },
25 | },
26 | address: '西湖区工专路 77 号',
27 | phone: '0752-268888888',
28 | },
29 | },
30 | 'GET /api/rule': {
31 | data: [
32 | {
33 | key: 99,
34 | disabled: false,
35 | href: 'https://ant.design',
36 | avatar: 'https://gw.alipayobjects.com/zos/rmsportal/udxAbMEhpwthVVcjLXik.png',
37 | name: 'TradeCode 99',
38 | owner: '曲丽丽',
39 | desc: '这是一段描述',
40 | callNo: 503,
41 | status: '0',
42 | updatedAt: '2022-12-06T05:00:57.040Z',
43 | createdAt: '2022-12-06T05:00:57.040Z',
44 | progress: 81,
45 | },
46 | {
47 | key: 98,
48 | disabled: false,
49 | href: 'https://ant.design',
50 | avatar: 'https://gw.alipayobjects.com/zos/rmsportal/eeHMaZBwmTvLdIwMfBpg.png',
51 | name: 'TradeCode 98',
52 | owner: '曲丽丽',
53 | desc: '这是一段描述',
54 | callNo: 164,
55 | status: '0',
56 | updatedAt: '2022-12-06T05:00:57.040Z',
57 | createdAt: '2022-12-06T05:00:57.040Z',
58 | progress: 12,
59 | },
60 | {
61 | key: 97,
62 | disabled: false,
63 | href: 'https://ant.design',
64 | avatar: 'https://gw.alipayobjects.com/zos/rmsportal/udxAbMEhpwthVVcjLXik.png',
65 | name: 'TradeCode 97',
66 | owner: '曲丽丽',
67 | desc: '这是一段描述',
68 | callNo: 174,
69 | status: '1',
70 | updatedAt: '2022-12-06T05:00:57.040Z',
71 | createdAt: '2022-12-06T05:00:57.040Z',
72 | progress: 81,
73 | },
74 | {
75 | key: 96,
76 | disabled: true,
77 | href: 'https://ant.design',
78 | avatar: 'https://gw.alipayobjects.com/zos/rmsportal/eeHMaZBwmTvLdIwMfBpg.png',
79 | name: 'TradeCode 96',
80 | owner: '曲丽丽',
81 | desc: '这是一段描述',
82 | callNo: 914,
83 | status: '0',
84 | updatedAt: '2022-12-06T05:00:57.040Z',
85 | createdAt: '2022-12-06T05:00:57.040Z',
86 | progress: 7,
87 | },
88 | {
89 | key: 95,
90 | disabled: false,
91 | href: 'https://ant.design',
92 | avatar: 'https://gw.alipayobjects.com/zos/rmsportal/udxAbMEhpwthVVcjLXik.png',
93 | name: 'TradeCode 95',
94 | owner: '曲丽丽',
95 | desc: '这是一段描述',
96 | callNo: 698,
97 | status: '2',
98 | updatedAt: '2022-12-06T05:00:57.040Z',
99 | createdAt: '2022-12-06T05:00:57.040Z',
100 | progress: 82,
101 | },
102 | {
103 | key: 94,
104 | disabled: false,
105 | href: 'https://ant.design',
106 | avatar: 'https://gw.alipayobjects.com/zos/rmsportal/eeHMaZBwmTvLdIwMfBpg.png',
107 | name: 'TradeCode 94',
108 | owner: '曲丽丽',
109 | desc: '这是一段描述',
110 | callNo: 488,
111 | status: '1',
112 | updatedAt: '2022-12-06T05:00:57.040Z',
113 | createdAt: '2022-12-06T05:00:57.040Z',
114 | progress: 14,
115 | },
116 | {
117 | key: 93,
118 | disabled: false,
119 | href: 'https://ant.design',
120 | avatar: 'https://gw.alipayobjects.com/zos/rmsportal/udxAbMEhpwthVVcjLXik.png',
121 | name: 'TradeCode 93',
122 | owner: '曲丽丽',
123 | desc: '这是一段描述',
124 | callNo: 580,
125 | status: '2',
126 | updatedAt: '2022-12-06T05:00:57.040Z',
127 | createdAt: '2022-12-06T05:00:57.040Z',
128 | progress: 77,
129 | },
130 | {
131 | key: 92,
132 | disabled: false,
133 | href: 'https://ant.design',
134 | avatar: 'https://gw.alipayobjects.com/zos/rmsportal/eeHMaZBwmTvLdIwMfBpg.png',
135 | name: 'TradeCode 92',
136 | owner: '曲丽丽',
137 | desc: '这是一段描述',
138 | callNo: 244,
139 | status: '3',
140 | updatedAt: '2022-12-06T05:00:57.040Z',
141 | createdAt: '2022-12-06T05:00:57.040Z',
142 | progress: 58,
143 | },
144 | {
145 | key: 91,
146 | disabled: false,
147 | href: 'https://ant.design',
148 | avatar: 'https://gw.alipayobjects.com/zos/rmsportal/udxAbMEhpwthVVcjLXik.png',
149 | name: 'TradeCode 91',
150 | owner: '曲丽丽',
151 | desc: '这是一段描述',
152 | callNo: 959,
153 | status: '0',
154 | updatedAt: '2022-12-06T05:00:57.040Z',
155 | createdAt: '2022-12-06T05:00:57.040Z',
156 | progress: 66,
157 | },
158 | {
159 | key: 90,
160 | disabled: true,
161 | href: 'https://ant.design',
162 | avatar: 'https://gw.alipayobjects.com/zos/rmsportal/eeHMaZBwmTvLdIwMfBpg.png',
163 | name: 'TradeCode 90',
164 | owner: '曲丽丽',
165 | desc: '这是一段描述',
166 | callNo: 958,
167 | status: '0',
168 | updatedAt: '2022-12-06T05:00:57.040Z',
169 | createdAt: '2022-12-06T05:00:57.040Z',
170 | progress: 72,
171 | },
172 | {
173 | key: 89,
174 | disabled: false,
175 | href: 'https://ant.design',
176 | avatar: 'https://gw.alipayobjects.com/zos/rmsportal/udxAbMEhpwthVVcjLXik.png',
177 | name: 'TradeCode 89',
178 | owner: '曲丽丽',
179 | desc: '这是一段描述',
180 | callNo: 301,
181 | status: '2',
182 | updatedAt: '2022-12-06T05:00:57.040Z',
183 | createdAt: '2022-12-06T05:00:57.040Z',
184 | progress: 2,
185 | },
186 | {
187 | key: 88,
188 | disabled: false,
189 | href: 'https://ant.design',
190 | avatar: 'https://gw.alipayobjects.com/zos/rmsportal/eeHMaZBwmTvLdIwMfBpg.png',
191 | name: 'TradeCode 88',
192 | owner: '曲丽丽',
193 | desc: '这是一段描述',
194 | callNo: 277,
195 | status: '1',
196 | updatedAt: '2022-12-06T05:00:57.040Z',
197 | createdAt: '2022-12-06T05:00:57.040Z',
198 | progress: 12,
199 | },
200 | {
201 | key: 87,
202 | disabled: false,
203 | href: 'https://ant.design',
204 | avatar: 'https://gw.alipayobjects.com/zos/rmsportal/udxAbMEhpwthVVcjLXik.png',
205 | name: 'TradeCode 87',
206 | owner: '曲丽丽',
207 | desc: '这是一段描述',
208 | callNo: 810,
209 | status: '1',
210 | updatedAt: '2022-12-06T05:00:57.040Z',
211 | createdAt: '2022-12-06T05:00:57.040Z',
212 | progress: 82,
213 | },
214 | {
215 | key: 86,
216 | disabled: false,
217 | href: 'https://ant.design',
218 | avatar: 'https://gw.alipayobjects.com/zos/rmsportal/eeHMaZBwmTvLdIwMfBpg.png',
219 | name: 'TradeCode 86',
220 | owner: '曲丽丽',
221 | desc: '这是一段描述',
222 | callNo: 780,
223 | status: '3',
224 | updatedAt: '2022-12-06T05:00:57.040Z',
225 | createdAt: '2022-12-06T05:00:57.040Z',
226 | progress: 22,
227 | },
228 | {
229 | key: 85,
230 | disabled: false,
231 | href: 'https://ant.design',
232 | avatar: 'https://gw.alipayobjects.com/zos/rmsportal/udxAbMEhpwthVVcjLXik.png',
233 | name: 'TradeCode 85',
234 | owner: '曲丽丽',
235 | desc: '这是一段描述',
236 | callNo: 705,
237 | status: '3',
238 | updatedAt: '2022-12-06T05:00:57.040Z',
239 | createdAt: '2022-12-06T05:00:57.040Z',
240 | progress: 12,
241 | },
242 | {
243 | key: 84,
244 | disabled: true,
245 | href: 'https://ant.design',
246 | avatar: 'https://gw.alipayobjects.com/zos/rmsportal/eeHMaZBwmTvLdIwMfBpg.png',
247 | name: 'TradeCode 84',
248 | owner: '曲丽丽',
249 | desc: '这是一段描述',
250 | callNo: 203,
251 | status: '0',
252 | updatedAt: '2022-12-06T05:00:57.040Z',
253 | createdAt: '2022-12-06T05:00:57.040Z',
254 | progress: 79,
255 | },
256 | {
257 | key: 83,
258 | disabled: false,
259 | href: 'https://ant.design',
260 | avatar: 'https://gw.alipayobjects.com/zos/rmsportal/udxAbMEhpwthVVcjLXik.png',
261 | name: 'TradeCode 83',
262 | owner: '曲丽丽',
263 | desc: '这是一段描述',
264 | callNo: 491,
265 | status: '2',
266 | updatedAt: '2022-12-06T05:00:57.040Z',
267 | createdAt: '2022-12-06T05:00:57.040Z',
268 | progress: 59,
269 | },
270 | {
271 | key: 82,
272 | disabled: false,
273 | href: 'https://ant.design',
274 | avatar: 'https://gw.alipayobjects.com/zos/rmsportal/eeHMaZBwmTvLdIwMfBpg.png',
275 | name: 'TradeCode 82',
276 | owner: '曲丽丽',
277 | desc: '这是一段描述',
278 | callNo: 73,
279 | status: '0',
280 | updatedAt: '2022-12-06T05:00:57.040Z',
281 | createdAt: '2022-12-06T05:00:57.040Z',
282 | progress: 100,
283 | },
284 | {
285 | key: 81,
286 | disabled: false,
287 | href: 'https://ant.design',
288 | avatar: 'https://gw.alipayobjects.com/zos/rmsportal/udxAbMEhpwthVVcjLXik.png',
289 | name: 'TradeCode 81',
290 | owner: '曲丽丽',
291 | desc: '这是一段描述',
292 | callNo: 406,
293 | status: '3',
294 | updatedAt: '2022-12-06T05:00:57.040Z',
295 | createdAt: '2022-12-06T05:00:57.040Z',
296 | progress: 61,
297 | },
298 | {
299 | key: 80,
300 | disabled: false,
301 | href: 'https://ant.design',
302 | avatar: 'https://gw.alipayobjects.com/zos/rmsportal/eeHMaZBwmTvLdIwMfBpg.png',
303 | name: 'TradeCode 80',
304 | owner: '曲丽丽',
305 | desc: '这是一段描述',
306 | callNo: 112,
307 | status: '2',
308 | updatedAt: '2022-12-06T05:00:57.040Z',
309 | createdAt: '2022-12-06T05:00:57.040Z',
310 | progress: 20,
311 | },
312 | ],
313 | total: 100,
314 | success: true,
315 | pageSize: 20,
316 | current: 1,
317 | },
318 | 'POST /api/login/outLogin': { data: {}, success: true },
319 | 'POST /api/login/account': {
320 | status: 'ok',
321 | type: 'account',
322 | currentAuthority: 'admin',
323 | },
324 | };
325 |
--------------------------------------------------------------------------------
/mock/route.ts:
--------------------------------------------------------------------------------
1 | export default {
2 | '/api/auth_routes': {
3 | '/form/advanced-form': { authority: ['admin', 'user'] },
4 | },
5 | };
6 |
--------------------------------------------------------------------------------
/mock/user.ts:
--------------------------------------------------------------------------------
1 | import { Request, Response } from 'express';
2 |
3 | const adminName = process.env.ADMIN_NAME;
4 | const passWord = process.env.PASS_WORD;
5 |
6 | const waitTime = (time: number = 100) => {
7 | return new Promise((resolve) => {
8 | setTimeout(() => {
9 | resolve(true);
10 | }, time);
11 | });
12 | };
13 |
14 | async function getFakeCaptcha(req: Request, res: Response) {
15 | await waitTime(2000);
16 | return res.json('captcha-xxx');
17 | }
18 |
19 | const { ANT_DESIGN_PRO_ONLY_DO_NOT_USE_IN_YOUR_PRODUCTION } = process.env;
20 |
21 | /**
22 | * 当前用户的权限,如果为空代表没登录
23 | * current user access, if is '', user need login
24 | * 如果是 pro 的预览,默认是有权限的
25 | */
26 | let access = ANT_DESIGN_PRO_ONLY_DO_NOT_USE_IN_YOUR_PRODUCTION === 'site' ? 'admin' : '';
27 |
28 | const getAccess = () => {
29 | return access;
30 | };
31 |
32 | // 代码中会兼容本地 service mock 以及部署站点的静态数据
33 | export default {
34 | // 支持值为 Object 和 Array
35 | 'GET /api/currentUser': (req: Request, res: Response) => {
36 | if (!getAccess()) {
37 | res.status(401).send({
38 | data: {
39 | isLogin: false,
40 | },
41 | errorCode: '401',
42 | errorMessage: '请先登录!',
43 | success: true,
44 | });
45 | return;
46 | }
47 | res.send({
48 | success: true,
49 | data: {
50 | name: adminName,
51 | avatar: 'https://gw.alipayobjects.com/zos/antfincdn/XAosXuNZyF/BiazfanxmamNRoxxVxka.png',
52 | userid: '00000001',
53 | email: 'antdesign@alipay.com',
54 | signature: '海纳百川,有容乃大',
55 | title: '交互专家',
56 | group: '蚂蚁金服-某某某事业群-某某平台部-某某技术部-UED',
57 | tags: [
58 | {
59 | key: '0',
60 | label: '很有想法的',
61 | },
62 | {
63 | key: '1',
64 | label: '专注设计',
65 | },
66 | {
67 | key: '2',
68 | label: '辣~',
69 | },
70 | {
71 | key: '3',
72 | label: '大长腿',
73 | },
74 | {
75 | key: '4',
76 | label: '川妹子',
77 | },
78 | {
79 | key: '5',
80 | label: '海纳百川',
81 | },
82 | ],
83 | notifyCount: 12,
84 | unreadCount: 11,
85 | country: 'China',
86 | access: getAccess(),
87 | geographic: {
88 | province: {
89 | label: '浙江省',
90 | key: '330000',
91 | },
92 | city: {
93 | label: '杭州市',
94 | key: '330100',
95 | },
96 | },
97 | address: '西湖区工专路 77 号',
98 | phone: '0752-268888888',
99 | },
100 | });
101 | },
102 | // GET POST 可省略
103 | 'GET /api/users': [
104 | {
105 | key: '1',
106 | name: 'John Brown',
107 | age: 32,
108 | address: 'New York No. 1 Lake Park',
109 | },
110 | {
111 | key: '2',
112 | name: 'Jim Green',
113 | age: 42,
114 | address: 'London No. 1 Lake Park',
115 | },
116 | {
117 | key: '3',
118 | name: 'Joe Black',
119 | age: 32,
120 | address: 'Sidney No. 1 Lake Park',
121 | },
122 | ],
123 | 'POST /api/login/account': async (req: Request, res: Response) => {
124 | const { password, username, type } = req.body;
125 | await waitTime(2000);
126 | if (password === password && username === adminName) {
127 | res.send({
128 | status: 'ok',
129 | type,
130 | currentAuthority: 'admin',
131 | });
132 | access = 'admin';
133 | return;
134 | }
135 | res.send({
136 | status: 'error',
137 | type,
138 | currentAuthority: 'guest',
139 | });
140 | access = 'guest';
141 | },
142 | 'POST /api/login/outLogin': (req: Request, res: Response) => {
143 | access = '';
144 | res.send({ data: {}, success: true });
145 | },
146 | 'POST /api/register': (req: Request, res: Response) => {
147 | res.send({ status: 'ok', currentAuthority: 'user', success: true });
148 | },
149 | 'GET /api/500': (req: Request, res: Response) => {
150 | res.status(500).send({
151 | timestamp: 1513932555104,
152 | status: 500,
153 | error: 'error',
154 | message: 'error',
155 | path: '/base/category/list',
156 | });
157 | },
158 | 'GET /api/404': (req: Request, res: Response) => {
159 | res.status(404).send({
160 | timestamp: 1513932643431,
161 | status: 404,
162 | error: 'Not Found',
163 | message: 'No message available',
164 | path: '/base/category/list/2121212',
165 | });
166 | },
167 | 'GET /api/403': (req: Request, res: Response) => {
168 | res.status(403).send({
169 | timestamp: 1513932555104,
170 | status: 403,
171 | error: 'Forbidden',
172 | message: 'Forbidden',
173 | path: '/base/category/list',
174 | });
175 | },
176 | 'GET /api/401': (req: Request, res: Response) => {
177 | res.status(401).send({
178 | timestamp: 1513932555104,
179 | status: 401,
180 | error: 'Unauthorized',
181 | message: 'Unauthorized',
182 | path: '/base/category/list',
183 | });
184 | },
185 |
186 | 'GET /api/login/captcha': getFakeCaptcha,
187 | };
188 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "midjourney-proxy-admin",
3 | "version": "1.0",
4 | "private": true,
5 | "description": "An out-of-box UI solution for enterprise applications",
6 | "scripts": {
7 | "analyze": "cross-env ANALYZE=1 max build",
8 | "build": "max build",
9 | "deploy": "npm run build && npm run gh-pages",
10 | "dev": "npm run start:dev",
11 | "gh-pages": "gh-pages -d dist",
12 | "i18n-remove": "pro i18n-remove --locale=zh-CN --write",
13 | "postinstall": "max setup",
14 | "jest": "jest",
15 | "lint": "npm run lint:js && npm run lint:prettier && npm run tsc",
16 | "lint-staged": "lint-staged",
17 | "lint-staged:js": "eslint --ext .js,.jsx,.ts,.tsx ",
18 | "lint:fix": "eslint --fix --cache --ext .js,.jsx,.ts,.tsx --format=pretty ./src ",
19 | "lint:js": "eslint --cache --ext .js,.jsx,.ts,.tsx --format=pretty ./src",
20 | "lint:prettier": "prettier -c --write \"**/**.{js,jsx,tsx,ts,less,md,json}\" --end-of-line auto",
21 | "openapi": "max openapi",
22 | "prettier": "prettier -c --write \"**/**.{js,jsx,tsx,ts,less,md,json}\"",
23 | "preview": "npm run build && max preview --port 8000",
24 | "record": "cross-env NODE_ENV=development REACT_APP_ENV=test max record --scene=login",
25 | "serve": "umi-serve",
26 | "start": "cross-env UMI_ENV=dev max dev",
27 | "start:dev": "cross-env REACT_APP_ENV=dev MOCK=none UMI_ENV=dev max dev",
28 | "start:no-mock": "cross-env MOCK=none UMI_ENV=dev max dev",
29 | "start:pre": "cross-env REACT_APP_ENV=pre UMI_ENV=dev max dev",
30 | "start:test": "cross-env REACT_APP_ENV=test MOCK=none UMI_ENV=dev max dev",
31 | "test": "jest",
32 | "test:coverage": "npm run jest -- --coverage",
33 | "test:update": "npm run jest -- -u",
34 | "tsc": "tsc --noEmit"
35 | },
36 | "lint-staged": {
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.8.0",
49 | "@ant-design/pro-components": "^2.3.57",
50 | "@ant-design/use-emotion-css": "1.0.4",
51 | "@umijs/route-utils": "^2.2.2",
52 | "antd": "^5.2.2",
53 | "classnames": "^2.3.2",
54 | "lodash": "^4.17.21",
55 | "moment": "^2.29.4",
56 | "omit.js": "^2.0.2",
57 | "rc-menu": "^9.8.2",
58 | "rc-util": "^5.27.2",
59 | "react": "^18.2.0",
60 | "react-dev-inspector": "^1.8.4",
61 | "react-dom": "^18.2.0",
62 | "react-helmet-async": "^1.3.0"
63 | },
64 | "devDependencies": {
65 | "@ant-design/pro-cli": "^2.1.5",
66 | "@testing-library/react": "^13.4.0",
67 | "@types/classnames": "^2.3.1",
68 | "@types/express": "^4.17.17",
69 | "@types/history": "^4.7.11",
70 | "@types/jest": "^29.4.0",
71 | "@types/lodash": "^4.14.191",
72 | "@types/react": "^18.0.28",
73 | "@types/react-dom": "^18.0.11",
74 | "@types/react-helmet": "^6.1.6",
75 | "@umijs/fabric": "^2.14.1",
76 | "@umijs/lint": "^4.0.52",
77 | "@umijs/max": "^4.0.52",
78 | "cross-env": "^7.0.3",
79 | "eslint": "^8.34.0",
80 | "express": "^4.18.2",
81 | "gh-pages": "^3.2.3",
82 | "husky": "^7.0.4",
83 | "jest": "^29.4.3",
84 | "jest-environment-jsdom": "^29.4.3",
85 | "lint-staged": "^10.5.4",
86 | "mockjs": "^1.1.0",
87 | "prettier": "^2.8.4",
88 | "swagger-ui-dist": "^4.15.5",
89 | "ts-node": "^10.9.1",
90 | "typescript": "^4.9.5",
91 | "umi-presets-pro": "^2.0.2"
92 | },
93 | "engines": {
94 | "node": ">=12.0.0"
95 | }
96 | }
97 |
--------------------------------------------------------------------------------
/public/CNAME:
--------------------------------------------------------------------------------
1 | preview.pro.ant.design
--------------------------------------------------------------------------------
/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rancytech/mj-proxy-admin/969850bfbc599cefbf51ee9d701cca3cb38e66e7/public/favicon.ico
--------------------------------------------------------------------------------
/public/icons/icon-128x128.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rancytech/mj-proxy-admin/969850bfbc599cefbf51ee9d701cca3cb38e66e7/public/icons/icon-128x128.png
--------------------------------------------------------------------------------
/public/icons/icon-192x192.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rancytech/mj-proxy-admin/969850bfbc599cefbf51ee9d701cca3cb38e66e7/public/icons/icon-192x192.png
--------------------------------------------------------------------------------
/public/icons/icon-512x512.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rancytech/mj-proxy-admin/969850bfbc599cefbf51ee9d701cca3cb38e66e7/public/icons/icon-512x512.png
--------------------------------------------------------------------------------
/public/logo.svg:
--------------------------------------------------------------------------------
1 | Group 28 Copy 5 Created with Sketch.
--------------------------------------------------------------------------------
/public/pro_icon.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/public/scripts/loading.js:
--------------------------------------------------------------------------------
1 | /**
2 | * loading 占位
3 | * 解决首次加载时白屏的问题
4 | */
5 | (function () {
6 | const _root = document.querySelector('#root');
7 | if (_root && _root.innerHTML === '') {
8 | _root.innerHTML = `
9 |
174 |
175 |
183 |
184 |
185 |
186 |
187 |
188 |
189 |
190 |
191 |
192 |
193 |
194 | 正在加载资源
195 |
196 |
197 | 初次加载资源可能需要较多时间 请耐心等待
198 |
199 |
200 | `;
201 | }
202 | })();
203 |
--------------------------------------------------------------------------------
/src/access.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * @see https://umijs.org/zh-CN/plugins/plugin-access
3 | * */
4 | export default function access(initialState: { currentUser?: API.CurrentUser } | undefined) {
5 | const { currentUser } = initialState ?? {};
6 | return {
7 | canAdmin: currentUser && currentUser.access === 'admin',
8 | };
9 | }
10 |
--------------------------------------------------------------------------------
/src/app.tsx:
--------------------------------------------------------------------------------
1 | import Footer from '@/components/Footer';
2 | import { SelectLang } from '@/components/RightContent';
3 | import type { Settings as LayoutSettings } from '@ant-design/pro-components';
4 | import { SettingDrawer } from '@ant-design/pro-components';
5 | import type { RunTimeLayoutConfig } from '@umijs/max';
6 | import { history } from '@umijs/max';
7 | import defaultSettings from '../config/defaultSettings';
8 | import { AvatarDropdown, AvatarName } from './components/RightContent/AvatarDropdown';
9 | import { errorConfig } from './requestErrorConfig';
10 | import { currentUser as queryCurrentUser } from './services/ant-design-pro/api';
11 | const isDev = process.env.NODE_ENV === 'development';
12 | const loginPath = '/user/login';
13 |
14 | /**
15 | * @see https://umijs.org/zh-CN/plugins/plugin-initial-state
16 | * */
17 | export async function getInitialState(): Promise<{
18 | settings?: Partial;
19 | currentUser?: API.CurrentUser;
20 | loading?: boolean;
21 | fetchUserInfo?: () => Promise;
22 | }> {
23 | const fetchUserInfo = async () => {
24 | try {
25 | const msg = await queryCurrentUser({
26 | skipErrorHandler: true,
27 | });
28 | return msg.data;
29 | } catch (error) {
30 | history.push(loginPath);
31 | }
32 | return undefined;
33 | };
34 | // 如果不是登录页面,执行
35 | const { location } = history;
36 | if (location.pathname !== loginPath) {
37 | const currentUser = await fetchUserInfo();
38 | return {
39 | fetchUserInfo,
40 | currentUser,
41 | settings: defaultSettings as Partial,
42 | };
43 | }
44 | return {
45 | fetchUserInfo,
46 | settings: defaultSettings as Partial,
47 | };
48 | }
49 |
50 | // ProLayout 支持的api https://procomponents.ant.design/components/layout
51 | export const layout: RunTimeLayoutConfig = ({ initialState, setInitialState }) => {
52 | return {
53 | actionsRender: () => [ ],
54 | avatarProps: {
55 | src: initialState?.currentUser?.avatar,
56 | title: ,
57 | render: (_, avatarChildren) => {
58 | return {avatarChildren} ;
59 | },
60 | },
61 | waterMarkProps: {
62 | content: initialState?.currentUser?.name,
63 | },
64 | footerRender: () => ,
65 | onPageChange: () => {
66 | const { location } = history;
67 | // 如果没有登录,重定向到 login
68 | if (!initialState?.currentUser && location.pathname !== loginPath) {
69 | history.push(loginPath);
70 | }
71 | },
72 | layoutBgImgList: [
73 | {
74 | src: 'https://mdn.alipayobjects.com/yuyan_qk0oxh/afts/img/D2LWSqNny4sAAAAAAAAAAAAAFl94AQBr',
75 | left: 85,
76 | bottom: 100,
77 | height: '303px',
78 | },
79 | {
80 | src: 'https://mdn.alipayobjects.com/yuyan_qk0oxh/afts/img/C2TWRpJpiC0AAAAAAAAAAAAAFl94AQBr',
81 | bottom: -68,
82 | right: -45,
83 | height: '303px',
84 | },
85 | {
86 | src: 'https://mdn.alipayobjects.com/yuyan_qk0oxh/afts/img/F6vSTbj8KpYAAAAAAAAAAAAAFl94AQBr',
87 | bottom: 0,
88 | left: 0,
89 | width: '331px',
90 | },
91 | ],
92 | links: [],
93 | menuHeaderRender: undefined,
94 | // 自定义 403 页面
95 | // unAccessible: unAccessible
,
96 | // 增加一个 loading 的状态
97 | childrenRender: (children) => {
98 | // if (initialState?.loading) return ;
99 | return (
100 | <>
101 | {children}
102 | {
107 | setInitialState((preInitialState) => ({
108 | ...preInitialState,
109 | settings,
110 | }));
111 | }}
112 | />
113 | >
114 | );
115 | },
116 | ...initialState?.settings,
117 | };
118 | };
119 |
120 | /**
121 | * @name request 配置,可以配置错误处理
122 | * 它基于 axios 和 ahooks 的 useRequest 提供了一套统一的网络请求和错误处理方案。
123 | * @doc https://umijs.org/docs/max/request#配置
124 | */
125 | export const request = {
126 | ...errorConfig,
127 | };
128 |
--------------------------------------------------------------------------------
/src/components/Footer/index.tsx:
--------------------------------------------------------------------------------
1 | import { GithubOutlined } from '@ant-design/icons';
2 | import { DefaultFooter } from '@ant-design/pro-components';
3 | import { useIntl } from '@umijs/max';
4 | import React from 'react';
5 |
6 | const Footer: React.FC = () => {
7 | const intl = useIntl();
8 | const defaultMessage = intl.formatMessage({
9 | id: 'app.copyright.produced',
10 | defaultMessage: 'Midjourney Proxy Admin',
11 | });
12 |
13 | const currentYear = new Date().getFullYear();
14 |
15 | return (
16 | ,
31 | href: 'https://github.com/novicezk/midjourney-proxy',
32 | blankTarget: true,
33 | },
34 | {
35 | key: 'Midjourney Proxy Plus',
36 | title: 'Midjourney Proxy Plus',
37 | href: 'https://github.com/litter-coder/midjourney-proxy-plus',
38 | blankTarget: true,
39 | },
40 | ]}
41 | />
42 | );
43 | };
44 |
45 | export default Footer;
46 |
--------------------------------------------------------------------------------
/src/components/HeaderDropdown/index.tsx:
--------------------------------------------------------------------------------
1 | import { Dropdown } from 'antd';
2 | import type { DropDownProps } from 'antd/es/dropdown';
3 | import React from 'react';
4 | import { useEmotionCss } from '@ant-design/use-emotion-css';
5 | import classNames from 'classnames';
6 |
7 | export type HeaderDropdownProps = {
8 | overlayClassName?: string;
9 | placement?: 'bottomLeft' | 'bottomRight' | 'topLeft' | 'topCenter' | 'topRight' | 'bottomCenter';
10 | } & Omit;
11 |
12 | const HeaderDropdown: React.FC = ({ overlayClassName: cls, ...restProps }) => {
13 | const className = useEmotionCss(({ token }) => {
14 | return {
15 | [`@media screen and (max-width: ${token.screenXS})`]: {
16 | width: '100%',
17 | },
18 | };
19 | });
20 | return ;
21 | };
22 |
23 | export default HeaderDropdown;
24 |
--------------------------------------------------------------------------------
/src/components/RightContent/AvatarDropdown.tsx:
--------------------------------------------------------------------------------
1 | import { outLogin } from '@/services/ant-design-pro/api';
2 | import { LogoutOutlined, SettingOutlined, UserOutlined } from '@ant-design/icons';
3 | import { useEmotionCss } from '@ant-design/use-emotion-css';
4 | import { history, useModel } from '@umijs/max';
5 | import { Spin } from 'antd';
6 | import { stringify } from 'querystring';
7 | import type { MenuInfo } from 'rc-menu/lib/interface';
8 | import React, { useCallback } from 'react';
9 | import { flushSync } from 'react-dom';
10 | import HeaderDropdown from '../HeaderDropdown';
11 |
12 | export type GlobalHeaderRightProps = {
13 | menu?: boolean;
14 | children?: React.ReactNode;
15 | };
16 |
17 | export const AvatarName = () => {
18 | const { initialState } = useModel('@@initialState');
19 | const { currentUser } = initialState || {};
20 | return {currentUser?.name} ;
21 | };
22 |
23 | export const AvatarDropdown: React.FC = ({ menu, children }) => {
24 | /**
25 | * 退出登录,并且将当前的 url 保存
26 | */
27 | const loginOut = async () => {
28 | await outLogin();
29 | const { search, pathname } = window.location;
30 | const urlParams = new URL(window.location.href).searchParams;
31 | /** 此方法会跳转到 redirect 参数所在的位置 */
32 | const redirect = urlParams.get('redirect');
33 | // Note: There may be security issues, please note
34 | if (window.location.pathname !== '/user/login' && !redirect) {
35 | history.replace({
36 | pathname: '/user/login',
37 | search: stringify({
38 | redirect: pathname + search,
39 | }),
40 | });
41 | }
42 | };
43 | const actionClassName = useEmotionCss(({ token }) => {
44 | return {
45 | display: 'flex',
46 | height: '48px',
47 | marginLeft: 'auto',
48 | overflow: 'hidden',
49 | alignItems: 'center',
50 | padding: '0 8px',
51 | cursor: 'pointer',
52 | borderRadius: token.borderRadius,
53 | '&:hover': {
54 | backgroundColor: token.colorBgTextHover,
55 | },
56 | };
57 | });
58 | const { initialState, setInitialState } = useModel('@@initialState');
59 |
60 | const onMenuClick = useCallback(
61 | (event: MenuInfo) => {
62 | const { key } = event;
63 | if (key === 'logout') {
64 | flushSync(() => {
65 | setInitialState((s) => ({ ...s, currentUser: undefined }));
66 | });
67 | loginOut();
68 | return;
69 | }
70 | history.push(`/account/${key}`);
71 | },
72 | [setInitialState],
73 | );
74 |
75 | const loading = (
76 |
77 |
84 |
85 | );
86 |
87 | if (!initialState) {
88 | return loading;
89 | }
90 |
91 | const { currentUser } = initialState;
92 |
93 | if (!currentUser || !currentUser.name) {
94 | return loading;
95 | }
96 |
97 | const menuItems = [
98 | ...(menu
99 | ? [
100 | {
101 | key: 'center',
102 | icon: ,
103 | label: '个人中心',
104 | },
105 | {
106 | key: 'settings',
107 | icon: ,
108 | label: '个人设置',
109 | },
110 | {
111 | type: 'divider' as const,
112 | },
113 | ]
114 | : []),
115 | {
116 | key: 'logout',
117 | icon: ,
118 | label: '退出登录',
119 | },
120 | ];
121 |
122 | return (
123 |
130 | {children}
131 |
132 | );
133 | };
134 |
--------------------------------------------------------------------------------
/src/components/RightContent/index.tsx:
--------------------------------------------------------------------------------
1 | import { QuestionCircleOutlined } from '@ant-design/icons';
2 | import { SelectLang as UmiSelectLang } from '@umijs/max';
3 | import React from 'react';
4 |
5 | export type SiderTheme = 'light' | 'dark';
6 |
7 | export const SelectLang = () => {
8 | return (
9 |
14 | );
15 | };
16 |
17 | export const Question = () => {
18 | return (
19 | {
25 | window.open('https://pro.ant.design/docs/getting-started');
26 | }}
27 | >
28 |
29 |
30 | );
31 | };
32 |
--------------------------------------------------------------------------------
/src/global.less:
--------------------------------------------------------------------------------
1 | html,
2 | body,
3 | #root {
4 | height: 100%;
5 | margin: 0;
6 | padding: 0;
7 | font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial,
8 | 'Noto Sans', sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol',
9 | 'Noto Color Emoji';
10 | }
11 |
12 | .colorWeak {
13 | filter: invert(80%);
14 | }
15 |
16 | .ant-layout {
17 | min-height: 100vh;
18 | }
19 | .ant-pro-sider.ant-layout-sider.ant-pro-sider-fixed {
20 | left: unset;
21 | }
22 |
23 | canvas {
24 | display: block;
25 | }
26 |
27 | body {
28 | text-rendering: optimizeLegibility;
29 | -webkit-font-smoothing: antialiased;
30 | -moz-osx-font-smoothing: grayscale;
31 | }
32 |
33 | ul,
34 | ol {
35 | list-style: none;
36 | }
37 |
38 | @media (max-width: 768px) {
39 | .ant-table {
40 | width: 100%;
41 | overflow-x: auto;
42 | &-thead > tr,
43 | &-tbody > tr {
44 | > th,
45 | > td {
46 | white-space: pre;
47 | > span {
48 | display: block;
49 | }
50 | }
51 | }
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/src/global.tsx:
--------------------------------------------------------------------------------
1 | import { useIntl } from '@umijs/max';
2 | import { Button, message, notification } from 'antd';
3 | import defaultSettings from '../config/defaultSettings';
4 |
5 | const { pwa } = defaultSettings;
6 | const isHttps = document.location.protocol === 'https:';
7 |
8 | const clearCache = () => {
9 | // remove all caches
10 | if (window.caches) {
11 | caches
12 | .keys()
13 | .then((keys) => {
14 | keys.forEach((key) => {
15 | caches.delete(key);
16 | });
17 | })
18 | .catch((e) => console.log(e));
19 | }
20 | };
21 |
22 | // if pwa is true
23 | if (pwa) {
24 | // Notify user if offline now
25 | window.addEventListener('sw.offline', () => {
26 | message.warning(useIntl().formatMessage({ id: 'app.pwa.offline' }));
27 | });
28 |
29 | // Pop up a prompt on the page asking the user if they want to use the latest version
30 | window.addEventListener('sw.updated', (event: Event) => {
31 | const e = event as CustomEvent;
32 | const reloadSW = async () => {
33 | // Check if there is sw whose state is waiting in ServiceWorkerRegistration
34 | // https://developer.mozilla.org/en-US/docs/Web/API/ServiceWorkerRegistration
35 | const worker = e.detail && e.detail.waiting;
36 | if (!worker) {
37 | return true;
38 | }
39 | // Send skip-waiting event to waiting SW with MessageChannel
40 | await new Promise((resolve, reject) => {
41 | const channel = new MessageChannel();
42 | channel.port1.onmessage = (msgEvent) => {
43 | if (msgEvent.data.error) {
44 | reject(msgEvent.data.error);
45 | } else {
46 | resolve(msgEvent.data);
47 | }
48 | };
49 | worker.postMessage({ type: 'skip-waiting' }, [channel.port2]);
50 | });
51 |
52 | clearCache();
53 | window.location.reload();
54 | return true;
55 | };
56 | const key = `open${Date.now()}`;
57 | const btn = (
58 | {
61 | notification.destroy(key);
62 | reloadSW();
63 | }}
64 | >
65 | {useIntl().formatMessage({ id: 'app.pwa.serviceworker.updated.ok' })}
66 |
67 | );
68 | notification.open({
69 | message: useIntl().formatMessage({ id: 'app.pwa.serviceworker.updated' }),
70 | description: useIntl().formatMessage({ id: 'app.pwa.serviceworker.updated.hint' }),
71 | btn,
72 | key,
73 | onClose: async () => null,
74 | });
75 | });
76 | } else if ('serviceWorker' in navigator && isHttps) {
77 | // unregister service worker
78 | const { serviceWorker } = navigator;
79 | if (serviceWorker.getRegistrations) {
80 | serviceWorker.getRegistrations().then((sws) => {
81 | sws.forEach((sw) => {
82 | sw.unregister();
83 | });
84 | });
85 | }
86 | serviceWorker.getRegistration().then((sw) => {
87 | if (sw) sw.unregister();
88 | });
89 |
90 | clearCache();
91 | }
92 |
--------------------------------------------------------------------------------
/src/locales/en-US.ts:
--------------------------------------------------------------------------------
1 | import component from './en-US/component';
2 | import globalHeader from './en-US/globalHeader';
3 | import menu from './en-US/menu';
4 | import pages from './en-US/pages';
5 | import pwa from './en-US/pwa';
6 | import settingDrawer from './en-US/settingDrawer';
7 | import settings from './en-US/settings';
8 |
9 | export default {
10 | 'navBar.lang': 'Languages',
11 | 'layout.user.link.help': 'Help',
12 | 'layout.user.link.privacy': 'Privacy',
13 | 'layout.user.link.terms': 'Terms',
14 | 'app.copyright.produced': 'Produced by Ant Financial Experience Department',
15 | 'app.preview.down.block': 'Download this page to your local project',
16 | 'app.welcome.link.fetch-blocks': 'Get all block',
17 | 'app.welcome.link.block-list': 'Quickly build standard, pages based on `block` development',
18 | ...globalHeader,
19 | ...menu,
20 | ...settingDrawer,
21 | ...settings,
22 | ...pwa,
23 | ...component,
24 | ...pages,
25 | };
26 |
--------------------------------------------------------------------------------
/src/locales/en-US/component.ts:
--------------------------------------------------------------------------------
1 | export default {
2 | 'component.tagSelect.expand': 'Expand',
3 | 'component.tagSelect.collapse': 'Collapse',
4 | 'component.tagSelect.all': 'All',
5 | };
6 |
--------------------------------------------------------------------------------
/src/locales/en-US/globalHeader.ts:
--------------------------------------------------------------------------------
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 | };
18 |
--------------------------------------------------------------------------------
/src/locales/en-US/menu.ts:
--------------------------------------------------------------------------------
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.account-list': 'Account Manager',
27 | 'menu.task-list': 'Task Search',
28 | 'menu.list.basic-list': 'Basic List',
29 | 'menu.list.card-list': 'Card List',
30 | 'menu.list.search-list': 'Search List',
31 | 'menu.list.search-list.articles': 'Search List(articles)',
32 | 'menu.list.search-list.projects': 'Search List(projects)',
33 | 'menu.list.search-list.applications': 'Search List(applications)',
34 | 'menu.profile': 'Profile',
35 | 'menu.profile.basic': 'Basic Profile',
36 | 'menu.profile.advanced': 'Advanced Profile',
37 | 'menu.result': 'Result',
38 | 'menu.result.success': 'Success',
39 | 'menu.result.fail': 'Fail',
40 | 'menu.exception': 'Exception',
41 | 'menu.exception.not-permission': '403',
42 | 'menu.exception.not-find': '404',
43 | 'menu.exception.server-error': '500',
44 | 'menu.exception.trigger': 'Trigger',
45 | 'menu.account': 'Account',
46 | 'menu.account.center': 'Account Center',
47 | 'menu.account.settings': 'Account Settings',
48 | 'menu.account.trigger': 'Trigger Error',
49 | 'menu.account.logout': 'Logout',
50 | 'menu.editor': 'Graphic Editor',
51 | 'menu.editor.flow': 'Flow Editor',
52 | 'menu.editor.mind': 'Mind Editor',
53 | 'menu.editor.koni': 'Koni Editor',
54 | };
55 |
--------------------------------------------------------------------------------
/src/locales/en-US/pages.ts:
--------------------------------------------------------------------------------
1 | export default {
2 | 'pages.layouts.userLayout.title':
3 | 'Midjourney Proxy is the most influential midjourney design specification in github',
4 | 'pages.login.accountLogin.tab': 'Account Login',
5 | 'pages.login.accountLogin.errorMessage': 'Incorrect username/password(admin/ant.design)',
6 | 'pages.login.failure': 'Login failed, please try again!',
7 | 'pages.login.success': 'Login successful!',
8 | 'pages.login.username.placeholder': 'Username: admin',
9 | 'pages.login.username.required': 'Please input your username!',
10 | 'pages.login.password.placeholder': 'Password: 123456',
11 | 'pages.login.password.required': 'Please input your password!',
12 | 'pages.login.phoneLogin.tab': 'Phone Login',
13 | 'pages.login.phoneLogin.errorMessage': 'Verification Code Error',
14 | 'pages.login.phoneNumber.placeholder': 'Phone Number',
15 | 'pages.login.phoneNumber.required': 'Please input your phone number!',
16 | 'pages.login.phoneNumber.invalid': 'Phone number is invalid!',
17 | 'pages.login.captcha.placeholder': 'Verification Code',
18 | 'pages.login.captcha.required': 'Please input verification code!',
19 | 'pages.login.phoneLogin.getVerificationCode': 'Get Code',
20 | 'pages.getCaptchaSecondText': 'sec(s)',
21 | 'pages.login.rememberMe': 'Remember me',
22 | 'pages.login.forgotPassword': 'Forgot Password ?',
23 | 'pages.login.submit': 'Login',
24 | 'pages.login.loginWith': 'Login with :',
25 | 'pages.login.registerAccount': 'Register Account',
26 | 'pages.welcome.link': 'Welcome',
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': 'batch deletion',
67 | 'pages.searchTable.batchApproval': 'batch approval',
68 | };
69 |
--------------------------------------------------------------------------------
/src/locales/en-US/pwa.ts:
--------------------------------------------------------------------------------
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 | };
7 |
--------------------------------------------------------------------------------
/src/locales/en-US/settingDrawer.ts:
--------------------------------------------------------------------------------
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 | };
32 |
--------------------------------------------------------------------------------
/src/locales/en-US/settings.ts:
--------------------------------------------------------------------------------
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 | };
61 |
--------------------------------------------------------------------------------
/src/locales/zh-CN.ts:
--------------------------------------------------------------------------------
1 | import component from './zh-CN/component';
2 | import globalHeader from './zh-CN/globalHeader';
3 | import menu from './zh-CN/menu';
4 | import pages from './zh-CN/pages';
5 | import pwa from './zh-CN/pwa';
6 | import settingDrawer from './zh-CN/settingDrawer';
7 | import settings from './zh-CN/settings';
8 |
9 | export default {
10 | 'navBar.lang': '语言',
11 | 'layout.user.link.help': '帮助',
12 | 'layout.user.link.privacy': '隐私',
13 | 'layout.user.link.terms': '条款',
14 | 'app.copyright.produced': 'Midjourney Proxy Admin',
15 | 'app.preview.down.block': '下载此页面到本地项目',
16 | 'app.welcome.link.fetch-blocks': '获取全部区块',
17 | 'app.welcome.link.block-list': '基于 block 开发,快速构建标准页面',
18 | ...pages,
19 | ...globalHeader,
20 | ...menu,
21 | ...settingDrawer,
22 | ...settings,
23 | ...pwa,
24 | ...component,
25 | };
26 |
--------------------------------------------------------------------------------
/src/locales/zh-CN/component.ts:
--------------------------------------------------------------------------------
1 | export default {
2 | 'component.tagSelect.expand': '展开',
3 | 'component.tagSelect.collapse': '收起',
4 | 'component.tagSelect.all': '全部',
5 | };
6 |
--------------------------------------------------------------------------------
/src/locales/zh-CN/globalHeader.ts:
--------------------------------------------------------------------------------
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 | };
18 |
--------------------------------------------------------------------------------
/src/locales/zh-CN/menu.ts:
--------------------------------------------------------------------------------
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.account-list': '账户管理',
27 | 'menu.task-list': '任务查询',
28 | 'menu.list.basic-list': '标准列表',
29 | 'menu.list.card-list': '卡片列表',
30 | 'menu.list.search-list': '搜索列表',
31 | 'menu.list.search-list.articles': '搜索列表(文章)',
32 | 'menu.list.search-list.projects': '搜索列表(项目)',
33 | 'menu.list.search-list.applications': '搜索列表(应用)',
34 | 'menu.profile': '详情页',
35 | 'menu.profile.basic': '基础详情页',
36 | 'menu.profile.advanced': '高级详情页',
37 | 'menu.result': '结果页',
38 | 'menu.result.success': '成功页',
39 | 'menu.result.fail': '失败页',
40 | 'menu.exception': '异常页',
41 | 'menu.exception.not-permission': '403',
42 | 'menu.exception.not-find': '404',
43 | 'menu.exception.server-error': '500',
44 | 'menu.exception.trigger': '触发错误',
45 | 'menu.account': '个人页',
46 | 'menu.account.center': '个人中心',
47 | 'menu.account.settings': '个人设置',
48 | 'menu.account.trigger': '触发报错',
49 | 'menu.account.logout': '退出登录',
50 | 'menu.editor': '图形编辑器',
51 | 'menu.editor.flow': '流程编辑器',
52 | 'menu.editor.mind': '脑图编辑器',
53 | 'menu.editor.koni': '拓扑编辑器',
54 | };
55 |
--------------------------------------------------------------------------------
/src/locales/zh-CN/pages.ts:
--------------------------------------------------------------------------------
1 | export default {
2 | 'pages.layouts.userLayout.title': 'Midjourney Proxy 是GitHub最具影响力的 Midjourney开源项目',
3 | 'pages.login.accountLogin.tab': '账户密码登录',
4 | 'pages.login.accountLogin.errorMessage': '错误的用户名和密码(admin/ant.design)',
5 | 'pages.login.failure': '登录失败,请重试!',
6 | 'pages.login.success': '登录成功!',
7 | 'pages.login.username.placeholder': '用户名: admin',
8 | 'pages.login.username.required': '用户名是必填项!',
9 | 'pages.login.password.placeholder': '密码: 123456',
10 | 'pages.login.password.required': '密码是必填项!',
11 | 'pages.login.phoneLogin.tab': '手机号登录',
12 | 'pages.login.phoneLogin.errorMessage': '验证码错误',
13 | 'pages.login.phoneNumber.placeholder': '请输入手机号!',
14 | 'pages.login.phoneNumber.required': '手机号是必填项!',
15 | 'pages.login.phoneNumber.invalid': '不合法的手机号!',
16 | 'pages.login.captcha.placeholder': '请输入验证码!',
17 | 'pages.login.captcha.required': '验证码是必填项!',
18 | 'pages.login.phoneLogin.getVerificationCode': '获取验证码',
19 | 'pages.getCaptchaSecondText': '秒后重新获取',
20 | 'pages.login.rememberMe': '自动登录',
21 | 'pages.login.forgotPassword': '忘记密码 ?',
22 | 'pages.login.submit': '登录',
23 | 'pages.login.loginWith': '其他登录方式 :',
24 | 'pages.login.registerAccount': '注册账户',
25 | 'pages.welcome.link': '欢迎使用',
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 | };
66 |
--------------------------------------------------------------------------------
/src/locales/zh-CN/pwa.ts:
--------------------------------------------------------------------------------
1 | export default {
2 | 'app.pwa.offline': '当前处于离线状态',
3 | 'app.pwa.serviceworker.updated': '有新内容',
4 | 'app.pwa.serviceworker.updated.hint': '请点击“刷新”按钮或者手动刷新页面',
5 | 'app.pwa.serviceworker.updated.ok': '刷新',
6 | };
7 |
--------------------------------------------------------------------------------
/src/locales/zh-CN/settingDrawer.ts:
--------------------------------------------------------------------------------
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': '固定 Header',
21 | 'app.setting.fixedsidebar': '固定侧边菜单',
22 | 'app.setting.fixedsidebar.hint': '侧边菜单布局时可配置',
23 | 'app.setting.hideheader': '下滑时隐藏 Header',
24 | 'app.setting.hideheader.hint': '固定 Header 时可配置',
25 | 'app.setting.othersettings': '其他设置',
26 | 'app.setting.weakmode': '色弱模式',
27 | 'app.setting.copy': '拷贝设置',
28 | 'app.setting.copyinfo': '拷贝成功,请到 config/defaultSettings.js 中替换默认配置',
29 | 'app.setting.production.hint':
30 | '配置栏只在开发环境用于预览,生产环境不会展现,请拷贝后手动修改配置文件',
31 | };
32 |
--------------------------------------------------------------------------------
/src/locales/zh-CN/settings.ts:
--------------------------------------------------------------------------------
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 | 'app.settings.security.email': '备用邮箱',
34 | 'app.settings.security.email-description': '已绑定邮箱',
35 | 'app.settings.security.mfa': 'MFA 设备',
36 | 'app.settings.security.mfa-description': '未绑定 MFA 设备,绑定后,可以进行二次确认',
37 | 'app.settings.security.modify': '修改',
38 | 'app.settings.security.set': '设置',
39 | 'app.settings.security.bind': '绑定',
40 | 'app.settings.binding.taobao': '绑定淘宝',
41 | 'app.settings.binding.taobao-description': '当前未绑定淘宝账号',
42 | 'app.settings.binding.alipay': '绑定支付宝',
43 | 'app.settings.binding.alipay-description': '当前未绑定支付宝账号',
44 | 'app.settings.binding.dingding': '绑定钉钉',
45 | 'app.settings.binding.dingding-description': '当前未绑定钉钉账号',
46 | 'app.settings.binding.bind': '绑定',
47 | 'app.settings.notification.password': '账户密码',
48 | 'app.settings.notification.password-description': '其他用户的消息将以站内信的形式通知',
49 | 'app.settings.notification.messages': '系统消息',
50 | 'app.settings.notification.messages-description': '系统消息将以站内信的形式通知',
51 | 'app.settings.notification.todo': '待办任务',
52 | 'app.settings.notification.todo-description': '待办任务将以站内信的形式通知',
53 | 'app.settings.open': '开',
54 | 'app.settings.close': '关',
55 | };
56 |
--------------------------------------------------------------------------------
/src/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "Ant Design Pro",
3 | "short_name": "Ant Design Pro",
4 | "display": "standalone",
5 | "start_url": "./?utm_source=homescreen",
6 | "theme_color": "#002140",
7 | "background_color": "#001529",
8 | "icons": [
9 | {
10 | "src": "icons/icon-192x192.png",
11 | "sizes": "192x192"
12 | },
13 | {
14 | "src": "icons/icon-128x128.png",
15 | "sizes": "128x128"
16 | },
17 | {
18 | "src": "icons/icon-512x512.png",
19 | "sizes": "512x512"
20 | }
21 | ]
22 | }
23 |
--------------------------------------------------------------------------------
/src/pages/404.tsx:
--------------------------------------------------------------------------------
1 | import { history } from '@umijs/max';
2 | import { Button, Result } from 'antd';
3 | import React from 'react';
4 |
5 | const NoFoundPage: React.FC = () => (
6 | history.push('/')}>
12 | Back Home
13 |
14 | }
15 | />
16 | );
17 |
18 | export default NoFoundPage;
19 |
--------------------------------------------------------------------------------
/src/pages/AccountList/components/ColumnBuilder.tsx:
--------------------------------------------------------------------------------
1 | import EditContent from '@/pages/AccountList/components/contents/EditContent ';
2 | import MoreContent from '@/pages/AccountList/components/contents/MoreContent ';
3 | import { EditOutlined, ToolOutlined } from '@ant-design/icons';
4 | import { Button, FormInstance, Space, Tag } from 'antd';
5 | import { ColumnType } from 'antd/lib/table';
6 | import moment from 'moment';
7 | import DelButton from './button/DelButton';
8 | import SyncButton from './button/SyncButton';
9 |
10 | const ColumnBuilder = ({
11 | form,
12 | modalFooter,
13 | openModal,
14 | triggerRefreshAccount,
15 | handleEdit,
16 | }: {
17 | form: FormInstance;
18 | modalFooter: any;
19 | openModal: (title: string, content: any, footer: any, modalWidth: number) => void;
20 | triggerRefreshAccount: () => void;
21 | handleEdit: (values: Record) => void;
22 | }) => {
23 | const columns = [
24 | {
25 | title: '账号名',
26 | dataIndex: 'name',
27 | render: (text: string, record: Record) => (
28 | openModal('账户信息', , null, 1000)}>
29 | {text}
30 |
31 | ),
32 | } as ColumnType>,
33 | {
34 | title: '状态',
35 | dataIndex: 'enable',
36 | width: 100,
37 | align: 'center',
38 | render: (enable: boolean) => {
39 | let color = enable ? 'green' : 'volcano';
40 | let text = enable ? '启用' : '未启用';
41 | return {text} ;
42 | },
43 | } as ColumnType>,
44 | {
45 | title: 'Remix',
46 | dataIndex: 'remix',
47 | width: 100,
48 | align: 'center',
49 | render: (remix: boolean) => {
50 | let color = remix ? 'green' : 'volcano';
51 | let text = remix ? '开启' : '关闭';
52 | return {text} ;
53 | },
54 | } as ColumnType>,
55 | {
56 | title: '模式',
57 | dataIndex: 'mode',
58 | width: 120,
59 | align: 'center',
60 | render: (text: string, record: Record) => record['displays']['mode'],
61 | } as ColumnType>,
62 | {
63 | title: '快速时间剩余',
64 | dataIndex: 'fastTimeRemaining',
65 | } as ColumnType>,
66 | {
67 | title: '续订时间',
68 | dataIndex: 'renewDate',
69 | align: 'center',
70 | render: (renewData: number) => {
71 | return moment(renewData).format('YYYY-MM-DD HH:mm');
72 | },
73 | } as ColumnType>,
74 | {
75 | title: '操作',
76 | dataIndex: 'operation',
77 | key: 'operation',
78 | render: (value: any, record: Record) => {
79 | return (
80 |
81 |
82 | }
86 | onClick={() =>
87 | openModal(
88 | '修改账户',
89 | ,
90 | modalFooter,
91 | 500,
92 | )
93 | }
94 | />
95 | }
98 | onClick={() =>
99 | openModal(
100 | '更新账户并重连',
101 | ,
102 | modalFooter,
103 | 500,
104 | )
105 | }
106 | />
107 |
108 |
109 | );
110 | },
111 | } as ColumnType>,
112 | ];
113 |
114 | return columns;
115 | };
116 |
117 | export default ColumnBuilder;
118 |
--------------------------------------------------------------------------------
/src/pages/AccountList/components/Modal.tsx:
--------------------------------------------------------------------------------
1 | import { Modal } from 'antd';
2 |
3 | const MyModal = ({
4 | title,
5 | modalVisible,
6 | hideModal,
7 | modalContent,
8 | footer,
9 | modalWidth,
10 | }: {
11 | title: string;
12 | modalVisible: boolean;
13 | hideModal: () => void;
14 | modalContent: any;
15 | footer: any;
16 | modalWidth: number;
17 | }) => {
18 | return (
19 |
26 | {modalContent}
27 |
28 | );
29 | };
30 |
31 | export default MyModal;
32 |
--------------------------------------------------------------------------------
/src/pages/AccountList/components/button/DelButton.tsx:
--------------------------------------------------------------------------------
1 | import { deleteAccount } from '@/services/ant-design-pro/api';
2 | import { Button, Popconfirm } from 'antd';
3 | import { DeleteOutlined } from '@ant-design/icons';
4 | import React, { useState } from 'react';
5 |
6 | // 定义 DelButton 接受的 props 类型
7 | interface DelButtonProps {
8 | record: Record;
9 | onSuccess: () => void; // 新增成功回调函数
10 | }
11 |
12 | const DelButton: React.FC = ({ record, onSuccess }) => {
13 | const [open, setOpen] = useState(false);
14 | const [confirmLoading, setConfirmLoading] = useState(false);
15 |
16 | const showPopconfirm = () => {
17 | setOpen(true);
18 | };
19 |
20 | const handleOk = async () => {
21 | setConfirmLoading(true);
22 |
23 | // 假设你有一个名为deleteRecord的函数来执行API删除操作
24 | try {
25 | await deleteAccount(record.id);
26 | setOpen(false);
27 | if (onSuccess) {
28 | onSuccess();
29 | }
30 | } catch (error) {
31 | console.error(error);
32 | } finally {
33 | setConfirmLoading(false);
34 | }
35 | };
36 |
37 | const handleCancel = () => {
38 | setOpen(false);
39 | };
40 |
41 | return (
42 |
51 | } onClick={showPopconfirm}>
52 |
53 |
54 | );
55 | };
56 |
57 | export default DelButton;
58 |
--------------------------------------------------------------------------------
/src/pages/AccountList/components/button/SyncButton.tsx:
--------------------------------------------------------------------------------
1 | import { refreshAccount } from '@/services/ant-design-pro/api';
2 | import { Button, Popconfirm } from 'antd';
3 | import { SyncOutlined } from '@ant-design/icons';
4 | import React, { useState } from 'react';
5 |
6 | interface SyncButtonProps {
7 | record: Record;
8 | onSuccess: () => void; // 新增成功回调函数
9 | }
10 |
11 | const SyncButton: React.FC = ({ record, onSuccess }) => {
12 | const [open, setOpen] = useState(false);
13 | const [confirmLoading, setConfirmLoading] = useState(false);
14 |
15 | const showPopconfirm = () => {
16 | setOpen(true);
17 | };
18 |
19 | const handleOk = async () => {
20 | setConfirmLoading(true);
21 |
22 | try {
23 | await refreshAccount(record.id);
24 | setOpen(false);
25 | if (onSuccess) {
26 | onSuccess();
27 | }
28 | } catch (error) {
29 | console.error(error);
30 | } finally {
31 | setConfirmLoading(false);
32 | }
33 | };
34 |
35 | const handleCancel = () => {
36 | setOpen(false);
37 | };
38 |
39 | return (
40 |
48 | } onClick={showPopconfirm}>
49 | 同步
50 |
51 |
52 | );
53 | };
54 |
55 | export default SyncButton;
56 |
--------------------------------------------------------------------------------
/src/pages/AccountList/components/contents/AddContent .tsx:
--------------------------------------------------------------------------------
1 | import { Card, Col, Form, FormInstance, Input, InputNumber, Row } from 'antd';
2 | import { useEffect } from 'react';
3 |
4 | const AddContent = ({
5 | form,
6 | onSubmit,
7 | }: {
8 | form: FormInstance;
9 | onSubmit: (values: any) => void;
10 | }) => {
11 | // 使用 useEffect 来在组件挂载时设置表单的初始值
12 | useEffect(() => {
13 | form.setFieldsValue({
14 | sessionId: '9c4055428e13bcbf2248a6b36084c5f3',
15 | userAgent:
16 | 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/112.0.0.0 Safari/537.36',
17 | coreSize: 3,
18 | queueSize: 10,
19 | timeoutMinutes: 5,
20 | });
21 | });
22 |
23 | return (
24 |
79 | );
80 | };
81 |
82 | export default AddContent;
83 |
--------------------------------------------------------------------------------
/src/pages/AccountList/components/contents/EditContent .tsx:
--------------------------------------------------------------------------------
1 | import { Card, Form, FormInstance, Input, InputNumber } from 'antd';
2 | import { useEffect } from 'react';
3 |
4 | const EditorContent = ({
5 | form,
6 | onSubmit,
7 | record,
8 | }: {
9 | form: FormInstance;
10 | onSubmit: (values: any) => void;
11 | record: Record;
12 | }) => {
13 | // 当组件挂载或者record更新时,设置表单的初始值
14 | useEffect(() => {
15 | form.setFieldsValue(record);
16 | });
17 |
18 | return (
19 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 | );
43 | };
44 |
45 | export default EditorContent;
46 |
--------------------------------------------------------------------------------
/src/pages/AccountList/components/contents/MoreContent .tsx:
--------------------------------------------------------------------------------
1 | import { Card, Descriptions, Tag, Tooltip } from 'antd';
2 | import moment from 'moment';
3 |
4 | const MoreContent = ({ record }: { record: Record }) => {
5 | const getStatusTag = (enable: boolean, enableText: string, disableText: string) => {
6 | let color = enable ? 'green' : 'volcano';
7 | let text = enable ? enableText : disableText;
8 | return {text} ;
9 | };
10 |
11 | const changeDate = (date: string) => {
12 | return moment(date).format('YYYY-MM-DD HH:mm');
13 | };
14 |
15 | return (
16 | <>
17 |
18 |
19 | {record.name}
20 | {record.guildId}
21 | {record.channelId}
22 |
23 |
24 | {(record.userToken && record.userToken.substring(0, 12) + '...') || '未提供'}
25 |
26 |
27 |
28 |
29 | {(record.sessionId && record.sessionId.substring(0, 12) + '...') || '未提供'}
30 |
31 |
32 |
33 |
34 | {(record.userAgent && record.userAgent.substring(0, 12) + '...') || '未提供'}
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 | {getStatusTag(record.enable, '启用', '未启用')}
44 |
45 | {record.version}
46 | {record['displays']['mode']}
47 |
48 | {getStatusTag(record.remix, '开启', '关闭')}
49 |
50 |
51 | {getStatusTag(record.raw, '开启', '关闭')}
52 |
53 |
54 | {getStatusTag(record.publicMode, '是', '否')}
55 |
56 | {record.fastTimeRemaining}
57 | {record.relaxedUsage}
58 | {record.lifetimeUsage}
59 |
60 |
61 |
62 |
63 | {record.coreSize}
64 | {record.queueSize}
65 | {record.timeoutMinutes}
66 |
67 |
68 |
69 |
70 |
71 | {changeDate(record.dateCreated)}
72 | {record['displays']['stylize']}
73 |
74 | {record['displays']['variation']}
75 |
76 |
77 | {record['displays']['subscribePlan']}
78 |
79 | {record['displays']['billedWay']}
80 | {changeDate(record.renewDate)}
81 |
82 |
83 | >
84 | );
85 | };
86 |
87 | export default MoreContent;
88 |
--------------------------------------------------------------------------------
/src/pages/AccountList/index.less:
--------------------------------------------------------------------------------
1 | .tableToolbar{
2 | text-align: right;
3 | padding: 15px 0;
4 | }
5 |
--------------------------------------------------------------------------------
/src/pages/AccountList/index.tsx:
--------------------------------------------------------------------------------
1 | import ColumnBuilder from '@/pages/AccountList/components/ColumnBuilder';
2 | import AddContent from '@/pages/AccountList/components/contents/AddContent ';
3 | import MyModal from '@/pages/AccountList/components/Modal';
4 | import { createAccount, queryAccount, updateAccount } from '@/services/ant-design-pro/api';
5 | import { useIntl } from '@@/exports';
6 | import { ReloadOutlined, UserAddOutlined } from '@ant-design/icons';
7 | import { PageContainer } from '@ant-design/pro-components';
8 | import { Button, Card, Col, Form, Pagination, Row, Space, Table } from 'antd';
9 | import React, { useEffect, useState } from 'react';
10 | import styles from './index.less';
11 |
12 | const AccountList: React.FC = () => {
13 | // 初始化 dataSource 状态为空数组
14 | const [dataSource, setDataSource] = useState([]);
15 | const [modalVisible, setModalVisible] = useState(false);
16 | const [modalContent, setModalContent] = useState({});
17 | const [title, setTitle] = useState('');
18 | const [footer, setFooter] = useState({});
19 | const [modalWidth, setModalWidth] = useState(1000);
20 | const [refresh, setRefresh] = useState(0);
21 | const [form] = Form.useForm();
22 |
23 | const openModal = (title: string, content: any, footer: any, modalWidth: number) => {
24 | form.resetFields();
25 | setTitle(title);
26 | setModalContent(content);
27 | setFooter(footer);
28 | setModalWidth(modalWidth);
29 | setModalVisible(true);
30 | };
31 |
32 | const hideModal = () => {
33 | setModalContent({});
34 | setFooter({});
35 | setModalVisible(false);
36 | };
37 |
38 | const modalFooter = () => {
39 | return (
40 | <>
41 |
42 | 取消
43 |
44 | form.submit()}>
45 | 提交
46 |
47 | >
48 | );
49 | };
50 |
51 | const intl = useIntl();
52 | const defaultHeader = intl.formatMessage({
53 | id: 'menu.list.account-list',
54 | defaultMessage: 'Account Table',
55 | });
56 |
57 | // 定义一个 triggerRefresh 函数,使其增加 refresh 的值,从而触发重新渲染
58 | const triggerRefreshAccount = () => {
59 | setRefresh(refresh + 1);
60 | };
61 |
62 | const fetchData = async () => {
63 | const res = await queryAccount({});
64 | setDataSource(res.content);
65 | };
66 |
67 | const handleAdd = async (values: Record) => {
68 | await createAccount(values);
69 | triggerRefreshAccount();
70 | hideModal();
71 | };
72 |
73 | const handleEdit = async (values: Record) => {
74 | await updateAccount(values.id, values);
75 | triggerRefreshAccount();
76 | hideModal();
77 | };
78 |
79 | useEffect(() => {
80 | fetchData();
81 | }, [refresh]);
82 |
83 | const beforeLayout = () => {
84 | return (
85 |
86 |
87 | {defaultHeader}
88 |
89 |
90 |
91 | }
94 | onClick={() => {
95 | openModal(
96 | '新增账户',
97 | ,
98 | modalFooter,
99 | 1000,
100 | );
101 | }}
102 | >
103 | 添加
104 |
105 | }>
106 | 刷新
107 |
108 |
109 |
110 |
111 | );
112 | };
113 | const afterLayout = () => {
114 | return (
115 |
116 |
117 |
118 |
119 |
120 |
121 | );
122 | };
123 |
124 | return (
125 |
126 |
127 | {beforeLayout()}
128 |
140 | {afterLayout()}
141 |
142 |
150 |
151 | );
152 | };
153 |
154 | export default AccountList;
155 |
--------------------------------------------------------------------------------
/src/pages/Task/List/index.less:
--------------------------------------------------------------------------------
1 | .tableToolbar{
2 | text-align: right;
3 | padding: 15px 0;
4 | }
5 |
--------------------------------------------------------------------------------
/src/pages/Task/List/index.tsx:
--------------------------------------------------------------------------------
1 | import { queryTask } from '@/services/ant-design-pro/api';
2 | import { useIntl } from '@@/exports';
3 | import { PageContainer } from '@ant-design/pro-components';
4 | import { Button, Card, Col, Pagination, Row, Space, Table, Tag, Progress } from 'antd';
5 | import React, { useEffect, useState } from 'react';
6 | import styles from './index.less';
7 |
8 | const List: React.FC = () => {
9 | // 初始化 dataSource 状态为空数组
10 | const [dataSource, setDataSource] = useState([]);
11 |
12 | const intl = useIntl();
13 | const defaultHeader = intl.formatMessage({
14 | id: 'menu.task-list',
15 | defaultMessage: 'Task Table',
16 | });
17 |
18 | useEffect(() => {
19 | const fetchData = async () => {
20 | const res = await queryTask({});
21 | // 使用状态 setter 函数更新 dataSource
22 | setDataSource(res.content);
23 | };
24 |
25 | fetchData();
26 | }, []);
27 |
28 | const columns = [
29 | {
30 | title: '任务ID',
31 | dataIndex: 'id',
32 | width: 200,
33 | align: 'center'
34 | },
35 | {
36 | title: '类型',
37 | dataIndex: 'action',
38 | width: 120,
39 | align: 'center',
40 | render: (text, record) => record['displays']['action'],
41 | },
42 | {
43 | title: '提交时间',
44 | dataIndex: 'submitTime',
45 | width: 180,
46 | align: 'center',
47 | render: (text, record) => record['displays']['submitTime'],
48 | },
49 | {
50 | title: '状态',
51 | dataIndex: 'status',
52 | width: 120,
53 | align: 'center',
54 | render: (text, record) => {
55 | let color = 'default';
56 | if (text == 'NOT_START') {
57 | color = 'default';
58 | } else if (text == 'SUBMITTED') {
59 | color = 'lime';
60 | } else if (text == 'MODAL') {
61 | color = 'warning';
62 | } else if (text == 'IN_PROGRESS') {
63 | color = 'processing';
64 | } else if (text == 'FAILURE') {
65 | color = 'error';
66 | } else if (text == 'SUCCESS') {
67 | color = 'success';
68 | }
69 | return {record['displays']['status']}
70 | }
71 | },
72 | {
73 | title: '进度',
74 | dataIndex: 'progress',
75 | width: 130,
76 | align: 'center',
77 | showInfo: false,
78 | render: (text, record) => {
79 | let percent = 0;
80 | if (text && text.indexOf('%') > 0) {
81 | percent = parseInt(text.substring(0, text.indexOf('%')));
82 | }
83 | let status = 'normal';
84 | if (record['status'] == 'SUCCESS') {
85 | status = 'success';
86 | } else if (record['status'] == 'FAILURE') {
87 | status = 'exception';
88 | }
89 | return
90 | }
91 | },
92 | {
93 | title: '任务描述',
94 | dataIndex: 'description',
95 | ellipsis: true
96 | },
97 | ];
98 |
99 | const searchLayout = () => { };
100 | const afterLayout = () => {
101 | return (
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 | );
110 | };
111 |
112 | return (
113 |
114 | {searchLayout()}
115 |
116 |
117 | {afterLayout()}
118 |
119 |
120 | );
121 | };
122 |
123 | export default List;
124 |
--------------------------------------------------------------------------------
/src/pages/User/Login/index.tsx:
--------------------------------------------------------------------------------
1 | import Footer from '@/components/Footer';
2 | import { login } from '@/services/ant-design-pro/api';
3 | import { getFakeCaptcha } from '@/services/ant-design-pro/login';
4 | import {
5 | AlipayCircleOutlined,
6 | LockOutlined,
7 | MobileOutlined,
8 | TaobaoCircleOutlined,
9 | UserOutlined,
10 | WeiboCircleOutlined,
11 | } from '@ant-design/icons';
12 | import {
13 | LoginForm,
14 | ProFormCaptcha,
15 | ProFormCheckbox,
16 | ProFormText,
17 | } from '@ant-design/pro-components';
18 | import { useEmotionCss } from '@ant-design/use-emotion-css';
19 | import { FormattedMessage, Helmet, history, SelectLang, useIntl, useModel } from '@umijs/max';
20 | import { Alert, message, Tabs } from 'antd';
21 | import React, { useState } from 'react';
22 | import { flushSync } from 'react-dom';
23 | import Settings from '../../../../config/defaultSettings';
24 |
25 | const ActionIcons = () => {
26 | const langClassName = useEmotionCss(({ token }) => {
27 | return {
28 | marginLeft: '8px',
29 | color: 'rgba(0, 0, 0, 0.2)',
30 | fontSize: '24px',
31 | verticalAlign: 'middle',
32 | cursor: 'pointer',
33 | transition: 'color 0.3s',
34 | '&:hover': {
35 | color: token.colorPrimaryActive,
36 | },
37 | };
38 | });
39 |
40 | return (
41 | <>
42 |
43 |
44 |
45 | >
46 | );
47 | };
48 |
49 | const Lang = () => {
50 | const langClassName = useEmotionCss(({ token }) => {
51 | return {
52 | width: 42,
53 | height: 42,
54 | lineHeight: '42px',
55 | position: 'fixed',
56 | right: 16,
57 | borderRadius: token.borderRadius,
58 | ':hover': {
59 | backgroundColor: token.colorBgTextHover,
60 | },
61 | };
62 | });
63 |
64 | return (
65 |
66 | {SelectLang && }
67 |
68 | );
69 | };
70 |
71 | const LoginMessage: React.FC<{
72 | content: string;
73 | }> = ({ content }) => {
74 | return (
75 |
83 | );
84 | };
85 |
86 | const Login: React.FC = () => {
87 | const [userLoginState, setUserLoginState] = useState({});
88 | const [type, setType] = useState('account');
89 | const { initialState, setInitialState } = useModel('@@initialState');
90 |
91 | const containerClassName = useEmotionCss(() => {
92 | return {
93 | display: 'flex',
94 | flexDirection: 'column',
95 | height: '100vh',
96 | overflow: 'auto',
97 | backgroundImage:
98 | "url('https://mdn.alipayobjects.com/yuyan_qk0oxh/afts/img/V-_oS6r-i7wAAAAAAAAAAAAAFl94AQBr')",
99 | backgroundSize: '100% 100%',
100 | };
101 | });
102 |
103 | const intl = useIntl();
104 |
105 | const fetchUserInfo = async () => {
106 | const userInfo = await initialState?.fetchUserInfo?.();
107 | if (userInfo) {
108 | flushSync(() => {
109 | setInitialState((s) => ({
110 | ...s,
111 | currentUser: userInfo,
112 | }));
113 | });
114 | }
115 | };
116 |
117 | const handleSubmit = async (values: API.LoginParams) => {
118 | try {
119 | // 登录
120 | const msg = await login({ ...values, type });
121 | if (msg.status === 'ok') {
122 | const defaultLoginSuccessMessage = intl.formatMessage({
123 | id: 'pages.login.success',
124 | defaultMessage: '登录成功!',
125 | });
126 | message.success(defaultLoginSuccessMessage);
127 | await fetchUserInfo();
128 | const urlParams = new URL(window.location.href).searchParams;
129 | history.push(urlParams.get('redirect') || '/');
130 | return;
131 | }
132 | console.log(msg);
133 | // 如果失败去设置用户错误信息
134 | setUserLoginState(msg);
135 | } catch (error) {
136 | const defaultLoginFailureMessage = intl.formatMessage({
137 | id: 'pages.login.failure',
138 | defaultMessage: '登录失败,请重试!',
139 | });
140 | console.log(error);
141 | message.error(defaultLoginFailureMessage);
142 | }
143 | };
144 | const { status, type: loginType } = userLoginState;
145 |
146 | return (
147 |
148 |
149 |
150 | {intl.formatMessage({
151 | id: 'menu.login',
152 | defaultMessage: '登录页',
153 | })}
154 | - {Settings.title}
155 |
156 |
157 |
158 |
164 |
}
170 | title="Midjourney Proxy Admin"
171 | subTitle={intl.formatMessage({ id: 'pages.layouts.userLayout.title' })}
172 | initialValues={{
173 | autoLogin: true,
174 | }}
175 | onFinish={async (values) => {
176 | await handleSubmit(values as API.LoginParams);
177 | }}
178 | >
179 |
193 |
194 | {status === 'error' && loginType === 'account' && (
195 |
201 | )}
202 | {type === 'account' && (
203 | <>
204 |
,
209 | }}
210 | placeholder={intl.formatMessage({
211 | id: 'pages.login.username.placeholder',
212 | defaultMessage: '用户名: admin',
213 | })}
214 | rules={[
215 | {
216 | required: true,
217 | message: (
218 |
222 | ),
223 | },
224 | ]}
225 | />
226 |
,
231 | }}
232 | placeholder={intl.formatMessage({
233 | id: 'pages.login.password.placeholder',
234 | defaultMessage: '密码: 123456',
235 | })}
236 | rules={[
237 | {
238 | required: true,
239 | message: (
240 |
244 | ),
245 | },
246 | ]}
247 | />
248 | >
249 | )}
250 |
251 | {status === 'error' && loginType === 'mobile' &&
}
252 | {type === 'mobile' && (
253 | <>
254 |
,
258 | }}
259 | name="mobile"
260 | placeholder={intl.formatMessage({
261 | id: 'pages.login.phoneNumber.placeholder',
262 | defaultMessage: '手机号',
263 | })}
264 | rules={[
265 | {
266 | required: true,
267 | message: (
268 |
272 | ),
273 | },
274 | {
275 | pattern: /^1\d{10}$/,
276 | message: (
277 |
281 | ),
282 | },
283 | ]}
284 | />
285 |
,
289 | }}
290 | captchaProps={{
291 | size: 'large',
292 | }}
293 | placeholder={intl.formatMessage({
294 | id: 'pages.login.captcha.placeholder',
295 | defaultMessage: '请输入验证码',
296 | })}
297 | captchaTextRender={(timing, count) => {
298 | if (timing) {
299 | return `${count} ${intl.formatMessage({
300 | id: 'pages.getCaptchaSecondText',
301 | defaultMessage: '获取验证码',
302 | })}`;
303 | }
304 | return intl.formatMessage({
305 | id: 'pages.login.phoneLogin.getVerificationCode',
306 | defaultMessage: '获取验证码',
307 | });
308 | }}
309 | name="captcha"
310 | rules={[
311 | {
312 | required: true,
313 | message: (
314 |
318 | ),
319 | },
320 | ]}
321 | onGetCaptcha={async (phone) => {
322 | const result = await getFakeCaptcha({
323 | phone,
324 | });
325 | if (!result) {
326 | return;
327 | }
328 | message.success('获取验证码成功!验证码为:1234');
329 | }}
330 | />
331 | >
332 | )}
333 |
349 |
350 |
351 |
352 |
353 | );
354 | };
355 |
356 | export default Login;
357 |
--------------------------------------------------------------------------------
/src/pages/User/Login/login.test.tsx:
--------------------------------------------------------------------------------
1 | import { render, fireEvent, act } from '@testing-library/react';
2 | import React from 'react';
3 | import { TestBrowser } from '@@/testBrowser';
4 |
5 | // @ts-ignore
6 | import { startMock } from '@@/requestRecordMock';
7 |
8 | const waitTime = (time: number = 100) => {
9 | return new Promise((resolve) => {
10 | setTimeout(() => {
11 | resolve(true);
12 | }, time);
13 | });
14 | };
15 |
16 | let server: {
17 | close: () => void;
18 | };
19 |
20 | describe('Login Page', () => {
21 | beforeAll(async () => {
22 | server = await startMock({
23 | port: 8000,
24 | scene: 'login',
25 | });
26 | });
27 |
28 | afterAll(() => {
29 | server?.close();
30 | });
31 |
32 | it('should show login form', async () => {
33 | const historyRef = React.createRef();
34 | const rootContainer = render(
35 | ,
41 | );
42 |
43 | await rootContainer.findAllByText('Ant Design');
44 |
45 | act(() => {
46 | historyRef.current?.push('/user/login');
47 | });
48 |
49 | expect(rootContainer.baseElement?.querySelector('.ant-pro-form-login-desc')?.textContent).toBe(
50 | 'Ant Design is the most influential web design specification in Xihu district',
51 | );
52 |
53 | expect(rootContainer.asFragment()).toMatchSnapshot();
54 |
55 | rootContainer.unmount();
56 | });
57 |
58 | it('should login success', async () => {
59 | const historyRef = React.createRef();
60 | const rootContainer = render(
61 | ,
67 | );
68 |
69 | await rootContainer.findAllByText('Ant Design');
70 |
71 | const userNameInput = await rootContainer.findByPlaceholderText('Username: admin or user');
72 |
73 | act(() => {
74 | fireEvent.change(userNameInput, { target: { value: 'admin' } });
75 | });
76 |
77 | const passwordInput = await rootContainer.findByPlaceholderText('Password: ant.design');
78 |
79 | act(() => {
80 | fireEvent.change(passwordInput, { target: { value: 'ant.design' } });
81 | });
82 |
83 | await (await rootContainer.findByText('Login')).click();
84 |
85 | // 等待接口返回结果
86 | await waitTime(5000);
87 |
88 | await rootContainer.findAllByText('Ant Design Pro');
89 |
90 | expect(rootContainer.asFragment()).toMatchSnapshot();
91 |
92 | await waitTime(2000);
93 |
94 | rootContainer.unmount();
95 | });
96 | });
97 |
--------------------------------------------------------------------------------
/src/pages/Welcome.tsx:
--------------------------------------------------------------------------------
1 | import { PageContainer } from '@ant-design/pro-components';
2 | import { useModel } from '@umijs/max';
3 | import { Card, theme } from 'antd';
4 | import React from 'react';
5 |
6 | /**
7 | * 每个单独的卡片,为了复用样式抽成了组件
8 | * @param param0
9 | * @returns
10 | */
11 | const InfoCard: React.FC<{
12 | title: string;
13 | index: number;
14 | desc: string;
15 | href: string;
16 | }> = ({ title, href, index, desc }) => {
17 | const { useToken } = theme;
18 |
19 | const { token } = useToken();
20 |
21 | return (
22 |
35 |
42 |
56 | {index}
57 |
58 |
65 | {title}
66 |
67 |
68 |
77 | {desc}
78 |
79 |
80 | 了解更多 {'>'}
81 |
82 |
83 | );
84 | };
85 |
86 | const Welcome: React.FC = () => {
87 | const { token } = theme.useToken();
88 | const { initialState } = useModel('@@initialState');
89 | return (
90 |
91 |
102 |
111 |
117 | 欢迎使用 Midjourney Proxy Admin
118 |
119 |
129 | Midjourney Proxy Admin 是一个整合了 umi,Ant Design 和 ProComponents
130 | 的后台管理界面。致力于提供Midjourney Proxy的各种管理功能。
131 |
132 |
139 |
145 |
151 |
157 |
158 |
159 |
160 |
161 | );
162 | };
163 |
164 | export default Welcome;
165 |
--------------------------------------------------------------------------------
/src/requestErrorConfig.ts:
--------------------------------------------------------------------------------
1 | import type { RequestOptions } from '@@/plugin-request/request';
2 | import type { RequestConfig } from '@umijs/max';
3 | import { message, notification } from 'antd';
4 |
5 | const MJ_API_SECRET = process.env.UMI_APP_MJ_API_SECRET || '';
6 |
7 | // 错误处理方案: 错误类型
8 | enum ErrorShowType {
9 | SILENT = 0,
10 | WARN_MESSAGE = 1,
11 | ERROR_MESSAGE = 2,
12 | NOTIFICATION = 3,
13 | REDIRECT = 9,
14 | }
15 | // 与后端约定的响应数据格式
16 | interface ResponseStructure {
17 | success: boolean;
18 | data: any;
19 | errorCode?: number;
20 | errorMessage?: string;
21 | showType?: ErrorShowType;
22 | }
23 |
24 | /**
25 | * @name 错误处理
26 | * pro 自带的错误处理, 可以在这里做自己的改动
27 | * @doc https://umijs.org/docs/max/request#配置
28 | */
29 | export const errorConfig: RequestConfig = {
30 | // 错误处理: umi@3 的错误处理方案。
31 | errorConfig: {
32 | // 错误抛出
33 | errorThrower: (res) => {
34 | const { success, data, errorCode, errorMessage, showType } =
35 | res as unknown as ResponseStructure;
36 | if (!success) {
37 | const error: any = new Error(errorMessage);
38 | error.name = 'BizError';
39 | error.info = { errorCode, errorMessage, showType, data };
40 | throw error; // 抛出自制的错误
41 | }
42 | },
43 | // 错误接收及处理
44 | errorHandler: (error: any, opts: any) => {
45 | if (opts?.skipErrorHandler) throw error;
46 | // 我们的 errorThrower 抛出的错误。
47 | if (error.name === 'BizError') {
48 | const errorInfo: ResponseStructure | undefined = error.info;
49 | if (errorInfo) {
50 | const { errorMessage, errorCode } = errorInfo;
51 | switch (errorInfo.showType) {
52 | case ErrorShowType.SILENT:
53 | // do nothing
54 | break;
55 | case ErrorShowType.WARN_MESSAGE:
56 | message.warning(errorMessage);
57 | break;
58 | case ErrorShowType.ERROR_MESSAGE:
59 | message.error(errorMessage);
60 | break;
61 | case ErrorShowType.NOTIFICATION:
62 | notification.open({
63 | description: errorMessage,
64 | message: errorCode,
65 | });
66 | break;
67 | case ErrorShowType.REDIRECT:
68 | // TODO: redirect
69 | break;
70 | default:
71 | message.error(errorMessage);
72 | }
73 | }
74 | } else if (error.response) {
75 | // Axios 的错误
76 | // 请求成功发出且服务器也响应了状态码,但状态代码超出了 2xx 的范围
77 | message.error(`Response status:${error.response.status}`);
78 | } else if (error.request) {
79 | // 请求已经成功发起,但没有收到响应
80 | // \`error.request\` 在浏览器中是 XMLHttpRequest 的实例,
81 | // 而在node.js中是 http.ClientRequest 的实例
82 | message.error('None response! Please retry.');
83 | } else {
84 | // 发送请求时出了点问题
85 | message.error('Request error, please retry.');
86 | }
87 | },
88 | },
89 |
90 | // 请求拦截器
91 | requestInterceptors: [
92 | (config: RequestOptions) => {
93 | // 拦截请求配置,进行个性化处理。
94 | config.headers = {
95 | ...config.headers,
96 | 'mj-api-secret': MJ_API_SECRET, // 你的自定义头部
97 | };
98 | return { ...config };
99 | },
100 | ],
101 |
102 | // 响应拦截器
103 | responseInterceptors: [
104 | (response) => {
105 | // 拦截响应数据,进行个性化处理
106 | const { data } = response as unknown as ResponseStructure;
107 | if (data?.code) {
108 | if (data.code === 200 || data.code === 1) {
109 | message.success(data?.description || '请求失败!');
110 | } else {
111 | message.error(data?.description || '请求失败!');
112 | }
113 | }
114 | if (data?.success === false) {
115 | message.error('请求失败!');
116 | }
117 | return response;
118 | },
119 | ],
120 | };
121 |
--------------------------------------------------------------------------------
/src/service-worker.js:
--------------------------------------------------------------------------------
1 | /* eslint-disable no-restricted-globals */
2 | /* eslint-disable no-underscore-dangle */
3 | /* globals workbox */
4 | workbox.core.setCacheNameDetails({
5 | prefix: 'antd-pro',
6 | suffix: 'v5',
7 | });
8 | // Control all opened tabs ASAP
9 | workbox.clientsClaim();
10 |
11 | /**
12 | * Use precaching list generated by workbox in build process.
13 | * https://developers.google.com/web/tools/workbox/reference-docs/latest/workbox.precaching
14 | */
15 | workbox.precaching.precacheAndRoute(self.__precacheManifest || []);
16 |
17 | /**
18 | * Register a navigation route.
19 | * https://developers.google.com/web/tools/workbox/modules/workbox-routing#how_to_register_a_navigation_route
20 | */
21 | workbox.routing.registerNavigationRoute('/index.html');
22 |
23 | /**
24 | * Use runtime cache:
25 | * https://developers.google.com/web/tools/workbox/reference-docs/latest/workbox.routing#.registerRoute
26 | *
27 | * Workbox provides all common caching strategies including CacheFirst, NetworkFirst etc.
28 | * https://developers.google.com/web/tools/workbox/reference-docs/latest/workbox.strategies
29 | */
30 |
31 | /** Handle API requests */
32 | workbox.routing.registerRoute(/\/api\//, workbox.strategies.networkFirst());
33 |
34 | /** Handle third party requests */
35 | workbox.routing.registerRoute(
36 | /^https:\/\/gw\.alipayobjects\.com\//,
37 | workbox.strategies.networkFirst(),
38 | );
39 | workbox.routing.registerRoute(
40 | /^https:\/\/cdnjs\.cloudflare\.com\//,
41 | workbox.strategies.networkFirst(),
42 | );
43 | workbox.routing.registerRoute(/\/color.less/, workbox.strategies.networkFirst());
44 |
45 | /** Response to client after skipping waiting with MessageChannel */
46 | addEventListener('message', (event) => {
47 | const replyPort = event.ports[0];
48 | const message = event.data;
49 | if (replyPort && message && message.type === 'skip-waiting') {
50 | event.waitUntil(
51 | self.skipWaiting().then(
52 | () => {
53 | replyPort.postMessage({
54 | error: null,
55 | });
56 | },
57 | (error) => {
58 | replyPort.postMessage({
59 | error,
60 | });
61 | },
62 | ),
63 | );
64 | }
65 | });
66 |
--------------------------------------------------------------------------------
/src/services/ant-design-pro/api.ts:
--------------------------------------------------------------------------------
1 | // @ts-ignore
2 | /* eslint-disable */
3 | import { request } from '@umijs/max';
4 |
5 | /** 获取当前的用户 GET /api/currentUser */
6 | export async function currentUser(options?: { [key: string]: any }) {
7 | return request<{
8 | data: API.CurrentUser;
9 | }>('/api/currentUser', {
10 | method: 'GET',
11 | ...(options || {}),
12 | });
13 | }
14 |
15 | /** 退出登录接口 POST /api/login/outLogin */
16 | export async function outLogin(options?: { [key: string]: any }) {
17 | return request>('/api/login/outLogin', {
18 | method: 'POST',
19 | ...(options || {}),
20 | });
21 | }
22 |
23 | /** 登录接口 POST /api/login/account */
24 | export async function login(body: API.LoginParams, options?: { [key: string]: any }) {
25 | return request('/api/login/account', {
26 | method: 'POST',
27 | headers: {
28 | 'Content-Type': 'application/json',
29 | },
30 | data: body,
31 | ...(options || {}),
32 | });
33 | }
34 |
35 | /** 此处后端没有提供注释 GET /api/notices */
36 | export async function getNotices(options?: { [key: string]: any }) {
37 | return request('/api/notices', {
38 | method: 'GET',
39 | ...(options || {}),
40 | });
41 | }
42 |
43 | /** 获取规则列表 GET /api/rule */
44 | export async function rule(
45 | params: {
46 | // query
47 | /** 当前的页码 */
48 | current?: number;
49 | /** 页面的容量 */
50 | pageSize?: number;
51 | },
52 | options?: { [key: string]: any },
53 | ) {
54 | return request('/api/rule', {
55 | method: 'GET',
56 | params: {
57 | ...params,
58 | },
59 | ...(options || {}),
60 | });
61 | }
62 |
63 | /** 新建规则 PUT /api/rule */
64 | export async function updateRule(options?: { [key: string]: any }) {
65 | return request('/api/rule', {
66 | method: 'PUT',
67 | ...(options || {}),
68 | });
69 | }
70 |
71 | /** 新建规则 POST /api/rule */
72 | export async function addRule(options?: { [key: string]: any }) {
73 | return request('/api/rule', {
74 | method: 'POST',
75 | ...(options || {}),
76 | });
77 | }
78 |
79 | /** 删除规则 DELETE /api/rule */
80 | export async function removeRule(options?: { [key: string]: any }) {
81 | return request>('/api/rule', {
82 | method: 'DELETE',
83 | ...(options || {}),
84 | });
85 | }
86 |
87 | /** MJ 接口 */
88 |
89 | /** POST /mj/account/create */
90 | export async function createAccount(data: object, options?: { [key: string]: any }) {
91 | return request>('/mj/account/create', {
92 | method: 'POST',
93 | data: data,
94 | ...(options || {}),
95 | });
96 | }
97 |
98 | /** POST /mj/account/query */
99 | export async function queryAccount(data: object, options?: { [key: string]: any }) {
100 | return request>('/mj/account/query', {
101 | method: 'POST',
102 | data: data,
103 | ...(options || {}),
104 | });
105 | }
106 |
107 | /** POST /mj/account/{id}/sync-info */
108 | export async function refreshAccount(id: string, options?: { [key: string]: any }) {
109 | return request>(`/mj/account/${id}/sync-info`, {
110 | method: 'POST',
111 | ...(options || {}),
112 | });
113 | }
114 |
115 | /** PUT /mj/account/{id}/update */
116 | export async function updateAccount(id: string, data: object, options?: { [key: string]: any }) {
117 | return request>(`/mj/account/${id}/update`, {
118 | method: 'PUT',
119 | data: data,
120 | ...(options || {}),
121 | });
122 | }
123 |
124 | /** DELETE /mj/account/{id}/delete */
125 | export async function deleteAccount(id: string, options?: { [key: string]: any }) {
126 | return request>(`/mj/account/${id}/delete`, {
127 | method: 'DELETE',
128 | ...(options || {}),
129 | });
130 | }
131 |
132 | export async function queryTask(data: object, options?: { [key: string]: any }) {
133 | return request>('/mj/task-admin/query', {
134 | method: 'POST',
135 | data: data,
136 | ...(options || {}),
137 | });
138 | }
139 |
--------------------------------------------------------------------------------
/src/services/ant-design-pro/index.ts:
--------------------------------------------------------------------------------
1 | // @ts-ignore
2 | /* eslint-disable */
3 | // API 更新时间:
4 | // API 唯一标识:
5 | import * as api from './api';
6 | import * as login from './login';
7 | export default {
8 | api,
9 | login,
10 | };
11 |
--------------------------------------------------------------------------------
/src/services/ant-design-pro/login.ts:
--------------------------------------------------------------------------------
1 | // @ts-ignore
2 | /* eslint-disable */
3 | import { request } from '@umijs/max';
4 |
5 | /** 发送验证码 POST /api/login/captcha */
6 | export async function getFakeCaptcha(
7 | params: {
8 | // query
9 | /** 手机号 */
10 | phone?: string;
11 | },
12 | options?: { [key: string]: any },
13 | ) {
14 | return request('/api/login/captcha', {
15 | method: 'GET',
16 | params: {
17 | ...params,
18 | },
19 | ...(options || {}),
20 | });
21 | }
22 |
--------------------------------------------------------------------------------
/src/services/ant-design-pro/typings.d.ts:
--------------------------------------------------------------------------------
1 | // @ts-ignore
2 | /* eslint-disable */
3 |
4 | declare namespace API {
5 | type CurrentUser = {
6 | name?: string;
7 | avatar?: string;
8 | userid?: string;
9 | email?: string;
10 | signature?: string;
11 | title?: string;
12 | group?: string;
13 | tags?: { key?: string; label?: string }[];
14 | notifyCount?: number;
15 | unreadCount?: number;
16 | country?: string;
17 | access?: string;
18 | geographic?: {
19 | province?: { label?: string; key?: string };
20 | city?: { label?: string; key?: string };
21 | };
22 | address?: string;
23 | phone?: string;
24 | };
25 |
26 | type LoginResult = {
27 | status?: string;
28 | type?: string;
29 | currentAuthority?: string;
30 | };
31 |
32 | type PageParams = {
33 | current?: number;
34 | pageSize?: number;
35 | };
36 |
37 | type RuleListItem = {
38 | key?: number;
39 | disabled?: boolean;
40 | href?: string;
41 | avatar?: string;
42 | name?: string;
43 | owner?: string;
44 | desc?: string;
45 | callNo?: number;
46 | status?: number;
47 | updatedAt?: string;
48 | createdAt?: string;
49 | progress?: number;
50 | };
51 |
52 | type RuleList = {
53 | data?: RuleListItem[];
54 | /** 列表的内容总数 */
55 | total?: number;
56 | success?: boolean;
57 | };
58 |
59 | type FakeCaptcha = {
60 | code?: number;
61 | status?: string;
62 | };
63 |
64 | type LoginParams = {
65 | username?: string;
66 | password?: string;
67 | autoLogin?: boolean;
68 | type?: string;
69 | };
70 |
71 | type ErrorResponse = {
72 | /** 业务约定的错误码 */
73 | errorCode: string;
74 | /** 业务上的错误信息 */
75 | errorMessage?: string;
76 | /** 业务上的请求是否成功 */
77 | success?: boolean;
78 | };
79 |
80 | type NoticeIconList = {
81 | data?: NoticeIconItem[];
82 | /** 列表的内容总数 */
83 | total?: number;
84 | success?: boolean;
85 | };
86 |
87 | type NoticeIconItemType = 'notification' | 'message' | 'event';
88 |
89 | type NoticeIconItem = {
90 | id?: string;
91 | extra?: string;
92 | key?: string;
93 | read?: boolean;
94 | avatar?: string;
95 | title?: string;
96 | status?: string;
97 | datetime?: string;
98 | description?: string;
99 | type?: NoticeIconItemType;
100 | };
101 | }
102 |
--------------------------------------------------------------------------------
/src/services/swagger/index.ts:
--------------------------------------------------------------------------------
1 | // @ts-ignore
2 | /* eslint-disable */
3 | // API 更新时间:
4 | // API 唯一标识:
5 | import * as pet from './pet';
6 | import * as store from './store';
7 | import * as user from './user';
8 | export default {
9 | pet,
10 | store,
11 | user,
12 | };
13 |
--------------------------------------------------------------------------------
/src/services/swagger/pet.ts:
--------------------------------------------------------------------------------
1 | // @ts-ignore
2 | /* eslint-disable */
3 | import { request } from '@umijs/max';
4 |
5 | /** Update an existing pet PUT /pet */
6 | export async function updatePet(body: API.Pet, options?: { [key: string]: any }) {
7 | return request('/pet', {
8 | method: 'PUT',
9 | headers: {
10 | 'Content-Type': 'application/json',
11 | },
12 | data: body,
13 | ...(options || {}),
14 | });
15 | }
16 |
17 | /** Add a new pet to the store POST /pet */
18 | export async function addPet(body: API.Pet, options?: { [key: string]: any }) {
19 | return request('/pet', {
20 | method: 'POST',
21 | headers: {
22 | 'Content-Type': 'application/json',
23 | },
24 | data: body,
25 | ...(options || {}),
26 | });
27 | }
28 |
29 | /** Find pet by ID Returns a single pet GET /pet/${param0} */
30 | export async function getPetById(
31 | // 叠加生成的Param类型 (非body参数swagger默认没有生成对象)
32 | params: API.getPetByIdParams,
33 | options?: { [key: string]: any },
34 | ) {
35 | const { petId: param0, ...queryParams } = params;
36 | return request(`/pet/${param0}`, {
37 | method: 'GET',
38 | params: { ...queryParams },
39 | ...(options || {}),
40 | });
41 | }
42 |
43 | /** Updates a pet in the store with form data POST /pet/${param0} */
44 | export async function updatePetWithForm(
45 | // 叠加生成的Param类型 (非body参数swagger默认没有生成对象)
46 | params: API.updatePetWithFormParams,
47 | body: { name?: string; status?: string },
48 | options?: { [key: string]: any },
49 | ) {
50 | const { petId: param0, ...queryParams } = params;
51 | const formData = new FormData();
52 |
53 | Object.keys(body).forEach((ele) => {
54 | const item = (body as any)[ele];
55 |
56 | if (item !== undefined && item !== null) {
57 | formData.append(
58 | ele,
59 | typeof item === 'object' && !(item instanceof File) ? JSON.stringify(item) : item,
60 | );
61 | }
62 | });
63 |
64 | return request(`/pet/${param0}`, {
65 | method: 'POST',
66 | params: { ...queryParams },
67 | data: formData,
68 | ...(options || {}),
69 | });
70 | }
71 |
72 | /** Deletes a pet DELETE /pet/${param0} */
73 | export async function deletePet(
74 | // 叠加生成的Param类型 (非body参数swagger默认没有生成对象)
75 | params: API.deletePetParams & {
76 | // header
77 | api_key?: string;
78 | },
79 | options?: { [key: string]: any },
80 | ) {
81 | const { petId: param0, ...queryParams } = params;
82 | return request(`/pet/${param0}`, {
83 | method: 'DELETE',
84 | headers: {},
85 | params: { ...queryParams },
86 | ...(options || {}),
87 | });
88 | }
89 |
90 | /** uploads an image POST /pet/${param0}/uploadImage */
91 | export async function uploadFile(
92 | // 叠加生成的Param类型 (非body参数swagger默认没有生成对象)
93 | params: API.uploadFileParams,
94 | body: { additionalMetadata?: string; file?: string },
95 | file?: File,
96 | options?: { [key: string]: any },
97 | ) {
98 | const { petId: param0, ...queryParams } = params;
99 | const formData = new FormData();
100 |
101 | if (file) {
102 | formData.append('file', file);
103 | }
104 |
105 | Object.keys(body).forEach((ele) => {
106 | const item = (body as any)[ele];
107 |
108 | if (item !== undefined && item !== null) {
109 | formData.append(
110 | ele,
111 | typeof item === 'object' && !(item instanceof File) ? JSON.stringify(item) : item,
112 | );
113 | }
114 | });
115 |
116 | return request(`/pet/${param0}/uploadImage`, {
117 | method: 'POST',
118 | params: { ...queryParams },
119 | data: formData,
120 | requestType: 'form',
121 | ...(options || {}),
122 | });
123 | }
124 |
125 | /** Finds Pets by status Multiple status values can be provided with comma separated strings GET /pet/findByStatus */
126 | export async function findPetsByStatus(
127 | // 叠加生成的Param类型 (非body参数swagger默认没有生成对象)
128 | params: API.findPetsByStatusParams,
129 | options?: { [key: string]: any },
130 | ) {
131 | return request('/pet/findByStatus', {
132 | method: 'GET',
133 | params: {
134 | ...params,
135 | },
136 | ...(options || {}),
137 | });
138 | }
139 |
140 | /** Finds Pets by tags Muliple tags can be provided with comma separated strings. Use tag1, tag2, tag3 for testing. GET /pet/findByTags */
141 | export async function findPetsByTags(
142 | // 叠加生成的Param类型 (非body参数swagger默认没有生成对象)
143 | params: API.findPetsByTagsParams,
144 | options?: { [key: string]: any },
145 | ) {
146 | return request('/pet/findByTags', {
147 | method: 'GET',
148 | params: {
149 | ...params,
150 | },
151 | ...(options || {}),
152 | });
153 | }
154 |
--------------------------------------------------------------------------------
/src/services/swagger/store.ts:
--------------------------------------------------------------------------------
1 | // @ts-ignore
2 | /* eslint-disable */
3 | import { request } from '@umijs/max';
4 |
5 | /** Returns pet inventories by status Returns a map of status codes to quantities GET /store/inventory */
6 | export async function getInventory(options?: { [key: string]: any }) {
7 | return request>('/store/inventory', {
8 | method: 'GET',
9 | ...(options || {}),
10 | });
11 | }
12 |
13 | /** Place an order for a pet POST /store/order */
14 | export async function placeOrder(body: API.Order, options?: { [key: string]: any }) {
15 | return request('/store/order', {
16 | method: 'POST',
17 | data: body,
18 | ...(options || {}),
19 | });
20 | }
21 |
22 | /** Find purchase order by ID For valid response try integer IDs with value >= 1 and <= 10. Other values will generated exceptions GET /store/order/${param0} */
23 | export async function getOrderById(
24 | // 叠加生成的Param类型 (非body参数swagger默认没有生成对象)
25 | params: API.getOrderByIdParams,
26 | options?: { [key: string]: any },
27 | ) {
28 | const { orderId: param0, ...queryParams } = params;
29 | return request(`/store/order/${param0}`, {
30 | method: 'GET',
31 | params: { ...queryParams },
32 | ...(options || {}),
33 | });
34 | }
35 |
36 | /** Delete purchase order by ID For valid response try integer IDs with positive integer value. Negative or non-integer values will generate API errors DELETE /store/order/${param0} */
37 | export async function deleteOrder(
38 | // 叠加生成的Param类型 (非body参数swagger默认没有生成对象)
39 | params: API.deleteOrderParams,
40 | options?: { [key: string]: any },
41 | ) {
42 | const { orderId: param0, ...queryParams } = params;
43 | return request(`/store/order/${param0}`, {
44 | method: 'DELETE',
45 | params: { ...queryParams },
46 | ...(options || {}),
47 | });
48 | }
49 |
--------------------------------------------------------------------------------
/src/services/swagger/typings.d.ts:
--------------------------------------------------------------------------------
1 | declare namespace API {
2 | type ApiResponse = {
3 | code?: number;
4 | type?: string;
5 | message?: string;
6 | };
7 |
8 | type Category = {
9 | id?: number;
10 | name?: string;
11 | };
12 |
13 | type deleteOrderParams = {
14 | /** ID of the order that needs to be deleted */
15 | orderId: number;
16 | };
17 |
18 | type deletePetParams = {
19 | api_key?: string;
20 | /** Pet id to delete */
21 | petId: number;
22 | };
23 |
24 | type deleteUserParams = {
25 | /** The name that needs to be deleted */
26 | username: string;
27 | };
28 |
29 | type findPetsByStatusParams = {
30 | /** Status values that need to be considered for filter */
31 | status: ('available' | 'pending' | 'sold')[];
32 | };
33 |
34 | type findPetsByTagsParams = {
35 | /** Tags to filter by */
36 | tags: string[];
37 | };
38 |
39 | type getOrderByIdParams = {
40 | /** ID of pet that needs to be fetched */
41 | orderId: number;
42 | };
43 |
44 | type getPetByIdParams = {
45 | /** ID of pet to return */
46 | petId: number;
47 | };
48 |
49 | type getUserByNameParams = {
50 | /** The name that needs to be fetched. Use user1 for testing. */
51 | username: string;
52 | };
53 |
54 | type loginUserParams = {
55 | /** The user name for login */
56 | username: string;
57 | /** The password for login in clear text */
58 | password: string;
59 | };
60 |
61 | type Order = {
62 | id?: number;
63 | petId?: number;
64 | quantity?: number;
65 | shipDate?: string;
66 | /** Order Status */
67 | status?: 'placed' | 'approved' | 'delivered';
68 | complete?: boolean;
69 | };
70 |
71 | type Pet = {
72 | id?: number;
73 | category?: Category;
74 | name: string;
75 | photoUrls: string[];
76 | tags?: Tag[];
77 | /** pet status in the store */
78 | status?: 'available' | 'pending' | 'sold';
79 | };
80 |
81 | type Tag = {
82 | id?: number;
83 | name?: string;
84 | };
85 |
86 | type updatePetWithFormParams = {
87 | /** ID of pet that needs to be updated */
88 | petId: number;
89 | };
90 |
91 | type updateUserParams = {
92 | /** name that need to be updated */
93 | username: string;
94 | };
95 |
96 | type uploadFileParams = {
97 | /** ID of pet to update */
98 | petId: number;
99 | };
100 |
101 | type User = {
102 | id?: number;
103 | username?: string;
104 | firstName?: string;
105 | lastName?: string;
106 | email?: string;
107 | password?: string;
108 | phone?: string;
109 | /** User Status */
110 | userStatus?: number;
111 | };
112 | }
113 |
--------------------------------------------------------------------------------
/src/services/swagger/user.ts:
--------------------------------------------------------------------------------
1 | // @ts-ignore
2 | /* eslint-disable */
3 | import { request } from '@umijs/max';
4 |
5 | /** Create user This can only be done by the logged in user. POST /user */
6 | export async function createUser(body: API.User, options?: { [key: string]: any }) {
7 | return request('/user', {
8 | method: 'POST',
9 | data: body,
10 | ...(options || {}),
11 | });
12 | }
13 |
14 | /** Get user by user name GET /user/${param0} */
15 | export async function getUserByName(
16 | // 叠加生成的Param类型 (非body参数swagger默认没有生成对象)
17 | params: API.getUserByNameParams,
18 | options?: { [key: string]: any },
19 | ) {
20 | const { username: param0, ...queryParams } = params;
21 | return request(`/user/${param0}`, {
22 | method: 'GET',
23 | params: { ...queryParams },
24 | ...(options || {}),
25 | });
26 | }
27 |
28 | /** Updated user This can only be done by the logged in user. PUT /user/${param0} */
29 | export async function updateUser(
30 | // 叠加生成的Param类型 (非body参数swagger默认没有生成对象)
31 | params: API.updateUserParams,
32 | body: API.User,
33 | options?: { [key: string]: any },
34 | ) {
35 | const { username: param0, ...queryParams } = params;
36 | return request(`/user/${param0}`, {
37 | method: 'PUT',
38 | params: { ...queryParams },
39 | data: body,
40 | ...(options || {}),
41 | });
42 | }
43 |
44 | /** Delete user This can only be done by the logged in user. DELETE /user/${param0} */
45 | export async function deleteUser(
46 | // 叠加生成的Param类型 (非body参数swagger默认没有生成对象)
47 | params: API.deleteUserParams,
48 | options?: { [key: string]: any },
49 | ) {
50 | const { username: param0, ...queryParams } = params;
51 | return request(`/user/${param0}`, {
52 | method: 'DELETE',
53 | params: { ...queryParams },
54 | ...(options || {}),
55 | });
56 | }
57 |
58 | /** Creates list of users with given input array POST /user/createWithArray */
59 | export async function createUsersWithArrayInput(
60 | body: API.User[],
61 | options?: { [key: string]: any },
62 | ) {
63 | return request('/user/createWithArray', {
64 | method: 'POST',
65 | data: body,
66 | ...(options || {}),
67 | });
68 | }
69 |
70 | /** Creates list of users with given input array POST /user/createWithList */
71 | export async function createUsersWithListInput(body: API.User[], options?: { [key: string]: any }) {
72 | return request('/user/createWithList', {
73 | method: 'POST',
74 | data: body,
75 | ...(options || {}),
76 | });
77 | }
78 |
79 | /** Logs user into the system GET /user/login */
80 | export async function loginUser(
81 | // 叠加生成的Param类型 (非body参数swagger默认没有生成对象)
82 | params: API.loginUserParams,
83 | options?: { [key: string]: any },
84 | ) {
85 | return request('/user/login', {
86 | method: 'GET',
87 | params: {
88 | ...params,
89 | },
90 | ...(options || {}),
91 | });
92 | }
93 |
94 | /** Logs out current logged in user session GET /user/logout */
95 | export async function logoutUser(options?: { [key: string]: any }) {
96 | return request('/user/logout', {
97 | method: 'GET',
98 | ...(options || {}),
99 | });
100 | }
101 |
--------------------------------------------------------------------------------
/src/typings.d.ts:
--------------------------------------------------------------------------------
1 | declare module 'slash2';
2 | declare module '*.css';
3 | declare module '*.less';
4 | declare module '*.scss';
5 | declare module '*.sass';
6 | declare module '*.svg';
7 | declare module '*.png';
8 | declare module '*.jpg';
9 | declare module '*.jpeg';
10 | declare module '*.gif';
11 | declare module '*.bmp';
12 | declare module '*.tiff';
13 | declare module 'omit.js';
14 | declare module 'numeral';
15 | declare module '@antv/data-set';
16 | declare module 'mockjs';
17 | declare module 'react-fittext';
18 | declare module 'bizcharts-plugin-slider';
19 |
20 | declare const REACT_APP_ENV: 'test' | 'dev' | 'pre' | false;
21 |
--------------------------------------------------------------------------------
/tests/setupTests.jsx:
--------------------------------------------------------------------------------
1 | const localStorageMock = {
2 | getItem: jest.fn(),
3 | setItem: jest.fn(),
4 | removeItem: jest.fn(),
5 | clear: jest.fn(),
6 | };
7 |
8 | global.localStorage = localStorageMock;
9 |
10 | Object.defineProperty(URL, 'createObjectURL', {
11 | writable: true,
12 | value: jest.fn(),
13 | });
14 |
15 | class Worker {
16 | constructor(stringUrl) {
17 | this.url = stringUrl;
18 | this.onmessage = () => {};
19 | }
20 |
21 | postMessage(msg) {
22 | this.onmessage(msg);
23 | }
24 | }
25 | window.Worker = Worker;
26 |
27 | /* eslint-disable global-require */
28 | if (typeof window !== 'undefined') {
29 | // ref: https://github.com/ant-design/ant-design/issues/18774
30 | if (!window.matchMedia) {
31 | Object.defineProperty(global.window, 'matchMedia', {
32 | writable: true,
33 | configurable: true,
34 | value: jest.fn(() => ({
35 | matches: false,
36 | addListener: jest.fn(),
37 | removeListener: jest.fn(),
38 | })),
39 | });
40 | }
41 | if (!window.matchMedia) {
42 | Object.defineProperty(global.window, 'matchMedia', {
43 | writable: true,
44 | configurable: true,
45 | value: jest.fn((query) => ({
46 | matches: query.includes('max-width'),
47 | addListener: jest.fn(),
48 | removeListener: jest.fn(),
49 | })),
50 | });
51 | }
52 | }
53 | const errorLog = console.error;
54 | Object.defineProperty(global.window.console, 'error', {
55 | writable: true,
56 | configurable: true,
57 | value: (...rest) => {
58 | const logStr = rest.join('');
59 | if (logStr.includes('Warning: An update to %s inside a test was not wrapped in act(...)')) {
60 | return;
61 | }
62 | errorLog(...rest);
63 | },
64 | });
65 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "esnext",
4 | "module": "esnext",
5 | "moduleResolution": "node",
6 | "importHelpers": true,
7 | "jsx": "preserve",
8 | "esModuleInterop": true,
9 | "sourceMap": true,
10 | "baseUrl": "./",
11 | "skipLibCheck": true,
12 | "experimentalDecorators": true,
13 | "strict": true,
14 | "resolveJsonModule": true,
15 | "allowSyntheticDefaultImports": true,
16 | "paths": {
17 | "@/*": ["./src/*"],
18 | "@@/*": ["./src/.umi/*"],
19 | "@@test/*": ["./src/.umi-test/*"]
20 | }
21 | },
22 | "include": ["./**/*.d.ts", "./**/*.ts", "./**/*.tsx"]
23 | }
24 |
--------------------------------------------------------------------------------
/types/cache/cache.json:
--------------------------------------------------------------------------------
1 | {}
2 |
--------------------------------------------------------------------------------
/types/cache/mock/login.mock.cache.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | 'GET /api/currentUser': {
3 | data: {
4 | name: 'Serati Ma',
5 | avatar: 'https://gw.alipayobjects.com/zos/rmsportal/BiazfanxmamNRoxxVxka.png',
6 | userid: '00000001',
7 | email: 'antdesign@alipay.com',
8 | signature: '海纳百川,有容乃大',
9 | title: '交互专家',
10 | group: '蚂蚁金服-某某某事业群-某某平台部-某某技术部-UED',
11 | tags: [
12 | { key: '0', label: '很有想法的' },
13 | { key: '1', label: '专注设计' },
14 | { key: '2', label: '辣~' },
15 | { key: '3', label: '大长腿' },
16 | { key: '4', label: '川妹子' },
17 | { key: '5', label: '海纳百川' },
18 | ],
19 | notifyCount: 12,
20 | unreadCount: 11,
21 | country: 'China',
22 | geographic: {
23 | province: { label: '浙江省', key: '330000' },
24 | city: { label: '杭州市', key: '330100' },
25 | },
26 | address: '西湖区工专路 77 号',
27 | phone: '0752-268888888',
28 | },
29 | },
30 | 'GET /api/rule': {
31 | data: [
32 | {
33 | key: 99,
34 | disabled: false,
35 | href: 'https://ant.design',
36 | avatar: 'https://gw.alipayobjects.com/zos/rmsportal/udxAbMEhpwthVVcjLXik.png',
37 | name: 'TradeCode 99',
38 | owner: '曲丽丽',
39 | desc: '这是一段描述',
40 | callNo: 503,
41 | status: '0',
42 | updatedAt: '2022-12-06T05:00:57.040Z',
43 | createdAt: '2022-12-06T05:00:57.040Z',
44 | progress: 81,
45 | },
46 | {
47 | key: 98,
48 | disabled: false,
49 | href: 'https://ant.design',
50 | avatar: 'https://gw.alipayobjects.com/zos/rmsportal/eeHMaZBwmTvLdIwMfBpg.png',
51 | name: 'TradeCode 98',
52 | owner: '曲丽丽',
53 | desc: '这是一段描述',
54 | callNo: 164,
55 | status: '0',
56 | updatedAt: '2022-12-06T05:00:57.040Z',
57 | createdAt: '2022-12-06T05:00:57.040Z',
58 | progress: 12,
59 | },
60 | {
61 | key: 97,
62 | disabled: false,
63 | href: 'https://ant.design',
64 | avatar: 'https://gw.alipayobjects.com/zos/rmsportal/udxAbMEhpwthVVcjLXik.png',
65 | name: 'TradeCode 97',
66 | owner: '曲丽丽',
67 | desc: '这是一段描述',
68 | callNo: 174,
69 | status: '1',
70 | updatedAt: '2022-12-06T05:00:57.040Z',
71 | createdAt: '2022-12-06T05:00:57.040Z',
72 | progress: 81,
73 | },
74 | {
75 | key: 96,
76 | disabled: true,
77 | href: 'https://ant.design',
78 | avatar: 'https://gw.alipayobjects.com/zos/rmsportal/eeHMaZBwmTvLdIwMfBpg.png',
79 | name: 'TradeCode 96',
80 | owner: '曲丽丽',
81 | desc: '这是一段描述',
82 | callNo: 914,
83 | status: '0',
84 | updatedAt: '2022-12-06T05:00:57.040Z',
85 | createdAt: '2022-12-06T05:00:57.040Z',
86 | progress: 7,
87 | },
88 | {
89 | key: 95,
90 | disabled: false,
91 | href: 'https://ant.design',
92 | avatar: 'https://gw.alipayobjects.com/zos/rmsportal/udxAbMEhpwthVVcjLXik.png',
93 | name: 'TradeCode 95',
94 | owner: '曲丽丽',
95 | desc: '这是一段描述',
96 | callNo: 698,
97 | status: '2',
98 | updatedAt: '2022-12-06T05:00:57.040Z',
99 | createdAt: '2022-12-06T05:00:57.040Z',
100 | progress: 82,
101 | },
102 | {
103 | key: 94,
104 | disabled: false,
105 | href: 'https://ant.design',
106 | avatar: 'https://gw.alipayobjects.com/zos/rmsportal/eeHMaZBwmTvLdIwMfBpg.png',
107 | name: 'TradeCode 94',
108 | owner: '曲丽丽',
109 | desc: '这是一段描述',
110 | callNo: 488,
111 | status: '1',
112 | updatedAt: '2022-12-06T05:00:57.040Z',
113 | createdAt: '2022-12-06T05:00:57.040Z',
114 | progress: 14,
115 | },
116 | {
117 | key: 93,
118 | disabled: false,
119 | href: 'https://ant.design',
120 | avatar: 'https://gw.alipayobjects.com/zos/rmsportal/udxAbMEhpwthVVcjLXik.png',
121 | name: 'TradeCode 93',
122 | owner: '曲丽丽',
123 | desc: '这是一段描述',
124 | callNo: 580,
125 | status: '2',
126 | updatedAt: '2022-12-06T05:00:57.040Z',
127 | createdAt: '2022-12-06T05:00:57.040Z',
128 | progress: 77,
129 | },
130 | {
131 | key: 92,
132 | disabled: false,
133 | href: 'https://ant.design',
134 | avatar: 'https://gw.alipayobjects.com/zos/rmsportal/eeHMaZBwmTvLdIwMfBpg.png',
135 | name: 'TradeCode 92',
136 | owner: '曲丽丽',
137 | desc: '这是一段描述',
138 | callNo: 244,
139 | status: '3',
140 | updatedAt: '2022-12-06T05:00:57.040Z',
141 | createdAt: '2022-12-06T05:00:57.040Z',
142 | progress: 58,
143 | },
144 | {
145 | key: 91,
146 | disabled: false,
147 | href: 'https://ant.design',
148 | avatar: 'https://gw.alipayobjects.com/zos/rmsportal/udxAbMEhpwthVVcjLXik.png',
149 | name: 'TradeCode 91',
150 | owner: '曲丽丽',
151 | desc: '这是一段描述',
152 | callNo: 959,
153 | status: '0',
154 | updatedAt: '2022-12-06T05:00:57.040Z',
155 | createdAt: '2022-12-06T05:00:57.040Z',
156 | progress: 66,
157 | },
158 | {
159 | key: 90,
160 | disabled: true,
161 | href: 'https://ant.design',
162 | avatar: 'https://gw.alipayobjects.com/zos/rmsportal/eeHMaZBwmTvLdIwMfBpg.png',
163 | name: 'TradeCode 90',
164 | owner: '曲丽丽',
165 | desc: '这是一段描述',
166 | callNo: 958,
167 | status: '0',
168 | updatedAt: '2022-12-06T05:00:57.040Z',
169 | createdAt: '2022-12-06T05:00:57.040Z',
170 | progress: 72,
171 | },
172 | {
173 | key: 89,
174 | disabled: false,
175 | href: 'https://ant.design',
176 | avatar: 'https://gw.alipayobjects.com/zos/rmsportal/udxAbMEhpwthVVcjLXik.png',
177 | name: 'TradeCode 89',
178 | owner: '曲丽丽',
179 | desc: '这是一段描述',
180 | callNo: 301,
181 | status: '2',
182 | updatedAt: '2022-12-06T05:00:57.040Z',
183 | createdAt: '2022-12-06T05:00:57.040Z',
184 | progress: 2,
185 | },
186 | {
187 | key: 88,
188 | disabled: false,
189 | href: 'https://ant.design',
190 | avatar: 'https://gw.alipayobjects.com/zos/rmsportal/eeHMaZBwmTvLdIwMfBpg.png',
191 | name: 'TradeCode 88',
192 | owner: '曲丽丽',
193 | desc: '这是一段描述',
194 | callNo: 277,
195 | status: '1',
196 | updatedAt: '2022-12-06T05:00:57.040Z',
197 | createdAt: '2022-12-06T05:00:57.040Z',
198 | progress: 12,
199 | },
200 | {
201 | key: 87,
202 | disabled: false,
203 | href: 'https://ant.design',
204 | avatar: 'https://gw.alipayobjects.com/zos/rmsportal/udxAbMEhpwthVVcjLXik.png',
205 | name: 'TradeCode 87',
206 | owner: '曲丽丽',
207 | desc: '这是一段描述',
208 | callNo: 810,
209 | status: '1',
210 | updatedAt: '2022-12-06T05:00:57.040Z',
211 | createdAt: '2022-12-06T05:00:57.040Z',
212 | progress: 82,
213 | },
214 | {
215 | key: 86,
216 | disabled: false,
217 | href: 'https://ant.design',
218 | avatar: 'https://gw.alipayobjects.com/zos/rmsportal/eeHMaZBwmTvLdIwMfBpg.png',
219 | name: 'TradeCode 86',
220 | owner: '曲丽丽',
221 | desc: '这是一段描述',
222 | callNo: 780,
223 | status: '3',
224 | updatedAt: '2022-12-06T05:00:57.040Z',
225 | createdAt: '2022-12-06T05:00:57.040Z',
226 | progress: 22,
227 | },
228 | {
229 | key: 85,
230 | disabled: false,
231 | href: 'https://ant.design',
232 | avatar: 'https://gw.alipayobjects.com/zos/rmsportal/udxAbMEhpwthVVcjLXik.png',
233 | name: 'TradeCode 85',
234 | owner: '曲丽丽',
235 | desc: '这是一段描述',
236 | callNo: 705,
237 | status: '3',
238 | updatedAt: '2022-12-06T05:00:57.040Z',
239 | createdAt: '2022-12-06T05:00:57.040Z',
240 | progress: 12,
241 | },
242 | {
243 | key: 84,
244 | disabled: true,
245 | href: 'https://ant.design',
246 | avatar: 'https://gw.alipayobjects.com/zos/rmsportal/eeHMaZBwmTvLdIwMfBpg.png',
247 | name: 'TradeCode 84',
248 | owner: '曲丽丽',
249 | desc: '这是一段描述',
250 | callNo: 203,
251 | status: '0',
252 | updatedAt: '2022-12-06T05:00:57.040Z',
253 | createdAt: '2022-12-06T05:00:57.040Z',
254 | progress: 79,
255 | },
256 | {
257 | key: 83,
258 | disabled: false,
259 | href: 'https://ant.design',
260 | avatar: 'https://gw.alipayobjects.com/zos/rmsportal/udxAbMEhpwthVVcjLXik.png',
261 | name: 'TradeCode 83',
262 | owner: '曲丽丽',
263 | desc: '这是一段描述',
264 | callNo: 491,
265 | status: '2',
266 | updatedAt: '2022-12-06T05:00:57.040Z',
267 | createdAt: '2022-12-06T05:00:57.040Z',
268 | progress: 59,
269 | },
270 | {
271 | key: 82,
272 | disabled: false,
273 | href: 'https://ant.design',
274 | avatar: 'https://gw.alipayobjects.com/zos/rmsportal/eeHMaZBwmTvLdIwMfBpg.png',
275 | name: 'TradeCode 82',
276 | owner: '曲丽丽',
277 | desc: '这是一段描述',
278 | callNo: 73,
279 | status: '0',
280 | updatedAt: '2022-12-06T05:00:57.040Z',
281 | createdAt: '2022-12-06T05:00:57.040Z',
282 | progress: 100,
283 | },
284 | {
285 | key: 81,
286 | disabled: false,
287 | href: 'https://ant.design',
288 | avatar: 'https://gw.alipayobjects.com/zos/rmsportal/udxAbMEhpwthVVcjLXik.png',
289 | name: 'TradeCode 81',
290 | owner: '曲丽丽',
291 | desc: '这是一段描述',
292 | callNo: 406,
293 | status: '3',
294 | updatedAt: '2022-12-06T05:00:57.040Z',
295 | createdAt: '2022-12-06T05:00:57.040Z',
296 | progress: 61,
297 | },
298 | {
299 | key: 80,
300 | disabled: false,
301 | href: 'https://ant.design',
302 | avatar: 'https://gw.alipayobjects.com/zos/rmsportal/eeHMaZBwmTvLdIwMfBpg.png',
303 | name: 'TradeCode 80',
304 | owner: '曲丽丽',
305 | desc: '这是一段描述',
306 | callNo: 112,
307 | status: '2',
308 | updatedAt: '2022-12-06T05:00:57.040Z',
309 | createdAt: '2022-12-06T05:00:57.040Z',
310 | progress: 20,
311 | },
312 | ],
313 | total: 100,
314 | success: true,
315 | pageSize: 20,
316 | current: 1,
317 | },
318 | 'POST /api/login/outLogin': { data: {}, success: true },
319 | 'POST /api/login/account': {
320 | status: 'ok',
321 | type: 'account',
322 | currentAuthority: 'admin',
323 | },
324 | };
325 |
--------------------------------------------------------------------------------
/types/cache/mock/mock.cache.js:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rancytech/mj-proxy-admin/969850bfbc599cefbf51ee9d701cca3cb38e66e7/types/cache/mock/mock.cache.js
--------------------------------------------------------------------------------
/types/index.d.ts:
--------------------------------------------------------------------------------
1 | export namespace API {
2 | /** GET /api/currentUser */
3 | export type GET_API_CURRENT_USER_QUERY = {
4 | /** example: 123 */
5 | token: string;
6 | };
7 |
8 | export type GET_API_CURRENT_USER_PAYLOAD = Record;
9 |
10 | export type GET_API_CURRENT_USER_RES = {
11 | /** example: {"name": "Serati Ma", "avatar": "https: //gw.alipayobjects.com/zos/rmsportal/BiazfanxmamNRoxxVxka.png", "userid": "00000001", "email": "antdesign@alipay.com", "signature": "海纳百川,有容乃大", "title": "交互专家", "group": "蚂蚁金服-某某某事业群-某某平台部-某某技术部-UED", "tags": [{"key": "0", "label": "很有想法的"}, {"key": "1", "label": "专注设计"}, {"key": "2", "label": "辣~"}, {"key": "3", "label": "大长腿"}, {"key": "4", "label": "川妹子"}, {"key": "5", "label": "海纳百川"}], "notifyCount": 12, "unreadCount": 11, "country": "China", "geographic": {"province": {"label": "浙江省", "key": "330000"}, "city": {"label": "杭州市", "key": "330100"}}, "address": "西湖区工专路 77 号", "phone": "0752-268888888"} */
12 | data: {
13 | name: string;
14 | avatar: string;
15 | userid: string;
16 | email: string;
17 | signature: string;
18 | title: string;
19 | group: string;
20 | tags: {
21 | key: string;
22 | label: string;
23 | }[];
24 | notifyCount: number;
25 | unreadCount: number;
26 | country: string;
27 | geographic: {
28 | province: {
29 | label: string;
30 | key: string;
31 | };
32 | city: {
33 | label: string;
34 | key: string;
35 | };
36 | };
37 | address: string;
38 | phone: string;
39 | };
40 | };
41 |
42 | /** GET /api/rule */
43 | export type GET_API_RULE_QUERY = {
44 | /** example: 123 */
45 | token: string;
46 | /** example: 1 */
47 | current: string;
48 | /** example: 20 */
49 | pageSize: string;
50 | };
51 |
52 | export type GET_API_RULE_PAYLOAD = Record;
53 |
54 | export type GET_API_RULE_RES = {
55 | /** example: [{"key": 99, "disabled": false, "href": "https: //ant.design", "avatar": "https: //gw.alipayobjects.com/zos/rmsportal/udxAbMEhpwthVVcjLXik.png", "name": "TradeCode 99", "owner": "曲丽丽", "desc": "这是一段描述", "callNo": 503, "status": "0", "updatedAt": "2022-12-06T05: 00: 57.040Z", "createdAt": "2022-12-06T05: 00: 57.040Z", "progress": 81}, {"key": 98, "disabled": false, "href": "https: //ant.design", "avatar": "https: //gw.alipayobjects.com/zos/rmsportal/eeHMaZBwmTvLdIwMfBpg.png", "name": "TradeCode 98", "owner": "曲丽丽", "desc": "这是一段描述", "callNo": 164, "status": "0", "updatedAt": "2022-12-06T05: 00: 57.040Z", "createdAt": "2022-12-06T05: 00: 57.040Z", "progress": 12}, {"key": 97, "disabled": false, "href": "https: //ant.design", "avatar": "https: //gw.alipayobjects.com/zos/rmsportal/udxAbMEhpwthVVcjLXik.png", "name": "TradeCode 97", "owner": "曲丽丽", "desc": "这是一段描述", "callNo": 174, "status": "1", "updatedAt": "2022-12-06T05: 00: 57.040Z", "createdAt": "2022-12-06T05: 00: 57.040Z", "progress": 81}, {"key": 96, "disabled": true, "href": "https: //ant.design", "avatar": "https: //gw.alipayobjects.com/zos/rmsportal/eeHMaZBwmTvLdIwMfBpg.png", "name": "TradeCode 96", "owner": "曲丽丽", "desc": "这是一段描述", "callNo": 914, "status": "0", "updatedAt": "2022-12-06T05: 00: 57.040Z", "createdAt": "2022-12-06T05: 00: 57.040Z", "progress": 7}, {"key": 95, "disabled": false, "href": "https: //ant.design", "avatar": "https: //gw.alipayobjects.com/zos/rmsportal/udxAbMEhpwthVVcjLXik.png", "name": "TradeCode 95", "owner": "曲丽丽", "desc": "这是一段描述", "callNo": 698, "status": "2", "updatedAt": "2022-12-06T05: 00: 57.040Z", "createdAt": "2022-12-06T05: 00: 57.040Z", "progress": 82}, {"key": 94, "disabled": false, "href": "https: //ant.design", "avatar": "https: //gw.alipayobjects.com/zos/rmsportal/eeHMaZBwmTvLdIwMfBpg.png", "name": "TradeCode 94", "owner": "曲丽丽", "desc": "这是一段描述", "callNo": 488, "status": "1", "updatedAt": "2022-12-06T05: 00: 57.040Z", "createdAt": "2022-12-06T05: 00: 57.040Z", "progress": 14}, {"key": 93, "disabled": false, "href": "https: //ant.design", "avatar": "https: //gw.alipayobjects.com/zos/rmsportal/udxAbMEhpwthVVcjLXik.png", "name": "TradeCode 93", "owner": "曲丽丽", "desc": "这是一段描述", "callNo": 580, "status": "2", "updatedAt": "2022-12-06T05: 00: 57.040Z", "createdAt": "2022-12-06T05: 00: 57.040Z", "progress": 77}, {"key": 92, "disabled": false, "href": "https: //ant.design", "avatar": "https: //gw.alipayobjects.com/zos/rmsportal/eeHMaZBwmTvLdIwMfBpg.png", "name": "TradeCode 92", "owner": "曲丽丽", "desc": "这是一段描述", "callNo": 244, "status": "3", "updatedAt": "2022-12-06T05: 00: 57.040Z", "createdAt": "2022-12-06T05: 00: 57.040Z", "progress": 58}, {"key": 91, "disabled": false, "href": "https: //ant.design", "avatar": "https: //gw.alipayobjects.com/zos/rmsportal/udxAbMEhpwthVVcjLXik.png", "name": "TradeCode 91", "owner": "曲丽丽", "desc": "这是一段描述", "callNo": 959, "status": "0", "updatedAt": "2022-12-06T05: 00: 57.040Z", "createdAt": "2022-12-06T05: 00: 57.040Z", "progress": 66}, {"key": 90, "disabled": true, "href": "https: //ant.design", "avatar": "https: //gw.alipayobjects.com/zos/rmsportal/eeHMaZBwmTvLdIwMfBpg.png", "name": "TradeCode 90", "owner": "曲丽丽", "desc": "这是一段描述", "callNo": 958, "status": "0", "updatedAt": "2022-12-06T05: 00: 57.040Z", "createdAt": "2022-12-06T05: 00: 57.040Z", "progress": 72}, {"key": 89, "disabled": false, "href": "https: //ant.design", "avatar": "https: //gw.alipayobjects.com/zos/rmsportal/udxAbMEhpwthVVcjLXik.png", "name": "TradeCode 89", "owner": "曲丽丽", "desc": "这是一段描述", "callNo": 301, "status": "2", "updatedAt": "2022-12-06T05: 00: 57.040Z", "createdAt": "2022-12-06T05: 00: 57.040Z", "progress": 2}, {"key": 88, "disabled": false, "href": "https: //ant.design", "avatar": "https: //gw.alipayobjects.com/zos/rmsportal/eeHMaZBwmTvLdIwMfBpg.png", "name": "TradeCode 88", "owner": "曲丽丽", "desc": "这是一段描述", "callNo": 277, "status": "1", "updatedAt": "2022-12-06T05: 00: 57.040Z", "createdAt": "2022-12-06T05: 00: 57.040Z", "progress": 12}, {"key": 87, "disabled": false, "href": "https: //ant.design", "avatar": "https: //gw.alipayobjects.com/zos/rmsportal/udxAbMEhpwthVVcjLXik.png", "name": "TradeCode 87", "owner": "曲丽丽", "desc": "这是一段描述", "callNo": 810, "status": "1", "updatedAt": "2022-12-06T05: 00: 57.040Z", "createdAt": "2022-12-06T05: 00: 57.040Z", "progress": 82}, {"key": 86, "disabled": false, "href": "https: //ant.design", "avatar": "https: //gw.alipayobjects.com/zos/rmsportal/eeHMaZBwmTvLdIwMfBpg.png", "name": "TradeCode 86", "owner": "曲丽丽", "desc": "这是一段描述", "callNo": 780, "status": "3", "updatedAt": "2022-12-06T05: 00: 57.040Z", "createdAt": "2022-12-06T05: 00: 57.040Z", "progress": 22}, {"key": 85, "disabled": false, "href": "https: //ant.design", "avatar": "https: //gw.alipayobjects.com/zos/rmsportal/udxAbMEhpwthVVcjLXik.png", "name": "TradeCode 85", "owner": "曲丽丽", "desc": "这是一段描述", "callNo": 705, "status": "3", "updatedAt": "2022-12-06T05: 00: 57.040Z", "createdAt": "2022-12-06T05: 00: 57.040Z", "progress": 12}, {"key": 84, "disabled": true, "href": "https: //ant.design", "avatar": "https: //gw.alipayobjects.com/zos/rmsportal/eeHMaZBwmTvLdIwMfBpg.png", "name": "TradeCode 84", "owner": "曲丽丽", "desc": "这是一段描述", "callNo": 203, "status": "0", "updatedAt": "2022-12-06T05: 00: 57.040Z", "createdAt": "2022-12-06T05: 00: 57.040Z", "progress": 79}, {"key": 83, "disabled": false, "href": "https: //ant.design", "avatar": "https: //gw.alipayobjects.com/zos/rmsportal/udxAbMEhpwthVVcjLXik.png", "name": "TradeCode 83", "owner": "曲丽丽", "desc": "这是一段描述", "callNo": 491, "status": "2", "updatedAt": "2022-12-06T05: 00: 57.040Z", "createdAt": "2022-12-06T05: 00: 57.040Z", "progress": 59}, {"key": 82, "disabled": false, "href": "https: //ant.design", "avatar": "https: //gw.alipayobjects.com/zos/rmsportal/eeHMaZBwmTvLdIwMfBpg.png", "name": "TradeCode 82", "owner": "曲丽丽", "desc": "这是一段描述", "callNo": 73, "status": "0", "updatedAt": "2022-12-06T05: 00: 57.040Z", "createdAt": "2022-12-06T05: 00: 57.040Z", "progress": 100}, {"key": 81, "disabled": false, "href": "https: //ant.design", "avatar": "https: //gw.alipayobjects.com/zos/rmsportal/udxAbMEhpwthVVcjLXik.png", "name": "TradeCode 81", "owner": "曲丽丽", "desc": "这是一段描述", "callNo": 406, "status": "3", "updatedAt": "2022-12-06T05: 00: 57.040Z", "createdAt": "2022-12-06T05: 00: 57.040Z", "progress": 61}, {"key": 80, "disabled": false, "href": "https: //ant.design", "avatar": "https: //gw.alipayobjects.com/zos/rmsportal/eeHMaZBwmTvLdIwMfBpg.png", "name": "TradeCode 80", "owner": "曲丽丽", "desc": "这是一段描述", "callNo": 112, "status": "2", "updatedAt": "2022-12-06T05: 00: 57.040Z", "createdAt": "2022-12-06T05: 00: 57.040Z", "progress": 20}] */
56 | data: {
57 | key: number;
58 | disabled: boolean;
59 | href: string;
60 | avatar: string;
61 | name: string;
62 | owner: string;
63 | desc: string;
64 | callNo: number;
65 | status: string;
66 | updatedAt: string;
67 | createdAt: string;
68 | progress: number;
69 | }[];
70 | /** example: 100 */
71 | total: number;
72 | /** example: true */
73 | success: boolean;
74 | /** example: 20 */
75 | pageSize: number;
76 | /** example: 1 */
77 | current: number;
78 | };
79 |
80 | /** POST /api/login/outLogin */
81 | export type POST_API_LOGIN_OUT_LOGIN_QUERY = {
82 | /** example: 123 */
83 | token: string;
84 | };
85 |
86 | export type POST_API_LOGIN_OUT_LOGIN_PAYLOAD = Record;
87 |
88 | export type POST_API_LOGIN_OUT_LOGIN_RES = {
89 | /** example: {} */
90 | data: Record;
91 | /** example: true */
92 | success: boolean;
93 | };
94 |
95 | /** POST /api/login/account */
96 | export type POST_API_LOGIN_ACCOUNT_QUERY = {
97 | /** example: 123 */
98 | token: string;
99 | };
100 |
101 | export type POST_API_LOGIN_ACCOUNT_PAYLOAD = {
102 | /** example: admin */
103 | username: string;
104 | /** example: ant.design */
105 | password: string;
106 | /** example: true */
107 | autoLogin: boolean;
108 | /** example: account */
109 | type: string;
110 | };
111 |
112 | export type POST_API_LOGIN_ACCOUNT_RES = {
113 | /** example: ok */
114 | status: string;
115 | /** example: account */
116 | type: string;
117 | /** example: admin */
118 | currentAuthority: string;
119 | };
120 | }
121 |
--------------------------------------------------------------------------------