Repository: zhangkun-Jser/react-kit Branch: master Commit: df46946e8e9d Files: 48 Total size: 89.8 KB Directory structure: gitextract_wmw74cji/ ├── .babelrc ├── .eslintignore ├── .eslintrc.js ├── .gitignore ├── README.md ├── asset/ │ └── css/ │ ├── app.less │ ├── base.less │ ├── icon.less │ ├── reset.less │ ├── variables.less │ └── vendor_antd.less ├── build/ │ ├── env.js │ ├── postcss.config.js │ ├── publicPath.js │ ├── theme.js │ ├── webpack.base.conf.js │ ├── webpack.dev.conf.js │ └── webpack.prod.conf.js ├── index.html ├── mock/ │ ├── config/ │ │ └── mockConfig.json │ ├── data/ │ │ └── skuunit/ │ │ ├── deleteSkuUnit.json │ │ ├── showSkuUnitList.json │ │ └── updateSkuUnitPause.json │ └── template/ │ └── query/ │ └── table.template ├── package.json ├── src/ │ ├── app.jsx │ ├── common/ │ │ └── request.js │ ├── components/ │ │ ├── ErrorBoundary.jsx │ │ ├── Layout/ │ │ │ ├── Footer.jsx │ │ │ ├── Header.jsx │ │ │ ├── Sider.jsx │ │ │ └── index.css │ │ ├── Pages/ │ │ │ ├── index.css │ │ │ └── index.jsx │ │ └── index.jsx │ ├── constants/ │ │ ├── constant.js │ │ └── url.js │ ├── redux/ │ │ ├── configureStore.js │ │ ├── reducers.js │ │ └── skuunit/ │ │ ├── api.js │ │ └── skuunit.js │ └── routers/ │ ├── PrimaryLayout.jsx │ ├── Skuunit/ │ │ ├── columns.jsx │ │ └── index.jsx │ ├── index.jsx │ └── index.less ├── template.html └── webpack.config.js ================================================ FILE CONTENTS ================================================ ================================================ FILE: .babelrc ================================================ { "presets": [ "@babel/preset-env", "@babel/preset-react" ], "plugins": [ [ "@babel/plugin-proposal-decorators", { "legacy": true } ], "@babel/plugin-proposal-class-properties", "@babel/plugin-proposal-export-default-from", "@babel/plugin-transform-runtime", "@babel/plugin-syntax-dynamic-import", "react-hot-loader/babel", [ "import", { "libraryName": "antd", "libraryDirectory": "es", "style": true } ], [ "transform-imports", { "react-router": { "transform": "react-router/${member}", "preventFullImport": true } } ] ], "env": { "production": { "plugins": [ [ "transform-react-remove-prop-types", { "mode": "wrap", "ignoreFilenames": [ "node_modules" ] } ] ] } } } ================================================ FILE: .eslintignore ================================================ node_modules dist ================================================ FILE: .eslintrc.js ================================================ module.exports = { "env": { "node": true, "browser": true, "es6": true, "commonjs": true, }, "parser": "babel-eslint", "parserOptions": { "ecmaVersion": 6, "sourceType": "module", "ecmaFeatures": { "jsx": true, "modules": true, "experimentalObjectRestSpread": true } }, "plugins": [ "react", "react-hooks" ], "rules": { "react-hooks/rules-of-hooks": "error", "react-hooks/exhaustive-deps": "warn", // 推荐规则 0="off", 1="warn", 2="error" "no-compare-neg-zero": 2, "no-cond-assign": 2, "no-console": 0,// 不采用 构建时自己去除 "no-constant-condition": 2,// 采用 "no-control-regex": 2,// 采用 "no-debugger": 0,// node端采用,web端不采用 "no-dupe-args": 2, // 采用 "no-dupe-keys": 2,// 采用 "no-duplicate-case": 2,// 采用 "no-empty": 2, // 采用 "no-empty-character-class": 2,// 采用 "no-ex-assign": 2, // 采用 "no-extra-boolean-cast": 2, // 采用 "no-extra-parens": 0, // 不采用 "no-extra-semi": 2,// 采用 "no-func-assign": 2,// 采用 "no-inner-declarations": 2, // 采用 "no-invalid-regexp": 2,// 采用 "no-irregular-whitespace": 2,// 采用 "no-obj-calls": 2,// 采用 "no-regex-spaces": 2,// 采用 "no-sparse-arrays": 2,// 采用 "no-unexpected-multiline": 2,// 采用 "no-unreachable": 2,// 采用 "no-unsafe-finally": 2,// 采用 "no-unsafe-negation": 2,// 采用 "use-isnan": 2,// 采用 "valid-typeof": 2,// 采用 "no-case-declarations": 2, // 采用 "no-empty-pattern": 2,// 采用 "no-fallthrough": 2,// 采用 "no-global-assign": 2,// 采用 "no-octal": 2,// 采用 "no-redeclare": 2,// 采用 "no-self-assign": 2,// 采用 "no-unused-labels": 2,// 采用 "no-useless-escape": 2,// 采用 "no-delete-var": 2,// 采用 "no-undef": 2,// 采用 "no-unused-vars": 2,// 采用 "no-mixed-spaces-and-tabs": 2,// 采用 "constructor-super": 2,// 采用 "no-class-assign": 2,// 采用 "no-const-assign": 2,// 采用 "no-dupe-class-members": 2,// 采用 "no-new-symbol": 2,// 采用 "no-this-before-super": 2,// 采用 "require-yield": 2,// 采用 // 拓展规则 "for-direction": 2,// 采用 "getter-return": 2,// 采用 "no-await-in-loop": 2,// 采用 "no-prototype-builtins": 2,// 采用 "no-template-curly-in-string": 2,// 采用 // valid-jsdoc 不采用,基础组件强制使用 "accessor-pairs": 2,// 采用 "array-callback-return": 2,// 采用 "block-scoped-var": 2,// 采用 "class-methods-use-this": 1,//采用(warning,可能报错很多) "curly": 1,// 采用 warning "default-case": 2,// 采用 "eqeqeq": 1,// 采用 warning "guard-for-in": 2,// 采用 "no-caller": 2, // 采用 "no-eval": 2,// 采用 "no-extend-native": 2,// 采用 "no-extra-label": 2,// 采用 "no-floating-decimal": 2,// 采用 "no-implied-eval": 2,// 采用 // 临时关闭,有误报 "no-invalid-this": 0,// 采用 "no-iterator": 2,// 采用 "no-labels": 2,// 采用 "no-lone-blocks": 2,// 采用 "no-throw-literal": 2,// 采用 "no-unmodified-loop-condition": 1,// 采用 warning "no-useless-concat": 2,// 采用 "no-useless-return": 2,// 采用 "radix": 2,// 采用 "require-await": 2,// 采用 //关于Node.js或在浏览器中使用CommonJS的相关规则 "callback-return": 2,// 采用 // 临时关闭 "global-require": 0,// 采用 "handle-callback-err": 2,// 采用 "no-buffer-constructor": 2,// 采用 "no-mixed-requires": 2,// 采用 "no-new-require": 2,// 采用 // 代码风格 // 在数组开括号后和闭括号前强制换行 "array-bracket-newline": 0, // 不采用 // 禁止或强制在括号内使用空格 "array-bracket-spacing": [2, "never"], // 采用,禁止在数组括号内出现空格, // 强制数组元素间出现换行 "array-element-newline": 0, // 不采用 // 禁止或强制在代码块中开括号前和闭括号后有空格 "block-spacing": [2, "always"], // 采用,要求使用一个或多个空格 // 强制在代码块中使用一致的大括号风格 "brace-style": [2, "1tbs", { allowSingleLine: true }], // 强制 one true brace style(一种代码风格,将大括号放在控制语句或声明语句同一行的位置) // 可以有例外情况, allowSingleLine允许块的开括号和闭括号在 同一行 // 要求使用骆驼拼写法 "camelcase": [2, { properties: "never" }], // 采用,但不检查属性名称 // 强制或禁止对注释的第一个字母大写 "capitalized-comments": 0, // 不采用 // 要求或禁止使用拖尾逗号 "comma-dangle": [2, { arrays: "always-multiline", objects: "always-multiline", imports: "always-multiline", exports: "always-multiline", functions: "always-multiline", }], // 采用,当最后一个元素或属性与闭括号 ] 或 } 在 不同的行时,要求使用拖尾逗号;当在 同一行时,禁止使用拖尾逗号。 // 强制在逗号周围使用空格 "comma-spacing": [2, { before: false, after: true }], // 采用,禁止在逗号前使用空格,要求在逗号后使用一个或多个空格 // 逗号风格 "comma-style": [2, "last", { // last 要求逗号放在数组元素、对象属性或变量声明之后,且在同一行 exceptions: { // 额外规则 包含与 JavaScript 代码的抽象语法树 (AST) 的节点类型对应的属性: ArrayExpression: false, // 忽略数组字面量的逗号风格 ArrayPattern: false, // 忽略数组的解构赋值语句中的逗号风格 ArrowFunctionExpression: false, // 忽略箭头函数表达式的参数中的逗号风格 CallExpression: false, // 忽略函数调用的参数中的逗号风格 FunctionDeclaration: false, // 忽略函数声明的参数中的逗号风格 FunctionExpression: false, // 忽略函数表达式的参数中的逗号风格 ImportDeclaration: false, // 忽略 import 语句中的逗号风格 ObjectExpression: false, // 忽略对象字面量的逗号风格 ObjectPattern: false, // 忽略对象的解构赋值中的逗号风格 VariableDeclaration: false, // 忽略变量声明的逗号风格 NewExpression: false, // 忽略构造函数表达式参数中的逗号风格 } // !!! 注意,以上配置全为false,即不忽略 }], // 采用 // 禁止或强制在计算属性中使用空格 "computed-property-spacing": [2, 'never'], // 采用,禁止在计算属性内使用空格 // 要求一致的 This "consistent-this": 0, // 不采用 // 要求或禁止文件末尾保留一行空行 "eol-last": [2, 'always'], // 采用 强制使用换行 (LF) // 要求或禁止在函数标识符和其调用之间有空格 "func-call-spacing": [2, 'never'], // 禁止在函数名和开括号之间有空格 // 要求函数名与赋值给它们的变量名或属性名相匹配 "func-name-matching": 0, // 不采用 // 要求或禁止命名的 function 表达式 "func-names": 1, // 警告 // 强制 function 声明或表达式的一致性 "func-style": 0, // 不采用 // 强制在函数括号内使用一致的换行 "function-paren-newline": [2, 'consistent'], // 采用, 要求每个括号使用一致的换行。如果一个括号有换行,另一个括号没有换行,则报错。 // 禁止使用指定的标识符 "id-blacklist": 0, // 不采用 // 强制标识符的最小和最大长度 "id-length": 0, // 不采用 // 要求标识符匹配一个指定的正则表达式 "id-match": 0, // 不采用 // 强制隐式返回的箭头函数体的位置 "implicit-arrow-linebreak": [2, 'beside'], // 采用 禁止在箭头函数体之前出现换行 // 强制使用一致的缩进 "indent": [2, 2, { SwitchCase: 1, VariableDeclarator: 1, outerIIFEBody: 1, // MemberExpression: null, FunctionDeclaration: { parameters: 1, body: 1 }, FunctionExpression: { parameters: 1, body: 1 }, CallExpression: { arguments: 1 }, ArrayExpression: 1, ObjectExpression: 1, ImportDeclaration: 1, flatTernaryExpressions: false, // list derived from https://github.com/benjamn/ast-types/blob/HEAD/def/jsx.js ignoredNodes: ['JSXElement', 'JSXElement > *', 'JSXAttribute', 'JSXIdentifier', 'JSXNamespacedName', 'JSXMemberExpression', 'JSXSpreadAttribute', 'JSXExpressionContainer', 'JSXOpeningElement', 'JSXClosingElement', 'JSXText', 'JSXEmptyExpression', 'JSXSpreadChild'], ignoreComments: false }], // 采用 一般2个缩进,特殊语句见配置 // 强制在 JSX 属性中一致地使用双引号或单引号 "jsx-quotes": 0, // 不采用 // 强制在对象字面量的属性中键和值之间使用一致的间距 "key-spacing": [2, { beforeColon: false, afterColon: true }], // 采用, 禁止在对象字面量的键和冒号之间存在空格,要求在对象字面量的冒号和值之间存在至少有一个空格 // 强制在关键字前后使用一致的空格 "keyword-spacing": [2, { before: true, // 要求在关键字之前至少有一个空格 after: true, // 要求在关键字之后至少有一个空格 overrides: { // 允许覆盖指定的关键字的空格风格 return: { after: true }, throw: { after: true }, case: { after: true } } }], // 采用 // 强制行注释的位置 "line-comment-position": 0, // 不采用 // 强制使用一致的换行风格 "linebreak-style": [2, 'unix'], // 采用,强制使用 Unix 换行符: \n。 // 要求在注释周围有空行 "lines-around-comment": 0, //不采用 // 要求或禁止类成员之间出现空行 "lines-between-class-members": [2, 'always', { exceptAfterSingleLine: false }],// 采用 // 强制可嵌套的块的最大深度 "max-depth": 0, // 不采用 // 强制一行的最大长度 "max-len": [2, 100, 2, { ignoreUrls: true, ignoreComments: false, ignoreRegExpLiterals: true, ignoreStrings: true, ignoreTemplateLiterals: true, }],// 采用,最长100,tab字符宽度为2 // 强制最大行数 "max-lines": 0, // 不采用 // 强制回调函数最大嵌套深度 "max-nested-callbacks": 0, // 不采用 // 强制函数定义中最多允许的参数数量 "max-params": 0, //不采用 // 强制函数块最多允许的的语句数量 "max-statements": 0, // 不采用 // 强制每一行中所允许的最大语句数量 "max-statements-per-line": 0, // 不采用 // 强制对多行注释使用特定风格 "multiline-comment-style": 0, // 不采用 // 要求或禁止在三元操作数中间换行 "multiline-ternary": 0, // 不采用 // 要求构造函数首字母大写 "new-cap": [2, { newIsCap: true, // 要求调用 new 操作符时有首字母大小的函数 newIsCapExceptions: [], capIsNew: false, // 要求调用首字母大写的函数时有 new 操作符 capIsNewExceptions: ['Immutable.Map', 'Immutable.Set', 'Immutable.List'], // 允许调用指定的首字母大写的函数时没有 new 操作符 }], // 采用 // 要求调用无参构造函数时有圆括号 "new-parens": 2, // 采用 // 要求方法链中每个调用都有一个换行符 "newline-per-chained-call": [2, { ignoreChainWithDepth: 4 }], // 允许在同一行成链的深度为4 // 禁用 Array 构造函数 "no-array-constructor": 2, // 采用 // 禁用按位运算符 "no-bitwise": 2, // 采用 // 禁用 continue 语句 "no-continue": 2, // 采用 // 禁止在代码后使用内联注释 "no-inline-comments": 0, // 不采用 // 禁止 if 作为唯一的语句出现在 else 语句中 "no-lonely-if": 2, // 采用 // 禁止混合使用不同的操作符 "no-mixed-operators": [2, { groups: [ ['%', '**'], ['%', '+'], ['%', '-'], ['%', '*'], ['%', '/'], ['**', '+'], ['**', '-'], ['**', '*'], ['**', '/'], ['&', '|', '^', '~', '<<', '>>', '>>>'], ['==', '!=', '===', '!==', '>', '>=', '<', '<='], ['&&', '||'], ['in', 'instanceof'] ], allowSamePrecedence: false }], // 采用 // 禁止连续赋值 "no-multi-assign": 2, // 采用 // 禁止出现多行空行 "no-multiple-empty-lines": [2, { max: 2, maxEOF: 0 }], // 采用 // 禁用否定的表达式 "no-negated-condition": 0, // 不采用 // 禁用嵌套的三元表达式 "no-nested-ternary": 2, // 采用 // 禁用 Object 的构造函数 "no-new-object": 2, // 采用 // 禁用一元操作符 ++ 和 -- // 临时关闭 "no-plusplus": 0, // 采用 // 禁用特定的语法 "no-restricted-syntax": [ 2, { selector: 'ForInStatement', message: 'for..in loops iterate over the entire prototype chain, which is virtually never what you want. Use Object.{keys,values,entries}, and iterate over the resulting array.', }, { selector: 'ForOfStatement', message: 'iterators/generators require regenerator-runtime, which is too heavyweight for this guide to allow them. Separately, loops should be avoided in favor of array iterations.', }, { selector: 'LabeledStatement', message: 'Labels are a form of GOTO; using them makes code confusing and hard to maintain and understand.', }, { selector: 'WithStatement', message: '`with` is disallowed in strict mode because it makes code impossible to predict and optimize.', }, ], // 采用 // 禁用 tab "no-tabs": 2, // 采用 // 禁用三元操作符 "no-ternary": 0, //不采用 // 禁用行尾空格 "no-trailing-spaces": [2, { skipBlankLines: false, ignoreComments: false, }], // 采用 // 禁止标识符中有悬空下划线(临时关闭) /* "no-underscore-dangle": [2, { allow: [], allowAfterThis: false, allowAfterSuper: false, enforceInMethodNames: false, }], */ // 采用 // 禁止可以在有更简单的可替代的表达式时使用三元操作符 "no-unneeded-ternary": [2, { defaultAssignment: false }], // 禁止属性前有空白 "no-whitespace-before-property": 2, //采用 // 强制单个语句的位置 "nonblock-statement-body-position": [2, 'beside', { overrides: {} }], // 采用 // 强制大括号内换行符的一致性 "object-curly-newline": [2, { ObjectExpression: { minProperties: 4, multiline: true, consistent: true }, ObjectPattern: { minProperties: 4, multiline: true, consistent: true }, ImportDeclaration: { minProperties: 4, multiline: true, consistent: true }, ExportDeclaration: { minProperties: 4, multiline: true, consistent: true }, }],// 采用 // 强制在大括号中使用一致的空格 "object-curly-spacing": [2, 'always'], // 采用 // 强制将对象的属性放在不同的行上 "object-property-newline": [2, { allowAllPropertiesOnSameLine: true, }], // 强制函数中的变量要么一起声明要么分开声明 "one-var": [2, 'never'], // 采用, 要求每个作用域有多个变量声明 // 要求或禁止在变量声明周围换行 "one-var-declaration-per-line": [2, 'always'], // 采用,强制每个变量声明都换行 // 要求或禁止在可能的情况下使用简化的赋值操作符 "operator-assignment": [2, 'always'], //采用,要求尽可能地简化赋值操作 // 强制操作符使用一致的换行符 "operator-linebreak": [2, 'before', { overrides: { '=': 'none' } }], // 采用, 要求把换行符放在操作符前面 // 要求或禁止块内填充 "padded-blocks": [2, { blocks: 'never', classes: 'never', switches: 'never' }], // 采用 // 要求或禁止在语句间填充空行 "padding-line-between-statements": 0, // 不采用 // 要求对象字面量属性名称用引号括起来 "quote-props": [2, 'as-needed', { keywords: false, unnecessary: true, numbers: false }], //采用 // 强制使用一致的反勾号、双引号或单引号 "quotes": [2, 'single', { avoidEscape: true }], // 采用 // 要求使用 JSDoc 注释 "require-jsdoc": 0, // 不采用 // 要求或禁止使用分号代替 ASI "semi": [2, 'always'], // 采用,要求在语句末尾使用分号 // 强制分号之前和之后使用一致的空格 "semi-spacing": ['error', { before: false, after: true }], // 采用 // 强制分号的位置 "semi-style": [2, 'last'], // 采用,强制分号出现在句子末尾。 // 要求对象属性按序排列 "sort-keys": 0, // 不采用 // 要求同一个声明块中的变量按顺序排列 "sort-vars": 0, // 不采用 // 强制在块之前使用一致的空格 "space-before-blocks": 2, // 采用 // 强制在 function的左括号之前使用一致的空格 "space-before-function-paren": [2, { anonymous: 'always', named: 'never', asyncArrow: 'always' }], // 采用 // 强制在圆括号内使用一致的空格 "space-in-parens": [2, 'never'], // 采用,强制圆括号内没有空格 // 要求操作符周围有空格 "space-infix-ops": 2, // 采用 // 强制在一元操作符前后使用一致的空格 "space-unary-ops": [2, { words: true, nonwords: false, overrides: { }, }], // 采用 // 强制在注释中 // 或 /* 使用一致的空格 "spaced-comment": [2, 'always', { line: { exceptions: ['-', '+'], markers: ['=', '!'], // space here to support sprockets directives }, block: { exceptions: ['-', '+'], markers: ['=', '!'], // space here to support sprockets directives balanced: true, } }], // 采用 // 强制在 switch 的冒号左右有空格 "switch-colon-spacing": [2, { after: true, before: false }], // 采用 // 要求或禁止在模板标记和它们的字面量之间有空格 "template-tag-spacing": [2, 'never'], // 禁止在一个标记的函数和它的模板字面量之间有空格 // 要求或禁止 Unicode 字节顺序标记 (BOM) "unicode-bom": [2, 'never'], // 采用 // 要求正则表达式被括号括起来 "wrap-regex": 0, // 不采用 // react相关配置 // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/display-name.md 'react/display-name': ['off', { ignoreTranspilerName: false }], // Forbid certain propTypes (any, array, object) // https://github.com/yannickcr/eslint-plugin-react/blob/843d71a432baf0f01f598d7cf1eea75ad6896e4b/docs/rules/forbid-prop-types.md 'react/forbid-prop-types': ['error', { forbid: ['any', 'array', 'object'], checkContextTypes: true, checkChildContextTypes: true, }], // Forbid certain props on DOM Nodes // https://github.com/yannickcr/eslint-plugin-react/blob/843d71a432baf0f01f598d7cf1eea75ad6896e4b/docs/rules/forbid-dom-props.md 'react/forbid-dom-props': ['off', { forbid: [] }], // Enforce boolean attributes notation in JSX // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/jsx-boolean-value.md 'react/jsx-boolean-value': ['error', 'never', { always: [] }], // Validate closing bracket location in JSX // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/jsx-closing-bracket-location.md 'react/jsx-closing-bracket-location': ['error', 'line-aligned'], // Validate closing tag location in JSX // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/jsx-closing-tag-location.md 'react/jsx-closing-tag-location': 'error', // Enforce or disallow spaces inside of curly braces in JSX attributes // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/jsx-curly-spacing.md 'react/jsx-curly-spacing': ['error', 'never', { allowMultiline: true }], // Enforce event handler naming conventions in JSX // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/jsx-handler-names.md 'react/jsx-handler-names': ['off', { eventHandlerPrefix: 'handle', eventHandlerPropPrefix: 'on', }], // Validate props indentation in JSX // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/jsx-indent-props.md 'react/jsx-indent-props': ['error', 2], // Validate JSX has key prop when in array or iterator // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/jsx-key.md 'react/jsx-key': 'off', // Limit maximum of props on a single line in JSX // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/jsx-max-props-per-line.md 'react/jsx-max-props-per-line': ['error', { maximum: 1, when: 'multiline' }], // Prevent usage of .bind() in JSX props // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/jsx-no-bind.md 'react/jsx-no-bind': ['error', { ignoreRefs: true, allowArrowFunctions: true, allowBind: false, }], // Prevent duplicate props in JSX // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/jsx-no-duplicate-props.md 'react/jsx-no-duplicate-props': ['error', { ignoreCase: true }], // Prevent usage of unwrapped JSX strings // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/jsx-no-literals.md 'react/jsx-no-literals': ['off', { noStrings: true }], // Disallow undeclared variables in JSX // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/jsx-no-undef.md 'react/jsx-no-undef': 'error', // Enforce PascalCase for user-defined JSX components // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/jsx-pascal-case.md 'react/jsx-pascal-case': ['error', { allowAllCaps: true, ignore: [], }], // Enforce propTypes declarations alphabetical sorting // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/sort-prop-types.md 'react/sort-prop-types': ['off', { ignoreCase: true, callbacksLast: false, requiredFirst: false, sortShapeProp: true, }], // Deprecated in favor of react/jsx-sort-props 'react/jsx-sort-prop-types': 'off', // Enforce props alphabetical sorting // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/jsx-sort-props.md 'react/jsx-sort-props': ['off', { ignoreCase: true, callbacksLast: false, shorthandFirst: false, shorthandLast: false, noSortAlphabetically: false, reservedFirst: true, }], // Enforce defaultProps declarations alphabetical sorting // https://github.com/yannickcr/eslint-plugin-react/blob/843d71a432baf0f01f598d7cf1eea75ad6896e4b/docs/rules/jsx-sort-default-props.md 'react/jsx-sort-default-props': ['off', { ignoreCase: true, }], // Prevent React to be incorrectly marked as unused // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/jsx-uses-react.md 'react/jsx-uses-react': ['error'], // Prevent variables used in JSX to be incorrectly marked as unused // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/jsx-uses-vars.md 'react/jsx-uses-vars': 'error', // Prevent usage of dangerous JSX properties // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/no-danger.md 'react/no-danger': 'warn', // Prevent usage of deprecated methods // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/no-deprecated.md 'react/no-deprecated': ['error'], // Prevent usage of setState in componentDidMount // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/no-did-mount-set-state.md // this is necessary for server-rendering 'react/no-did-mount-set-state': 'off', // Prevent usage of setState in componentDidUpdate // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/no-did-update-set-state.md 'react/no-did-update-set-state': 'error', // Prevent usage of setState in componentWillUpdate // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/no-will-update-set-state.md 'react/no-will-update-set-state': 'error', // Prevent direct mutation of this.state // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/no-direct-mutation-state.md 'react/no-direct-mutation-state': 'off', // Prevent usage of isMounted // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/no-is-mounted.md 'react/no-is-mounted': 'error', // Prevent multiple component definition per file // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/no-multi-comp.md 'react/no-multi-comp': ['error', { ignoreStateless: true }], // Prevent usage of setState // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/no-set-state.md 'react/no-set-state': 'off', // Prevent using string references // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/no-string-refs.md 'react/no-string-refs': 'error', // Prevent usage of unknown DOM property // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/no-unknown-property.md 'react/no-unknown-property': 'error', // Require ES6 class declarations over React.createClass // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/prefer-es6-class.md 'react/prefer-es6-class': ['error', 'always'], // Require stateless functions when not using lifecycle methods, setState or ref // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/prefer-stateless-function.md 'react/prefer-stateless-function': ['error', { ignorePureComponents: true }], // Prevent missing props validation in a React component definition // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/prop-types.md 'react/prop-types': ['error', { ignore: [], customValidators: [], skipUndeclared: false }], // Prevent missing React when using JSX // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/react-in-jsx-scope.md 'react/react-in-jsx-scope': 'error', // Require render() methods to return something // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/require-render-return.md 'react/require-render-return': 'error', // Prevent extra closing tags for components without children // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/self-closing-comp.md 'react/self-closing-comp': 'error', // Enforce component methods order // https://github.com/yannickcr/eslint-plugin-react/blob/843d71a432baf0f01f598d7cf1eea75ad6896e4b/docs/rules/sort-comp.md 'react/sort-comp': ['error', { order: [ 'static-methods', 'instance-variables', 'lifecycle', '/^on.+$/', 'getters', 'setters', '/^(get|set)(?!(InitialState$|DefaultProps$|ChildContext$)).+$/', 'instance-methods', 'everything-else', 'rendering', ], groups: { lifecycle: [ 'displayName', 'propTypes', 'contextTypes', 'childContextTypes', 'mixins', 'statics', 'defaultProps', 'constructor', 'getDefaultProps', 'getInitialState', 'state', 'getChildContext', 'componentWillMount', 'componentDidMount', 'componentWillReceiveProps', 'shouldComponentUpdate', 'componentWillUpdate', 'componentDidUpdate', 'componentWillUnmount', ], rendering: [ '/^render.+$/', 'render' ], }, }], // Prevent missing parentheses around multilines JSX // https://github.com/yannickcr/eslint-plugin-react/blob/843d71a432baf0f01f598d7cf1eea75ad6896e4b/docs/rules/jsx-wrap-multilines.md 'react/jsx-wrap-multilines': ['error', { declaration: 'parens-new-line', assignment: 'parens-new-line', return: 'parens-new-line', arrow: 'parens-new-line', condition: 'parens-new-line', logical: 'parens-new-line', prop: 'parens-new-line', }], // 当元素为多行时,要求JSX元素中的第一个属性位于新行上。 // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/jsx-first-prop-new-line.md 'react/jsx-first-prop-new-line': [2, 'multiline-multiprop'], // 采用,如果JSX标签占用多个行并且有多个属性,则第一个属性应该总是放置在一个新行上。这是默认值。 // 在JSX的等号两侧是否强制留有空格 // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/jsx-equals-spacing.md 'react/jsx-equals-spacing': [2, 'never'], // 采用,等号周围的不允许空格 // 强制jsx的缩进风格 // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/jsx-indent.md 'react/jsx-indent': [2, 2], // 采用,两个空格作为缩进 // 不允许不安全的target='_blank'用法, 具体见连接说明 // https://github.com/yannickcr/eslint-plugin-react/blob/ac102885765be5ff37847a871f239c6703e1c7cc/docs/rules/jsx-no-target-blank.md 'react/jsx-no-target-blank': [2, { enforceDynamicLinks: 'always' }], // 采用,如果是动态链接则不强制 // 只有.jsx文件允许写JSX语法 // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/jsx-filename-extension.md 'react/jsx-filename-extension': [2, { extensions: ['.jsx'] }],// 采用 // 防止JS注释意外的作为文本注入到JSX中 // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/jsx-no-comment-textnodes.md 'react/jsx-no-comment-textnodes': 2, // 采用,jsx的注释问题 // 不允许使用react或者reactdom的render方法的返回值 // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/no-render-return-value.md 'react/no-render-return-value': 2, // 采用,建议采用ref // 要求有shouldComponentUpdate方法, 或者采用PureRenderMixin // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/require-optimization.md 'react/require-optimization': [0, { allowDecorators: [] }], // 不采用 // 禁止使用findDOMNode()方法 // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/no-find-dom-node.md 'react/no-find-dom-node': 2, // 采用 // 在Components中禁用一些特定的props // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/forbid-component-props.md 'react/forbid-component-props': 0, // 不采用 // 禁用特定元素 // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/forbid-elements.md 'react/forbid-elements': 0, // 不采用 // 避免在children和dangerouslySetInnerHTML属性共存时出现问题 // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/no-danger-with-children.md 'react/no-danger-with-children': 2, // 采用 // 防止未使用的propType定义 // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/no-unused-prop-types.md 'react/no-unused-prop-types': [2, { customValidators: [ ], skipShapeProps: true, }], // 采用 // 要求样式的值为对象或者变量 // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/style-prop-object.md 'react/style-prop-object': 2, // 采用 // 禁止无效字符的出现 // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/no-unescaped-entities.md 'react/no-unescaped-entities': 2, // 采用 // 禁止通过children props来传值 // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/no-children-prop.md 'react/no-children-prop': 2, // 采用 // 在JSX打开和关闭括号内和周围验证空格 // https://github.com/yannickcr/eslint-plugin-react/blob/843d71a432baf0f01f598d7cf1eea75ad6896e4b/docs/rules/jsx-tag-spacing.md 'react/jsx-tag-spacing': [2, { closingSlash: 'never', beforeSelfClosing: 'always', afterOpening: 'never', beforeClosing: 'never', }], // 采用, 不留空格 // 强制在闭JSX元素标签之前留空格 // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/jsx-space-before-closing.md // Deprecated in favor of jsx-tag-spacing 'react/jsx-space-before-closing': 0, // 不采用 // 禁止使用数组的索引作为元素的key属性 // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/no-array-index-key.md 'react/no-array-index-key': 2, // 采用, 原因可以见链接 // 强制给每个不是必须的属性定义一个默认值 // https://github.com/yannickcr/eslint-plugin-react/blob/843d71a432baf0f01f598d7cf1eea75ad6896e4b/docs/rules/require-default-props.md 'react/require-default-props': [0, { forbidDefaultForRequired: true, }], // 采用, 写了isRequired的属性则禁止设置默认值 // 禁止使用没有被export的prototype // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/forbid-foreign-prop-types.md 'react/forbid-foreign-prop-types': ['warn', { allowInPropTypes: true }], // 设置为警告级别 // 不要给单标签的jsx元素传入children // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/void-dom-elements-no-children.md 'react/void-dom-elements-no-children': 2, // 采用 // 强制所有的默认prop都对应一个非必须的proptype // https://github.com/yannickcr/eslint-plugin-react/blob/9e13ae2c51e44872b45cc15bf1ac3a72105bdd0e/docs/rules/default-props-match-prop-types.md 'react/default-props-match-prop-types': [2, { allowRequiredDefaults: false }], // 采用 // 继承React.PureComponent时,禁止使用shouldComponentUpdate // https://github.com/yannickcr/eslint-plugin-react/blob/9e13ae2c51e44872b45cc15bf1ac3a72105bdd0e/docs/rules/no-redundant-should-component-update.md 'react/no-redundant-should-component-update': 2, // 采用s // 禁止存在未使用的state值 // https://github.com/yannickcr/eslint-plugin-react/pull/1103/ 'react/no-unused-state': 2, // 采用 // 强制布尔值属性命名的的一致性 // https://github.com/yannickcr/eslint-plugin-react/blob/843d71a432baf0f01f598d7cf1eea75ad6896e4b/docs/rules/boolean-prop-naming.md 'react/boolean-prop-naming': 0, // 不采用 // 防止常见的套管缺陷 // https://github.com/yannickcr/eslint-plugin-react/blob/73abadb697034b5ccb514d79fb4689836fe61f91/docs/rules/no-typos.md 'react/no-typos': 2, // 采用 // 禁止在props和children中使用无意义的大括号 // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/jsx-curly-brace-presence.md 'react/jsx-curly-brace-presence': [2, { props: 'never', children: 'never' }], // 采用 // 每行一个jsx元素 // https://github.com/yannickcr/eslint-plugin-react/blob/843d71a432baf0f01f598d7cf1eea75ad6896e4b/docs/rules/jsx-one-expression-per-line.md // TODO: re-enable when an option for text children is available 'react/jsx-one-expression-per-line': 0, // 不采用 // 强制使用desconstruct的一致性 // https://github.com/yannickcr/eslint-plugin-react/blob/843d71a432baf0f01f598d7cf1eea75ad6896e4b/docs/rules/destructuring-assignment.md // TODO: re-enable when component detection is fixed 'react/destructuring-assignment': 0, // 不采用 // 禁止在this.setState中使用this.state // https://github.com/yannickcr/eslint-plugin-react/blob/843d71a432baf0f01f598d7cf1eea75ad6896e4b/docs/rules/no-access-state-in-setstate.md 'react/no-access-state-in-setstate': 2, // 采用 // 禁止使用没有显式type属性的按钮元素 // https://github.com/yannickcr/eslint-plugin-react/blob/843d71a432baf0f01f598d7cf1eea75ad6896e4b/docs/rules/button-has-type.md 'react/button-has-type': [2, { button: true, submit: true, reset: false, }], // 采用 // 确保内联标签两边有空格 'react/jsx-child-element-spacing': 0, // 不采用 // 禁止在无状态组件中使用this // https://github.com/yannickcr/eslint-plugin-react/blob/843d71a432baf0f01f598d7cf1eea75ad6896e4b/docs/rules/no-this-in-sfc.md 'react/no-this-in-sfc': 2, // 采用 // 检查jsx最大深度 // https://github.com/yannickcr/eslint-plugin-react/blob/abe8381c0d6748047224c430ce47f02e40160ed0/docs/rules/jsx-max-depth.md 'react/jsx-max-depth': 0, // 不采用 // 禁止在jsx props之间存在多个空格 // https://github.com/yannickcr/eslint-plugin-react/blob/ac102885765be5ff37847a871f239c6703e1c7cc/docs/rules/jsx-props-no-multi-spaces.md 'react/jsx-props-no-multi-spaces': 2, // 采用 // 不允许使用UNSAFE_ methods // https://github.com/yannickcr/eslint-plugin-react/blob/157cc932be2cfaa56b3f5b45df6f6d4322a2f660/docs/rules/no-unsafe.md 'react/no-unsafe': 0, // 不采用 } }; ================================================ FILE: .gitignore ================================================ node_modules npm-debug.log yarn-error.log target node src/_tmp static dist coverage rekit_temp_app _book .nyc_output .tmp .DS_Store Thumbs.db .vscode ================================================ FILE: README.md ================================================ # react-starter PC 端 react+redux 的脚手架,持续进行更新迭代。 ## 特点 1. 当中集成了当前react生态的几个主流技术栈: - react 16.12.0 - ant design 3.26.2 - react-router 5.1.2 - redux 4.0.4 - redux-thunk 2.2.0 - webpack 4.41.2 2. 符合目前前后端分离项目的开发、部署、测试、上线各环节及操作的要求。 3. 优化了传统redux-thunk中action、reducer书写上的不便。 4. 根据路由地址,懒加载所需模块,就是说目前脚手架中提供的示例代码无需删除,只需要确保路由无效即可 5. 增加错误边界,捕捉嵌套组件在生命过程中发生的错误 6. 封装 axios(`src/common/request.js`),对失败的请求进行了统一处理,使用方法: ``` import { get, post, put, del } from 'common/request'; post(URL, params).then(res => {}); // 如果需要自行处理请求失败的情况,也可以直接引入 axios 实例 import axios from 'common/request'; axios.post(URL, params) .then(res => {}) .catch(error => {}) ``` ## 文件目录 > 注意:带 ✎ 号的表示示例配置,可以根据自己的项目配置自行替换;带 +/- 号的表示示例组件,可保留修改或删除 ``` ├─asset // ========================= 静态资源目录 │ ├─css | | ├─app.less // ========================= 样式文件入口 | | ├─base.less // ========================= 基础样式 | | ├─icon.less // ========================= iconfont 样式(✎) | | ├─reset.less // ========================= 样式重置文件 | | ├─variables.less // ========================= 变量文件(✎) | | └─vendor_antd.less // ========================= 专门用于覆盖 antd 样式(✎) │ ├─font // ========================= iconfont文件,在icon.less文件中引用 | | ├─iconfont.eot(+/-) | | ├─iconfont.svg(+/-) | | ├─iconfont.ttf(+/-) | | └─iconfont.woff(+/-) │ └─img // ========================= 图片资源存放目录 │ ├─build // ========================= webpack 相关配置目录 │ ├─postcss.config.js // ========================= postcss 相关配置 │ ├─publicPath.js // ========================= 发布路径(✎) │ ├─theme.js // ========================= antd 主题配置(✎) │ ├─webpack.base.conf.js // ========================= webpack 公用项配置 │ ├─webpack.dev.conf.js // ========================= webpack 开发/联调环境项配置 │ └─webpack.prod.conf.js // ========================= webpack 生产/测试环境项配置 │ ├─mock // ========================= mock 文件目录 │ ├─config │ ├─data │ └─template │ ├─src // ========================= js 源码目录 │ ├─common // ========================= 公共模块目录 │ │ └─request.js // ========================= 对axios进行封装 │ │ │ ├─components // ========================= 纯组件目录 │ │ ├─Layout // ========================= 布局相关组件目录 │ │ │ ├─Footer.jsx(+/-) │ │ │ ├─Header.jsx(+/-) │ │ │ └─Sider.jsx(+/-) │ │ ├─Pages(+/-) // ========================= 分页组件 │ │ ├─ErrorBoundary.jsx // ========================= 错误边界组件 │ │ └─index.js(+/-) │ │ │ ├─constants │ │ ├─constant.js // ========================= 常量集中管理目录(✎) │ │ └─url.js // ========================= url集中管理目录(✎) │ │ │ ├─redux // ========================= redux 处理目录 │ │ ├─skuunit(+/-) │ │ ├─configureStore.js // ========================= store/中间件配置 │ │ └─reducers.js // ========================= 所有reducer入口(✎) │ │ │ ├─routers // ========================= 按路由划分的业务模块目录 │ │ ├─Skuunit(+/-) │ │ ├─index.jsx(✎) │ │ └─PrimaryLayout.jsx(+/-) │ │ │ └─app.jsx // ========================= 项目入口 │ ├─.babelrc // ========================= babel配置文件 ├─.eslintignore ├─.eslintrc.js // ========================= eslint规则文件 ├─.gitignore ├─index.html ├─package-lock.json ├─package.json ├─README.md(✎) ├─template.html(✎) // ========================= html嵌入模板 └─webpack.config.js // ========================= webpack配置入口 ``` ## Changelog - 2019.07.15 - [x] src目录支持使用less - [x] 兼容IE11 - 2019.06.11 - [x] 更改UI - 2019.06.03 - [x] 升级babel 7 - [x] 更新示例组件 - [x] 修复eslint报错 - [x] 完善README.md - 2019.05.22 - [x] 完成基础版本,给出规范目录结构,满足打包构建路由懒加载数据管理错误处理一条龙服务。 ## babel7依赖说明 基础依赖: - @babel/cli: 为babel的脚手架工具 - @babel/core: babel-core是作为babel的核心存在,babel的核心api都在这个模块里面,比如:transform,用于字符串转码得到AST - @babel/plugin-proposal-class-properties: 解析class类的属性 - @babel/plugin-proposal-decorators: 解析装饰器模式语法,如使用react-redux的@connect - @babel/plugin-proposal-export-default-from: 解析export xxx from 'xxx'语法 - @babel/plugin-transform-runtime: 防止转换后的代码重复 - @babel/preset-env : 官方解释“用于编写下一代JavaScript的编译器”,编译成浏览器认识的JavaScript标准 - @babel/preset-react: 用于编译react的jsx,开发react应用必备 - babel-loader: 就是用于编译JavaScript代码 - babel-plugin-import: 用于进行按需加载 代码优化相关依赖: - [babel-plugin-transform-imports](https://www.npmjs.com/package/babel-plugin-transform-imports):去除未使用的模块 - [babel-plugin-transform-react-remove-prop-types](https://www.npmjs.com/package/babel-plugin-transform-react-remove-prop-types): 生产环境下移除不必要的 React propTypes,以减少代码体积 ## 参考文章 - 2019.06.03 - [Webpack4+Babel7优化70%速度](https://juejin.im/post/5c763885e51d457380771ab0#heading-11) - [一口(很长的)气了解 babel](https://juejin.im/post/5c19c5e0e51d4502a232c1c6#heading-0) - 2019.05.22 - [Web Performance Optimization with webpack](https://developers.google.com/web/fundamentals/performance/webpack/) - [Webpack 4 配置最佳实践](https://juejin.im/post/5b304f1f51882574c72f19b0#heading-1) - [Webpack 4 默认分包策略](https://panjiachen.github.io/awesome-bookmarks/blog/webpack/webpack4-b.html) - [webpack-libs-optimizations](https://github.com/GoogleChromeLabs/webpack-libs-optimizations) 朋友们如果有一些对本项目得建议,或者想法欢迎去 github 提 issues,我将持续改进优化该项目 ================================================ FILE: asset/css/app.less ================================================ /* 样式文件入口 */ @import 'reset.less'; @import 'variables.less'; @import 'icon.less'; @import 'base.less'; // 样式覆盖 @import url("vendor_antd.less"); ================================================ FILE: asset/css/base.less ================================================ /* 基础样式 */ html, body { font-size: 14px; height: 100%; } #app { height: 100%; } .fl { .fl(); } .fr { .fr(); } .ml15 { margin-left: 5px; } .ml10 { margin-left: 10px; } .ml15 { margin-left: 15px; } .ml20 { margin-left: 20px; } .mr5 { margin-right: 5px; } .mr10 { margin-right: 10px; } .mr15 { margin-right: 15px; } .mr20 { margin-right: 20px; } .m15 { margin: 15px; } .mt15 { margin-top: 15px; } .mt30 { margin-top: 30px; } .mt45 { margin-top: 45px; } .mb10 { margin-bottom: 10px; } .mb15 { margin-bottom: 15px; } .mb20 { margin-bottom: 20px; } ================================================ FILE: asset/css/icon.less ================================================ /* iconfont样式 */ @font-face { font-family: "iconfont"; // src: url('../font/iconfont.eot'); /* IE9*/ // src: url('../font/iconfont.eot') format('embedded-opentype'), /* IE6-IE8 */ // url('../font/iconfont.woff') format('woff'), /* chrome, firefox */ // url('../font/iconfont.ttf') format('truetype'), /* chrome, firefox, opera, Safari, Android, iOS 4.2+*/ // url('../font/iconfont.svg') format('svg'); /* iOS 4.1- */ } .iconfont { font-family: "iconfont" !important; font-size: 16px; font-style: normal; display: inline-block; vertical-align: baseline; text-align: center; text-transform: none; line-height: 1; text-rendering: optimizeLegibility; -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; } ================================================ FILE: asset/css/reset.less ================================================ /* 样式重置文件 */ // // 1. Remove default margin padding. // html, body, div, ul, li, h1, h2, h3, h4, h5, h6, p, dl, dt, dd, ol, form, input, textarea, th, td, select { margin: 0; padding: 0; } // HTML5 display definitions // ========================================================================== // // Correct `block` display not defined for any HTML5 element in IE 8/9. // Correct `block` display not defined for `details` or `summary` in IE 10/11 // and Firefox. // Correct `block` display not defined for `main` in IE 11. // article, aside, details, figcaption, figure, footer, header, hgroup, main, menu, nav, section, summary { display: block; } // // 1. Correct `inline-block` display not defined in IE 8/9. // 2. Normalize vertical alignment of `progress` in Chrome, Firefox, and Opera. // audio, canvas, progress, video { display: inline-block; vertical-align: baseline; } // // Prevent modern browsers from displaying `audio` without controls. // Remove excess height in iOS 5 devices. // audio:not([controls]) { display: none; height: 0; } // // Address `[hidden]` styling not present in IE 8/9/10. // Hide the `template` element in IE 8/9/10/11, Safari, and Firefox < 22. // [hidden], template { display: none; } // Links // ========================================================================== // // Remove the gray background color from active links in IE 10. // a { background-color: transparent; } // // Improve readability of focused elements when they are also in an // active/hover state. // a:active, a:hover { outline: 0; } // Text-level semantics // ========================================================================== // // Address styling not present in IE 8/9/10/11, Safari, and Chrome. // abbr[title] { border-bottom: 1px dotted; } // // Address style set to `bolder` in Firefox 4+, Safari, and Chrome. // b, strong { font-weight: bold; } // // Address styling not present in Safari and Chrome. // dfn { font-style: italic; } // // Unified "h1" - "h6" font weight // h1, h2, h3, h4, h5, h6{font-weight:normal;} // // Remove ul-ol the default styles // ul,ol { list-style: none; } // // Address styling not present in IE 8/9. // mark { background: #ff0; color: #000; } // // Address inconsistent and variable font size in all browsers. // small { font-size: 80%; } // // Prevent `sub` and `sup` affecting `line-height` in all browsers. // sub, sup { font-size: 75%; line-height: 0; position: relative; vertical-align: baseline; } sup { top: -0.5em; } sub { bottom: -0.25em; } // Embedded content // ========================================================================== // // Remove border when inside `a` element in IE 8/9/10. // img { border: 0; } // // Correct overflow not hidden in IE 9/10/11. // svg:not(:root) { overflow: hidden; } // Grouping content // ========================================================================== // // Address margin not present in IE 8/9 and Safari. // figure { margin: 1em 40px; } // // Address differences between Firefox and other browsers. // hr { box-sizing: content-box; height: 0; } // // Contain overflow in all browsers. // pre { overflow: auto; } // // Address odd `em`-unit font size rendering in all browsers. // code, kbd, pre, samp { font-family: monospace, monospace; font-size: 1em; } // Forms // ========================================================================== // // Known limitation: by default, Chrome and Safari on OS X allow very limited // styling of `select`, unless a `border` property is set. // // // 1. Correct color not being inherited. // Known issue: affects color of disabled elements. // 2. Correct font properties not being inherited. // 3. Address margins set differently in Firefox 4+, Safari, and Chrome. // button, input, optgroup, select, textarea { color: inherit; // 1 font: inherit; // 2 margin: 0; // 3 } // // Address `overflow` set to `hidden` in IE 8/9/10/11. // button { overflow: visible; } // // Address inconsistent `text-transform` inheritance for `button` and `select`. // All other form control elements do not inherit `text-transform` values. // Correct `button` style inheritance in Firefox, IE 8/9/10/11, and Opera. // Correct `select` style inheritance in Firefox. // button, select { text-transform: none; } // // 1. Avoid the WebKit bug in Android 4.0.* where (2) destroys native `audio` // and `video` controls. // 2. Correct inability to style clickable `input` types in iOS. // 3. Improve usability and consistency of cursor style between image-type // `input` and others. // button, html input[type="button"], // 1 input[type="reset"], input[type="submit"] { -webkit-appearance: button; // 2 cursor: pointer; // 3 } // // Re-set default cursor for disabled elements. // button[disabled], html input[disabled] { cursor: default; } // // Remove inner padding and border in Firefox 4+. // button::-moz-focus-inner, input::-moz-focus-inner { border: 0; padding: 0; } // // Address Firefox 4+ setting `line-height` on `input` using `!important` in // the UA stylesheet. // input { line-height: normal; } // // It's recommended that you don't attempt to style these elements. // Firefox's implementation doesn't respect box-sizing, padding, or width. // // 1. Address box sizing set to `content-box` in IE 8/9/10. // 2. Remove excess padding in IE 8/9/10. // input[type="checkbox"], input[type="radio"] { box-sizing: border-box; // 1 padding: 0; // 2 } // // Fix the cursor style for Chrome's increment/decrement buttons. For certain // `font-size` values of the `input`, it causes the cursor style of the // decrement button to change from `default` to `text`. // input[type="number"]::-webkit-inner-spin-button, input[type="number"]::-webkit-outer-spin-button { height: auto; } // // 1. Address `appearance` set to `searchfield` in Safari and Chrome. // 2. Address `box-sizing` set to `border-box` in Safari and Chrome. // input[type="search"] { -webkit-appearance: textfield; // 1 box-sizing: content-box; //2 } // // Remove inner padding and search cancel button in Safari and Chrome on OS X. // Safari (but not Chrome) clips the cancel button when the search input has // padding (and `textfield` appearance). // input[type="search"]::-webkit-search-cancel-button, input[type="search"]::-webkit-search-decoration { -webkit-appearance: none; } // // Define consistent border, margin, and padding. // fieldset { border: 1px solid #c0c0c0; margin: 0 2px; padding: 0.35em 0.625em 0.75em; } // // 1. Correct `color` not being inherited in IE 8/9/10/11. // 2. Remove padding so people aren't caught out if they zero out fieldsets. // legend { border: 0; // 1 padding: 0; // 2 } // // 1.Don't allow the user to zoom // 2.Remove default vertical scrollbar in IE 8/9/10/11. // textarea { resize: none; overflow: auto; } // // Don't inherit the `font-weight` (applied by a rule above). // NOTE: the default cannot safely be changed in Chrome and Safari on OS X. // optgroup { font-weight: bold; } // Tables // ========================================================================== // // Remove most spacing between table cells. // table { border-collapse: collapse; border-spacing: 0; } td, th { padding: 0; } em, b, i { font-style: normal; font-weight: normal; } /* �������� */ input, select { vertical-align:baseline } ================================================ FILE: asset/css/variables.less ================================================ // // ==变量 // -------------------------------------------------- //== 颜色 // //## 灰色及品牌标记颜色引导. @gray-base: #000; @gray-darker: lighten(@gray-base, 13.5%); // #222 @gray-dark: lighten(@gray-base, 20%); // #333 @gray-dim: lighten(@gray-base, 25%); // #404040 系统主要文字颜色 @gray: lighten(@gray-base, 33.5%); // #555 @gray-light: lighten(@gray-base, 46.7%); // #777 @gray-minor: lighten(@gray-base, 59.2%); // #979797 系统次要文字颜色 @gray-lighter: lighten(@gray-base, 93.5%); // #eee @brand-link: #2577FF; //链接 @brand-hover: #094D86;; //链接hover @brand-success: #3BA861; //状态成功、正常 @brand-warning: #f0ad4e; //提示警告 @brand-danger: #B01207; //危险、失败 @brand-forbid: #b4b4b4; //不可用 @title-background: #E9EBF1; //标题、条幅背景色 //== Scaffolding // //## Settings for some of the most global styles. //** Background color for ``. @body-bg: #f1f2f3; //** Global text color on ``. @text-color: @gray-dim; //** Global textual link color. @link-color: @brand-link; //** Link hover color set via `darken()` function. @link-hover-color: @brand-hover; //** Link hover decoration. @link-hover-decoration: underline; //== Typography // //## Font, line-height, and color for body text, headings, and more. @font-family-sans-serif: "Microsoft Yahei", Arial , Tahoma , Helvetica, sans-serif; @font-family-serif: Georgia, "Times New Roman", Times, serif; //** Default monospace fonts for ``, ``, and `
`.
@font-family-monospace:   Menlo, Monaco, Consolas, "Courier New", monospace;
@font-family-base:        @font-family-sans-serif;

@font-size-base:          14px;
@font-size-large:         ceil((@font-size-base * 1.25)); // ~18px
@font-size-medium:        ceil((@font-size-base * 1.14)); // ~16px 
@font-size-small:         ceil((@font-size-base * 0.85)); // ~12px

@font-size-h1:            floor((@font-size-base * 2.6)); // ~36px
@font-size-h2:            floor((@font-size-base * 2.15)); // ~30px
@font-size-h3:            ceil((@font-size-base * 1.7)); // ~24px
@font-size-h4:            ceil((@font-size-base * 1.25)); // ~18px
@font-size-h5:            @font-size-base;
@font-size-h6:            ceil((@font-size-base * 0.85)); // ~12px

//** Unit-less `line-height` for use in components like buttons.
@line-height-base:        1.428571429; // 20/14
//** Computed "line-height" (`font-size` * `line-height`) for use with `margin`, `padding`, etc.
@line-height-computed:    floor((@font-size-base * @line-height-base)); // ~20px

//** By default, this inherits from the ``.
@headings-font-family:    inherit;
@headings-font-weight:    500;
@headings-line-height:    1.1;
@headings-color:          inherit;



//== Components
//
//## Define common padding and border radius sizes and more. Values based on 14px text and 1.428 line-height (~20px to start).

@padding-base-vertical:     6px;
@padding-base-horizontal:   12px;

@padding-large-vertical:    10px;
@padding-large-horizontal:  16px;

@padding-small-vertical:    5px;
@padding-small-horizontal:  10px;

@padding-xs-vertical:       1px;
@padding-xs-horizontal:     5px;

@line-height-large:         1.3333333; // extra decimals for Win 8.1 Chrome
@line-height-small:         1.5;

@border-radius-base:        4px;
@border-radius-large:       6px;
@border-radius-small:       3px;


// Medium screen / desktop
@screen-md:                  992px;
@screen-md-min:              @screen-md;
// Large screen / wide desktop
@screen-lg:                  1200px;
@screen-lg-min:              @screen-lg;


//
//==混合变量
//
// WebKit-style focus
.tab-focus() {
  // WebKit-specific. Other browsers will keep their default outline style.
  outline: 5px auto -webkit-focus-ring-color;
  outline-offset: -2px;
}

// 隐藏文本
.text-hide() {
  font: 0/0 a;
  color: transparent;
  text-shadow: none;
  background-color: transparent;
  border: 0;
}

//
//混合工具变量
//

//去除a和label的虚线
.remove_dotted(){
  a,label {blr:~'expression(this.onFocus=this.blur())'}
  a,label {outline:none;}
}
.font(@size:14px){
    font-size:@size;
}
.h100(){
    height:100%;
}
.w100(){
    width:100%;
}
//边框设置
.border(@w:1px,@c:#eee){
    border:@w solid @c;
}
//定位
.pos(r){
    position:relative;
}
.pos(a){
    position:absolute;
}
.pos(f){
    position:fixed;
}
//背景图片,.bg("..images/1.png");
.bg(@url){
    background:url(@url) no-repeat;
}
//浮动,div{.fr;}
.fl(){
    float:left;
}
.fr(){
    float:right;
}

.list-sn(){
    list-style:none;
}
//垂直居中
.pos-box-cc(@w,@h,@pos:absolute){
    width:@w;
    height:@h;
    left:50%;
    top:50%;
    margin:-@w/2 0 0 -@h/2;
    position: @pos;
}
// 居中对齐一个块级元素
.center-block() {
  display: block;
  margin-left: auto;
  margin-right: auto;
}

//文字居中
.tc(){
    text-align:center;
}
//文字垂直居中
.tcc(@h){
    text-align:center;
    line-height:@h;
}
.l-h(@h){
    height:@h;
    line-height:@h;
}

// 文本溢出
// 需要inline-block或块适当的样式
.text-overflow() {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

//display
.d-b(){
    display:block;
}
.d-i(){
    display:inline;
}
.d-ib(){
    display:inline-block;
    *display:inline;
    *zoom:1;
}
.d-t(){
    display:table;
}
.d-n(){
    display:none;
}
.t-n(@p:none){
    text-decoration:@p;
}
.tc(){
    text-align:center;
}
.tl(){
    text-align:left;
}
.tr(){
    text-align:right;
}
.va-m(){
  vertical-align: middle;
}

//图标距离

.m-icon-left(@l-distance){
  margin-left:@l-distance;
}
.m-icon-right(@r-distance){
  margin-right:@r-distance;
}


//圆角
.radius(@r){
    -webkit-border-radius:@r;
       -moz-border-radius:@r;
            border-radius:@r;
}

//消息圆角

.message(@me-t,@me-r){
  background-color:#F44336;
  width: 8px;
  height: 8px;
  .pos(a);
  top:@me-t;
  right:@me-r;
}

//三角形
.triangle(top,@w:5px,@c:#556066){
    border-width:@w;
    border-color:transparent transparent @c transparent;
    border-style:dashed dashed solid dashed;
}

.triangle(bottom,@w:5px,@c:#556066){
    border-width:@w;
    border-color:@c transparent transparent transparent;
    border-style:solid dashed dashed dashed;

}
.triangle(left,@w:5px,@c:#556066){
    border-width:@w;
    border-color:transparent @c transparent transparent;
    border-style:dashed dashed dashed solid;
}

.triangle(right,@w:5px,@c:#556066){
    border-width:@w;
    border-color:transparent transparent transparent @c;
    border-style:dashed solid dashed dashed;

}
.triangle(@_){
    width:0;
    height:0;
    overflow:hidden;
    font-size:0;
}
.clearfix(){
  *zoom: 1;
  &:before,
  &:after{
    display:table;
    content: "";
  }
  &:after {
    clear: both;
  }
}

.box-sizing(@box){
  -webkit-box-sizing:@box;
     -moz-box-sizing:@box;
      -ms-box-sizing:@box;
       -o-box-sizing:@box;
              sizing:@box;
}
.box-shadow(@shadow){
  -webkit-box-shadow:@shadow;
     -moz-box-shadow:@shadow;
      -ms-box-shadow:@shadow;
       -o-box-shadow:@shadow;
              shadow:@shadow;
}

//过度
.transition(@trans){
    -webkit-transition:@trans;
       -moz-transition:@trans;
        -ms-transition:@trans;
         -o-transition:@trans;
            transition:@trans;
}
.transform-origin(@origin){
    -webkit-transition-origin:@origin;
       -moz-transition-origin:@origin;
        -ms-transition-origin:@origin;
         -o-transition-origin:@origin;
            transition-origin:@origin;
}
.transform(@transform){
    -webkit-transform:@transform;
       -moz-transform:@transform;
        -ms-transform:@transform;
         -o-transform:@transform;
            transform:@transform;
}
.create3d(@h){
  -webkit-perspective:@h;
          perspective:@h;
}
.use3d(){
    -webkit-transform-style:preserve-3d;
            transform-style:preserve-3d;
}

//动画
.animation(@as){
    -webkit-animation:@as;
       -moz-animation:@as;
         -o-animation:@as;
            animation:@as;
}
.trans3d(){
    -webkit-transform-style:preserve-3d;
       -moz-transform-style:preserve-3d;
            transform-style:preserve-3d;
}
.trans-origin(@to){
  -webkit-transform-origin:@to;
     -moz-transform-origin:@to;
          transform-origin:@to;
}




================================================
FILE: asset/css/vendor_antd.less
================================================
.newAnt {
  .ant-layout.ant-layout-has-sider {
    height: 100%;
  }
}


================================================
FILE: build/env.js
================================================

const ENVS = {
  PUB: ['pub', 'production'],
  QA: 'qa',
  PROFILE: 'profile',
  DEV: ['dev', 'development'],
};

const ENVS_ARR = [];

Object.keys(ENVS).forEach(key => {
  const value = ENVS[key];
  if (Array.isArray(value)) {
    value.forEach(i => ENVS_ARR.push(i));
  } else {
    ENVS_ARR.push(value);
  }
});

let currentEnv = process.env.NODE_ENV;

/**
 * 判断当前环境参数是否符合规范
 * @param {string} env
 */
function checkEnv(env) {
  if (!env) {
    throw new Error('NODE_ENV is not defined');
    return false;
  }

  if (ENVS_ARR.indexOf(env) === -1) {
    throw new Error(`NODE_ENV must be one of ${JSON.stringify(ENVS_ARR)},currentEnv is:${env}`);
  }

  return true;
}

// 线上环境
const isProduction = () => {
  return checkEnv(currentEnv) && ENVS.PUB.includes(currentEnv);
};

// 非线上环境
const isNotProduction = () => !isProduction();

// 测试环境
const isQA = () => {
  return checkEnv(currentEnv) && currentEnv === ENVS.QA;
};

// 联调环境
const isProfile = () => {
  return checkEnv(currentEnv) && currentEnv === ENVS.PROFILE;
};

// 开发环境
// const isDev = () => !(isProfile() || isProduction() || isQA());
const isDev = () => {
  return checkEnv(currentEnv) && ENVS.DEV.includes(currentEnv);
};

/**
 * 获取所有环境变量
 * @returns ENVS_ARR
 */
function getEnvs() {
  return ENVS_ARR;
}

/**
 * 设置环境变量
 * @param {string} env
 */
function setEnv(env) {
  if (ENVS_ARR.indexOf(env) === -1) {
    throw new Error(`NODE_ENV provided must be one of ${JSON.stringify(ENVS_ARR)},your param is:${env}`);
  }
  currentEnv = env;
  console.log(`NODE_ENV is set successfully,currentEnv is ${env}`);
}

/**
 * 设置线上环境
 */
const setProduction = () => setEnv(ENVS.PUB[0]);

/**
 * 设置测试环境
 */
const setQA = () => setEnv(ENVS.QA);

/**
 * 设置联调环境
 */
const setProfile = () => setEnv(ENVS.PROFILE);

/**
 * 设置开发环境
 */
const setDev = () => setEnv(ENVS.DEV[0]);

/**
 * 开发和线上环境配置,默认为开发环境配置,适用于前后端未分离项目(用hash做缓存)
 * @param  {*} devConfig 开发环境配置
 * @param  {*} pubConfig 线上环境配置
 * @returns (isQA() || isProduction()) ? pubConfig : devConfig
 */
function env(devConfig, pubConfig) {
  if (!pubConfig && pubConfig !== '') {
    pubConfig = devConfig;
  }

  if (isQA() || isProduction()) {
    return pubConfig;
  }

  return devConfig;
}

module.exports = {
  getEnvs,
  setEnv,
  setProduction,
  setQA,
  setProfile,
  setDev,
  env,
  isProduction,
  isNotProduction,
  isQA,
  isProfile,
  isDev,
  PUB: ENVS.PUB[0],
  QA: ENVS.QA,
  PROFILE: ENVS.PROFILE,
  DEV: ENVS.DEV[0],
};


================================================
FILE: build/postcss.config.js
================================================
module.exports = {
  plugins: [
    require('autoprefixer'),
  ],
};


================================================
FILE: build/publicPath.js
================================================
let pkg = require('../package.json');
const Env = require('./env');

// 不同环境下的静态资源的url根路径
let PRODUCT_STATIC_URL = 'http://static.hello.com';
let QA_STATIC_URL = 'http://qa.static.hello.com';
let PROFILE_STATIC_URL = 'http://dev.static.hello.com'; // 联调环境静态资源存放路径
let DEV_STATIC_URL = '/dist';

// 根据环境来获取不同的静态资源部署的根路径
let publicPath = (function getPublicPath() {
  let staticUrl = DEV_STATIC_URL;
  if (Env.isDev()) {
    return staticUrl;
  }

  if (Env.isProduction()) {
    staticUrl = PRODUCT_STATIC_URL;
  }

  if (Env.isQA()) {
    staticUrl = QA_STATIC_URL;
  }

  if (Env.isProfile()) {
    staticUrl = PROFILE_STATIC_URL;
  }

  return `${staticUrl}/${pkg.name}/${pkg.version}`;
})();

module.exports = publicPath;


================================================
FILE: build/theme.js
================================================
module.exports = {
  'font-family': 'Microsoft Yahei, Arial , Tahoma , Helvetica, sans-serif',
};


================================================
FILE: build/webpack.base.conf.js
================================================
const path = require('path');
const webpack = require('webpack');

const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const CleanWebpackPlugin = require('clean-webpack-plugin');

const resolve = path.resolve;
const publicPath = require('./publicPath');
const theme = require('./theme.js');
const postcssConfigPath = resolve(__dirname, './postcss.config.js');

module.exports = {
  entry: {
    app: './src/app.jsx',
  },
  output: {
    path: resolve(__dirname, '../dist'),
    filename: '[name].js',
    publicPath: publicPath + '/',
    chunkFilename: '[name].js',
    crossOriginLoading: 'anonymous',
  },
  resolve: {
    extensions: ['.js', '.jsx', '.less', '.scss', '.css'],
    alias: {
      common: resolve('src/common'),
      components: resolve('src/components'),
      constants: resolve('src/constants'),
      modules: resolve('src/modules'),
      reduxDir: resolve('src/redux'),
    },
  },
  module: {
    rules: [
      {
        test: /\.(js|jsx)$/i,
        exclude: /node_modules/,
        use: {
          loader: 'babel-loader',
        },
      },
      {
        test: /\.css/i,
        include: [
          resolve('src'),
        ],
        use: [
          MiniCssExtractPlugin.loader,
          {
            loader: 'css-loader?importLoader=1&modules&localIdentName=[path]___[name]__[local]___[hash:base64:5]',
          },
          {
            loader: 'postcss-loader',
            options: {
              sourceMap: true,
              config: {
                path: postcssConfigPath,
              },
            },
          },
        ],
      },
      {
        test: /\.less$/,
        include: [resolve('src')],
        use: [
          MiniCssExtractPlugin.loader,
          {
            loader:
              'css-loader?importLoader=1&modules&localIdentName=[path]___[name]__[local]___[hash:base64:5]',
            options: {
              modules: true,
            },
          },
          {
            loader: 'postcss-loader',
            options: {
              sourceMap: true,
              config: {
                path: postcssConfigPath,
              },
            },
          },
          {
            loader: 'less-loader',
            options: {
              javascriptEnabled: true,
              modifyVars: theme,
            },
          },
        ],
      },
      {
        test: /\.less/i,
        include: [
          resolve('asset/css'),
          resolve('node_modules/antd/'),
        ],
        use: [
          MiniCssExtractPlugin.loader,
          {
            loader: 'css-loader?importLoader=1&modules&localIdentName=[path]___[name]__[local]___[hash:base64:5]',
            options: {
              minimize: true, // css压缩
            },
          },
          {
            loader: 'postcss-loader',
            options: {
              sourceMap: true,
              config: {
                path: postcssConfigPath,
              },
            },
          },
          {
            loader: 'less-loader',
            options: {
              javascriptEnabled: true,
              modifyVars: theme,
            },
          },
        ],
      },
      {
        test: /\.(png|jpg|gif|svg|eot|ttf|woff|woff2)$/,
        use: ['file-loader?limit=1000&name=files/[md5:hash:base64:10].[ext]'],
      },
      {
        test: /\.(html|htm)$/,
        use: 'html-withimg-loader',
      },
    ],
  },
  plugins: [
    new webpack.ContextReplacementPlugin(/moment[\/\\]locale$/, /^\.\/zh\-cn$/),
    // extract css
    new MiniCssExtractPlugin({
      filename: '[name].css',
      chunkFilename: '[name].css',
    }),
    // clean output bundle directory
    new CleanWebpackPlugin(),
  ],
};


================================================
FILE: build/webpack.dev.conf.js
================================================
let mockport = 8000;

process.argv.forEach(function setMockPort(val, index) {
  if (val === '--env.mockport') {
    mockport = process.argv[index + 1];
    return false;
  }
});

module.exports = {
  mode: 'development',
  devtool: 'source-map',
  devServer: {
    // 服务器外部可访问要声明host
    host: '0.0.0.0',
    hot: true,
    inline: true,
    open: true,
    proxy: {
      '**/*.action': {
        target: 'http://localhost:' + mockport,
        /* bypass: function bypass(req, res, proxyOptions) {
          // handle default jsp action
          if (req.headers.accept.indexOf('html') != -1) {
            return 'index.html';
          }
        }, */
      },
    },
  },
};


================================================
FILE: build/webpack.prod.conf.js
================================================
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
// const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;

module.exports = {
  mode: 'production',
  plugins: [
    // analyze bundle size
    // new BundleAnalyzerPlugin(),
    new HtmlWebpackPlugin({
      template: path.resolve(process.cwd(), './template.html'),
      filename: path.resolve(process.cwd(), './dist', 'index.html'), // 写入的文件
    }),
  ],
  optimization: {
    splitChunks: {
      name: true,
      cacheGroups: {
        default: {
          minChunks: 2,
          priority: -20,
          reuseExistingChunk: true,
        },
        libs: {
          test: /[\\/]node_modules[\\/]/,
          priority: -10,
        },
        styles: {
          test: /(\.less|\.css)$/,
          priority: -10,
        },
      },
    },
  },
};


================================================
FILE: index.html
================================================




  
  
  PC端React+redux脚手架
  



  
================================================ FILE: mock/config/mockConfig.json ================================================ { "dataSource": ["json", "template", "server"], "json": { "path": "/mock/data/", "wrap": true }, "server": [{ "host": "http://localhost:8080/mock", "serverParams": { "index": 1 }, "statusCode": [200], "rejectUnauthorized": false, "secureProtocol": "SSLv3_method", "cookie": "", "proxy": "" }, { "host": "http://localhost:8081/", "serverParams": { "index": 2 }, "statusCode": [200], "rejectUnauthorized": false, "secureProtocol": "SSLv3_method", "cookie": "", "proxy": "" }], "template": { "path": "/mock/template/" } } ================================================ FILE: mock/data/skuunit/deleteSkuUnit.json ================================================ { "enabled": true, "value": "success", "success": { "flag": "0", "msg": [ "" ], "data": [] }, "error": { "flag": "1", "msg": [ "input error" ], "data": [] } } ================================================ FILE: mock/data/skuunit/showSkuUnitList.json ================================================ { "enabled": true, "value": "success", "success": { "flag": "0", "msg": ["系统繁忙"], "data": { "dataList": [ { "skuUnitId": "185715847_1", "skuUnitName": "1单元名称", "isPause": 1, "skuSetId": "23489551", "skuSetName": "上海鲜花", "groupCount": 5, "planCount": 2, "denyKeyCount": 9, "checkStatus": 1, "checkOkCount": 3000, "checkRejectCount": 200, "ideas": [ { "deveiceType": 2, "ideaType": 2 } ] }, { "skuUnitId": "185715847_2", "skuUnitName": "2单元名称2", "isPause": 0, "skuSetId": "23489556", "skuSetName": "沈阳鲜花", "groupCount": 5, "planCount": 2, "denyKeyCount": 7, "checkStatus": 1, "checkOkCount": 3000, "checkRejectCount": 200, "ideas": [ { "deveiceType": 1, "ideaType": 2 }, { "deveiceType": 2, "ideaType": 1 } ] }, { "skuUnitId": "185715847_3", "skuUnitName": "3单元名称", "isPause": 0, "skuSetId": "23489555", "skuSetName": "兰州鲜花", "groupCount": 5, "planCount": 2, "denyKeyCount": 12, "checkStatus": 0, "checkOkCount": 3000, "checkRejectCount": 200, "ideas": [ { "deveiceType": 2, "ideaType": 1 } ] }, { "skuUnitId": "185715847_4", "skuUnitName": "4单元名称", "isPause": 1, "skuSetId": "23489552", "skuSetName": "北京二手车", "groupCount": 5, "planCount": 2, "denyKeyCount": 4, "checkStatus": 1, "checkOkCount": 3000, "checkRejectCount": 200, "ideas": [ { "deveiceType": 2, "ideaType": 2 } ] }, { "skuUnitId": "185715847_5", "skuUnitName": "5单元名称", "isPause": 0, "skuSetId": "23489553", "skuSetName": "香港鲜花", "groupCount": 5, "planCount": 2, "denyKeyCount": 7, "checkStatus": -1, "checkOkCount": 3000, "checkRejectCount": 200, "ideas": [ { "deveiceType": 1, "ideaType": 1 } ] } ], "totalNumber": 50 } }, "error": { "flag": "1", "msg": [ "系统繁忙" ], "data": [] } } ================================================ FILE: mock/data/skuunit/updateSkuUnitPause.json ================================================ { "enabled": true, "value": "success", "success": { "flag": "0", "msg": [ "修改失败" ], "data": [] }, "error": { "flag": "1", "msg": [ "input error" ], "data": [] } } ================================================ FILE: mock/template/query/table.template ================================================ { 'table|1-10': [ { 'id|100-1000': 100, 'name|1': ['A','B','C','D','E','F','G','H','I','J','K','L','M','N'], 'height|50-300': 50, 'weight|50-100.1-10': 50, 'age|1-100': 1, 'email': '@EMAIL' } ] } ================================================ FILE: package.json ================================================ { "name": "react-starter", "description": "react+redux的脚手架,持续进行更新迭代", "version": "1.0.1", "main": "app.js", "scripts": { "start": "cross-env NODE_ENV=development webpack-dev-server --progress --colors", "profile": "cross-env NODE_ENV=profile webpack --progress --colors --display-modules", "qa": "cross-env NODE_ENV=qa webpack --progress --colors", "pub": "cross-env NODE_ENV=production webpack --progress --colors ", "lint": "eslint src" }, "dependencies": { "antd": "^3.26.2", "axios": "^0.18.1", "babel-polyfill": "^6.26.0", "lodash": "^4.17.15", "moment": "^2.24.0", "prop-types": "^15.7.2", "react": "^16.12.0", "react-dom": "^16.12.0", "react-redux": "^5.1.2", "react-router-dom": "^5.1.2", "redux": "^4.0.4", "redux-thunk": "^2.2.0" }, "devDependencies": { "@babel/cli": "^7.7.5", "@babel/core": "^7.7.5", "@babel/plugin-proposal-class-properties": "^7.7.4", "@babel/plugin-proposal-decorators": "^7.7.4", "@babel/plugin-proposal-export-default-from": "^7.7.4", "@babel/plugin-syntax-dynamic-import": "^7.7.4", "@babel/plugin-transform-runtime": "^7.7.6", "@babel/preset-env": "^7.7.6", "@babel/preset-react": "^7.7.4", "@hot-loader/react-dom": "^16.11.0", "autoprefixer": "^8.6.5", "babel-eslint": "^10.0.3", "babel-loader": "^8.0.6", "babel-plugin-import": "^1.13.0", "babel-plugin-transform-imports": "^1.5.1", "babel-plugin-transform-react-remove-prop-types": "^0.4.24", "clean-webpack-plugin": "^2.0.2", "css-loader": "^0.28.9", "eslint": "^5.16.0", "eslint-plugin-react": "^7.17.0", "eslint-plugin-react-hooks": "^1.7.0", "file-loader": "^1.1.11", "html-webpack-plugin": "^3.2.0", "html-withimg-loader": "^0.1.16", "less": "^3.10.3", "less-loader": "^4.1.0", "mini-css-extract-plugin": "^0.4.5", "postcss-loader": "^2.1.6", "react-hot-loader": "^4.12.18", "redux-logger": "^3.0.6", "url-loader": "^1.1.2", "cross-env": "^6.0.3", "webpack": "^4.41.2", "webpack-bundle-analyzer": "^3.6.0", "webpack-cli": "^3.3.10", "webpack-dev-server": "^3.9.0", "webpack-merge": "^4.2.2" }, "repository": { "type": "git", "url": "https://github.com/zhangkun-Jser/react-starter.git" }, "keywords": [], "author": "zk", "license": "ISC", "bugs": { "url": "https://github.com/zhangkun-Jser/react-starter/issues" }, "homepage": "https://github.com/zhangkun-Jser/react-starter", "eslintIgnore": [ "dist/**" ] } ================================================ FILE: src/app.jsx ================================================ /* * 项目入口 */ import 'babel-polyfill'; import React from 'react'; import ReactDOM from 'react-dom'; import { Provider } from 'react-redux'; import { HashRouter } from 'react-router-dom'; import '../asset/css/app.less'; import store from './redux/configureStore'; import Root from './routers'; ReactDOM.render( , document.getElementById('app'), ); ================================================ FILE: src/common/request.js ================================================ // 参考:https://ykloveyxk.github.io/2017/02/25/axios%E5%85%A8%E6%94%BB%E7%95%A5/ import OriginAxios from 'axios'; import { message } from 'antd'; const axios = OriginAxios.create({ timeout: 20000, }); export function get(url, data) { return axios.get(url, { params: data, }); } // By default, axios serializes JavaScript objects to JSON export function post(url, data) { return axios({ url, method: 'post', data, }); } // By default, axios serializes JavaScript objects to JSON export function put(url, data) { return axios({ url, method: 'put', data, }); } export function del(url, data) { return axios({ url, method: 'delete', data, }); } // Add a request interceptor axios.interceptors.request.use( function config(config) { // Do something before request is sent return config; }, function error(error) { // Do something with request error console.log('request error, HTTP CODE: ', error.response.status); return Promise.reject(error); }, ); // 返回状态判断(添加响应拦截器) axios.interceptors.response.use( res => { if (res.data && res.data.flag === 1) { let errorMsg = res.data.msg; message.error(errorMsg); return Promise.reject(errorMsg); } return res; }, error => { // 用户登录的时候会拿到一个基础信息,比如用户名,token,过期时间戳 // 直接丢localStorage或者sessionStorage if (error.response.status === 401) { // 若是接口访问的时候没有发现有鉴权的基础信息,直接返回登录页 // history.push('login'); window.location = '/login.html'; } }, ); export default axios; ================================================ FILE: src/components/ErrorBoundary.jsx ================================================ // 错误边界 import React, { Component } from 'react'; import PropTypes from 'prop-types'; class ErrorBoundary extends Component { static propTypes = { children: PropTypes.oneOfType([PropTypes.any]), }; constructor(props) { super(props); this.state = { hasError: false }; } static getDerivedStateFromError() { // Update state so the next render will show the fallback UI. return { hasError: true }; } componentDidCatch() { // You can also log the error to an error reporting service // logErrorToMyService(error, info); this.setState({ hasError: true, }); } render() { if (this.state.hasError) { // You can render any custom fallback UI return

Something went wrong.

; } return this.props.children; } } export default ErrorBoundary; ================================================ FILE: src/components/Layout/Footer.jsx ================================================ import React from 'react'; import styles from './index.css'; export default () => (
Copyright© 2019 Sogou Biztech. All Rights Reserved.
); ================================================ FILE: src/components/Layout/Header.jsx ================================================ import React from 'react'; import PropTypes from 'prop-types'; import { Layout, Icon, Menu, Dropdown, Avatar, } from 'antd'; import styles from './index.css'; import AVATAR from '../../../asset/img/avatar.png'; const { Header } = Layout; const HeaderComp = ({ collapsed = false, onToggle, }) => { const menu = ( 个人中心 设置 退出登录 ); const handleToggle = () => { onToggle && onToggle(); }; return (
dev
); }; HeaderComp.propTypes = { collapsed: PropTypes.bool, onToggle: PropTypes.func, }; export default HeaderComp; ================================================ FILE: src/components/Layout/Sider.jsx ================================================ import React, { useState } from 'react'; import PropTypes from 'prop-types'; import { Link } from 'react-router-dom'; import { Menu, Layout, Icon } from 'antd'; import styles from './index.css'; import LOGO from '../../../asset/img/logo.svg'; const { Sider } = Layout; const SiderComp = ({ history, pathname, collapsed = false, }) => { const pathArr = pathname.split('/').filter(i => i); const homeKey = 'home'; const getDefaultSelectedKeys = () => { const selectKey = pathArr.length ? pathArr[pathArr.length - 1] : ''; return selectKey ? [selectKey] : [homeKey]; }; const [selectedKeys, setSelectedKeys] = useState(getDefaultSelectedKeys()); const handleSelect = ({ selectedKeys }) => { setSelectedKeys(selectedKeys); history.push(`/${selectedKeys}`); }; window.onhashchange = () => { setSelectedKeys(getDefaultSelectedKeys()); }; return (
{!collapsed &&

脚手架

}
欢迎 nav 2
); }; SiderComp.propTypes = { history: PropTypes.objectOf(PropTypes.any), pathname: PropTypes.string, collapsed: PropTypes.bool, }; export default SiderComp; ================================================ FILE: src/components/Layout/index.css ================================================ /* Header */ .header { background: #fff; padding: 0 20px; position: relative; } .header-right { position: absolute; top: 0; right: 24px; bottom: 0; } .header-menu { width: 150px; } .user-name { margin-left: 10px; } .account { color: rgba(0, 0, 0, 0.65); cursor: pointer; padding: 0 12px; display: inline-block; transition: all .3s; height: 100%; } .account i { font-size: 16px; vertical-align: middle; } .account:hover { background: #e6f7ff; } /* Sider */ .trigger { font-size: 18px; line-height: 64px; padding: 0 10px; cursor: pointer; transition: color .3s; } .trigger:hover { color: #1890ff; } .menu-logo { position: relative; height: 64px; padding-left: 24px; overflow: hidden; line-height: 64px; background: #001529; transition: all 0.3s; } .menu-logo a { color: #1890FF; text-decoration: none; background-color: transparent; outline: none; cursor: pointer; transition: color 0.3s; } .menu-logo img { display: inline-block; width: 32px; vertical-align: middle; } .menu-logo h1 { display: inline-block; margin: 0 0 0 12px; color: white; font-weight: 600; font-size: 20px; vertical-align: middle; } /* Footer */ .footer { text-align: center; font-size: 14px; padding: 20px; } ================================================ FILE: src/components/Pages/index.css ================================================ .page-wrapper { margin: 16px 0px 16px 16px; line-height: 32px; overflow: hidden; } ================================================ FILE: src/components/Pages/index.jsx ================================================ /** * 表格分页组件 */ import React, { Component } from 'react'; import { Pagination } from 'antd'; import PropTypes from 'prop-types'; import styles from './index.css'; class Pages extends Component { static propTypes = { pageSizeOptions: PropTypes.arrayOf(PropTypes.string), defaultCurrent: PropTypes.number, total: PropTypes.number, current: PropTypes.number, pageSize: PropTypes.number, loadTable: PropTypes.func, style: PropTypes.objectOf(PropTypes.any), } static defaultProps = { pageSizeOptions: ['10', '20', '30', '40'], defaultCurrent: 1, total: 0, current: 1, pageSize: 10, style: { float: 'right', }, }; state = { current: this.props.current, pageSize: this.props.pageSize, }; static getDerivedStateFromProps(props) { return { current: props.current, pageSize: props.pageSize, total: props.total, }; } handlePageChange = (page, pageSize) => { this.loadTable({ page, pageSize, }); } handlePageSizeChange = (current, pageSize) => { this.loadTable({ page: this.props.defaultCurrent, pageSize, }); } loadTable({ page, pageSize }) { this.setState({ current: page, pageSize, }); this.props.loadTable(false, { page, pageSize, }); } reset() { const { current, pageSize } = this.defaultProps; this.setState({ current, pageSize }); } render() { const { total, pageSizeOptions, style } = this.props; const { current, pageSize } = this.state; return (
共{total}条
); } } export default Pages; ================================================ FILE: src/components/index.jsx ================================================ import Header from './Layout/Header'; import Sider from './Layout/Sider'; import Footer from './Layout/Footer'; import Pages from './Pages'; import ErrorBoundary from './ErrorBoundary'; export { Header, Sider, Footer, Pages, ErrorBoundary, }; ================================================ FILE: src/constants/constant.js ================================================ export const ACTION_TYPE_ADD_ERROR = 'app/ADD_ERROR'; // 分页默认信息 export const PAGE_DATA = { pageNo: 1, pageSize: 10, }; ================================================ FILE: src/constants/url.js ================================================ // 单元列表查询展示 export const SHOW_UNIT_LIST_URL = '/skuunit/showSkuUnitList.action'; // 删除单元 export const DELETE_UNIT_URL = '/skuunit/deleteSkuUnit.action'; ================================================ FILE: src/redux/configureStore.js ================================================ import { compose, createStore, applyMiddleware } from 'redux'; import logger from 'redux-logger'; import thunk from 'redux-thunk'; import reducer from './reducers'; const middleware = [thunk]; const isNotProduction = process.env.NODE_ENV !== 'production'; if (isNotProduction) { middleware.push(logger); } // 判断当前浏览器是否安装了 REDUX_DEVTOOL 插件 const shouldCompose = isNotProduction && typeof window === 'object' && window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__; const composeEnhancers = shouldCompose ? window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__({ // Specify here name, actionsBlacklist, actionsCreators and other options }) : compose; /* 调用 applyMiddleware ,使用 middleware 来增强 createStore */ const configureStore = composeEnhancers(applyMiddleware(...middleware))(createStore); const store = configureStore(reducer); window.Store = store; if (module.hot) { module.hot.accept('./reducers.js', () => { console.log('reducer changed'); store.replaceReducer(require('./reducers').default); }); } export default store; ================================================ FILE: src/redux/reducers.js ================================================ import { combineReducers } from 'redux'; import skuunit from './skuunit/skuunit'; const reducers = { skuunit, }; export default combineReducers(reducers); ================================================ FILE: src/redux/skuunit/api.js ================================================ import { get, post } from 'common/request'; import { SHOW_UNIT_LIST_URL, DELETE_UNIT_URL, } from 'constants/url'; /** * 单元列表查询展示 * @param {*} params */ export function showSkuunitListApi(params) { if (!params) { return Promise.reject('params is wrong'); } return get(SHOW_UNIT_LIST_URL, params) .then(res => { // 开发时调试等待效果; return new Promise((resolve) => { setTimeout(() => { return resolve(res); }, 1000); }); }) .then(res => res.data); } /** * 删除单元 * @param {} params */ export function deleteSkuUnitApi(params) { if (!params) { return Promise.reject('params is wrong'); } return post(DELETE_UNIT_URL, params) .then(res => res.data); } ================================================ FILE: src/redux/skuunit/skuunit.js ================================================ /** * author: niuxiaoyu * description: 单元 * date: 2018/6/6 */ import { ACTION_TYPE_ADD_ERROR } from 'constants/constant'; import { showSkuunitListApi, deleteSkuUnitApi, } from './api'; const SHOW_LOADING = 'skuunit/SHOW_LOADING'; const HIDE_LOADING = 'skuunit/HIDE_LOADING'; const SHOW_SKU_UNIT_LIST = 'skuunit/SHOW_SKU_UNIT_LIST'; const DELETE_SKUUNIT = 'skuunit/DELETE_SKUUNIT'; const initialState = { dataList: [], isLoading: false, error: '', }; export default function reducer(state = initialState, action = {}) { switch (action.type) { case SHOW_LOADING: return Object.assign({}, state, { isLoading: true, }); case HIDE_LOADING: return Object.assign({}, state, { isLoading: false, }); case SHOW_SKU_UNIT_LIST: return Object.assign({}, state, { dataList: action.payload.data.dataList, totalNumber: action.payload.data.totalNumber, }); case DELETE_SKUUNIT: return Object.assign({}, state, { dataList: _deleteskuUnits(state.dataList, action.payload), }); default: return state; } } /** * 删除单元 * @param {*} source * @param {*} skuUnitIdList */ const _deleteskuUnits = (source, params) => { const { skuUnitIdList } = params; const newSource = []; for (let i = 0; i < source.length; i++) { const target = skuUnitIdList.find(skuUnitId => skuUnitId === source[i].skuUnitId); if (!target) { newSource.push(source[i]); } } return newSource; }; /** * 单元列表查询展示 * @param {*} params */ export function showSkuunitList(planName) { return async dispatch => { try { dispatch({ type: SHOW_LOADING }); const res = await showSkuunitListApi(planName); await dispatch({ type: SHOW_SKU_UNIT_LIST, payload: res }); dispatch({ type: HIDE_LOADING }); } catch (err) { dispatch({ type: HIDE_LOADING }); console.log(err); } }; } /** * 删除单元 * @param {} skuUnitIdList */ export function deleteSkuUnit(skuUnitIdList) { return async dispatch => { try { const param = { skuUnitIdList }; const res = await deleteSkuUnitApi(param); dispatch({ type: DELETE_SKUUNIT, payload: param, res }); } catch (err) { dispatch({ type: ACTION_TYPE_ADD_ERROR, payload: { errorMsg: err } }); } }; } ================================================ FILE: src/routers/PrimaryLayout.jsx ================================================ import React, { Suspense, useState, lazy } from 'react'; import PropTypes from 'prop-types'; import { Switch, Route, Redirect } from 'react-router-dom'; import { Layout, Spin } from 'antd'; import styles from './index.less'; import { Header, Sider, Footer, ErrorBoundary, } from 'components'; const { Content } = Layout; const Skuunit = lazy(() => import(/* webpackChunkName: "home" */ './Skuunit')); const PrimaryLayout = (props) => { const [collapsed, setCollapsed] = useState(false); const toggleCollapse = () => { setCollapsed(!collapsed); }; return (
}>
nav2
} />