) => {
67 | setOpen(!open);
68 | if (onClick) {
69 | onClick(event);
70 | }
71 | }, [open, onClick]);
72 | return (
73 |
74 |
84 | {iconJsx}
85 |
86 | {title}
87 |
88 |
92 |
93 |
94 |
95 | {
96 | React.Children.map(children, child => (isListItem(child) ? React.cloneElement(child as React.ReactElement, {
97 | style: {paddingLeft: isMenuOpen ? pl : (theme.overrides?.MuiListItem?.gutters as any).paddingLeft},
98 | }) : child))
99 | }
100 |
101 |
102 |
103 | );
104 | };
105 |
106 | export default SubList;
107 |
--------------------------------------------------------------------------------
/scripts/webpack/webpack.dll.conf.js:
--------------------------------------------------------------------------------
1 | const webpack = require('webpack');
2 | const path = require('path');
3 | const merge = require('webpack-merge');
4 | const HappyPack = require('happypack');
5 | const CleanWebpackPlugin = require('clean-webpack-plugin');
6 | // const UglifyJSPlugin = require('uglifyjs-webpack-plugin');
7 |
8 | const TerserPlugin = require('terser-webpack-plugin');
9 | const OptimizeCSSAssetsPlugin = require('optimize-css-assets-webpack-plugin');
10 |
11 | // const baseConfig = require('./webpack.base.conf');
12 | const { getPath, getArg } = require('./utils');
13 | const { dllPath: dllBasePath } = require('./config');
14 |
15 | const { project } = getArg();
16 |
17 | // const { channel, project } = getArg();
18 | const dllVersion = '1.0.0';
19 | /**
20 | * @param {*} mode = ['development', 'production']
21 | */
22 | module.exports = mode => {
23 | const dllPath = path.resolve(dllBasePath, mode);
24 | // getPath('scripts', 'dll', mode);
25 | const isDev = mode === 'development';
26 | return {
27 | entry: {
28 | utilsDll: [
29 | 'core-js/stable',
30 | 'regenerator-runtime/runtime',
31 | 'axios',
32 | 'convert-key',
33 | 'delay-load',
34 | // 'natur-persist',
35 | // 'natur-service',
36 | 'dayjs',
37 | 'qs',
38 | // 'color',
39 | // 'lodash/curry',
40 | // 'lodash/cloneDeep',
41 | // 'lodash/fp/pipe',
42 | // 'lodash/fp/curry',
43 | // 'react-motion',
44 | ],
45 | baseDll: [
46 | 'react',
47 | 'react-dom',
48 | 'react-router',
49 | 'react-router-dom',
50 | 'hoist-non-react-statics',
51 | 'history',
52 | 'classnames',
53 | // 'natur',
54 | // 'react-redux',
55 | // 'redux',
56 | // 'redux-thunk',
57 | // 'reselect',
58 | ],
59 | // fpDll: [
60 | // 'lodash/curry',
61 | // 'lodash/cloneDeep',
62 | // 'lodash/fp/pipe',
63 | // ],
64 | },
65 | mode,
66 | devtool: isDev ? 'eval-source-map' : false,
67 | output: {
68 | filename: `[name]_${dllVersion}.dll.js`,
69 | path: dllPath,
70 | library: '[name]',
71 | },
72 | module: {
73 | rules: [
74 | {
75 | test: /\.js$/,
76 | include: [getPath(project)],
77 | // loader: 'babel-loader',
78 | loader: 'happypack/loader?id=babel',
79 | },
80 | {
81 | test: /\.(s?css)$/,
82 | use: [
83 | {
84 | loader: 'style-loader',
85 | },
86 | {
87 | loader: 'css-loader',
88 | },
89 | {
90 | loader: 'postcss-loader',
91 | },
92 | {
93 | loader: 'sass-loader',
94 | },
95 | ],
96 | },
97 | ],
98 | },
99 | optimization: {
100 | minimizer: [
101 | new TerserPlugin({
102 | test: /\.js(\?.*)?$/i,
103 | parallel: true,
104 | cache: true,
105 | terserOptions: {
106 | output: {
107 | comments: false,
108 | },
109 | compress: {
110 | warnings: false,
111 | drop_debugger: true,
112 | drop_console: true,
113 | },
114 | },
115 | }),
116 | new OptimizeCSSAssetsPlugin(),
117 | ],
118 | },
119 | plugins: [
120 | new HappyPack({
121 | id: 'babel',
122 | threads: 1,
123 | loaders: [
124 | {
125 | loader: 'babel-loader',
126 | cacheDirectory: true,
127 | },
128 | ],
129 | }),
130 | new webpack.DllPlugin({
131 | name: '[name]', // json文件名
132 | path: path.join(dllPath, '[name].json'), // 生成映射表json文件地址
133 | }),
134 | // 删除文件
135 | new CleanWebpackPlugin(),
136 | ],
137 | };
138 | };
139 |
--------------------------------------------------------------------------------
/mock-server/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | [travis-image]: https://api.travis-ci.org/nestjs/nest.svg?branch=master
6 | [travis-url]: https://travis-ci.org/nestjs/nest
7 | [linux-image]: https://img.shields.io/travis/nestjs/nest/master.svg?label=linux
8 | [linux-url]: https://travis-ci.org/nestjs/nest
9 |
10 | A progressive Node.js framework for building efficient and scalable server-side applications, heavily inspired by Angular.
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
26 |
27 | ## Description
28 |
29 | [Nest](https://github.com/nestjs/nest) framework TypeScript starter repository.
30 |
31 | ## Installation
32 |
33 | ```bash
34 | $ npm install
35 | ```
36 |
37 | ## Running the app
38 |
39 | ```bash
40 | # development
41 | $ npm run start
42 |
43 | # watch mode
44 | $ npm run start:dev
45 |
46 | # production mode
47 | $ npm run start:prod
48 | ```
49 |
50 | ## Test
51 |
52 | ```bash
53 | # unit tests
54 | $ npm run test
55 |
56 | # e2e tests
57 | $ npm run test:e2e
58 |
59 | # test coverage
60 | $ npm run test:cov
61 | ```
62 |
63 | ## Support
64 |
65 | Nest is an MIT-licensed open source project. It can grow thanks to the sponsors and support by the amazing backers. If you'd like to join them, please [read more here](https://docs.nestjs.com/support).
66 |
67 | ## Stay in touch
68 |
69 | - Author - [Kamil Myśliwiec](https://kamilmysliwiec.com)
70 | - Website - [https://nestjs.com](https://nestjs.com/)
71 | - Twitter - [@nestframework](https://twitter.com/nestframework)
72 |
73 | ## License
74 |
75 | Nest is [MIT licensed](LICENSE).
76 |
--------------------------------------------------------------------------------
/src/theme/unit.ts:
--------------------------------------------------------------------------------
1 | // import r from './r';
2 |
3 | import { Options } from 'jss-plugin-default-unit';
4 |
5 | const px = 'px';
6 | const ms = 'ms';
7 | const percent = '%';
8 | // const vw = r;
9 | const current = px;
10 | /**
11 | * Generated jss-plugin-default-unit CSS property units
12 | *
13 | * @type object
14 | */
15 | const options: Options = {
16 | // Animation properties
17 | 'animation-delay': ms,
18 | 'animation-duration': ms,
19 |
20 | // Background properties
21 | 'background-position': current,
22 | 'background-position-x': current,
23 | 'background-position-y': current,
24 | 'background-size': current,
25 |
26 | // Border Properties
27 | border: px,
28 | 'border-bottom': px,
29 | 'border-bottom-left-radius': px,
30 | 'border-bottom-right-radius': px,
31 | 'border-bottom-width': px,
32 | 'border-left': px,
33 | 'border-left-width': px,
34 | 'border-radius': px,
35 | 'border-right': px,
36 | 'border-right-width': px,
37 | 'border-top': px,
38 | 'border-top-left-radius': px,
39 | 'border-top-right-radius': px,
40 | 'border-top-width': px,
41 | 'border-width': px,
42 |
43 | // Margin properties
44 | margin: current,
45 | 'margin-bottom': current,
46 | 'margin-left': current,
47 | 'margin-right': current,
48 | 'margin-top': current,
49 |
50 | // Padding properties
51 | padding: current,
52 | 'padding-bottom': current,
53 | 'padding-left': current,
54 | 'padding-right': current,
55 | 'padding-top': current,
56 |
57 | // Mask properties
58 | 'mask-position-x': current,
59 | 'mask-position-y': current,
60 | 'mask-size': current,
61 |
62 | // Width and height properties
63 | height: current,
64 | width: current,
65 | 'min-height': current,
66 | 'max-height': current,
67 | 'min-width': current,
68 | 'max-width': current,
69 |
70 | // Position properties
71 | bottom: current,
72 | left: current,
73 | top: current,
74 | right: current,
75 |
76 | // Shadow properties
77 | 'box-shadow': current,
78 | 'text-shadow': current,
79 |
80 | // Column properties
81 | 'column-gap': current,
82 | 'column-rule': current,
83 | 'column-rule-width': current,
84 | 'column-width': current,
85 |
86 | // Font and text properties
87 | 'font-size': current,
88 | 'font-size-delta': current,
89 | 'letter-spacing': current,
90 | 'text-indent': current,
91 | 'text-stroke': current,
92 | 'text-stroke-width': current,
93 | 'word-spacing': current,
94 |
95 | // Motion properties
96 | motion: current,
97 | 'motion-offset': current,
98 |
99 | // Outline properties
100 | outline: current,
101 | 'outline-offset': current,
102 | 'outline-width': current,
103 |
104 | // Perspective properties
105 | perspective: current,
106 | 'perspective-origin-x': percent,
107 | 'perspective-origin-y': percent,
108 |
109 | // Transform properties
110 | 'transform-origin': percent,
111 | 'transform-origin-x': percent,
112 | 'transform-origin-y': percent,
113 | 'transform-origin-z': percent,
114 |
115 | // Transition properties
116 | 'transition-delay': ms,
117 | 'transition-duration': ms,
118 |
119 | // Alignment properties
120 | 'vertical-align': current,
121 | 'flex-basis': current,
122 |
123 | // Some random properties
124 | 'shape-margin': current,
125 | size: current,
126 |
127 | // Grid properties
128 | grid: current,
129 | 'grid-gap': current,
130 | 'grid-row-gap': current,
131 | 'grid-column-gap': current,
132 | 'grid-template-rows': current,
133 | 'grid-template-columns': current,
134 | 'grid-auto-rows': current,
135 | 'grid-auto-columns': current,
136 |
137 | // Not existing properties.
138 | // Used to avoid issues with jss-plugin-expand integration.
139 | 'box-shadow-x': current,
140 | 'box-shadow-y': current,
141 | 'box-shadow-blur': current,
142 | 'box-shadow-spread': current,
143 | 'font-line-height': current,
144 | 'text-shadow-x': current,
145 | 'text-shadow-y': current,
146 | 'text-shadow-blur': current,
147 | };
148 |
149 |
150 | export default options;
151 |
--------------------------------------------------------------------------------
/src/http/index.ts:
--------------------------------------------------------------------------------
1 | import axios, { AxiosRequestConfig } from 'axios';
2 | import { dateFormatting } from '@/utils';
3 | import SHA256 from 'crypto-js/sha256';
4 | import channel from './config';
5 | import history from '../routes/history';
6 |
7 | const { server: serverUrl } = channel;
8 |
9 |
10 | // 字典排序
11 | const azSort = (data: {[p: string]: any}) => {
12 | const obj: {[p: string]: any} = {};
13 | Object.keys(data)
14 | .sort()
15 | .forEach(i => {
16 | obj[i] = data[i];
17 | });
18 | let tmpStr = '';
19 | /* eslint-disable */
20 | for (const i in obj) {
21 | tmpStr += `${i}=${obj[i]}&`;
22 | }
23 | /* eslint-enable */
24 | tmpStr = tmpStr.substr(0, tmpStr.length - 1);
25 | // console.log('----->.....', tmpStr);
26 | return tmpStr;
27 | };
28 |
29 | const createRequestId = () => Math.random().toString(36).substr(2, 12) + Date.now();
30 |
31 | // 生成唯一串
32 | let requestId: string;
33 | const refreshId = () => requestId = createRequestId();
34 |
35 | history.listen(refreshId);
36 |
37 | // 拼接参数
38 | // requestNo规则: 请求参数+url+authCode+ Uid + time => SHA256 保证唯一且同一请求不变
39 | const appendParams = (data = {}, url: string) => ({
40 | chnId: '10003',
41 | requestType: 'PC',
42 | requestNo: SHA256(`${JSON.stringify(data)}${url}${window.localStorage.authCode ? window.localStorage.authCode : ''}${requestId}`).toString().substring(0, 32),
43 | requestTime: dateFormatting('yyyy-MM-dd hh:mm:ss', new Date().toString()),
44 | authCode: window.localStorage.authCode ? window.localStorage.authCode : '',
45 | });
46 |
47 | // 默认配置
48 | const config = {
49 | baseURL: serverUrl,
50 | headers: {
51 | 'Content-Type': 'application/json;charset=utf-8',
52 | },
53 | timeout: 1000 * 20,
54 | withCredentials: true,
55 | };
56 | // 创建实例对象
57 | const instance = axios.create(config);
58 |
59 | const createRequestRecords = (requestNo: string) => ({
60 | requestNo,
61 | timestamp: Date.now(),
62 | });
63 |
64 | type TRequestCache = {
65 | [p: string]: {
66 | requestNo: string;
67 | timestamp: number;
68 | }
69 | };
70 | const timeLimit = 666;// 前端接口请求防重复点击阀值
71 | const requestCache: TRequestCache = {};
72 | interface IAxiosRequestConfig extends AxiosRequestConfig {
73 | loading?: boolean;
74 | }
75 | // 请求拦截器
76 | instance.interceptors.request.use((requestConfig: IAxiosRequestConfig) => {
77 | // 请求之前,搞些事情
78 | const { loading = true, data, url: _url } = requestConfig;
79 | const url = _url || '';
80 | let params = appendParams(data, url);
81 | // 前端防重判断
82 | if (requestCache[url] && requestCache[url].requestNo === params.requestNo) {
83 | const { timestamp } = requestCache[url];
84 | // const { timeLimit } = requestCache;
85 | const now = Date.now();
86 | if (now - timestamp < timeLimit) {
87 | requestCache[url].timestamp = now;
88 | throw new Error('请勿重复操作!');
89 | } else {
90 | refreshId();
91 | params = appendParams(data, url);
92 | }
93 | }
94 | requestCache[url] = createRequestRecords(params.requestNo);
95 | const beforeSort = { ...data, ...params }; // 拼接参数
96 | const afterSort = azSort(beforeSort); // 参数排序
97 |
98 | const signData = SHA256(afterSort); // 参数加密
99 | requestConfig.data = { ...beforeSort, sign: signData };
100 |
101 | return requestConfig;
102 | },
103 | error => Promise.reject(error));
104 |
105 | // 响应拦截器
106 | instance.interceptors.response.use(
107 | response => {
108 | // nativeLog(response);
109 | // 服务端响应成功时,搞些事情
110 | // console.log(`请求成功----->${JSON.stringify(response)}`);
111 | const { responseCode } = response.data;
112 | // console.log('-----.', responseCode);
113 | if (responseCode === '3001') {
114 | // message.error('登录超时,请重新登录', 1);
115 | history.replace('/login');
116 | }
117 | if (responseCode !== '200') {
118 | throw response; // eslint-disable-line
119 | }
120 | // console.log('-----.', responseCode);
121 | return response;
122 | },
123 | error => {
124 | // 服务端响应失败时,搞些事情
125 | if (error.message) {
126 | // message.error(error.message, 2);
127 | } else {
128 | // message.error('请求失败,请重试', 2);
129 | refreshId();
130 | }
131 | return Promise.reject(error);
132 | },
133 | );
134 |
135 | export default instance;
136 |
--------------------------------------------------------------------------------
/src/components/base/DynamicForm/style.scss:
--------------------------------------------------------------------------------
1 | @import '~@site/theme/style';
2 |
3 | .text {
4 | display: inline-block;
5 | overflow: hidden;
6 | text-align: left;
7 | text-overflow: ellipsis;
8 | white-space: nowrap;
9 | user-select: all;
10 | color: #333;
11 | }
12 |
13 | :global {
14 | $borderColor: $defaultMainColor;
15 | $hoverColor: lighten($defaultMainColor, 40);
16 | $shadow: 0 0 0 2px lighten($defaultMainColor, 40);
17 | .dynamic-form {
18 | .small {
19 | width: 200px;
20 | height: 24px;
21 | }
22 | // .ant-form-explain {
23 | // display: table-cell;
24 | // }
25 | }
26 | @mixin inputActiveBorder($color) {
27 | .ant-input:not([disabled]):hover,
28 | .ant-input:focus {
29 | border-color: $color !important;
30 | box-shadow: 0 0 0 2px lighten($color, 40) !important;
31 | }
32 | }
33 | @include inputActiveBorder($defaultMainColor);
34 | .has-error {
35 | @include inputActiveBorder(#f5222d);
36 | }
37 | .ant-form-item-label > label,
38 | .ant-form-item, {
39 | color: #696969;
40 | }
41 | .ant-input-lg,
42 | .ant-select-lg {
43 | font-size: 14px!important;
44 | color: #696969!important;
45 | }
46 | .ant-form-item-label label:after {
47 | position: relative;
48 | top: -0.5px;
49 | margin: 0 8px 0 2px;
50 | content: '';
51 | }
52 | .ant-form-explain {
53 | text-align: left;
54 | }
55 | .is-validating.has-feedback .ant-form-item-children-icon {
56 | color: $defaultMainColor;
57 | }
58 |
59 | // 日期input 样式覆盖
60 | .ant-calendar-today .ant-calendar-date {
61 | border-color: $borderColor !important;
62 | color: $defaultMainColor !important;
63 | }
64 | .ant-calendar-selected-date .ant-calendar-date {
65 | background: $defaultMainColor !important;
66 | }
67 | .ant-calendar-date:hover,
68 | .ant-calendar-selected-day .ant-calendar-date {
69 | background: lighten($defaultMainColor, 40) !important;
70 | }
71 | .ant-calendar-header a:hover {
72 | color: $defaultMainColor !important;
73 | }
74 | .ant-calendar-picker:hover .ant-calendar-picker-input:not(.ant-input-disabled) {
75 | border-color: $borderColor !important;
76 | }
77 | .ant-calendar-tbody .ant-calendar-active-week .ant-calendar-today .ant-calendar-date {
78 | padding: 0;
79 | margin-left: 6px;
80 | }
81 | .ant-calendar-tbody .ant-calendar-active-week .ant-calendar-today .ant-calendar-date:before {
82 | border: none;
83 | }
84 | // select input 样式覆盖
85 | .ant-select-open {
86 | .ant-select-selection {
87 | border-color: $borderColor !important;
88 | box-shadow: $shadow !important;
89 | }
90 | }
91 | .ant-select-focused {
92 | .ant-select-selection,
93 | .ant-select-selection:focus,
94 | .ant-select-selection:active {
95 | border-color: $borderColor !important;
96 | box-shadow: $shadow !important;
97 | }
98 | }
99 | .ant-select-selection:focus,
100 | .ant-select-selection:active {
101 | border-color: $borderColor !important;
102 | box-shadow: $shadow !important;
103 | }
104 | .ant-select-selection:hover {
105 | border-color: $borderColor !important;
106 | }
107 |
108 | .ant-select-dropdown-menu-item:hover,
109 | .ant-select-dropdown-menu-item-active {
110 | background-color: $hoverColor !important;
111 | }
112 | .ant-select-dropdown-menu-item-selected {
113 | background-color: lighten($defaultMainColor, 50) !important;
114 | }
115 |
116 | // checkbox样式覆盖
117 | .ant-checkbox-wrapper:hover .ant-checkbox-inner,
118 | .ant-checkbox-input:focus + .ant-checkbox-inner,
119 | .ant-checkbox:hover .ant-checkbox-inner,
120 | .ant-checkbox-checked .ant-checkbox-inner,
121 | .ant-checkbox-checked:after {
122 | border-color: $borderColor !important;
123 | }
124 | .ant-checkbox-checked .ant-checkbox-inner {
125 | background-color: $defaultMainColor !important;
126 | }
127 |
128 | // radio样式覆盖
129 | .ant-radio-wrapper:hover .ant-radio-inner,
130 | .ant-radio-input:focus + .ant-radio-inner,
131 | .ant-radio:hover .ant-radio-inner,
132 | .ant-radio-checked .ant-radio-inner,
133 | .ant-radio-checked:after {
134 | border-color: $borderColor !important;
135 | }
136 | .ant-radio-checked .ant-radio-inner::after,
137 | .ant-radio-inner:after {
138 | background-color: $defaultMainColor !important;
139 | }
140 | }
141 |
--------------------------------------------------------------------------------
/src/assets/plant-01.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/scripts/webpack/utils.js:
--------------------------------------------------------------------------------
1 | const path = require('path');
2 | const webpack = require('webpack');
3 | const minimist = require('minimist');
4 | const glob = require('glob');
5 | const AddAssetHtmlPlugin = require('add-asset-html-webpack-plugin');
6 | const MiniCssExtractPlugin = require('mini-css-extract-plugin');
7 |
8 | const {
9 | defaultChannel,
10 | defaultProject,
11 | } = require('../../buildConfig/defaultConfig');
12 |
13 | const getPath = (...$path) => path.join(__dirname, '..', '..', ...$path);
14 |
15 | const getArg = () => {
16 | const arg = minimist(process.argv.slice(2));
17 | const res = {
18 | ...arg,
19 | project: arg.project || defaultProject,
20 | channel: arg.channel || defaultChannel,
21 | PROJECT_ENV: arg.PROJECT_ENV || process.env.NODE_ENV || 'production',
22 | };
23 | return res;
24 | };
25 |
26 | module.exports.getPath = getPath;
27 | module.exports.getArg = getArg;
28 |
29 | // dll 通用配置 start
30 | const { dllPath, jsPublicPath, cssPublicPath } = require('./config');
31 |
32 | const { channel, project } = getArg();
33 | const getAddAssethtmlPluginsConfig = mode => glob.sync(`${dllPath}/${mode}/*.dll.js`).map(
34 | dllPath => new AddAssetHtmlPlugin({
35 | filepath: dllPath,
36 | includeSourcemap: false,
37 | typeOfAsset: 'js',
38 | outputPath: 'js',
39 | publicPath: jsPublicPath.endsWith('/') ? `${jsPublicPath}js/` : `${jsPublicPath}/js/`,
40 | }),
41 | );
42 | const getDllReferencePluginsConfig = mode => glob.sync(`${dllPath}/${mode}/*.json`).map(
43 | dllJsonPath => new webpack.DllReferencePlugin({
44 | // name: 'js/reactDll_1.0.0.dll.js',
45 | manifest: dllJsonPath,
46 | }),
47 | );
48 |
49 | module.exports.addDllPluginsConfig = mode => [
50 | // new webpack.DllReferencePlugin({
51 | // // name: 'js/reactDll_1.0.0.dll.js',
52 | // manifest: getPath('server', 'dll', mode, 'reactDll.json'),
53 | // }),
54 | ...getDllReferencePluginsConfig(mode),
55 | ...getAddAssethtmlPluginsConfig(mode),
56 | ];
57 |
58 |
59 | const cssModule = [
60 | new RegExp('src/|\modules'),
61 | new RegExp('src//|\components'),
62 | ]
63 | module.exports.createStyleLoader = (mode, isDev = mode === 'development') => [
64 | {
65 | test: /\.(s?css)$/,
66 | include: cssModule,
67 | use: [
68 | // isDev ? 'style-loader' :
69 | {
70 | loader: MiniCssExtractPlugin.loader,
71 | options: {
72 | hmr: isDev,
73 | publicPath: cssPublicPath,
74 | },
75 | },
76 | {
77 | loader: 'css-loader',
78 | options: {
79 | modules: true,
80 | importLoaders: 1,
81 | localIdentName: isDev
82 | ? '[local]-[hash:base64:8]'
83 | : '[hash:base64:16]',
84 | },
85 | },
86 | isDev ? null : {
87 | loader: 'postcss-loader',
88 | },
89 | {
90 | loader: 'sass-loader',
91 | },
92 | ].filter(Boolean),
93 | },
94 | {
95 | test: /\.(s?css)$/,
96 | exclude: cssModule,
97 | use: [
98 | // isDev ? 'style-loader' :
99 | {
100 | loader: MiniCssExtractPlugin.loader,
101 | options: {
102 | hmr: isDev,
103 | publicPath: cssPublicPath,
104 | },
105 | },
106 | {
107 | loader: 'css-loader',
108 | },
109 | isDev ? null : {
110 | loader: 'postcss-loader',
111 | },
112 | {
113 | loader: 'sass-loader',
114 | },
115 | ].filter(Boolean),
116 | },
117 | {
118 | test: /\.less$/,
119 | include: cssModule,
120 | use: [
121 | // isDev ? 'style-loader' :
122 | {
123 | loader: MiniCssExtractPlugin.loader,
124 | options: {
125 | hmr: isDev,
126 | publicPath: cssPublicPath,
127 | },
128 | },
129 | {
130 | loader: 'css-loader',
131 | options: {
132 | modules: true,
133 | importLoaders: 1,
134 | localIdentName: isDev
135 | ? '[local]-[hash:base64:8]'
136 | : '[hash:base64:16]',
137 | },
138 | },
139 | isDev ? null : {
140 | loader: 'postcss-loader',
141 | },
142 | {
143 | loader: 'less-loader',
144 | },
145 | ].filter(Boolean),
146 | },
147 | {
148 | test: /\.less$/,
149 | exclude: cssModule,
150 | use: [
151 | {
152 | loader: MiniCssExtractPlugin.loader,
153 | options: {
154 | hmr: isDev,
155 | publicPath: cssPublicPath,
156 | },
157 | },
158 | {
159 | loader: 'css-loader',
160 | },
161 | isDev ? null : {
162 | loader: 'postcss-loader',
163 | },
164 | {
165 | loader: 'less-loader',
166 | },
167 | ].filter(Boolean),
168 | },
169 | ];
170 |
171 | module.exports.createStylePlugin = (mode, isDev) => new MiniCssExtractPlugin(isDev ? {
172 | chunkFilename: 'style/[name].[contenthash:8].css',
173 | }: {
174 | filename: 'style/[name].[contenthash:8].css',
175 | });
176 |
177 | // dll 通用配置 end
178 |
--------------------------------------------------------------------------------
/src/components/base/Table/index.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import MUIDataTable, { MUIDataTableProps } from 'mui-datatables';
3 | import TableFooter from '@material-ui/core/TableFooter';
4 | import TableRow from '@material-ui/core/TableRow';
5 | import TableCell from '@material-ui/core/TableCell';
6 | import TablePagination from '@material-ui/core/TablePagination';
7 |
8 |
9 | type TableProps<
10 | D extends {},
11 | RSI extends number | string,
12 | IDName extends keyof D = keyof D,
13 | > = Omit & {
14 | /**
15 | * 分页数据
16 | */
17 | pagination: {
18 | /** 总数据量 */
19 | total: number;
20 | /** 当前页数,从1开始算 */
21 | page: number;
22 | /** 每页的数据有多少行 */
23 | rowsPerPage: number;
24 | },
25 | /**
26 | * 当分页页数改变时
27 | */
28 | onPageChange?: (page: number) => void;
29 | /**
30 | * 当,当前的每页数据有多少行的配置更新时
31 | */
32 | onRowsPerPageChange?: (page: number) => void;
33 | /**
34 | * 当表格选中项更新时
35 | */
36 | onRowSelectionChange?: (newRowsSelected: RSI[]) => void;
37 | /**
38 | * 表格标题
39 | */
40 | title?: string;
41 | /**
42 | * 被选中的项目id数组,可以是index索引,
43 | * 也可以是数据的id,如果是id数据的话,必须要声明idName的值,
44 | * 也就是每行数据的id数据对应的名字
45 | * */
46 | rowsSelected?: RSI[];
47 | /**
48 | * 数据的id对应的名字
49 | */
50 | idName?: IDName;
51 | data: D[];
52 | };
53 |
54 |
55 | const Table = <
56 | D extends {},
57 | RSI extends number | string,
58 | >({
59 | options,
60 | data,
61 | pagination,
62 | onPageChange,
63 | onRowsPerPageChange,
64 | onRowSelectionChange,
65 | idName,
66 | rowsSelected = [],
67 | title = '',
68 | ...restProps
69 | }: TableProps) => {
70 | const _rowsSelected = React.useMemo(() => {
71 | if (idName) {
72 | return data.reduce((idxs, d, currentIndex) => {
73 | if (rowsSelected.indexOf(d[idName] as any) > -1) {
74 | idxs.push(currentIndex);
75 | }
76 | return idxs;
77 | }, [] as number[]);
78 | }
79 | return rowsSelected;
80 | }, [data, idName, rowsSelected]);
81 | const _options = React.useMemo(() => ({
82 | filterType: 'checkbox' as 'checkbox',
83 | jumpToPage: true,
84 | selectToolbarPlacement: 'none',
85 | tableId: 'name',
86 | print: false,
87 | searchable: false,
88 | search: false,
89 | serverSide: true,
90 | download: false,
91 | filter: false,
92 | rowsSelected: _rowsSelected,
93 | rowsPerPageOptions: options?.rowsPerPageOptions || [5, 10, 15, 20, 25],
94 | textLabels: {
95 | selectedRows: {
96 | text: '行已选择',
97 | delete: '删除',
98 | deleteAria: '删除所选数据',
99 | },
100 | toolbar: {
101 | viewColumns: '需要显示的列',
102 | },
103 | viewColumns: {
104 | title: '显示列',
105 | titleAria: '显示/隐藏 表格列',
106 | },
107 | pagination: {
108 | next: '下一页',
109 | previous: '上一页',
110 | rowsPerPage: '每页行数:',
111 | displayRows: '共',
112 | jumpToPage: '跳转到',
113 | },
114 | },
115 | customFooter: (
116 | count: number,
117 | page: number,
118 | rowsPerPage: number,
119 | changeRowsPerPage: any,
120 | changePage: any,
121 | ) => (
122 |
123 |
124 |
125 | `${from}-${to} 共 ${_count !== -1 ? _count : `more than ${to}`} 条数据`}
132 | onChangePage={(event, np: number) => changePage(np)}
133 | onChangeRowsPerPage={e => changeRowsPerPage(e.target.value)}
134 | />
135 |
136 |
137 |
138 |
139 | ),
140 | onChangePage: (np: number) => onPageChange && onPageChange(np + 1),
141 | onChangeRowsPerPage: (size: number) => onRowsPerPageChange && onRowsPerPageChange(size),
142 | onRowSelectionChange: (currentRowsSelected: any, allRowsSelected: any, selectedIndexs: any) => {
143 | if (onRowSelectionChange) {
144 | if (idName) {
145 | // @ts-ignore
146 | onRowSelectionChange(selectedIndexs.map((si: number) => data[si][idName]));
147 | } else {
148 | onRowSelectionChange(selectedIndexs);
149 | }
150 | }
151 | },
152 | ...options,
153 | }), [
154 | _rowsSelected,
155 | options,
156 | pagination.total,
157 | pagination.rowsPerPage,
158 | pagination.page,
159 | onPageChange,
160 | onRowsPerPageChange,
161 | onRowSelectionChange,
162 | idName,
163 | data,
164 | ]);
165 |
166 |
167 | return (
168 |
174 | );
175 | };
176 | export default Table;
177 |
--------------------------------------------------------------------------------
/src/components/business/Menu/index.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import {
3 | Drawer,
4 | List,
5 | ListItem,
6 | ListItemIcon,
7 | ListItemText,
8 | Icon,
9 | } from '@material-ui/core';
10 | import {
11 | makeStyles,
12 | Theme,
13 | ThemeProvider,
14 | } from '@material-ui/core/styles';
15 | import { inject } from '@/store';
16 | import clsx from 'classnames';
17 | import { menuTheme } from '@/theme/material';
18 |
19 | // icon
20 | import SubList from '@biz/SubList';
21 | import { Link, useLocation } from 'react-router-dom';
22 | import sideBarBgImg from '@/assets/sidebar.jpg';
23 |
24 | const drawerWidth = 260;
25 |
26 | const useStyles = makeStyles((theme: Theme) => ({
27 | drawer: {
28 | position: 'relative',
29 | zIndex: 1,
30 | flexShrink: 0,
31 | width: drawerWidth,
32 | whiteSpace: 'nowrap',
33 | },
34 | paper: {
35 | backgroundImage: `url(${sideBarBgImg})`,
36 | backgroundPosition: 'center',
37 | backgroundSize: 'cover',
38 | backgroundColor: '#fff',
39 | },
40 | drawerOpen: {
41 | width: drawerWidth,
42 | transition:
43 | theme.transitions.create('width', {
44 | easing: theme.transitions.easing.sharp,
45 | duration: theme.transitions.duration.leavingScreen,
46 | }),
47 | },
48 | drawerClose: {
49 | width: theme.spacing(8),
50 | overflowX: 'hidden',
51 | transition:
52 | theme.transitions.create('width', {
53 | easing: theme.transitions.easing.sharp,
54 | duration: theme.transitions.duration.leavingScreen,
55 | }),
56 | },
57 | listWrapper: {
58 | width: '100%',
59 | minHeight: '100%',
60 | overflow: 'auto',
61 | overflowX: 'hidden',
62 | backgroundColor: 'rgba(0, 0, 0, 0.6)',
63 | },
64 | list: {
65 | position: 'relative',
66 | zIndex: 4,
67 | padding: '0 10px',
68 | paddingBottom: '100px',
69 | },
70 | }));
71 | const injector = inject([
72 | 'app',
73 | { state: ['isMenuOpen', 'menuData'] },
74 | ]);
75 | const AppMenu: React.FC = ({ app }) => {
76 | const classes = useStyles();
77 | const { isMenuOpen, menuData } = app.state;
78 | const [$open, setOpen] = React.useState(isMenuOpen);
79 | React.useEffect(() => {
80 | if (isMenuOpen === false) {
81 | setOpen(false);
82 | }
83 | }, [isMenuOpen, setOpen]);
84 | const open = React.useMemo(() => {
85 | if (isMenuOpen) {
86 | return isMenuOpen;
87 | }
88 | return $open;
89 | }, [isMenuOpen, $open]);
90 | const openMenu = React.useCallback(() => {
91 | if (!isMenuOpen) {
92 | setOpen(true);
93 | }
94 | }, [setOpen, isMenuOpen]);
95 | const closeMenu = React.useCallback(() => {
96 | if (!isMenuOpen) {
97 | setOpen(false);
98 | }
99 | }, [setOpen, isMenuOpen]);
100 |
101 | const { pathname } = useLocation();
102 | return (
103 |
104 |
120 |
121 |
122 | {menuData.map((item: any) => {
123 | if (!item.children) {
124 | return (
125 |
132 | {item.icon && (
133 |
134 | {item.icon}
135 |
136 | )}
137 |
138 |
139 | );
140 | }
141 | const isSubListSelected = item.children.some(
142 | ({ to }: any) => pathname.includes(to),
143 | );
144 | return (
145 | {item.icon}}
151 | open={isSubListSelected}
152 | >
153 | {item.children.map((subItem: any) => (
154 |
161 | {subItem.icon && (
162 |
163 | {subItem.icon}
164 |
165 | )}
166 |
167 |
168 | ))}
169 |
170 | );
171 | })}
172 |
173 |
174 |
175 |
176 | );
177 | };
178 |
179 | export default injector(AppMenu);
180 |
--------------------------------------------------------------------------------
/src/modules/Page2/index.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | // import Button from '@base/IconButton';
3 | // import Input from '@base/Input';
4 | // import Icon from '@material-ui/core/Icon';
5 | import styles from './style.scss';
6 | import {
7 | Button,
8 | LinearProgress,
9 | FormControlLabel,
10 | Radio,
11 | } from '@material-ui/core';
12 | import { inject } from '@/store';
13 | import { TextField, RadioGroup } from 'formik-material-ui';
14 | import { Field, useFormik, FormikProvider } from 'formik';
15 | import Checkbox from '@/components/base/Checkbox';
16 | import ErrorMsgBoxHOC from '@/components/base/ErrorMsgBoxHOC';
17 | import { DatePicker } from 'formik-material-ui-pickers';
18 | import Table from '@/components/base/Table';
19 |
20 | const injector = inject(
21 | [
22 | 'page2',
23 | {
24 | state: [s => s.rowsSelected],
25 | maps: ['countIsOdd'],
26 | },
27 | ],
28 | [
29 | 'app',
30 | {
31 | state: [s => s.name],
32 | maps: ['getLangData'],
33 | },
34 | ],
35 | );
36 |
37 | type PageProps = typeof injector.type;
38 |
39 | const _RadioGroup = ErrorMsgBoxHOC(RadioGroup);
40 |
41 | const columns = [
42 | {
43 | name: 'name',
44 | label: 'Name',
45 | options: {
46 | filter: true,
47 | sort: true,
48 | },
49 | },
50 | {
51 | name: 'company',
52 | label: 'Company',
53 | options: {
54 | filter: true,
55 | sort: false,
56 | },
57 | },
58 | {
59 | name: 'city',
60 | label: 'City',
61 | options: {
62 | filter: true,
63 | sort: false,
64 | },
65 | },
66 | {
67 | name: 'state',
68 | label: 'State',
69 | options: {
70 | filter: true,
71 | sort: false,
72 | },
73 | },
74 | ];
75 |
76 | const data = [
77 | {
78 | id: '1',
79 | name: 'Joe James',
80 | company: 'Test Corp',
81 | city: 'Yonkers',
82 | state: 'NY',
83 | },
84 | {
85 | id: '2',
86 | name: 'John Walsh',
87 | company: 'Test Corp',
88 | city: 'Hartford',
89 | state: 'CT',
90 | },
91 | {
92 | id: '3',
93 | name: 'Bob Herm',
94 | company: 'Test Corp',
95 | city: 'Tampa',
96 | state: 'FL',
97 | },
98 | {
99 | id: '4',
100 | name: 'James Houston',
101 | company: 'Test Corp',
102 | city: 'Dallas',
103 | state: 'TX',
104 | },
105 | ];
106 |
107 | const Page2: React.FC = ({ page2, app }) => {
108 | const { actions } = page2;
109 | app.actions.closeMenu;
110 | const formikbag = useFormik({
111 | initialValues: {
112 | email: '',
113 | date: '',
114 | password: '',
115 | checked: false,
116 | activity: '',
117 | },
118 | onSubmit: (values, { setSubmitting }) => {
119 | setSubmitting(false);
120 | actions.changePageName(values.email);
121 | // console.log(JSON.stringify(values, null, 2));
122 | },
123 | });
124 | const { isSubmitting, submitForm } = formikbag;
125 |
126 | // const changePage2 = (e: React.ChangeEvent) => actions.changePageName(e.target.value);
127 | return (
128 |
129 |
141 |
142 |
149 |
150 | (!/^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,4}$/i.test(v)
156 | ? 'Invalid email address'
157 | : undefined)}
158 | />
159 |
160 | (!!v ? undefined : 'checked required!')}
165 | />
166 |
167 | (!!v ? undefined : 'acivity required!')}
171 | >
172 | }
175 | label="Painting"
176 | disabled={isSubmitting}
177 | />
178 | }
181 | label="Drawing"
182 | disabled={isSubmitting}
183 | />
184 | }
187 | label="None"
188 | disabled
189 | />
190 |
191 |
192 |
198 | {isSubmitting && }
199 |
200 |
208 |
209 |
210 | );
211 | };
212 |
213 | export default injector(Page2);
214 |
--------------------------------------------------------------------------------
/src/utils/color.js:
--------------------------------------------------------------------------------
1 | import pipe from 'lodash/fp/pipe';
2 | import Color from 'color';
3 |
4 | const hexColorStrWithHeaderReg = /^#(([0-9a-z]{3})|([0-9a-z]{6}))$/i;
5 | const hexColorStrWithOutHeaderReg = /^(([0-9a-z]{3})|([0-9a-z]{6}))$/i;
6 | const rgbaColorStrReg = /^rgb(a)?\(([\d.]{1,3}(,)?(\s)?){3,4}\)$/i;
7 |
8 | /**
9 | * 校验、并过滤,16进制颜色的‘#’
10 | * @param {*} hexColorStr
11 | */
12 | function filterHexColorHeader(hexColorStr) {
13 | if (
14 | !hexColorStrWithHeaderReg.test(hexColorStr)
15 | && !hexColorStrWithOutHeaderReg.test(hexColorStr)
16 | ) {
17 | throw new Error('hex color format error!');
18 | }
19 | if (hexColorStrWithHeaderReg.test(hexColorStr)) {
20 | return hexColorStr.slice(1);
21 | }
22 | return hexColorStr;
23 | }
24 | function pad2(num) {
25 | let t = num.toString(16);
26 | if (t.length === 1) t = `0${t}`;
27 | return t;
28 | }
29 | function _toNum3(colorStr) {
30 | if (colorStr.length === 3) {
31 | colorStr = colorStr[0]
32 | + colorStr[0]
33 | + colorStr[1]
34 | + colorStr[1]
35 | + colorStr[2]
36 | + colorStr[2];
37 | }
38 | const r = parseInt(colorStr.slice(0, 2), 16);
39 | const g = parseInt(colorStr.slice(2, 4), 16);
40 | const b = parseInt(colorStr.slice(4, 6), 16);
41 | return [r, g, b];
42 | }
43 | const toNum3 = pipe(filterHexColorHeader, _toNum3);
44 |
45 | function mix(color1, color2, weight1, alpha1, alpha2) {
46 | color1 = color1.replace('#', '');
47 | color2 = color2.replace('#', '');
48 | if (weight1 === undefined) weight1 = 0.5;
49 | if (alpha1 === undefined) alpha1 = 1;
50 | if (alpha2 === undefined) alpha2 = 1;
51 |
52 | const w = 2 * weight1 - 1;
53 | const alphaDelta = alpha1 - alpha2;
54 | const w1 = ((w * alphaDelta === -1 ? w : (w + alphaDelta) / (1 + w * alphaDelta)) + 1) / 2;
55 | const w2 = 1 - w1;
56 |
57 | const nums1 = toNum3(color1);
58 | const nums2 = toNum3(color2);
59 | const r = Math.round(w1 * nums1[0] + w2 * nums2[0]);
60 | const g = Math.round(w1 * nums1[1] + w2 * nums2[1]);
61 | const b = Math.round(w1 * nums1[2] + w2 * nums2[2]);
62 | return `#${pad2(r)}${pad2(g)}${pad2(b)}`;
63 | }
64 |
65 | /**
66 | * 改变颜色深度
67 | * @param {*} colorStr 16进制格式的颜色字符串
68 | * @param {*} rate 比率0-1
69 | */
70 | function rgba(colorStr, rate) {
71 | const nums = toNum3(colorStr);
72 | let r = nums[0];
73 | let g = nums[1];
74 | let b = nums[2];
75 | return (
76 | (r = Math.round((1 - rate) * r)),
77 | (g = Math.round((1 - rate) * g)),
78 | (b = Math.round((1 - rate) * b)),
79 | (r = pad2(r)),
80 | (g = pad2(g)),
81 | (b = pad2(b)),
82 | `#${r}${g}${b}`
83 | );
84 | }
85 |
86 | /**
87 | * 转化16进制的颜色值为rgba的颜色值
88 | * 如果传入rgba格式的颜色,则直接返回,不作处理
89 | * @param {*} colorStr 16进制的颜色值 #fff、#ffffff
90 | * @param {*} opacity 透明度
91 | */
92 | function toRgbaColor(colorStr, opacity = 1) {
93 | if (typeof colorStr !== 'string') {
94 | throw new Error('color str should be string type');
95 | }
96 | if (rgbaColorStrReg.test(colorStr)) {
97 | const numPikcerReg = /(\d+,)/g;
98 | const rgbArr = colorStr.match(numPikcerReg).join('').split(',');
99 | rgbArr.length = 3;
100 | return `rgba(${rgbArr.join(',')}, ${opacity})`;
101 | }
102 | if (colorStr.startsWith('#')) {
103 | colorStr = colorStr.slice(1);
104 | }
105 | return `rgba(${toNum3(colorStr).join(',')}, ${opacity})`;
106 | }
107 |
108 | /**
109 | * 转换rgba格式的颜色值到16进制的颜色值
110 | * 如果是16进制的颜色,则直接返回
111 | * @param {*} rgbaColorStr rgba格式的颜色值
112 | * return 16进制的颜色值
113 | */
114 | function toHexColor(rgbaColorStr) {
115 | if (
116 | hexColorStrWithHeaderReg.test(rgbaColorStr)
117 | || hexColorStrWithOutHeaderReg.test(rgbaColorStr)
118 | ) {
119 | return rgbaColorStr;
120 | }
121 | if (!rgbaColorStrReg.test(rgbaColorStr)) {
122 | throw new Error('rgba color format error!');
123 | }
124 | const numPikcerReg = /(\d+,)/g;
125 | const rgbArr = rgbaColorStr.match(numPikcerReg).join('').split(',');
126 | const rate = 0;
127 | let r = rgbArr[0];
128 | let g = rgbArr[1];
129 | let b = rgbArr[2];
130 | return (
131 | (r = Math.round((1 - rate) * r)),
132 | (g = Math.round((1 - rate) * g)),
133 | (b = Math.round((1 - rate) * b)),
134 | (r = pad2(r)),
135 | (g = pad2(g)),
136 | (b = pad2(b)),
137 | `#${r}${g}${b}`
138 | );
139 | }
140 |
141 | /**
142 | * 淡化颜色
143 | * @param {*} colorStr 颜色值,支持16进制的颜色和rgba格式
144 | * @param {*} weight 淡化比重 0-1
145 | */
146 | function lighten(colorStr, weight) {
147 | colorStr = toHexColor(colorStr);
148 | return mix('fff', colorStr, weight);
149 | }
150 |
151 | /**
152 | * 加深颜色
153 | * @param {*} colorStr 颜色值,支持16进制的颜色和rgba格式
154 | * @param {*} weight 淡化比重 0-1
155 | */
156 | function darken(colorStr, weight) {
157 | colorStr = toHexColor(colorStr);
158 | return mix('000', colorStr, weight);
159 | }
160 |
161 |
162 | class _Color extends Color {
163 | lighten(ratio) {
164 | const oldValue = this.string();
165 | super.lighten(ratio);
166 | const newValue = this.string();
167 | if (oldValue === newValue) {
168 | return new _Color(lighten(oldValue, ratio));
169 | }
170 | return this;
171 | }
172 | }
173 |
174 | export default _Color;
175 |
176 | // export {
177 | // lighten, // 淡化
178 | // darken, // 加深
179 | // mix, // 混合
180 | // toNum3,
181 | // rgba,
182 | // toRgbaColor,
183 | // toHexColor,
184 | // pad2,
185 | // };
186 |
--------------------------------------------------------------------------------
/src/components/base/DynamicForm/lib/BaseForm.js:
--------------------------------------------------------------------------------
1 | import React, {Component} from 'react';
2 | import {PropTypes} from 'prop-types';
3 | import classnames from 'classnames';
4 | import {Form, Row, Col} from 'antd';
5 | import InputFactory from './InputFactory';
6 | import MyFormItem from './Item';
7 | import {createFormItem} from './utils';
8 | import {
9 | defaultFormItemLayout,
10 | // defaultButtonItemLayout,
11 | // tailFormItemLayout,
12 | LAYOUT,
13 | } from './config';
14 |
15 | // const FormItem = Form.Item;
16 |
17 | class BaseForm extends Component {
18 | static LAYOUT = LAYOUT;
19 |
20 | componentDidUpdate() {
21 | this.setInitialValue(this.props.formFields);
22 | }
23 |
24 | onSubmit = e => {
25 | const {onSubmit, form} = this.props;
26 | e.preventDefault();
27 | form.validateFieldsAndScroll(onSubmit);
28 | };
29 |
30 | getFieldsValue = () => this.form.getFieldsValue();
31 |
32 | setInitialValue = formFields => this.initialValue = this.formatFormValues(this.filterNoConfigFields(formFields));
33 | setFields = formFields => this.props.form.setFields(this.formatFormValues(this.filterNoConfigFields(formFields)));
34 | setFieldsValue = formFields => this.props.form.setFieldsValue(this.filterNoConfigFields(formFields));
35 |
36 | getFormItemLayout = () => {
37 | const {layout} = this.props;
38 | const formItemLayout = layout === LAYOUT.HORIZONTAL ? defaultFormItemLayout : null;
39 | // const buttonItemLayout = layout === LAYOUT.HORIZONTAL ? defaultButtonItemLayout : null;
40 | return formItemLayout;
41 | };
42 | getChildren = () => {
43 | return this.props.children;
44 | };
45 | filterNoConfigFields = formFields => {
46 | const { fieldsConfig } = this.props;
47 | const newFormFields = Object.keys(fieldsConfig)
48 | .filter(key => formFields[key] !== undefined)
49 | .reduce((obj, key) => {
50 | obj[key] = formFields[key];
51 | return obj;
52 | }, {});
53 | return newFormFields;
54 | }
55 |
56 | createFormItemByConfig = formItemConfig => {
57 | const itemLayout = this.getFormItemLayout();
58 | const { form, formItemLayout } = this.props;
59 | const _formItemLayout = { ...formItemLayout };
60 | const layoutProps = ['span', 'xs', 'sm', 'md', 'lg', 'xl', 'xxl'];
61 | layoutProps.forEach(layoutProp => {
62 | if (!!formItemConfig.props[layoutProp]) {
63 | _formItemLayout[layoutProp] = formItemConfig.props[layoutProp];
64 | }
65 | });
66 | const { getFieldDecorator } = form;
67 | if (!!_formItemLayout && Object.keys(_formItemLayout).length) {
68 | return (
69 |
70 |
75 |
76 | );
77 | }
78 | return (
79 |
85 | );
86 | };
87 |
88 | validateFieldsAndScroll = cb => this.props.form.validateFieldsAndScroll(cb);
89 |
90 | /**
91 | * 数据格式转换
92 | * { name: 'tom' } => { name: { value: 'tom; } }
93 | * @param values
94 | * @returns {{}}
95 | */
96 | formatFormValues = values => {
97 | const res = {};
98 |
99 | for (let key in values) { // eslint-disable-line
100 | res[key] = {value: values[key]};
101 | }
102 | return res;
103 | };
104 |
105 | resetFields = resetValue => {
106 | if (resetValue) {
107 | this.props.form.setFields(this.formatFormValues(this.filterNoConfigFields(resetValue)));
108 | } else {
109 | setTimeout(() => this.props.form.setFields(this.initialValue), 16);
110 | }
111 | };
112 |
113 | /* eslint-disable */
114 | createFormItemsConfig({formFields, fieldsConfig}) {
115 | const formItems = [];
116 | for (let key in formFields) {
117 | if (formFields.hasOwnProperty(key) && fieldsConfig.hasOwnProperty(key)) {
118 | let fieldConfig = fieldsConfig[key];
119 |
120 | if (fieldConfig instanceof InputFactory) {
121 | fieldConfig = fieldConfig.getInput();
122 | }
123 | const params = {
124 | ...fieldConfig,
125 | name: key,
126 | value: formFields[key],
127 | };
128 | formItems.push(createFormItem(params));
129 | }
130 | }
131 | return formItems;
132 | }
133 | /* eslint-enable */
134 |
135 | render() {
136 | const {layout, gutter} = this.props;
137 | const {className, style} = this.props;
138 | const formItems = this.createFormItemsConfig(this.props);
139 | const formProps = {
140 | style,
141 | className,
142 | layout,
143 | onSubmit: this.onSubmit,
144 | };
145 | const formCls = classnames('dynamic-form', className);
146 | return (
147 |
153 | );
154 | }
155 | }
156 |
157 | BaseForm.defaultProps = {
158 | layout: LAYOUT.HORIZONTAL,
159 | onChange: () => {
160 | },
161 | onSubmit: () => {
162 | },
163 | formItemLayout: {},
164 | gutter: 0,
165 | };
166 | BaseForm.propTypes = {
167 | formFields: PropTypes.object.isRequired, // eslint-disable-line
168 | fieldsConfig: PropTypes.object.isRequired, // eslint-disable-line
169 | layout: PropTypes.oneOf([LAYOUT.HORIZONTAL, LAYOUT.INLINE, LAYOUT.VERTICAL]),
170 | onChange: PropTypes.func,
171 | onSubmit: PropTypes.func,
172 | formItemLayout: PropTypes.object, // eslint-disable-line
173 | gutter: PropTypes.number,
174 | };
175 |
176 | export default BaseForm;
177 |
--------------------------------------------------------------------------------
/src/components/base/DynamicForm/lib/InputFactory.js:
--------------------------------------------------------------------------------
1 | import cloneDeep from 'lodash/cloneDeep';
2 | import {defaultConfig, INPUT_TYPE} from './config';
3 | /**
4 | * 生成dynamicform配置的工具类
5 | */
6 | export default class InputFactory {
7 | input = {};
8 |
9 | constructor(arg = '') {
10 | if (typeof arg === 'string') {
11 | this.input = {
12 | label: arg,
13 | type: defaultConfig.type,
14 | };
15 | } else if (typeof arg === 'object') {
16 | if (arg instanceof InputFactory === true) {
17 | arg = arg.getInput();
18 | }
19 | this.init(arg);
20 | }
21 | }
22 |
23 | init(input) {
24 | this.input = cloneDeep(input);
25 | return this;
26 | }
27 |
28 | fieldOption(config) {
29 | if (!this.input.fieldOption) {
30 | this.input.fieldOption = {};
31 | }
32 | this.input.fieldOption = {
33 | ...this.input.fieldOption,
34 | ...config,
35 | };
36 | return this;
37 | }
38 |
39 | rule(rule) {
40 | if (!Array.isArray(this.input.rules)) {
41 | this.input.rules = [];
42 | }
43 | this.input.rules.push(rule);
44 | return this;
45 | }
46 |
47 | removeRules() {
48 | this.input.rules = [];
49 | return this;
50 | }
51 |
52 | prop(prop) {
53 | if (!this.input.props) {
54 | this.input.props = {};
55 | }
56 | this.input.props = {
57 | ...this.input.props,
58 | ...prop,
59 | };
60 | return this;
61 | }
62 |
63 | itemProp(prop) {
64 | if (!this.input.itemProps) {
65 | this.input.itemProps = {};
66 | }
67 | this.input.itemProps = {
68 | ...this.input.itemProps,
69 | ...prop,
70 | };
71 | return this;
72 | }
73 |
74 | label(label) {
75 | this.input.label = label;
76 | return this;
77 | }
78 |
79 | trigger(ListenerType = 'onChange') {
80 | return this.fieldOption({
81 | validateTrigger: ListenerType,
82 | });
83 | }
84 | span(colNum) {
85 | if (!!colNum) {
86 | return this.itemProp({span: colNum});
87 | }
88 | return this;
89 | }
90 | layout(config) {
91 | if (!!config) {
92 | return this.itemProp(config);
93 | }
94 | return this;
95 | }
96 |
97 | isButton(text, onClick) {
98 | this.input = {
99 | // label,
100 | type: INPUT_TYPE.BUTTON,
101 | props: {onClick},
102 | };
103 | return this;
104 | }
105 |
106 | isDisable() {
107 | return this.prop({
108 | disabled: true,
109 | });
110 | }
111 |
112 | isEnable() {
113 | return this.prop({
114 | disabled: false,
115 | });
116 | }
117 |
118 | isRequired(message = `请输入${this.input.label}!`) {
119 | return this.rule({
120 | required: true,
121 | message,
122 | });
123 | }
124 | isNotRequired() {
125 | if (Array.isArray(this.input.rules)) {
126 | const target = this.input.rules.find(rule => rule.required === true);
127 | if (!!target) {
128 | target.required = false;
129 | }
130 | }
131 | return this;
132 | // return this.rule({
133 | // required: false,
134 | // });
135 | }
136 |
137 | ph(placeholder = `请输入${this.input.label}`) {
138 | return this.prop({placeholder});
139 | }
140 |
141 | /**
142 | * 为表单元素添加验证方法
143 | * 可以是已经存在的验证type,或者自定义的验证方法
144 | * @param validator function
145 | * @returns {InputFactory}
146 | */
147 | validator(validator) {
148 | if (!validator) {
149 | throw new Error('没有validator参数');
150 | }
151 | if (typeof validator !== 'function') {
152 | throw new Error('validator参数应该是一个函数!');
153 | }
154 | return this.rule({validator});
155 | }
156 |
157 | on(ListenerName, listener) {
158 | const lowerCaseReg = /^[a-z]*$/;
159 | if (lowerCaseReg.test(ListenerName.charAt(0))) {
160 | throw new Error(`事件名称:${ListenerName}的首字母应该是大写!`);
161 | }
162 | return this.prop({
163 | [`on${ListenerName}`]: listener,
164 | });
165 | }
166 |
167 | isPassword() {
168 | return this.prop({
169 | type: 'password',
170 | });
171 | }
172 |
173 | isRadio(options) {
174 | if (!options) throw new Error('没有传options');
175 | this.input.type = INPUT_TYPE.RADIO;
176 | this.input.options = options;
177 | return this;
178 | }
179 |
180 | isCheckbox(options) {
181 | if (!options) throw new Error('没有传options');
182 | this.input.type = INPUT_TYPE.CHECKBOX;
183 | this.input.options = options;
184 | return this;
185 | }
186 |
187 | // select框带搜索功能时,的过滤选项方法
188 | filterOption(input, option) { // eslint-disable-line
189 | const reg = new RegExp(input, 'i');
190 | return reg.test(option.props.children);
191 | }
192 |
193 | isSelect(options, config = {showSearch: false, multiple: false}) {
194 | const {showSearch, multiple} = config;
195 | if (!options) throw new Error('没有传options');
196 | this.input.type = INPUT_TYPE.SELECT;
197 | this.input.options = options;
198 | if (showSearch) {
199 | this.prop({
200 | filterOption: this.filterOption,
201 | showSearch,
202 | });
203 | }
204 | if (multiple) {
205 | this.prop({
206 | mode: 'multiple',
207 | });
208 | }
209 | return this;
210 | }
211 |
212 | isDatePicker(format = 'YYYY-MM-DD') {
213 | this.input.type = INPUT_TYPE.DATEPICKER;
214 | return this.prop({
215 | format,
216 | });
217 | }
218 |
219 | isDateRangePicker(format = 'YYYY-MM-DD') {
220 | this.input.type = INPUT_TYPE.RANGEPICKER;
221 | return this.prop({
222 | format,
223 | });
224 | }
225 |
226 | isTextArea() {
227 | this.input.type = INPUT_TYPE.TEXTAREA;
228 | return this;
229 | }
230 |
231 | isText() {
232 | this.input.type = INPUT_TYPE.TEXT;
233 | return this;
234 | }
235 |
236 | is({type, prop, options}) {
237 | const _type = type.toUpperCase();
238 | if (!INPUT_TYPE[_type]) {
239 | throw new Error(`${type} 类型的组件不存在!`);
240 | }
241 |
242 | this.input.type = INPUT_TYPE[_type];
243 | /* eslint-disable */
244 | !!options && (this.input.options = options);
245 | !!prop && this.prop(prop);
246 | /* eslint-enable */
247 | return this;
248 | }
249 | clone() {
250 | return new InputFactory(this);
251 | }
252 |
253 | getInput() {
254 | return {...this.input};
255 | }
256 | }
257 |
258 | export const createInputFactory = label => new InputFactory(label);
259 |
--------------------------------------------------------------------------------
/scripts/webpack/webpack.base.conf.js:
--------------------------------------------------------------------------------
1 | // const path = require('path');
2 | const webpack = require("webpack");
3 | const HappyPack = require("happypack");
4 | const TerserPlugin = require("terser-webpack-plugin");
5 | const OptimizeCSSAssetsPlugin = require("optimize-css-assets-webpack-plugin");
6 | const HtmlWebpackPlugin = require("html-webpack-plugin");
7 | const ScriptExtHtmlWebpackPlugin = require("script-ext-html-webpack-plugin");
8 | const LodashModuleReplacementPlugin = require("lodash-webpack-plugin");
9 | const ProgressBarPlugin = require('progress-bar-webpack-plugin');
10 | const clearConsole = require('react-dev-utils/clearConsole');
11 | const chalk = require('chalk');
12 | const InlineChunk = require('inline-chunk');
13 |
14 | const {
15 | getPath,
16 | getArg,
17 | addDllPluginsConfig,
18 | createStyleLoader,
19 | createStylePlugin
20 | } = require("./utils");
21 |
22 | const { distPath, publicPath } = require("./config");
23 |
24 | const { project, channel, PROJECT_ENV } = getArg();
25 |
26 | const mode = process.env.NODE_ENV;
27 | const isDev = mode === "development";
28 |
29 | module.exports = {
30 | performance: false,
31 | entry: {
32 | index: getPath(project, "index")
33 | },
34 | output: {
35 | path: distPath
36 | },
37 | resolve: {
38 | // 设置模块导入规则,import/require时会直接在这些目录找文件
39 | modules: [
40 | getPath(`${project}/components/business`),
41 | getPath(`${project}/components/base`),
42 | getPath("common/components/business"),
43 | getPath("common/components/base"),
44 | "node_modules"
45 | ],
46 | // import导入时省略后缀
47 | extensions: [".ts", ".tsx", ".js", ".jsx", ".scss", ".css"],
48 | // import导入时别名
49 | alias: {
50 | // common
51 | "@": getPath(`${project}`),
52 | "@base": getPath(`${project}/components/base`),
53 | "@history": getPath(`${project}/routes/history`),
54 | "@biz": getPath(`${project}/components/business`),
55 | "@styles": getPath(`${project}/utils/styles`),
56 | "@channel": getPath(`buildConfig/channel/${channel}`)
57 | }
58 | },
59 | module: {
60 | rules: [
61 | {
62 | test: /\.(j|t)s(x)?$/,
63 | exclude: /node_modules/,
64 | loader: "happypack/loader?id=babel"
65 | },
66 | ...createStyleLoader(mode, isDev),
67 | {
68 | test: /\.(png|jpe?g|gif)(\?.*)?$/,
69 | use: [
70 | {
71 | loader: "url-loader",
72 | options: {
73 | name: "img/[name].[ext]",
74 | limit: 2048,
75 | fallback: "file-loader"
76 | }
77 | }
78 | ]
79 | },
80 | {
81 | test: /\.svg$/,
82 | use: [
83 | {
84 | loader: "svg-sprite-loader",
85 | options: {
86 | symbolId: "icon-[name]"
87 | }
88 | }
89 | ]
90 | },
91 | {
92 | test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/,
93 | use: "url-loader"
94 | },
95 | {
96 | test: /\.html$/,
97 | use: "html-loader",
98 | include: new RegExp(`(/|\)${project}`),
99 | exclude: new RegExp(`(/|\)${project}(/|\)index.html`)
100 | },
101 | {
102 | test: /\.json$/,
103 | type: "javascript/auto",
104 | use: "json-loader"
105 | }
106 | ]
107 | },
108 | optimization: {
109 | splitChunks: {
110 | cacheGroups: {
111 | vendors: {
112 | priority: 0,
113 | name: 'vendors',
114 | test: new RegExp(`((/|\)${project})|((/|\)node_modules)`),
115 | // test: /\/node_modules/,
116 | minChunks: 2
117 | },
118 | styles: {
119 | priority: 9,
120 | name: "styles",
121 | test: /\.(s)?css$/,
122 | }
123 | },
124 | chunks: "all",
125 | minChunks: 1,
126 | minSize: 0,
127 | name: true
128 | },
129 | minimizer: [
130 | new TerserPlugin({
131 | test: /\.js(\?.*)?$/i,
132 | parallel: true,
133 | cache: true,
134 | sourceMap: true,
135 | // warnings: false,
136 | terserOptions: {
137 | output: {
138 | comments: false
139 | },
140 | compress: {
141 | warnings: false,
142 | drop_debugger: true,
143 | drop_console: true
144 | }
145 | }
146 | }),
147 | new OptimizeCSSAssetsPlugin()
148 | ]
149 | },
150 | plugins: [
151 | new ProgressBarPlugin({
152 | summary: false,
153 | complete: '■',
154 | width: 20,
155 | // complete: '=',
156 | // format: `building ${chalk.cyan.bold(':bar ')} :percent ( :elapseds )`,
157 | // format: `🍵 ${chalk.cyan.bold(':bar ')}${chalk.yellow(':percent ( :elapseds )')}`,
158 | format: '🍵 ' + chalk.cyan.bold(':bar ') + chalk.cyan.bold(':percent ') + chalk.yellow.bold('( :elapseds )'),
159 | customSummary: (times) => {
160 | clearConsole();
161 | setTimeout(() => {
162 | console.log(chalk.yellow.bold('⏰ 编译时间:' + times));
163 | }, 0)
164 | }
165 | }),
166 | new LodashModuleReplacementPlugin({
167 | collections: true,
168 | paths: true
169 | }),
170 | new webpack.DefinePlugin({
171 | "process.env.PROJECT_ENV": JSON.stringify(PROJECT_ENV),
172 | "process.env.BASE_URL": JSON.stringify(publicPath)
173 | }),
174 | new HappyPack({
175 | id: "babel",
176 | threads: 1,
177 | verbose: false,
178 | loaders: [
179 | {
180 | loader: "babel-loader",
181 | cacheDirectory: true
182 | }
183 | ]
184 | }),
185 | createStylePlugin(mode, isDev),
186 | new HtmlWebpackPlugin({
187 | title: "react project template",
188 | filename: "index.html",
189 | template: `./${project}/index.html`,
190 | // favicon: isDev ? '' : `${project}/favicon.ico`,
191 | // 防止各channel项目一样时,不生成html文件
192 | cache: false,
193 | minify: {
194 | removeComments: true,
195 | collapseWhitespace: true,
196 | removeRedundantAttributes: true,
197 | useShortDoctype: true,
198 | removeEmptyAttributes: true,
199 | removeStyleLinkTypeAttributes: true,
200 | keepClosingSlash: true,
201 | minifyJS: true,
202 | minifyCSS: true,
203 | minifyURLs: true
204 | },
205 | inject: true
206 | // hash: true,
207 | }),
208 | new ScriptExtHtmlWebpackPlugin({
209 | defaultAttribute: "defer"
210 | }),
211 | new InlineChunk({
212 | name: 'theme',
213 | test: new RegExp(`(/|\)${project}(/|\)theme(/|\)native`),
214 | }),
215 | ...addDllPluginsConfig(mode)
216 | ]
217 | };
218 |
--------------------------------------------------------------------------------
/src/theme/material.ts:
--------------------------------------------------------------------------------
1 | import { createMuiTheme } from '@material-ui/core/styles';
2 | import { zhCN } from '@material-ui/core/locale';
3 | import { Shadows } from '@material-ui/core/styles/shadows';
4 | import r from './r';
5 | // import { lightBlue } from '@material-ui/core/colors';
6 |
7 | const defaultShadow: Shadows = [
8 | 'none',
9 | '0px 2px 1px -1px rgba(0,0,0,0.2),0px 1px 1px 0px rgba(0,0,0,0.14),0px 1px 3px 0px rgba(0,0,0,0.12)',
10 | '0px 3px 1px -2px rgba(0,0,0,0.2),0px 2px 2px 0px rgba(0,0,0,0.14),0px 1px 5px 0px rgba(0,0,0,0.12)',
11 | '0px 3px 3px -2px rgba(0,0,0,0.2),0px 3px 4px 0px rgba(0,0,0,0.14),0px 1px 8px 0px rgba(0,0,0,0.12)',
12 | '0px 2px 4px -1px rgba(0,0,0,0.2),0px 4px 5px 0px rgba(0,0,0,0.14),0px 1px 10px 0px rgba(0,0,0,0.12)',
13 | '0px 3px 5px -1px rgba(0,0,0,0.2),0px 5px 8px 0px rgba(0,0,0,0.14),0px 1px 14px 0px rgba(0,0,0,0.12)',
14 | '0px 3px 5px -1px rgba(0,0,0,0.2),0px 6px 10px 0px rgba(0,0,0,0.14),0px 1px 18px 0px rgba(0,0,0,0.12)',
15 | '0px 4px 5px -2px rgba(0,0,0,0.2),0px 7px 10px 1px rgba(0,0,0,0.14),0px 2px 16px 1px rgba(0,0,0,0.12)',
16 | '0px 5px 5px -3px rgba(0,0,0,0.2),0px 8px 10px 1px rgba(0,0,0,0.14),0px 3px 14px 2px rgba(0,0,0,0.12)',
17 | '0px 5px 6px -3px rgba(0,0,0,0.2),0px 9px 12px 1px rgba(0,0,0,0.14),0px 3px 16px 2px rgba(0,0,0,0.12)',
18 | '0px 6px 6px -3px rgba(0,0,0,0.2),0px 10px 14px 1px rgba(0,0,0,0.14),0px 4px 18px 3px rgba(0,0,0,0.12)',
19 | '0px 6px 7px -4px rgba(0,0,0,0.2),0px 11px 15px 1px rgba(0,0,0,0.14),0px 4px 20px 3px rgba(0,0,0,0.12)',
20 | '0px 7px 8px -4px rgba(0,0,0,0.2),0px 12px 17px 2px rgba(0,0,0,0.14),0px 5px 22px 4px rgba(0,0,0,0.12)',
21 | '0px 7px 8px -4px rgba(0,0,0,0.2),0px 13px 19px 2px rgba(0,0,0,0.14),0px 5px 24px 4px rgba(0,0,0,0.12)',
22 | '0px 7px 9px -4px rgba(0,0,0,0.2),0px 14px 21px 2px rgba(0,0,0,0.14),0px 5px 26px 4px rgba(0,0,0,0.12)',
23 | '0px 8px 9px -5px rgba(0,0,0,0.2),0px 15px 22px 2px rgba(0,0,0,0.14),0px 6px 28px 5px rgba(0,0,0,0.12)',
24 | '0px 8px 10px -5px rgba(0,0,0,0.2),0px 16px 24px 2px rgba(0,0,0,0.14),0px 6px 30px 5px rgba(0,0,0,0.12)',
25 | '0px 8px 11px -5px rgba(0,0,0,0.2),0px 17px 26px 2px rgba(0,0,0,0.14),0px 6px 32px 5px rgba(0,0,0,0.12)',
26 | '0px 9px 11px -5px rgba(0,0,0,0.2),0px 18px 28px 2px rgba(0,0,0,0.14),0px 7px 34px 6px rgba(0,0,0,0.12)',
27 | '0px 9px 12px -6px rgba(0,0,0,0.2),0px 19px 29px 2px rgba(0,0,0,0.14),0px 7px 36px 6px rgba(0,0,0,0.12)',
28 | '0px 10px 13px -6px rgba(0,0,0,0.2),0px 20px 31px 3px rgba(0,0,0,0.14),0px 8px 38px 7px rgba(0,0,0,0.12)',
29 | '0px 10px 13px -6px rgba(0,0,0,0.2),0px 21px 33px 3px rgba(0,0,0,0.14),0px 8px 40px 7px rgba(0,0,0,0.12)',
30 | '0px 10px 14px -6px rgba(0,0,0,0.2),0px 22px 35px 3px rgba(0,0,0,0.14),0px 8px 42px 7px rgba(0,0,0,0.12)',
31 | '0px 11px 14px -7px rgba(0,0,0,0.2),0px 23px 36px 3px rgba(0,0,0,0.14),0px 9px 44px 8px rgba(0,0,0,0.12)',
32 | '0px 11px 15px -7px rgba(0,0,0,0.2),0px 24px 38px 3px rgba(0,0,0,0.14),0px 9px 46px 8px rgba(0,0,0,0.12)',
33 | ];
34 |
35 | const mainShadow: Shadows = defaultShadow.slice() as Shadows;
36 |
37 | mainShadow[24] = '0 12px 20px -10px rgba(33, 150, 243,.28), 0 4px 20px 0 rgba(0, 0, 0,.12), 0 7px 8px -5px rgba(33, 150, 243,.2)';
38 |
39 | const theme = createMuiTheme(
40 | {
41 | palette: {
42 | primary: {
43 | light: '#64b5f6',
44 | main: '#2196f3',
45 | dark: '#1976d2',
46 | contrastText: '#fff',
47 | },
48 | secondary: {
49 | // light: '#64b5f6',
50 | main: '#2196f3',
51 | // dark: '#1976d2',
52 | // contrastText: '#fff',
53 | },
54 | },
55 | shape: {
56 | borderRadius: 0,
57 | },
58 | shadows: mainShadow,
59 | props: {
60 | MuiTextField: {
61 | variant: 'outlined',
62 | size: 'small',
63 | type: 'text',
64 | },
65 | // MuiCheckbox: {
66 | // color: 'primary',
67 | // },
68 | // MuiRadio: {
69 | // color: 'primary',
70 | // },
71 | },
72 | overrides: {
73 | MuiOutlinedInput: {
74 | inputMarginDense: {
75 | paddingTop: 9.5,
76 | paddingBottom: 9.5,
77 | },
78 | root: {
79 | '&$focused $notchedOutline': {
80 | borderWidth: 1,
81 | // borderColor: 'blue',
82 | },
83 | '&:hover $notchedOutline': {
84 | // borderColor: '#2196f3',
85 | },
86 | },
87 | },
88 | // @ts-ignore
89 | MUIDataTableHeadCell: {
90 | sortAction: {
91 | display: 'flex',
92 | alignItems: 'center',
93 | },
94 | },
95 | MuiAlert: {
96 | root: {
97 | minWidth: 250,
98 | },
99 | },
100 | // date picker的主题样式覆盖,详情请看
101 | // @ts-ignore
102 | // MuiPickersDay: {
103 | // day: {
104 | // color: lightBlue.A700,
105 | // },
106 | // daySelected: {
107 | // backgroundColor: lightBlue['400'],
108 | // },
109 | // dayDisabled: {
110 | // color: lightBlue['100'],
111 | // },
112 | // current: {
113 | // color: lightBlue['900'],
114 | // },
115 | // },
116 | // MuiPickersModal: {
117 | // dialogAction: {
118 | // color: lightBlue['400'],
119 | // },
120 | // },
121 | // MuiButton: {
122 | // root: {
123 | // padding: `${r(6)} ${r(16)}`,
124 | // },
125 | // },
126 | },
127 | spacing: px => r(px * 8),
128 | },
129 | zhCN,
130 | );
131 |
132 | export const menuTheme = createMuiTheme(
133 | {
134 | palette: {
135 | type: 'dark',
136 | action: {
137 | hover: 'rgba(200, 200, 200, 0.2)',
138 | hoverOpacity: 0.2,
139 | selected: 'rgba(200, 200, 200, 0.2)',
140 | selectedOpacity: 0.2,
141 | },
142 | },
143 | overrides: {
144 | MuiDrawer: {
145 | paperAnchorDockedLeft: {
146 | borderRight: 'none',
147 | },
148 | },
149 | MuiListItem: {
150 | root: {
151 | marginTop: 10,
152 | borderRadius: 0,
153 | '&$selected,&$selected:hover': {
154 | boxShadow: mainShadow[24],
155 | backgroundColor: theme.palette.primary.main,
156 | },
157 | transition:
158 | 'all cubic-bezier(0.4, 0, 0.2, 1) 0.3s!important',
159 | },
160 | gutters: {
161 | paddingLeft: 10,
162 | },
163 | },
164 | MuiListItemIcon: {
165 | root: {
166 | minWidth: 45,
167 | },
168 | },
169 | },
170 | },
171 | zhCN,
172 | );
173 |
174 | export default theme;
175 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "react-app",
3 | "version": "1.0.0",
4 | "description": "react project",
5 | "main": "index.js",
6 | "scripts": {
7 | "dev": "cross-env NODE_ENV=development node scripts/dev.js --PROJECT_ENV development",
8 | "mock": "cd mock-server && npm run start:dev",
9 | "build:dll": "node scripts/build_dll.js",
10 | "build:dev": "cross-env NODE_ENV=development node scripts/build.js --PROJECT_ENV development",
11 | "build:stg": "cross-env NODE_ENV=development node scripts/build.js --PROJECT_ENV stg",
12 | "build:prd": "cross-env NODE_ENV=production node scripts/build.js --PROJECT_ENV production",
13 | "create:module": "node scripts/create-module/index.js",
14 | "lint": "lint-staged",
15 | "cc": "cd ./mock-server && npm run create:controller"
16 | },
17 | "husky": {
18 | "hooks": {
19 | "pre-commit": "lint-staged"
20 | }
21 | },
22 | "lint-staged": {
23 | "src/**/*.{js,jsx,ts,tsx}": [
24 | "eslint --fix"
25 | ]
26 | },
27 | "keywords": [
28 | "webpack",
29 | "react",
30 | "starter",
31 | "typescript",
32 | "template"
33 | ],
34 | "author": "empty916",
35 | "license": "MIT",
36 | "devDependencies": {
37 | "@babel/cli": "^7.8.4",
38 | "@babel/core": "^7.8.4",
39 | "@babel/plugin-proposal-async-generator-functions": "^7.8.3",
40 | "@babel/plugin-proposal-class-properties": "^7.8.3",
41 | "@babel/plugin-proposal-decorators": "^7.8.3",
42 | "@babel/plugin-proposal-export-default-from": "^7.8.3",
43 | "@babel/plugin-proposal-function-bind": "^7.8.3",
44 | "@babel/plugin-proposal-object-rest-spread": "^7.8.3",
45 | "@babel/plugin-syntax-dynamic-import": "^7.8.3",
46 | "@babel/plugin-transform-react-jsx-self": "^7.8.3",
47 | "@babel/plugin-transform-runtime": "^7.8.3",
48 | "@babel/preset-env": "^7.8.4",
49 | "@babel/preset-react": "^7.8.3",
50 | "@babel/preset-typescript": "^7.8.3",
51 | "@types/classnames": "^2.2.8",
52 | "@types/color": "^3.0.0",
53 | "@types/crypto-js": "^3.1.44",
54 | "@types/d3": "^5.7.2",
55 | "@types/history": "^4.7.5",
56 | "@types/hoist-non-react-statics": "^3.3.1",
57 | "@types/loadable__component": "^5.10.0",
58 | "@types/lodash": "^4.14.134",
59 | "@types/mui-datatables": "^3.4.1",
60 | "@types/qs": "^6.9.2",
61 | "@types/react": "^17.0.43",
62 | "@types/react-dom": "^17.0.14",
63 | "@types/react-motion": "^0.0.29",
64 | "@types/react-router-dom": "5.1.2",
65 | "@typescript-eslint/eslint-plugin": "^2.19.2",
66 | "@typescript-eslint/parser": "^2.19.2",
67 | "@webpack-cli/generate-loader": "^0.1.5",
68 | "@webpack-cli/init": "^0.1.5",
69 | "@webpack-cli/serve": "^0.1.5",
70 | "add-asset-html-webpack-plugin": "^3.1.3",
71 | "babel-eslint": "^10.0.3",
72 | "babel-loader": "^8.0.6",
73 | "babel-plugin-import": "^1.13.0",
74 | "babel-plugin-lodash": "^3.3.4",
75 | "chalk": "^4.0.0",
76 | "chokidar": "^3.4.2",
77 | "clean-webpack-plugin": "^2.0.2",
78 | "cross-env": "^5.2.0",
79 | "css-loader": "^2.1.1",
80 | "cv-script": "^0.0.6",
81 | "eslint": "^6.8.0",
82 | "eslint-config-airbnb": "^18.0.1",
83 | "eslint-friendly-formatter": "^4.0.1",
84 | "eslint-loader": "^3.0.3",
85 | "eslint-plugin-import": "^2.17.3",
86 | "eslint-plugin-jsx-a11y": "^6.2.1",
87 | "eslint-plugin-react": "^7.13.0",
88 | "eslint-plugin-react-hooks": "1.x",
89 | "file-loader": "^3.0.1",
90 | "fork-ts-checker-webpack-plugin": "^4.0.3",
91 | "friendly-errors-webpack-plugin": "^1.7.0",
92 | "glob": "^7.1.6",
93 | "happypack": "^5.0.1",
94 | "html-loader": "^0.5.5",
95 | "html-webpack-plugin": "3.2.0",
96 | "http-server": "^0.11.1",
97 | "husky": "^4.2.5",
98 | "inline-chunk": "^2.0.0",
99 | "json-loader": "^0.5.7",
100 | "less": "^3.10.3",
101 | "less-loader": "^5.0.0",
102 | "lint-staged": "^10.1.3",
103 | "lodash-webpack-plugin": "^0.11.5",
104 | "match-lazy-module": "^1.0.3",
105 | "mini-css-extract-plugin": "^0.6.0",
106 | "minimist": "^1.2.0",
107 | "nodemon": "^2.0.2",
108 | "optimize-css-assets-webpack-plugin": "^5.0.1",
109 | "ora": "^3.4.0",
110 | "postcss": "^7.0.16",
111 | "postcss-cssnext": "^3.1.0",
112 | "postcss-loader": "^3.0.0",
113 | "progress-bar-webpack-plugin": "^2.1.0",
114 | "react-dev-utils": "^10.2.1",
115 | "redux": "^4.0.4",
116 | "rimraf": "^2.6.3",
117 | "sass": "^1.26.10",
118 | "sass-loader": "8.0.1",
119 | "script-ext-html-webpack-plugin": "^2.1.4",
120 | "shelljs": "^0.8.3",
121 | "source-map-loader": "^0.2.4",
122 | "style-loader": "^0.23.1",
123 | "stylelint": "10.1.0",
124 | "stylelint-config-recommended": "^2.2.0",
125 | "stylelint-config-standard": "^18.3.0",
126 | "stylelint-order": "^3.0.1",
127 | "stylelint-webpack-plugin": "^0.10.5",
128 | "svg-sprite-loader": "^4.1.6",
129 | "terser-webpack-plugin": "^1.2.4",
130 | "typescript": "^3.9.7",
131 | "url-loader": "^1.1.2",
132 | "webpack": "4.39.0",
133 | "webpack-bundle-analyzer": "^3.6.0",
134 | "webpack-cli": "^3.3.2",
135 | "webpack-dev-server": "^3.3.1",
136 | "webpack-merge": "^4.2.1"
137 | },
138 | "dependencies": {
139 | "@date-io/date-fns": "1.x",
140 | "@loadable/component": "^5.10.1",
141 | "@material-ui/core": "^4.11.0",
142 | "@material-ui/icons": "^4.9.1",
143 | "@material-ui/lab": "^4.0.0-alpha.49",
144 | "@material-ui/pickers": "^3.2.10",
145 | "axios": "0.19.2",
146 | "classnames": "^2.2.6",
147 | "color": "^3.1.2",
148 | "convert-key": "^1.0.3",
149 | "core-js": "^3.6.5",
150 | "crypto-js": "^4.0.0",
151 | "date-fns": "^2.16.1",
152 | "dayjs": "^1.8.19",
153 | "decimal.js": "^10.2.0",
154 | "delay-load": "^1.0.0",
155 | "formik": "2.1.4",
156 | "formik-material-ui": "^2.0.1",
157 | "formik-material-ui-lab": "^0.0.4",
158 | "formik-material-ui-pickers": "^0.0.10",
159 | "history": "^4.9.0",
160 | "hoist-non-react-statics": "^3.3.2",
161 | "jss": "^10.4.0",
162 | "jss-plugin-default-unit": "^10.4.0",
163 | "lodash": "^4.17.15",
164 | "mui-datatables": "^3.4.1",
165 | "natur": "^2.1.10",
166 | "natur-immer": "^1.0.6-beta3",
167 | "natur-persist": "^1.2.5",
168 | "natur-promise-watcher": "^1.0.1",
169 | "natur-service": "^2.1.6",
170 | "qs": "^6.9.3",
171 | "react": "16.10.1",
172 | "react-dom": "16.10.1",
173 | "react-router": "^5.1.2",
174 | "react-router-dom": "^5.1.2",
175 | "regenerator-runtime": "^0.13.5"
176 | },
177 | "browserslist": [
178 | "> 1%",
179 | "last 2 versions",
180 | "not ie < 9"
181 | ]
182 | }
183 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | /* Basic Options */
4 | // "incremental": true, /* Enable incremental compilation */
5 | "target": "es5", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019' or 'ESNEXT'. */
6 | "module": "esnext", /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', or 'ESNext'. */
7 | "lib": ["esnext", "dom", "dom.iterable"], /* Specify library files to be included in the compilation. */
8 | "allowJs": true, /* Allow javascript files to be compiled. */
9 | "checkJs": false, /* Report errors in .js files. */
10 | "skipLibCheck": true,
11 | "jsx": "preserve", /* Specify JSX code generation: 'preserve', 'react-native', or 'react'. */
12 | // "declaration": true, /* Generates corresponding '.d.ts' file. */
13 | // "declarationDir": "./lib/@types/react-store",
14 | // "declarationMap": true, /* Generates a sourcemap for each corresponding '.d.ts' file. */
15 | // "sourceMap": true, /* Generates corresponding '.map' file. */
16 | // "outFile": "./lib/react-store", /* Concatenate and emit output to single file. */
17 | "outDir": "./dist/common/src/", /* Redirect output structure to the directory. */
18 | // "rootDir": "./src/react-store", /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */
19 | // "composite": true, /* Enable project compilation */
20 | // "tsBuildInfoFile": "./", /* Specify file to store incremental compilation information */
21 | // "removeComments": true, /* Do not emit comments to output. */
22 | "noEmit": false, /* Do not emit outputs. */
23 | // "importHelpers": true, /* Import emit helpers from 'tslib'. */
24 | // "downlevelIteration": true, /* Provide full support for iterables in 'for-of', spread, and destructuring when targeting 'ES5' or 'ES3'. */
25 | "isolatedModules": false, /* Transpile each file as a separate module (similar to 'ts.transpileModule'). */
26 |
27 | /* Strict Type-Checking Options */
28 | "strict": true, /* Enable all strict type-checking options. */
29 | // "noImplicitAny": true, /* Raise error on expressions and declarations with an implied 'any' type. */
30 | // "strictNullChecks": true, /* Enable strict null checks. */
31 | // "strictFunctionTypes": true, /* Enable strict checking of function types. */
32 | // "strictBindCallApply": true, /* Enable strict 'bind', 'call', and 'apply' methods on functions. */
33 | // "strictPropertyInitialization": true, /* Enable strict checking of property initialization in classes. */
34 | // "noImplicitThis": true, /* Raise error on 'this' expressions with an implied 'any' type. */
35 | // "alwaysStrict": true, /* Parse in strict mode and emit "use strict" for each source file. */
36 |
37 | /* Additional Checks */
38 | // "noUnusedLocals": true, /* Report errors on unused locals. */
39 | // "noUnusedParameters": true, /* Report errors on unused parameters. */
40 | // "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */
41 | // "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */
42 | "resolveJsonModule": true,
43 | /* Module Resolution Options */
44 | "moduleResolution": "node", /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */
45 | "baseUrl": "./", /* Base directory to resolve non-absolute module names. */
46 | "paths": {
47 | "@channel": ["buildConfig/channel/common/index.ts"],
48 | "@channel/*": ["buildConfig/channel/common/*"],
49 | "@history": ["src/routes/history.ts"],
50 | // "@": ["src"],
51 | "@/*": ["src/*"],
52 | "@base/*": ["src/components/base/*"],
53 | "@biz/*": ["src/components/business/*"],
54 | "@redux-devtool": ["src/store/common/redux.devtool.ts"],
55 | }, /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */
56 | // "rootDirs": [], /* List of root folders whose combined content represents the structure of the project at runtime. */
57 | "typeRoots": [
58 | "node_modules/@types",
59 | "typings"
60 | ], /* List of folders to include type definitions from. */
61 | // "types": [], /* Type declaration files to be included in compilation. */
62 | "allowSyntheticDefaultImports": true, /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */
63 | "esModuleInterop": true, /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */
64 | // "preserveSymlinks": true, /* Do not resolve the real path of symlinks. */
65 | // "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */
66 |
67 | /* Source Map Options */
68 | "sourceRoot": "./dist/common/src/", /* Specify the location where debugger should locate TypeScript files instead of source locations. */
69 | // "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */
70 | "inlineSourceMap": true, /* Emit a single file with source maps instead of having a separate file. */
71 | "inlineSources": true, /* Emit the source alongside the sourcemaps within a single file; requires '--inlineSourceMap' or '--sourceMap' to be set. */
72 |
73 | /* Experimental Options */
74 | "experimentalDecorators": true /* Enables experimental support for ES7 decorators. */
75 | // "emitDecoratorMetadata": true, /* Enables experimental support for emitting type metadata for decorators. */
76 | },
77 | "exclude": [
78 | "node_modules",
79 | "@channel",
80 | "scripts",
81 | "createPage",
82 | "mock-server"
83 | ],
84 | "include": [
85 | "src",
86 | "typings"
87 | ]
88 | }
89 |
--------------------------------------------------------------------------------
/.eslintrc.js:
--------------------------------------------------------------------------------
1 | // http://eslint.org/docs/user-guide/configuring
2 |
3 | module.exports = {
4 | root: true,
5 | env: {
6 | browser: true,
7 | // "commonjs": true,
8 | es6: true,
9 | },
10 | extends: [
11 | // "eslint:recommended",
12 | // "plugin:react/recommended"
13 | 'airbnb',
14 | ],
15 | // extends: "eslint:recommended",
16 | globals: {
17 | $: true,
18 | process: true,
19 | __dirname: true,
20 | },
21 | parser: 'babel-eslint',
22 | parserOptions: {
23 | //es6的module模式
24 | sourceType: 'module',
25 | ecmaFeatures: {
26 | experimentalObjectRestSpread: true,
27 | jsx: true,
28 | },
29 | ecmaVersion: 11, // es2020
30 | },
31 | settings: {
32 | 'import/ignore': ['node_modules', 'DynamicForm', '.s?css', '@w*'],
33 | },
34 | plugins: ['react', 'react-hooks', 'import', 'jsx-a11y'],
35 |
36 | overrides: [{
37 | files: ['**/*.ts', '**/*.tsx'],
38 | parser: '@typescript-eslint/parser',
39 | parserOptions: {
40 | ecmaVersion: 11,
41 | sourceType: 'module',
42 | ecmaFeatures: {
43 | jsx: true,
44 | },
45 | // typescript-eslint specific options
46 | warnOnUnsupportedTypeScriptVersion: true,
47 | },
48 | plugins: ['@typescript-eslint'],
49 | // If adding a typescript-eslint version of an existing ESLint rule,
50 | // make sure to disable the ESLint rule here.
51 | rules: {
52 | // TypeScript's `noFallthroughCasesInSwitch` option is more robust (#6906)
53 | 'default-case': 'off',
54 | // 'tsc' already handles this (https://github.com/typescript-eslint/typescript-eslint/issues/291)
55 | 'no-dupe-class-members': 'off',
56 | // Add TypeScript specific rules (and turn off ESLint equivalents)
57 | // '@typescript-eslint/no-angle-bracket-type-assertion': 'warn',
58 | 'no-array-constructor': 'off',
59 | '@typescript-eslint/no-array-constructor': 'warn',
60 | '@typescript-eslint/no-namespace': 'error',
61 | 'no-unused-vars': 1,
62 | '@typescript-eslint/no-unused-vars': [
63 | 'warn',
64 | {
65 | args: 'none',
66 | ignoreRestSiblings: true,
67 | },
68 | ],
69 | 'no-useless-constructor': 'off',
70 | '@typescript-eslint/no-useless-constructor': 'warn',
71 | },
72 | }],
73 | rules: {
74 | 'import/no-unresolved': 0,
75 | 'import/extensions': 0,
76 | 'import/order': 0,
77 | 'import/prefer-default-export': 0,
78 |
79 | 'react/prop-types': 0,
80 | 'react/jsx-filename-extension': 0,
81 | 'react/prefer-stateless-function': 0,
82 | 'react/jsx-indent': [2, 'tab'],
83 | 'react/jsx-indent-props': [2, 'tab'],
84 | 'react/jsx-tag-spacing': 0,
85 | 'react/jsx-props-no-spreading': 0,
86 | 'react/require-default-props': 0,
87 | // // @off 同构应用需要在 didMount 里写 setState
88 | 'react/no-did-mount-set-state': 0,
89 | 'react/button-has-type': 0,
90 | "react-hooks/rules-of-hooks": "error", // Checks rules of Hooks
91 | "react-hooks/exhaustive-deps": "warn", // Checks effect dependencies
92 |
93 | 'jsx-a11y/anchor-is-valid': 0,
94 | 'jsx-a11y/click-events-have-key-events': 0,
95 | 'jsx-a11y/mouse-events-have-key-events': 0,
96 | 'jsx-a11y/no-noninteractive-element-interactions': 0,
97 | 'jsx-a11y/no-static-element-interactions': 0,
98 | 'jsx-a11y/aria-role': 0,
99 | 'jsx-a11y/alt-text': 0,
100 | 'jsx-a11y/heading-has-content': 0,
101 | 'jsx-a11y/anchor-has-content': 0,
102 |
103 | 'no-return-assign': 0,
104 | 'consistent-return': 0,
105 | 'no-console': 0,
106 | 'no-plusplus': 0,
107 | 'linebreak-style': 0,
108 | 'no-unused-expressions': 0,
109 | // 0、1、2分别表示不开启检查、警告、错误
110 | indent: [2, 'tab', { SwitchCase: 1 }], // tab缩进
111 | // 圈复杂度
112 | complexity: [2, 9],
113 | 'max-params': [2, 7],
114 | 'max-depth': [2, 4],
115 | 'no-multiple-empty-lines': 0,
116 | 'max-len': [
117 | 'error',
118 | {
119 | code: 150,
120 | tabWidth: 4,
121 | ignoreComments: true,
122 | ignoreUrls: true,
123 | ignoreStrings: true,
124 | ignoreTemplateLiterals: true,
125 | ignoreRegExpLiterals: true,
126 | },
127 | ],
128 | 'no-tabs': 0,
129 | 'object-curly-newline': [
130 | 0,
131 | {
132 | ObjectExpression: 'always',
133 | ObjectPattern: { multiline: true },
134 | ImportDeclaration: 'never',
135 | ExportDeclaration: {
136 | multiline: true,
137 | },
138 | },
139 | ],
140 | 'object-curly-spacing': 0,
141 |
142 | 'arrow-parens': [2, 'as-needed'],
143 | // 最大回调层数
144 | 'max-nested-callbacks': [2, 3],
145 | 'no-unused-vars': [
146 | 1,
147 | {
148 | argsIgnorePattern: '^React',
149 | varsIgnorePattern: '[Rr]eact|[Ss]tyle',
150 | },
151 | ],
152 | 'no-extra-boolean-cast': 0,
153 | 'array-callback-return': 0,
154 | 'no-param-reassign': 0,
155 | 'jsx-quotes': [0, 'prefer-double'], //强制在JSX属性(jsx-quotes)中一致使用双引号
156 | 'no-underscore-dangle': 0,
157 | 'quote-props': 0,
158 | // "no-native-reassign": 2,//不能重写native对象
159 | // // if while function 后面的{必须与if在同一行,java风格。
160 | // "brace-style": [2, "1tbs", { "allowSingleLine": true }],
161 | // // 双峰驼命名格式
162 | // "camelcase": 2,
163 | // // 以方括号取对象属性时,[ 后面和 ] 前面是否需要空格, 可选参数 never, always
164 | // "computed-property-spacing": [2,"never"],
165 | // //允许箭头函数可以省略小括号
166 | // 'arrow-parens': 0,
167 | // 'no-extra-semi': 2, // 不允许多余的分号
168 | // //允许使用async-await函数
169 | // 'generator-star-spacing': 0,
170 | // //在开发环境开启debugger功能,生产环境禁止使用debugger
171 | // 'no-debugger': process.env.NODE_ENV === 'development' ? 0 : 2,
172 | // "quotes": [2, "single"], //单引号
173 | // "no-var": 2, //对var警告
174 | // "semi": ["error", "always"], //不强制使用分号
175 | // "no-irregular-whitespace": 0, //不规则的空白不允许
176 | // "no-alert": 2, //禁止使用alert confirm prompt
177 | // "no-lone-blocks": 0, //禁止不必要的嵌套块
178 | // "no-class-assign": 2, //禁止给类赋值
179 | // "no-cond-assign": 2, //禁止在条件表达式中使用赋值语句
180 | // "no-const-assign": 2, //禁止修改const声明的变量
181 | // "no-delete-var": 2, //不能对var声明的变量使用delete操作符
182 | // "no-dupe-keys": 2, //在创建对象字面量时不允许键重复
183 | // "no-duplicate-case": 2, //switch中的case标签不能重复
184 | // "no-dupe-args": 2, //函数参数不能重复
185 | // "no-empty": 2, //块语句中的内容不能为空
186 | // "no-func-assign": 2, //禁止重复的函数声明
187 | // "no-invalid-this": 0, //禁止无效的this,只能用在构造器,类,对象字面量
188 | // "no-redeclare": 2, //禁止重复声明变量
189 | // "no-spaced-func": 2, //函数调用时 函数名与()之间不能有空格
190 | // "no-this-before-super": 0, //在调用super()之前不能使用this或super
191 | // "no-undef": 2, //不能有未定义的变量
192 | // "no-use-before-define": 2, //未定义前不能使用
193 | // // "camelcase": 0, //强制驼峰法命名
194 | // "no-mixed-spaces-and-tabs": 0, //禁止混用tab和空格
195 | // "prefer-arrow-callback": 0, //比较喜欢箭头回调
196 | // "arrow-spacing": 0, //=>的前/后括号
197 | //
198 | // // 禁止在 componentDidMount 里面使用 setState
199 |
200 | // // 禁止在 componentDidUpdate 里面使用 setState
201 | // 'react/no-did-update-set-state': 2,
202 | // // 禁止拼写错误
203 |
204 | // 'react/no-typos': 2,
205 | // // 禁止使用字符串 ref
206 | // 'react/no-string-refs': 2,
207 | // // @fixable 禁止出现 HTML 中的属性,如 class
208 | // 'react/no-unknown-property': 2,
209 | // // 禁止出现未使用的 propTypes
210 | // // @off 不强制要求写 propTypes
211 | // 'react/no-unused-prop-types': 2,
212 | // // 出现 jsx 的地方必须 import React
213 | // // @off 已经在 no-undef 中限制了
214 | // 'react/react-in-jsx-scope': 0,
215 | // // 非 required 的 prop 必须有 defaultProps
216 | // // @off 不强制要求写 propTypes
217 | // 'react/require-default-props': 0,
218 | // // render 方法中必须有返回值
219 | // 'react/require-render-return': 2,
220 | // // @fixable 组件内没有 children 时,必须使用自闭和写法
221 | // // @off 没必要限制
222 | // 'react/self-closing-comp': 0,
223 | // // style 属性的取值必须是 object
224 | // 'react/style-prop-object': 2,
225 | // // HTML 中的自闭和标签禁止有 children
226 | // 'react/void-dom-elements-no-children': 2,
227 | // // 数组中的 jsx 必须有 key
228 | // 'react/jsx-key': 2,
229 | // // 禁止在 jsx 中使用像注释的字符串
230 | // 'react/jsx-no-comment-textnodes': 2,
231 | // // 禁止出现重复的 props
232 | // 'react/jsx-no-duplicate-props': 2,
233 | // // 禁止使用未定义的 jsx elemet
234 | // 'react/jsx-no-undef': 2,
235 | // // jsx 文件必须 import React
236 | // 'react/jsx-uses-react': 2,
237 | // // 定义了的 jsx element 必须使用
238 | // 'react/jsx-uses-vars': 2,
239 | // // @fixable 多行的 jsx 必须有括号包起来
240 | // // @off 没必要限制
241 | // 'react/jsx-wrap-multilines': 2,
242 | // "react/no-array-index-key": 2, // 遍历出来的节点必须加key
243 | // "react/no-children-prop": 2, // 禁止使用children作为prop
244 | // "react/no-direct-mutation-state": 2, // 禁止直接this.state = 方式修改state 必须使用setState
245 | },
246 | };
247 |
--------------------------------------------------------------------------------
/.stylelintrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | 'extends': [
3 | 'stylelint-config-standard',
4 | 'stylelint-config-recommended',
5 | ],
6 | 'plugins': ['stylelint-order'],
7 | 'rules': {
8 | 'order/order': [
9 | // "at-rules",
10 | // "declarations",
11 | 'custom-properties',
12 | 'dollar-variables',
13 | 'rules',
14 | ],
15 | 'order/properties-order': [
16 | 'position',
17 | 'z-index',
18 | 'top',
19 | 'bottom',
20 | 'left',
21 | 'right',
22 | 'float',
23 | 'clear',
24 | 'columns',
25 | 'columns-width',
26 | 'columns-count',
27 | 'column-rule',
28 | 'column-rule-width',
29 | 'column-rule-style',
30 | 'column-rule-color',
31 | 'column-fill',
32 | 'column-span',
33 | 'column-gap',
34 | 'display',
35 | 'grid',
36 | 'grid-template-rows',
37 | 'grid-template-columns',
38 | 'grid-template-areas',
39 | 'grid-auto-rows',
40 | 'grid-auto-columns',
41 | 'grid-auto-flow',
42 | 'grid-column-gap',
43 | 'grid-row-gap',
44 | 'grid-template',
45 | 'grid-template-rows',
46 | 'grid-template-columns',
47 | 'grid-template-areas',
48 | 'grid-gap',
49 | 'grid-row-gap',
50 | 'grid-column-gap',
51 | 'grid-area',
52 | 'grid-row-start',
53 | 'grid-row-end',
54 | 'grid-column-start',
55 | 'grid-column-end',
56 | 'grid-column',
57 | 'grid-column-start',
58 | 'grid-column-end',
59 | 'grid-row',
60 | 'grid-row-start',
61 | 'grid-row-end',
62 | 'flex',
63 | 'flex-grow',
64 | 'flex-shrink',
65 | 'flex-basis',
66 | 'flex-flow',
67 | 'flex-direction',
68 | 'flex-wrap',
69 | 'justify-content',
70 | 'align-content',
71 | 'align-items',
72 | 'align-self',
73 | 'order',
74 | 'table-layout',
75 | 'empty-cells',
76 | 'caption-side',
77 | 'border-collapse',
78 | 'border-spacing',
79 | 'list-style',
80 | 'list-style-type',
81 | 'list-style-position',
82 | 'list-style-image',
83 | 'ruby-align',
84 | 'ruby-merge',
85 | 'ruby-position',
86 | 'box-sizing',
87 | 'width',
88 | 'min-width',
89 | 'max-width',
90 | 'height',
91 | 'min-height',
92 | 'max-height',
93 | 'padding',
94 | 'padding-top',
95 | 'padding-right',
96 | 'padding-bottom',
97 | 'padding-left',
98 | 'margin',
99 | 'margin-top',
100 | 'margin-right',
101 | 'margin-bottom',
102 | 'margin-left',
103 | 'border',
104 | 'border-width',
105 | 'border-top-width',
106 | 'border-right-width',
107 | 'border-bottom-width',
108 | 'border-left-width',
109 | 'border-style',
110 | 'border-top-style',
111 | 'border-right-style',
112 | 'border-bottom-style',
113 | 'border-left-style',
114 | 'border-color',
115 | 'border-top-color',
116 | 'border-right-color',
117 | 'border-bottom-color',
118 | 'border-left-color',
119 | 'border-image',
120 | 'border-image-source',
121 | 'border-image-slice',
122 | 'border-image-width',
123 | 'border-image-outset',
124 | 'border-image-repeat',
125 | 'border-top',
126 | 'border-top-width',
127 | 'border-top-style',
128 | 'border-top-color',
129 | 'border-top',
130 | 'border-right-width',
131 | 'border-right-style',
132 | 'border-right-color',
133 | 'border-bottom',
134 | 'border-bottom-width',
135 | 'border-bottom-style',
136 | 'border-bottom-color',
137 | 'border-left',
138 | 'border-left-width',
139 | 'border-left-style',
140 | 'border-left-color',
141 | 'border-radius',
142 | 'border-top-right-radius',
143 | 'border-bottom-right-radius',
144 | 'border-bottom-left-radius',
145 | 'border-top-left-radius',
146 | 'outline',
147 | 'outline-width',
148 | 'outline-color',
149 | 'outline-style',
150 | 'outline-offset',
151 | 'overflow',
152 | 'overflow-x',
153 | 'overflow-y',
154 | 'resize',
155 | 'visibility',
156 | 'font',
157 | 'font-style',
158 | 'font-variant',
159 | 'font-weight',
160 | 'font-stretch',
161 | 'font-size',
162 | 'font-family',
163 | 'font-synthesis',
164 | 'font-size-adjust',
165 | 'font-kerning',
166 | 'line-height',
167 | 'text-align',
168 | 'text-align-last',
169 | 'vertical-align',
170 | 'text-overflow',
171 | 'text-justify',
172 | 'text-transform',
173 | 'text-indent',
174 | 'text-emphasis',
175 | 'text-emphasis-style',
176 | 'text-emphasis-color',
177 | 'text-emphasis-position',
178 | 'text-decoration',
179 | 'text-decoration-color',
180 | 'text-decoration-style',
181 | 'text-decoration-line',
182 | 'text-underline-position',
183 | 'text-shadow',
184 | 'white-space',
185 | 'overflow-wrap',
186 | 'word-wrap',
187 | 'word-break',
188 | 'line-break',
189 | 'hyphens',
190 | 'letter-spacing',
191 | 'word-spacing',
192 | 'quotes',
193 | 'tab-size',
194 | 'orphans',
195 | 'writing-mode',
196 | 'text-combine-upright',
197 | 'unicode-bidi',
198 | 'text-orientation',
199 | 'direction',
200 | 'text-rendering',
201 | 'font-feature-settings',
202 | 'font-language-override',
203 | 'image-rendering',
204 | 'image-orientation',
205 | 'image-resolution',
206 | 'shape-image-threshold',
207 | 'shape-outside',
208 | 'shape-margin',
209 | 'color',
210 | 'background',
211 | 'background-image',
212 | 'background-position',
213 | 'background-size',
214 | 'background-repeat',
215 | 'background-origin',
216 | 'background-clip',
217 | 'background-attachment',
218 | 'background-color',
219 | 'background-blend-mode',
220 | 'isolation',
221 | 'clip-path',
222 | 'mask',
223 | 'mask-image',
224 | 'mask-mode',
225 | 'mask-position',
226 | 'mask-size',
227 | 'mask-repeat',
228 | 'mask-origin',
229 | 'mask-clip',
230 | 'mask-composite',
231 | 'mask-type',
232 | 'filter',
233 | 'box-shadow',
234 | 'opacity',
235 | 'transform-style',
236 | 'transform',
237 | 'transform-box',
238 | 'transform-origin',
239 | 'perspective',
240 | 'perspective-origin',
241 | 'backface-visibility',
242 | 'transition',
243 | 'transition-property',
244 | 'transition-duration',
245 | 'transition-timing-function',
246 | 'transition-delay',
247 | 'animation',
248 | 'animation-name',
249 | 'animation-duration',
250 | 'animation-timing-function',
251 | 'animation-delay',
252 | 'animation-iteration-count',
253 | 'animation-direction',
254 | 'animation-fill-mode',
255 | 'animation-play-state',
256 | 'scroll-behavior',
257 | 'scroll-snap-type',
258 | 'scroll-snap-destination',
259 | 'scroll-snap-coordinate',
260 | 'cursor',
261 | 'touch-action',
262 | 'caret-color',
263 | 'ime-mode',
264 | 'object-fit',
265 | 'object-position',
266 | 'content',
267 | 'counter-reset',
268 | 'counter-increment',
269 | 'will-change',
270 | 'pointer-events',
271 | 'all',
272 | 'page-break-before',
273 | 'page-break-after',
274 | 'page-break-inside',
275 | 'widows',
276 | ],
277 | 'selector-type-case': null,
278 | 'selector-type-no-unknown': null,
279 | 'indentation': 'tab',
280 | 'color-no-invalid-hex': true,
281 | 'font-family-no-missing-generic-family-keyword': null,
282 | 'font-family-name-quotes': null,
283 | 'function-url-quotes': 'always',
284 | 'at-rule-no-unknown': null,
285 | 'no-eol-whitespace': null,
286 | 'selector-attribute-quotes': 'always',
287 | 'string-quotes': 'single',
288 | 'selector-pseudo-element-colon-notation': null,
289 | 'at-rule-no-vendor-prefix': true,
290 | 'media-feature-name-no-vendor-prefix': null,
291 | 'media-feature-name-no-unknown': null,
292 | 'property-no-vendor-prefix': null,
293 | 'selector-no-vendor-prefix': true,
294 | 'value-no-vendor-prefix': true,
295 | 'selector-pseudo-class-no-unknown': null,
296 | 'shorthand-property-no-redundant-values': null,
297 | 'at-rule-empty-line-before': null,
298 | 'at-rule-name-space-after': null,
299 | 'comment-empty-line-before': null,
300 | 'declaration-bang-space-before': null,
301 | 'declaration-empty-line-before': null,
302 | 'function-comma-newline-after': null,
303 | 'function-name-case': null,
304 | 'function-parentheses-newline-inside': null,
305 | 'function-max-empty-lines': null,
306 | 'function-whitespace-after': null,
307 | 'number-leading-zero': null,
308 | 'number-no-trailing-zeros': null,
309 | 'rule-empty-line-before': null,
310 | 'selector-combinator-space-after': null,
311 | 'selector-list-comma-newline-after': null,
312 | // "selector-pseudo-element-colon-notation": null,
313 | 'unit-no-unknown': null,
314 | 'no-descending-specificity': null,
315 | 'value-list-max-empty-lines': null,
316 | },
317 | };
318 |
--------------------------------------------------------------------------------