未验证 提交 06dd7fa6 编写于 作者: 信鑫-King 提交者: GitHub

features: use umi 3 (#6039)

* refactor: use umi 3

* fix: lint

* fix: d.ts

* fix: ignoreMomentLocale

* feat: add preset ui

* fix: preset

* fix: plugins

* fix: plugin

* fix: config

* fix: antd locale

* 🐛 bugfix: update types

* 🐛 bugfix:fix types error

* feat: add @umijs/plugin-blocks

* block support umi@3

* update deps

* update deps

* remove umi-plugin-antd-icon-config

* lint: fix ts error

* ignore: ignore build

* 🚀 Deploy: do not run yarn lint

* fix e2e test

* fix e2e test

* fix lgmt error

* 🚀 Deploy: add GA_KEY
Co-authored-by: Nchenshuai2144 <qixian.cs@outlook.com>
上级 b60aa50f
...@@ -11,11 +11,12 @@ jobs: ...@@ -11,11 +11,12 @@ jobs:
uses: actions/checkout@master uses: actions/checkout@master
- run: yarn - run: yarn
- run: yarn run lint - run: yarn run lint
- run: yarn run tsc # - run: yarn run tsc
- name: Build and Deploy - name: Build and Deploy
uses: JamesIves/github-pages-deploy-action@master uses: JamesIves/github-pages-deploy-action@master
env: env:
CI: true CI: true
GA_KEY: UA-72788897-6
PROGRESS: none PROGRESS: none
GIT_CONFIG_NAME: qixian.cs GIT_CONFIG_NAME: qixian.cs
GIT_CONFIG_EMAIL: qixian.cs@outlook.com GIT_CONFIG_EMAIL: qixian.cs@outlook.com
...@@ -24,4 +25,4 @@ jobs: ...@@ -24,4 +25,4 @@ jobs:
GITHUB_TOKEN: ${{ secrets.ACTION_TOKEN }} GITHUB_TOKEN: ${{ secrets.ACTION_TOKEN }}
BRANCH: gh-pages BRANCH: gh-pages
FOLDER: 'dist/' FOLDER: 'dist/'
BUILD_SCRIPT: yarn && npm uninstall husky && npm run site && git checkout . && git clean -df BUILD_SCRIPT: yarn && npm uninstall husky && yarn add umi-plugin-antd-theme umi-plugin-pro && npm run site && git checkout . && git clean -df
...@@ -19,3 +19,4 @@ LICENSE ...@@ -19,3 +19,4 @@ LICENSE
yarn-error.log yarn-error.log
.history .history
CNAME CNAME
/build
import { IConfig, IPlugin } from 'umi-types'; // https://umijs.org/config/
import defaultSettings from './defaultSettings'; // https://umijs.org/config/ import { defineConfig, utils } from 'umi';
import slash from 'slash2'; import defaultSettings from './defaultSettings';
import themePluginConfig from './themePluginConfig';
import proxy from './proxy'; import proxy from './proxy';
import webpackPlugin from './plugin.config'; import webpackPlugin from './plugin.config';
const { pwa } = defaultSettings; const { winPath } = utils;
// preview.pro.ant.design only do not use in your production ; // preview.pro.ant.design only do not use in your production ;
// preview.pro.ant.design 专用环境变量,请不要在你的项目中使用它。 // preview.pro.ant.design 专用环境变量,请不要在你的项目中使用它。
const { ANT_DESIGN_PRO_ONLY_DO_NOT_USE_IN_YOUR_PRODUCTION, REACT_APP_ENV } = process.env; const { ANT_DESIGN_PRO_ONLY_DO_NOT_USE_IN_YOUR_PRODUCTION, REACT_APP_ENV, GA_KEY } = process.env;
const isAntDesignProPreview = ANT_DESIGN_PRO_ONLY_DO_NOT_USE_IN_YOUR_PRODUCTION === 'site';
const plugins: IPlugin[] = [
['umi-plugin-antd-icon-config', {}],
[
'umi-plugin-react',
{
antd: true,
dva: {
hmr: true,
},
locale: {
// default false
enable: true,
// default zh-CN
default: 'zh-CN',
// default true, when it is true, will use `navigator.language` overwrite default
baseNavigator: true,
},
dynamicImport: {
loadingComponent: './components/PageLoading/index',
webpackChunkName: true,
level: 3,
},
pwa: pwa
? {
workboxPluginMode: 'InjectManifest',
workboxOptions: {
importWorkboxFrom: 'local',
},
}
: false,
// default close dll, because issue https://github.com/ant-design/ant-design-pro/issues/4665
// dll features https://webpack.js.org/plugins/dll-plugin/
// dll: {
// include: ['dva', 'dva/router', 'dva/saga', 'dva/fetch'],
// exclude: ['@babel/runtime'],
// },
},
],
[
'umi-plugin-pro-block',
{
moveMock: false,
moveService: false,
modifyRequest: true,
autoAddMenu: true,
},
],
];
if (isAntDesignProPreview) { export default defineConfig({
// 针对 preview.pro.ant.design 的 GA 统计代码
plugins.push([
'umi-plugin-ga',
{
code: 'UA-72788897-6',
},
]);
plugins.push([
'umi-plugin-pro',
{
serverUrl: 'https://proapi.azurewebsites.net',
},
]);
plugins.push(['umi-plugin-antd-theme', themePluginConfig]);
}
export default {
plugins,
hash: true, hash: true,
antd: {},
analytics: GA_KEY ? { ga: GA_KEY } : false,
dva: {
hmr: true,
},
locale: {
// default zh-CN
default: 'zh-CN',
// default true, when it is true, will use `navigator.language` overwrite default
antd: true,
baseNavigator: true,
},
dynamicImport: {
loading: '@/components/PageLoading/index',
},
targets: { targets: {
ie: 11, ie: 11,
}, },
...@@ -164,36 +109,36 @@ export default { ...@@ -164,36 +109,36 @@ export default {
ANT_DESIGN_PRO_ONLY_DO_NOT_USE_IN_YOUR_PRODUCTION || '', // preview.pro.ant.design only do not use in your production ; preview.pro.ant.design 专用环境变量,请不要在你的项目中使用它。 ANT_DESIGN_PRO_ONLY_DO_NOT_USE_IN_YOUR_PRODUCTION || '', // preview.pro.ant.design only do not use in your production ; preview.pro.ant.design 专用环境变量,请不要在你的项目中使用它。
}, },
ignoreMomentLocale: true, ignoreMomentLocale: true,
lessLoaderOptions: { lessLoader: {
javascriptEnabled: true, javascriptEnabled: true,
}, },
disableRedirectHoist: true, cssLoader: {
cssLoaderOptions: { modules: {
modules: true, getLocalIdent: (
getLocalIdent: ( context: {
context: { resourcePath: string;
resourcePath: string; },
}, _: string,
_: string, localName: string,
localName: string, ) => {
) => { if (
if ( context.resourcePath.includes('node_modules') ||
context.resourcePath.includes('node_modules') || context.resourcePath.includes('ant.design.pro.less') ||
context.resourcePath.includes('ant.design.pro.less') || context.resourcePath.includes('global.less')
context.resourcePath.includes('global.less') ) {
) { return localName;
}
const match = context.resourcePath.match(/src(.*)/);
if (match && match[1]) {
const antdProPath = match[1].replace('.less', '');
const arr = winPath(antdProPath)
.split('/')
.map((a: string) => a.replace(/([A-Z])/g, '-$1'))
.map((a: string) => a.toLowerCase());
return `antd-pro${arr.join('-')}-${localName}`.replace(/--/g, '-');
}
return localName; return localName;
} },
const match = context.resourcePath.match(/src(.*)/);
if (match && match[1]) {
const antdProPath = match[1].replace('.less', '');
const arr = slash(antdProPath)
.split('/')
.map((a: string) => a.replace(/([A-Z])/g, '-$1'))
.map((a: string) => a.toLowerCase());
return `antd-pro${arr.join('-')}-${localName}`.replace(/--/g, '-');
}
return localName;
}, },
}, },
manifest: { manifest: {
...@@ -201,4 +146,4 @@ export default { ...@@ -201,4 +146,4 @@ export default {
}, },
proxy: proxy[REACT_APP_ENV || 'dev'], proxy: proxy[REACT_APP_ENV || 'dev'],
chainWebpack: webpackPlugin, chainWebpack: webpackPlugin,
} as IConfig; });
export default {
theme: [
{
key: 'dark',
fileName: 'dark.css',
theme: 'dark',
},
{
key: 'dust',
fileName: 'dust.css',
modifyVars: {
'@primary-color': '#F5222D',
},
},
{
key: 'volcano',
fileName: 'volcano.css',
modifyVars: {
'@primary-color': '#FA541C',
},
},
{
key: 'sunset',
fileName: 'sunset.css',
modifyVars: {
'@primary-color': '#FAAD14',
},
},
{
key: 'cyan',
fileName: 'cyan.css',
modifyVars: {
'@primary-color': '#13C2C2',
},
},
{
key: 'green',
fileName: 'green.css',
modifyVars: {
'@primary-color': '#52C41A',
},
},
{
key: 'geekblue',
fileName: 'geekblue.css',
modifyVars: {
'@primary-color': '#2F54EB',
},
},
{
key: 'purple',
fileName: 'purple.css',
modifyVars: {
'@primary-color': '#722ED1',
},
},
{
key: 'dust',
theme: 'dark',
fileName: 'dark-dust.css',
modifyVars: {
'@primary-color': '#F5222D',
},
},
{
key: 'volcano',
theme: 'dark',
fileName: 'dark-volcano.css',
modifyVars: {
'@primary-color': '#FA541C',
},
},
{
key: 'sunset',
theme: 'dark',
fileName: 'dark-sunset.css',
modifyVars: {
'@primary-color': '#FAAD14',
},
},
{
key: 'cyan',
theme: 'dark',
fileName: 'dark-cyan.css',
modifyVars: {
'@primary-color': '#13C2C2',
},
},
{
key: 'green',
theme: 'dark',
fileName: 'dark-green.css',
modifyVars: {
'@primary-color': '#52C41A',
},
},
{
key: 'geekblue',
theme: 'dark',
fileName: 'dark-geekblue.css',
modifyVars: {
'@primary-color': '#2F54EB',
},
},
{
key: 'purple',
theme: 'dark',
fileName: 'dark-purple.css',
modifyVars: {
'@primary-color': '#722ED1',
},
},
],
};
module.exports = { module.exports = {
testURL: 'http://localhost:8000', testURL: 'http://localhost:8000',
preset: 'jest-puppeteer',
extraSetupFiles: ['./tests/setupTests.js'], extraSetupFiles: ['./tests/setupTests.js'],
globals: { globals: {
ANT_DESIGN_PRO_ONLY_DO_NOT_USE_IN_YOUR_PRODUCTION: false, ANT_DESIGN_PRO_ONLY_DO_NOT_USE_IN_YOUR_PRODUCTION: false,
......
// eslint-disable-next-line import/no-extraneous-dependencies // eslint-disable-next-line import/no-extraneous-dependencies
import { Request, Response } from 'express'; import { Request, Response } from 'express';
import { parse } from 'url'; import { parse } from 'url';
import { TableListItem, TableListParams } from './data.d'; import { TableListItem, TableListParams } from '@/pages/ListTableList/data';
// mock tableListDataSource // mock tableListDataSource
const genList = (current: number, pageSize: number) => { const genList = (current: number, pageSize: number) => {
...@@ -34,13 +34,12 @@ const genList = (current: number, pageSize: number) => { ...@@ -34,13 +34,12 @@ const genList = (current: number, pageSize: number) => {
let tableListDataSource = genList(1, 100); let tableListDataSource = genList(1, 100);
function getRule(req: Request, res: Response, u: string) { function getRule(req: Request, res: Response, u: string) {
let url = u; let realUrl = u;
if (!url || Object.prototype.toString.call(url) !== '[object String]') { if (!realUrl || Object.prototype.toString.call(realUrl) !== '[object String]') {
// eslint-disable-next-line prefer-destructuring realUrl = req.url;
url = req.url;
} }
const { current = 1, pageSize = 10 } = req.query; const { current = 1, pageSize = 10 } = req.query;
const params = (parse(url, true).query as unknown) as TableListParams; const params = (parse(realUrl, true).query as unknown) as TableListParams;
let dataSource = [...tableListDataSource].slice((current - 1) * pageSize, current * pageSize); let dataSource = [...tableListDataSource].slice((current - 1) * pageSize, current * pageSize);
if (params.sorter) { if (params.sorter) {
...@@ -84,10 +83,9 @@ function getRule(req: Request, res: Response, u: string) { ...@@ -84,10 +83,9 @@ function getRule(req: Request, res: Response, u: string) {
} }
function postRule(req: Request, res: Response, u: string, b: Request) { function postRule(req: Request, res: Response, u: string, b: Request) {
let url = u; let realUrl = u;
if (!url || Object.prototype.toString.call(url) !== '[object String]') { if (!realUrl || Object.prototype.toString.call(realUrl) !== '[object String]') {
// eslint-disable-next-line prefer-destructuring realUrl = req.url;
url = req.url;
} }
const body = (b && b.body) || req.body; const body = (b && b.body) || req.body;
......
...@@ -14,10 +14,10 @@ ...@@ -14,10 +14,10 @@
"docker:dev": "docker-compose -f ./docker/docker-compose.dev.yml up", "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: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", "docker:tag": "docker tag ant-design-pro antdesign/ant-design-pro",
"fetch:blocks": "pro fetch-blocks && npm run prettier", "fetch:blocks": "pro fetch-blocks --branch=umi@3 && npm run prettier",
"gh-pages": "cp CNAME ./dist/ && gh-pages -d dist", "gh-pages": "cp CNAME ./dist/ && gh-pages -d dist",
"i18n-remove": "pro i18n-remove --locale=zh-CN --write", "i18n-remove": "pro i18n-remove --locale=zh-CN --write",
"lint": "npm run lint:js && npm run lint:style && npm run lint:prettier", "lint": "umi g tmp && npm run lint:js && npm run lint:style && npm run lint:prettier",
"lint:prettier": "prettier --check \"**/*\" --end-of-line auto", "lint:prettier": "prettier --check \"**/*\" --end-of-line auto",
"lint-staged": "lint-staged", "lint-staged": "lint-staged",
"lint-staged:js": "eslint --ext .js,.jsx,.ts,.tsx ", "lint-staged:js": "eslint --ext .js,.jsx,.ts,.tsx ",
...@@ -36,8 +36,7 @@ ...@@ -36,8 +36,7 @@
"test": "umi test", "test": "umi test",
"test:all": "node ./tests/run-tests.js", "test:all": "node ./tests/run-tests.js",
"test:component": "umi test ./src/components", "test:component": "umi test ./src/components",
"tsc": "tsc", "tsc": "tsc"
"ui": "umi ui"
}, },
"husky": { "husky": {
"hooks": { "hooks": {
...@@ -57,28 +56,20 @@ ...@@ -57,28 +56,20 @@
"not ie <= 10" "not ie <= 10"
], ],
"dependencies": { "dependencies": {
"@ant-design/icons": "^4.0.0-alpha.19", "@ant-design/icons": "^4.0.0",
"@ant-design/pro-layout": "^5.0.0", "@ant-design/pro-layout": "^5.0.0",
"@ant-design/pro-table": "^2.0.0", "@ant-design/pro-table": "^2.0.0",
"@antv/data-set": "^0.11.1", "antd": "^4.0.0",
"antd": "^4.0.2",
"classnames": "^2.2.6", "classnames": "^2.2.6",
"dva": "^2.6.0-beta.16",
"lodash": "^4.17.11", "lodash": "^4.17.11",
"moment": "^2.24.0", "moment": "^2.24.0",
"omit.js": "^1.0.2", "omit.js": "^1.0.2",
"path-to-regexp": "2.4.0", "path-to-regexp": "2.4.0",
"qs": "^6.9.0", "qs": "^6.9.0",
"react": "^16.8.6", "react": "^16.8.6",
"react-copy-to-clipboard": "^5.0.1",
"react-dom": "^16.8.6", "react-dom": "^16.8.6",
"react-helmet": "^5.2.1", "react-helmet": "^5.2.1",
"redux": "^4.0.1", "umi": "^3.0.0",
"umi": "^2.13.0",
"umi-plugin-antd-icon-config": "^1.0.2",
"umi-plugin-antd-theme": "1.2.0-0",
"umi-plugin-pro-block": "^1.3.2",
"umi-plugin-react": "^1.14.10",
"umi-request": "^1.0.8", "umi-request": "^1.0.8",
"use-merge-value": "^1.0.1" "use-merge-value": "^1.0.1"
}, },
...@@ -94,6 +85,10 @@ ...@@ -94,6 +85,10 @@
"@types/react-dom": "^16.8.4", "@types/react-dom": "^16.8.4",
"@types/react-helmet": "^5.0.13", "@types/react-helmet": "^5.0.13",
"@umijs/fabric": "^2.0.2", "@umijs/fabric": "^2.0.2",
"@umijs/plugin-blocks": "^2.0.5",
"@umijs/preset-ant-design-pro": "^1.0.1",
"@umijs/preset-react": "^1.3.0",
"@umijs/preset-ui": "^2.0.9",
"chalk": "^3.0.0", "chalk": "^3.0.0",
"cross-env": "^7.0.0", "cross-env": "^7.0.0",
"cross-port-killer": "^1.1.1", "cross-port-killer": "^1.1.1",
...@@ -101,19 +96,12 @@ ...@@ -101,19 +96,12 @@
"express": "^4.17.1", "express": "^4.17.1",
"gh-pages": "^2.0.1", "gh-pages": "^2.0.1",
"husky": "^4.0.7", "husky": "^4.0.7",
"jest-puppeteer": "^4.2.0",
"jsdom-global": "^3.0.2", "jsdom-global": "^3.0.2",
"lint-staged": "^10.0.0", "lint-staged": "^10.0.0",
"mockjs": "^1.0.1-beta3", "mockjs": "^1.0.1-beta3",
"node-fetch": "^2.6.0",
"prettier": "^1.19.1", "prettier": "^1.19.1",
"pro-download": "1.0.1", "pro-download": "1.0.1",
"serverless-http": "^2.0.2", "stylelint": "^13.0.0"
"stylelint": "^13.0.0",
"umi-plugin-antd-icon-config": "^1.0.2",
"umi-plugin-ga": "^1.1.3",
"umi-plugin-pro": "^1.0.3",
"umi-types": "^0.5.9"
}, },
"optionalDependencies": { "optionalDependencies": {
"puppeteer": "^2.0.0" "puppeteer": "^2.0.0"
......
...@@ -21,7 +21,7 @@ const Authorized: React.FunctionComponent<AuthorizedProps> = ({ ...@@ -21,7 +21,7 @@ const Authorized: React.FunctionComponent<AuthorizedProps> = ({
authority, authority,
noMatch = ( noMatch = (
<Result <Result
status={403} status="403"
title="403" title="403"
subTitle="Sorry, you are not authorized to access this page." subTitle="Sorry, you are not authorized to access this page."
/> />
......
...@@ -2,14 +2,13 @@ import { LogoutOutlined, SettingOutlined, UserOutlined } from '@ant-design/icons ...@@ -2,14 +2,13 @@ import { LogoutOutlined, SettingOutlined, UserOutlined } from '@ant-design/icons
import { Avatar, Menu, Spin } from 'antd'; import { Avatar, Menu, Spin } from 'antd';
import { ClickParam } from 'antd/es/menu'; import { ClickParam } from 'antd/es/menu';
import React from 'react'; import React from 'react';
import { connect } from 'dva'; import { history, ConnectProps, connect } from 'umi';
import { router } from 'umi'; import { ConnectState } from '@/models/connect';
import { ConnectProps, ConnectState } from '@/models/connect';
import { CurrentUser } from '@/models/user'; import { CurrentUser } from '@/models/user';
import HeaderDropdown from '../HeaderDropdown'; import HeaderDropdown from '../HeaderDropdown';
import styles from './index.less'; import styles from './index.less';
export interface GlobalHeaderRightProps extends ConnectProps { export interface GlobalHeaderRightProps extends Partial<ConnectProps> {
currentUser?: CurrentUser; currentUser?: CurrentUser;
menu?: boolean; menu?: boolean;
} }
...@@ -30,7 +29,7 @@ class AvatarDropdown extends React.Component<GlobalHeaderRightProps> { ...@@ -30,7 +29,7 @@ class AvatarDropdown extends React.Component<GlobalHeaderRightProps> {
return; return;
} }
router.push(`/account/${key}`); history.push(`/account/${key}`);
}; };
render(): React.ReactNode { render(): React.ReactNode {
......
import React, { Component } from 'react'; import React, { Component } from 'react';
import { connect, ConnectProps } from 'umi';
import { Tag, message } from 'antd'; import { Tag, message } from 'antd';
import { connect } from 'dva';
import groupBy from 'lodash/groupBy'; import groupBy from 'lodash/groupBy';
import moment from 'moment'; import moment from 'moment';
import { NoticeItem } from '@/models/global'; import { NoticeItem } from '@/models/global';
import { CurrentUser } from '@/models/user'; import { CurrentUser } from '@/models/user';
import { ConnectProps, ConnectState } from '@/models/connect'; import { ConnectState } from '@/models/connect';
import NoticeIcon from '../NoticeIcon'; import NoticeIcon from '../NoticeIcon';
import styles from './index.less'; import styles from './index.less';
export interface GlobalHeaderRightProps extends ConnectProps { export interface GlobalHeaderRightProps extends Partial<ConnectProps> {
notices?: NoticeItem[]; notices?: NoticeItem[];
currentUser?: CurrentUser; currentUser?: CurrentUser;
fetchingNotices?: boolean; fetchingNotices?: boolean;
......
import { Tooltip, Tag } from 'antd'; import { Tooltip, Tag } from 'antd';
import { QuestionCircleOutlined } from '@ant-design/icons'; import { QuestionCircleOutlined } from '@ant-design/icons';
import React from 'react'; import React from 'react';
import { connect } from 'dva'; import { connect, ConnectProps } from 'umi';
import { ConnectProps, ConnectState } from '@/models/connect'; import { ConnectState } from '@/models/connect';
import Avatar from './AvatarDropdown'; import Avatar from './AvatarDropdown';
import HeaderSearch from '../HeaderSearch'; import HeaderSearch from '../HeaderSearch';
import SelectLang from '../SelectLang'; import SelectLang from '../SelectLang';
import styles from './index.less'; import styles from './index.less';
export type SiderTheme = 'light' | 'dark'; export type SiderTheme = 'light' | 'dark';
export interface GlobalHeaderRightProps extends ConnectProps { export interface GlobalHeaderRightProps extends Partial<ConnectProps> {
theme?: SiderTheme; theme?: SiderTheme;
layout: 'sidemenu' | 'topmenu'; layout: 'sidemenu' | 'topmenu';
} }
......
import { GlobalOutlined } from '@ant-design/icons'; import { GlobalOutlined } from '@ant-design/icons';
import { Menu } from 'antd'; import { Menu } from 'antd';
import { getLocale, setLocale } from 'umi-plugin-react/locale'; import { getLocale, setLocale } from 'umi';
import { ClickParam } from 'antd/es/menu'; import { ClickParam } from 'antd/es/menu';
import React from 'react'; import React from 'react';
import classNames from 'classnames'; import classNames from 'classnames';
......
...@@ -3,6 +3,8 @@ const RouterConfig = require('../../config/config').default.routes; ...@@ -3,6 +3,8 @@ const RouterConfig = require('../../config/config').default.routes;
const BASE_URL = `http://localhost:${process.env.PORT || 8000}`; const BASE_URL = `http://localhost:${process.env.PORT || 8000}`;
const getBrowser = require('./getBrowser');
function formatter(routes, parentPath = '') { function formatter(routes, parentPath = '') {
const fixedParentPath = parentPath.replace(/\/{1,}/g, '/'); const fixedParentPath = parentPath.replace(/\/{1,}/g, '/');
let result = []; let result = [];
...@@ -19,7 +21,15 @@ function formatter(routes, parentPath = '') { ...@@ -19,7 +21,15 @@ function formatter(routes, parentPath = '') {
return uniq(result.filter(item => !!item)); return uniq(result.filter(item => !!item));
} }
let browser;
let page;
beforeAll(async () => { beforeAll(async () => {
browser = await getBrowser();
});
beforeEach(async () => {
page = await browser.newPage();
await page.goto(`${BASE_URL}`); await page.goto(`${BASE_URL}`);
await page.evaluate(() => { await page.evaluate(() => {
localStorage.setItem('antd-pro-authority', '["admin"]'); localStorage.setItem('antd-pro-authority', '["admin"]');
...@@ -43,3 +53,7 @@ describe('Ant Design Pro E2E test', () => { ...@@ -43,3 +53,7 @@ describe('Ant Design Pro E2E test', () => {
it(`test pages ${route}`, testPage(route)); it(`test pages ${route}`, testPage(route));
}); });
}); });
afterAll(() => {
browser.close();
});
// ps https://github.com/GoogleChrome/puppeteer/issues/3120 import puppeteer from 'puppeteer';
module.exports = {
launch: { const getBrowser = async () => {
const browser = await puppeteer.launch({
args: [ args: [
'--disable-gpu', '--disable-gpu',
'--disable-dev-shm-usage', '--disable-dev-shm-usage',
...@@ -8,5 +9,8 @@ module.exports = { ...@@ -8,5 +9,8 @@ module.exports = {
'--no-zygote', '--no-zygote',
'--no-sandbox', '--no-sandbox',
], ],
}, });
return browser;
}; };
module.exports = getBrowser;
const getBrowser = require('./getBrowser');
const BASE_URL = `http://localhost:${process.env.PORT || 8000}`; const BASE_URL = `http://localhost:${process.env.PORT || 8000}`;
let browser;
let page;
beforeAll(async () => {
browser = await getBrowser();
});
beforeEach(async () => {
page = await browser.newPage();
await page.goto(`${BASE_URL}`);
await page.evaluate(() => {
localStorage.setItem('antd-pro-authority', '["admin"]');
});
});
describe('Homepage', () => { describe('Homepage', () => {
it('topmenu should have footer', async () => { it('topmenu should have footer', async () => {
const params = '?navTheme=light&layout=topmenu'; const params = '?navTheme=light&layout=topmenu';
...@@ -13,3 +30,7 @@ describe('Homepage', () => { ...@@ -13,3 +30,7 @@ describe('Homepage', () => {
expect(haveFooter).toBeTruthy(); expect(haveFooter).toBeTruthy();
}); });
}); });
afterAll(() => {
browser.close();
});
import { Button, message, notification } from 'antd'; import { Button, message, notification } from 'antd';
import React from 'react'; import React from 'react';
import { formatMessage } from 'umi-plugin-react/locale'; import { formatMessage } from 'umi';
import defaultSettings from '../config/defaultSettings'; import defaultSettings from '../config/defaultSettings';
const { pwa } = defaultSettings; const { pwa } = defaultSettings;
......
...@@ -9,11 +9,8 @@ import ProLayout, { ...@@ -9,11 +9,8 @@ import ProLayout, {
Settings, Settings,
DefaultFooter, DefaultFooter,
} from '@ant-design/pro-layout'; } from '@ant-design/pro-layout';
import { formatMessage } from 'umi-plugin-react/locale';
import React, { useEffect } from 'react'; import React, { useEffect } from 'react';
import { Link } from 'umi'; import { Link, useIntl, connect, Dispatch } from 'umi';
import { Dispatch } from 'redux';
import { connect } from 'dva';
import { GithubOutlined } from '@ant-design/icons'; import { GithubOutlined } from '@ant-design/icons';
import { Result, Button } from 'antd'; import { Result, Button } from 'antd';
import Authorized from '@/utils/Authorized'; import Authorized from '@/utils/Authorized';
...@@ -121,6 +118,8 @@ const BasicLayout: React.FC<BasicLayoutProps> = props => { ...@@ -121,6 +118,8 @@ const BasicLayout: React.FC<BasicLayoutProps> = props => {
const authorized = getAuthorityFromRouter(props.route.routes, location.pathname || '/') || { const authorized = getAuthorityFromRouter(props.route.routes, location.pathname || '/') || {
authority: undefined, authority: undefined,
}; };
const { formatMessage } = useIntl();
return ( return (
<ProLayout <ProLayout
logo={logo} logo={logo}
......
import React from 'react'; import React from 'react';
import { connect } from 'dva';
import { PageLoading } from '@ant-design/pro-layout'; import { PageLoading } from '@ant-design/pro-layout';
import { Redirect } from 'umi'; import { Redirect, connect, ConnectProps } from 'umi';
import { stringify } from 'querystring'; import { stringify } from 'querystring';
import { ConnectState, ConnectProps } from '@/models/connect'; import { ConnectState } from '@/models/connect';
import { CurrentUser } from '@/models/user'; import { CurrentUser } from '@/models/user';
interface SecurityLayoutProps extends ConnectProps { interface SecurityLayoutProps extends ConnectProps {
......
import { DefaultFooter, MenuDataItem, getMenuData, getPageTitle } from '@ant-design/pro-layout'; import { DefaultFooter, MenuDataItem, getMenuData, getPageTitle } from '@ant-design/pro-layout';
import { Helmet } from 'react-helmet'; import { Helmet } from 'react-helmet';
import { Link } from 'umi'; import { Link, useIntl, ConnectProps, connect } from 'umi';
import React from 'react'; import React from 'react';
import { formatMessage } from 'umi-plugin-react/locale';
import { connect } from 'dva';
import SelectLang from '@/components/SelectLang'; import SelectLang from '@/components/SelectLang';
import { ConnectProps, ConnectState } from '@/models/connect'; import { ConnectState } from '@/models/connect';
import logo from '../assets/logo.svg'; import logo from '../assets/logo.svg';
import styles from './UserLayout.less'; import styles from './UserLayout.less';
export interface UserLayoutProps extends ConnectProps { export interface UserLayoutProps extends Partial<ConnectProps> {
breadcrumbNameMap: { breadcrumbNameMap: {
[path: string]: MenuDataItem; [path: string]: MenuDataItem;
}; };
...@@ -28,6 +26,7 @@ const UserLayout: React.FC<UserLayoutProps> = props => { ...@@ -28,6 +26,7 @@ const UserLayout: React.FC<UserLayoutProps> = props => {
pathname: '', pathname: '',
}, },
} = props; } = props;
const { formatMessage } = useIntl();
const { breadcrumb } = getMenuData(routes); const { breadcrumb } = getMenuData(routes);
const title = getPageTitle({ const title = getPageTitle({
pathname: location.pathname, pathname: location.pathname,
......
import { AnyAction } from 'redux';
import { MenuDataItem } from '@ant-design/pro-layout'; import { MenuDataItem } from '@ant-design/pro-layout';
import { RouterTypes } from 'umi';
import { GlobalModelState } from './global'; import { GlobalModelState } from './global';
import { DefaultSettings as SettingModelState } from '../../config/defaultSettings'; import { DefaultSettings as SettingModelState } from '../../config/defaultSettings';
import { UserModelState } from './user'; import { UserModelState } from './user';
...@@ -31,10 +29,3 @@ export interface ConnectState { ...@@ -31,10 +29,3 @@ export interface ConnectState {
export interface Route extends MenuDataItem { export interface Route extends MenuDataItem {
routes?: Route[]; routes?: Route[];
} }
/**
* @type T: Params matched in dynamic routing
*/
export interface ConnectProps<T = {}> extends Partial<RouterTypes<Route, T>> {
dispatch?: Dispatch<AnyAction>;
}
import { Reducer } from 'redux'; import { Subscription, Reducer, Effect } from 'umi';
import { Subscription, Effect } from 'dva';
import { NoticeIconData } from '@/components/NoticeIcon'; import { NoticeIconData } from '@/components/NoticeIcon';
import { queryNotices } from '@/services/user'; import { queryNotices } from '@/services/user';
......
import { Reducer } from 'redux';
import { Effect } from 'dva';
import { stringify } from 'querystring'; import { stringify } from 'querystring';
import { router } from 'umi'; import { history, Reducer, Effect } from 'umi';
import { fakeAccountLogin } from '@/services/login'; import { fakeAccountLogin } from '@/services/login';
import { setAuthority } from '@/utils/authority'; import { setAuthority } from '@/utils/authority';
...@@ -56,7 +54,7 @@ const Model: LoginModelType = { ...@@ -56,7 +54,7 @@ const Model: LoginModelType = {
return; return;
} }
} }
router.replace(redirect || '/'); history.replace(redirect || '/');
} }
}, },
...@@ -64,7 +62,7 @@ const Model: LoginModelType = { ...@@ -64,7 +62,7 @@ const Model: LoginModelType = {
const { redirect } = getPageQuery(); const { redirect } = getPageQuery();
// Note: There may be security issues, please note // Note: There may be security issues, please note
if (window.location.pathname !== '/user/login' && !redirect) { if (window.location.pathname !== '/user/login' && !redirect) {
router.replace({ history.replace({
pathname: '/user/login', pathname: '/user/login',
search: stringify({ search: stringify({
redirect: window.location.href, redirect: window.location.href,
......
import { Reducer } from 'redux'; import { Reducer } from 'umi';
import defaultSettings, { DefaultSettings } from '../../config/defaultSettings'; import defaultSettings, { DefaultSettings } from '../../config/defaultSettings';
export interface SettingModelType { export interface SettingModelType {
......
import { Effect } from 'dva'; import { Effect, Reducer } from 'umi';
import { Reducer } from 'redux';
import { queryCurrent, query as queryUsers } from '@/services/user'; import { queryCurrent, query as queryUsers } from '@/services/user';
......
import { Button, Result } from 'antd'; import { Button, Result } from 'antd';
import React from 'react'; import React from 'react';
import { router } from 'umi'; import { history } from 'umi';
const NoFoundPage: React.FC<{}> = () => ( const NoFoundPage: React.FC<{}> = () => (
<Result <Result
...@@ -8,7 +8,7 @@ const NoFoundPage: React.FC<{}> = () => ( ...@@ -8,7 +8,7 @@ const NoFoundPage: React.FC<{}> = () => (
title="404" title="404"
subTitle="Sorry, the page you visited does not exist." subTitle="Sorry, the page you visited does not exist."
extra={ extra={
<Button type="primary" onClick={() => router.push('/')}> <Button type="primary" onClick={() => history.push('/')}>
Back Home Back Home
</Button> </Button>
} }
......
import React from 'react'; import React from 'react';
import { Redirect } from 'umi'; import { Redirect, connect, ConnectProps } from 'umi';
import { connect } from 'dva';
import Authorized from '@/utils/Authorized'; import Authorized from '@/utils/Authorized';
import { getRouteAuthority } from '@/utils/utils'; import { getRouteAuthority } from '@/utils/utils';
import { ConnectProps, ConnectState, UserModelState } from '@/models/connect'; import { ConnectState, UserModelState } from '@/models/connect';
interface AuthComponentProps extends ConnectProps { interface AuthComponentProps extends ConnectProps {
user: UserModelState; user: UserModelState;
......
...@@ -33,7 +33,7 @@ interface LoginType extends React.FC<LoginProps> { ...@@ -33,7 +33,7 @@ interface LoginType extends React.FC<LoginProps> {
const Login: LoginType = props => { const Login: LoginType = props => {
const { className } = props; const { className } = props;
const [tabs, setTabs] = useState<string[]>([]); const [tabs, setTabs] = useState<string[]>([]);
const [active, setActive] = useState(); const [active, setActive] = useState({});
const [type, setType] = useMergeValue('', { const [type, setType] = useMergeValue('', {
value: props.activeKey, value: props.activeKey,
onChange: props.onTabChange, onChange: props.onTabChange,
...@@ -65,6 +65,7 @@ const Login: LoginType = props => { ...@@ -65,6 +65,7 @@ const Login: LoginType = props => {
}, },
}, },
updateActive: activeItem => { updateActive: activeItem => {
if (!active) return;
if (active[type]) { if (active[type]) {
active[type].push(activeItem); active[type].push(activeItem);
} else { } else {
......
import { AlipayCircleOutlined, TaobaoCircleOutlined, WeiboCircleOutlined } from '@ant-design/icons'; import { AlipayCircleOutlined, TaobaoCircleOutlined, WeiboCircleOutlined } from '@ant-design/icons';
import { Alert, Checkbox } from 'antd'; import { Alert, Checkbox } from 'antd';
import React, { useState } from 'react'; import React, { useState } from 'react';
import { Dispatch, AnyAction } from 'redux'; import { Link, connect, Dispatch } from 'umi';
import { Link } from 'umi';
import { connect } from 'dva';
import { StateType } from '@/models/login'; import { StateType } from '@/models/login';
import { LoginParamsType } from '@/services/login'; import { LoginParamsType } from '@/services/login';
import { ConnectState } from '@/models/connect'; import { ConnectState } from '@/models/connect';
...@@ -13,7 +11,7 @@ import styles from './style.less'; ...@@ -13,7 +11,7 @@ import styles from './style.less';
const { Tab, UserName, Password, Mobile, Captcha, Submit } = LoginFrom; const { Tab, UserName, Password, Mobile, Captcha, Submit } = LoginFrom;
interface LoginProps { interface LoginProps {
dispatch: Dispatch<AnyAction>; dispatch: Dispatch;
userLogin: StateType; userLogin: StateType;
submitting?: boolean; submitting?: boolean;
} }
......
import { getAuthority } from './authority';
describe('getAuthority should be strong', () => {
it('string', () => {
expect(getAuthority('admin')).toEqual(['admin']);
});
it('array with double quotes', () => {
expect(getAuthority('"admin"')).toEqual(['admin']);
});
it('array with single item', () => {
expect(getAuthority('["admin"]')).toEqual(['admin']);
});
it('array with multiple items', () => {
expect(getAuthority('["admin", "guest"]')).toEqual(['admin', 'guest']);
});
});
...@@ -18,19 +18,9 @@ ...@@ -18,19 +18,9 @@
"experimentalDecorators": true, "experimentalDecorators": true,
"strict": true, "strict": true,
"paths": { "paths": {
"@/*": ["./src/*"] "@/*": ["./src/*"],
"@@/*": ["./src/.umi/*"]
} }
}, },
"exclude": [ "exclude": ["node_modules", "build", "dist", "scripts", "src/.umi/*", "webpack", "jest"]
"node_modules",
"build",
"dist",
"scripts",
"acceptance-tests",
"webpack",
"jest",
"src/setupTests.ts",
"tslint:latest",
"tslint-config-prettier"
]
} }
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册