diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index eb6bf71e..101ea9ea 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -7,7 +7,7 @@ jobs: runs-on: ${{ matrix.os }} strategy: matrix: - node_version: [12.x, 14.x] + node_version: [16.x, 14.x] os: [ubuntu-latest, windows-latest, macOS-latest] steps: - uses: actions/checkout@v1 @@ -20,7 +20,7 @@ jobs: - run: yarn run lint - run: yarn run tsc - run: yarn run build - - run: yarn run test:all + - run: yarn run test:e2e env: CI: true PROGRESS: none diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index 12397635..bcaa437f 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -15,7 +15,7 @@ jobs: run: yarn - name: plugins - run: yarn add umi-plugin-antd-theme umi-plugin-pro umi-plugin-setting-drawer + run: yarn add umi-plugin-pro - name: fetch-blocks run: yarn run pro fetch-blocks @@ -26,6 +26,6 @@ jobs: - name: Deploy uses: peaceiris/actions-gh-pages@v3 with: - deploy_key: ${{ secrets.ACTIONS_DEPLOY_KEY }} + github_token: ${{ secrets.GITHUB_TOKEN }} publish_dir: ./dist force_orphan: true diff --git a/.gitignore b/.gitignore index 7fd9f581..21ab9fb7 100644 --- a/.gitignore +++ b/.gitignore @@ -8,7 +8,6 @@ _roadhog-api-doc # production /dist -/.vscode # misc .DS_Store @@ -19,8 +18,9 @@ yarn-error.log .idea yarn.lock package-lock.json +pnpm-lock.yaml *bak -.vscode + # visual studio code .history diff --git a/.husky/.gitignore b/.husky/.gitignore new file mode 100644 index 00000000..31354ec1 --- /dev/null +++ b/.husky/.gitignore @@ -0,0 +1 @@ +_ diff --git a/.husky/commit-msg b/.husky/commit-msg new file mode 100755 index 00000000..d50cdcf9 --- /dev/null +++ b/.husky/commit-msg @@ -0,0 +1,7 @@ +#!/bin/sh +. "$(dirname "$0")/_/husky.sh" + +# Export Git hook params +export GIT_PARAMS=$* + +npx --no-install fabric verify-commit diff --git a/.husky/pre-commit b/.husky/pre-commit new file mode 100755 index 00000000..d37daa07 --- /dev/null +++ b/.husky/pre-commit @@ -0,0 +1,4 @@ +#!/bin/sh +. "$(dirname "$0")/_/husky.sh" + +npx --no-install lint-staged diff --git a/.stylelintrc.js b/.stylelintrc.js index c2030787..a1184de4 100644 --- a/.stylelintrc.js +++ b/.stylelintrc.js @@ -1,5 +1,3 @@ -const fabric = require('@umijs/fabric'); - module.exports = { - ...fabric.stylelint, + extends: [require.resolve('@umijs/fabric/dist/stylelint')], }; diff --git a/.vscode/extensions.json b/.vscode/extensions.json new file mode 100644 index 00000000..33f300da --- /dev/null +++ b/.vscode/extensions.json @@ -0,0 +1,8 @@ +{ + "recommendations": [ + "esbenp.prettier-vscode", + "dbaeumer.vscode-eslint", + "stylelint.vscode-stylelint", + "wangzy.sneak-mark" + ] +} diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 00000000..a5d9d035 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,5 @@ +{ + "editor.formatOnSave": true, + "prettier.requireConfig": true, + "editor.defaultFormatter": "esbenp.prettier-vscode" +} diff --git a/Dockerfile b/Dockerfile index 35380f48..f828e839 100644 --- a/Dockerfile +++ b/Dockerfile @@ -7,7 +7,7 @@ RUN yarn COPY ./ ./ -RUN npm run test:all +RUN npm run test:e2e RUN npm run fetch:blocks diff --git a/Dockerfile.dev b/Dockerfile.dev index b2c3d258..18446181 100644 --- a/Dockerfile.dev +++ b/Dockerfile.dev @@ -3,7 +3,7 @@ FROM node:latest WORKDIR /usr/src/app/ COPY package.json ./ -RUN npm install --silent --no-cache --registry=https://registry.npm.taobao.org +RUN npm install --silent --no-cache --registry=https://registry.npmmirror.com COPY ./ ./ diff --git a/Dockerfile.hub b/Dockerfile.hub index 58bbcbc7..94534d3e 100644 --- a/Dockerfile.hub +++ b/Dockerfile.hub @@ -7,7 +7,7 @@ RUN yarn COPY ./ ./ -RUN npm run test:all +RUN npm run test:e2e RUN npm run fetch:blocks diff --git a/README.md b/README.md index 71f517a0..16b06c2f 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@ Language : 🇺🇸 | [🇨🇳](./README.zh-CN.md) | [🇷🇺](./README.ru-RU. An out-of-box UI solution for enterprise applications as a React boilerplate. -[![Build Status](https://dev.azure.com/ant-design/ant-design-pro/_apis/build/status/ant-design.ant-design-pro?branchName=master)](https://dev.azure.com/ant-design/ant-design-pro/_build/latest?definitionId=1?branchName=master) ![Github Action](https://github.com/ant-design/ant-design-pro/workflows/Node%20CI/badge.svg) ![Deploy](https://github.com/ant-design/ant-design-pro/workflows/Deploy%20CI/badge.svg) [![Dependencies](https://img.shields.io/david/ant-design/ant-design-pro.svg)](https://david-dm.org/ant-design/ant-design-pro) [![DevDependencies](https://img.shields.io/david/dev/ant-design/ant-design-pro.svg)](https://david-dm.org/ant-design/ant-design-pro?type=dev) +[![Build Status](https://dev.azure.com/ant-design/ant-design-pro/_apis/build/status/ant-design.ant-design-pro?branchName=master)](https://dev.azure.com/ant-design/ant-design-pro/_build/latest?definitionId=1?branchName=master) ![Github Action](https://github.com/ant-design/ant-design-pro/workflows/Node%20CI/badge.svg) ![Deploy](https://github.com/ant-design/ant-design-pro/workflows/Deploy%20CI/badge.svg) [![Gitter](https://img.shields.io/gitter/room/ant-design/pro-english.svg?style=flat-square&logoWidth=20&logo=data%3Aimage%2Fsvg%2Bxml%3Bbase64%2CPD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz4NCjxzdmcgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayIgd2lkdGg9IjEyMzUiIGhlaWdodD0iNjUwIiB2aWV3Qm94PSIwIDAgNzQxMCAzOTAwIj4NCjxyZWN0IHdpZHRoPSI3NDEwIiBoZWlnaHQ9IjM5MDAiIGZpbGw9IiNiMjIyMzQiLz4NCjxwYXRoIGQ9Ik0wLDQ1MEg3NDEwbTAsNjAwSDBtMCw2MDBINzQxMG0wLDYwMEgwbTAsNjAwSDc0MTBtMCw2MDBIMCIgc3Ryb2tlPSIjZmZmIiBzdHJva2Utd2lkdGg9IjMwMCIvPg0KPHJlY3Qgd2lkdGg9IjI5NjQiIGhlaWdodD0iMjEwMCIgZmlsbD0iIzNjM2I2ZSIvPg0KPGcgZmlsbD0iI2ZmZiI%2BDQo8ZyBpZD0iczE4Ij4NCjxnIGlkPSJzOSI%2BDQo8ZyBpZD0iczUiPg0KPGcgaWQ9InM0Ij4NCjxwYXRoIGlkPSJzIiBkPSJNMjQ3LDkwIDMxNy41MzQyMzAsMzA3LjA4MjAzOSAxMzIuODczMjE4LDE3Mi45MTc5NjFIMzYxLjEyNjc4MkwxNzYuNDY1NzcwLDMwNy4wODIwMzl6Ii8%2BDQo8dXNlIHhsaW5rOmhyZWY9IiNzIiB5PSI0MjAiLz4NCjx1c2UgeGxpbms6aHJlZj0iI3MiIHk9Ijg0MCIvPg0KPHVzZSB4bGluazpocmVmPSIjcyIgeT0iMTI2MCIvPg0KPC9nPg0KPHVzZSB4bGluazpocmVmPSIjcyIgeT0iMTY4MCIvPg0KPC9nPg0KPHVzZSB4bGluazpocmVmPSIjczQiIHg9IjI0NyIgeT0iMjEwIi8%2BDQo8L2c%2BDQo8dXNlIHhsaW5rOmhyZWY9IiNzOSIgeD0iNDk0Ii8%2BDQo8L2c%2BDQo8dXNlIHhsaW5rOmhyZWY9IiNzMTgiIHg9Ijk4OCIvPg0KPHVzZSB4bGluazpocmVmPSIjczkiIHg9IjE5NzYiLz4NCjx1c2UgeGxpbms6aHJlZj0iI3M1IiB4PSIyNDcwIi8%2BDQo8L2c%2BDQo8L3N2Zz4%3D)](https://gitter.im/ant-design/pro-english?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge) [![Join the chat at https://gitter.im/ant-design/ant-design-pro](https://img.shields.io/gitter/room/ant-design/ant-design-pro.svg?style=flat-square&logoWidth=20&logo=data%3Aimage%2Fsvg%2Bxml%3Bbase64%2CPD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz4NCjxzdmcgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayIgd2lkdGg9IjkwMCIgaGVpZ2h0PSI2MDAiIHZpZXdCb3g9IjAgMCAzMCAyMCI%2BDQo8ZGVmcz4NCjxwYXRoIGlkPSJzIiBkPSJNMCwtMSAwLjU4Nzc4NSwwLjgwOTAxNyAtMC45NTEwNTcsLTAuMzA5MDE3SDAuOTUxMDU3TC0wLjU4Nzc4NSwwLjgwOTAxN3oiIGZpbGw9IiNmZmRlMDAiLz4NCjwvZGVmcz4NCjxyZWN0IHdpZHRoPSIzMCIgaGVpZ2h0PSIyMCIgZmlsbD0iI2RlMjkxMCIvPg0KPHVzZSB4bGluazpocmVmPSIjcyIgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoNSw1KSBzY2FsZSgzKSIvPg0KPHVzZSB4bGluazpocmVmPSIjcyIgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoMTAsMikgcm90YXRlKDIzLjAzNjI0MykiLz4NCjx1c2UgeGxpbms6aHJlZj0iI3MiIHRyYW5zZm9ybT0idHJhbnNsYXRlKDEyLDQpIHJvdGF0ZSg0NS44Njk4OTgpIi8%2BDQo8dXNlIHhsaW5rOmhyZWY9IiNzIiB0cmFuc2Zvcm09InRyYW5zbGF0ZSgxMiw3KSByb3RhdGUoNjkuOTQ1Mzk2KSIvPg0KPHVzZSB4bGluazpocmVmPSIjcyIgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoMTAsOSkgcm90YXRlKDIwLjY1OTgwOCkiLz4NCjwvc3ZnPg%3D%3D)](https://gitter.im/ant-design/ant-design-pro?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge) [![Build With Umi](https://img.shields.io/badge/build%20with-umi-028fe4.svg?style=flat-square)](http://umijs.org/) ![](https://badgen.net/badge/icon/Ant%20Design?icon=https://gw.alipayobjects.com/zos/antfincdn/Pp4WPgVDB3/KDpgvguMpGfqaHPjicRK.svg&label) diff --git a/config/config.ts b/config/config.ts index dee1c080..21a024d2 100644 --- a/config/config.ts +++ b/config/config.ts @@ -316,9 +316,13 @@ export default defineConfig({ component: '404', }, ], + access: {}, // Theme for antd: https://ant.design/docs/react/customize-theme-cn theme: { - 'primary-color': defaultSettings.primaryColor, + // 如果不想要 configProvide 动态设置主题需要把这个设置为 default + // 只有设置为 variable, 才能使用 configProvide 动态设置主色调 + // https://ant.design/docs/react/customize-theme-variable-cn + 'root-entry-name': 'variable', }, // esbuild is father build tools // https://umijs.org/plugins/plugin-esbuild diff --git a/config/proxy.ts b/config/proxy.ts index d6c81ea7..a8194b7e 100644 --- a/config/proxy.ts +++ b/config/proxy.ts @@ -8,15 +8,18 @@ */ export default { dev: { + // localhost:8000/api/** -> https://preview.pro.ant.design/api/** '/api/': { + // 要代理的地址 target: 'https://preview.pro.ant.design', + // 配置了这个可以从 http 代理到 https + // 依赖 origin 的功能可能需要这个,比如 cookie changeOrigin: true, - pathRewrite: { '^': '' }, }, }, test: { '/api/': { - target: 'https://preview.pro.ant.design', + target: 'https://proapi.azurewebsites.net', changeOrigin: true, pathRewrite: { '^': '' }, }, diff --git a/config/routes.ts b/config/routes.ts index ea518bba..92b61fbe 100644 --- a/config/routes.ts +++ b/config/routes.ts @@ -4,14 +4,9 @@ layout: false, routes: [ { - path: '/user', - routes: [ - { - name: 'login', - path: '/user/login', - component: './user/Login', - }, - ], + name: 'login', + path: '/user/login', + component: './user/Login', }, { component: './404', diff --git a/jest.config.js b/jest.config.js index 09a2211b..47295738 100644 --- a/jest.config.js +++ b/jest.config.js @@ -1,6 +1,5 @@ module.exports = { testURL: 'http://localhost:8000', - testEnvironment: './tests/PuppeteerEnvironment', verbose: false, extraSetupFiles: ['./tests/setupTests.js'], globals: { diff --git a/jsconfig.json b/jsconfig.json index f87334d4..197bee5d 100644 --- a/jsconfig.json +++ b/jsconfig.json @@ -1,5 +1,6 @@ { "compilerOptions": { + "jsx": "react-jsx", "emitDecoratorMetadata": true, "experimentalDecorators": true, "baseUrl": ".", diff --git a/mock/listTableList.ts b/mock/listTableList.ts index d58a1bdc..08ed86de 100644 --- a/mock/listTableList.ts +++ b/mock/listTableList.ts @@ -1,4 +1,3 @@ -// eslint-disable-next-line import/no-extraneous-dependencies import { Request, Response } from 'express'; import moment from 'moment'; import { parse } from 'url'; diff --git a/package.json b/package.json index 0e7050d8..a7a9c406 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "ant-design-pro", - "version": "5.0.0", + "version": "5.2.0", "private": true, "description": "An out-of-box UI solution for enterprise applications", "scripts": { @@ -26,19 +26,19 @@ "lint:prettier": "prettier -c --write \"src/**/*\" --end-of-line auto", "lint:style": "stylelint --fix \"src/**/*.less\" --syntax less", "openapi": "umi openapi", - "precommit": "lint-staged", + "playwright": "playwright install && playwright test", + "prepare": "husky install", "prettier": "prettier -c --write \"src/**/*\"", + "serve": "umi-serve", "start": "cross-env UMI_ENV=dev umi dev", "start:dev": "cross-env REACT_APP_ENV=dev MOCK=none UMI_ENV=dev umi dev", "start:no-mock": "cross-env MOCK=none UMI_ENV=dev umi dev", "start:no-ui": "cross-env UMI_UI=none UMI_ENV=dev umi dev", "start:pre": "cross-env REACT_APP_ENV=pre UMI_ENV=dev umi dev", "start:test": "cross-env REACT_APP_ENV=test MOCK=none UMI_ENV=dev umi dev", - "pretest": "node ./tests/beforeTest", "test": "umi test", - "test:all": "node ./tests/run-tests.js", "test:component": "umi test ./src/components", - "serve": "umi-serve", + "test:e2e": "node ./tests/run-tests.js", "tsc": "tsc --noEmit" }, "lint-staged": { @@ -55,73 +55,75 @@ ], "dependencies": { "@ant-design/charts": "^0.9.4", - "@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", + "@ant-design/icons": "^4.7.0", + "@ant-design/pro-card": "^1.19.0", + "@ant-design/pro-descriptions": "^1.10.0", + "@ant-design/pro-form": "^1.64.0", + "@ant-design/pro-layout": "^6.35.0", + "@ant-design/pro-table": "^2.71.0", "@antv/data-set": "^0.11.0", "@antv/l7": "^2.3.7", "@antv/l7-maps": "^2.3.7", "@antv/l7-react": "^2.1.9", - "@umijs/route-utils": "^1.0.36", + "@umijs/route-utils": "^2.0.0", "ahooks": "^2.0.0", - "antd": "^4.14.0", + "antd": "^4.19.0", "bizcharts": "^3.5.3-beta.0", "bizcharts-plugin-slider": "^2.1.1-beta.1", - "classnames": "^2.2.6", + "classnames": "^2.3.0", "gg-editor": "^2.0.2", - "lodash": "^4.17.11", + "lodash": "^4.17.0", "lodash-decorators": "^6.0.0", - "moment": "^2.25.3", + "moment": "^2.29.0", "numeral": "^2.0.6", "nzh": "^1.0.3", "omit.js": "^2.0.2", + "rc-menu": "^9.1.0", + "rc-util": "^5.16.0", "react": "^17.0.0", - "react-dev-inspector": "^1.1.1", + "react-dev-inspector": "^1.7.0", "react-dom": "^17.0.0", "react-fittext": "^1.0.0", - "react-helmet-async": "^1.0.4", + "react-helmet-async": "^1.2.0", "react-router": "^4.3.1", "umi": "^3.5.0", "umi-serve": "^1.9.10" }, "devDependencies": { - "@ant-design/pro-cli": "^2.0.2", + "@ant-design/pro-cli": "^2.1.0", + "@playwright/test": "^1.17.0", "@types/express": "^4.17.0", - "@types/history": "^4.7.2", + "@types/history": "^4.7.0", "@types/jest": "^26.0.0", - "@types/lodash": "^4.14.144", + "@types/lodash": "^4.14.0", "@types/react": "^17.0.0", "@types/react-dom": "^17.0.0", "@types/react-helmet": "^6.1.0", - "@umijs/fabric": "^2.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-dumi": "^1.1.7", - "@umijs/preset-react": "^1.8.17", - "@umijs/yorkie": "^2.0.3", - "carlo": "^0.9.46", + "@umijs/fabric": "^2.8.0", + "@umijs/openapi": "^1.3.0", + "@umijs/plugin-blocks": "^2.2.0", + "@umijs/plugin-esbuild": "^1.4.0", + "@umijs/plugin-openapi": "^1.3.0", + "@umijs/preset-ant-design-pro": "^1.3.0", + "@umijs/preset-dumi": "^1.1.0", + "@umijs/preset-react": "^2.1.0", "cross-env": "^7.0.0", - "cross-port-killer": "^1.1.1", - "detect-installer": "^1.0.1", - "enzyme": "^3.11.0", - "eslint": "^7.1.0", - "express": "^4.17.1", - "gh-pages": "^3.0.0", - "jsdom-global": "^3.0.2", + "cross-port-killer": "^1.3.0", + "detect-installer": "^1.0.0", + "eslint": "^7.32.0", + "gh-pages": "^3.2.0", + "husky": "^7.0.4", + "jsdom-global": "^3.0.0", "lint-staged": "^10.0.0", - "mockjs": "^1.0.1-beta3", - "prettier": "^2.3.2", - "puppeteer-core": "^8.0.0", + "mockjs": "^1.1.0", + "prettier": "^2.5.0", "stylelint": "^13.0.0", - "typescript": "^4.2.2" + "swagger-ui-react": "^3.52.0", + "typescript": "^4.5.0", + "umi-serve": "^1.9.10" }, "engines": { - "node": ">=10.0.0" + "node": ">=12.0.0" }, "create-umi": { "ignoreScript": [ @@ -151,8 +153,5 @@ "CNAME", "create-umi" ] - }, - "gitHooks": { - "commit-msg": "fabric verify-commit" } } \ No newline at end of file diff --git a/playwright.config.ts b/playwright.config.ts new file mode 100644 index 00000000..ec1b31db --- /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/src/access.ts b/src/access.ts index 2ec89bdd..e823e24b 100644 --- a/src/access.ts +++ b/src/access.ts @@ -1,8 +1,8 @@ /** * @see https://umijs.org/zh-CN/plugins/plugin-access * */ -export default function access(initialState: { currentUser?: API.CurrentUser | undefined }) { - const { currentUser } = initialState || {}; +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 index f037d946..58571014 100644 --- a/src/app.tsx +++ b/src/app.tsx @@ -1,4 +1,5 @@ import type { Settings as LayoutSettings } from '@ant-design/pro-layout'; +import { SettingDrawer } from '@ant-design/pro-layout'; import { PageLoading } from '@ant-design/pro-layout'; import type { RunTimeLayoutConfig } from 'umi'; import { history, Link } from 'umi'; @@ -6,6 +7,7 @@ import RightContent from '@/components/RightContent'; import Footer from '@/components/Footer'; import { currentUser as queryCurrentUser } from './services/ant-design-pro/api'; import { BookOutlined, LinkOutlined } from '@ant-design/icons'; +import defaultSettings from '../config/defaultSettings'; const isDev = process.env.NODE_ENV === 'development'; const loginPath = '/user/login'; @@ -21,6 +23,7 @@ export const initialStateConfig = { export async function getInitialState(): Promise<{ settings?: Partial; currentUser?: API.CurrentUser; + loading?: boolean; fetchUserInfo?: () => Promise; }> { const fetchUserInfo = async () => { @@ -32,23 +35,23 @@ export async function getInitialState(): Promise<{ } return undefined; }; - // 如果是登录页面,不执行 + // 如果不是登录页面,执行 if (history.location.pathname !== loginPath) { const currentUser = await fetchUserInfo(); return { fetchUserInfo, currentUser, - settings: {}, + settings: defaultSettings, }; } return { fetchUserInfo, - settings: {}, + settings: defaultSettings, }; } // ProLayout 支持的api https://procomponents.ant.design/components/layout -export const layout: RunTimeLayoutConfig = ({ initialState }) => { +export const layout: RunTimeLayoutConfig = ({ initialState, setInitialState }) => { return { rightContentRender: () => , disableContentMargin: false, @@ -65,11 +68,11 @@ export const layout: RunTimeLayoutConfig = ({ initialState }) => { }, links: isDev ? [ - + OpenAPI 文档 , - + 业务组件文档 , @@ -78,6 +81,28 @@ export const layout: RunTimeLayoutConfig = ({ initialState }) => { menuHeaderRender: undefined, // 自定义 403 页面 // unAccessible:
unAccessible
, + // 增加一个 loading 的状态 + childrenRender: (children, props) => { + // if (initialState?.loading) return ; + return ( + <> + {children} + {!props.location?.pathname?.includes('/login') && ( + { + setInitialState((preInitialState) => ({ + ...preInitialState, + settings, + })); + }} + /> + )} + + ); + }, ...initialState?.settings, }; }; diff --git a/src/components/Footer/index.tsx b/src/components/Footer/index.tsx index b259d6f8..4336de96 100644 --- a/src/components/Footer/index.tsx +++ b/src/components/Footer/index.tsx @@ -2,7 +2,7 @@ import { useIntl } from 'umi'; import { GithubOutlined } from '@ant-design/icons'; import { DefaultFooter } from '@ant-design/pro-layout'; -export default () => { +const Footer: React.FC = () => { const intl = useIntl(); const defaultMessage = intl.formatMessage({ id: 'app.copyright.produced', @@ -37,3 +37,5 @@ export default () => { /> ); }; + +export default Footer; diff --git a/src/components/HeaderDropdown/index.less b/src/components/HeaderDropdown/index.less index 004b53eb..f583513f 100644 --- a/src/components/HeaderDropdown/index.less +++ b/src/components/HeaderDropdown/index.less @@ -1,4 +1,4 @@ -@import '~antd/es/style/themes/default.less'; +@import (reference) '~antd/es/style/themes/index'; .container > * { background-color: @popover-bg; diff --git a/src/components/HeaderSearch/index.less b/src/components/HeaderSearch/index.less index 15060bdc..7dc3eaa0 100644 --- a/src/components/HeaderSearch/index.less +++ b/src/components/HeaderSearch/index.less @@ -1,4 +1,4 @@ -@import '~antd/es/style/themes/default.less'; +@import (reference) '~antd/es/style/themes/index'; .headerSearch { display: inline-flex; diff --git a/src/components/HeaderSearch/index.tsx b/src/components/HeaderSearch/index.tsx index 91cc697e..d282de14 100644 --- a/src/components/HeaderSearch/index.tsx +++ b/src/components/HeaderSearch/index.tsx @@ -1,4 +1,5 @@ import { SearchOutlined } from '@ant-design/icons'; +import type { InputRef } from 'antd'; import { AutoComplete, Input } from 'antd'; import useMergedState from 'rc-util/es/hooks/useMergedState'; import type { AutoCompleteProps } from 'antd/es/auto-complete'; @@ -31,7 +32,7 @@ const HeaderSearch: React.FC = (props) => { ...restProps } = props; - const inputRef = useRef(null); + const inputRef = useRef(null); const [value, setValue] = useMergedState(defaultValue, { value: props.value, @@ -74,7 +75,7 @@ const HeaderSearch: React.FC = (props) => { className={inputClass} value={value} options={restProps.options} - onChange={setValue} + onChange={(completeValue) => setValue(completeValue)} > ) => { return unreadMsg; }; -const NoticeIconView = () => { +const NoticeIconView: React.FC = () => { const { initialState } = useModel('@@initialState'); const { currentUser } = initialState || {}; const [notices, setNotices] = useState([]); diff --git a/src/components/RightContent/AvatarDropdown.tsx b/src/components/RightContent/AvatarDropdown.tsx index d9e67311..fc768c29 100644 --- a/src/components/RightContent/AvatarDropdown.tsx +++ b/src/components/RightContent/AvatarDropdown.tsx @@ -17,14 +17,14 @@ export type GlobalHeaderRightProps = { */ const loginOut = async () => { await outLogin(); - const { query = {}, pathname } = history.location; + const { query = {}, search, pathname } = history.location; const { redirect } = query; // Note: There may be security issues, please note if (window.location.pathname !== '/user/login' && !redirect) { history.replace({ pathname: '/user/login', search: stringify({ - redirect: pathname, + redirect: pathname + search, }), }); } diff --git a/src/components/RightContent/index.less b/src/components/RightContent/index.less index 486e80c9..6d6f1986 100644 --- a/src/components/RightContent/index.less +++ b/src/components/RightContent/index.less @@ -1,4 +1,4 @@ -@import '~antd/es/style/themes/default.less'; +@import (reference) '~antd/es/style/themes/index'; @pro-header-hover-bg: rgba(0, 0, 0, 0.025); diff --git a/src/e2e/baseLayout.e2e.js b/src/e2e/baseLayout.e2e.js deleted file mode 100644 index 459391cd..00000000 --- a/src/e2e/baseLayout.e2e.js +++ /dev/null @@ -1,61 +0,0 @@ -const { uniq } = require('lodash'); -const RouterConfig = require('../../config/config').default.routes; - -const BASE_URL = `http://localhost:${process.env.PORT || 8001}`; - -function formatter(routes, parentPath = '') { - const fixedParentPath = parentPath.replace(/\/{1,}/g, '/'); - let result = []; - routes.forEach((item) => { - if (item.path && !item.path.startsWith('/')) { - result.push(`${fixedParentPath}/${item.path}`.replace(/\/{1,}/g, '/')); - } - if (item.path && item.path.startsWith('/')) { - result.push(`${item.path}`.replace(/\/{1,}/g, '/')); - } - if (item.routes) { - result = result.concat( - formatter(item.routes, item.path ? `${fixedParentPath}/${item.path}` : parentPath), - ); - } - }); - return uniq(result.filter((item) => !!item)); -} - -beforeEach(async () => { - await page.goto(`${BASE_URL}`); - await page.evaluate(() => { - localStorage.setItem('antd-pro-authority', '["admin"]'); - }); -}); - -describe('Ant Design Pro E2E test', () => { - const testPage = (path) => async () => { - await page.goto(`${BASE_URL}${path}`); - await page.waitForSelector('footer', { - timeout: 2000, - }); - const haveFooter = await page.evaluate( - () => document.getElementsByTagName('footer').length > 0, - ); - expect(haveFooter).toBeTruthy(); - }; - - const routers = formatter(RouterConfig); - routers.forEach((route) => { - it(`test pages ${route}`, testPage(route)); - }); - - it('topmenu should have footer', async () => { - const params = '?navTheme=light&layout=topmenu'; - await page.goto(`${BASE_URL}${params}`); - await page.waitForSelector('footer', { - timeout: 2000, - }); - - const haveFooter = await page.evaluate( - () => document.getElementsByTagName('footer').length > 0, - ); - expect(haveFooter).toBeTruthy(); - }); -}); diff --git a/src/e2e/baseLayout.e2e.spec.ts b/src/e2e/baseLayout.e2e.spec.ts new file mode 100644 index 00000000..e1d62c71 --- /dev/null +++ b/src/e2e/baseLayout.e2e.spec.ts @@ -0,0 +1,45 @@ +import type { Page } from '@playwright/test'; +import { test, expect } from '@playwright/test'; +const { uniq } = require('lodash'); +const RouterConfig = require('../../config/routes').default; + +const BASE_URL = `http://localhost:${process.env.PORT || 8001}`; + +function formatter(routes: any, parentPath = ''): string[] { + const fixedParentPath = parentPath.replace(/\/{1,}/g, '/'); + let result: string[] = []; + routes.forEach((item: { path: string; routes: string }) => { + if (item.path && !item.path.startsWith('/')) { + result.push(`${fixedParentPath}/${item.path}`.replace(/\/{1,}/g, '/')); + } + if (item.path && item.path.startsWith('/')) { + result.push(`${item.path}`.replace(/\/{1,}/g, '/')); + } + if (item.routes) { + result = result.concat( + formatter(item.routes, item.path ? `${fixedParentPath}/${item.path}` : parentPath), + ); + } + }); + return uniq(result.filter((item) => !!item)); +} + +const testPage = (path: string, page: Page) => async () => { + await page.evaluate(() => { + localStorage.setItem('antd-pro-authority', '["admin"]'); + }); + await page.goto(`${BASE_URL}${path}`); + await page.waitForSelector('footer', { + timeout: 2000, + }); + const haveFooter = await page.evaluate(() => document.getElementsByTagName('footer').length > 0); + expect(haveFooter).toBeTruthy(); +}; + +const routers = formatter(RouterConfig); + +routers.forEach((route) => { + test(`test route page ${route}`, async ({ page }) => { + await testPage(route, page); + }); +}); diff --git a/src/global.less b/src/global.less index 21a21f5e..0d6158c7 100644 --- a/src/global.less +++ b/src/global.less @@ -1,4 +1,4 @@ -@import '~antd/es/style/themes/default.less'; +@import '~antd/es/style/variable.less'; html, body, diff --git a/src/pages/Admin.tsx b/src/pages/Admin.tsx index abd291f6..d8922fa2 100644 --- a/src/pages/Admin.tsx +++ b/src/pages/Admin.tsx @@ -4,7 +4,7 @@ import { Card, Typography, Alert } from 'antd'; import { PageHeaderWrapper } from '@ant-design/pro-layout'; import { useIntl } from 'umi'; -export default (): React.ReactNode => { +const Admin: React.FC = () => { const intl = useIntl(); return ( { ); }; + +export default Admin; diff --git a/src/pages/Welcome.less b/src/pages/Welcome.less index 914c40d8..224a7932 100644 --- a/src/pages/Welcome.less +++ b/src/pages/Welcome.less @@ -1,4 +1,4 @@ -@import '~antd/lib/style/themes/default.less'; +@import (reference) '~antd/es/style/themes/index'; .pre { margin: 12px 0; diff --git a/src/pages/Welcome.tsx b/src/pages/Welcome.tsx index 12bf6d2a..9041b181 100644 --- a/src/pages/Welcome.tsx +++ b/src/pages/Welcome.tsx @@ -12,8 +12,9 @@ const CodePreview: React.FC = ({ children }) => ( ); -export default (): React.ReactNode => { +const Welcome: React.FC = () => { const intl = useIntl(); + return ( @@ -61,3 +62,5 @@ export default (): React.ReactNode => { ); }; + +export default Welcome; diff --git a/src/pages/document.ejs b/src/pages/document.ejs index bfc96300..6db8355f 100644 --- a/src/pages/document.ejs +++ b/src/pages/document.ejs @@ -2,6 +2,7 @@ + { - // find can use package manager - const packages = detectInstaller(join(__dirname, '../')); - // get installed package manager - const packageName = packages.find(detectInstaller.hasPackageCommand) || 'npm'; - console.log(`🤖 will use ${packageName} install puppeteer`); - const command = `${packageName} ${packageName.includes('yarn') ? 'add' : 'i'} puppeteer`; - execSync(command, { - stdio: 'inherit', - }); -}; - -const initPuppeteer = async () => { - try { - // eslint-disable-next-line import/no-unresolved - const findChromePath = await findChrome({}); - const { executablePath } = findChromePath; - console.log(`🧲 find you browser in ${executablePath}`); - return; - } catch (error) { - console.log('🧲 no find chrome'); - } - - try { - require.resolve('puppeteer'); - } catch (error) { - // need install puppeteer - await installPuppeteer(); - } -}; - -initPuppeteer(); diff --git a/tests/getBrowser.js b/tests/getBrowser.js deleted file mode 100644 index 3b4ef331..00000000 --- a/tests/getBrowser.js +++ /dev/null @@ -1,45 +0,0 @@ -/* eslint-disable global-require */ -/* eslint-disable import/no-extraneous-dependencies */ -const findChrome = require('carlo/lib/find_chrome'); - -const getBrowser = async () => { - try { - // eslint-disable-next-line import/no-unresolved - const puppeteer = require('puppeteer'); - const browser = await puppeteer.launch({ - args: [ - '--disable-gpu', - '--disable-dev-shm-usage', - '--no-first-run', - '--no-zygote', - '--no-sandbox', - ], - }); - return browser; - } catch (error) { - // console.log(error) - } - - try { - // eslint-disable-next-line import/no-unresolved - const puppeteer = require('puppeteer-core'); - const findChromePath = await findChrome({}); - const { executablePath } = findChromePath; - const browser = await puppeteer.launch({ - executablePath, - args: [ - '--disable-gpu', - '--disable-dev-shm-usage', - '--no-first-run', - '--no-zygote', - '--no-sandbox', - ], - }); - return browser; - } catch (error) { - console.log('🧲 no find chrome'); - } - throw new Error('no find puppeteer'); -}; - -module.exports = getBrowser; diff --git a/tests/run-tests.js b/tests/run-tests.js index 368cd5e3..d4e0fc8e 100644 --- a/tests/run-tests.js +++ b/tests/run-tests.js @@ -1,8 +1,5 @@ -/* eslint-disable eslint-comments/disable-enable-pair */ /* eslint-disable @typescript-eslint/no-var-requires */ -/* eslint-disable eslint-comments/no-unlimited-disable */ const { spawn } = require('child_process'); -// eslint-disable-next-line import/no-extraneous-dependencies const { kill } = require('cross-port-killer'); const env = Object.create(process.env); @@ -36,13 +33,13 @@ startServer.stdout.on('data', (data) => { console.log('Development server is started, ready to run tests.'); const testCmd = spawn( /^win/.test(process.platform) ? 'npm.cmd' : 'npm', - ['test', '--', '--maxWorkers=1', '--runInBand'], + ['run', 'playwright'], { stdio: 'inherit', }, ); testCmd.on('exit', (code) => { - console.log(code); + console.log('服务已经退出,退出码:', code); startServer.kill(); process.exit(code); }); diff --git a/tsconfig.json b/tsconfig.json index 90a06413..e7a27859 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -26,6 +26,7 @@ "include": [ "mock/**/*", "src/**/*", + "playwright.config.ts", "tests/**/*", "test/**/*", "__test__/**/*",