diff --git a/.eslintrc.js b/.eslintrc.js index 7372d96bce7291a91dbecf4a1c7ce9f14b1f862c..a255138957075be25d94ee9dd7451155f39ff285 100644 --- a/.eslintrc.js +++ b/.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, }, }; diff --git a/.gitignore b/.gitignore index 67e95cb3fc7136507a25be7b68f104f5f82d1a3a..18b814abcf91eebf1cd3acb7b8e86c0de27baa67 100644 --- a/.gitignore +++ b/.gitignore @@ -34,4 +34,5 @@ functions/* # screenshot screenshot -.firebase \ No newline at end of file +.firebase +.eslintcache diff --git a/.prettierignore b/.prettierignore index 7abf48a03e3e8789f00e58f7268520a637eb3829..a1842db59dbde3015e993068202fe95436389552 100644 --- a/.prettierignore +++ b/.prettierignore @@ -13,4 +13,7 @@ docker Dockerfile* .gitignore .prettierignore -LICENSE \ No newline at end of file +LICENSE +.eslintcache +*.lock +yarn-error.log \ No newline at end of file diff --git a/.prettierrc b/.prettierrc deleted file mode 100644 index 4d911faad1e455d949ba1c8e70100a52c79ff29e..0000000000000000000000000000000000000000 --- a/.prettierrc +++ /dev/null @@ -1,20 +0,0 @@ -{ - "singleQuote": true, - "trailingComma": "all", - "printWidth": 100, - "proseWrap": "never", - "overrides": [ - { - "files": ".prettierrc", - "options": { - "parser": "json" - } - }, - { - "files": "document.ejs", - "options": { - "parser": "html" - } - } - ] -} diff --git a/.prettierrc.js b/.prettierrc.js new file mode 100644 index 0000000000000000000000000000000000000000..7b597d78917c7e33a81bb3e83c6067f6a9a970e6 --- /dev/null +++ b/.prettierrc.js @@ -0,0 +1,5 @@ +const fabric = require('@umijs/fabric'); + +module.exports = { + ...fabric.prettier, +}; diff --git a/.stylelintrc.js b/.stylelintrc.js new file mode 100644 index 0000000000000000000000000000000000000000..c2030787de72c5cffc44099fbdbae55c32afd482 --- /dev/null +++ b/.stylelintrc.js @@ -0,0 +1,5 @@ +const fabric = require('@umijs/fabric'); + +module.exports = { + ...fabric.stylelint, +}; diff --git a/.stylelintrc.json b/.stylelintrc.json deleted file mode 100644 index 215bf08119eadd5005ddbef70291a652520e697b..0000000000000000000000000000000000000000 --- a/.stylelintrc.json +++ /dev/null @@ -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 - } -} diff --git a/config/config.ts b/config/config.ts index 6673a548e8f67b32ab444158435870c0447deeb8..ee8811032ba6accfbaa61bf5d91b59022abaf3c7 100644 --- a/config/config.ts +++ b/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 ; diff --git a/config/plugin.config.ts b/config/plugin.config.ts index ce6fd398ad8f55c9749e17acd9c5d6fb618f5530..4b4ed895083dc62bf1449c8f4899bb6a213577ea 100644 --- a/config/plugin.config.ts +++ b/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); diff --git a/package.json b/package.json index 8e05310ec68428205084a59b55002d4f4df9461d..f300a83690e6200ee65cff58d3aecef4ab04e369 100644 --- a/package.json +++ b/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" ] } -} +} \ No newline at end of file diff --git a/src/components/Authorized/Authorized.tsx b/src/components/Authorized/Authorized.tsx index a8bbb5e049dd919b611b27eb42ca692f09fb049b..b5eff807d971975d5352ae39ee0477fb61b2ac24 100644 --- a/src/components/Authorized/Authorized.tsx +++ b/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 & { +type IAuthorizedType = React.FunctionComponent & { Secured: typeof Secured; check: typeof check; AuthorizedRoute: typeof AuthorizedRoute; }; -const Authorized: React.FunctionComponent = ({ +const Authorized: React.FunctionComponent = ({ 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}; }; diff --git a/src/components/Authorized/AuthorizedRoute.tsx b/src/components/Authorized/AuthorizedRoute.tsx index 7ff18c55003e982c96475f406b51016bad41aad0..7743eaeea67b126f9f8c5d6c8f023b7ad5a75b02 100644 --- a/src/components/Authorized/AuthorizedRoute.tsx +++ b/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; render: (props: any) => React.ReactNode; @@ -11,7 +12,7 @@ interface IAuthorizedRoutePops { authority: IAuthorityType; } -const AuthorizedRoute: React.SFC = ({ +const AuthorizedRoute: React.SFC = ({ component: Component, render, authority, diff --git a/src/components/Authorized/CheckPermissions.tsx b/src/components/Authorized/CheckPermissions.tsx index 1d599e37bd77bf6027b6470c03a43a83e5b56fd7..caa15a3873ff97f07c8313282fc6d00b4ca2d5bb 100644 --- a/src/components/Authorized/CheckPermissions.tsx +++ b/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 + | Promise | ((currentAuthority: string | string[]) => IAuthorityType); /** diff --git a/src/components/Authorized/PromiseRender.tsx b/src/components/Authorized/PromiseRender.tsx index 41db7fa042fdded03b94e37fc41148fe10e76ef8..10b8fc9f931e196ab5856bc7186b5b0223ffa102 100644 --- a/src/components/Authorized/PromiseRender.tsx +++ b/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 { +interface PromiseRenderProps { ok: T; error: K; - promise: Promise; + promise: Promise; } -interface IPromiseRenderState { - component: React.ComponentClass | React.FunctionComponent; +interface PromiseRenderState { + component: React.ComponentClass | React.FunctionComponent; } export default class PromiseRender extends React.Component< - IPromiseRenderProps, - IPromiseRenderState + PromiseRenderProps, + PromiseRenderState > { - state: IPromiseRenderState = { + state: PromiseRenderState = { component: () => null, }; @@ -26,10 +26,7 @@ export default class PromiseRender extends React.Component< this.setRenderComponent(this.props); } - shouldComponentUpdate = ( - nextProps: IPromiseRenderProps, - nextState: IPromiseRenderState, - ) => { + shouldComponentUpdate = (nextProps: PromiseRenderProps, nextState: PromiseRenderState) => { const { component } = this.state; if (!isEqual(nextProps, this.props)) { this.setRenderComponent(nextProps); @@ -39,7 +36,7 @@ export default class PromiseRender extends React.Component< }; // set render Component : ok or error - setRenderComponent(props: IPromiseRenderProps) { + setRenderComponent(props: PromiseRenderProps) { const ok = this.checkIsInstantiation(props.ok); const error = this.checkIsInstantiation(props.error); props.promise @@ -47,6 +44,7 @@ export default class PromiseRender extends React.Component< this.setState({ component: ok, }); + return true; }) .catch(() => { this.setState({ @@ -60,10 +58,10 @@ export default class PromiseRender extends React.Component< // Authorized render is already instantiated, children is no instantiated // Secured is not instantiated checkIsInstantiation = ( - target: React.ReactNode | React.ComponentClass, - ): React.FunctionComponent => { + target: React.ReactNode | React.ComponentClass, + ): React.FunctionComponent => { if (isComponentClass(target)) { - const Target = target as React.ComponentClass; + const Target = target as React.ComponentClass; return (props: any) => ; } if (React.isValidElement(target)) { @@ -75,6 +73,7 @@ export default class PromiseRender extends React.Component< render() { const { component: Component } = this.state; const { ok, error, promise, ...rest } = this.props; + return Component ? ( ) : ( diff --git a/src/components/Authorized/Secured.tsx b/src/components/Authorized/Secured.tsx index 4582ed091d8497c85e99fefcf53d1bc3e094a4d4..0bdbbe4e66788cfd3f0466bbbb5cbe14f4d6524a 100644 --- a/src/components/Authorized/Secured.tsx +++ b/src/components/Authorized/Secured.tsx @@ -7,9 +7,7 @@ import CheckPermissions from './CheckPermissions'; */ const Exception403 = () => 403; -export const isComponentClass = ( - component: React.ComponentClass | 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 | React.ReactNode) => { +const checkIsInstantiation = (target: React.ComponentClass | React.ReactNode) => { if (isComponentClass(target)) { - const Target = target as React.ComponentClass; + const Target = target as React.ComponentClass; return (props: any) => ; } 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 = false; + let classError: boolean | React.FunctionComponent = false; if (error) { - classError = (() => error) as React.FunctionComponent; + classError = (() => error) as React.FunctionComponent; } if (!authority) { throw new Error('authority is required'); } - return function decideAuthority(target: React.ComponentClass | React.ReactNode) { + return function decideAuthority(target: React.ComponentClass | React.ReactNode) { const component = CheckPermissions(authority, target, classError || Exception403); return checkIsInstantiation(component); }; diff --git a/src/components/Authorized/renderAuthorize.ts b/src/components/Authorized/renderAuthorize.ts index af7586cc5be25e1bcb3223e81c329719a50c3142..df008750168e2473f16c1cae5bc518b628186fc7 100644 --- a/src/components/Authorized/renderAuthorize.ts +++ b/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 = (Authorized: T): ((currentAuthority: CurrentAuthorityType) => T) => ( currentAuthority: CurrentAuthorityType, -) => { +): T => { if (currentAuthority) { if (typeof currentAuthority === 'function') { CURRENT = currentAuthority(); diff --git a/src/components/CopyBlock/index.tsx b/src/components/CopyBlock/index.tsx index bb24d5842db7a89fcfba622d09c008d52c7fa818..2292078ed489f1886571009b7cfc300254a155be 100644 --- a/src/components/CopyBlock/index.tsx +++ b/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, diff --git a/src/components/GlobalHeader/AvatarDropdown.tsx b/src/components/GlobalHeader/AvatarDropdown.tsx index af43b887820bbf4dec49ddf63225ae6b8809453d..2d105d51eb91427deb423e0925e1e51e3bfe50dc 100644 --- a/src/components/GlobalHeader/AvatarDropdown.tsx +++ b/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 { } router.push(`/account/${key}`); }; - render() { + + render(): React.ReactNode { const { currentUser = {}, menu } = this.props; if (!menu) { return ( diff --git a/src/components/GlobalHeader/NoticeIconView.tsx b/src/components/GlobalHeader/NoticeIconView.tsx index d79550b028811385c7a29d4efa2088354ea7e1bd..52a26e8a0a97e006522304cba34d1812aaab3caa 100644 --- a/src/components/GlobalHeader/NoticeIconView.tsx +++ b/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 { + 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 { 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 { 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(); diff --git a/src/components/GlobalHeader/RightContent.tsx b/src/components/GlobalHeader/RightContent.tsx index cf4f5bf6e538f440a228aba3678427c470a36467..9200785daf02123b233d95550d9e224b0a83b4c4 100644 --- a/src/components/GlobalHeader/RightContent.tsx +++ b/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 { - render() { - const { theme, layout } = this.props; - let className = styles.right; +const GlobalHeaderRight: React.SFC = 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 ( -
- { - console.log('input', value); // tslint:disable-line no-console - }} - onPressEnter={value => { - console.log('enter', value); // tslint:disable-line no-console - }} - /> - + { + console.log('input', value); + }} + onPressEnter={value => { + console.log('enter', value); + }} + /> + + - - - - - - -
- ); - } -} + + + + + + + ); +}; export default connect(({ settings }: ConnectState) => ({ theme: settings.navTheme, diff --git a/src/components/HeaderDropdown/index.tsx b/src/components/HeaderDropdown/index.tsx index 897c8199e88fe9e349fee21408a28ad5011975df..f668a2b6615ff431c6940def97455f428ac227e8 100644 --- a/src/components/HeaderDropdown/index.tsx +++ b/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'; diff --git a/src/components/HeaderSearch/index.tsx b/src/components/HeaderSearch/index.tsx index dc252603c3a76122c5465d290f0c1efb3e2fd2d8..a6e8698e9259502dce39e7643c549fd92cf09575 100644 --- a/src/components/HeaderSearch/index.tsx +++ b/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 { + 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 { diff --git a/src/components/NoticeIcon/NoticeList.less b/src/components/NoticeIcon/NoticeList.less old mode 100644 new mode 100755 index df69aa7f7d55b0dc9999877b9495373a941e168b..ce07d712b8a239711c435fcaa18faecc2808e356 --- a/src/components/NoticeIcon/NoticeList.less +++ b/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 { diff --git a/src/components/NoticeIcon/NoticeList.tsx b/src/components/NoticeIcon/NoticeList.tsx index 53a58ca13c264996b2cc17e7d51f13430e0e2c7c..4b0923d879aac73e6abf0f6f5e94a227b0fa7856 100644 --- a/src/components/NoticeIcon/NoticeList.tsx +++ b/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; diff --git a/src/components/NoticeIcon/index.tsx b/src/components/NoticeIcon/index.tsx index 4ead5bf6f081a76a298705f1617e8c8ae39e5ffc..322ca3e8543c96221c04b1df7132e4fac32a1ce2 100644 --- a/src/components/NoticeIcon/index.tsx +++ b/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 { 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 { 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) => { - 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 ( - - 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} - /> - - ); - }); + const panes = React.Children.map( + children, + (child: React.ReactElement): 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 ( + + 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} + /> + + ); + }, + ); return ( @@ -121,7 +125,7 @@ export default class NoticeIcon extends Component { ); } - 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 { } }; - render() { + render(): React.ReactNode { const { className, count, popupVisible, bell } = this.props; const { visible } = this.state; const noticeButtonClass = classNames(className, styles.noticeButton); diff --git a/src/components/SelectLang/index.tsx b/src/components/SelectLang/index.tsx index ac8309d953721ac87b2d5d9b0823516837f3842f..c45354dbe8a05720231d268cca84d5be5906dff3 100644 --- a/src/components/SelectLang/index.tsx +++ b/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 = 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': '简体中文', diff --git a/src/components/SettingDrawer/themeColorClient.js b/src/components/SettingDrawer/themeColorClient.ts similarity index 54% rename from src/components/SettingDrawer/themeColorClient.js rename to src/components/SettingDrawer/themeColorClient.ts index a0e476dece71ebc3c47cf2cebfd791f77b02ea42..8aa3b360ba12857f0506be35793ae9bde4cb07b5 100644 --- a/src/components/SettingDrawer/themeColorClient.js +++ b/src/components/SettingDrawer/themeColorClient.ts @@ -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; diff --git a/src/e2e/baseLayout.e2e.js b/src/e2e/baseLayout.e2e.js index 63f58a5a85e29479bf8c1bc1311d12c26486e402..589e76ce3e3dddbe3f6f7baa821ec61535221098 100644 --- a/src/e2e/baseLayout.e2e.js +++ b/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}`; diff --git a/src/global.tsx b/src/global.tsx index 3a97886280b2db3d7938bf32bb382ea2de61a31e..11bad45e5240610566d824a85031f4dc4148b3db 100644 --- a/src/global.tsx +++ b/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'); + }); } diff --git a/src/layouts/BasicLayout.tsx b/src/layouts/BasicLayout.tsx index c6cc156cc5cc986831df91997ec52424901183f8..0a755599511fd4c0a8c6d9596bd4619aae02bd14 100644 --- a/src/layouts/BasicLayout.tsx +++ b/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', }} > - + = 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 = props => { { - return {defaultDom}; - }} - breadcrumbRender={(routers = []) => { - return [ - { - path: '/', - breadcrumbName: formatMessage({ - id: 'menu.home', - defaultMessage: 'Home', - }), - }, - ...routers, - ]; - }} + menuItemRender={(menuItemProps, defaultDom) => ( + {defaultDom} + )} + breadcrumbRender={(routers = []) => [ + { + path: '/', + breadcrumbName: formatMessage({ + id: 'menu.home', + defaultMessage: 'Home', + }), + }, + ...routers, + ]} footerRender={footerRender} menuDataRender={menuDataRender} formatMessage={formatMessage} diff --git a/src/layouts/UserLayout.less b/src/layouts/UserLayout.less old mode 100644 new mode 100755 index 96998cee7034781ee85a80d88d40a19f5c1837c5..cdc207ef66a07f9b5895182cff8c8b6d48c9c3da --- a/src/layouts/UserLayout.less +++ b/src/layouts/UserLayout.less @@ -32,7 +32,7 @@ } .content { - padding: 32px 0 24px 0; + padding: 32px 0 24px; } } diff --git a/src/layouts/UserLayout.tsx b/src/layouts/UserLayout.tsx index aecf7e9f231800a81e1ecef872843e6a96aa0969..6f916fbc4f704158f1765ff208d9fe0744f16341 100644 --- a/src/layouts/UserLayout.tsx +++ b/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 }; diff --git a/src/locales/en-US.ts b/src/locales/en-US.ts index 4ecc7fa7d043ed55708f44dc29c75712008b188f..4ff9776f286fe3f093682851ebf8269c624d3e67 100644 --- a/src/locales/en-US.ts +++ b/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', diff --git a/src/locales/pt-BR.ts b/src/locales/pt-BR.ts index 3ba8ef133ca9db5623f4471903c752af4ee17a4a..ee3733bb575f6230726b2034975fc70400b68bec 100644 --- a/src/locales/pt-BR.ts +++ b/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', diff --git a/src/locales/zh-CN.ts b/src/locales/zh-CN.ts index ec02f5e31e477e1ca0b1f909970a571cf149a07e..b37ec0f91ca9a38603abaea9bcbfe6f55aedcb02 100644 --- a/src/locales/zh-CN.ts +++ b/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': '语言', diff --git a/src/locales/zh-TW.ts b/src/locales/zh-TW.ts index 1eeaffe20554498a045e3ea449beb97b449b930d..6ad5f931927b7a1ba073620ca58518d098713ed6 100644 --- a/src/locales/zh-TW.ts +++ b/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': '語言', diff --git a/src/models/connect.d.ts b/src/models/connect.d.ts index e6327ded406ae07c5a0886d9a9921ab3702918be..3956c48d3c0df001a310bc512eb7bef1cdc2f82e 100644 --- a/src/models/connect.d.ts +++ b/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: (func: (state: ConnectState) => T) => T }, -) => void; +import { UserModelState } from './user'; -/** - * @type P: Type of payload - * @type C: Type of callback - */ -export type Dispatch =

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: (func: (state: ConnectState) => T) => T }, +) => void; + +/** + * @type P: Type of payload + * @type C: Type of callback + */ +export type Dispatch =

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 extends Partial> { dispatch?: Dispatch; } - -export default ConnectState; diff --git a/src/models/global.ts b/src/models/global.ts index 24a32d106f9c3d98d305ec28c7423792543eaca0..367ac486171ffd9bac371993b0e1d4822a039895 100644 --- a/src/models/global.ts +++ b/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); } diff --git a/src/models/login.ts b/src/models/login.ts index 18dfb971529859bc2be8d7eeb9125b23402c59ce..4e250a79cbb09187244f5d2a31045fd3a90bcb6f 100644 --- a/src/models/login.ts +++ b/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]); } diff --git a/src/models/setting.ts b/src/models/setting.ts index 8e2695bd53a88784e7ab09d467523e791f3cca82..15beb48e1c3b5a06b548cb7c32811a949920111c 100644 --- a/src/models/setting.ts +++ b/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'); diff --git a/src/models/user.ts b/src/models/user.ts index 8f3b208a872d33535cab474a1b8e9b1175a2ae0d..426ff9aa5d14cb9c766c1dc83c26e7c66022878a 100644 --- a/src/models/user.ts +++ b/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; diff --git a/src/pages/Authorized.tsx b/src/pages/Authorized.tsx index 6f502d36848ed9097ed63ec29f52e33e2e25628b..9951b11e70ae6c5dc40ebf0c985290565b585097 100644 --- a/src/pages/Authorized.tsx +++ b/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 = ({ route = { routes: [], }, - location, + location = { + pathname: '', + }, user, }) => { const { currentUser } = user; @@ -37,7 +39,7 @@ const AuthComponent: React.FC = ({ const isLogin = currentUser && currentUser.name; return ( : } > {children} diff --git a/src/pages/Welcome.tsx b/src/pages/Welcome.tsx index 2d394094c997a1f51b10264364fade3bb1afe4b8..33dd2f53f2840f0bd79d620d56702268a8494b8f 100644 --- a/src/pages/Welcome.tsx +++ b/src/pages/Welcome.tsx @@ -1,6 +1,6 @@ import React from 'react'; -export default () => ( +export default (): React.ReactNode => (

Want to add more pages? Please refer to{' '} diff --git a/src/service-worker.js b/src/service-worker.js index 3ba9780b556532c4b2f3a5cdd39e604819620d0a..8ff92b6c9a2b601b5b88707c6a52ad1942af758b 100644 --- a/src/service-worker.js +++ b/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 || []); /** diff --git a/src/typings.d.ts b/src/typings.d.ts index 9606b9d77710f81ebe7b6598a1fab1b3602f3526..fff79b4347eb239612e60fb3129d76ad766f1722 100644 --- a/src/typings.d.ts +++ b/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; diff --git a/src/utils/Authorized.ts b/src/utils/Authorized.ts index 71ab611b9cfee152ac4a11b37fc300e1fb350e91..807fc1feb12acb959ef41e09aaa8c89515bdb4d4 100644 --- a/src/utils/Authorized.ts +++ b/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()); }; diff --git a/src/utils/authority.test.ts b/src/utils/authority.test.ts index 6401291775e9f32016fbb83f3948e1f606af56b8..44d74bbd30b2e93647be8f69f028e9ade87d7102 100644 --- a/src/utils/authority.test.ts +++ b/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']); }); diff --git a/src/utils/authority.ts b/src/utils/authority.ts index bf0fa80609629b29d4710982a49f358ad3a8a266..35c1f38643726afed327bb44f0f94a1c36255cc4 100644 --- a/src/utils/authority.ts +++ b/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']; } diff --git a/src/utils/request.ts b/src/utils/request.ts index 128596009f8eed1cbff4adbd36df58eaaa65aca3..3babf7e7c0d7951585ac8525d7bbffe47c316183 100644 --- a/src/utils/request.ts +++ b/src/utils/request.ts @@ -5,12 +5,6 @@ import { extend } from 'umi-request'; import { notification } from 'antd'; -interface ResponseError 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, + }); + } }; /** diff --git a/src/utils/utils.test.ts b/src/utils/utils.test.ts index 3828b880ff0eb8106fa233faf2b252128e4cc0fb..c2bb32dc70dbfb874408675f9e551ca56d5ac434 100644 --- a/src/utils/utils.test.ts +++ b/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(); diff --git a/src/utils/utils.ts b/src/utils/utils.ts index e7c70ce38f9a2051f685688ad83c35c4b308c543..6cbf7d57479353ff6c87485e561b19a93b204df9 100644 --- a/src/utils/utils.ts +++ b/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') { diff --git a/tests/run-tests.js b/tests/run-tests.js index a562c52403abe0033d3beb5418eed39fd2a32fdb..ea531ef1d014e8a64688acbcd1779dad2120b9ed 100644 --- a/tests/run-tests.js +++ b/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'); diff --git a/tslint.yml b/tslint.yml deleted file mode 100644 index 7f4cc04dfa53ecbfe2b3be750cf578a57a21ee63..0000000000000000000000000000000000000000 --- a/tslint.yml +++ /dev/null @@ -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