소스 검색

✨ 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: chenshuai2144 <qixian.cs@outlook.com>
信鑫-King 5 년 전
부모
커밋
06dd7fa621

+ 3 - 2
.github/workflows/deploy.yml

@@ -11,11 +11,12 @@ jobs:
         uses: actions/checkout@master
       - run: yarn
       - run: yarn run lint
-      - run: yarn run tsc
+      # - run: yarn run tsc
       - name: Build and Deploy
         uses: JamesIves/github-pages-deploy-action@master
         env:
           CI: true
+          GA_KEY: UA-72788897-6
           PROGRESS: none
           GIT_CONFIG_NAME: qixian.cs
           GIT_CONFIG_EMAIL: qixian.cs@outlook.com
@@ -24,4 +25,4 @@ jobs:
           GITHUB_TOKEN: ${{ secrets.ACTION_TOKEN }}
           BRANCH: gh-pages
           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

+ 1 - 0
.prettierignore

@@ -19,3 +19,4 @@ LICENSE
 yarn-error.log
 .history
 CNAME
+/build

+ 49 - 104
config/config.ts

@@ -1,87 +1,32 @@
-import { IConfig, IPlugin } from 'umi-types';
-import defaultSettings from './defaultSettings'; // https://umijs.org/config/
-import slash from 'slash2';
-import themePluginConfig from './themePluginConfig';
+// https://umijs.org/config/
+import { defineConfig, utils } from 'umi';
+import defaultSettings from './defaultSettings';
 import proxy from './proxy';
 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 专用环境变量,请不要在你的项目中使用它。
-const { ANT_DESIGN_PRO_ONLY_DO_NOT_USE_IN_YOUR_PRODUCTION, REACT_APP_ENV } = 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,
-    },
-  ],
-];
+const { ANT_DESIGN_PRO_ONLY_DO_NOT_USE_IN_YOUR_PRODUCTION, REACT_APP_ENV, GA_KEY } = process.env;
 
-if (isAntDesignProPreview) {
-  // 针对 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,
+export default defineConfig({
   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: {
     ie: 11,
   },
@@ -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 专用环境变量,请不要在你的项目中使用它。
   },
   ignoreMomentLocale: true,
-  lessLoaderOptions: {
+  lessLoader: {
     javascriptEnabled: true,
   },
-  disableRedirectHoist: true,
-  cssLoaderOptions: {
-    modules: true,
-    getLocalIdent: (
-      context: {
-        resourcePath: string;
-      },
-      _: string,
-      localName: string,
-    ) => {
-      if (
-        context.resourcePath.includes('node_modules') ||
-        context.resourcePath.includes('ant.design.pro.less') ||
-        context.resourcePath.includes('global.less')
-      ) {
+  cssLoader: {
+    modules: {
+      getLocalIdent: (
+        context: {
+          resourcePath: string;
+        },
+        _: string,
+        localName: string,
+      ) => {
+        if (
+          context.resourcePath.includes('node_modules') ||
+          context.resourcePath.includes('ant.design.pro.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;
-      }
-      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: {
@@ -201,4 +146,4 @@ export default {
   },
   proxy: proxy[REACT_APP_ENV || 'dev'],
   chainWebpack: webpackPlugin,
-} as IConfig;
+});

+ 0 - 115
config/themePluginConfig.ts

@@ -1,115 +0,0 @@
-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',
-      },
-    },
-  ],
-};

+ 0 - 12
jest-puppeteer.config.js

@@ -1,12 +0,0 @@
-// ps https://github.com/GoogleChrome/puppeteer/issues/3120
-module.exports = {
-  launch: {
-    args: [
-      '--disable-gpu',
-      '--disable-dev-shm-usage',
-      '--no-first-run',
-      '--no-zygote',
-      '--no-sandbox',
-    ],
-  },
-};

+ 0 - 1
jest.config.js

@@ -1,6 +1,5 @@
 module.exports = {
   testURL: 'http://localhost:8000',
-  preset: 'jest-puppeteer',
   extraSetupFiles: ['./tests/setupTests.js'],
   globals: {
     ANT_DESIGN_PRO_ONLY_DO_NOT_USE_IN_YOUR_PRODUCTION: false,

+ 8 - 10
src/pages/ListTableList/_mock.ts

@@ -1,7 +1,7 @@
 // eslint-disable-next-line import/no-extraneous-dependencies
 import { Request, Response } from 'express';
 import { parse } from 'url';
-import { TableListItem, TableListParams } from './data.d';
+import { TableListItem, TableListParams } from '@/pages/ListTableList/data';
 
 // mock tableListDataSource
 const genList = (current: number, pageSize: number) => {
@@ -34,13 +34,12 @@ const genList = (current: number, pageSize: number) => {
 let tableListDataSource = genList(1, 100);
 
 function getRule(req: Request, res: Response, u: string) {
-  let url = u;
-  if (!url || Object.prototype.toString.call(url) !== '[object String]') {
-    // eslint-disable-next-line prefer-destructuring
-    url = req.url;
+  let realUrl = u;
+  if (!realUrl || Object.prototype.toString.call(realUrl) !== '[object String]') {
+    realUrl = req.url;
   }
   const { current = 1, pageSize = 10 } = req.query;
-  const params = (parse(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);
   if (params.sorter) {
@@ -84,10 +83,9 @@ function getRule(req: Request, res: Response, u: string) {
 }
 
 function postRule(req: Request, res: Response, u: string, b: Request) {
-  let url = u;
-  if (!url || Object.prototype.toString.call(url) !== '[object String]') {
-    // eslint-disable-next-line prefer-destructuring
-    url = req.url;
+  let realUrl = u;
+  if (!realUrl || Object.prototype.toString.call(realUrl) !== '[object String]') {
+    realUrl = req.url;
   }
 
   const body = (b && b.body) || req.body;

+ 11 - 23
package.json

@@ -14,10 +14,10 @@
     "docker:dev": "docker-compose -f ./docker/docker-compose.dev.yml up",
     "docker:push": "npm run docker-hub:build && npm run docker:tag && docker push antdesign/ant-design-pro",
     "docker:tag": "docker tag ant-design-pro antdesign/ant-design-pro",
-    "fetch:blocks": "pro fetch-blocks && npm run prettier",
+    "fetch:blocks": "pro fetch-blocks --branch=umi@3 && npm run prettier",
     "gh-pages": "cp CNAME ./dist/ && gh-pages -d dist",
     "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-staged": "lint-staged",
     "lint-staged:js": "eslint --ext .js,.jsx,.ts,.tsx ",
@@ -36,8 +36,7 @@
     "test": "umi test",
     "test:all": "node ./tests/run-tests.js",
     "test:component": "umi test ./src/components",
-    "tsc": "tsc",
-    "ui": "umi ui"
+    "tsc": "tsc"
   },
   "husky": {
     "hooks": {
@@ -57,28 +56,20 @@
     "not ie <= 10"
   ],
   "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-table": "^2.0.0",
-    "@antv/data-set": "^0.11.1",
-    "antd": "^4.0.2",
+    "antd": "^4.0.0",
     "classnames": "^2.2.6",
-    "dva": "^2.6.0-beta.16",
     "lodash": "^4.17.11",
     "moment": "^2.24.0",
     "omit.js": "^1.0.2",
     "path-to-regexp": "2.4.0",
     "qs": "^6.9.0",
     "react": "^16.8.6",
-    "react-copy-to-clipboard": "^5.0.1",
     "react-dom": "^16.8.6",
     "react-helmet": "^5.2.1",
-    "redux": "^4.0.1",
-    "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": "^3.0.0",
     "umi-request": "^1.0.8",
     "use-merge-value": "^1.0.1"
   },
@@ -94,6 +85,10 @@
     "@types/react-dom": "^16.8.4",
     "@types/react-helmet": "^5.0.13",
     "@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",
     "cross-env": "^7.0.0",
     "cross-port-killer": "^1.1.1",
@@ -101,19 +96,12 @@
     "express": "^4.17.1",
     "gh-pages": "^2.0.1",
     "husky": "^4.0.7",
-    "jest-puppeteer": "^4.2.0",
     "jsdom-global": "^3.0.2",
     "lint-staged": "^10.0.0",
     "mockjs": "^1.0.1-beta3",
-    "node-fetch": "^2.6.0",
     "prettier": "^1.19.1",
     "pro-download": "1.0.1",
-    "serverless-http": "^2.0.2",
-    "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"
+    "stylelint": "^13.0.0"
   },
   "optionalDependencies": {
     "puppeteer": "^2.0.0"

+ 1 - 1
src/components/Authorized/Authorized.tsx

@@ -21,7 +21,7 @@ const Authorized: React.FunctionComponent<AuthorizedProps> = ({
   authority,
   noMatch = (
     <Result
-      status={403}
+      status="403"
       title="403"
       subTitle="Sorry, you are not authorized to access this page."
     />

+ 4 - 5
src/components/GlobalHeader/AvatarDropdown.tsx

@@ -2,14 +2,13 @@ import { LogoutOutlined, SettingOutlined, UserOutlined } from '@ant-design/icons
 import { Avatar, Menu, Spin } from 'antd';
 import { ClickParam } from 'antd/es/menu';
 import React from 'react';
-import { connect } from 'dva';
-import { router } from 'umi';
-import { ConnectProps, ConnectState } from '@/models/connect';
+import { history, ConnectProps, connect } from 'umi';
+import { ConnectState } from '@/models/connect';
 import { CurrentUser } from '@/models/user';
 import HeaderDropdown from '../HeaderDropdown';
 import styles from './index.less';
 
-export interface GlobalHeaderRightProps extends ConnectProps {
+export interface GlobalHeaderRightProps extends Partial<ConnectProps> {
   currentUser?: CurrentUser;
   menu?: boolean;
 }
@@ -30,7 +29,7 @@ class AvatarDropdown extends React.Component<GlobalHeaderRightProps> {
       return;
     }
 
-    router.push(`/account/${key}`);
+    history.push(`/account/${key}`);
   };
 
   render(): React.ReactNode {

+ 3 - 3
src/components/GlobalHeader/NoticeIconView.tsx

@@ -1,15 +1,15 @@
 import React, { Component } from 'react';
+import { connect, ConnectProps } from 'umi';
 import { Tag, message } from 'antd';
-import { connect } from 'dva';
 import groupBy from 'lodash/groupBy';
 import moment from 'moment';
 import { NoticeItem } from '@/models/global';
 import { CurrentUser } from '@/models/user';
-import { ConnectProps, ConnectState } from '@/models/connect';
+import { ConnectState } from '@/models/connect';
 import NoticeIcon from '../NoticeIcon';
 import styles from './index.less';
 
-export interface GlobalHeaderRightProps extends ConnectProps {
+export interface GlobalHeaderRightProps extends Partial<ConnectProps> {
   notices?: NoticeItem[];
   currentUser?: CurrentUser;
   fetchingNotices?: boolean;

+ 3 - 3
src/components/GlobalHeader/RightContent.tsx

@@ -1,15 +1,15 @@
 import { Tooltip, Tag } from 'antd';
 import { QuestionCircleOutlined } from '@ant-design/icons';
 import React from 'react';
-import { connect } from 'dva';
-import { ConnectProps, ConnectState } from '@/models/connect';
+import { connect, ConnectProps } from 'umi';
+import { ConnectState } from '@/models/connect';
 import Avatar from './AvatarDropdown';
 import HeaderSearch from '../HeaderSearch';
 import SelectLang from '../SelectLang';
 import styles from './index.less';
 
 export type SiderTheme = 'light' | 'dark';
-export interface GlobalHeaderRightProps extends ConnectProps {
+export interface GlobalHeaderRightProps extends Partial<ConnectProps> {
   theme?: SiderTheme;
   layout: 'sidemenu' | 'topmenu';
 }

+ 1 - 1
src/components/SelectLang/index.tsx

@@ -1,6 +1,6 @@
 import { GlobalOutlined } from '@ant-design/icons';
 import { Menu } from 'antd';
-import { getLocale, setLocale } from 'umi-plugin-react/locale';
+import { getLocale, setLocale } from 'umi';
 import { ClickParam } from 'antd/es/menu';
 import React from 'react';
 import classNames from 'classnames';

+ 14 - 0
src/e2e/baseLayout.e2e.js

@@ -3,6 +3,8 @@ const RouterConfig = require('../../config/config').default.routes;
 
 const BASE_URL = `http://localhost:${process.env.PORT || 8000}`;
 
+const getBrowser = require('./getBrowser');
+
 function formatter(routes, parentPath = '') {
   const fixedParentPath = parentPath.replace(/\/{1,}/g, '/');
   let result = [];
@@ -19,7 +21,15 @@ function formatter(routes, parentPath = '') {
   return uniq(result.filter(item => !!item));
 }
 
+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"]');
@@ -43,3 +53,7 @@ describe('Ant Design Pro E2E test', () => {
     it(`test pages ${route}`, testPage(route));
   });
 });
+
+afterAll(() => {
+  browser.close();
+});

+ 16 - 0
src/e2e/getBrowser.js

@@ -0,0 +1,16 @@
+import puppeteer from 'puppeteer';
+
+const getBrowser = async () => {
+  const browser = await puppeteer.launch({
+    args: [
+      '--disable-gpu',
+      '--disable-dev-shm-usage',
+      '--no-first-run',
+      '--no-zygote',
+      '--no-sandbox',
+    ],
+  });
+  return browser;
+};
+
+module.exports = getBrowser;

+ 21 - 0
src/e2e/topMenu.e2e.js

@@ -1,5 +1,22 @@
+const getBrowser = require('./getBrowser');
+
 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', () => {
   it('topmenu should have footer', async () => {
     const params = '?navTheme=light&layout=topmenu';
@@ -13,3 +30,7 @@ describe('Homepage', () => {
     expect(haveFooter).toBeTruthy();
   });
 });
+
+afterAll(() => {
+  browser.close();
+});

+ 1 - 1
src/global.tsx

@@ -1,7 +1,7 @@
 import { Button, message, notification } from 'antd';
 
 import React from 'react';
-import { formatMessage } from 'umi-plugin-react/locale';
+import { formatMessage } from 'umi';
 import defaultSettings from '../config/defaultSettings';
 
 const { pwa } = defaultSettings;

+ 3 - 4
src/layouts/BasicLayout.tsx

@@ -9,11 +9,8 @@ import ProLayout, {
   Settings,
   DefaultFooter,
 } from '@ant-design/pro-layout';
-import { formatMessage } from 'umi-plugin-react/locale';
 import React, { useEffect } from 'react';
-import { Link } from 'umi';
-import { Dispatch } from 'redux';
-import { connect } from 'dva';
+import { Link, useIntl, connect, Dispatch } from 'umi';
 import { GithubOutlined } from '@ant-design/icons';
 import { Result, Button } from 'antd';
 import Authorized from '@/utils/Authorized';
@@ -121,6 +118,8 @@ const BasicLayout: React.FC<BasicLayoutProps> = props => {
   const authorized = getAuthorityFromRouter(props.route.routes, location.pathname || '/') || {
     authority: undefined,
   };
+  const { formatMessage } = useIntl();
+
   return (
     <ProLayout
       logo={logo}

+ 2 - 3
src/layouts/SecurityLayout.tsx

@@ -1,9 +1,8 @@
 import React from 'react';
-import { connect } from 'dva';
 import { PageLoading } from '@ant-design/pro-layout';
-import { Redirect } from 'umi';
+import { Redirect, connect, ConnectProps } from 'umi';
 import { stringify } from 'querystring';
-import { ConnectState, ConnectProps } from '@/models/connect';
+import { ConnectState } from '@/models/connect';
 import { CurrentUser } from '@/models/user';
 
 interface SecurityLayoutProps extends ConnectProps {

+ 4 - 5
src/layouts/UserLayout.tsx

@@ -1,15 +1,13 @@
 import { DefaultFooter, MenuDataItem, getMenuData, getPageTitle } from '@ant-design/pro-layout';
 import { Helmet } from 'react-helmet';
-import { Link } from 'umi';
+import { Link, useIntl, ConnectProps, connect } from 'umi';
 import React from 'react';
-import { formatMessage } from 'umi-plugin-react/locale';
-import { connect } from 'dva';
 import SelectLang from '@/components/SelectLang';
-import { ConnectProps, ConnectState } from '@/models/connect';
+import { ConnectState } from '@/models/connect';
 import logo from '../assets/logo.svg';
 import styles from './UserLayout.less';
 
-export interface UserLayoutProps extends ConnectProps {
+export interface UserLayoutProps extends Partial<ConnectProps> {
   breadcrumbNameMap: {
     [path: string]: MenuDataItem;
   };
@@ -28,6 +26,7 @@ const UserLayout: React.FC<UserLayoutProps> = props => {
       pathname: '',
     },
   } = props;
+  const { formatMessage } = useIntl();
   const { breadcrumb } = getMenuData(routes);
   const title = getPageTitle({
     pathname: location.pathname,

+ 0 - 9
src/models/connect.d.ts

@@ -1,6 +1,4 @@
-import { AnyAction } from 'redux';
 import { MenuDataItem } from '@ant-design/pro-layout';
-import { RouterTypes } from 'umi';
 import { GlobalModelState } from './global';
 import { DefaultSettings as SettingModelState } from '../../config/defaultSettings';
 import { UserModelState } from './user';
@@ -31,10 +29,3 @@ export interface ConnectState {
 export interface Route extends MenuDataItem {
   routes?: Route[];
 }
-
-/**
- * @type T: Params matched in dynamic routing
- */
-export interface ConnectProps<T = {}> extends Partial<RouterTypes<Route, T>> {
-  dispatch?: Dispatch<AnyAction>;
-}

+ 1 - 2
src/models/global.ts

@@ -1,5 +1,4 @@
-import { Reducer } from 'redux';
-import { Subscription, Effect } from 'dva';
+import { Subscription, Reducer, Effect } from 'umi';
 
 import { NoticeIconData } from '@/components/NoticeIcon';
 import { queryNotices } from '@/services/user';

+ 3 - 5
src/models/login.ts

@@ -1,7 +1,5 @@
-import { Reducer } from 'redux';
-import { Effect } from 'dva';
 import { stringify } from 'querystring';
-import { router } from 'umi';
+import { history, Reducer, Effect } from 'umi';
 
 import { fakeAccountLogin } from '@/services/login';
 import { setAuthority } from '@/utils/authority';
@@ -56,7 +54,7 @@ const Model: LoginModelType = {
             return;
           }
         }
-        router.replace(redirect || '/');
+        history.replace(redirect || '/');
       }
     },
 
@@ -64,7 +62,7 @@ const Model: LoginModelType = {
       const { redirect } = getPageQuery();
       // Note: There may be security issues, please note
       if (window.location.pathname !== '/user/login' && !redirect) {
-        router.replace({
+        history.replace({
           pathname: '/user/login',
           search: stringify({
             redirect: window.location.href,

+ 1 - 1
src/models/setting.ts

@@ -1,4 +1,4 @@
-import { Reducer } from 'redux';
+import { Reducer } from 'umi';
 import defaultSettings, { DefaultSettings } from '../../config/defaultSettings';
 
 export interface SettingModelType {

+ 1 - 2
src/models/user.ts

@@ -1,5 +1,4 @@
-import { Effect } from 'dva';
-import { Reducer } from 'redux';
+import { Effect, Reducer } from 'umi';
 
 import { queryCurrent, query as queryUsers } from '@/services/user';
 

+ 2 - 2
src/pages/404.tsx

@@ -1,6 +1,6 @@
 import { Button, Result } from 'antd';
 import React from 'react';
-import { router } from 'umi';
+import { history } from 'umi';
 
 const NoFoundPage: React.FC<{}> = () => (
   <Result
@@ -8,7 +8,7 @@ const NoFoundPage: React.FC<{}> = () => (
     title="404"
     subTitle="Sorry, the page you visited does not exist."
     extra={
-      <Button type="primary" onClick={() => router.push('/')}>
+      <Button type="primary" onClick={() => history.push('/')}>
         Back Home
       </Button>
     }

+ 2 - 3
src/pages/Authorized.tsx

@@ -1,9 +1,8 @@
 import React from 'react';
-import { Redirect } from 'umi';
-import { connect } from 'dva';
+import { Redirect, connect, ConnectProps } from 'umi';
 import Authorized from '@/utils/Authorized';
 import { getRouteAuthority } from '@/utils/utils';
-import { ConnectProps, ConnectState, UserModelState } from '@/models/connect';
+import { ConnectState, UserModelState } from '@/models/connect';
 
 interface AuthComponentProps extends ConnectProps {
   user: UserModelState;

+ 2 - 1
src/pages/user/login/components/Login/index.tsx

@@ -33,7 +33,7 @@ interface LoginType extends React.FC<LoginProps> {
 const Login: LoginType = props => {
   const { className } = props;
   const [tabs, setTabs] = useState<string[]>([]);
-  const [active, setActive] = useState();
+  const [active, setActive] = useState({});
   const [type, setType] = useMergeValue('', {
     value: props.activeKey,
     onChange: props.onTabChange,
@@ -65,6 +65,7 @@ const Login: LoginType = props => {
           },
         },
         updateActive: activeItem => {
+          if (!active) return;
           if (active[type]) {
             active[type].push(activeItem);
           } else {

+ 2 - 4
src/pages/user/login/index.tsx

@@ -1,9 +1,7 @@
 import { AlipayCircleOutlined, TaobaoCircleOutlined, WeiboCircleOutlined } from '@ant-design/icons';
 import { Alert, Checkbox } from 'antd';
 import React, { useState } from 'react';
-import { Dispatch, AnyAction } from 'redux';
-import { Link } from 'umi';
-import { connect } from 'dva';
+import { Link, connect, Dispatch } from 'umi';
 import { StateType } from '@/models/login';
 import { LoginParamsType } from '@/services/login';
 import { ConnectState } from '@/models/connect';
@@ -13,7 +11,7 @@ import styles from './style.less';
 
 const { Tab, UserName, Password, Mobile, Captcha, Submit } = LoginFrom;
 interface LoginProps {
-  dispatch: Dispatch<AnyAction>;
+  dispatch: Dispatch;
   userLogin: StateType;
   submitting?: boolean;
 }

+ 0 - 16
src/utils/authority.test.ts

@@ -1,16 +0,0 @@
-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']);
-  });
-});

+ 3 - 13
tsconfig.json

@@ -18,19 +18,9 @@
     "experimentalDecorators": true,
     "strict": true,
     "paths": {
-      "@/*": ["./src/*"]
+      "@/*": ["./src/*"],
+      "@@/*": ["./src/.umi/*"]
     }
   },
-  "exclude": [
-    "node_modules",
-    "build",
-    "dist",
-    "scripts",
-    "acceptance-tests",
-    "webpack",
-    "jest",
-    "src/setupTests.ts",
-    "tslint:latest",
-    "tslint-config-prettier"
-  ]
+  "exclude": ["node_modules", "build", "dist", "scripts", "src/.umi/*", "webpack", "jest"]
 }