From 2c393893362eb55f68f864267e5e0c1beff2d233 Mon Sep 17 00:00:00 2001 From: nili Date: Wed, 30 Mar 2022 11:38:36 +0800 Subject: [PATCH] init --- .editorconfig | 16 + .eslintignore | 8 + .eslintrc.js | 8 + .gitignore | 41 ++ .prettierignore | 23 + .prettierrc.js | 5 + .stylelintrc.js | 5 + README.md | 57 ++ config/config.dev.ts | 15 + config/config.ts | 75 +++ config/defaultSettings.ts | 21 + config/oneapi.json | 593 ++++++++++++++++++ config/proxy.ts | 34 + config/routes.ts | 58 ++ jest.config.js | 9 + jsconfig.json | 11 + mock/listTableList.ts | 174 +++++ mock/notices.ts | 107 ++++ mock/route.ts | 5 + mock/user.ts | 203 ++++++ package.json | 109 ++++ playwright.config.ts | 22 + public/CNAME | 1 + public/favicon.ico | Bin 0 -> 4286 bytes public/icons/icon-128x128.png | Bin 0 -> 1329 bytes public/icons/icon-192x192.png | Bin 0 -> 1856 bytes public/icons/icon-512x512.png | Bin 0 -> 5082 bytes public/logo.svg | 1 + public/pro_icon.svg | 5 + src/access.ts | 9 + src/app.tsx | 108 ++++ src/components/Footer/index.tsx | 41 ++ src/components/HeaderDropdown/index.less | 16 + src/components/HeaderDropdown/index.tsx | 17 + src/components/HeaderSearch/index.less | 25 + src/components/HeaderSearch/index.tsx | 102 +++ src/components/NoticeIcon/NoticeIcon.tsx | 126 ++++ src/components/NoticeIcon/NoticeList.less | 103 +++ src/components/NoticeIcon/NoticeList.tsx | 113 ++++ src/components/NoticeIcon/index.less | 35 ++ src/components/NoticeIcon/index.tsx | 153 +++++ .../RightContent/AvatarDropdown.tsx | 103 +++ src/components/RightContent/index.less | 84 +++ src/components/RightContent/index.tsx | 62 ++ src/components/index.md | 272 ++++++++ src/e2e/baseLayout.e2e.spec.ts | 45 ++ src/global.less | 57 ++ src/global.tsx | 91 +++ src/locales/bn-BD.ts | 26 + src/locales/bn-BD/component.ts | 5 + src/locales/bn-BD/globalHeader.ts | 17 + src/locales/bn-BD/menu.ts | 52 ++ src/locales/bn-BD/pages.ts | 70 +++ src/locales/bn-BD/pwa.ts | 7 + src/locales/bn-BD/settingDrawer.ts | 31 + src/locales/bn-BD/settings.ts | 59 ++ src/locales/en-US.ts | 25 + src/locales/en-US/component.ts | 5 + src/locales/en-US/globalHeader.ts | 17 + src/locales/en-US/menu.ts | 52 ++ src/locales/en-US/pages.ts | 70 +++ src/locales/en-US/pwa.ts | 6 + src/locales/en-US/settingDrawer.ts | 31 + src/locales/en-US/settings.ts | 60 ++ src/locales/fa-IR.ts | 24 + src/locales/fa-IR/component.ts | 5 + src/locales/fa-IR/globalHeader.ts | 17 + src/locales/fa-IR/menu.ts | 52 ++ src/locales/fa-IR/pages.ts | 67 ++ src/locales/fa-IR/pwa.ts | 7 + src/locales/fa-IR/settingDrawer.ts | 32 + src/locales/fa-IR/settings.ts | 60 ++ src/locales/id-ID.ts | 25 + src/locales/id-ID/component.ts | 5 + src/locales/id-ID/globalHeader.ts | 17 + src/locales/id-ID/menu.ts | 52 ++ src/locales/id-ID/pages.ts | 70 +++ src/locales/id-ID/pwa.ts | 7 + src/locales/id-ID/settingDrawer.ts | 32 + src/locales/id-ID/settings.ts | 60 ++ src/locales/ja-JP.ts | 24 + src/locales/ja-JP/component.ts | 5 + src/locales/ja-JP/globalHeader.ts | 17 + src/locales/ja-JP/menu.ts | 52 ++ src/locales/ja-JP/pages.ts | 67 ++ src/locales/ja-JP/pwa.ts | 7 + src/locales/ja-JP/settingDrawer.ts | 31 + src/locales/ja-JP/settings.ts | 59 ++ src/locales/pt-BR.ts | 22 + src/locales/pt-BR/component.ts | 5 + src/locales/pt-BR/globalHeader.ts | 18 + src/locales/pt-BR/menu.ts | 52 ++ src/locales/pt-BR/pages.ts | 70 +++ src/locales/pt-BR/pwa.ts | 7 + src/locales/pt-BR/settingDrawer.ts | 32 + src/locales/pt-BR/settings.ts | 60 ++ src/locales/zh-CN.ts | 25 + src/locales/zh-CN/component.ts | 5 + src/locales/zh-CN/globalHeader.ts | 17 + src/locales/zh-CN/menu.ts | 52 ++ src/locales/zh-CN/pages.ts | 67 ++ src/locales/zh-CN/pwa.ts | 6 + src/locales/zh-CN/settingDrawer.ts | 31 + src/locales/zh-CN/settings.ts | 55 ++ src/locales/zh-TW.ts | 20 + src/locales/zh-TW/component.ts | 5 + src/locales/zh-TW/globalHeader.ts | 17 + src/locales/zh-TW/menu.ts | 52 ++ src/locales/zh-TW/pwa.ts | 6 + src/locales/zh-TW/settingDrawer.ts | 31 + src/locales/zh-TW/settings.ts | 55 ++ src/manifest.json | 22 + src/pages/404.tsx | 18 + src/pages/Admin.tsx | 45 ++ src/pages/TableList/components/UpdateForm.tsx | 209 ++++++ src/pages/TableList/index.tsx | 393 ++++++++++++ src/pages/Welcome.less | 8 + src/pages/Welcome.tsx | 66 ++ src/pages/document.ejs | 236 +++++++ src/pages/user/Login/index.less | 50 ++ src/pages/user/Login/index.tsx | 286 +++++++++ src/service-worker.js | 65 ++ src/services/ant-design-pro/api.ts | 85 +++ src/services/ant-design-pro/index.ts | 10 + src/services/ant-design-pro/login.ts | 21 + src/services/ant-design-pro/typings.d.ts | 101 +++ src/services/swagger/index.ts | 12 + src/services/swagger/pet.ts | 166 +++++ src/services/swagger/store.ts | 54 ++ src/services/swagger/typings.d.ts | 52 ++ src/services/swagger/user.ts | 114 ++++ src/typings.d.ts | 24 + tests/run-tests.js | 47 ++ tests/setupTests.js | 10 + tsconfig.json | 42 ++ 135 files changed, 7341 insertions(+) create mode 100644 .editorconfig create mode 100644 .eslintignore create mode 100644 .eslintrc.js create mode 100644 .gitignore create mode 100644 .prettierignore create mode 100644 .prettierrc.js create mode 100644 .stylelintrc.js create mode 100644 README.md create mode 100644 config/config.dev.ts create mode 100644 config/config.ts create mode 100644 config/defaultSettings.ts create mode 100644 config/oneapi.json create mode 100644 config/proxy.ts create mode 100644 config/routes.ts create mode 100644 jest.config.js create mode 100644 jsconfig.json create mode 100644 mock/listTableList.ts create mode 100644 mock/notices.ts create mode 100644 mock/route.ts create mode 100644 mock/user.ts create mode 100644 package.json create mode 100644 playwright.config.ts create mode 100644 public/CNAME create mode 100644 public/favicon.ico create mode 100644 public/icons/icon-128x128.png create mode 100644 public/icons/icon-192x192.png create mode 100644 public/icons/icon-512x512.png create mode 100644 public/logo.svg create mode 100644 public/pro_icon.svg create mode 100644 src/access.ts create mode 100644 src/app.tsx create mode 100644 src/components/Footer/index.tsx create mode 100644 src/components/HeaderDropdown/index.less create mode 100644 src/components/HeaderDropdown/index.tsx create mode 100644 src/components/HeaderSearch/index.less create mode 100644 src/components/HeaderSearch/index.tsx create mode 100644 src/components/NoticeIcon/NoticeIcon.tsx create mode 100755 src/components/NoticeIcon/NoticeList.less create mode 100644 src/components/NoticeIcon/NoticeList.tsx create mode 100644 src/components/NoticeIcon/index.less create mode 100644 src/components/NoticeIcon/index.tsx create mode 100644 src/components/RightContent/AvatarDropdown.tsx create mode 100644 src/components/RightContent/index.less create mode 100644 src/components/RightContent/index.tsx create mode 100644 src/components/index.md create mode 100644 src/e2e/baseLayout.e2e.spec.ts create mode 100644 src/global.less create mode 100644 src/global.tsx create mode 100644 src/locales/bn-BD.ts create mode 100644 src/locales/bn-BD/component.ts create mode 100644 src/locales/bn-BD/globalHeader.ts create mode 100644 src/locales/bn-BD/menu.ts create mode 100644 src/locales/bn-BD/pages.ts create mode 100644 src/locales/bn-BD/pwa.ts create mode 100644 src/locales/bn-BD/settingDrawer.ts create mode 100644 src/locales/bn-BD/settings.ts create mode 100644 src/locales/en-US.ts create mode 100644 src/locales/en-US/component.ts create mode 100644 src/locales/en-US/globalHeader.ts create mode 100644 src/locales/en-US/menu.ts create mode 100644 src/locales/en-US/pages.ts create mode 100644 src/locales/en-US/pwa.ts create mode 100644 src/locales/en-US/settingDrawer.ts create mode 100644 src/locales/en-US/settings.ts create mode 100644 src/locales/fa-IR.ts create mode 100644 src/locales/fa-IR/component.ts create mode 100644 src/locales/fa-IR/globalHeader.ts create mode 100644 src/locales/fa-IR/menu.ts create mode 100644 src/locales/fa-IR/pages.ts create mode 100644 src/locales/fa-IR/pwa.ts create mode 100644 src/locales/fa-IR/settingDrawer.ts create mode 100644 src/locales/fa-IR/settings.ts create mode 100644 src/locales/id-ID.ts create mode 100644 src/locales/id-ID/component.ts create mode 100644 src/locales/id-ID/globalHeader.ts create mode 100644 src/locales/id-ID/menu.ts create mode 100644 src/locales/id-ID/pages.ts create mode 100644 src/locales/id-ID/pwa.ts create mode 100644 src/locales/id-ID/settingDrawer.ts create mode 100644 src/locales/id-ID/settings.ts create mode 100644 src/locales/ja-JP.ts create mode 100644 src/locales/ja-JP/component.ts create mode 100644 src/locales/ja-JP/globalHeader.ts create mode 100644 src/locales/ja-JP/menu.ts create mode 100644 src/locales/ja-JP/pages.ts create mode 100644 src/locales/ja-JP/pwa.ts create mode 100644 src/locales/ja-JP/settingDrawer.ts create mode 100644 src/locales/ja-JP/settings.ts create mode 100644 src/locales/pt-BR.ts create mode 100644 src/locales/pt-BR/component.ts create mode 100644 src/locales/pt-BR/globalHeader.ts create mode 100644 src/locales/pt-BR/menu.ts create mode 100644 src/locales/pt-BR/pages.ts create mode 100644 src/locales/pt-BR/pwa.ts create mode 100644 src/locales/pt-BR/settingDrawer.ts create mode 100644 src/locales/pt-BR/settings.ts create mode 100644 src/locales/zh-CN.ts create mode 100644 src/locales/zh-CN/component.ts create mode 100644 src/locales/zh-CN/globalHeader.ts create mode 100644 src/locales/zh-CN/menu.ts create mode 100644 src/locales/zh-CN/pages.ts create mode 100644 src/locales/zh-CN/pwa.ts create mode 100644 src/locales/zh-CN/settingDrawer.ts create mode 100644 src/locales/zh-CN/settings.ts create mode 100644 src/locales/zh-TW.ts create mode 100644 src/locales/zh-TW/component.ts create mode 100644 src/locales/zh-TW/globalHeader.ts create mode 100644 src/locales/zh-TW/menu.ts create mode 100644 src/locales/zh-TW/pwa.ts create mode 100644 src/locales/zh-TW/settingDrawer.ts create mode 100644 src/locales/zh-TW/settings.ts create mode 100644 src/manifest.json create mode 100644 src/pages/404.tsx create mode 100644 src/pages/Admin.tsx create mode 100644 src/pages/TableList/components/UpdateForm.tsx create mode 100644 src/pages/TableList/index.tsx create mode 100644 src/pages/Welcome.less create mode 100644 src/pages/Welcome.tsx create mode 100644 src/pages/document.ejs create mode 100644 src/pages/user/Login/index.less create mode 100644 src/pages/user/Login/index.tsx create mode 100644 src/service-worker.js create mode 100644 src/services/ant-design-pro/api.ts create mode 100644 src/services/ant-design-pro/index.ts create mode 100644 src/services/ant-design-pro/login.ts create mode 100644 src/services/ant-design-pro/typings.d.ts create mode 100644 src/services/swagger/index.ts create mode 100644 src/services/swagger/pet.ts create mode 100644 src/services/swagger/store.ts create mode 100644 src/services/swagger/typings.d.ts create mode 100644 src/services/swagger/user.ts create mode 100644 src/typings.d.ts create mode 100644 tests/run-tests.js create mode 100644 tests/setupTests.js create mode 100644 tsconfig.json diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..7e3649a --- /dev/null +++ b/.editorconfig @@ -0,0 +1,16 @@ +# http://editorconfig.org +root = true + +[*] +indent_style = space +indent_size = 2 +end_of_line = lf +charset = utf-8 +trim_trailing_whitespace = true +insert_final_newline = true + +[*.md] +trim_trailing_whitespace = false + +[Makefile] +indent_style = tab diff --git a/.eslintignore b/.eslintignore new file mode 100644 index 0000000..8336e93 --- /dev/null +++ b/.eslintignore @@ -0,0 +1,8 @@ +/lambda/ +/scripts +/config +.history +public +dist +.umi +mock \ No newline at end of file diff --git a/.eslintrc.js b/.eslintrc.js new file mode 100644 index 0000000..b882c20 --- /dev/null +++ b/.eslintrc.js @@ -0,0 +1,8 @@ +module.exports = { + extends: [require.resolve('@umijs/fabric/dist/eslint')], + globals: { + ANT_DESIGN_PRO_ONLY_DO_NOT_USE_IN_YOUR_PRODUCTION: true, + page: true, + REACT_APP_ENV: true, + }, +}; diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..479b7ab --- /dev/null +++ b/.gitignore @@ -0,0 +1,41 @@ +# See https://help.github.com/articles/ignoring-files/ for more about ignoring files. + +# dependencies +**/node_modules +# roadhog-api-doc ignore +/src/utils/request-temp.js +_roadhog-api-doc + +# production +/dist + +# misc +.DS_Store +npm-debug.log* +yarn-error.log + +/coverage +.idea +yarn.lock +package-lock.json +pnpm-lock.yaml +*bak + + +# visual studio code +.history +*.log +functions/* +.temp/** + +# umi +.umi +.umi-production + +# screenshot +screenshot +.firebase +.eslintcache + +build +.vscode \ No newline at end of file diff --git a/.prettierignore b/.prettierignore new file mode 100644 index 0000000..d17efb4 --- /dev/null +++ b/.prettierignore @@ -0,0 +1,23 @@ +**/*.svg +package.json +.umi +.umi-production +/dist +.dockerignore +.DS_Store +.eslintignore +*.png +*.toml +docker +.editorconfig +Dockerfile* +.gitignore +.prettierignore +LICENSE +.eslintcache +*.lock +yarn-error.log +.history +CNAME +/build +/public \ No newline at end of file diff --git a/.prettierrc.js b/.prettierrc.js new file mode 100644 index 0000000..7b597d7 --- /dev/null +++ b/.prettierrc.js @@ -0,0 +1,5 @@ +const fabric = require('@umijs/fabric'); + +module.exports = { + ...fabric.prettier, +}; diff --git a/.stylelintrc.js b/.stylelintrc.js new file mode 100644 index 0000000..c203078 --- /dev/null +++ b/.stylelintrc.js @@ -0,0 +1,5 @@ +const fabric = require('@umijs/fabric'); + +module.exports = { + ...fabric.stylelint, +}; diff --git a/README.md b/README.md new file mode 100644 index 0000000..4c89a72 --- /dev/null +++ b/README.md @@ -0,0 +1,57 @@ +# Ant Design Pro + +This project is initialized with [Ant Design Pro](https://pro.ant.design). Follow is the quick guide for how to use. + +## Environment Prepare + +Install `node_modules`: + +```bash +npm install +``` + +or + +```bash +yarn +``` + +## Provided Scripts + +Ant Design Pro provides some useful script to help you quick start and build with web project, code style check and test. + +Scripts provided in `package.json`. It's safe to modify or add additional script: + +### Start project + +```bash +npm start +``` + +### Build project + +```bash +npm run build +``` + +### Check code style + +```bash +npm run lint +``` + +You can also use script to auto fix some lint error: + +```bash +npm run lint:fix +``` + +### Test code + +```bash +npm test +``` + +## More + +You can view full document on our [official website](https://pro.ant.design). And welcome any feedback in our [github](https://github.com/ant-design/ant-design-pro). diff --git a/config/config.dev.ts b/config/config.dev.ts new file mode 100644 index 0000000..ab0e590 --- /dev/null +++ b/config/config.dev.ts @@ -0,0 +1,15 @@ +// https://umijs.org/config/ +import { defineConfig } from 'umi'; + +export default defineConfig({ + plugins: [ + // https://github.com/zthxxx/react-dev-inspector + 'react-dev-inspector/plugins/umi/react-inspector', + ], + // https://github.com/zthxxx/react-dev-inspector#inspector-loader-props + inspectorConfig: { + exclude: [], + babelPlugins: [], + babelOptions: {}, + }, +}); diff --git a/config/config.ts b/config/config.ts new file mode 100644 index 0000000..72eca9d --- /dev/null +++ b/config/config.ts @@ -0,0 +1,75 @@ +// https://umijs.org/config/ +import { defineConfig } from 'umi'; +import { join } from 'path'; + +import defaultSettings from './defaultSettings'; +import proxy from './proxy'; +import routes from './routes'; + +const { REACT_APP_ENV } = process.env; + +export default defineConfig({ + hash: true, + antd: {}, + dva: { + hmr: true, + }, + layout: { + // https://umijs.org/zh-CN/plugins/plugin-layout + locale: true, + siderWidth: 208, + ...defaultSettings, + }, + // https://umijs.org/zh-CN/plugins/plugin-locale + locale: { + // default zh-CN + default: 'zh-CN', + antd: true, + // default true, when it is true, will use `navigator.language` overwrite default + baseNavigator: true, + }, + dynamicImport: { + loading: '@ant-design/pro-layout/es/PageLoading', + }, + targets: { + ie: 11, + }, + // umi routes: https://umijs.org/docs/routing + routes, + // Theme for antd: https://ant.design/docs/react/customize-theme-cn + theme: { + // 如果不想要 configProvide 动态设置主题需要把这个设置为 default + // 只有设置为 variable, 才能使用 configProvide 动态设置主色调 + // https://ant.design/docs/react/customize-theme-variable-cn + 'root-entry-name': 'variable', + }, + // esbuild is father build tools + // https://umijs.org/plugins/plugin-esbuild + esbuild: {}, + title: false, + ignoreMomentLocale: true, + proxy: proxy[REACT_APP_ENV || 'dev'], + manifest: { + basePath: '/', + }, + // Fast Refresh 热更新 + fastRefresh: {}, + openAPI: [ + { + requestLibPath: "import { request } from 'umi'", + // 或者使用在线的版本 + // schemaPath: "https://gw.alipayobjects.com/os/antfincdn/M%24jrzTTYJN/oneapi.json" + schemaPath: join(__dirname, 'oneapi.json'), + mock: false, + }, + { + requestLibPath: "import { request } from 'umi'", + schemaPath: 'https://gw.alipayobjects.com/os/antfincdn/CA1dOm%2631B/openapi.json', + projectName: 'swagger', + }, + ], + nodeModulesTransform: { type: 'none' }, + mfsu: {}, + webpack5: {}, + exportStatic: {}, +}); diff --git a/config/defaultSettings.ts b/config/defaultSettings.ts new file mode 100644 index 0000000..15397c6 --- /dev/null +++ b/config/defaultSettings.ts @@ -0,0 +1,21 @@ +import { Settings as LayoutSettings } from '@ant-design/pro-layout'; + +const Settings: LayoutSettings & { + pwa?: boolean; + logo?: string; +} = { + navTheme: 'light', + // 拂晓蓝 + primaryColor: '#1890ff', + layout: 'mix', + contentWidth: 'Fluid', + fixedHeader: false, + fixSiderbar: true, + colorWeak: false, + title: 'Ant Design Pro', + pwa: false, + logo: 'https://gw.alipayobjects.com/zos/rmsportal/KDpgvguMpGfqaHPjicRK.svg', + iconfontUrl: '', +}; + +export default Settings; diff --git a/config/oneapi.json b/config/oneapi.json new file mode 100644 index 0000000..c77d988 --- /dev/null +++ b/config/oneapi.json @@ -0,0 +1,593 @@ +{ + "openapi": "3.0.1", + "info": { + "title": "Ant Design Pro", + "version": "1.0.0" + }, + "servers": [ + { + "url": "http://localhost:8000/" + }, + { + "url": "https://localhost:8000/" + } + ], + "paths": { + "/api/currentUser": { + "get": { + "tags": ["api"], + "description": "获取当前的用户", + "operationId": "currentUser", + "responses": { + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CurrentUser" + } + } + } + }, + "401": { + "description": "Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + } + } + }, + "x-swagger-router-controller": "api" + }, + "/api/login/captcha": { + "post": { + "description": "发送验证码", + "operationId": "getFakeCaptcha", + "tags": ["login"], + "parameters": [ + { + "name": "phone", + "in": "query", + "description": "手机号", + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/FakeCaptcha" + } + } + } + } + } + } + }, + "/api/login/outLogin": { + "post": { + "description": "登录接口", + "operationId": "outLogin", + "tags": ["login"], + "responses": { + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "type": "object" + } + } + } + }, + "401": { + "description": "Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + } + } + }, + "x-swagger-router-controller": "api" + }, + "/api/login/account": { + "post": { + "tags": ["login"], + "description": "登录接口", + "operationId": "login", + "requestBody": { + "description": "登录系统", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/LoginParams" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/LoginResult" + } + } + } + }, + "401": { + "description": "Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + } + }, + "x-codegen-request-body-name": "body" + }, + "x-swagger-router-controller": "api" + }, + "/api/notices": { + "summary": "getNotices", + "description": "NoticeIconItem", + "get": { + "tags": ["api"], + "operationId": "getNotices", + "responses": { + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/NoticeIconList" + } + } + } + } + } + } + }, + "/api/rule": { + "get": { + "tags": ["rule"], + "description": "获取规则列表", + "operationId": "rule", + "parameters": [ + { + "name": "current", + "in": "query", + "description": "当前的页码", + "schema": { + "type": "number" + } + }, + { + "name": "pageSize", + "in": "query", + "description": "页面的容量", + "schema": { + "type": "number" + } + } + ], + "responses": { + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RuleList" + } + } + } + }, + "401": { + "description": "Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + } + } + }, + "post": { + "tags": ["rule"], + "description": "新建规则", + "operationId": "addRule", + "responses": { + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RuleListItem" + } + } + } + }, + "401": { + "description": "Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + } + } + }, + "put": { + "tags": ["rule"], + "description": "新建规则", + "operationId": "updateRule", + "responses": { + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RuleListItem" + } + } + } + }, + "401": { + "description": "Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + } + } + }, + "delete": { + "tags": ["rule"], + "description": "删除规则", + "operationId": "removeRule", + "responses": { + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "type": "object" + } + } + } + }, + "401": { + "description": "Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + } + } + }, + "x-swagger-router-controller": "api" + }, + "/swagger": { + "x-swagger-pipe": "swagger_raw" + } + }, + "components": { + "schemas": { + "CurrentUser": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "avatar": { + "type": "string" + }, + "userid": { + "type": "string" + }, + "email": { + "type": "string" + }, + "signature": { + "type": "string" + }, + "title": { + "type": "string" + }, + "group": { + "type": "string" + }, + "tags": { + "type": "array", + "items": { + "type": "object", + "properties": { + "key": { + "type": "string" + }, + "label": { + "type": "string" + } + } + } + }, + "notifyCount": { + "type": "integer", + "format": "int32" + }, + "unreadCount": { + "type": "integer", + "format": "int32" + }, + "country": { + "type": "string" + }, + "access": { + "type": "string" + }, + "geographic": { + "type": "object", + "properties": { + "province": { + "type": "object", + "properties": { + "label": { + "type": "string" + }, + "key": { + "type": "string" + } + } + }, + "city": { + "type": "object", + "properties": { + "label": { + "type": "string" + }, + "key": { + "type": "string" + } + } + } + } + }, + "address": { + "type": "string" + }, + "phone": { + "type": "string" + } + } + }, + "LoginResult": { + "type": "object", + "properties": { + "status": { + "type": "string" + }, + "type": { + "type": "string" + }, + "currentAuthority": { + "type": "string" + } + } + }, + "PageParams": { + "type": "object", + "properties": { + "current": { + "type": "number" + }, + "pageSize": { + "type": "number" + } + } + }, + "RuleListItem": { + "type": "object", + "properties": { + "key": { + "type": "integer", + "format": "int32" + }, + "disabled": { + "type": "boolean" + }, + "href": { + "type": "string" + }, + "avatar": { + "type": "string" + }, + "name": { + "type": "string" + }, + "owner": { + "type": "string" + }, + "desc": { + "type": "string" + }, + "callNo": { + "type": "integer", + "format": "int32" + }, + "status": { + "type": "integer", + "format": "int32" + }, + "updatedAt": { + "type": "string", + "format": "datetime" + }, + "createdAt": { + "type": "string", + "format": "datetime" + }, + "progress": { + "type": "integer", + "format": "int32" + } + } + }, + "RuleList": { + "type": "object", + "properties": { + "data": { + "type": "array", + "items": { + "$ref": "#/components/schemas/RuleListItem" + } + }, + "total": { + "type": "integer", + "description": "列表的内容总数", + "format": "int32" + }, + "success": { + "type": "boolean" + } + } + }, + "FakeCaptcha": { + "type": "object", + "properties": { + "code": { + "type": "integer", + "format": "int32" + }, + "status": { + "type": "string" + } + } + }, + "LoginParams": { + "type": "object", + "properties": { + "username": { + "type": "string" + }, + "password": { + "type": "string" + }, + "autoLogin": { + "type": "boolean" + }, + "type": { + "type": "string" + } + } + }, + "ErrorResponse": { + "required": ["errorCode"], + "type": "object", + "properties": { + "errorCode": { + "type": "string", + "description": "业务约定的错误码" + }, + "errorMessage": { + "type": "string", + "description": "业务上的错误信息" + }, + "success": { + "type": "boolean", + "description": "业务上的请求是否成功" + } + } + }, + "NoticeIconList": { + "type": "object", + "properties": { + "data": { + "type": "array", + "items": { + "$ref": "#/components/schemas/NoticeIconItem" + } + }, + "total": { + "type": "integer", + "description": "列表的内容总数", + "format": "int32" + }, + "success": { + "type": "boolean" + } + } + }, + "NoticeIconItemType": { + "title": "NoticeIconItemType", + "description": "已读未读列表的枚举", + "type": "string", + "properties": {}, + "enum": ["notification", "message", "event"] + }, + "NoticeIconItem": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "extra": { + "type": "string", + "format": "any" + }, + "key": { "type": "string" }, + "read": { + "type": "boolean" + }, + "avatar": { + "type": "string" + }, + "title": { + "type": "string" + }, + "status": { + "type": "string" + }, + "datetime": { + "type": "string", + "format": "date" + }, + "description": { + "type": "string" + }, + "type": { + "extensions": { + "x-is-enum": true + }, + "$ref": "#/components/schemas/NoticeIconItemType" + } + } + } + } + } +} diff --git a/config/proxy.ts b/config/proxy.ts new file mode 100644 index 0000000..a8194b7 --- /dev/null +++ b/config/proxy.ts @@ -0,0 +1,34 @@ +/** + * 在生产环境 代理是无法生效的,所以这里没有生产环境的配置 + * ------------------------------- + * The agent cannot take effect in the production environment + * so there is no configuration of the production environment + * For details, please see + * https://pro.ant.design/docs/deploy + */ +export default { + dev: { + // localhost:8000/api/** -> https://preview.pro.ant.design/api/** + '/api/': { + // 要代理的地址 + target: 'https://preview.pro.ant.design', + // 配置了这个可以从 http 代理到 https + // 依赖 origin 的功能可能需要这个,比如 cookie + changeOrigin: true, + }, + }, + test: { + '/api/': { + target: 'https://proapi.azurewebsites.net', + changeOrigin: true, + pathRewrite: { '^': '' }, + }, + }, + pre: { + '/api/': { + target: 'your pre url', + changeOrigin: true, + pathRewrite: { '^': '' }, + }, + }, +}; diff --git a/config/routes.ts b/config/routes.ts new file mode 100644 index 0000000..ea518bb --- /dev/null +++ b/config/routes.ts @@ -0,0 +1,58 @@ +export default [ + { + path: '/user', + layout: false, + routes: [ + { + path: '/user', + routes: [ + { + name: 'login', + path: '/user/login', + component: './user/Login', + }, + ], + }, + { + component: './404', + }, + ], + }, + { + path: '/welcome', + name: 'welcome', + icon: 'smile', + component: './Welcome', + }, + { + path: '/admin', + name: 'admin', + icon: 'crown', + access: 'canAdmin', + component: './Admin', + routes: [ + { + path: '/admin/sub-page', + name: 'sub-page', + icon: 'smile', + component: './Welcome', + }, + { + component: './404', + }, + ], + }, + { + name: 'list.table-list', + icon: 'table', + path: '/list', + component: './TableList', + }, + { + path: '/', + redirect: '/welcome', + }, + { + component: './404', + }, +]; diff --git a/jest.config.js b/jest.config.js new file mode 100644 index 0000000..4729573 --- /dev/null +++ b/jest.config.js @@ -0,0 +1,9 @@ +module.exports = { + testURL: 'http://localhost:8000', + verbose: false, + extraSetupFiles: ['./tests/setupTests.js'], + globals: { + ANT_DESIGN_PRO_ONLY_DO_NOT_USE_IN_YOUR_PRODUCTION: false, + localStorage: null, + }, +}; diff --git a/jsconfig.json b/jsconfig.json new file mode 100644 index 0000000..197bee5 --- /dev/null +++ b/jsconfig.json @@ -0,0 +1,11 @@ +{ + "compilerOptions": { + "jsx": "react-jsx", + "emitDecoratorMetadata": true, + "experimentalDecorators": true, + "baseUrl": ".", + "paths": { + "@/*": ["./src/*"] + } + } +} diff --git a/mock/listTableList.ts b/mock/listTableList.ts new file mode 100644 index 0000000..08ed86d --- /dev/null +++ b/mock/listTableList.ts @@ -0,0 +1,174 @@ +import { Request, Response } from 'express'; +import moment from 'moment'; +import { parse } from 'url'; + +// mock tableListDataSource +const genList = (current: number, pageSize: number) => { + const tableListDataSource: API.RuleListItem[] = []; + + for (let i = 0; i < pageSize; i += 1) { + const index = (current - 1) * 10 + i; + tableListDataSource.push({ + key: index, + disabled: i % 6 === 0, + href: 'https://ant.design', + avatar: [ + 'https://gw.alipayobjects.com/zos/rmsportal/eeHMaZBwmTvLdIwMfBpg.png', + 'https://gw.alipayobjects.com/zos/rmsportal/udxAbMEhpwthVVcjLXik.png', + ][i % 2], + name: `TradeCode ${index}`, + owner: '曲丽丽', + desc: '这是一段描述', + callNo: Math.floor(Math.random() * 1000), + status: Math.floor(Math.random() * 10) % 4, + updatedAt: moment().format('YYYY-MM-DD'), + createdAt: moment().format('YYYY-MM-DD'), + progress: Math.ceil(Math.random() * 100), + }); + } + tableListDataSource.reverse(); + return tableListDataSource; +}; + +let tableListDataSource = genList(1, 100); + +function getRule(req: Request, res: Response, u: string) { + let realUrl = u; + if (!realUrl || Object.prototype.toString.call(realUrl) !== '[object String]') { + realUrl = req.url; + } + const { current = 1, pageSize = 10 } = req.query; + const params = parse(realUrl, true).query as unknown as API.PageParams & + API.RuleListItem & { + sorter: any; + filter: any; + }; + + let dataSource = [...tableListDataSource].slice( + ((current as number) - 1) * (pageSize as number), + (current as number) * (pageSize as number), + ); + if (params.sorter) { + const sorter = JSON.parse(params.sorter); + dataSource = dataSource.sort((prev, next) => { + let sortNumber = 0; + Object.keys(sorter).forEach((key) => { + if (sorter[key] === 'descend') { + if (prev[key] - next[key] > 0) { + sortNumber += -1; + } else { + sortNumber += 1; + } + return; + } + if (prev[key] - next[key] > 0) { + sortNumber += 1; + } else { + sortNumber += -1; + } + }); + return sortNumber; + }); + } + if (params.filter) { + const filter = JSON.parse(params.filter as any) as { + [key: string]: string[]; + }; + if (Object.keys(filter).length > 0) { + dataSource = dataSource.filter((item) => { + return Object.keys(filter).some((key) => { + if (!filter[key]) { + return true; + } + if (filter[key].includes(`${item[key]}`)) { + return true; + } + return false; + }); + }); + } + } + + if (params.name) { + dataSource = dataSource.filter((data) => data?.name?.includes(params.name || '')); + } + const result = { + data: dataSource, + total: tableListDataSource.length, + success: true, + pageSize, + current: parseInt(`${params.current}`, 10) || 1, + }; + + return res.json(result); +} + +function postRule(req: Request, res: Response, u: string, b: Request) { + let realUrl = u; + if (!realUrl || Object.prototype.toString.call(realUrl) !== '[object String]') { + realUrl = req.url; + } + + const body = (b && b.body) || req.body; + const { method, name, desc, key } = body; + + switch (method) { + /* eslint no-case-declarations:0 */ + case 'delete': + tableListDataSource = tableListDataSource.filter((item) => key.indexOf(item.key) === -1); + break; + case 'post': + (() => { + const i = Math.ceil(Math.random() * 10000); + const newRule: API.RuleListItem = { + key: tableListDataSource.length, + href: 'https://ant.design', + avatar: [ + 'https://gw.alipayobjects.com/zos/rmsportal/eeHMaZBwmTvLdIwMfBpg.png', + 'https://gw.alipayobjects.com/zos/rmsportal/udxAbMEhpwthVVcjLXik.png', + ][i % 2], + name, + owner: '曲丽丽', + desc, + callNo: Math.floor(Math.random() * 1000), + status: Math.floor(Math.random() * 10) % 2, + updatedAt: moment().format('YYYY-MM-DD'), + createdAt: moment().format('YYYY-MM-DD'), + progress: Math.ceil(Math.random() * 100), + }; + tableListDataSource.unshift(newRule); + return res.json(newRule); + })(); + return; + + case 'update': + (() => { + let newRule = {}; + tableListDataSource = tableListDataSource.map((item) => { + if (item.key === key) { + newRule = { ...item, desc, name }; + return { ...item, desc, name }; + } + return item; + }); + return res.json(newRule); + })(); + return; + default: + break; + } + + const result = { + list: tableListDataSource, + pagination: { + total: tableListDataSource.length, + }, + }; + + res.json(result); +} + +export default { + 'GET /api/rule': getRule, + 'POST /api/rule': postRule, +}; diff --git a/mock/notices.ts b/mock/notices.ts new file mode 100644 index 0000000..732dd58 --- /dev/null +++ b/mock/notices.ts @@ -0,0 +1,107 @@ +import { Request, Response } from 'express'; + +const getNotices = (req: Request, res: Response) => { + res.json({ + data: [ + { + id: '000000001', + avatar: 'https://gw.alipayobjects.com/zos/rmsportal/ThXAXghbEsBCCSDihZxY.png', + title: '你收到了 14 份新周报', + datetime: '2017-08-09', + type: 'notification', + }, + { + id: '000000002', + avatar: 'https://gw.alipayobjects.com/zos/rmsportal/OKJXDXrmkNshAMvwtvhu.png', + title: '你推荐的 曲妮妮 已通过第三轮面试', + datetime: '2017-08-08', + type: 'notification', + }, + { + id: '000000003', + avatar: 'https://gw.alipayobjects.com/zos/rmsportal/kISTdvpyTAhtGxpovNWd.png', + title: '这种模板可以区分多种通知类型', + datetime: '2017-08-07', + read: true, + type: 'notification', + }, + { + id: '000000004', + avatar: 'https://gw.alipayobjects.com/zos/rmsportal/GvqBnKhFgObvnSGkDsje.png', + title: '左侧图标用于区分不同的类型', + datetime: '2017-08-07', + type: 'notification', + }, + { + id: '000000005', + avatar: 'https://gw.alipayobjects.com/zos/rmsportal/ThXAXghbEsBCCSDihZxY.png', + title: '内容不要超过两行字,超出时自动截断', + datetime: '2017-08-07', + type: 'notification', + }, + { + id: '000000006', + avatar: 'https://gw.alipayobjects.com/zos/rmsportal/fcHMVNCjPOsbUGdEduuv.jpeg', + title: '曲丽丽 评论了你', + description: '描述信息描述信息描述信息', + datetime: '2017-08-07', + type: 'message', + clickClose: true, + }, + { + id: '000000007', + avatar: 'https://gw.alipayobjects.com/zos/rmsportal/fcHMVNCjPOsbUGdEduuv.jpeg', + title: '朱偏右 回复了你', + description: '这种模板用于提醒谁与你发生了互动,左侧放『谁』的头像', + datetime: '2017-08-07', + type: 'message', + clickClose: true, + }, + { + id: '000000008', + avatar: 'https://gw.alipayobjects.com/zos/rmsportal/fcHMVNCjPOsbUGdEduuv.jpeg', + title: '标题', + description: '这种模板用于提醒谁与你发生了互动,左侧放『谁』的头像', + datetime: '2017-08-07', + type: 'message', + clickClose: true, + }, + { + id: '000000009', + title: '任务名称', + description: '任务需要在 2017-01-12 20:00 前启动', + extra: '未开始', + status: 'todo', + type: 'event', + }, + { + id: '000000010', + title: '第三方紧急代码变更', + description: '冠霖提交于 2017-01-06,需在 2017-01-07 前完成代码变更任务', + extra: '马上到期', + status: 'urgent', + type: 'event', + }, + { + id: '000000011', + title: '信息安全考试', + description: '指派竹尔于 2017-01-09 前完成更新并发布', + extra: '已耗时 8 天', + status: 'doing', + type: 'event', + }, + { + id: '000000012', + title: 'ABCD 版本发布', + description: '冠霖提交于 2017-01-06,需在 2017-01-07 前完成代码变更任务', + extra: '进行中', + status: 'processing', + type: 'event', + }, + ], + }); +}; + +export default { + 'GET /api/notices': getNotices, +}; diff --git a/mock/route.ts b/mock/route.ts new file mode 100644 index 0000000..418d10f --- /dev/null +++ b/mock/route.ts @@ -0,0 +1,5 @@ +export default { + '/api/auth_routes': { + '/form/advanced-form': { authority: ['admin', 'user'] }, + }, +}; diff --git a/mock/user.ts b/mock/user.ts new file mode 100644 index 0000000..75edd34 --- /dev/null +++ b/mock/user.ts @@ -0,0 +1,203 @@ +import { Request, Response } from 'express'; + +const waitTime = (time: number = 100) => { + return new Promise((resolve) => { + setTimeout(() => { + resolve(true); + }, time); + }); +}; + +async function getFakeCaptcha(req: Request, res: Response) { + await waitTime(2000); + return res.json('captcha-xxx'); +} + +const { ANT_DESIGN_PRO_ONLY_DO_NOT_USE_IN_YOUR_PRODUCTION } = process.env; + +/** + * 当前用户的权限,如果为空代表没登录 + * current user access, if is '', user need login + * 如果是 pro 的预览,默认是有权限的 + */ +let access = ANT_DESIGN_PRO_ONLY_DO_NOT_USE_IN_YOUR_PRODUCTION === 'site' ? 'admin' : ''; + +const getAccess = () => { + return access; +}; + +// 代码中会兼容本地 service mock 以及部署站点的静态数据 +export default { + // 支持值为 Object 和 Array + 'GET /api/currentUser': (req: Request, res: Response) => { + if (!getAccess()) { + res.status(401).send({ + data: { + isLogin: false, + }, + errorCode: '401', + errorMessage: '请先登录!', + success: true, + }); + return; + } + res.send({ + success: true, + data: { + name: 'Serati Ma', + avatar: 'https://gw.alipayobjects.com/zos/antfincdn/XAosXuNZyF/BiazfanxmamNRoxxVxka.png', + userid: '00000001', + email: 'antdesign@alipay.com', + signature: '海纳百川,有容乃大', + title: '交互专家', + group: '蚂蚁金服-某某某事业群-某某平台部-某某技术部-UED', + tags: [ + { + key: '0', + label: '很有想法的', + }, + { + key: '1', + label: '专注设计', + }, + { + key: '2', + label: '辣~', + }, + { + key: '3', + label: '大长腿', + }, + { + key: '4', + label: '川妹子', + }, + { + key: '5', + label: '海纳百川', + }, + ], + notifyCount: 12, + unreadCount: 11, + country: 'China', + access: getAccess(), + geographic: { + province: { + label: '浙江省', + key: '330000', + }, + city: { + label: '杭州市', + key: '330100', + }, + }, + address: '西湖区工专路 77 号', + phone: '0752-268888888', + }, + }); + }, + // GET POST 可省略 + 'GET /api/users': [ + { + key: '1', + name: 'John Brown', + age: 32, + address: 'New York No. 1 Lake Park', + }, + { + key: '2', + name: 'Jim Green', + age: 42, + address: 'London No. 1 Lake Park', + }, + { + key: '3', + name: 'Joe Black', + age: 32, + address: 'Sidney No. 1 Lake Park', + }, + ], + 'POST /api/login/account': async (req: Request, res: Response) => { + const { password, username, type } = req.body; + await waitTime(2000); + if (password === 'ant.design' && username === 'admin') { + res.send({ + status: 'ok', + type, + currentAuthority: 'admin', + }); + access = 'admin'; + return; + } + if (password === 'ant.design' && username === 'user') { + res.send({ + status: 'ok', + type, + currentAuthority: 'user', + }); + access = 'user'; + return; + } + if (type === 'mobile') { + res.send({ + status: 'ok', + type, + currentAuthority: 'admin', + }); + access = 'admin'; + return; + } + + res.send({ + status: 'error', + type, + currentAuthority: 'guest', + }); + access = 'guest'; + }, + 'POST /api/login/outLogin': (req: Request, res: Response) => { + access = ''; + res.send({ data: {}, success: true }); + }, + 'POST /api/register': (req: Request, res: Response) => { + res.send({ status: 'ok', currentAuthority: 'user', success: true }); + }, + 'GET /api/500': (req: Request, res: Response) => { + res.status(500).send({ + timestamp: 1513932555104, + status: 500, + error: 'error', + message: 'error', + path: '/base/category/list', + }); + }, + 'GET /api/404': (req: Request, res: Response) => { + res.status(404).send({ + timestamp: 1513932643431, + status: 404, + error: 'Not Found', + message: 'No message available', + path: '/base/category/list/2121212', + }); + }, + 'GET /api/403': (req: Request, res: Response) => { + res.status(403).send({ + timestamp: 1513932555104, + status: 403, + error: 'Forbidden', + message: 'Forbidden', + path: '/base/category/list', + }); + }, + 'GET /api/401': (req: Request, res: Response) => { + res.status(401).send({ + timestamp: 1513932555104, + status: 401, + error: 'Unauthorized', + message: 'Unauthorized', + path: '/base/category/list', + }); + }, + + 'GET /api/login/captcha': getFakeCaptcha, +}; diff --git a/package.json b/package.json new file mode 100644 index 0000000..6b2f975 --- /dev/null +++ b/package.json @@ -0,0 +1,109 @@ +{ + "name": "ant-design-pro", + "version": "5.2.0", + "private": true, + "description": "An out-of-box UI solution for enterprise applications", + "scripts": { + "analyze": "cross-env ANALYZE=1 umi build", + "build": "umi build", + "deploy": "npm run build && npm run gh-pages", + "dev": "npm run start:dev", + "gh-pages": "gh-pages -d dist", + "i18n-remove": "pro i18n-remove --locale=zh-CN --write", + "postinstall": "umi g tmp", + "lint": "umi g tmp && npm run lint:js && npm run lint:style && npm run lint:prettier && npm run tsc", + "lint-staged": "lint-staged", + "lint-staged:js": "eslint --ext .js,.jsx,.ts,.tsx ", + "lint:fix": "eslint --fix --cache --ext .js,.jsx,.ts,.tsx --format=pretty ./src && npm run lint:style", + "lint:js": "eslint --cache --ext .js,.jsx,.ts,.tsx --format=pretty ./src", + "lint:prettier": "prettier -c --write \"src/**/*\" --end-of-line auto", + "lint:style": "stylelint --fix \"src/**/*.less\" --syntax less", + "openapi": "umi openapi", + "playwright": "playwright install && playwright test", + "precommit": "lint-staged", + "prettier": "prettier -c --write \"src/**/*\"", + "serve": "umi-serve", + "start": "cross-env UMI_ENV=dev umi dev", + "start:dev": "cross-env REACT_APP_ENV=dev MOCK=none UMI_ENV=dev umi dev", + "start:no-mock": "cross-env MOCK=none UMI_ENV=dev umi dev", + "start:no-ui": "cross-env UMI_UI=none UMI_ENV=dev umi dev", + "start:pre": "cross-env REACT_APP_ENV=pre UMI_ENV=dev umi dev", + "start:test": "cross-env REACT_APP_ENV=test MOCK=none UMI_ENV=dev umi dev", + "test": "umi test", + "test:component": "umi test ./src/components", + "test:e2e": "node ./tests/run-tests.js", + "tsc": "tsc --noEmit" + }, + "lint-staged": { + "**/*.less": "stylelint --syntax less", + "**/*.{js,jsx,ts,tsx}": "npm run lint-staged:js", + "**/*.{js,jsx,tsx,ts,less,md,json}": [ + "prettier --write" + ] + }, + "browserslist": [ + "> 1%", + "last 2 versions", + "not ie <= 10" + ], + "dependencies": { + "@ant-design/icons": "^4.7.0", + "@ant-design/pro-card": "^1.19.0", + "@ant-design/pro-descriptions": "^1.10.0", + "@ant-design/pro-form": "^1.60.0", + "@ant-design/pro-layout": "^6.34.0", + "@ant-design/pro-table": "^2.69.0", + "@umijs/route-utils": "^2.0.0", + "antd": "^4.18.0", + "classnames": "^2.3.0", + "lodash": "^4.17.0", + "moment": "^2.29.0", + "omit.js": "^2.0.2", + "rc-menu": "^9.1.0", + "rc-util": "^5.16.0", + "react": "^17.0.0", + "react-dev-inspector": "^1.7.0", + "react-dom": "^17.0.0", + "react-helmet-async": "^1.2.0", + "umi": "^3.5.0" + }, + "devDependencies": { + "@ant-design/pro-cli": "^2.1.0", + "@playwright/test": "^1.17.0", + "@types/express": "^4.17.0", + "@types/history": "^4.7.0", + "@types/jest": "^26.0.0", + "@types/lodash": "^4.14.0", + "@types/react": "^17.0.0", + "@types/react-dom": "^17.0.0", + "@types/react-helmet": "^6.1.0", + "@umijs/fabric": "^2.8.0", + "@umijs/openapi": "^1.3.0", + "@umijs/plugin-blocks": "^2.2.0", + "@umijs/plugin-esbuild": "^1.4.0", + "@umijs/plugin-openapi": "^1.3.0", + "@umijs/preset-ant-design-pro": "^1.3.0", + "@umijs/preset-dumi": "^1.1.0", + "@umijs/preset-react": "^1.8.17", + "@umijs/yorkie": "^2.0.5", + "cross-env": "^7.0.0", + "cross-port-killer": "^1.3.0", + "detect-installer": "^1.0.0", + "eslint": "^7.32.0", + "gh-pages": "^3.2.0", + "jsdom-global": "^3.0.0", + "lint-staged": "^10.0.0", + "mockjs": "^1.1.0", + "prettier": "^2.5.0", + "stylelint": "^13.0.0", + "swagger-ui-react": "^3.52.0", + "typescript": "^4.5.0", + "umi-serve": "^1.9.10" + }, + "engines": { + "node": ">=12.0.0" + }, + "gitHooks": { + "commit-msg": "fabric verify-commit" + } +} diff --git a/playwright.config.ts b/playwright.config.ts new file mode 100644 index 0000000..ec1b31d --- /dev/null +++ b/playwright.config.ts @@ -0,0 +1,22 @@ +// playwright.config.ts +import type { PlaywrightTestConfig } from '@playwright/test'; +import { devices } from '@playwright/test'; + +const config: PlaywrightTestConfig = { + forbidOnly: !!process.env.CI, + retries: process.env.CI ? 2 : 0, + use: { + trace: 'on-first-retry', + }, + projects: [ + { + name: 'chromium', + use: { ...devices['Desktop Chrome'] }, + }, + { + name: 'firefox', + use: { ...devices['Desktop Firefox'] }, + }, + ], +}; +export default config; diff --git a/public/CNAME b/public/CNAME new file mode 100644 index 0000000..30c2d4d --- /dev/null +++ b/public/CNAME @@ -0,0 +1 @@ +preview.pro.ant.design \ No newline at end of file diff --git a/public/favicon.ico b/public/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..e2e93252988b5bed120ddeb168eb577bf0caa881 GIT binary patch literal 4286 zcmeHLO(?8k6#ga#Gs=ufvobTd=q@y~nOLdYlqL%nQY4WqC`)z}3y~cqOAAF4VkQd~ z^3$wrC}~D1l%!eMp!|gA&U5sN?~b{2>)yNg-s#Lc-+9k_p7Y<=XqqYcDJs(Vt%dBG z=F~Lpdy*pd!Y zozZT}udJ-#| zqiAVq0sT@kAMMobbULxOw+F6{j*egO8CQ?TgTuo^+}_@zrlv;l$*-)e#PRVl?(Xgo zi9`?zg>ZFsh0DuJ)Ya9!von7LPN@OfP9IfH_4tet&&I~a#O{TK1<3*LO-xJ( zZ*Fc5%v&m4#Z&mZySs8{XJ-fV^Yh@{p`ju8d_J)w6+U%`!(qAC+uJK~qg_o+O|QC` zTVg0Y{jSHqzP^^&G0t4^cwF+$`egj?@9$AtTMLuPB>7!lUKY8&zP?xd=H_PM(U;HS ztNi!(_oJes0<5X6tu4{BxVR{FO)_x3UauFkv$I%RTSHe@7dAIHF+4mhc?|>t(!XbCXVQZk z8yje7Xh2R*&WCwdw&?N4$H!4$Uyq)i9_;V$3;y)^-LKhmkv{Z#(CP^-7Wn# zI5-H>z`y`nTU%u=f7Tbh-mg5fPT0G8`Wx6^vD1(>-r0;AFlyjWYC!!D@UQlt)%}4^ literal 0 HcmV?d00001 diff --git a/public/icons/icon-128x128.png b/public/icons/icon-128x128.png new file mode 100644 index 0000000000000000000000000000000000000000..48d0e2339a60a637b94319c65e8654289b4f4b6c GIT binary patch literal 1329 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

*C7T$?5!b= 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 28 Copy 5Created with Sketch. \ No newline at end of file diff --git a/public/pro_icon.svg b/public/pro_icon.svg new file mode 100644 index 0000000..e075b78 --- /dev/null +++ b/public/pro_icon.svg @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/src/access.ts b/src/access.ts new file mode 100644 index 0000000..e823e24 --- /dev/null +++ b/src/access.ts @@ -0,0 +1,9 @@ +/** + * @see https://umijs.org/zh-CN/plugins/plugin-access + * */ +export default function access(initialState: { currentUser?: API.CurrentUser } | undefined) { + const { currentUser } = initialState ?? {}; + return { + canAdmin: currentUser && currentUser.access === 'admin', + }; +} diff --git a/src/app.tsx b/src/app.tsx new file mode 100644 index 0000000..5857101 --- /dev/null +++ b/src/app.tsx @@ -0,0 +1,108 @@ +import type { Settings as LayoutSettings } from '@ant-design/pro-layout'; +import { SettingDrawer } from '@ant-design/pro-layout'; +import { PageLoading } from '@ant-design/pro-layout'; +import type { RunTimeLayoutConfig } from 'umi'; +import { history, Link } from 'umi'; +import RightContent from '@/components/RightContent'; +import Footer from '@/components/Footer'; +import { currentUser as queryCurrentUser } from './services/ant-design-pro/api'; +import { BookOutlined, LinkOutlined } from '@ant-design/icons'; +import defaultSettings from '../config/defaultSettings'; + +const isDev = process.env.NODE_ENV === 'development'; +const loginPath = '/user/login'; + +/** 获取用户信息比较慢的时候会展示一个 loading */ +export const initialStateConfig = { + loading: , +}; + +/** + * @see https://umijs.org/zh-CN/plugins/plugin-initial-state + * */ +export async function getInitialState(): Promise<{ + settings?: Partial; + currentUser?: API.CurrentUser; + loading?: boolean; + fetchUserInfo?: () => Promise; +}> { + const fetchUserInfo = async () => { + try { + const msg = await queryCurrentUser(); + return msg.data; + } catch (error) { + history.push(loginPath); + } + return undefined; + }; + // 如果不是登录页面,执行 + if (history.location.pathname !== loginPath) { + const currentUser = await fetchUserInfo(); + return { + fetchUserInfo, + currentUser, + settings: defaultSettings, + }; + } + return { + fetchUserInfo, + settings: defaultSettings, + }; +} + +// ProLayout 支持的api https://procomponents.ant.design/components/layout +export const layout: RunTimeLayoutConfig = ({ initialState, setInitialState }) => { + return { + rightContentRender: () => , + disableContentMargin: false, + waterMarkProps: { + content: initialState?.currentUser?.name, + }, + footerRender: () =>