From 336c03e8683d26d0c0fad4ae4a1fcbdd4ecc4231 Mon Sep 17 00:00:00 2001 From: eric Date: Mon, 14 Nov 2022 21:19:37 +0800 Subject: [PATCH] feat: init --- README.md | 105 ++-- config/config.ts | 136 ++++ config/defaultSettings.ts | 24 + config/oneapi.json | 593 ++++++++++++++++++ config/proxy.ts | 33 + config/routes.ts | 61 ++ jsconfig.json | 11 + mock/listTableList.ts | 174 +++++ mock/notices.ts | 107 ++++ mock/route.ts | 5 + mock/user.ts | 203 ++++++ package.json | 82 +++ 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 | 129 ++++ src/components/Footer/index.tsx | 44 ++ src/components/HeaderDropdown/index.less | 16 + src/components/HeaderDropdown/index.tsx | 21 + src/components/HeaderSearch/index.less | 25 + src/components/HeaderSearch/index.tsx | 101 +++ src/components/NoticeIcon/NoticeIcon.tsx | 126 ++++ src/components/NoticeIcon/NoticeList.less | 103 +++ src/components/NoticeIcon/NoticeList.tsx | 112 ++++ src/components/NoticeIcon/index.less | 35 ++ src/components/NoticeIcon/index.tsx | 152 +++++ .../RightContent/AvatarDropdown.tsx | 115 ++++ src/components/RightContent/index.less | 82 +++ src/components/RightContent/index.tsx | 62 ++ src/components/index.md | 267 ++++++++ src/e2e/baseLayout.e2e.spec.ts | 44 ++ src/global.less | 50 ++ 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 | 68 ++ 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 | 68 ++ 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 | 17 + 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 | 65 ++ 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/pages.ts | 65 ++ 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 | 397 ++++++++++++ src/pages/User/Login/index.less | 48 ++ src/pages/User/Login/index.tsx | 298 +++++++++ src/pages/Welcome.tsx | 154 +++++ src/requestErrorConfig.ts | 109 ++++ 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 | 153 +++++ src/services/swagger/store.ts | 48 ++ src/services/swagger/typings.d.ts | 112 ++++ src/services/swagger/user.ts | 100 +++ src/typings.d.ts | 24 + tests/run-tests.js | 47 ++ tests/setupTests.js | 10 + tsconfig.json | 41 ++ 126 files changed, 7301 insertions(+), 70 deletions(-) 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 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/pages.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/User/Login/index.less create mode 100644 src/pages/User/Login/index.tsx create mode 100644 src/pages/Welcome.tsx create mode 100644 src/requestErrorConfig.ts 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/README.md b/README.md index 04fd8e1..4c89a72 100644 --- a/README.md +++ b/README.md @@ -1,92 +1,57 @@ -# city-admin +# 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 -## Getting started +Install `node_modules`: -To make it easy for you to get started with GitLab, here's a list of recommended next steps. - -Already a pro? Just edit this README.md and make it your own. Want to make it easy? [Use the template at the bottom](#editing-this-readme)! - -## Add your files - -- [ ] [Create](https://docs.gitlab.com/ee/user/project/repository/web_editor.html#create-a-file) or [upload](https://docs.gitlab.com/ee/user/project/repository/web_editor.html#upload-a-file) files -- [ ] [Add files using the command line](https://docs.gitlab.com/ee/gitlab-basics/add-file.html#add-a-file-using-the-command-line) or push an existing Git repository with the following command: - -``` -cd existing_repo -git remote add origin http://gitlab.yidooplanet.com/gitlab-instance-0a899031/city-admin.git -git branch -M main -git push -uf origin main +```bash +npm install ``` -## Integrate with your tools +or -- [ ] [Set up project integrations](http://gitlab.yidooplanet.com/gitlab-instance-0a899031/city-admin/-/settings/integrations) +```bash +yarn +``` -## Collaborate with your team +## Provided Scripts -- [ ] [Invite team members and collaborators](https://docs.gitlab.com/ee/user/project/members/) -- [ ] [Create a new merge request](https://docs.gitlab.com/ee/user/project/merge_requests/creating_merge_requests.html) -- [ ] [Automatically close issues from merge requests](https://docs.gitlab.com/ee/user/project/issues/managing_issues.html#closing-issues-automatically) -- [ ] [Enable merge request approvals](https://docs.gitlab.com/ee/user/project/merge_requests/approvals/) -- [ ] [Automatically merge when pipeline succeeds](https://docs.gitlab.com/ee/user/project/merge_requests/merge_when_pipeline_succeeds.html) +Ant Design Pro provides some useful script to help you quick start and build with web project, code style check and test. -## Test and Deploy +Scripts provided in `package.json`. It's safe to modify or add additional script: -Use the built-in continuous integration in GitLab. +### Start project -- [ ] [Get started with GitLab CI/CD](https://docs.gitlab.com/ee/ci/quick_start/index.html) -- [ ] [Analyze your code for known vulnerabilities with Static Application Security Testing(SAST)](https://docs.gitlab.com/ee/user/application_security/sast/) -- [ ] [Deploy to Kubernetes, Amazon EC2, or Amazon ECS using Auto Deploy](https://docs.gitlab.com/ee/topics/autodevops/requirements.html) -- [ ] [Use pull-based deployments for improved Kubernetes management](https://docs.gitlab.com/ee/user/clusters/agent/) -- [ ] [Set up protected environments](https://docs.gitlab.com/ee/ci/environments/protected_environments.html) +```bash +npm start +``` -*** +### Build project -# Editing this README +```bash +npm run build +``` -When you're ready to make this README your own, just edit this file and use the handy template below (or feel free to structure it however you want - this is just a starting point!). Thank you to [makeareadme.com](https://www.makeareadme.com/) for this template. +### Check code style -## Suggestions for a good README -Every project is different, so consider which of these sections apply to yours. The sections used in the template are suggestions for most open source projects. Also keep in mind that while a README can be too long and detailed, too long is better than too short. If you think your README is too long, consider utilizing another form of documentation rather than cutting out information. +```bash +npm run lint +``` -## Name -Choose a self-explaining name for your project. +You can also use script to auto fix some lint error: -## Description -Let people know what your project can do specifically. Provide context and add a link to any reference visitors might be unfamiliar with. A list of Features or a Background subsection can also be added here. If there are alternatives to your project, this is a good place to list differentiating factors. +```bash +npm run lint:fix +``` -## Badges -On some READMEs, you may see small images that convey metadata, such as whether or not all the tests are passing for the project. You can use Shields to add some to your README. Many services also have instructions for adding a badge. +### Test code -## Visuals -Depending on what you are making, it can be a good idea to include screenshots or even a video (you'll frequently see GIFs rather than actual videos). Tools like ttygif can help, but check out Asciinema for a more sophisticated method. +```bash +npm test +``` -## Installation -Within a particular ecosystem, there may be a common way of installing things, such as using Yarn, NuGet, or Homebrew. However, consider the possibility that whoever is reading your README is a novice and would like more guidance. Listing specific steps helps remove ambiguity and gets people to using your project as quickly as possible. If it only runs in a specific context like a particular programming language version or operating system or has dependencies that have to be installed manually, also add a Requirements subsection. +## More -## Usage -Use examples liberally, and show the expected output if you can. It's helpful to have inline the smallest example of usage that you can demonstrate, while providing links to more sophisticated examples if they are too long to reasonably include in the README. - -## Support -Tell people where they can go to for help. It can be any combination of an issue tracker, a chat room, an email address, etc. - -## Roadmap -If you have ideas for releases in the future, it is a good idea to list them in the README. - -## Contributing -State if you are open to contributions and what your requirements are for accepting them. - -For people who want to make changes to your project, it's helpful to have some documentation on how to get started. Perhaps there is a script that they should run or some environment variables that they need to set. Make these steps explicit. These instructions could also be useful to your future self. - -You can also document commands to lint the code or run tests. These steps help to ensure high code quality and reduce the likelihood that the changes inadvertently break something. Having instructions for running tests is especially helpful if it requires external setup, such as starting a Selenium server for testing in a browser. - -## Authors and acknowledgment -Show your appreciation to those who have contributed to the project. - -## License -For open source projects, say how it is licensed. - -## Project status -If you have run out of energy or time for your project, put a note at the top of the README saying that development has slowed down or stopped completely. Someone may choose to fork your project or volunteer to step in as a maintainer or owner, allowing your project to keep going. You can also make an explicit request for maintainers. +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.ts b/config/config.ts new file mode 100644 index 0000000..4a4067c --- /dev/null +++ b/config/config.ts @@ -0,0 +1,136 @@ +// https://umijs.org/config/ +import { defineConfig } from '@umijs/max'; +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({ + /** + * @name 开启 hash 模式 + * @description 让 build 之后的产物包含 hash 后缀。通常用于增量发布和避免浏览器加载缓存。 + * @doc https://umijs.org/docs/api/config#hash + */ + hash: true, + + /** + * @name 兼容性设置 + * @description 设置 ie11 不一定完美兼容,需要检查自己使用的所有依赖 + * @doc https://umijs.org/docs/api/config#targets + */ + // targets: { + // ie: 11, + // }, + /** + * @name 路由的配置,不在路由中引入的文件不会编译 + * @description 只支持 path,component,routes,redirect,wrappers,title 的配置 + * @doc https://umijs.org/docs/guides/routes + */ + // umi routes: https://umijs.org/docs/routing + routes, + /** + * @name 主题的配置 + * @description 虽然叫主题,但是其实只是 less 的变量设置 + * @doc antd的主题设置 https://ant.design/docs/react/customize-theme-cn + * @doc umi 的theme 配置 https://umijs.org/docs/api/config#theme + */ + theme: { + // 如果不想要 configProvide 动态设置主题需要把这个设置为 default + // 只有设置为 variable, 才能使用 configProvide 动态设置主色调 + 'root-entry-name': 'variable', + }, + /** + * @name moment 的国际化配置 + * @description 如果对国际化没有要求,打开之后能减少js的包大小 + * @doc https://umijs.org/docs/api/config#ignoremomentlocale + */ + ignoreMomentLocale: true, + /** + * @name 代理配置 + * @description 可以让你的本地服务器代理到你的服务器上,这样你就可以访问服务器的数据了 + * @see 要注意以下 代理只能在本地开发时使用,build 之后就无法使用了。 + * @doc 代理介绍 https://umijs.org/docs/guides/proxy + * @doc 代理配置 https://umijs.org/docs/api/config#proxy + */ + proxy: proxy[REACT_APP_ENV || 'dev'], + /** + * @name 快速热更新配置 + * @description 一个不错的热更新组件,更新时可以保留 state + */ + fastRefresh: true, + //============== 以下都是max的插件配置 =============== + /** + * @name 数据流插件 + * @@doc https://umijs.org/docs/max/data-flow + */ + model: {}, + /** + * 一个全局的初始数据流,可以用它在插件之间共享数据 + * @description 可以用来存放一些全局的数据,比如用户信息,或者一些全局的状态,全局初始状态在整个 Umi 项目的最开始创建。 + * @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 + */ + initialState: {}, + /** + * @name layout 插件 + * @doc https://umijs.org/docs/max/layout-menu + */ + layout: { + locale: true, + ...defaultSettings, + }, + /** + * @name 国际化插件 + * @doc https://umijs.org/docs/max/i18n + */ + locale: { + // default zh-CN + default: 'zh-CN', + antd: true, + // default true, when it is true, will use `navigator.language` overwrite default + baseNavigator: true, + }, + /** + * @name antd 插件 + * @description 内置了 babel import 插件 + * @doc https://umijs.org/docs/max/antd#antd + */ + antd: {}, + /** + * @name 网络请求配置 + * @description 它基于 axios 和 ahooks 的 useRequest 提供了一套统一的网络请求和错误处理方案。 + * @doc https://umijs.org/docs/max/request + */ + request: {}, + /** + * @name 权限插件 + * @description 基于 initialState 的权限插件,必须先打开 initialState + * @doc https://umijs.org/docs/max/access + */ + access: {}, + //================ pro 插件配置 ================= + presets: ['umi-presets-pro'], + /** + * @name openAPI 插件的配置 + * @description 基于 openapi 的规范生成serve 和mock,能减少很多样板代码 + * @doc https://pro.ant.design/zh-cn/docs/openapi/ + */ + openAPI: [ + { + requestLibPath: "import { request } from '@umijs/max'", + // 或者使用在线的版本 + // schemaPath: "https://gw.alipayobjects.com/os/antfincdn/M%24jrzTTYJN/oneapi.json" + schemaPath: join(__dirname, 'oneapi.json'), + mock: false, + }, + { + requestLibPath: "import { request } from '@umijs/max'", + schemaPath: 'https://gw.alipayobjects.com/os/antfincdn/CA1dOm%2631B/openapi.json', + projectName: 'swagger', + }, + ], + mfsu: { + exclude :['@playwright/test'] + }, +}); diff --git a/config/defaultSettings.ts b/config/defaultSettings.ts new file mode 100644 index 0000000..6bd3ddf --- /dev/null +++ b/config/defaultSettings.ts @@ -0,0 +1,24 @@ +import { Settings as LayoutSettings } from '@ant-design/pro-components'; + +/** + * @name + */ +const Settings: LayoutSettings & { + pwa?: boolean; + logo?: string; +} = { + navTheme: 'light', + // 拂晓蓝 + colorPrimary: '#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..59e3d82 --- /dev/null +++ b/config/proxy.ts @@ -0,0 +1,33 @@ +/** + * @name 代理的配置 + * @see 在生产环境 代理是无法生效的,所以这里没有生产环境的配置 + * ------------------------------- + * 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 + * + * @doc https://umijs.org/docs/guides/proxy + */ +export default { + dev: {}, + /** + * @name 详细的代理配置 + * @doc https://github.com/chimurai/http-proxy-middleware + */ + test: { + // localhost:8000/api/** -> https://preview.pro.ant.design/api/** + '/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..f4d076a --- /dev/null +++ b/config/routes.ts @@ -0,0 +1,61 @@ +/** + * @name umi 的路由配置 + * @description 只支持 path,component,routes,redirect,wrappers,title 的配置 + * @param path path 只支持两种占位符配置,第一种是动态参数 :id 的形式,第二种是 * 通配符,通配符只能出现路由字符串的最后。 + * @param component 配置 location 和 path 匹配后用于渲染的 React 组件路径。可以是绝对路径,也可以是相对路径,如果是相对路径,会从 src/pages 开始找起。 + * @param routes 配置子路由,通常在需要为多个路径增加 layout 组件时使用。 + * @param redirect 配置路由跳转 + * @param wrappers 配置路由组件的包装组件,通过包装组件可以为当前的路由组件组合进更多的功能。 比如,可以用于路由级别的权限校验 + * @doc https://umijs.org/docs/guides/routes + */ +export default [ + { + path: '/user', + layout: false, + routes: [ + { + name: 'login', + path: '/user/login', + component: './User/Login', + }, + ], + }, + { + path: '/welcome', + name: 'welcome', + icon: 'smile', + component: './Welcome', + }, + { + path: '/admin', + name: 'admin', + icon: 'crown', + access: 'canAdmin', + routes: [ + { + path: '/admin', + redirect: '/admin/sub-page', + }, + { + path: '/admin/sub-page', + name: 'sub-page', + component: './Admin', + }, + ], + }, + { + name: 'list.table-list', + icon: 'table', + path: '/list', + component: './TableList', + }, + { + path: '/', + redirect: '/welcome', + }, + { + path: '*', + layout: false, + component: './404', + }, +]; 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..61858bd --- /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://mdn.alipayobjects.com/yuyan_qk0oxh/afts/img/MSbDR4FR2MUAAAAAAAAAAAAAFl94AQBr', + title: '你收到了 14 份新周报', + datetime: '2017-08-09', + type: 'notification', + }, + { + id: '000000002', + avatar: 'https://mdn.alipayobjects.com/yuyan_qk0oxh/afts/img/hX-PTavYIq4AAAAAAAAAAAAAFl94AQBr', + title: '你推荐的 曲妮妮 已通过第三轮面试', + datetime: '2017-08-08', + type: 'notification', + }, + { + id: '000000003', + avatar: 'https://mdn.alipayobjects.com/yuyan_qk0oxh/afts/img/jHX5R5l3QjQAAAAAAAAAAAAAFl94AQBr', + title: '这种模板可以区分多种通知类型', + datetime: '2017-08-07', + read: true, + type: 'notification', + }, + { + id: '000000004', + avatar: 'https://mdn.alipayobjects.com/yuyan_qk0oxh/afts/img/Wr4mQqx6jfwAAAAAAAAAAAAAFl94AQBr', + title: '左侧图标用于区分不同的类型', + datetime: '2017-08-07', + type: 'notification', + }, + { + id: '000000005', + avatar: 'https://mdn.alipayobjects.com/yuyan_qk0oxh/afts/img/Mzj_TbcWUj4AAAAAAAAAAAAAFl94AQBr', + title: '内容不要超过两行字,超出时自动截断', + datetime: '2017-08-07', + type: 'notification', + }, + { + id: '000000006', + avatar: 'https://mdn.alipayobjects.com/yuyan_qk0oxh/afts/img/eXLzRbPqQE4AAAAAAAAAAAAAFl94AQBr', + title: '曲丽丽 评论了你', + description: '描述信息描述信息描述信息', + datetime: '2017-08-07', + type: 'message', + clickClose: true, + }, + { + id: '000000007', + avatar: 'https://mdn.alipayobjects.com/yuyan_qk0oxh/afts/img/w5mRQY2AmEEAAAAAAAAAAAAAFl94AQBr', + title: '朱偏右 回复了你', + description: '这种模板用于提醒谁与你发生了互动,左侧放『谁』的头像', + datetime: '2017-08-07', + type: 'message', + clickClose: true, + }, + { + id: '000000008', + avatar: 'https://mdn.alipayobjects.com/yuyan_qk0oxh/afts/img/wPadR5M9918AAAAAAAAAAAAAFl94AQBr', + 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..0a0595f --- /dev/null +++ b/package.json @@ -0,0 +1,82 @@ +{ + "name": "ant-design-pro", + "version": "6.0.0-beta.1", + "private": true, + "description": "An out-of-box UI solution for enterprise applications", + "scripts": { + "analyze": "cross-env ANALYZE=1 max build", + "build": "max 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": "max setup", + "lint": "npm run lint:js && 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 ", + "lint:js": "eslint --cache --ext .js,.jsx,.ts,.tsx --format=pretty ./src", + "lint:prettier": "prettier -c --write \"src/**/*\" --end-of-line auto", + "openapi": "max openapi", + "playwright": "playwright install && playwright test", + "prepare": "husky install", + "prettier": "prettier -c --write \"src/**/*\"", + "serve": "umi-serve", + "start": "cross-env UMI_ENV=dev max dev", + "start:dev": "cross-env REACT_APP_ENV=dev MOCK=none UMI_ENV=dev max dev", + "start:no-mock": "cross-env MOCK=none UMI_ENV=dev max dev", + "start:pre": "cross-env REACT_APP_ENV=pre UMI_ENV=dev max dev", + "start:test": "cross-env REACT_APP_ENV=test MOCK=none UMI_ENV=dev max dev", + "test:e2e": "node ./tests/run-tests.js", + "tsc": "tsc --noEmit" + }, + "lint-staged": { + "**/*.{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-components": "^2.3.13", + "@umijs/route-utils": "^2.1.3", + "antd": "^4.23.3", + "classnames": "^2.3.2", + "lodash": "^4.17.21", + "moment": "^2.29.4", + "omit.js": "^2.0.2", + "rc-menu": "^9.6.4", + "rc-util": "^5.24.4", + "react": "^17.0.0", + "react-dev-inspector": "^1.8.1", + "react-dom": "^17.0.0", + "react-helmet-async": "^1.3.0" + }, + "devDependencies": { + "@ant-design/pro-cli": "^2.1.0", + "@playwright/test": "^1.26.1", + "@types/classnames": "^2.3.1", + "@types/express": "^4.17.14", + "@types/history": "^4.7.11", + "@types/lodash": "^4.14.186", + "@types/react": "^17.0.0", + "@types/react-dom": "^17.0.0", + "@types/react-helmet": "^6.1.5", + "@umijs/fabric": "^2.11.1", + "@umijs/max": "^4.0.24", + "@umijs/openapi": "^1.7.0", + "cross-env": "^7.0.3", + "cross-port-killer": "^1.4.0", + "detect-installer": "^1.0.2", + "eslint": "^7.32.0", + "gh-pages": "^3.2.0", + "husky": "^7.0.4", + "lint-staged": "^10.0.0", + "mockjs": "^1.1.0", + "prettier": "^2.7.1", + "swagger-ui-dist": "^4.14.2", + "typescript": "^4.8.4", + "umi-presets-pro": "^1.0.5", + "umi-serve": "^1.9.11" + }, + "engines": { "node": ">=12.0.0" } +} 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 zcmV-11C0002qP)t-s01zMl z|Nj6D8~_Fu01Fxg6ePmN$p8o!Wo~x>2^n*Hg!uURG(b#MUupdO{0J2!4IC;FATIs> z{@dN(0umtr1r+b_@!;a*CNn?_87S=S?pR`MN>yMYFg?=M*UQe)wYk2qw7Hs~re<$= zUubdX=<2(_!=9w6l9``_ijrw^dn`Fc6(TV7_4V=c^R2SBrmV4+ouiMHoQsl~euj;D zf{I&ZZXPQ+87DQ^+ugv!$3;(CLQPmWL{HY)+rGlaqNuHRe}`^&fKXdyK1x+FK1#;P z&4`be_4fDTmuWjt2mKnbI+d3dfa z_PK3Vs;zn;Ot~u#==o*X;a^nmOa{(_o(tmupuw*`!> z#lUp|BbBD0;J$!S0To9DO4Z9XkCjq#?=s{WNE{XLCFmLibLLfxLm3nt7Wj-&k{L;X zo_Y$L6(Ax^@lhJo2uNHK09we3_>jt`DV`HX8L^}WDJO|ULbS4T1b_}iycq`oGqNH4 zQJi@^@H`%ly#;k8%FIH(J#9^VH>(ITJX3)9M7)?mVD;pNM){2bV!rABC{yFeZXJ$q ztx`Yw*~Rj#FGR_Att-g zD?cOlXM^rD8S|s+a(LB7MePP8cKc zywqQW@o0gm8Qu`-)UW-9#K*uQ5WQT5&CGnc%phPyF4p1{wj=@#5xikX3uI#(QrZ2A z66>Nxz+a;r*Km#iEGXxR4BFJu%9#^UG{?XqFlA~7sH!J&2X_C6*P7#50#^tYfm`)p zW2Pupno-5;QoKQ)pCj;SS_MKc4Ai#W7h-YyLi$8b7l@8b3KjwX8wCk}6<4mh7hj6_ z@bnBUa)|InAVBSV)+C{3Cf$?e`b2@nQysp0$(swOoN^Eg(SO9p-Q1JJAAwzQCMaKm&bGV`U^9&pp0Gf^+nkRf5 zTo3@>xi{%p{gIpTF5iJBic@aIws^IC=-v{M~W@Y%aj8G0o!r2b|HdROb8D$LP+n;FWCW`~3a=G(k;6PFes57kh(>00a~S6Cwx} zCE()Z^7Hf|E zp{K0H$jr97ziM-QXK;BCA1!ozgl>0$_4f8XNmSCn^FkCmKwfQVshbU{p46e2L| z?Ct65?BnI;*4f+4&(glZ#fXoVgo~3^UusNOVjC$ov9`LWtg%K>TsK2c$;{8e#mKwA z!>qBksjjnFVr-b6q?4MUQCw!>t&JT300tCEL_t(|+U?tESK2@nfZ>~&tE5s!683!u zK~Xjr+;FLD`~QDWsaSMof{9Cdrsv$}Q$GacB{NB8tUw4Mgb+dqA%qY@2qA(L2bqdsgXsNTkuvsd$l2J=n5?~ND zV51VpX8=z3{QjGMB}&;~4lO{w(%Zazp$0@daWIEhfP~dcYQd#z&@Au`02`W0K*FSZ zfqww-)R9s!Ht2yOKmf3z60nW%2IP#}oH2Xl0XAm~+{S+rP&t$X9^nbbm-x&1;@!yj zY%9B7sWA>ZWO1jKtCah(e))TD=Eenv9so{D4rn_thqYd2VK|ELzaOufx!t$^NZw>p z+Iyj>U-gQu^kN;GPyD#d{Hh5IJ3uJYc+Z77Ufo$Ggo+WA3Y*@1Ky13)LZP31QSeOBPQ+XWH1FRE< znBBKzO$T4zWLDa@R>>EuJj^qYQEmht0qwfj(!SqQ@iKSEQ|m2>2OGl+_^GUFJOcJ= zXWHkFZpC2%v9sp4{dDSJXp{It8QP8>kQnV+s>T(A1*ey{eEoDWoB-?=l!qbE1M&?^ zlg>TQVZNQW0vG^>5+Gs?WoT;w-lorADbq%mw`k7}=L`%i*#i$2a3L12&mCI9?2mZ} z0W6}7?qr1p3pjZ?4xKwIs2_Fw@&LQuwzS(*uz)OwIWgMMHdb!T{tPE4SL`S{H8xBn!v4)5iQu&kynG zu&g>v%A749U2|<^9&fVG1>_^~CFZ7LP5t_J?7+%^=uNif%+;?)oeFwB$`&F5UWnO^ z@}M}gKew%NvbE6ZWlDEPmWMOGGN93O87@!W;veOx{0)tnve;1G63u~Z#6xV7>iY035`Xj%Y> z^Q5IJ+!Y6p_dlYRYQ(^}0Dwm;s0$Yi48Tdj3g$4vO$-3uKeU2KKGd0O0O0VXbm7s4;+l9&n%p_`cvI z?LB&meH%ahXIS9}0Jn2n$9YS(u{W#$gO9RPz0KE#GlYO}vMs$dg#n^%CpUU*6%xDq zjs0(lqaT-@s*4=qi*zHU+H4m-VgiXywKHFAtzxV0RWJzsovKI0FJxD0TBWR9R(cUae(Kj zsHF%%MItH28Xp(4*{bSm0T9Fm0Q3xia~uV&0pKeFz=jn7(rEx(_sVL}lf@aG=xQ21 z0F-Q4EcX2Td}n9x&+_W8>Dj@bW1Zc-_04UCC6!U}srDWLO4_FE!gBaD92g8{VR7ke zeM?AW92o=O(D2yXclnR)y=d4(E-o&bT05P*g06FjUtV6efBWI#2OS@uWaJd^OFd|4 zX;(M2f>E+f{F>2!?AX@%T}(mq>gp=@<7Y-5siUJ~-_R&H)yX)dzBXt+ zw3(irC-{#uAUx*V_rCAF0|y6(^qk`23R+NfVtI9aeo@&CE(t4VpY6XpJmUB8uHDiy zwv9=Ab9i`ka&k&{ODsM)GxBB9kG{dJ?Y|`SJP2xb|L_=>Cn3tZ=GbsG#NjrD{p8lZ+I*;#ZnN-&`)3V#iUD zMPI6J3dwTaR#efm@D7ZSx~Kc$)0fQLLZ9GgMwTwT5=zbJZ|@6AUcO3G4f|Pz+YOkF zhRS^y7A`o43kXqaS`P?+li?F^ab$DRnFBz%pmtx;FkotX)-fdI0SkIBo$3`kjGW}D zZoDeqLtS3Q2M@@N^6~Cb6R{D+s3aFqxA6=o*eHod92P`F`24wEin51>RC@xG+Cw7; zO)`T9mP{{KyOZ{F0<#_c5Bhd4GTu4*uXH<{{3_)6x4r)lr){`JohSKG%?$A}zhMc= zW|@O)q8WsJQ6EdzmKROG;U@_#(HhDuk8bo;Ho`*6l-2u9HLE*dOv$E=6gvnZLNCqP zprds6w}?9hZO+b`b-ggB&>$s;nRQSt|AE@)FA7Ls_I33&;P}~m=VyWbcR}f(OIN21 zGvpB_DYkJ<3MQsNhOEYodj;5({(pE0$4P!F$)xqVIwo83~69g=48RK+49na7Q(C zC7>Jw*DbzEje?QUe|LGt3nX|7Uz)%oP2zR&b4Op@R)(SMB<|5c3@$a63w&>G*}*

*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..f989ec3 --- /dev/null +++ b/src/app.tsx @@ -0,0 +1,129 @@ +import Footer from '@/components/Footer'; +import RightContent from '@/components/RightContent'; +import { LinkOutlined } from '@ant-design/icons'; +import type { Settings as LayoutSettings } from '@ant-design/pro-components'; +import { SettingDrawer } from '@ant-design/pro-components'; +import type { RunTimeLayoutConfig } from '@umijs/max'; +import { history, Link } from '@umijs/max'; +import defaultSettings from '../config/defaultSettings'; +import { errorConfig } from './requestErrorConfig'; +import { currentUser as queryCurrentUser } from './services/ant-design-pro/api'; + +const isDev = process.env.NODE_ENV === 'development'; +const loginPath = '/user/login'; + +/** + * @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({ + skipErrorHandler: true, + }); + return msg.data; + } catch (error) { + history.push(loginPath); + } + return undefined; + }; + // 如果不是登录页面,执行 + if (window.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: () => , + waterMarkProps: { + content: initialState?.currentUser?.name, + }, + footerRender: () =>