Browse Source

merge

pull/9795/head
期贤 4 years ago
parent
commit
8adf6dd244
  1. 4
      .github/workflows/ci.yml
  2. 4
      .github/workflows/deploy.yml
  3. 4
      .gitignore
  4. 1
      .husky/.gitignore
  5. 7
      .husky/commit-msg
  6. 4
      .husky/pre-commit
  7. 4
      .stylelintrc.js
  8. 8
      .vscode/extensions.json
  9. 5
      .vscode/settings.json
  10. 2
      Dockerfile
  11. 2
      Dockerfile.dev
  12. 2
      Dockerfile.hub
  13. 2
      README.md
  14. 6
      config/config.ts
  15. 7
      config/proxy.ts
  16. 11
      config/routes.ts
  17. 1
      jest.config.js
  18. 1
      jsconfig.json
  19. 1
      mock/listTableList.ts
  20. 89
      package.json
  21. 22
      playwright.config.ts
  22. 4
      src/access.ts
  23. 37
      src/app.tsx
  24. 4
      src/components/Footer/index.tsx
  25. 2
      src/components/HeaderDropdown/index.less
  26. 2
      src/components/HeaderSearch/index.less
  27. 5
      src/components/HeaderSearch/index.tsx
  28. 2
      src/components/NoticeIcon/NoticeList.less
  29. 4
      src/components/NoticeIcon/index.less
  30. 2
      src/components/NoticeIcon/index.tsx
  31. 4
      src/components/RightContent/AvatarDropdown.tsx
  32. 2
      src/components/RightContent/index.less
  33. 61
      src/e2e/baseLayout.e2e.js
  34. 45
      src/e2e/baseLayout.e2e.spec.ts
  35. 2
      src/global.less
  36. 4
      src/pages/Admin.tsx
  37. 2
      src/pages/Welcome.less
  38. 5
      src/pages/Welcome.tsx
  39. 1
      src/pages/document.ejs
  40. 2
      src/pages/user/Login/index.less
  41. 1
      src/service-worker.js
  42. 41
      tests/PuppeteerEnvironment.js
  43. 39
      tests/beforeTest.js
  44. 45
      tests/getBrowser.js
  45. 7
      tests/run-tests.js
  46. 1
      tsconfig.json

4
.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

4
.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

4
.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

1
.husky/.gitignore

@ -0,0 +1 @@
_

7
.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

4
.husky/pre-commit

@ -0,0 +1,4 @@
#!/bin/sh
. "$(dirname "$0")/_/husky.sh"
npx --no-install lint-staged

4
.stylelintrc.js

@ -1,5 +1,3 @@
const fabric = require('@umijs/fabric');
module.exports = {
...fabric.stylelint,
extends: [require.resolve('@umijs/fabric/dist/stylelint')],
};

8
.vscode/extensions.json

@ -0,0 +1,8 @@
{
"recommendations": [
"esbenp.prettier-vscode",
"dbaeumer.vscode-eslint",
"stylelint.vscode-stylelint",
"wangzy.sneak-mark"
]
}

5
.vscode/settings.json

@ -0,0 +1,5 @@
{
"editor.formatOnSave": true,
"prettier.requireConfig": true,
"editor.defaultFormatter": "esbenp.prettier-vscode"
}

2
Dockerfile

@ -7,7 +7,7 @@ RUN yarn
COPY ./ ./
RUN npm run test:all
RUN npm run test:e2e
RUN npm run fetch:blocks

2
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 ./ ./

2
Dockerfile.hub

@ -7,7 +7,7 @@ RUN yarn
COPY ./ ./
RUN npm run test:all
RUN npm run test:e2e
RUN npm run fetch:blocks

2
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)

6
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

7
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: { '^': '' },
},

11
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',

1
jest.config.js

@ -1,6 +1,5 @@
module.exports = {
testURL: 'http://localhost:8000',
testEnvironment: './tests/PuppeteerEnvironment',
verbose: false,
extraSetupFiles: ['./tests/setupTests.js'],
globals: {

1
jsconfig.json

@ -1,5 +1,6 @@
{
"compilerOptions": {
"jsx": "react-jsx",
"emitDecoratorMetadata": true,
"experimentalDecorators": true,
"baseUrl": ".",

1
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';

89
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"
}
}

22
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;

4
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',
};

37
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<LayoutSettings>;
currentUser?: API.CurrentUser;
loading?: boolean;
fetchUserInfo?: () => Promise<API.CurrentUser | undefined>;
}> {
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: () => <RightContent />,
disableContentMargin: false,
@ -65,11 +68,11 @@ export const layout: RunTimeLayoutConfig = ({ initialState }) => {
},
links: isDev
? [
<Link to="/umi/plugin/openapi" target="_blank">
<Link key="openapi" to="/umi/plugin/openapi" target="_blank">
<LinkOutlined />
<span>OpenAPI </span>
</Link>,
<Link to="/~docs">
<Link to="/~docs" key="docs">
<BookOutlined />
<span></span>
</Link>,
@ -78,6 +81,28 @@ export const layout: RunTimeLayoutConfig = ({ initialState }) => {
menuHeaderRender: undefined,
// 自定义 403 页面
// unAccessible: <div>unAccessible</div>,
// 增加一个 loading 的状态
childrenRender: (children, props) => {
// if (initialState?.loading) return <PageLoading />;
return (
<>
{children}
{!props.location?.pathname?.includes('/login') && (
<SettingDrawer
disableUrlParams
enableDarkTheme
settings={initialState?.settings}
onSettingChange={(settings) => {
setInitialState((preInitialState) => ({
...preInitialState,
settings,
}));
}}
/>
)}
</>
);
},
...initialState?.settings,
};
};

4
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;

2
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;

2
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;

5
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<HeaderSearchProps> = (props) => {
...restProps
} = props;
const inputRef = useRef<Input | null>(null);
const inputRef = useRef<InputRef | null>(null);
const [value, setValue] = useMergedState<string | undefined>(defaultValue, {
value: props.value,
@ -74,7 +75,7 @@ const HeaderSearch: React.FC<HeaderSearchProps> = (props) => {
className={inputClass}
value={value}
options={restProps.options}
onChange={setValue}
onChange={(completeValue) => setValue(completeValue)}
>
<Input
size="small"

2
src/components/NoticeIcon/NoticeList.less

@ -1,4 +1,4 @@
@import '~antd/es/style/themes/default.less';
@import (reference) '~antd/es/style/themes/index';
.list {
max-height: 400px;

4
src/components/NoticeIcon/index.less

@ -1,4 +1,4 @@
@import '~antd/es/style/themes/default.less';
@import (reference) '~antd/es/style/themes/index';
.popover {
position: relative;
@ -28,7 +28,7 @@
.ant-tabs-nav-scroll {
text-align: center;
}
.ant-tabs-bar {
.ant-tabs-nav {
margin-bottom: 0;
}
}

2
src/components/NoticeIcon/index.tsx

@ -70,7 +70,7 @@ const getUnreadData = (noticeData: Record<string, API.NoticeIconItem[]>) => {
return unreadMsg;
};
const NoticeIconView = () => {
const NoticeIconView: React.FC = () => {
const { initialState } = useModel('@@initialState');
const { currentUser } = initialState || {};
const [notices, setNotices] = useState<API.NoticeIconItem[]>([]);

4
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,
}),
});
}

2
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);

61
src/e2e/baseLayout.e2e.js

@ -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();
});
});

45
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);
});
});

2
src/global.less

@ -1,4 +1,4 @@
@import '~antd/es/style/themes/default.less';
@import '~antd/es/style/variable.less';
html,
body,

4
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 (
<PageHeaderWrapper
@ -41,3 +41,5 @@ export default (): React.ReactNode => {
</PageHeaderWrapper>
);
};
export default Admin;

2
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;

5
src/pages/Welcome.tsx

@ -12,8 +12,9 @@ const CodePreview: React.FC = ({ children }) => (
</pre>
);
export default (): React.ReactNode => {
const Welcome: React.FC = () => {
const intl = useIntl();
return (
<PageContainer>
<Card>
@ -61,3 +62,5 @@ export default (): React.ReactNode => {
</PageContainer>
);
};
export default Welcome;

1
src/pages/document.ejs

@ -2,6 +2,7 @@
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="theme-color" content="#1890ff" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta
name="keywords"

2
src/pages/user/Login/index.less

@ -1,4 +1,4 @@
@import '~antd/es/style/themes/default.less';
@import (reference) '~antd/es/style/themes/index';
.container {
display: flex;

1
src/service-worker.js

@ -1,4 +1,3 @@
/* eslint-disable eslint-comments/disable-enable-pair */
/* eslint-disable no-restricted-globals */
/* eslint-disable no-underscore-dangle */
/* globals workbox */

41
tests/PuppeteerEnvironment.js

@ -1,41 +0,0 @@
// eslint-disable-next-line
const NodeEnvironment = require('jest-environment-node');
const getBrowser = require('./getBrowser');
class PuppeteerEnvironment extends NodeEnvironment {
// Jest is not available here, so we have to reverse engineer
// the setTimeout function, see https://github.com/facebook/jest/blob/v23.1.0/packages/jest-runtime/src/index.js#L823
setTimeout(timeout) {
if (this.global.jasmine) {
// eslint-disable-next-line no-underscore-dangle
this.global.jasmine.DEFAULT_TIMEOUT_INTERVAL = timeout;
} else {
this.global[Symbol.for('TEST_TIMEOUT_SYMBOL')] = timeout;
}
}
async setup() {
const browser = await getBrowser();
const page = await browser.newPage();
this.global.browser = browser;
this.global.page = page;
}
async teardown() {
const { page, browser } = this.global;
if (page) {
await page.close();
}
if (browser) {
await browser.disconnect();
}
if (browser) {
await browser.close();
}
}
}
module.exports = PuppeteerEnvironment;

39
tests/beforeTest.js

@ -1,39 +0,0 @@
/* eslint-disable global-require */
/* eslint-disable import/no-extraneous-dependencies */
const { execSync } = require('child_process');
const { join } = require('path');
const findChrome = require('carlo/lib/find_chrome');
const detectInstaller = require('detect-installer');
const installPuppeteer = () => {
// 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();

45
tests/getBrowser.js

@ -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;

7
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);
});

1
tsconfig.json

@ -26,6 +26,7 @@
"include": [
"mock/**/*",
"src/**/*",
"playwright.config.ts",
"tests/**/*",
"test/**/*",
"__test__/**/*",

Loading…
Cancel
Save