Przeglądaj źródła

fix ts file error for eslint (#4336)

* fix ts file error for eslint

* add format-imports script

* sort all code

* remove lint:ts

* remove tslint

* remove lint:ts

* up layout

* remove a empty test

* rm tslint config

* use umi config

* prettier all code

* use @umijs/fabric

* [CodeFactor] Apply fixes to commit 69d4f9a

[ci skip] [skip ci]
陈帅 6 lat temu
rodzic
commit
277ea60169
53 zmienionych plików z 435 dodań i 557 usunięć
  1. 6 36
      .eslintrc.js
  2. 2 1
      .gitignore
  3. 4 1
      .prettierignore
  4. 0 20
      .prettierrc
  5. 5 0
      .prettierrc.js
  6. 5 0
      .stylelintrc.js
  7. 0 13
      .stylelintrc.json
  8. 4 2
      config/config.ts
  9. 4 5
      config/plugin.config.ts
  10. 18 35
      package.json
  11. 8 9
      src/components/Authorized/Authorized.tsx
  12. 4 3
      src/components/Authorized/AuthorizedRoute.tsx
  13. 2 2
      src/components/Authorized/CheckPermissions.tsx
  14. 16 17
      src/components/Authorized/PromiseRender.tsx
  15. 6 8
      src/components/Authorized/Secured.tsx
  16. 4 1
      src/components/Authorized/renderAuthorize.ts
  17. 15 11
      src/components/CopyBlock/index.tsx
  18. 6 5
      src/components/GlobalHeader/AvatarDropdown.tsx
  19. 39 34
      src/components/GlobalHeader/NoticeIconView.tsx
  20. 54 55
      src/components/GlobalHeader/RightContent.tsx
  21. 2 2
      src/components/HeaderDropdown/index.tsx
  22. 18 13
      src/components/HeaderSearch/index.tsx
  23. 1 1
      src/components/NoticeIcon/NoticeList.less
  24. 6 6
      src/components/NoticeIcon/NoticeList.tsx
  25. 43 39
      src/components/NoticeIcon/index.tsx
  26. 4 4
      src/components/SelectLang/index.tsx
  27. 12 9
      src/components/SettingDrawer/themeColorClient.js
  28. 0 2
      src/e2e/baseLayout.e2e.js
  29. 11 5
      src/global.tsx
  30. 33 31
      src/layouts/BasicLayout.tsx
  31. 1 1
      src/layouts/UserLayout.less
  32. 5 5
      src/layouts/UserLayout.tsx
  33. 2 2
      src/locales/en-US.ts
  34. 2 2
      src/locales/pt-BR.ts
  35. 2 2
      src/locales/zh-CN.ts
  36. 2 2
      src/locales/zh-TW.ts
  37. 20 21
      src/models/connect.d.ts
  38. 12 11
      src/models/global.ts
  39. 5 4
      src/models/login.ts
  40. 6 3
      src/models/setting.ts
  41. 2 2
      src/models/user.ts
  42. 8 6
      src/pages/Authorized.tsx
  43. 1 1
      src/pages/Welcome.tsx
  44. 3 2
      src/service-worker.js
  45. 3 1
      src/typings.d.ts
  46. 5 4
      src/utils/Authorized.ts
  47. 0 4
      src/utils/authority.test.ts
  48. 6 3
      src/utils/authority.ts
  49. 10 14
      src/utils/request.ts
  50. 4 5
      src/utils/utils.test.ts
  51. 1 3
      src/utils/utils.ts
  52. 3 1
      tests/run-tests.js
  53. 0 88
      tslint.yml

+ 6 - 36
.eslintrc.js

@@ -1,42 +1,12 @@
+const fabric = require('@umijs/fabric');
+
 module.exports = {
-  parser: 'babel-eslint',
-  extends: ['airbnb', 'prettier', 'plugin:compat/recommended'],
-  env: {
-    browser: true,
-    node: true,
-    es6: true,
-    mocha: true,
-    jest: true,
-    jasmine: true,
+  ...fabric.default,
+  rules: {
+    ...fabric.default.rules,
   },
   globals: {
-    page: true,
     ANT_DESIGN_PRO_ONLY_DO_NOT_USE_IN_YOUR_PRODUCTION: true,
-  },
-  rules: {
-    'react/jsx-filename-extension': [1, { extensions: ['.js'] }],
-    'react/jsx-wrap-multilines': 0,
-    'react/prop-types': 0,
-    'react/forbid-prop-types': 0,
-    'react/jsx-one-expression-per-line': 0,
-    'import/no-unresolved': [2, { ignore: ['^@/', '^umi/'] }],
-    'import/no-extraneous-dependencies': [
-      2,
-      {
-        optionalDependencies: true,
-        devDependencies: ['**/tests/**.js', '/mock/**/**.js', '**/**.test.js'],
-      },
-    ],
-    'import/no-cycle': 0,
-    'jsx-a11y/no-noninteractive-element-interactions': 0,
-    'jsx-a11y/click-events-have-key-events': 0,
-    'jsx-a11y/no-static-element-interactions': 0,
-    'jsx-a11y/anchor-is-valid': 0,
-    'linebreak-style': 0,
-  },
-  settings: {
-    // support import modules from TypeScript files in JavaScript files
-    'import/resolver': { node: { extensions: ['.js', '.ts', '.tsx'] } },
-    polyfills: ['fetch', 'promises', 'url', 'object-assign'],
+    page: true,
   },
 };

+ 2 - 1
.gitignore

@@ -34,4 +34,5 @@ functions/*
 
 # screenshot
 screenshot
-.firebase
+.firebase
+.eslintcache

+ 4 - 1
.prettierignore

@@ -13,4 +13,7 @@ docker
 Dockerfile*
 .gitignore
 .prettierignore
-LICENSE
+LICENSE
+.eslintcache
+*.lock
+yarn-error.log

+ 0 - 20
.prettierrc

@@ -1,20 +0,0 @@
-{
-  "singleQuote": true,
-  "trailingComma": "all",
-  "printWidth": 100,
-  "proseWrap": "never",
-  "overrides": [
-    {
-      "files": ".prettierrc",
-      "options": {
-        "parser": "json"
-      }
-    },
-    {
-      "files": "document.ejs",
-      "options": {
-        "parser": "html"
-      }
-    }
-  ]
-}

+ 5 - 0
.prettierrc.js

@@ -0,0 +1,5 @@
+const fabric = require('@umijs/fabric');
+
+module.exports = {
+  ...fabric.prettier,
+};

+ 5 - 0
.stylelintrc.js

@@ -0,0 +1,5 @@
+const fabric = require('@umijs/fabric');
+
+module.exports = {
+  ...fabric.stylelint,
+};

+ 0 - 13
.stylelintrc.json

@@ -1,13 +0,0 @@
-{
-  "extends": [
-    "stylelint-config-standard",
-    "stylelint-config-css-modules",
-    "stylelint-config-rational-order",
-    "stylelint-config-prettier"
-  ],
-  "plugins": ["stylelint-order", "stylelint-declaration-block-no-ignored-properties"],
-  "rules": {
-    "no-descending-specificity": null,
-    "plugin/declaration-block-no-ignored-properties": true
-  }
-}

+ 4 - 2
config/config.ts

@@ -1,9 +1,11 @@
+import { IConfig, IPlugin } from 'umi-types';
+
+import defaultSettings from './defaultSettings';
 // https://umijs.org/config/
 import os from 'os';
 import slash from 'slash2';
-import { IPlugin, IConfig } from 'umi-types';
-import defaultSettings from './defaultSettings';
 import webpackPlugin from './plugin.config';
+
 const { pwa, primaryColor } = defaultSettings;
 
 // preview.pro.ant.design only do not use in your production ;

+ 4 - 5
config/plugin.config.ts

@@ -1,10 +1,9 @@
 // Change theme plugin
-
-// import MergeLessPlugin from 'antd-pro-merge-less';
-// import AntDesignThemePlugin from 'antd-theme-webpack-plugin';
+// eslint-disable-next-line eslint-comments/abdeils - enable - pair;
+/* eslint-disable import/no-extraneous-dependencies */
 import ThemeColorReplacer from 'webpack-theme-color-replacer';
-import path from 'path';
 import generate from '@ant-design/colors/lib/generate';
+import path from 'path';
 
 function getModulePackageName(module: { context: string }) {
   if (!module.context) return null;
@@ -107,7 +106,7 @@ const getAntdSerials = (color: string) => {
   const lightNum = 9;
   const devide10 = 10;
   // 淡化(即less的tint)
-  const lightens = new Array(lightNum).fill().map((t, i) => {
+  const lightens = new Array(lightNum).fill(undefined).map((_, i: number) => {
     return ThemeColorReplacer.varyColor.lighten(color, i / devide10);
   });
   const colorPalettes = generate(color);

+ 18 - 35
package.json

@@ -14,25 +14,23 @@
     "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": "node ./scripts/fetch-blocks.js",
+    "format-imports": "import-sort --write '**/*.{js,jsx,ts,tsx}'",
     "functions:build": "netlify-lambda build ./lambda",
     "functions:run": "cross-env NODE_ENV=dev netlify-lambda serve ./lambda",
-    "lint": "npm run lint:js && npm run lint:ts && npm run lint:style && npm run lint:prettier",
+    "lint": "npm run lint:js && npm run lint:style && npm run lint:prettier",
     "lint-staged": "lint-staged",
-    "lint-staged:js": "eslint --ext .js",
-    "lint-staged:ts": "tslint",
-    "lint:fix": "eslint --fix --ext .js src tests && npm run lint:style && npm run tslint:fix",
-    "lint:js": "eslint --ext .js src tests",
+    "lint-staged:js": "eslint --ext .js,.jsx,.ts,.tsx ",
+    "lint:fix": "eslint --fix --cache --ext .js,.jsx,.ts,.tsx --format=pretty ./src && npm run lint:style",
+    "lint:js": "eslint --cache --ext .js,.jsx,.ts,.tsx --format=pretty ./src",
     "lint:prettier": "check-prettier lint",
     "lint:style": "stylelint --fix \"src/**/*.less\" --syntax less",
-    "lint:ts": "tslint -p . -c tslint.yml",
-    "prettier": "prettier -c --write '**/*'",
+    "prettier": "prettier -c --write **/*",
     "site": "npm run fetch:blocks && npm run functions:build && umi build",
     "start": "umi dev",
     "start:no-mock": "cross-env MOCK=none umi dev",
     "test": "umi test",
     "test:all": "node ./tests/run-tests.js",
-    "test:component": "umi test ./src/components",
-    "tslint:fix": "tslint --fix \"src/**/*.ts*\""
+    "test:component": "umi test ./src/components"
   },
   "husky": {
     "hooks": {
@@ -41,12 +39,12 @@
   },
   "lint-staged": {
     "**/*.less": "stylelint --syntax less",
-    "**/*.{js,jsx}": "npm run lint-staged:js",
     "**/*.{js,jsx,tsx,ts,less,md,json}": [
       "prettier --write",
       "git add"
     ],
-    "**/*.{ts,tsx}": "npm run lint-staged:ts"
+    "**/*.{js,jsx}": "npm run lint-staged:js",
+    "**/*.{js,ts,tsx}": "npm run lint-staged:js"
   },
   "browserslist": [
     "> 1%",
@@ -54,7 +52,7 @@
     "not ie <= 10"
   ],
   "dependencies": {
-    "@ant-design/pro-layout": "^4.4.2",
+    "@ant-design/pro-layout": "^4.5.0",
     "@antv/data-set": "^0.10.2",
     "antd": "^3.19.1",
     "classnames": "^2.2.6",
@@ -75,6 +73,7 @@
     "react-dom": "^16.8.6",
     "react-media": "^1.9.2",
     "react-media-hook2": "^1.0.5",
+    "redux": "^4.0.1",
     "umi": "^2.7.2",
     "umi-plugin-ga": "^1.1.3",
     "umi-plugin-pro-block": "^1.3.2",
@@ -82,6 +81,7 @@
     "umi-request": "^1.0.7"
   },
   "devDependencies": {
+    "@ant-design/colors": "^3.1.0",
     "@types/classnames": "^2.2.7",
     "@types/history": "^4.7.2",
     "@types/jest": "^24.0.13",
@@ -90,8 +90,7 @@
     "@types/react": "^16.8.19",
     "@types/react-document-title": "^2.0.3",
     "@types/react-dom": "^16.8.4",
-    "antd-pro-merge-less": "^1.0.0",
-    "@ant-design/colors": "^3.1.0",
+    "@umijs/fabric": "^1.0.4",
     "babel-eslint": "^10.0.1",
     "chalk": "^2.4.2",
     "check-prettier": "^1.0.3",
@@ -99,20 +98,15 @@
     "cross-port-killer": "^1.1.1",
     "enzyme": "^3.9.0",
     "eslint": "^5.16.0",
-    "eslint-config-airbnb": "^17.1.0",
-    "eslint-config-prettier": "^4.3.0",
-    "eslint-plugin-babel": "^5.3.0",
-    "eslint-plugin-compat": "^3.1.1",
-    "eslint-plugin-import": "^2.17.3",
-    "eslint-plugin-jsx-a11y": "^6.2.1",
-    "eslint-plugin-markdown": "^1.0.0",
-    "eslint-plugin-react": "^7.13.0",
     "express": "^4.17.1",
     "gh-pages": "^2.0.1",
     "husky": "^2.3.0",
+    "import-sort-cli": "^6.0.0",
+    "import-sort-parser-babylon": "^6.0.0",
+    "import-sort-parser-typescript": "^6.0.0",
+    "import-sort-style-module": "^6.0.0",
     "jest-puppeteer": "^4.2.0",
     "jsdom-global": "^3.0.2",
-    "less": "^3.9.0",
     "lint-staged": "^8.1.7",
     "mockjs": "^1.0.1-beta3",
     "netlify-lambda": "^1.4.13",
@@ -120,17 +114,6 @@
     "prettier": "^1.17.1",
     "serverless-http": "^2.0.2",
     "slash2": "^2.0.0",
-    "stylelint": "^10.0.1",
-    "stylelint-config-css-modules": "^1.4.0",
-    "stylelint-config-prettier": "^5.2.0",
-    "stylelint-config-rational-order": "^0.1.2",
-    "stylelint-config-standard": "^18.3.0",
-    "stylelint-declaration-block-no-ignored-properties": "^2.1.0",
-    "stylelint-order": "^3.0.0",
-    "tslint": "^5.17.0",
-    "tslint-config-prettier": "^1.18.0",
-    "tslint-eslint-rules": "^5.4.0",
-    "tslint-react": "^4.0.0",
     "webpack-theme-color-replacer": "^1.1.5"
   },
   "optionalDependencies": {
@@ -174,4 +157,4 @@
       "create-umi"
     ]
   }
-}
+}

+ 8 - 9
src/components/Authorized/Authorized.tsx

@@ -1,28 +1,27 @@
-import CheckPermissions from './CheckPermissions';
-import { IAuthorityType } from './CheckPermissions';
-import Secured from './Secured';
-import check from './CheckPermissions';
-import AuthorizedRoute from './AuthorizedRoute';
 import React from 'react';
+import check, { IAuthorityType } from './CheckPermissions';
+
+import AuthorizedRoute from './AuthorizedRoute';
+import Secured from './Secured';
 
-interface IAuthorizedProps {
+interface AuthorizedProps {
   authority: IAuthorityType;
   noMatch?: React.ReactNode;
 }
 
-type IAuthorizedType = React.FunctionComponent<IAuthorizedProps> & {
+type IAuthorizedType = React.FunctionComponent<AuthorizedProps> & {
   Secured: typeof Secured;
   check: typeof check;
   AuthorizedRoute: typeof AuthorizedRoute;
 };
 
-const Authorized: React.FunctionComponent<IAuthorizedProps> = ({
+const Authorized: React.FunctionComponent<AuthorizedProps> = ({
   children,
   authority,
   noMatch = null,
 }) => {
   const childrenRender: React.ReactNode = typeof children === 'undefined' ? null : children;
-  const dom = CheckPermissions(authority, childrenRender, noMatch);
+  const dom = check(authority, childrenRender, noMatch);
   return <>{dom}</>;
 };
 

+ 4 - 3
src/components/Authorized/AuthorizedRoute.tsx

@@ -1,9 +1,10 @@
+import { Redirect, Route } from 'umi';
+
 import React from 'react';
-import { Route, Redirect } from 'umi';
 import Authorized from './Authorized';
 import { IAuthorityType } from './CheckPermissions';
 
-interface IAuthorizedRoutePops {
+interface AuthorizedRoutePops {
   currentAuthority: string;
   component: React.ComponentClass<any, any>;
   render: (props: any) => React.ReactNode;
@@ -11,7 +12,7 @@ interface IAuthorizedRoutePops {
   authority: IAuthorityType;
 }
 
-const AuthorizedRoute: React.SFC<IAuthorizedRoutePops> = ({
+const AuthorizedRoute: React.SFC<AuthorizedRoutePops> = ({
   component: Component,
   render,
   authority,

+ 2 - 2
src/components/Authorized/CheckPermissions.tsx

@@ -1,13 +1,13 @@
 import React from 'react';
+import { CURRENT } from './renderAuthorize';
 // eslint-disable-next-line import/no-cycle
 import PromiseRender from './PromiseRender';
-import { CURRENT } from './renderAuthorize';
 
 export type IAuthorityType =
   | undefined
   | string
   | string[]
-  | Promise<any>
+  | Promise<boolean>
   | ((currentAuthority: string | string[]) => IAuthorityType);
 
 /**

+ 16 - 17
src/components/Authorized/PromiseRender.tsx

@@ -1,24 +1,24 @@
+import React from 'react';
 import { Spin } from 'antd';
 import isEqual from 'lodash/isEqual';
-import React from 'react';
-// eslint-disable-next-line import/no-cycle
 import { isComponentClass } from './Secured';
+// eslint-disable-next-line import/no-cycle
 
-interface IPromiseRenderProps<T, K> {
+interface PromiseRenderProps<T, K> {
   ok: T;
   error: K;
-  promise: Promise<any>;
+  promise: Promise<boolean>;
 }
 
-interface IPromiseRenderState {
-  component: React.ComponentClass<any, any> | React.FunctionComponent<any>;
+interface PromiseRenderState {
+  component: React.ComponentClass | React.FunctionComponent;
 }
 
 export default class PromiseRender<T, K> extends React.Component<
-  IPromiseRenderProps<T, K>,
-  IPromiseRenderState
+  PromiseRenderProps<T, K>,
+  PromiseRenderState
 > {
-  state: IPromiseRenderState = {
+  state: PromiseRenderState = {
     component: () => null,
   };
 
@@ -26,10 +26,7 @@ export default class PromiseRender<T, K> extends React.Component<
     this.setRenderComponent(this.props);
   }
 
-  shouldComponentUpdate = (
-    nextProps: IPromiseRenderProps<T, K>,
-    nextState: IPromiseRenderState,
-  ) => {
+  shouldComponentUpdate = (nextProps: PromiseRenderProps<T, K>, nextState: PromiseRenderState) => {
     const { component } = this.state;
     if (!isEqual(nextProps, this.props)) {
       this.setRenderComponent(nextProps);
@@ -39,7 +36,7 @@ export default class PromiseRender<T, K> extends React.Component<
   };
 
   // set render Component : ok or error
-  setRenderComponent(props: IPromiseRenderProps<T, K>) {
+  setRenderComponent(props: PromiseRenderProps<T, K>) {
     const ok = this.checkIsInstantiation(props.ok);
     const error = this.checkIsInstantiation(props.error);
     props.promise
@@ -47,6 +44,7 @@ export default class PromiseRender<T, K> extends React.Component<
         this.setState({
           component: ok,
         });
+        return true;
       })
       .catch(() => {
         this.setState({
@@ -60,10 +58,10 @@ export default class PromiseRender<T, K> extends React.Component<
   // Authorized  render is already instantiated, children is no instantiated
   // Secured is not instantiated
   checkIsInstantiation = (
-    target: React.ReactNode | React.ComponentClass<any, any>,
-  ): React.FunctionComponent<any> => {
+    target: React.ReactNode | React.ComponentClass,
+  ): React.FunctionComponent => {
     if (isComponentClass(target)) {
-      const Target = target as React.ComponentClass<any, any>;
+      const Target = target as React.ComponentClass;
       return (props: any) => <Target {...props} />;
     }
     if (React.isValidElement(target)) {
@@ -75,6 +73,7 @@ export default class PromiseRender<T, K> extends React.Component<
   render() {
     const { component: Component } = this.state;
     const { ok, error, promise, ...rest } = this.props;
+
     return Component ? (
       <Component {...rest} />
     ) : (

+ 6 - 8
src/components/Authorized/Secured.tsx

@@ -7,9 +7,7 @@ import CheckPermissions from './CheckPermissions';
  */
 const Exception403 = () => 403;
 
-export const isComponentClass = (
-  component: React.ComponentClass<any, any> | React.ReactNode,
-): boolean => {
+export const isComponentClass = (component: React.ComponentClass | React.ReactNode): boolean => {
   if (!component) return false;
   const proto = Object.getPrototypeOf(component);
   if (proto === React.Component || proto === Function.prototype) return true;
@@ -20,9 +18,9 @@ export const isComponentClass = (
 // AuthorizedRoute is already instantiated
 // Authorized  render is already instantiated, children is no instantiated
 // Secured is not instantiated
-const checkIsInstantiation = (target: React.ComponentClass<any, any> | React.ReactNode) => {
+const checkIsInstantiation = (target: React.ComponentClass | React.ReactNode) => {
   if (isComponentClass(target)) {
-    const Target = target as React.ComponentClass<any, any>;
+    const Target = target as React.ComponentClass;
     return (props: any) => <Target {...props} />;
   }
   if (React.isValidElement(target)) {
@@ -52,14 +50,14 @@ const authorize = (authority: string, error?: React.ReactNode) => {
    * 防止传入字符串时找不到staticContext造成报错
    * String parameters can cause staticContext not found error
    */
-  let classError: boolean | React.FunctionComponent<any> = false;
+  let classError: boolean | React.FunctionComponent = false;
   if (error) {
-    classError = (() => error) as React.FunctionComponent<any>;
+    classError = (() => error) as React.FunctionComponent;
   }
   if (!authority) {
     throw new Error('authority is required');
   }
-  return function decideAuthority(target: React.ComponentClass<any, any> | React.ReactNode) {
+  return function decideAuthority(target: React.ComponentClass | React.ReactNode) {
     const component = CheckPermissions(authority, target, classError || Exception403);
     return checkIsInstantiation(component);
   };

+ 4 - 1
src/components/Authorized/renderAuthorize.ts

@@ -1,4 +1,7 @@
+/* eslint-disable eslint-comments/disable-enable-pair */
+/* eslint-disable import/no-mutable-exports */
 let CURRENT: string | string[] = 'NULL';
+
 type CurrentAuthorityType = string | string[] | (() => typeof CURRENT);
 /**
  * use  authority or getAuthority
@@ -6,7 +9,7 @@ type CurrentAuthorityType = string | string[] | (() => typeof CURRENT);
  */
 const renderAuthorize = <T>(Authorized: T): ((currentAuthority: CurrentAuthorityType) => T) => (
   currentAuthority: CurrentAuthorityType,
-) => {
+): T => {
   if (currentAuthority) {
     if (typeof currentAuthority === 'function') {
       CURRENT = currentAuthority();

+ 15 - 11
src/components/CopyBlock/index.tsx

@@ -1,25 +1,25 @@
+import { Icon, Popover, Typography } from 'antd';
+
+import { FormattedMessage } from 'umi-plugin-react/locale';
 import React from 'react';
-import { Icon, Typography, Popover } from 'antd';
-import styles from './index.less';
 import { connect } from 'dva';
-import * as H from 'history';
-import { FormattedMessage } from 'umi-plugin-react/locale';
 import { isAntDesignPro } from '@/utils/utils';
+import styles from './index.less';
 
-const firstUpperCase = (pathString: string) => {
-  return pathString
+const firstUpperCase = (pathString: string): string =>
+  pathString
     .replace('.', '')
-    .split(/\/|\-/)
-    .map(s => s.toLowerCase().replace(/( |^)[a-z]/g, L => L.toUpperCase()))
-    .filter(s => s)
+    .split(/\/|-/)
+    .map((s): string => s.toLowerCase().replace(/( |^)[a-z]/g, L => L.toUpperCase()))
+    .filter((s): boolean => !!s)
     .join('');
-};
 
 // when  click block copy, send block url to  ga
 const onBlockCopy = (label: string) => {
   if (!isAntDesignPro()) {
     return;
   }
+
   const ga = window && (window as any).ga;
   if (ga) {
     ga('send', 'event', {
@@ -48,7 +48,11 @@ const BlockCodeView: React.SFC<{
   );
 };
 
-type RoutingType = { location: H.Location };
+interface RoutingType {
+  location: {
+    pathname: string;
+  };
+}
 
 export default connect(({ routing }: { routing: RoutingType }) => ({
   location: routing.location,

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

@@ -1,9 +1,9 @@
-import React from 'react';
-import { Avatar, Menu, Spin, Icon } from 'antd';
-import { FormattedMessage } from 'umi-plugin-react/locale';
-import { ClickParam } from 'antd/es/menu';
+import { Avatar, Icon, Menu, Spin } from 'antd';
 import { ConnectProps, ConnectState } from '@/models/connect';
+import { ClickParam } from 'antd/es/menu';
 import { CurrentUser } from '@/models/user';
+import { FormattedMessage } from 'umi-plugin-react/locale';
+import React from 'react';
 import { connect } from 'dva';
 import router from 'umi/router';
 import HeaderDropdown from '../HeaderDropdown';
@@ -30,7 +30,8 @@ class AvatarDropdown extends React.Component<GlobalHeaderRightProps> {
     }
     router.push(`/account/${key}`);
   };
-  render() {
+
+  render(): React.ReactNode {
     const { currentUser = {}, menu } = this.props;
     if (!menu) {
       return (

+ 39 - 34
src/components/GlobalHeader/NoticeIconView.tsx

@@ -1,14 +1,15 @@
 import { ConnectProps, ConnectState } from '@/models/connect';
-import { NoticeItem } from '@/models/global';
-import { CurrentUser } from '@/models/user';
 import React, { Component } from 'react';
 import { Tag, message } from 'antd';
+
+import { CurrentUser } from '@/models/user';
+import NoticeIcon from '../NoticeIcon';
+import { NoticeItem } from '@/models/global';
+import { connect } from 'dva';
 import { formatMessage } from 'umi-plugin-react/locale';
-import moment from 'moment';
 import groupBy from 'lodash/groupBy';
-import NoticeIcon from '../NoticeIcon';
+import moment from 'moment';
 import styles from './index.less';
-import { connect } from 'dva';
 
 export interface GlobalHeaderRightProps extends ConnectProps {
   notices?: NoticeItem[];
@@ -19,6 +20,37 @@ export interface GlobalHeaderRightProps extends ConnectProps {
 }
 
 class GlobalHeaderRight extends Component<GlobalHeaderRightProps> {
+  componentDidMount() {
+    const { dispatch } = this.props;
+    if (dispatch) {
+      dispatch({
+        type: 'global/fetchNotices',
+      });
+    }
+  }
+
+  changeReadState = (clickedItem: NoticeItem): void => {
+    const { id } = clickedItem;
+    const { dispatch } = this.props;
+    if (dispatch) {
+      dispatch({
+        type: 'global/changeNoticeReadState',
+        payload: id,
+      });
+    }
+  };
+
+  handleNoticeClear = (title: string, key: string) => {
+    const { dispatch } = this.props;
+    message.success(`${formatMessage({ id: 'component.noticeIcon.cleared' })} ${title}`);
+    if (dispatch) {
+      dispatch({
+        type: 'global/clearNotices',
+        payload: key,
+      });
+    }
+  };
+
   getNoticeData = (): { [key: string]: NoticeItem[] } => {
     const { notices = [] } = this.props;
     if (notices.length === 0) {
@@ -52,7 +84,8 @@ class GlobalHeaderRight extends Component<GlobalHeaderRightProps> {
 
   getUnreadData = (noticeData: { [key: string]: NoticeItem[] }) => {
     const unreadMsg: { [key: string]: number } = {};
-    Object.entries(noticeData).forEach(([key, value]) => {
+    Object.keys(noticeData).forEach(key => {
+      const value = noticeData[key];
       if (!unreadMsg[key]) {
         unreadMsg[key] = 0;
       }
@@ -63,34 +96,6 @@ class GlobalHeaderRight extends Component<GlobalHeaderRightProps> {
     return unreadMsg;
   };
 
-  changeReadState = (clickedItem: NoticeItem) => {
-    const { id } = clickedItem;
-    const { dispatch } = this.props;
-    if (dispatch) {
-      dispatch({
-        type: 'global/changeNoticeReadState',
-        payload: id,
-      });
-    }
-  };
-  componentDidMount() {
-    const { dispatch } = this.props;
-    if (dispatch) {
-      dispatch({
-        type: 'global/fetchNotices',
-      });
-    }
-  }
-  handleNoticeClear = (title: string, key: string) => {
-    const { dispatch } = this.props;
-    message.success(`${formatMessage({ id: 'component.noticeIcon.cleared' })} ${title}`);
-    if (dispatch) {
-      dispatch({
-        type: 'global/clearNotices',
-        payload: key,
-      });
-    }
-  };
   render() {
     const { currentUser, fetchingNotices, onNoticeVisibleChange } = this.props;
     const noticeData = this.getNoticeData();

+ 54 - 55
src/components/GlobalHeader/RightContent.tsx

@@ -1,12 +1,13 @@
 import { ConnectProps, ConnectState } from '@/models/connect';
-import React, { Component } from 'react';
 import { Icon, Tooltip } from 'antd';
-import { formatMessage } from 'umi-plugin-react/locale';
+
+import Avatar from './AvatarDropdown';
 import HeaderSearch from '../HeaderSearch';
+import React from 'react';
 import SelectLang from '../SelectLang';
-import styles from './index.less';
-import Avatar from './AvatarDropdown';
 import { connect } from 'dva';
+import { formatMessage } from 'umi-plugin-react/locale';
+import styles from './index.less';
 
 export type SiderTheme = 'light' | 'dark';
 export interface GlobalHeaderRightProps extends ConnectProps {
@@ -14,60 +15,58 @@ export interface GlobalHeaderRightProps extends ConnectProps {
   layout: 'sidemenu' | 'topmenu';
 }
 
-class GlobalHeaderRight extends Component<GlobalHeaderRightProps> {
-  render() {
-    const { theme, layout } = this.props;
-    let className = styles.right;
+const GlobalHeaderRight: React.SFC<GlobalHeaderRightProps> = props => {
+  const { theme, layout } = props;
+  let className = styles.right;
 
-    if (theme === 'dark' && layout === 'topmenu') {
-      className = `${styles.right}  ${styles.dark}`;
-    }
+  if (theme === 'dark' && layout === 'topmenu') {
+    className = `${styles.right}  ${styles.dark}`;
+  }
 
-    return (
-      <div className={className}>
-        <HeaderSearch
-          className={`${styles.action} ${styles.search}`}
-          placeholder={formatMessage({
-            id: 'component.globalHeader.search',
-          })}
-          dataSource={[
-            formatMessage({
-              id: 'component.globalHeader.search.example1',
-            }),
-            formatMessage({
-              id: 'component.globalHeader.search.example2',
-            }),
-            formatMessage({
-              id: 'component.globalHeader.search.example3',
-            }),
-          ]}
-          onSearch={value => {
-            console.log('input', value); // tslint:disable-line no-console
-          }}
-          onPressEnter={value => {
-            console.log('enter', value); // tslint:disable-line no-console
-          }}
-        />
-        <Tooltip
-          title={formatMessage({
-            id: 'component.globalHeader.help',
-          })}
+  return (
+    <div className={className}>
+      <HeaderSearch
+        className={`${styles.action} ${styles.search}`}
+        placeholder={formatMessage({
+          id: 'component.globalHeader.search',
+        })}
+        dataSource={[
+          formatMessage({
+            id: 'component.globalHeader.search.example1',
+          }),
+          formatMessage({
+            id: 'component.globalHeader.search.example2',
+          }),
+          formatMessage({
+            id: 'component.globalHeader.search.example3',
+          }),
+        ]}
+        onSearch={value => {
+          console.log('input', value);
+        }}
+        onPressEnter={value => {
+          console.log('enter', value);
+        }}
+      />
+      <Tooltip
+        title={formatMessage({
+          id: 'component.globalHeader.help',
+        })}
+      >
+        <a
+          target="_blank"
+          href="https://pro.ant.design/docs/getting-started"
+          rel="noopener noreferrer"
+          className={styles.action}
         >
-          <a
-            target="_blank"
-            href="https://pro.ant.design/docs/getting-started"
-            rel="noopener noreferrer"
-            className={styles.action}
-          >
-            <Icon type="question-circle-o" />
-          </a>
-        </Tooltip>
-        <Avatar />
-        <SelectLang className={styles.action} />
-      </div>
-    );
-  }
-}
+          <Icon type="question-circle-o" />
+        </a>
+      </Tooltip>
+      <Avatar />
+      <SelectLang className={styles.action} />
+    </div>
+  );
+};
 
 export default connect(({ settings }: ConnectState) => ({
   theme: settings.navTheme,

+ 2 - 2
src/components/HeaderDropdown/index.tsx

@@ -1,6 +1,6 @@
-import React from 'react';
-import { Dropdown } from 'antd';
 import { DropDownProps } from 'antd/es/dropdown';
+import { Dropdown } from 'antd';
+import React from 'react';
 import classNames from 'classnames';
 import styles from './index.less';
 

+ 18 - 13
src/components/HeaderSearch/index.tsx

@@ -1,6 +1,7 @@
+import { AutoComplete, Icon, Input } from 'antd';
 import React, { Component } from 'react';
-import { Input, Icon, AutoComplete } from 'antd';
-import { DataSourceItemType } from 'antd/es/auto-complete';
+
+import { DataSourceItemType, AutoCompleteProps } from 'antd/es/auto-complete';
 import classNames from 'classnames';
 import debounce from 'lodash/debounce';
 import styles from './index.less';
@@ -45,7 +46,8 @@ export default class HeaderSearch extends Component<HeaderSearchProps, HeaderSea
     return null;
   }
 
-  private timeout: NodeJS.Timeout = null!;
+  private timeout: number | undefined = undefined;
+
   private inputRef: Input | null = null;
 
   constructor(props: HeaderSearchProps) {
@@ -68,20 +70,22 @@ export default class HeaderSearch extends Component<HeaderSearchProps, HeaderSea
     if (e.key === 'Enter') {
       const { onPressEnter } = this.props;
       const { value } = this.state;
-      this.timeout = setTimeout(() => {
+      this.timeout = window.setTimeout(() => {
         onPressEnter(value); // Fix duplicate onPressEnter
       }, 0);
     }
   };
 
-  onChange = (value: string) => {
-    const { onSearch, onChange } = this.props;
-    this.setState({ value });
-    if (onSearch) {
-      onSearch(value);
-    }
-    if (onChange) {
-      onChange(value);
+  onChange: AutoCompleteProps['onChange'] = value => {
+    if (typeof value === 'string') {
+      const { onSearch, onChange } = this.props;
+      this.setState({ value });
+      if (onSearch) {
+        onSearch(value);
+      }
+      if (onChange) {
+        onChange(value);
+      }
     }
   };
 
@@ -116,6 +120,7 @@ export default class HeaderSearch extends Component<HeaderSearchProps, HeaderSea
     const inputClass = classNames(styles.input, {
       [styles.show]: searchMode,
     });
+
     return (
       <span
         className={classNames(className, styles.headerSearch)}
@@ -133,7 +138,7 @@ export default class HeaderSearch extends Component<HeaderSearchProps, HeaderSea
           {...restProps}
           className={inputClass}
           value={value}
-          onChange={this.onChange as any}
+          onChange={this.onChange}
         >
           <Input
             ref={node => {

+ 1 - 1
src/components/NoticeIcon/NoticeList.less

@@ -68,7 +68,7 @@
 }
 
 .notFound {
-  padding: 73px 0 88px 0;
+  padding: 73px 0 88px;
   color: @text-color-secondary;
   text-align: center;
   img {

+ 6 - 6
src/components/NoticeIcon/NoticeList.tsx

@@ -1,21 +1,21 @@
-import React from 'react';
 import { Avatar, List } from 'antd';
+
+import React from 'react';
 import classNames from 'classnames';
-import styles from './NoticeList.less';
 import { NoticeIconData } from './index';
+import styles from './NoticeList.less';
 
 export interface NoticeIconTabProps {
   count?: number;
-  list?: NoticeIconData[];
   name?: string;
   showClear?: boolean;
   showViewMore?: boolean;
   style?: React.CSSProperties;
   title: string;
   tabKey: string;
-  data?: any[];
-  onClick?: (item: any) => void;
-  onClear?: (item: any) => void;
+  data?: NoticeIconData[];
+  onClick?: (item: NoticeIconData) => void;
+  onClear?: () => void;
   emptyText?: string;
   clearText?: string;
   viewMoreText?: string;

+ 43 - 39
src/components/NoticeIcon/index.tsx

@@ -1,8 +1,9 @@
+import { Badge, Icon, Spin, Tabs } from 'antd';
 import React, { Component } from 'react';
-import { Icon, Tabs, Badge, Spin } from 'antd';
 import classNames from 'classnames';
-import HeaderDropdown from '../HeaderDropdown';
 import NoticeList, { NoticeIconTabProps } from './NoticeList';
+
+import HeaderDropdown from '../HeaderDropdown';
 import styles from './index.less';
 
 const { TabPane } = Tabs;
@@ -40,11 +41,11 @@ export default class NoticeIcon extends Component<NoticeIconProps> {
   public static Tab: typeof NoticeList = NoticeList;
 
   static defaultProps = {
-    onItemClick: () => {},
-    onPopupVisibleChange: () => {},
-    onTabChange: () => {},
-    onClear: () => {},
-    onViewMore: () => {},
+    onItemClick: (): void => {},
+    onPopupVisibleChange: (): void => {},
+    onTabChange: (): void => {},
+    onClear: (): void => {},
+    onViewMore: (): void => {},
     loading: false,
     clearClose: false,
     emptyImage: 'https://gw.alipayobjects.com/zos/rmsportal/wAhyIChODzsoKIOBHcBk.svg',
@@ -54,64 +55,67 @@ export default class NoticeIcon extends Component<NoticeIconProps> {
     visible: false,
   };
 
-  onItemClick = (item: NoticeIconData, tabProps: NoticeIconTabProps) => {
+  onItemClick = (item: NoticeIconData, tabProps: NoticeIconTabProps): void => {
     const { onItemClick } = this.props;
     if (onItemClick) {
       onItemClick(item, tabProps);
     }
   };
 
-  onClear = (name: string, key: string) => {
+  onClear = (name: string, key: string): void => {
     const { onClear } = this.props;
     if (onClear) {
       onClear(name, key);
     }
   };
 
-  onTabChange = (tabType: string) => {
+  onTabChange = (tabType: string): void => {
     const { onTabChange } = this.props;
     if (onTabChange) {
       onTabChange(tabType);
     }
   };
 
-  onViewMore = (tabProps: NoticeIconTabProps, event: MouseEvent) => {
+  onViewMore = (tabProps: NoticeIconTabProps, event: MouseEvent): void => {
     const { onViewMore } = this.props;
     if (onViewMore) {
       onViewMore(tabProps, event);
     }
   };
 
-  getNotificationBox() {
+  getNotificationBox(): React.ReactNode {
     const { children, loading, clearText, viewMoreText } = this.props;
     if (!children) {
       return null;
     }
-    const panes = React.Children.map(children, (child: React.ReactElement<NoticeIconTabProps>) => {
-      if (!child) {
-        return null;
-      }
-      const { list, title, count, tabKey, showClear, showViewMore } = child.props;
-      const len = list && list.length ? list.length : 0;
-      const msgCount = count || count === 0 ? count : len;
-      const tabTitle: string = msgCount > 0 ? `${title} (${msgCount})` : title;
-      return (
-        <TabPane tab={tabTitle} key={title}>
-          <NoticeList
-            clearText={clearText}
-            viewMoreText={viewMoreText}
-            data={list}
-            onClear={() => this.onClear(title, tabKey)}
-            onClick={item => this.onItemClick(item, child.props)}
-            onViewMore={event => this.onViewMore(child.props, event)}
-            showClear={showClear}
-            showViewMore={showViewMore}
-            title={title}
-            {...child.props}
-          />
-        </TabPane>
-      );
-    });
+    const panes = React.Children.map(
+      children,
+      (child: React.ReactElement<NoticeIconTabProps>): React.ReactNode => {
+        if (!child) {
+          return null;
+        }
+        const { list, title, count, tabKey, showClear, showViewMore } = child.props;
+        const len = list && list.length ? list.length : 0;
+        const msgCount = count || count === 0 ? count : len;
+        const tabTitle: string = msgCount > 0 ? `${title} (${msgCount})` : title;
+        return (
+          <TabPane tab={tabTitle} key={title}>
+            <NoticeList
+              clearText={clearText}
+              viewMoreText={viewMoreText}
+              data={list}
+              onClear={(): void => this.onClear(title, tabKey)}
+              onClick={(item): void => this.onItemClick(item, child.props)}
+              onViewMore={(event): void => this.onViewMore(child.props, event)}
+              showClear={showClear}
+              showViewMore={showViewMore}
+              title={title}
+              {...child.props}
+            />
+          </TabPane>
+        );
+      },
+    );
     return (
       <Spin spinning={loading} delay={0}>
         <Tabs className={styles.tabs} onChange={this.onTabChange}>
@@ -121,7 +125,7 @@ export default class NoticeIcon extends Component<NoticeIconProps> {
     );
   }
 
-  handleVisibleChange = (visible: boolean) => {
+  handleVisibleChange = (visible: boolean): void => {
     const { onPopupVisibleChange } = this.props;
     this.setState({ visible });
     if (onPopupVisibleChange) {
@@ -129,7 +133,7 @@ export default class NoticeIcon extends Component<NoticeIconProps> {
     }
   };
 
-  render() {
+  render(): React.ReactNode {
     const { className, count, popupVisible, bell } = this.props;
     const { visible } = this.state;
     const noticeButtonClass = classNames(className, styles.noticeButton);

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

@@ -1,7 +1,7 @@
-import React from 'react';
-import { formatMessage, setLocale, getLocale } from 'umi-plugin-react/locale';
-import { Menu, Icon } from 'antd';
+import { Icon, Menu } from 'antd';
+import { formatMessage, getLocale, setLocale } from 'umi-plugin-react/locale';
 import { ClickParam } from 'antd/es/menu';
+import React from 'react';
 import classNames from 'classnames';
 import HeaderDropdown from '../HeaderDropdown';
 import styles from './index.less';
@@ -12,7 +12,7 @@ interface SelectLangProps {
 const SelectLang: React.FC<SelectLangProps> = props => {
   const { className } = props;
   const selectedLang = getLocale();
-  const changeLang = ({ key }: ClickParam) => setLocale(key, false);
+  const changeLang = ({ key }: ClickParam): void => setLocale(key, false);
   const locales = ['zh-CN', 'zh-TW', 'en-US', 'pt-BR'];
   const languageLabels = {
     'zh-CN': '简体中文',

+ 12 - 9
src/components/SettingDrawer/themeColorClient.js

@@ -1,23 +1,26 @@
+// eslint-disable-next-line eslint-comments/disable-enable-pair
 /* eslint-disable import/no-extraneous-dependencies */
-import generate from '@ant-design/colors/lib/generate';
 import client from 'webpack-theme-color-replacer/client';
+import generate from '@ant-design/colors/lib/generate';
 
 export default {
+  lastColor: '#1890ff',
   primaryColor: '#1890ff',
-  getAntdSerials(color) {
+  getAntdSerials(color: string) {
     // 淡化(即less的tint)
-    const lightens = new Array(9).fill().map((t, i) => {
-      return client.varyColor.lighten(color, i / 10);
-    });
+    const lightens = new Array(9).fill(0).map((_, i) => client.varyColor.lighten(color, i / 10));
     const colorPalettes = generate(color);
     return lightens.concat(colorPalettes);
   },
-  changeColor(newColor) {
+  changeColor(newColor: string) {
     const lastColor = this.lastColor || this.primaryColor;
     const options = {
-      cssUrl: '/css/theme-colors.css', // hash模式下用相对路径
-      oldColors: this.getAntdSerials(lastColor), // current colors array. The same as `matchColors`
-      newColors: this.getAntdSerials(newColor || this.primaryColor), // new colors array, one-to-one corresponde with `oldColors`
+      // hash模式下用相对路径
+      cssUrl: '/css/theme-colors.css',
+      // current colors array. The same as `matchColors`
+      oldColors: this.getAntdSerials(lastColor),
+      // new colors array, one-to-one corresponde with `oldColors`
+      newColors: this.getAntdSerials(newColor || this.primaryColor),
     };
     const promise = client.changer.changeColor(options, Promise);
     this.lastColor = lastColor;

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

@@ -1,5 +1,3 @@
-jest.mock('antd-pro-merge-less');
-
 const RouterConfig = require('../../config/config').default.routes;
 
 const BASE_URL = `http://localhost:${process.env.PORT || 8000}`;

+ 11 - 5
src/global.tsx

@@ -1,5 +1,6 @@
+import { Button, message, notification } from 'antd';
+
 import React from 'react';
-import { notification, Button, message } from 'antd';
 import { formatMessage } from 'umi-plugin-react/locale';
 import defaultSettings from '../config/defaultSettings';
 
@@ -19,7 +20,7 @@ if (pwa) {
       // https://developer.mozilla.org/en-US/docs/Web/API/ServiceWorkerRegistration
       const worker = e.detail && e.detail.waiting;
       if (!worker) {
-        return Promise.resolve();
+        return true;
       }
       // Send skip-waiting event to waiting SW with MessageChannel
       await new Promise((resolve, reject) => {
@@ -59,7 +60,12 @@ if (pwa) {
   });
 } else if ('serviceWorker' in navigator) {
   // eslint-disable-next-line compat/compat
-  navigator.serviceWorker.ready.then(registration => {
-    registration.unregister();
-  });
+  navigator.serviceWorker.ready
+    .then(registration => {
+      registration.unregister();
+      return true;
+    })
+    .catch(() => {
+      console.log('serviceWorker unregister error');
+    });
 }

+ 33 - 31
src/layouts/BasicLayout.tsx

@@ -4,21 +4,23 @@
  * https://github.com/ant-design/ant-design-pro-layout
  */
 
-import { ConnectState, ConnectProps } from '@/models/connect';
-import RightContent from '@/components/GlobalHeader/RightContent';
-import { connect } from 'dva';
-import React, { useState } from 'react';
-import logo from '../assets/logo.svg';
-import Authorized from '@/utils/Authorized';
-import { formatMessage } from 'umi-plugin-react/locale';
-import { isAntDesignPro } from '@/utils/utils';
+import { ConnectProps, ConnectState } from '@/models/connect';
 import {
+  MenuDataItem,
   BasicLayout as ProLayoutComponents,
   BasicLayoutProps as ProLayoutComponentsProps,
-  MenuDataItem,
   Settings,
 } from '@ant-design/pro-layout';
+import React, { useState } from 'react';
+
+import Authorized from '@/utils/Authorized';
 import Link from 'umi/link';
+import RightContent from '@/components/GlobalHeader/RightContent';
+import { connect } from 'dva';
+import { formatMessage } from 'umi-plugin-react/locale';
+import { isAntDesignPro } from '@/utils/utils';
+import logo from '../assets/logo.svg';
+
 export interface BasicLayoutProps extends ProLayoutComponentsProps, ConnectProps {
   breadcrumbNameMap: {
     [path: string]: MenuDataItem;
@@ -30,16 +32,18 @@ export type BasicLayoutContext = { [K in 'location']: BasicLayoutProps[K] } & {
     [path: string]: MenuDataItem;
   };
 };
+
 /**
  * use Authorized check all menu item
  */
-
-const menuDataRender = (menuList: MenuDataItem[]): MenuDataItem[] => {
-  return menuList.map(item => {
-    const localItem = { ...item, children: item.children ? menuDataRender(item.children) : [] };
+const menuDataRender = (menuList: MenuDataItem[]): MenuDataItem[] =>
+  menuList.map(item => {
+    const localItem = {
+      ...item,
+      children: item.children ? menuDataRender(item.children) : [],
+    };
     return Authorized.check(item.authority, localItem, null) as MenuDataItem;
   });
-};
 
 const footerRender: BasicLayoutProps['footerRender'] = (_, defaultDom) => {
   if (!isAntDesignPro()) {
@@ -54,7 +58,7 @@ const footerRender: BasicLayoutProps['footerRender'] = (_, defaultDom) => {
           textAlign: 'center',
         }}
       >
-        <a href="https://www.netlify.com" target="_blank">
+        <a href="https://www.netlify.com" target="_blank" rel="noopener noreferrer">
           <img
             src="https://www.netlify.com/img/global/badges/netlify-color-bg.svg"
             width="82px"
@@ -86,7 +90,7 @@ const BasicLayout: React.FC<BasicLayoutProps> = props => {
   /**
    * init variables
    */
-  const handleMenuCollapse = (payload: boolean) =>
+  const handleMenuCollapse = (payload: boolean): void =>
     dispatch &&
     dispatch({
       type: 'global/changeLayoutCollapsed',
@@ -97,21 +101,19 @@ const BasicLayout: React.FC<BasicLayoutProps> = props => {
     <ProLayoutComponents
       logo={logo}
       onCollapse={handleMenuCollapse}
-      menuItemRender={(menuItemProps, defaultDom) => {
-        return <Link to={menuItemProps.path}>{defaultDom}</Link>;
-      }}
-      breadcrumbRender={(routers = []) => {
-        return [
-          {
-            path: '/',
-            breadcrumbName: formatMessage({
-              id: 'menu.home',
-              defaultMessage: 'Home',
-            }),
-          },
-          ...routers,
-        ];
-      }}
+      menuItemRender={(menuItemProps, defaultDom) => (
+        <Link to={menuItemProps.path}>{defaultDom}</Link>
+      )}
+      breadcrumbRender={(routers = []) => [
+        {
+          path: '/',
+          breadcrumbName: formatMessage({
+            id: 'menu.home',
+            defaultMessage: 'Home',
+          }),
+        },
+        ...routers,
+      ]}
       footerRender={footerRender}
       menuDataRender={menuDataRender}
       formatMessage={formatMessage}

+ 1 - 1
src/layouts/UserLayout.less

@@ -32,7 +32,7 @@
   }
 
   .content {
-    padding: 32px 0 24px 0;
+    padding: 32px 0 24px;
   }
 }
 

+ 5 - 5
src/layouts/UserLayout.tsx

@@ -1,13 +1,13 @@
-import SelectLang from '@/components/SelectLang';
 import { ConnectProps, ConnectState } from '@/models/connect';
-import { connect } from 'dva';
-import React from 'react';
+import { DefaultFooter, MenuDataItem, getMenuData, getPageTitle } from '@ant-design/pro-layout';
 import DocumentTitle from 'react-document-title';
-import { formatMessage } from 'umi-plugin-react/locale';
 import Link from 'umi/link';
+import React from 'react';
+import SelectLang from '@/components/SelectLang';
+import { connect } from 'dva';
+import { formatMessage } from 'umi-plugin-react/locale';
 import logo from '../assets/logo.svg';
 import styles from './UserLayout.less';
-import { MenuDataItem, getPageTitle, getMenuData, DefaultFooter } from '@ant-design/pro-layout';
 
 export interface UserLayoutProps extends ConnectProps {
   breadcrumbNameMap: { [path: string]: MenuDataItem };

+ 2 - 2
src/locales/en-US.ts

@@ -1,9 +1,9 @@
+import component from './en-US/component';
 import globalHeader from './en-US/globalHeader';
 import menu from './en-US/menu';
+import pwa from './en-US/pwa';
 import settingDrawer from './en-US/settingDrawer';
 import settings from './en-US/settings';
-import pwa from './en-US/pwa';
-import component from './en-US/component';
 
 export default {
   'navBar.lang': 'Languages',

+ 2 - 2
src/locales/pt-BR.ts

@@ -1,9 +1,9 @@
+import component from './pt-BR/component';
 import globalHeader from './pt-BR/globalHeader';
 import menu from './pt-BR/menu';
+import pwa from './pt-BR/pwa';
 import settingDrawer from './pt-BR/settingDrawer';
 import settings from './pt-BR/settings';
-import pwa from './pt-BR/pwa';
-import component from './pt-BR/component';
 
 export default {
   'navBar.lang': 'Idiomas',

+ 2 - 2
src/locales/zh-CN.ts

@@ -1,9 +1,9 @@
+import component from './zh-CN/component';
 import globalHeader from './zh-CN/globalHeader';
 import menu from './zh-CN/menu';
+import pwa from './zh-CN/pwa';
 import settingDrawer from './zh-CN/settingDrawer';
 import settings from './zh-CN/settings';
-import pwa from './zh-CN/pwa';
-import component from './zh-CN/component';
 
 export default {
   'navBar.lang': '语言',

+ 2 - 2
src/locales/zh-TW.ts

@@ -1,9 +1,9 @@
+import component from './zh-TW/component';
 import globalHeader from './zh-TW/globalHeader';
 import menu from './zh-TW/menu';
+import pwa from './zh-TW/pwa';
 import settingDrawer from './zh-TW/settingDrawer';
 import settings from './zh-TW/settings';
-import pwa from './zh-TW/pwa';
-import component from './zh-TW/component';
 
 export default {
   'navBar.lang': '語言',

+ 20 - 21
src/models/connect.d.ts

@@ -1,27 +1,12 @@
-import { EffectsCommandMap } from 'dva';
 import { AnyAction } from 'redux';
+import { EffectsCommandMap } from 'dva';
+import { MenuDataItem } from '@ant-design/pro-layout';
 import { RouterTypes } from 'umi';
 import { GlobalModelState } from './global';
-import { UserModelState } from './user';
 import { DefaultSettings as SettingModelState } from '../../config/defaultSettings';
-import { MenuDataItem } from '@ant-design/pro-layout';
-export { GlobalModelState, SettingModelState, UserModelState };
-
-export type Effect = (
-  action: AnyAction,
-  effects: EffectsCommandMap & { select: <T>(func: (state: ConnectState) => T) => T },
-) => void;
+import { UserModelState } from './user';
 
-/**
- * @type P: Type of payload
- * @type C: Type of callback
- */
-export type Dispatch = <P = any, C = (payload: P) => void>(action: {
-  type: string;
-  payload?: P;
-  callback?: C;
-  [key: string]: any;
-}) => any;
+export { GlobalModelState, SettingModelState, UserModelState };
 
 export interface Loading {
   global: boolean;
@@ -41,6 +26,22 @@ export interface ConnectState {
   user: UserModelState;
 }
 
+export type Effect = (
+  action: AnyAction,
+  effects: EffectsCommandMap & { select: <T>(func: (state: ConnectState) => T) => T },
+) => void;
+
+/**
+ * @type P: Type of payload
+ * @type C: Type of callback
+ */
+export type Dispatch = <P = any, C = (payload: P) => void>(action: {
+  type: string;
+  payload?: P;
+  callback?: C;
+  [key: string]: any;
+}) => any;
+
 export interface Route extends MenuDataItem {
   routes?: Route[];
 }
@@ -52,5 +53,3 @@ export interface ConnectProps<T extends { [key: string]: any } = {}>
   extends Partial<RouterTypes<Route, T>> {
   dispatch?: Dispatch;
 }
-
-export default ConnectState;

+ 12 - 11
src/models/global.ts

@@ -1,13 +1,12 @@
-import { queryNotices } from '@/services/user';
-import { Subscription } from 'dva';
-import { Reducer } from 'redux';
-import { Effect } from './connect';
+import { Effect } from './connect.d';
 import { NoticeIconData } from '@/components/NoticeIcon';
+import { Reducer } from 'redux';
+import { Subscription } from 'dva';
+import { queryNotices } from '@/services/user';
 
 export interface NoticeItem extends NoticeIconData {
   id: string;
   type: string;
-  [key: string]: any;
 }
 
 export interface GlobalModelState {
@@ -84,10 +83,12 @@ const GlobalModel: GlobalModelType = {
           return notice;
         }),
       );
+
       yield put({
         type: 'saveNotices',
         payload: notices,
       });
+
       yield put({
         type: 'user/changeNotifyCount',
         payload: {
@@ -99,32 +100,32 @@ const GlobalModel: GlobalModelType = {
   },
 
   reducers: {
-    changeLayoutCollapsed(state = { notices: [], collapsed: true }, { payload }) {
+    changeLayoutCollapsed(state = { notices: [], collapsed: true }, { payload }): GlobalModelState {
       return {
         ...state,
         collapsed: payload,
       };
     },
-    saveNotices(state, { payload }) {
+    saveNotices(state, { payload }): GlobalModelState {
       return {
         collapsed: false,
         ...state,
         notices: payload,
       };
     },
-    saveClearedNotices(state = { notices: [], collapsed: true }, { payload }) {
+    saveClearedNotices(state = { notices: [], collapsed: true }, { payload }): GlobalModelState {
       return {
         collapsed: false,
         ...state,
-        notices: state.notices.filter(item => item.type !== payload),
+        notices: state.notices.filter((item): boolean => item.type !== payload),
       };
     },
   },
 
   subscriptions: {
-    setup({ history }) {
+    setup({ history }): void {
       // Subscribe history(url) change, trigger `load` action if pathname is `/`
-      return history.listen(({ pathname, search }) => {
+      history.listen(({ pathname, search }): void => {
         if (typeof (window as any).ga !== 'undefined') {
           (window as any).ga('send', 'pageview', pathname + search);
         }

+ 5 - 4
src/models/login.ts

@@ -1,9 +1,10 @@
-import { routerRedux } from 'dva/router';
-import { Reducer, AnyAction } from 'redux';
+import { AnyAction, Reducer } from 'redux';
+import { parse, stringify } from 'qs';
+
 import { EffectsCommandMap } from 'dva';
-import { stringify, parse } from 'qs';
+import { routerRedux } from 'dva/router';
 
-export function getPageQuery() {
+export function getPageQuery(): string {
   return parse(window.location.href.split('?')[1]);
 }
 

+ 6 - 3
src/models/setting.ts

@@ -1,5 +1,7 @@
-import { message } from 'antd';
 import { Reducer } from 'redux';
+// eslint-disable-next-line eslint-comments/disable-enable-pair
+/* eslint-disable promise/catch-or-return */
+import { message } from 'antd';
 import defaultSettings, { DefaultSettings } from '../../config/defaultSettings';
 import themeColorClient from '../components/SettingDrawer/themeColorClient';
 
@@ -35,8 +37,8 @@ const updateTheme: (primaryColor?: string) => void = primaryColor => {
   const hideMessage = message.loading('正在编译主题!', 0);
   function buildIt() {
     if (!(window as any).less) {
-      // tslint:disable-next-line no-console
-      return console.log('no less');
+      console.log('no less');
+      return;
     }
     setTimeout(() => {
       (window as any).less
@@ -45,6 +47,7 @@ const updateTheme: (primaryColor?: string) => void = primaryColor => {
         })
         .then(() => {
           hideMessage();
+          return true;
         })
         .catch(() => {
           message.error('Failed to update theme');

+ 2 - 2
src/models/user.ts

@@ -1,4 +1,5 @@
-import { query as queryUsers, queryCurrent } from '@/services/user';
+import { queryCurrent, query as queryUsers } from '@/services/user';
+
 import { Effect } from 'dva';
 import { Reducer } from 'redux';
 
@@ -8,7 +9,6 @@ export interface CurrentUser {
   title?: string;
   group?: string;
   signature?: string;
-  geographic?: any;
   tags?: {
     key: string;
     label: string;

+ 8 - 6
src/pages/Authorized.tsx

@@ -1,16 +1,16 @@
+import { ConnectProps, ConnectState, Route, UserModelState } from '@/models/connect';
 import Authorized from '@/utils/Authorized';
-import { ConnectProps, ConnectState, UserModelState, Route } from '@/models/connect';
-import { connect } from 'dva';
-import pathToRegexp from 'path-to-regexp';
 import React from 'react';
 import Redirect from 'umi/redirect';
+import { connect } from 'dva';
+import pathToRegexp from 'path-to-regexp';
 
 interface AuthComponentProps extends ConnectProps {
   user: UserModelState;
 }
 
 const getRouteAuthority = (path: string, routeData: Route[]) => {
-  let authorities: string[] | string | undefined = undefined;
+  let authorities: string[] | string | undefined;
   routeData.forEach(route => {
     // match prefix
     if (pathToRegexp(`${route.path}(.*)`).test(path)) {
@@ -29,7 +29,9 @@ const AuthComponent: React.FC<AuthComponentProps> = ({
   route = {
     routes: [],
   },
-  location,
+  location = {
+    pathname: '',
+  },
   user,
 }) => {
   const { currentUser } = user;
@@ -37,7 +39,7 @@ const AuthComponent: React.FC<AuthComponentProps> = ({
   const isLogin = currentUser && currentUser.name;
   return (
     <Authorized
-      authority={getRouteAuthority(location!.pathname, routes)!}
+      authority={getRouteAuthority(location.pathname, routes) || ''}
       noMatch={isLogin ? <Redirect to="/exception/403" /> : <Redirect to="/user/login" />}
     >
       {children}

+ 1 - 1
src/pages/Welcome.tsx

@@ -1,6 +1,6 @@
 import React from 'react';
 
-export default () => (
+export default (): React.ReactNode => (
   <p style={{ textAlign: 'center' }}>
     Want to add more pages? Please refer to{' '}
     <a href="https://pro.ant.design/docs/block-cn" target="_blank" rel="noopener noreferrer">

+ 3 - 2
src/service-worker.js

@@ -1,5 +1,7 @@
-/* globals workbox */
+/* eslint-disable eslint-comments/disable-enable-pair */
 /* eslint-disable no-restricted-globals */
+/* eslint-disable no-underscore-dangle */
+/* globals workbox */
 workbox.core.setCacheNameDetails({
   prefix: 'antd-pro',
   suffix: 'v1',
@@ -11,7 +13,6 @@ workbox.clientsClaim();
  * Use precaching list generated by workbox in build process.
  * https://developers.google.com/web/tools/workbox/reference-docs/latest/workbox.precaching
  */
-/* eslint-disable no-underscore-dangle */
 workbox.precaching.precacheAndRoute(self.__precacheManifest || []);
 
 /**

+ 3 - 1
src/typings.d.ts

@@ -1,5 +1,4 @@
 declare module 'slash2';
-declare module 'antd-pro-merge-less';
 declare module 'antd-theme-webpack-plugin';
 
 declare module '*.css';
@@ -19,8 +18,11 @@ declare module 'react-copy-to-clipboard';
 declare module 'react-fittext';
 declare module '@antv/data-set';
 declare module 'nzh/cn';
+declare module 'webpack-theme-color-replacer';
+declare module 'webpack-theme-color-replacer/client';
 
 declare let ga: Function;
+
 // preview.pro.ant.design only do not use in your production ;
 // preview.pro.ant.design 专用环境变量,请不要在你的项目中使用它。
 declare let ANT_DESIGN_PRO_ONLY_DO_NOT_USE_IN_YOUR_PRODUCTION: 'site' | undefined;

+ 5 - 4
src/utils/Authorized.ts

@@ -1,10 +1,11 @@
-import { default as RenderAuthorize } from '@/components/Authorized';
+import RenderAuthorize from '@/components/Authorized';
 import { getAuthority } from './authority';
-
-let Authorized = RenderAuthorize(getAuthority()); // eslint-disable-line
+/* eslint-disable eslint-comments/disable-enable-pair */
+/* eslint-disable import/no-mutable-exports */
+let Authorized = RenderAuthorize(getAuthority());
 
 // Reload the rights component
-const reloadAuthorized = () => {
+const reloadAuthorized = (): void => {
   Authorized = RenderAuthorize(getAuthority());
 };
 

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

@@ -1,10 +1,6 @@
-import 'jest';
 import { getAuthority } from './authority';
 
 describe('getAuthority should be strong', () => {
-  it('empty', () => {
-    expect(getAuthority(null)).toEqual(null); // default value
-  });
   it('string', () => {
     expect(getAuthority('admin')).toEqual(['admin']);
   });

+ 6 - 3
src/utils/authority.ts

@@ -1,19 +1,22 @@
 // use localStorage to store the authority info, which might be sent from server in actual project.
-export function getAuthority(str?: string): any {
+export function getAuthority(str?: string): string | string[] {
   // return localStorage.getItem('antd-pro-authority') || ['admin', 'user'];
   const authorityString =
     typeof str === 'undefined' ? localStorage.getItem('antd-pro-authority') : str;
   // authorityString could be admin, "admin", ["admin"]
   let authority;
   try {
-    authority = JSON.parse(authorityString!);
+    if (authorityString) {
+      authority = JSON.parse(authorityString);
+    }
   } catch (e) {
     authority = authorityString;
   }
   if (typeof authority === 'string') {
     return [authority];
   }
-  // preview.pro.ant.design only do not use in your production ; preview.pro.ant.design 专用环境变量,请不要在你的项目中使用它。
+  // preview.pro.ant.design only do not use in your production.
+  // preview.pro.ant.design 专用环境变量,请不要在你的项目中使用它。
   if (!authority && ANT_DESIGN_PRO_ONLY_DO_NOT_USE_IN_YOUR_PRODUCTION === 'site') {
     return ['admin'];
   }

+ 10 - 14
src/utils/request.ts

@@ -5,12 +5,6 @@
 import { extend } from 'umi-request';
 import { notification } from 'antd';
 
-interface ResponseError<D = any> extends Error {
-  name: string;
-  data: D;
-  response: Response;
-}
-
 const codeMessage = {
   200: '服务器成功返回请求的数据。',
   201: '新建或修改数据成功。',
@@ -32,15 +26,17 @@ const codeMessage = {
 /**
  * 异常处理程序
  */
-const errorHandler = (error: ResponseError) => {
-  const { response = {} as Response } = error;
-  const errortext = codeMessage[response.status] || response.statusText;
-  const { status, url } = response;
+const errorHandler = (error: { response: Response }): void => {
+  const { response } = error;
+  if (response && response.status) {
+    const errorText = codeMessage[response.status] || response.statusText;
+    const { status, url } = response;
 
-  notification.error({
-    message: `请求错误 ${status}: ${url}`,
-    description: errortext,
-  });
+    notification.error({
+      message: `请求错误 ${status}: ${url}`,
+      description: errorText,
+    });
+  }
 };
 
 /**

+ 4 - 5
src/utils/utils.test.ts

@@ -1,8 +1,7 @@
-import 'jest';
 import { isUrl } from './utils';
 
-describe('isUrl tests', () => {
-  it('should return false for invalid and corner case inputs', () => {
+describe('isUrl tests', (): void => {
+  it('should return false for invalid and corner case inputs', (): void => {
     expect(isUrl([] as any)).toBeFalsy();
     expect(isUrl({} as any)).toBeFalsy();
     expect(isUrl(false as any)).toBeFalsy();
@@ -13,7 +12,7 @@ describe('isUrl tests', () => {
     expect(isUrl('')).toBeFalsy();
   });
 
-  it('should return false for invalid URLs', () => {
+  it('should return false for invalid URLs', (): void => {
     expect(isUrl('foo')).toBeFalsy();
     expect(isUrl('bar')).toBeFalsy();
     expect(isUrl('bar/test')).toBeFalsy();
@@ -21,7 +20,7 @@ describe('isUrl tests', () => {
     expect(isUrl('ttp://example.com/')).toBeFalsy();
   });
 
-  it('should return true for valid URLs', () => {
+  it('should return true for valid URLs', (): void => {
     expect(isUrl('http://example.com/')).toBeTruthy();
     expect(isUrl('https://example.com/')).toBeTruthy();
     expect(isUrl('http://example.com/test/123')).toBeTruthy();

+ 1 - 3
src/utils/utils.ts

@@ -1,9 +1,7 @@
 /* eslint no-useless-escape:0 import/prefer-default-export:0 */
 const reg = /(((^https?:(?:\/\/)?)(?:[-;:&=\+\$,\w]+@)?[A-Za-z0-9.-]+(?::\d+)?|(?:www.|[-;:&=\+\$,\w]+@)[A-Za-z0-9.-]+)((?:\/[\+~%\/.\w-_]*)?\??(?:[-\+=&;%@.\w_]*)#?(?:[\w]*))?)$/;
 
-const isUrl = (path: string): boolean => {
-  return reg.test(path);
-};
+const isUrl = (path: string): boolean => reg.test(path);
 
 const isAntDesignPro = (): boolean => {
   if (ANT_DESIGN_PRO_ONLY_DO_NOT_USE_IN_YOUR_PRODUCTION === 'site') {

+ 3 - 1
tests/run-tests.js

@@ -1,4 +1,6 @@
-/* eslint-disable no-console */
+/* 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');
 const { kill } = require('cross-port-killer');
 

+ 0 - 88
tslint.yml

@@ -1,88 +0,0 @@
-defaultSeverity: error
-globals:
-  - ANT_DESIGN_PRO_ONLY_DO_NOT_USE_IN_YOUR_PRODUCTION: true
-extends:
-  - tslint-react
-  - tslint-eslint-rules
-  - tslint-config-prettier
-jsRules:
-rules:
-  class-name: true
-  eofline: true
-  forin: true
-  jsdoc-format: false
-  label-position: true
-  member-ordering:
-    - true
-    - order: statics-first
-  new-parens: true
-  no-arg: true
-  no-bitwise: true
-  no-conditional-assignment: true
-  no-consecutive-blank-lines: true
-  no-construct: true
-  no-debugger: true
-  no-duplicate-variable: true
-  no-eval: true
-  no-internal-module: true
-  no-multi-spaces: true
-  no-namespace: true
-  no-reference: true
-  no-shadowed-variable: true
-  no-string-literal: true
-  no-trailing-whitespace: true
-  no-unused-expression: true
-  no-var-keyword: true
-  one-variable-per-declaration:
-    - true
-    - ignore-for-loop
-  prefer-const:
-    - true
-    - destructuring: all
-  radix: true
-  space-in-parens: true
-  switch-default: true
-  trailing-comma:
-    - true
-    - singleline: never
-      multiline: always
-      esSpecCompliant: true
-  triple-equals:
-    - true
-    - allow-null-check
-  typedef-whitespace:
-    - true
-    - call-signature: nospace
-      index-signature: nospace
-      parameter: nospace
-      property-declaration: nospace
-      variable-declaration: nospace
-    - call-signature: onespace
-      index-signature: onespace
-      parameter: onespace
-      property-declaration: onespace
-      variable-declaration: onespace
-  use-isnan: true
-  variable-name:
-    - true
-    - allow-leading-underscore
-    - ban-keywords
-    - check-format
-    - allow-pascal-case
-  jsx-no-lambda: false
-  jsx-no-string-ref: false
-  jsx-boolean-value:
-    - true
-    - never
-  jsx-no-multiline-js: false
-  whitespace:
-    - true
-    - check-branch
-    - check-decl
-    - check-operator
-    - check-module
-    - check-separator
-    - check-rest-spread
-    - check-type
-    - check-type-operator
-    - check-preblock