diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 5ab468cb..311618b5 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -7,7 +7,7 @@ jobs: runs-on: ${{ matrix.os }} strategy: matrix: - node_version: [10.x, 12.x] + node_version: [12.x, 14.x] os: [ubuntu-latest, windows-latest, macOS-latest] steps: - uses: actions/checkout@v1 @@ -16,7 +16,7 @@ jobs: with: node-version: ${{ matrix.node_version }} - run: echo ${{github.ref}} - - run: npm install + - run: yarn - run: yarn run lint - run: yarn run tsc - run: yarn run build diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index adfd5300..d6ee7c51 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -12,10 +12,13 @@ jobs: uses: actions/checkout@master - name: install - run: npm install + run: yarn - name: plugins - run: yarn add umi-plugin-antd-theme umi-plugin-pro + run: yarn add umi-plugin-antd-theme umi-plugin-pro umi-plugin-setting-drawer + + - name: fetch-blocks + run: yarn run pro fetch-blocks --branch=v5 - name: site run: npm run site diff --git a/.github/workflows/preview-build.yml b/.github/workflows/preview-build.yml index 730f841b..e90dae65 100644 --- a/.github/workflows/preview-build.yml +++ b/.github/workflows/preview-build.yml @@ -15,9 +15,9 @@ jobs: - name: build run: | - npm install - npm install umi-plugin-pro --save - npm run build + yarn + yarn add umi-plugin-pro --save + yarn build - name: upload dist artifact uses: actions/upload-artifact@v2 diff --git a/.github/workflows/preview-deploy.yml b/.github/workflows/preview-deploy.yml index 84757119..11c9ff54 100644 --- a/.github/workflows/preview-deploy.yml +++ b/.github/workflows/preview-deploy.yml @@ -2,7 +2,7 @@ name: Preview Deploy on: workflow_run: - workflows: ["Preview Build"] + workflows: ['Preview Build'] types: - completed diff --git a/.github/workflows/rebase.yml b/.github/workflows/rebase.yml index a94c6437..f850ccc8 100644 --- a/.github/workflows/rebase.yml +++ b/.github/workflows/rebase.yml @@ -8,10 +8,10 @@ jobs: if: github.event.issue.pull_request != '' && contains(github.event.comment.body, '/rebase') runs-on: ubuntu-latest steps: - - uses: actions/checkout@master - with: - fetch-depth: 0 - - name: Automatic Rebase - uses: cirrus-actions/rebase@1.3 - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + - uses: actions/checkout@master + with: + fetch-depth: 0 + - name: Automatic Rebase + uses: cirrus-actions/rebase@1.3 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/README.md b/README.md index 47e5a1ef..16d38a22 100644 --- a/README.md +++ b/README.md @@ -21,7 +21,7 @@ An out-of-box UI solution for enterprise applications as a React boilerplate. - FAQ: http://pro.ant.design/docs/faq - Mirror Site in China: http://ant-design-pro.gitee.io -## 5.0 is ready for trial! 🎉🎉🎉 +## 5.0 is ready for preview! 🎉🎉🎉 [Try Ant Design Pro 5.0.0](https://beta-pro.ant.design/docs/upgrade-v5-cn) diff --git a/config/config.dev.ts b/config/config.dev.ts index ab0e590f..65de6750 100644 --- a/config/config.dev.ts +++ b/config/config.dev.ts @@ -12,4 +12,8 @@ export default defineConfig({ babelPlugins: [], babelOptions: {}, }, + // mfsu: {}, + // webpack5: { + // lazyCompilation: {}, + // }, }); diff --git a/config/config.ts b/config/config.ts index 698eaa6d..fc977cb8 100644 --- a/config/config.ts +++ b/config/config.ts @@ -1,5 +1,7 @@ // https://umijs.org/config/ import { defineConfig } from 'umi'; +import { join } from 'path'; + import defaultSettings from './defaultSettings'; import proxy from './proxy'; import routes from './routes'; @@ -12,9 +14,13 @@ export default defineConfig({ dva: { hmr: true, }, - history: { - type: 'browser', + 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', @@ -23,7 +29,7 @@ export default defineConfig({ baseNavigator: true, }, dynamicImport: { - loading: '@/components/PageLoading/index', + loading: '@ant-design/pro-layout/es/PageLoading', }, targets: { ie: 11, @@ -34,14 +40,33 @@ export default defineConfig({ theme: { 'primary-color': defaultSettings.primaryColor, }, + // esbuild is father build tools + // https://umijs.org/plugins/plugin-esbuild + esbuild: {}, title: false, ignoreMomentLocale: true, proxy: proxy[REACT_APP_ENV || 'dev'], manifest: { basePath: '/', }, - // 快速刷新功能 https://umijs.org/config#fastrefresh + // Fast Refresh 热更新 fastRefresh: {}, - esbuild: {}, + 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 index dd2015b2..15397c6c 100644 --- a/config/defaultSettings.ts +++ b/config/defaultSettings.ts @@ -1,23 +1,21 @@ -import { Settings as ProSettings } from '@ant-design/pro-layout'; +import { Settings as LayoutSettings } from '@ant-design/pro-layout'; -type DefaultSettings = Partial & { - pwa: boolean; -}; - -const proSettings: DefaultSettings = { - navTheme: 'dark', +const Settings: LayoutSettings & { + pwa?: boolean; + logo?: string; +} = { + navTheme: 'light', // 拂晓蓝 primaryColor: '#1890ff', - layout: 'side', + 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 type { DefaultSettings }; - -export default proSettings; +export default Settings; diff --git a/config/oneapi.json b/config/oneapi.json new file mode 100644 index 00000000..c77d988b --- /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/routes.ts b/config/routes.ts index 545d4053..a269819e 100644 --- a/config/routes.ts +++ b/config/routes.ts @@ -1,72 +1,51 @@ export default [ { - path: '/', - component: '../layouts/BlankLayout', + path: '/user', + layout: false, routes: [ { path: '/user', - component: '../layouts/UserLayout', routes: [ { name: 'login', path: '/user/login', - component: './User/login', + component: './user/Login', }, ], }, + ], + }, + { + path: '/welcome', + name: 'welcome', + icon: 'smile', + component: './Welcome', + }, + { + path: '/admin', + name: 'admin', + icon: 'crown', + access: 'canAdmin', + component: './Admin', + routes: [ { - path: '/', - component: '../layouts/SecurityLayout', - routes: [ - { - path: '/', - component: '../layouts/BasicLayout', - authority: ['admin', 'user'], - routes: [ - { - path: '/', - redirect: '/welcome', - }, - { - path: '/welcome', - name: 'welcome', - icon: 'smile', - component: './Welcome', - }, - { - path: '/admin', - name: 'admin', - icon: 'crown', - component: './Admin', - authority: ['admin'], - routes: [ - { - path: '/admin/sub-page', - name: 'sub-page', - icon: 'smile', - component: './Welcome', - authority: ['admin'], - }, - ], - }, - { - name: 'list.table-list', - icon: 'table', - path: '/list', - component: './TableList', - }, - { - component: './404', - }, - ], - }, - { - component: './404', - }, - ], + path: '/admin/sub-page', + name: 'sub-page', + icon: 'smile', + component: './Welcome', }, ], }, + { + 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 index 4c4eeaf7..09a2211b 100644 --- a/jest.config.js +++ b/jest.config.js @@ -2,6 +2,7 @@ module.exports = { testURL: 'http://localhost:8000', testEnvironment: './tests/PuppeteerEnvironment', verbose: false, + extraSetupFiles: ['./tests/setupTests.js'], globals: { ANT_DESIGN_PRO_ONLY_DO_NOT_USE_IN_YOUR_PRODUCTION: false, localStorage: null, diff --git a/mock/listTableList.ts b/mock/listTableList.ts index 9003724f..154b5891 100644 --- a/mock/listTableList.ts +++ b/mock/listTableList.ts @@ -1,10 +1,11 @@ // eslint-disable-next-line import/no-extraneous-dependencies import { Request, Response } from 'express'; -import { TableListItem, TableListParams } from '@/pages/TableList/data'; +import moment from 'moment'; +import { parse } from 'url'; // mock tableListDataSource const genList = (current: number, pageSize: number) => { - const tableListDataSource: TableListItem[] = []; + const tableListDataSource: API.RuleListItem[] = []; for (let i = 0; i < pageSize; i += 1) { const index = (current - 1) * 10 + i; @@ -21,8 +22,8 @@ const genList = (current: number, pageSize: number) => { desc: '这是一段描述', callNo: Math.floor(Math.random() * 1000), status: Math.floor(Math.random() * 10) % 4, - updatedAt: new Date(), - createdAt: new Date(), + updatedAt: moment().format('YYYY-MM-DD'), + createdAt: moment().format('YYYY-MM-DD'), progress: Math.ceil(Math.random() * 100), }); } @@ -38,13 +39,17 @@ function getRule(req: Request, res: Response, u: string) { realUrl = req.url; } const { current = 1, pageSize = 10 } = req.query; - const params = (new URLSearchParams(realUrl.split('?')[1]) as unknown) as TableListParams; + 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), ); - const sorter = JSON.parse(params.sorter as any); + const sorter = JSON.parse(params.sorter || ('{}' as any)); if (sorter) { dataSource = dataSource.sort((prev, next) => { let sortNumber = 0; @@ -86,14 +91,14 @@ function getRule(req: Request, res: Response, u: string) { } if (params.name) { - dataSource = dataSource.filter((data) => data.name.includes(params.name || '')); + dataSource = dataSource.filter((data) => data?.name?.includes(params.name || '')); } const result = { data: dataSource, total: tableListDataSource.length, success: true, pageSize, - current: parseInt(`${params.currentPage}`, 10) || 1, + current: parseInt(`${params.current}`, 10) || 1, }; return res.json(result); @@ -116,7 +121,7 @@ function postRule(req: Request, res: Response, u: string, b: Request) { case 'post': (() => { const i = Math.ceil(Math.random() * 10000); - const newRule = { + const newRule: API.RuleListItem = { key: tableListDataSource.length, href: 'https://ant.design', avatar: [ @@ -128,8 +133,8 @@ function postRule(req: Request, res: Response, u: string, b: Request) { desc, callNo: Math.floor(Math.random() * 1000), status: Math.floor(Math.random() * 10) % 2, - updatedAt: new Date(), - createdAt: new Date(), + updatedAt: moment().format('YYYY-MM-DD'), + createdAt: moment().format('YYYY-MM-DD'), progress: Math.ceil(Math.random() * 100), }; tableListDataSource.unshift(newRule); diff --git a/mock/notices.ts b/mock/notices.ts index b9e3bf29..732dd584 100644 --- a/mock/notices.ts +++ b/mock/notices.ts @@ -1,103 +1,105 @@ import { Request, Response } from 'express'; const getNotices = (req: Request, res: Response) => { - res.json([ - { - 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', - }, - ]); + 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 { diff --git a/mock/user.ts b/mock/user.ts index a722c228..49542887 100644 --- a/mock/user.ts +++ b/mock/user.ts @@ -13,58 +13,85 @@ async function getFakeCaptcha(req: Request, res: Response) { 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': { - 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', - geographic: { - province: { - label: '浙江省', - key: '330000', - }, - city: { - label: '杭州市', - key: '330100', + 'GET /api/currentUser': (req: Request, res: Response) => { + if (!getAccess()) { + res.status(401).send({ + data: { + isLogin: false, + }, + errorCode: '401', + errorMessage: '请先登录!', + success: true, + }); + return; + } + res.send({ + 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', + address: '西湖区工专路 77 号', + phone: '0752-268888888', + }); }, // GET POST 可省略 'GET /api/users': [ @@ -88,22 +115,24 @@ export default { }, ], 'POST /api/login/account': async (req: Request, res: Response) => { - const { password, userName, type } = req.body; + const { password, username, type } = req.body; await waitTime(2000); - if (password === 'ant.design' && userName === 'admin') { + if (password === 'ant.design' && username === 'admin') { res.send({ status: 'ok', type, currentAuthority: 'admin', }); + access = 'admin'; return; } - if (password === 'ant.design' && userName === 'user') { + if (password === 'ant.design' && username === 'user') { res.send({ status: 'ok', type, currentAuthority: 'user', }); + access = 'user'; return; } if (type === 'mobile') { @@ -112,6 +141,7 @@ export default { type, currentAuthority: 'admin', }); + access = 'admin'; return; } @@ -120,9 +150,14 @@ export default { 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' }); + res.send({ status: 'ok', currentAuthority: 'user', success: true }); }, 'GET /api/500': (req: Request, res: Response) => { res.status(500).send({ diff --git a/package.json b/package.json index 3268f13a..9055270a 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "ant-design-pro", - "version": "4.5.0", + "version": "5.0.0-beta.3", "private": true, "description": "An out-of-box UI solution for enterprise applications", "scripts": { @@ -15,16 +15,17 @@ "docker:dev": "docker-compose -f ./docker/docker-compose.dev.yml up", "docker:push": "npm run docker-hub:build && npm run docker:tag && docker push antdesign/ant-design-pro", "docker:tag": "docker tag ant-design-pro antdesign/ant-design-pro", - "fetch:blocks": "pro fetch-blocks && npm run prettier", "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", + "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 --check \"src/**/*\" --end-of-line auto", + "lint:prettier": "prettier -c --write \"src/**/*\" --end-of-line auto", "lint:style": "stylelint --fix \"src/**/*.less\" --syntax less", + "openapi": "umi openapi", "precommit": "lint-staged", "prettier": "prettier -c --write \"src/**/*\"", "site": "npm run fetch:blocks && npm run build", @@ -38,6 +39,7 @@ "test": "umi test", "test:all": "node ./tests/run-tests.js", "test:component": "umi test ./src/components", + "serve": "umi-serve", "tsc": "tsc --noEmit" }, "lint-staged": { @@ -53,26 +55,26 @@ "not ie <= 10" ], "dependencies": { - "@ant-design/icons": "^4.0.0", - "@ant-design/pro-descriptions": "^1.2.0", - "@ant-design/pro-form": "^1.3.0", - "@ant-design/pro-layout": "^6.9.0", - "@ant-design/pro-table": "^2.17.0", - "@umijs/route-utils": "^1.0.33", - "antd": "^4.15.0", + "@ant-design/icons": "^4.5.0", + "@ant-design/pro-descriptions": "^1.6.8", + "@ant-design/pro-form": "^1.18.3", + "@ant-design/pro-layout": "^6.15.3", + "@ant-design/pro-table": "^2.30.8", + "@umijs/route-utils": "^1.0.36", + "antd": "^4.14.0", "classnames": "^2.2.6", "lodash": "^4.17.11", "moment": "^2.25.3", "omit.js": "^2.0.2", - "react": "^16.14.0", + "react": "^17.0.0", "react-dev-inspector": "^1.1.1", "react-dom": "^17.0.0", "react-helmet-async": "^1.0.4", - "umi": "^3.4.1", - "umi-request": "^1.0.8" + "umi": "^3.5.0", + "umi-serve": "^1.9.10" }, "devDependencies": { - "@ant-design/pro-cli": "^1.0.28", + "@ant-design/pro-cli": "^2.0.2", "@types/classnames": "^2.2.7", "@types/express": "^4.17.0", "@types/history": "^4.7.2", @@ -81,14 +83,16 @@ "@types/react": "^17.0.0", "@types/react-dom": "^17.0.0", "@types/react-helmet": "^6.1.0", - "@umijs/fabric": "^2.5.1", + "@umijs/fabric": "^2.6.2", + "@umijs/openapi": "^1.1.14", "@umijs/plugin-blocks": "^2.0.5", "@umijs/plugin-esbuild": "^1.0.1", + "@umijs/plugin-openapi": "^1.2.0", "@umijs/preset-ant-design-pro": "^1.2.0", - "@umijs/preset-react": "^1.4.8", + "@umijs/preset-dumi": "^1.1.7", + "@umijs/preset-react": "^1.7.4", "@umijs/yorkie": "^2.0.3", "carlo": "^0.9.46", - "chalk": "^4.0.0", "cross-env": "^7.0.0", "cross-port-killer": "^1.1.1", "detect-installer": "^1.0.1", @@ -99,21 +103,14 @@ "jsdom-global": "^3.0.2", "lint-staged": "^10.0.0", "mockjs": "^1.0.1-beta3", - "prettier": "^2.0.1", + "prettier": "^2.3.2", "puppeteer-core": "^8.0.0", "stylelint": "^13.0.0", - "typescript": "^4.0.3" + "typescript": "^4.2.2" }, "engines": { "node": ">=10.0.0" }, - "checkFiles": [ - "src/**/*.js*", - "src/**/*.ts*", - "src/**/*.less", - "config/**/*.js*", - "scripts/**/*.js" - ], "create-umi": { "ignoreScript": [ "docker*", @@ -142,5 +139,8 @@ "CNAME", "create-umi" ] + }, + "gitHooks": { + "commit-msg": "fabric verify-commit" } } diff --git a/public/home_bg.png b/public/home_bg.png deleted file mode 100644 index 7c92a4be..00000000 Binary files a/public/home_bg.png and /dev/null differ diff --git a/src/assets/logo.svg b/public/logo.svg similarity index 100% rename from src/assets/logo.svg rename to public/logo.svg diff --git a/src/access.ts b/src/access.ts new file mode 100644 index 00000000..2ec89bdd --- /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 00000000..246cda9d --- /dev/null +++ b/src/app.tsx @@ -0,0 +1,136 @@ +import type { Settings as LayoutSettings } from '@ant-design/pro-layout'; +import { PageLoading } from '@ant-design/pro-layout'; +import { notification } from 'antd'; +import type { RequestConfig, 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'; + +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; + fetchUserInfo?: () => Promise; +}> { + const fetchUserInfo = async () => { + try { + const currentUser = await queryCurrentUser(); + return currentUser; + } catch (error) { + history.push(loginPath); + } + return undefined; + }; + // 如果是登录页面,不执行 + if (history.location.pathname !== loginPath) { + const currentUser = await fetchUserInfo(); + return { + fetchUserInfo, + currentUser, + settings: {}, + }; + } + return { + fetchUserInfo, + settings: {}, + }; +} + +/** + * 异常处理程序 + 200: '服务器成功返回请求的数据。', + 201: '新建或修改数据成功。', + 202: '一个请求已经进入后台排队(异步任务)。', + 204: '删除数据成功。', + 400: '发出的请求有错误,服务器没有进行新建或修改数据的操作。', + 401: '用户没有权限(令牌、用户名、密码错误)。', + 403: '用户得到授权,但是访问是被禁止的。', + 404: '发出的请求针对的是不存在的记录,服务器没有进行操作。', + 405: '请求方法不被允许。', + 406: '请求的格式不可得。', + 410: '请求的资源被永久删除,且不会再得到的。', + 422: '当创建一个对象时,发生一个验证错误。', + 500: '服务器发生错误,请检查服务器。', + 502: '网关错误。', + 503: '服务不可用,服务器暂时过载或维护。', + 504: '网关超时。', + //-----English + 200: The server successfully returned the requested data. ', + 201: New or modified data is successful. ', + 202: A request has entered the background queue (asynchronous task). ', + 204: Data deleted successfully. ', + 400: 'There was an error in the request sent, and the server did not create or modify data. ', + 401: The user does not have permission (token, username, password error). ', + 403: The user is authorized, but access is forbidden. ', + 404: The request sent was for a record that did not exist. ', + 405: The request method is not allowed. ', + 406: The requested format is not available. ', + 410': + 'The requested resource is permanently deleted and will no longer be available. ', + 422: When creating an object, a validation error occurred. ', + 500: An error occurred on the server, please check the server. ', + 502: Gateway error. ', + 503: The service is unavailable. ', + 504: The gateway timed out. ', + * @see https://beta-pro.ant.design/docs/request-cn + */ +export const request: RequestConfig = { + errorHandler: (error: any) => { + const { response } = error; + + if (!response) { + notification.error({ + description: '您的网络发生异常,无法连接服务器', + message: '网络异常', + }); + } + throw error; + }, +}; + +// ProLayout 支持的api https://procomponents.ant.design/components/layout +export const layout: RunTimeLayoutConfig = ({ initialState }) => { + return { + rightContentRender: () => , + disableContentMargin: false, + waterMarkProps: { + content: initialState?.currentUser?.name, + }, + footerRender: () =>