陈帅 7 years ago
parent
commit
69d4f9afd6
  1. 7
      .eslintrc.js
  2. 5
      .prettierignore
  3. 20
      .prettierrc
  4. 5
      .prettierrc.js
  5. 5
      .stylelintrc.js
  6. 13
      .stylelintrc.json
  7. 125
      create-umi/package.json
  8. 27
      package.json
  9. 2
      src/components/Authorized/CheckPermissions.tsx
  10. 11
      src/components/Authorized/PromiseRender.tsx
  11. 14
      src/components/Authorized/Secured.tsx
  12. 12
      src/components/CopyBlock/index.tsx
  13. 1
      src/components/GlobalHeader/AvatarDropdown.tsx
  14. 23
      src/components/HeaderSearch/index.tsx
  15. 7
      src/components/NoticeIcon/NoticeList.tsx
  16. 18
      src/components/SettingDrawer/themeColorClient.ts
  17. 2
      src/e2e/baseLayout.e2e.js
  18. 35
      src/layouts/BasicLayout.tsx
  19. 1
      src/layouts/UserLayout.tsx
  20. 3
      src/models/global.ts
  21. 1
      src/models/user.ts
  22. 1
      src/pages/Authorized.tsx
  23. 3
      src/typings.d.ts
  24. 5
      src/utils/authority.ts
  25. 4
      src/utils/utils.ts

7
.eslintrc.js

@ -1,7 +1,10 @@
const eslintConfig = require('umi-lint/config/.eslintrc.js');
const fabric = require('@umijs/fabric');
module.exports = {
...eslintConfig,
...fabric.default,
rules: {
...fabric.default.rules,
},
globals: {
ANT_DESIGN_PRO_ONLY_DO_NOT_USE_IN_YOUR_PRODUCTION: true,
page: true,

5
.prettierignore

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

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

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

5
.stylelintrc.js

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

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

125
create-umi/package.json

@ -1,125 +0,0 @@
{
"name": "ant-design-pro",
"version": "4.0.0",
"private": true,
"description": "An out-of-box UI solution for enterprise applications",
"scripts": {
"analyze": "cross-env ANALYZE=1 umi build",
"build": "umi build",
"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",
"lint:js": "eslint --ext .js src tests",
"lint:prettier": "check-prettier lint",
"lint:style": "stylelint --fix \"src/**/*.less\" --syntax less",
"prettier": " check-prettier write",
"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",
"fetch:blocks": "node ./scripts/fetch-blocks.js"
},
"husky": {
"hooks": {
"pre-commit": "npm run lint-staged"
}
},
"lint-staged": {
"**/*.less": "stylelint --syntax less",
"**/*.{js,jsx}": "npm run lint-staged:js",
"**/*.{js,ts,tsx,md,json,jsx,less}": [ "npm run prettier", "git add" ],
"**/*.{ts,tsx}": "npm run lint-staged:ts"
},
"browserslist": [ "> 1%", "last 2 versions", "not ie <= 10" ],
"dependencies": {
"@ant-design/pro-layout": "^4.4.2",
"@antv/data-set": "^0.10.2",
"antd": "^3.19.1",
"bizcharts": "^3.5.3-beta.0",
"bizcharts-plugin-slider": "^2.1.1-beta.1",
"classnames": "^2.2.6",
"dva": "^2.4.1",
"lodash": "^4.17.11",
"lodash-decorators": "^6.0.1",
"memoize-one": "^5.0.4",
"moment": "^2.24.0",
"numeral": "^2.0.6",
"omit.js": "^1.0.2",
"path-to-regexp": "^3.0.0",
"qs": "^6.7.0",
"rc-animate": "^2.8.3",
"react": "^16.8.6",
"react-container-query": "^0.11.0",
"react-copy-to-clipboard": "^5.0.1",
"react-document-title": "^2.0.3",
"react-dom": "^16.8.6",
"react-fittext": "^1.0.0",
"react-media": "^1.9.2",
"react-media-hook2": "^1.0.5"
},
"devDependencies": {
"@types/classnames": "^2.2.7",
"@types/history": "^4.7.2",
"@types/lodash": "^4.14.133",
"@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",
"antd-theme-webpack-plugin": "^1.2.0",
"babel-eslint": "^10.0.1",
"chalk": "^2.4.2",
"check-prettier": "^1.0.3",
"cross-env": "^5.2.0",
"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",
"jest-puppeteer": "^4.2.0",
"jsdom-global": "^3.0.2",
"less": "^3.9.0",
"lint-staged": "^8.1.7",
"merge-umi-mock-data": "^2.0.6",
"mockjs": "^1.0.1-beta3",
"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",
"umi": "^2.7.0-beta.2",
"umi-plugin-ga": "^1.1.3",
"umi-plugin-pro-block": "^1.3.2",
"umi-plugin-react": "^1.8.0-beta.1",
"umi-request": "^1.0.7"
},
"optionalDependencies": {
"puppeteer": "^1.17.0"
},
"engines": {
"node": ">=10.0.0"
},
"checkFiles": [
"src/**/*.js*",
"src/**/*.ts*",
"src/**/*.less",
"config/**/*.js*",
"scripts/**/*.js"
]
}

27
package.json

@ -24,7 +24,7 @@
"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",
"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",
@ -90,8 +90,7 @@
"@types/react": "^16.8.19",
"@types/react-document-title": "^2.0.3",
"@types/react-dom": "^16.8.4",
"@typescript-eslint/eslint-plugin": "^1.9.0",
"antd-pro-merge-less": "^1.0.0",
"@umijs/fabric": "^1.0.4",
"babel-eslint": "^10.0.1",
"chalk": "^2.4.2",
"check-prettier": "^1.0.3",
@ -99,20 +98,6 @@
"cross-port-killer": "^1.1.1",
"enzyme": "^3.9.0",
"eslint": "^5.16.0",
"eslint-config-airbnb": "^17.1.0",
"eslint-config-airbnb-typescript": "^4.0.0",
"eslint-config-prettier": "^4.1.0",
"eslint-formatter-pretty": "^2.1.1",
"eslint-plugin-babel": "^5.3.0",
"eslint-plugin-compat": "^3.1.1",
"eslint-plugin-eslint-comments": "^3.1.1",
"eslint-plugin-import": "^2.17.3",
"eslint-plugin-jest": "^22.4.1",
"eslint-plugin-jsx-a11y": "^6.2.0",
"eslint-plugin-markdown": "^1.0.0",
"eslint-plugin-promise": "^4.1.1",
"eslint-plugin-react": "^7.12.4",
"eslint-plugin-unicorn": "^8.0.1",
"express": "^4.17.1",
"gh-pages": "^2.0.1",
"husky": "^2.3.0",
@ -129,14 +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",
"umi-lint": "^2.0.1",
"webpack-theme-color-replacer": "^1.1.5"
},
"optionalDependencies": {

2
src/components/Authorized/CheckPermissions.tsx

@ -7,7 +7,7 @@ export type IAuthorityType =
| undefined
| string
| string[]
| Promise<any>
| Promise<boolean>
| ((currentAuthority: string | string[]) => IAuthorityType);
/**

11
src/components/Authorized/PromiseRender.tsx

@ -7,11 +7,11 @@ import { isComponentClass } from './Secured';
interface PromiseRenderProps<T, K> {
ok: T;
error: K;
promise: Promise<any>;
promise: Promise<boolean>;
}
interface PromiseRenderState {
component: React.ComponentClass<any, any> | React.FunctionComponent<any>;
component: React.ComponentClass | React.FunctionComponent;
}
export default class PromiseRender<T, K> extends React.Component<
@ -58,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)) {
@ -73,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} />
) : (

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

12
src/components/CopyBlock/index.tsx

@ -6,25 +6,21 @@ import { connect } from 'dva';
import { isAntDesignPro } from '@/utils/utils';
import styles from './index.less';
const firstUpperCase = (pathString: string): string => {
return pathString
const firstUpperCase = (pathString: string): string =>
pathString
.replace('.', '')
.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 {
ga: Function;
}).ga;
const ga = window && (window as any).ga;
if (ga) {
ga('send', 'event', {
eventCategory: 'block',

1
src/components/GlobalHeader/AvatarDropdown.tsx

@ -1,6 +1,5 @@
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';

23
src/components/HeaderSearch/index.tsx

@ -1,7 +1,7 @@
import { AutoComplete, Icon, Input } from 'antd';
import React, { Component } from 'react';
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';
@ -76,14 +76,16 @@ export default class HeaderSearch extends Component<HeaderSearchProps, HeaderSea
}
};
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);
}
}
};
@ -118,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)}
@ -135,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 => {

7
src/components/NoticeIcon/NoticeList.tsx

@ -7,16 +7,15 @@ 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;

18
src/components/SettingDrawer/themeColorClient.js → src/components/SettingDrawer/themeColorClient.ts

@ -4,21 +4,23 @@ 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;

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

35
src/layouts/BasicLayout.tsx

@ -32,19 +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 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()) {
@ -102,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
src/layouts/UserLayout.tsx

@ -1,6 +1,5 @@
import { ConnectProps, ConnectState } from '@/models/connect';
import { DefaultFooter, MenuDataItem, getMenuData, getPageTitle } from '@ant-design/pro-layout';
import DocumentTitle from 'react-document-title';
import Link from 'umi/link';
import React from 'react';

3
src/models/global.ts

@ -7,7 +7,6 @@ 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: {

1
src/models/user.ts

@ -9,7 +9,6 @@ export interface CurrentUser {
title?: string;
group?: string;
signature?: string;
geographic?: any;
tags?: {
key: string;
label: string;

1
src/pages/Authorized.tsx

@ -1,5 +1,4 @@
import { ConnectProps, ConnectState, Route, UserModelState } from '@/models/connect';
import Authorized from '@/utils/Authorized';
import React from 'react';
import Redirect from 'umi/redirect';

3
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';
@ -20,8 +19,10 @@ 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
src/utils/authority.ts

@ -1,5 +1,5 @@
// 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;
@ -15,7 +15,8 @@ export function getAuthority(str?: string): any {
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'];
}

4
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') {

Loading…
Cancel
Save