[
  {
    "path": ".babelrc",
    "content": "{\n  \"presets\": [\n    \"@babel/preset-env\",\n    \"@babel/preset-react\"\n  ],\n  \"plugins\": [\n    [\n      \"@babel/plugin-proposal-decorators\",\n      {\n        \"legacy\": true\n      }\n    ],\n    \"@babel/plugin-proposal-class-properties\",\n    \"@babel/plugin-proposal-export-default-from\",\n    \"@babel/plugin-transform-runtime\",\n    \"@babel/plugin-syntax-dynamic-import\",\n    \"react-hot-loader/babel\",\n    [\n      \"import\",\n      {\n        \"libraryName\": \"antd\",\n        \"libraryDirectory\": \"es\",\n        \"style\": true\n      }\n    ],\n    [\n      \"transform-imports\",\n      {\n        \"react-router\": {\n          \"transform\": \"react-router/${member}\",\n          \"preventFullImport\": true\n        }\n      }\n    ]\n  ],\n  \"env\": {\n    \"production\": {\n      \"plugins\": [\n        [\n          \"transform-react-remove-prop-types\",\n          {\n            \"mode\": \"wrap\",\n            \"ignoreFilenames\": [\n              \"node_modules\"\n            ]\n          }\n        ]\n      ]\n    }\n  }\n}"
  },
  {
    "path": ".eslintignore",
    "content": "node_modules\ndist"
  },
  {
    "path": ".eslintrc.js",
    "content": "module.exports = {\n  \"env\": {\n    \"node\": true,\n    \"browser\": true,\n    \"es6\": true,\n    \"commonjs\": true,\n  },\n  \"parser\": \"babel-eslint\",\n  \"parserOptions\": {\n    \"ecmaVersion\": 6,\n    \"sourceType\": \"module\",\n    \"ecmaFeatures\": {\n      \"jsx\": true,\n      \"modules\": true,\n      \"experimentalObjectRestSpread\": true\n    }\n  },\n  \"plugins\": [\n    \"react\",\n    \"react-hooks\"\n  ],\n  \"rules\": {\n    \"react-hooks/rules-of-hooks\": \"error\",\n    \"react-hooks/exhaustive-deps\": \"warn\",\n    // 推荐规则 0=\"off\", 1=\"warn\", 2=\"error\"\n    \"no-compare-neg-zero\": 2,\n    \"no-cond-assign\": 2,\n    \"no-console\": 0,// 不采用 构建时自己去除\n    \"no-constant-condition\": 2,// 采用\n    \"no-control-regex\": 2,// 采用\n    \"no-debugger\": 0,// node端采用，web端不采用\n    \"no-dupe-args\": 2, // 采用\n    \"no-dupe-keys\": 2,// 采用\n    \"no-duplicate-case\": 2,// 采用\n    \"no-empty\": 2, // 采用\n    \"no-empty-character-class\": 2,// 采用\n    \"no-ex-assign\": 2, // 采用\n    \"no-extra-boolean-cast\": 2, // 采用\n    \"no-extra-parens\": 0, // 不采用\n    \"no-extra-semi\": 2,// 采用\n    \"no-func-assign\": 2,// 采用\n    \"no-inner-declarations\": 2, // 采用\n    \"no-invalid-regexp\": 2,// 采用\n    \"no-irregular-whitespace\": 2,// 采用\n    \"no-obj-calls\": 2,// 采用\n    \"no-regex-spaces\": 2,// 采用\n    \"no-sparse-arrays\": 2,// 采用\n    \"no-unexpected-multiline\": 2,// 采用\n    \"no-unreachable\": 2,// 采用\n    \"no-unsafe-finally\": 2,// 采用\n    \"no-unsafe-negation\": 2,// 采用\n    \"use-isnan\": 2,// 采用\n    \"valid-typeof\": 2,// 采用\n    \"no-case-declarations\": 2, // 采用\n    \"no-empty-pattern\": 2,// 采用\n    \"no-fallthrough\": 2,// 采用\n    \"no-global-assign\": 2,// 采用\n    \"no-octal\": 2,// 采用\n    \"no-redeclare\": 2,// 采用\n    \"no-self-assign\": 2,// 采用\n    \"no-unused-labels\": 2,// 采用\n    \"no-useless-escape\": 2,// 采用\n    \"no-delete-var\": 2,// 采用\n    \"no-undef\": 2,// 采用\n    \"no-unused-vars\": 2,// 采用\n    \"no-mixed-spaces-and-tabs\": 2,// 采用\n    \"constructor-super\": 2,// 采用\n    \"no-class-assign\": 2,// 采用\n    \"no-const-assign\": 2,// 采用\n    \"no-dupe-class-members\": 2,// 采用\n    \"no-new-symbol\": 2,// 采用\n    \"no-this-before-super\": 2,// 采用\n    \"require-yield\": 2,// 采用\n\n    // 拓展规则\n    \"for-direction\": 2,// 采用\n    \"getter-return\": 2,// 采用\n    \"no-await-in-loop\": 2,// 采用\n    \"no-prototype-builtins\": 2,// 采用\n    \"no-template-curly-in-string\": 2,// 采用\n    // valid-jsdoc 不采用，基础组件强制使用\n    \"accessor-pairs\": 2,// 采用\n    \"array-callback-return\": 2,// 采用\n    \"block-scoped-var\": 2,// 采用\n    \"class-methods-use-this\": 1,//采用（warning，可能报错很多）\n    \"curly\": 1,// 采用 warning\n    \"default-case\": 2,// 采用\n    \"eqeqeq\": 1,// 采用  warning\n    \"guard-for-in\": 2,// 采用\n    \"no-caller\": 2, // 采用\n    \"no-eval\": 2,// 采用\n    \"no-extend-native\": 2,// 采用\n    \"no-extra-label\": 2,// 采用\n    \"no-floating-decimal\": 2,// 采用\n    \"no-implied-eval\": 2,// 采用\n    // 临时关闭，有误报\n    \"no-invalid-this\": 0,// 采用\n    \"no-iterator\": 2,// 采用\n    \"no-labels\": 2,// 采用\n    \"no-lone-blocks\": 2,// 采用\n    \"no-throw-literal\": 2,// 采用\n    \"no-unmodified-loop-condition\": 1,// 采用 warning\n    \"no-useless-concat\": 2,// 采用\n    \"no-useless-return\": 2,// 采用\n    \"radix\": 2,// 采用\n    \"require-await\": 2,// 采用\n\n    //关于Node.js或在浏览器中使用CommonJS的相关规则\n    \"callback-return\": 2,// 采用\n\n    // 临时关闭\n    \"global-require\": 0,// 采用\n    \"handle-callback-err\": 2,// 采用\n    \"no-buffer-constructor\": 2,// 采用\n    \"no-mixed-requires\": 2,// 采用\n    \"no-new-require\": 2,// 采用\n\n    // 代码风格\n    // 在数组开括号后和闭括号前强制换行\n    \"array-bracket-newline\": 0, // 不采用\n    // 禁止或强制在括号内使用空格\n    \"array-bracket-spacing\": [2, \"never\"], // 采用，禁止在数组括号内出现空格,\n    // 强制数组元素间出现换行\n    \"array-element-newline\": 0, // 不采用\n    // 禁止或强制在代码块中开括号前和闭括号后有空格\n    \"block-spacing\": [2, \"always\"], // 采用，要求使用一个或多个空格\n    // 强制在代码块中使用一致的大括号风格\n    \"brace-style\": [2, \"1tbs\", { allowSingleLine: true }], // 强制 one true brace style(一种代码风格，将大括号放在控制语句或声明语句同一行的位置)\n    // 可以有例外情况, allowSingleLine允许块的开括号和闭括号在 同一行\n    // 要求使用骆驼拼写法 \n    \"camelcase\": [2, { properties: \"never\" }], // 采用，但不检查属性名称\n    // 强制或禁止对注释的第一个字母大写\n    \"capitalized-comments\": 0, // 不采用\n    // 要求或禁止使用拖尾逗号\n    \"comma-dangle\": [2, {\n      arrays: \"always-multiline\",\n      objects: \"always-multiline\",\n      imports: \"always-multiline\",\n      exports: \"always-multiline\",\n      functions: \"always-multiline\",\n    }], // 采用，当最后一个元素或属性与闭括号 ] 或 } 在 不同的行时，要求使用拖尾逗号；当在 同一行时，禁止使用拖尾逗号。\n    // 强制在逗号周围使用空格\n    \"comma-spacing\": [2, { before: false, after: true }], // 采用，禁止在逗号前使用空格，要求在逗号后使用一个或多个空格\n    // 逗号风格\n    \"comma-style\": [2, \"last\", { // last 要求逗号放在数组元素、对象属性或变量声明之后，且在同一行\n      exceptions: {             // 额外规则 包含与 JavaScript 代码的抽象语法树 (AST) 的节点类型对应的属性：\n        ArrayExpression: false, // 忽略数组字面量的逗号风格\n        ArrayPattern: false,    // 忽略数组的解构赋值语句中的逗号风格\n        ArrowFunctionExpression: false, // 忽略箭头函数表达式的参数中的逗号风格\n        CallExpression: false, // 忽略函数调用的参数中的逗号风格\n        FunctionDeclaration: false, // 忽略函数声明的参数中的逗号风格\n        FunctionExpression: false,  // 忽略函数表达式的参数中的逗号风格\n        ImportDeclaration: false,  // 忽略 import 语句中的逗号风格\n        ObjectExpression: false, // 忽略对象字面量的逗号风格\n        ObjectPattern: false,   // 忽略对象的解构赋值中的逗号风格\n        VariableDeclaration: false, // 忽略变量声明的逗号风格\n        NewExpression: false, //  忽略构造函数表达式参数中的逗号风格\n      }                       // !!! 注意，以上配置全为false，即不忽略\n    }], // 采用\n    // 禁止或强制在计算属性中使用空格\n    \"computed-property-spacing\": [2, 'never'], // 采用，禁止在计算属性内使用空格\n    // 要求一致的 This\n    \"consistent-this\": 0, // 不采用\n    // 要求或禁止文件末尾保留一行空行\n    \"eol-last\": [2, 'always'], // 采用 强制使用换行 (LF)\n    // 要求或禁止在函数标识符和其调用之间有空格\n    \"func-call-spacing\": [2, 'never'], // 禁止在函数名和开括号之间有空格\n    // 要求函数名与赋值给它们的变量名或属性名相匹配\n    \"func-name-matching\": 0, // 不采用\n    // 要求或禁止命名的 function 表达式 \n    \"func-names\": 1, // 警告\n    // 强制 function 声明或表达式的一致性\n    \"func-style\": 0, // 不采用\n    // 强制在函数括号内使用一致的换行\n    \"function-paren-newline\": [2, 'consistent'], // 采用, 要求每个括号使用一致的换行。如果一个括号有换行，另一个括号没有换行，则报错。\n    // 禁止使用指定的标识符\n    \"id-blacklist\": 0, // 不采用\n    // 强制标识符的最小和最大长度\n    \"id-length\": 0, // 不采用\n    // 要求标识符匹配一个指定的正则表达式\n    \"id-match\": 0, // 不采用\n    // 强制隐式返回的箭头函数体的位置\n    \"implicit-arrow-linebreak\": [2, 'beside'], // 采用 禁止在箭头函数体之前出现换行\n    // 强制使用一致的缩进\n    \"indent\": [2, 2, {\n      SwitchCase: 1,\n      VariableDeclarator: 1,\n      outerIIFEBody: 1,\n      // MemberExpression: null,\n      FunctionDeclaration: {\n        parameters: 1,\n        body: 1\n      },\n      FunctionExpression: {\n        parameters: 1,\n        body: 1\n      },\n      CallExpression: {\n        arguments: 1\n      },\n      ArrayExpression: 1,\n      ObjectExpression: 1,\n      ImportDeclaration: 1,\n      flatTernaryExpressions: false,\n      // list derived from https://github.com/benjamn/ast-types/blob/HEAD/def/jsx.js\n      ignoredNodes: ['JSXElement', 'JSXElement > *', 'JSXAttribute', 'JSXIdentifier', 'JSXNamespacedName', 'JSXMemberExpression', 'JSXSpreadAttribute', 'JSXExpressionContainer', 'JSXOpeningElement', 'JSXClosingElement', 'JSXText', 'JSXEmptyExpression', 'JSXSpreadChild'],\n      ignoreComments: false\n    }], // 采用 一般2个缩进,特殊语句见配置\n    // 强制在 JSX 属性中一致地使用双引号或单引号\n    \"jsx-quotes\": 0, // 不采用\n    // 强制在对象字面量的属性中键和值之间使用一致的间距\n    \"key-spacing\": [2, { beforeColon: false, afterColon: true }], // 采用， 禁止在对象字面量的键和冒号之间存在空格，要求在对象字面量的冒号和值之间存在至少有一个空格\n    // 强制在关键字前后使用一致的空格\n    \"keyword-spacing\": [2, {\n      before: true, // 要求在关键字之前至少有一个空格\n      after: true, // 要求在关键字之后至少有一个空格\n      overrides: { // 允许覆盖指定的关键字的空格风格\n        return: { after: true },\n        throw: { after: true },\n        case: { after: true }\n      }\n    }], // 采用\n    // 强制行注释的位置\n    \"line-comment-position\": 0, // 不采用\n    // 强制使用一致的换行风格\n    \"linebreak-style\": [2, 'unix'], // 采用，强制使用 Unix 换行符： \\n。\n    // 要求在注释周围有空行\n    \"lines-around-comment\": 0, //不采用\n    // 要求或禁止类成员之间出现空行\n    \"lines-between-class-members\": [2, 'always', { exceptAfterSingleLine: false }],// 采用\n    // 强制可嵌套的块的最大深度\n    \"max-depth\": 0, // 不采用\n    // 强制一行的最大长度\n    \"max-len\": [2, 100, 2, {\n      ignoreUrls: true,\n      ignoreComments: false,\n      ignoreRegExpLiterals: true,\n      ignoreStrings: true,\n      ignoreTemplateLiterals: true,\n    }],// 采用，最长100，tab字符宽度为2\n    // 强制最大行数\n    \"max-lines\": 0, // 不采用\n    // 强制回调函数最大嵌套深度\n    \"max-nested-callbacks\": 0, // 不采用\n    // 强制函数定义中最多允许的参数数量\n    \"max-params\": 0, //不采用\n    // 强制函数块最多允许的的语句数量\n    \"max-statements\": 0, // 不采用\n    // 强制每一行中所允许的最大语句数量\n    \"max-statements-per-line\": 0, // 不采用\n    // 强制对多行注释使用特定风格\n    \"multiline-comment-style\": 0, // 不采用\n    // 要求或禁止在三元操作数中间换行\n    \"multiline-ternary\": 0, // 不采用\n    // 要求构造函数首字母大写\n    \"new-cap\": [2, {\n      newIsCap: true, // 要求调用 new 操作符时有首字母大小的函数\n      newIsCapExceptions: [],\n      capIsNew: false, // 要求调用首字母大写的函数时有 new 操作符\n      capIsNewExceptions: ['Immutable.Map', 'Immutable.Set', 'Immutable.List'], // 允许调用指定的首字母大写的函数时没有 new 操作符\n    }], // 采用\n    // 要求调用无参构造函数时有圆括号\n    \"new-parens\": 2, // 采用\n    // 要求方法链中每个调用都有一个换行符\n    \"newline-per-chained-call\": [2, { ignoreChainWithDepth: 4 }], // 允许在同一行成链的深度为4\n    // 禁用 Array 构造函数\n    \"no-array-constructor\": 2, // 采用\n    // 禁用按位运算符\n    \"no-bitwise\": 2, // 采用\n    // 禁用 continue 语句\n    \"no-continue\": 2, // 采用\n    // 禁止在代码后使用内联注释\n    \"no-inline-comments\": 0, // 不采用\n    // 禁止 if 作为唯一的语句出现在 else 语句中\n    \"no-lonely-if\": 2, // 采用\n    // 禁止混合使用不同的操作符\n    \"no-mixed-operators\": [2, {\n      groups: [\n        ['%', '**'],\n        ['%', '+'],\n        ['%', '-'],\n        ['%', '*'],\n        ['%', '/'],\n        ['**', '+'],\n        ['**', '-'],\n        ['**', '*'],\n        ['**', '/'],\n        ['&', '|', '^', '~', '<<', '>>', '>>>'],\n        ['==', '!=', '===', '!==', '>', '>=', '<', '<='],\n        ['&&', '||'],\n        ['in', 'instanceof']\n      ],\n      allowSamePrecedence: false\n    }], // 采用\n    // 禁止连续赋值\n    \"no-multi-assign\": 2, // 采用\n    // 禁止出现多行空行\n    \"no-multiple-empty-lines\": [2, { max: 2, maxEOF: 0 }], // 采用\n    // 禁用否定的表达式\n    \"no-negated-condition\": 0, // 不采用\n    // 禁用嵌套的三元表达式\n    \"no-nested-ternary\": 2, // 采用\n    // 禁用 Object 的构造函数\n    \"no-new-object\": 2, // 采用\n    // 禁用一元操作符 ++ 和 --\n    // 临时关闭\n    \"no-plusplus\": 0, // 采用\n    // 禁用特定的语法\n    \"no-restricted-syntax\": [\n      2,\n      {\n        selector: 'ForInStatement',\n        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.',\n      },\n      {\n        selector: 'ForOfStatement',\n        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.',\n      },\n      {\n        selector: 'LabeledStatement',\n        message: 'Labels are a form of GOTO; using them makes code confusing and hard to maintain and understand.',\n      },\n      {\n        selector: 'WithStatement',\n        message: '`with` is disallowed in strict mode because it makes code impossible to predict and optimize.',\n      },\n    ], // 采用\n    // 禁用 tab\n    \"no-tabs\": 2, // 采用\n    // 禁用三元操作符\n    \"no-ternary\": 0, //不采用\n    // 禁用行尾空格\n    \"no-trailing-spaces\": [2, {\n      skipBlankLines: false,\n      ignoreComments: false,\n    }], // 采用\n    // 禁止标识符中有悬空下划线（临时关闭）\n    /* \"no-underscore-dangle\": [2, {\n      allow: [],\n      allowAfterThis: false,\n      allowAfterSuper: false,\n      enforceInMethodNames: false,\n    }], */ // 采用\n    // 禁止可以在有更简单的可替代的表达式时使用三元操作符\n    \"no-unneeded-ternary\": [2, { defaultAssignment: false }],\n    // 禁止属性前有空白\n    \"no-whitespace-before-property\": 2, //采用\n    // 强制单个语句的位置\n    \"nonblock-statement-body-position\": [2, 'beside', { overrides: {} }], // 采用\n    // 强制大括号内换行符的一致性\n    \"object-curly-newline\": [2, {\n      ObjectExpression: { minProperties: 4, multiline: true, consistent: true },\n      ObjectPattern: { minProperties: 4, multiline: true, consistent: true },\n      ImportDeclaration: { minProperties: 4, multiline: true, consistent: true },\n      ExportDeclaration: { minProperties: 4, multiline: true, consistent: true },\n    }],// 采用\n    // 强制在大括号中使用一致的空格\n    \"object-curly-spacing\": [2, 'always'], // 采用\n    // 强制将对象的属性放在不同的行上\n    \"object-property-newline\": [2, {\n      allowAllPropertiesOnSameLine: true,\n    }],\n    // 强制函数中的变量要么一起声明要么分开声明\n    \"one-var\": [2, 'never'], // 采用, 要求每个作用域有多个变量声明\n    // 要求或禁止在变量声明周围换行\n    \"one-var-declaration-per-line\": [2, 'always'], // 采用，强制每个变量声明都换行\n    // 要求或禁止在可能的情况下使用简化的赋值操作符\n    \"operator-assignment\": [2, 'always'], //采用，要求尽可能地简化赋值操作\n    // 强制操作符使用一致的换行符\n    \"operator-linebreak\": [2, 'before', { overrides: { '=': 'none' } }], // 采用, 要求把换行符放在操作符前面\n    // 要求或禁止块内填充\n    \"padded-blocks\": [2, { blocks: 'never', classes: 'never', switches: 'never' }], // 采用\n    // 要求或禁止在语句间填充空行\n    \"padding-line-between-statements\": 0, // 不采用\n    // 要求对象字面量属性名称用引号括起来\n    \"quote-props\": [2, 'as-needed', { keywords: false, unnecessary: true, numbers: false }], //采用\n    // 强制使用一致的反勾号、双引号或单引号\n    \"quotes\": [2, 'single', { avoidEscape: true }], // 采用\n    // 要求使用 JSDoc 注释\n    \"require-jsdoc\": 0, // 不采用\n    // 要求或禁止使用分号代替 ASI\n    \"semi\": [2, 'always'], // 采用,要求在语句末尾使用分号\n    // 强制分号之前和之后使用一致的空格\n    \"semi-spacing\": ['error', { before: false, after: true }], // 采用\n    // 强制分号的位置\n    \"semi-style\": [2, 'last'], // 采用，强制分号出现在句子末尾。\n    // 要求对象属性按序排列\n    \"sort-keys\": 0, // 不采用\n    // 要求同一个声明块中的变量按顺序排列\n    \"sort-vars\": 0, // 不采用\n    // 强制在块之前使用一致的空格\n    \"space-before-blocks\": 2, // 采用\n    // 强制在 function的左括号之前使用一致的空格\n    \"space-before-function-paren\": [2, {\n      anonymous: 'always',\n      named: 'never',\n      asyncArrow: 'always'\n    }], // 采用\n    // 强制在圆括号内使用一致的空格\n    \"space-in-parens\": [2, 'never'], // 采用，强制圆括号内没有空格\n    // 要求操作符周围有空格\n    \"space-infix-ops\": 2, // 采用\n    // 强制在一元操作符前后使用一致的空格\n    \"space-unary-ops\": [2, {\n      words: true,\n      nonwords: false,\n      overrides: {\n      },\n    }], // 采用\n    // 强制在注释中 // 或 /* 使用一致的空格\n    \"spaced-comment\": [2, 'always', {\n      line: {\n        exceptions: ['-', '+'],\n        markers: ['=', '!'], // space here to support sprockets directives\n      },\n      block: {\n        exceptions: ['-', '+'],\n        markers: ['=', '!'], // space here to support sprockets directives\n        balanced: true,\n      }\n    }], // 采用\n    // 强制在 switch 的冒号左右有空格\n    \"switch-colon-spacing\": [2, { after: true, before: false }], // 采用\n    // 要求或禁止在模板标记和它们的字面量之间有空格\n    \"template-tag-spacing\": [2, 'never'], // 禁止在一个标记的函数和它的模板字面量之间有空格\n    // 要求或禁止 Unicode 字节顺序标记 (BOM)\n    \"unicode-bom\": [2, 'never'], // 采用\n    // 要求正则表达式被括号括起来\n    \"wrap-regex\": 0, // 不采用\n\n\n    // react相关配置\n    // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/display-name.md\n    'react/display-name': ['off', { ignoreTranspilerName: false }],\n\n    // Forbid certain propTypes (any, array, object)\n    // https://github.com/yannickcr/eslint-plugin-react/blob/843d71a432baf0f01f598d7cf1eea75ad6896e4b/docs/rules/forbid-prop-types.md\n    'react/forbid-prop-types': ['error', {\n      forbid: ['any', 'array', 'object'],\n      checkContextTypes: true,\n      checkChildContextTypes: true,\n    }],\n\n    // Forbid certain props on DOM Nodes\n    // https://github.com/yannickcr/eslint-plugin-react/blob/843d71a432baf0f01f598d7cf1eea75ad6896e4b/docs/rules/forbid-dom-props.md\n    'react/forbid-dom-props': ['off', { forbid: [] }],\n\n    // Enforce boolean attributes notation in JSX\n    // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/jsx-boolean-value.md\n    'react/jsx-boolean-value': ['error', 'never', { always: [] }],\n\n    // Validate closing bracket location in JSX\n    // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/jsx-closing-bracket-location.md\n    'react/jsx-closing-bracket-location': ['error', 'line-aligned'],\n\n    // Validate closing tag location in JSX\n    // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/jsx-closing-tag-location.md\n    'react/jsx-closing-tag-location': 'error',\n\n    // Enforce or disallow spaces inside of curly braces in JSX attributes\n    // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/jsx-curly-spacing.md\n    'react/jsx-curly-spacing': ['error', 'never', { allowMultiline: true }],\n\n    // Enforce event handler naming conventions in JSX\n    // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/jsx-handler-names.md\n    'react/jsx-handler-names': ['off', {\n      eventHandlerPrefix: 'handle',\n      eventHandlerPropPrefix: 'on',\n    }],\n\n    // Validate props indentation in JSX\n    // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/jsx-indent-props.md\n    'react/jsx-indent-props': ['error', 2],\n\n    // Validate JSX has key prop when in array or iterator\n    // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/jsx-key.md\n    'react/jsx-key': 'off',\n\n    // Limit maximum of props on a single line in JSX\n    // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/jsx-max-props-per-line.md\n    'react/jsx-max-props-per-line': ['error', { maximum: 1, when: 'multiline' }],\n\n    // Prevent usage of .bind() in JSX props\n    // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/jsx-no-bind.md\n    'react/jsx-no-bind': ['error', {\n      ignoreRefs: true,\n      allowArrowFunctions: true,\n      allowBind: false,\n    }],\n\n    // Prevent duplicate props in JSX\n    // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/jsx-no-duplicate-props.md\n    'react/jsx-no-duplicate-props': ['error', { ignoreCase: true }],\n\n    // Prevent usage of unwrapped JSX strings\n    // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/jsx-no-literals.md\n    'react/jsx-no-literals': ['off', { noStrings: true }],\n\n    // Disallow undeclared variables in JSX\n    // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/jsx-no-undef.md\n    'react/jsx-no-undef': 'error',\n\n    // Enforce PascalCase for user-defined JSX components\n    // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/jsx-pascal-case.md\n    'react/jsx-pascal-case': ['error', {\n      allowAllCaps: true,\n      ignore: [],\n    }],\n\n    // Enforce propTypes declarations alphabetical sorting\n    // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/sort-prop-types.md\n    'react/sort-prop-types': ['off', {\n      ignoreCase: true,\n      callbacksLast: false,\n      requiredFirst: false,\n      sortShapeProp: true,\n    }],\n\n    // Deprecated in favor of react/jsx-sort-props\n    'react/jsx-sort-prop-types': 'off',\n\n    // Enforce props alphabetical sorting\n    // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/jsx-sort-props.md\n    'react/jsx-sort-props': ['off', {\n      ignoreCase: true,\n      callbacksLast: false,\n      shorthandFirst: false,\n      shorthandLast: false,\n      noSortAlphabetically: false,\n      reservedFirst: true,\n    }],\n\n    // Enforce defaultProps declarations alphabetical sorting\n    // https://github.com/yannickcr/eslint-plugin-react/blob/843d71a432baf0f01f598d7cf1eea75ad6896e4b/docs/rules/jsx-sort-default-props.md\n    'react/jsx-sort-default-props': ['off', {\n      ignoreCase: true,\n    }],\n\n    // Prevent React to be incorrectly marked as unused\n    // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/jsx-uses-react.md\n    'react/jsx-uses-react': ['error'],\n\n    // Prevent variables used in JSX to be incorrectly marked as unused\n    // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/jsx-uses-vars.md\n    'react/jsx-uses-vars': 'error',\n\n    // Prevent usage of dangerous JSX properties\n    // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/no-danger.md\n    'react/no-danger': 'warn',\n\n    // Prevent usage of deprecated methods\n    // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/no-deprecated.md\n    'react/no-deprecated': ['error'],\n\n    // Prevent usage of setState in componentDidMount\n    // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/no-did-mount-set-state.md\n    // this is necessary for server-rendering\n    'react/no-did-mount-set-state': 'off',\n\n    // Prevent usage of setState in componentDidUpdate\n    // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/no-did-update-set-state.md\n    'react/no-did-update-set-state': 'error',\n\n    // Prevent usage of setState in componentWillUpdate\n    // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/no-will-update-set-state.md\n    'react/no-will-update-set-state': 'error',\n\n    // Prevent direct mutation of this.state\n    // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/no-direct-mutation-state.md\n    'react/no-direct-mutation-state': 'off',\n\n    // Prevent usage of isMounted\n    // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/no-is-mounted.md\n    'react/no-is-mounted': 'error',\n\n    // Prevent multiple component definition per file\n    // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/no-multi-comp.md\n    'react/no-multi-comp': ['error', { ignoreStateless: true }],\n\n    // Prevent usage of setState\n    // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/no-set-state.md\n    'react/no-set-state': 'off',\n\n    // Prevent using string references\n    // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/no-string-refs.md\n    'react/no-string-refs': 'error',\n\n    // Prevent usage of unknown DOM property\n    // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/no-unknown-property.md\n    'react/no-unknown-property': 'error',\n\n    // Require ES6 class declarations over React.createClass\n    // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/prefer-es6-class.md\n    'react/prefer-es6-class': ['error', 'always'],\n\n    // Require stateless functions when not using lifecycle methods, setState or ref\n    // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/prefer-stateless-function.md\n    'react/prefer-stateless-function': ['error', { ignorePureComponents: true }],\n\n    // Prevent missing props validation in a React component definition\n    // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/prop-types.md\n    'react/prop-types': ['error', {\n      ignore: [],\n      customValidators: [],\n      skipUndeclared: false\n    }],\n\n    // Prevent missing React when using JSX\n    // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/react-in-jsx-scope.md\n    'react/react-in-jsx-scope': 'error',\n\n    // Require render() methods to return something\n    // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/require-render-return.md\n    'react/require-render-return': 'error',\n\n    // Prevent extra closing tags for components without children\n    // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/self-closing-comp.md\n    'react/self-closing-comp': 'error',\n\n    // Enforce component methods order\n    // https://github.com/yannickcr/eslint-plugin-react/blob/843d71a432baf0f01f598d7cf1eea75ad6896e4b/docs/rules/sort-comp.md\n    'react/sort-comp': ['error', {\n      order: [\n        'static-methods',\n        'instance-variables',\n        'lifecycle',\n        '/^on.+$/',\n        'getters',\n        'setters',\n        '/^(get|set)(?!(InitialState$|DefaultProps$|ChildContext$)).+$/',\n        'instance-methods',\n        'everything-else',\n        'rendering',\n      ],\n      groups: {\n        lifecycle: [\n          'displayName',\n          'propTypes',\n          'contextTypes',\n          'childContextTypes',\n          'mixins',\n          'statics',\n          'defaultProps',\n          'constructor',\n          'getDefaultProps',\n          'getInitialState',\n          'state',\n          'getChildContext',\n          'componentWillMount',\n          'componentDidMount',\n          'componentWillReceiveProps',\n          'shouldComponentUpdate',\n          'componentWillUpdate',\n          'componentDidUpdate',\n          'componentWillUnmount',\n        ],\n        rendering: [\n          '/^render.+$/',\n          'render'\n        ],\n      },\n    }],\n\n    // Prevent missing parentheses around multilines JSX\n    // https://github.com/yannickcr/eslint-plugin-react/blob/843d71a432baf0f01f598d7cf1eea75ad6896e4b/docs/rules/jsx-wrap-multilines.md\n    'react/jsx-wrap-multilines': ['error', {\n      declaration: 'parens-new-line',\n      assignment: 'parens-new-line',\n      return: 'parens-new-line',\n      arrow: 'parens-new-line',\n      condition: 'parens-new-line',\n      logical: 'parens-new-line',\n      prop: 'parens-new-line',\n    }],\n\n    // 当元素为多行时，要求JSX元素中的第一个属性位于新行上。\n    // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/jsx-first-prop-new-line.md\n    'react/jsx-first-prop-new-line': [2, 'multiline-multiprop'], // 采用，如果JSX标签占用多个行并且有多个属性，则第一个属性应该总是放置在一个新行上。这是默认值。\n\n    // 在JSX的等号两侧是否强制留有空格\n    // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/jsx-equals-spacing.md\n    'react/jsx-equals-spacing': [2, 'never'], // 采用，等号周围的不允许空格\n\n    // 强制jsx的缩进风格\n    // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/jsx-indent.md\n    'react/jsx-indent': [2, 2], // 采用，两个空格作为缩进\n\n    // 不允许不安全的target='_blank'用法, 具体见连接说明\n    // https://github.com/yannickcr/eslint-plugin-react/blob/ac102885765be5ff37847a871f239c6703e1c7cc/docs/rules/jsx-no-target-blank.md\n    'react/jsx-no-target-blank': [2, { enforceDynamicLinks: 'always' }], // 采用，如果是动态链接则不强制\n\n    // 只有.jsx文件允许写JSX语法\n    // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/jsx-filename-extension.md\n    'react/jsx-filename-extension': [2, { extensions: ['.jsx'] }],// 采用\n\n    // 防止JS注释意外的作为文本注入到JSX中\n    // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/jsx-no-comment-textnodes.md\n    'react/jsx-no-comment-textnodes': 2, // 采用，jsx的注释问题\n\n    // 不允许使用react或者reactdom的render方法的返回值\n    // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/no-render-return-value.md\n    'react/no-render-return-value': 2, // 采用，建议采用ref\n\n    // 要求有shouldComponentUpdate方法, 或者采用PureRenderMixin\n    // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/require-optimization.md\n    'react/require-optimization': [0, { allowDecorators: [] }], // 不采用\n\n    // 禁止使用findDOMNode()方法\n    // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/no-find-dom-node.md\n    'react/no-find-dom-node': 2, // 采用\n\n    // 在Components中禁用一些特定的props\n    // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/forbid-component-props.md\n    'react/forbid-component-props': 0, // 不采用\n\n    // 禁用特定元素\n    // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/forbid-elements.md\n    'react/forbid-elements': 0, // 不采用\n\n    // 避免在children和dangerouslySetInnerHTML属性共存时出现问题\n    // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/no-danger-with-children.md\n    'react/no-danger-with-children': 2, // 采用\n\n    // 防止未使用的propType定义\n    // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/no-unused-prop-types.md\n    'react/no-unused-prop-types': [2, {\n      customValidators: [\n      ],\n      skipShapeProps: true,\n    }], // 采用\n\n    // 要求样式的值为对象或者变量\n    // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/style-prop-object.md\n    'react/style-prop-object': 2, // 采用\n\n    // 禁止无效字符的出现\n    // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/no-unescaped-entities.md\n    'react/no-unescaped-entities': 2, // 采用\n\n    // 禁止通过children props来传值\n    // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/no-children-prop.md\n    'react/no-children-prop': 2, // 采用\n\n    // 在JSX打开和关闭括号内和周围验证空格\n    // https://github.com/yannickcr/eslint-plugin-react/blob/843d71a432baf0f01f598d7cf1eea75ad6896e4b/docs/rules/jsx-tag-spacing.md\n    'react/jsx-tag-spacing': [2, {\n      closingSlash: 'never',\n      beforeSelfClosing: 'always',\n      afterOpening: 'never',\n      beforeClosing: 'never',\n    }], // 采用, 不留空格\n\n    // 强制在闭JSX元素标签之前留空格\n    // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/jsx-space-before-closing.md\n    // Deprecated in favor of jsx-tag-spacing\n    'react/jsx-space-before-closing': 0, // 不采用\n\n    // 禁止使用数组的索引作为元素的key属性\n    // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/no-array-index-key.md\n    'react/no-array-index-key': 2, // 采用, 原因可以见链接\n\n    // 强制给每个不是必须的属性定义一个默认值\n    // https://github.com/yannickcr/eslint-plugin-react/blob/843d71a432baf0f01f598d7cf1eea75ad6896e4b/docs/rules/require-default-props.md\n    'react/require-default-props': [0, {\n      forbidDefaultForRequired: true,\n    }], // 采用, 写了isRequired的属性则禁止设置默认值\n\n    // 禁止使用没有被export的prototype\n    // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/forbid-foreign-prop-types.md\n    'react/forbid-foreign-prop-types': ['warn', { allowInPropTypes: true }], // 设置为警告级别\n\n    // 不要给单标签的jsx元素传入children\n    // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/void-dom-elements-no-children.md\n    'react/void-dom-elements-no-children': 2, // 采用\n\n    // 强制所有的默认prop都对应一个非必须的proptype\n    // https://github.com/yannickcr/eslint-plugin-react/blob/9e13ae2c51e44872b45cc15bf1ac3a72105bdd0e/docs/rules/default-props-match-prop-types.md\n    'react/default-props-match-prop-types': [2, { allowRequiredDefaults: false }], // 采用\n\n    // 继承React.PureComponent时，禁止使用shouldComponentUpdate\n    // https://github.com/yannickcr/eslint-plugin-react/blob/9e13ae2c51e44872b45cc15bf1ac3a72105bdd0e/docs/rules/no-redundant-should-component-update.md\n    'react/no-redundant-should-component-update': 2, // 采用s\n\n    // 禁止存在未使用的state值\n    // https://github.com/yannickcr/eslint-plugin-react/pull/1103/\n    'react/no-unused-state': 2, // 采用\n\n    // 强制布尔值属性命名的的一致性\n    // https://github.com/yannickcr/eslint-plugin-react/blob/843d71a432baf0f01f598d7cf1eea75ad6896e4b/docs/rules/boolean-prop-naming.md\n    'react/boolean-prop-naming': 0, // 不采用\n\n    // 防止常见的套管缺陷\n    // https://github.com/yannickcr/eslint-plugin-react/blob/73abadb697034b5ccb514d79fb4689836fe61f91/docs/rules/no-typos.md\n    'react/no-typos': 2, // 采用\n\n    // 禁止在props和children中使用无意义的大括号\n    // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/jsx-curly-brace-presence.md\n    'react/jsx-curly-brace-presence': [2, { props: 'never', children: 'never' }], // 采用\n\n    // 每行一个jsx元素\n    // https://github.com/yannickcr/eslint-plugin-react/blob/843d71a432baf0f01f598d7cf1eea75ad6896e4b/docs/rules/jsx-one-expression-per-line.md\n    // TODO: re-enable when an option for text children is available\n    'react/jsx-one-expression-per-line': 0, // 不采用\n\n    // 强制使用desconstruct的一致性\n    // https://github.com/yannickcr/eslint-plugin-react/blob/843d71a432baf0f01f598d7cf1eea75ad6896e4b/docs/rules/destructuring-assignment.md\n    // TODO: re-enable when component detection is fixed\n    'react/destructuring-assignment': 0, // 不采用\n\n    // 禁止在this.setState中使用this.state\n    // https://github.com/yannickcr/eslint-plugin-react/blob/843d71a432baf0f01f598d7cf1eea75ad6896e4b/docs/rules/no-access-state-in-setstate.md\n    'react/no-access-state-in-setstate': 2, // 采用\n\n    // 禁止使用没有显式type属性的按钮元素\n    // https://github.com/yannickcr/eslint-plugin-react/blob/843d71a432baf0f01f598d7cf1eea75ad6896e4b/docs/rules/button-has-type.md\n    'react/button-has-type': [2, {\n      button: true,\n      submit: true,\n      reset: false,\n    }], // 采用\n\n    // 确保内联标签两边有空格\n    'react/jsx-child-element-spacing': 0, // 不采用\n\n    // 禁止在无状态组件中使用this\n    // https://github.com/yannickcr/eslint-plugin-react/blob/843d71a432baf0f01f598d7cf1eea75ad6896e4b/docs/rules/no-this-in-sfc.md\n    'react/no-this-in-sfc': 2, // 采用\n\n    // 检查jsx最大深度\n    // https://github.com/yannickcr/eslint-plugin-react/blob/abe8381c0d6748047224c430ce47f02e40160ed0/docs/rules/jsx-max-depth.md\n    'react/jsx-max-depth': 0, // 不采用\n\n    // 禁止在jsx props之间存在多个空格\n    // https://github.com/yannickcr/eslint-plugin-react/blob/ac102885765be5ff37847a871f239c6703e1c7cc/docs/rules/jsx-props-no-multi-spaces.md\n    'react/jsx-props-no-multi-spaces': 2, // 采用\n\n    // 不允许使用UNSAFE_ methods\n    // https://github.com/yannickcr/eslint-plugin-react/blob/157cc932be2cfaa56b3f5b45df6f6d4322a2f660/docs/rules/no-unsafe.md\n    'react/no-unsafe': 0, // 不采用\n  }\n};\n"
  },
  {
    "path": ".gitignore",
    "content": "node_modules\nnpm-debug.log\nyarn-error.log\ntarget\nnode\nsrc/_tmp\nstatic\ndist\ncoverage\nrekit_temp_app\n_book\n.nyc_output\n.tmp\n.DS_Store\nThumbs.db\n.vscode"
  },
  {
    "path": "README.md",
    "content": "# react-starter\n\nPC 端 react+redux 的脚手架，持续进行更新迭代。\n\n\n## 特点\n\n1. 当中集成了当前react生态的几个主流技术栈：\n  - react 16.12.0\n  - ant design 3.26.2\n  - react-router 5.1.2\n  - redux 4.0.4\n  - redux-thunk 2.2.0\n  - webpack 4.41.2\n\n2. 符合目前前后端分离项目的开发、部署、测试、上线各环节及操作的要求。\n3. 优化了传统redux-thunk中action、reducer书写上的不便。\n4. 根据路由地址，懒加载所需模块，就是说目前脚手架中提供的示例代码无需删除，只需要确保路由无效即可\n5. 增加错误边界，捕捉嵌套组件在生命过程中发生的错误\n6. 封装 axios（`src/common/request.js`），对失败的请求进行了统一处理，使用方法：\n\n```\nimport { get, post, put, del } from 'common/request';\npost(URL, params).then(res => {});\n\n// 如果需要自行处理请求失败的情况，也可以直接引入 axios 实例\nimport axios from 'common/request';\naxios.post(URL, params)\n  .then(res => {})\n  .catch(error => {})\n```\n\n## 文件目录\n\n> 注意：带 ✎ 号的表示示例配置，可以根据自己的项目配置自行替换；带 +/- 号的表示示例组件，可保留修改或删除\n\n```\n├─asset                     // ========================= 静态资源目录\n│  ├─css\n|  |  ├─app.less            // ========================= 样式文件入口\n|  |  ├─base.less           // ========================= 基础样式\n|  |  ├─icon.less           // ========================= iconfont 样式（✎）\n|  |  ├─reset.less          // ========================= 样式重置文件\n|  |  ├─variables.less      // ========================= 变量文件（✎）\n|  |  └─vendor_antd.less    // ========================= 专门用于覆盖 antd 样式（✎）\n│  ├─font                   // ========================= iconfont文件，在icon.less文件中引用\n|  |  ├─iconfont.eot（+/-）\n|  |  ├─iconfont.svg（+/-）\n|  |  ├─iconfont.ttf（+/-）\n|  |  └─iconfont.woff（+/-）\n│  └─img                    // ========================= 图片资源存放目录\n│\n├─build                     //  ========================= webpack 相关配置目录\n│   ├─postcss.config.js     //  ========================= postcss 相关配置\n│   ├─publicPath.js         //  ========================= 发布路径（✎）\n│   ├─theme.js              //  ========================= antd 主题配置（✎）\n│   ├─webpack.base.conf.js  //  ========================= webpack 公用项配置\n│   ├─webpack.dev.conf.js   //  ========================= webpack 开发/联调环境项配置\n│   └─webpack.prod.conf.js  //  ========================= webpack 生产/测试环境项配置\n│\n├─mock                      //  ========================= mock 文件目录\n│  ├─config\n│  ├─data\n│  └─template\n│\n├─src                       //  ========================= js 源码目录\n│   ├─common                //  ========================= 公共模块目录\n│   │  └─request.js         //  ========================= 对axios进行封装\n│   │ \n│   ├─components            //  ========================= 纯组件目录\n│   │  ├─Layout             //  ========================= 布局相关组件目录\n│   │  │  ├─Footer.jsx（+/-）\n│   │  │  ├─Header.jsx（+/-）\n│   │  │  └─Sider.jsx（+/-）\n│   │  ├─Pages（+/-）        //  ========================= 分页组件\n│   │  ├─ErrorBoundary.jsx  //  ========================= 错误边界组件\n│   │  └─index.js（+/-）\n│   │ \n│   ├─constants\n│   │  ├─constant.js        //  ========================= 常量集中管理目录（✎）\n│   │  └─url.js             //  ========================= url集中管理目录（✎）\n│   │ \n│   ├─redux                 //  ========================= redux 处理目录\n│   │  ├─skuunit（+/-）\n│   │  ├─configureStore.js  //  ========================= store/中间件配置\n│   │  └─reducers.js        //  ========================= 所有reducer入口（✎）\n│   │ \n│   ├─routers               //  ========================= 按路由划分的业务模块目录\n│   │  ├─Skuunit（+/-）\n│   │  ├─index.jsx（✎）\n│   │  └─PrimaryLayout.jsx（+/-）\n│   │ \n│   └─app.jsx               //  ========================= 项目入口\n│\n├─.babelrc                  //  ========================= babel配置文件\n├─.eslintignore\n├─.eslintrc.js              //  ========================= eslint规则文件\n├─.gitignore\n├─index.html\n├─package-lock.json\n├─package.json\n├─README.md（✎）\n├─template.html（✎）         //  ========================= html嵌入模板\n└─webpack.config.js         //  ========================= webpack配置入口\n\n```\n\n## Changelog\n\n- 2019.07.15\n  - [x] src目录支持使用less\n  - [x] 兼容IE11\n\n- 2019.06.11\n  - [x] 更改UI\n\n- 2019.06.03\n  - [x] 升级babel 7\n  - [x] 更新示例组件\n  - [x] 修复eslint报错\n  - [x] 完善README.md\n\n- 2019.05.22\n  - [x] 完成基础版本，给出规范目录结构，满足打包构建路由懒加载数据管理错误处理一条龙服务。\n\n\n## babel7依赖说明\n\n基础依赖：\n\n- @babel/cli: 为babel的脚手架工具\n- @babel/core: babel-core是作为babel的核心存在，babel的核心api都在这个模块里面，比如：transform，用于字符串转码得到AST\n- @babel/plugin-proposal-class-properties: 解析class类的属性\n- @babel/plugin-proposal-decorators: 解析装饰器模式语法，如使用react-redux的@connect\n- @babel/plugin-proposal-export-default-from: 解析export xxx from 'xxx'语法\n- @babel/plugin-transform-runtime: 防止转换后的代码重复\n- @babel/preset-env : 官方解释“用于编写下一代JavaScript的编译器”，编译成浏览器认识的JavaScript标准\n- @babel/preset-react: 用于编译react的jsx，开发react应用必备\n- babel-loader: 就是用于编译JavaScript代码\n- babel-plugin-import: 用于进行按需加载\n\n代码优化相关依赖：\n\n- [babel-plugin-transform-imports](https://www.npmjs.com/package/babel-plugin-transform-imports)：去除未使用的模块\n- [babel-plugin-transform-react-remove-prop-types](https://www.npmjs.com/package/babel-plugin-transform-react-remove-prop-types): 生产环境下移除不必要的 React propTypes，以减少代码体积\n\n\n## 参考文章\n\n- 2019.06.03\n  - [Webpack4+Babel7优化70%速度](https://juejin.im/post/5c763885e51d457380771ab0#heading-11)\n  - [一口(很长的)气了解 babel](https://juejin.im/post/5c19c5e0e51d4502a232c1c6#heading-0)\n- 2019.05.22\n  - [Web Performance Optimization with webpack](https://developers.google.com/web/fundamentals/performance/webpack/)\n  - [Webpack 4 配置最佳实践](https://juejin.im/post/5b304f1f51882574c72f19b0#heading-1)\n  - [Webpack 4 默认分包策略](https://panjiachen.github.io/awesome-bookmarks/blog/webpack/webpack4-b.html)\n  - [webpack-libs-optimizations](https://github.com/GoogleChromeLabs/webpack-libs-optimizations)\n\n\n朋友们如果有一些对本项目得建议，或者想法欢迎去 github 提 issues，我将持续改进优化该项目\n"
  },
  {
    "path": "asset/css/app.less",
    "content": "/* 样式文件入口 */\n@import 'reset.less';\n@import 'variables.less';\n@import 'icon.less';\n@import 'base.less';\n\n// 样式覆盖\n@import url(\"vendor_antd.less\");\n"
  },
  {
    "path": "asset/css/base.less",
    "content": "/* 基础样式 */\nhtml, body {\n  font-size: 14px;\n  height: 100%;\n}\n\n#app {\n  height: 100%;\n}\n\n.fl {\n\t.fl();\n}\n.fr {\n\t.fr();\n}\n\n.ml15 {\n\tmargin-left: 5px;\n}\n.ml10 {\n\tmargin-left: 10px;\n}\n.ml15 {\n\tmargin-left: 15px;\n}\n.ml20 {\n\tmargin-left: 20px;\n}\n.mr5 {\n\tmargin-right: 5px;\n}\n.mr10 {\n\tmargin-right: 10px;\n}\n.mr15 {\n\tmargin-right: 15px;\n}\n.mr20 {\n\tmargin-right: 20px;\n}\n.m15 {\n\tmargin: 15px;\n}\n.mt15 {\n\tmargin-top: 15px;\n}\n.mt30 {\n\tmargin-top: 30px;\n}\n.mt45 {\n\tmargin-top: 45px;\n}\n.mb10 {\n\tmargin-bottom: 10px;\n}\n.mb15 {\n\tmargin-bottom: 15px;\n}\n.mb20 {\n\tmargin-bottom: 20px;\n}"
  },
  {
    "path": "asset/css/icon.less",
    "content": "/* iconfont样式 */\n@font-face {\n  font-family: \"iconfont\";\n  // src: url('../font/iconfont.eot'); /* IE9*/\n  // src: url('../font/iconfont.eot') format('embedded-opentype'), /* IE6-IE8 */\n  // url('../font/iconfont.woff') format('woff'), /* chrome, firefox */\n  // url('../font/iconfont.ttf') format('truetype'), /* chrome, firefox, opera, Safari, Android, iOS 4.2+*/\n  // url('../font/iconfont.svg') format('svg'); /* iOS 4.1- */\n}\n\n.iconfont {\n  font-family: \"iconfont\" !important;\n  font-size: 16px;\n  font-style: normal;\n  display: inline-block;\n  vertical-align: baseline;\n  text-align: center;\n  text-transform: none;\n  line-height: 1;\n  text-rendering: optimizeLegibility;\n  -webkit-font-smoothing: antialiased;\n  -moz-osx-font-smoothing: grayscale;\n}\n\n"
  },
  {
    "path": "asset/css/reset.less",
    "content": "/* 样式重置文件 */\n\n//\n// 1. Remove default margin padding.\n//\n\nhtml, body, div, ul, li, h1, h2, h3, h4, h5, h6, p, dl, dt, dd, ol, form, input, textarea, th, td, select {\n  margin: 0;\n  padding: 0;\n}\n\n// HTML5 display definitions\n// ==========================================================================\n\n//\n// Correct `block` display not defined for any HTML5 element in IE 8/9.\n// Correct `block` display not defined for `details` or `summary` in IE 10/11\n// and Firefox.\n// Correct `block` display not defined for `main` in IE 11.\n//\n\narticle,\naside,\ndetails,\nfigcaption,\nfigure,\nfooter,\nheader,\nhgroup,\nmain,\nmenu,\nnav,\nsection,\nsummary {\n  display: block;\n}\n\n//\n// 1. Correct `inline-block` display not defined in IE 8/9.\n// 2. Normalize vertical alignment of `progress` in Chrome, Firefox, and Opera.\n//\n\naudio,\ncanvas,\nprogress,\nvideo {\n  display: inline-block;\n  vertical-align: baseline;\n}\n\n//\n// Prevent modern browsers from displaying `audio` without controls.\n// Remove excess height in iOS 5 devices.\n//\n\naudio:not([controls]) {\n  display: none;\n  height: 0;\n}\n\n//\n// Address `[hidden]` styling not present in IE 8/9/10.\n// Hide the `template` element in IE 8/9/10/11, Safari, and Firefox < 22.\n//\n\n[hidden],\ntemplate {\n  display: none;\n}\n\n// Links\n// ==========================================================================\n\n//\n// Remove the gray background color from active links in IE 10.\n//\n\na {\n  background-color: transparent;\n}\n\n//\n// Improve readability of focused elements when they are also in an\n// active/hover state.\n//\n\na:active,\na:hover {\n  outline: 0;\n}\n// Text-level semantics\n// ==========================================================================\n\n//\n// Address styling not present in IE 8/9/10/11, Safari, and Chrome.\n//\n\nabbr[title] {\n  border-bottom: 1px dotted;\n}\n\n//\n// Address style set to `bolder` in Firefox 4+, Safari, and Chrome.\n//\n\nb,\nstrong {\n  font-weight: bold;\n}\n\n//\n// Address styling not present in Safari and Chrome.\n//\n\ndfn {\n  font-style: italic;\n}\n\n//\n// Unified \"h1\" - \"h6\" font weight\n//\n\nh1, h2, h3, h4, h5, h6{font-weight:normal;}\n\n//\n// Remove ul-ol the default styles\n//\n\nul,ol {\n  list-style: none;\n}\n\n\n//\n// Address styling not present in IE 8/9.\n//\n\nmark {\n  background: #ff0;\n  color: #000;\n}\n\n//\n// Address inconsistent and variable font size in all browsers.\n//\n\nsmall {\n  font-size: 80%;\n}\n\n//\n// Prevent `sub` and `sup` affecting `line-height` in all browsers.\n//\n\nsub,\nsup {\n  font-size: 75%;\n  line-height: 0;\n  position: relative;\n  vertical-align: baseline;\n}\n\nsup {\n  top: -0.5em;\n}\n\nsub {\n  bottom: -0.25em;\n}\n\n// Embedded content\n// ==========================================================================\n\n//\n// Remove border when inside `a` element in IE 8/9/10.\n//\n\nimg {\n  border: 0;\n}\n\n//\n// Correct overflow not hidden in IE 9/10/11.\n//\n\nsvg:not(:root) {\n  overflow: hidden;\n}\n\n// Grouping content\n// ==========================================================================\n\n//\n// Address margin not present in IE 8/9 and Safari.\n//\n\nfigure {\n  margin: 1em 40px;\n}\n\n//\n// Address differences between Firefox and other browsers.\n//\n\nhr {\n  box-sizing: content-box;\n  height: 0;\n}\n\n//\n// Contain overflow in all browsers.\n//\n\npre {\n  overflow: auto;\n}\n\n//\n// Address odd `em`-unit font size rendering in all browsers.\n//\n\ncode,\nkbd,\npre,\nsamp {\n  font-family: monospace, monospace;\n  font-size: 1em;\n}\n\n// Forms\n// ==========================================================================\n\n//\n// Known limitation: by default, Chrome and Safari on OS X allow very limited\n// styling of `select`, unless a `border` property is set.\n//\n\n//\n// 1. Correct color not being inherited.\n//    Known issue: affects color of disabled elements.\n// 2. Correct font properties not being inherited.\n// 3. Address margins set differently in Firefox 4+, Safari, and Chrome.\n//\n\nbutton,\ninput,\noptgroup,\nselect,\ntextarea {\n  color: inherit; // 1\n  font: inherit; // 2\n  margin: 0; // 3\n}\n\n//\n// Address `overflow` set to `hidden` in IE 8/9/10/11.\n//\n\nbutton {\n  overflow: visible;\n}\n\n//\n// Address inconsistent `text-transform` inheritance for `button` and `select`.\n// All other form control elements do not inherit `text-transform` values.\n// Correct `button` style inheritance in Firefox, IE 8/9/10/11, and Opera.\n// Correct `select` style inheritance in Firefox.\n//\n\nbutton,\nselect {\n  text-transform: none;\n}\n\n//\n// 1. Avoid the WebKit bug in Android 4.0.* where (2) destroys native `audio`\n//    and `video` controls.\n// 2. Correct inability to style clickable `input` types in iOS.\n// 3. Improve usability and consistency of cursor style between image-type\n//    `input` and others.\n//\n\nbutton,\nhtml input[type=\"button\"], // 1\ninput[type=\"reset\"],\ninput[type=\"submit\"] {\n  -webkit-appearance: button; // 2\n  cursor: pointer; // 3\n}\n\n//\n// Re-set default cursor for disabled elements.\n//\n\nbutton[disabled],\nhtml input[disabled] {\n  cursor: default;\n}\n\n//\n// Remove inner padding and border in Firefox 4+.\n//\n\nbutton::-moz-focus-inner,\ninput::-moz-focus-inner {\n  border: 0;\n  padding: 0;\n}\n\n//\n// Address Firefox 4+ setting `line-height` on `input` using `!important` in\n// the UA stylesheet.\n//\n\ninput {\n  line-height: normal;\n}\n\n//\n// It's recommended that you don't attempt to style these elements.\n// Firefox's implementation doesn't respect box-sizing, padding, or width.\n//\n// 1. Address box sizing set to `content-box` in IE 8/9/10.\n// 2. Remove excess padding in IE 8/9/10.\n//\n\ninput[type=\"checkbox\"],\ninput[type=\"radio\"] {\n  box-sizing: border-box; // 1\n  padding: 0; // 2\n}\n\n//\n// Fix the cursor style for Chrome's increment/decrement buttons. For certain\n// `font-size` values of the `input`, it causes the cursor style of the\n// decrement button to change from `default` to `text`.\n//\n\ninput[type=\"number\"]::-webkit-inner-spin-button,\ninput[type=\"number\"]::-webkit-outer-spin-button {\n  height: auto;\n}\n\n//\n// 1. Address `appearance` set to `searchfield` in Safari and Chrome.\n// 2. Address `box-sizing` set to `border-box` in Safari and Chrome.\n//\n\ninput[type=\"search\"] {\n  -webkit-appearance: textfield; // 1\n  box-sizing: content-box; //2\n}\n\n//\n// Remove inner padding and search cancel button in Safari and Chrome on OS X.\n// Safari (but not Chrome) clips the cancel button when the search input has\n// padding (and `textfield` appearance).\n//\n\ninput[type=\"search\"]::-webkit-search-cancel-button,\ninput[type=\"search\"]::-webkit-search-decoration {\n  -webkit-appearance: none;\n}\n\n//\n// Define consistent border, margin, and padding.\n//\n\nfieldset {\n  border: 1px solid #c0c0c0;\n  margin: 0 2px;\n  padding: 0.35em 0.625em 0.75em;\n}\n\n//\n// 1. Correct `color` not being inherited in IE 8/9/10/11.\n// 2. Remove padding so people aren't caught out if they zero out fieldsets.\n//\n\nlegend {\n  border: 0; // 1\n  padding: 0; // 2\n}\n\n//\n// 1.Don't allow the user to zoom\n// 2.Remove default vertical scrollbar in IE 8/9/10/11.\n//\n\ntextarea {\n  resize: none;\n  overflow: auto;\n}\n\n//\n// Don't inherit the `font-weight` (applied by a rule above).\n// NOTE: the default cannot safely be changed in Chrome and Safari on OS X.\n//\n\noptgroup {\n  font-weight: bold;\n}\n\n// Tables\n// ==========================================================================\n\n//\n// Remove most spacing between table cells.\n//\n\ntable {\n  border-collapse: collapse;\n  border-spacing: 0;\n}\n\ntd,\nth {\n  padding: 0;\n}\n\n\nem, b, i {\n  font-style: normal;\n  font-weight: normal;\n}\n\n\n/* �������� */\ninput, select {\n    vertical-align:baseline\n}\n\n\n\n\n\n\n"
  },
  {
    "path": "asset/css/variables.less",
    "content": "//\n// ==变量\n// --------------------------------------------------\n\n\n//== 颜色\n//\n//## 灰色及品牌标记颜色引导.\n\n@gray-base:              #000;\n@gray-darker:            lighten(@gray-base, 13.5%); // #222\n@gray-dark:              lighten(@gray-base, 20%);   // #333\n@gray-dim:               lighten(@gray-base, 25%);   // #404040  系统主要文字颜色\n@gray:                   lighten(@gray-base, 33.5%); // #555\n@gray-light:             lighten(@gray-base, 46.7%); // #777\n@gray-minor:             lighten(@gray-base, 59.2%); // #979797  系统次要文字颜色\n@gray-lighter:           lighten(@gray-base, 93.5%); // #eee\n\n\n\n@brand-link:            #2577FF; //链接\n@brand-hover:           #094D86;; //链接hover\n@brand-success:         #3BA861; //状态成功、正常\n@brand-warning:         #f0ad4e; //提示警告\n@brand-danger:          #B01207; //危险、失败\n@brand-forbid:          #b4b4b4; //不可用\n@title-background:      #E9EBF1; //标题、条幅背景色\n\n\n\n//== Scaffolding\n//\n//## Settings for some of the most global styles.\n\n//** Background color for `<body>`.\n@body-bg:               #f1f2f3;\n//** Global text color on `<body>`.\n@text-color:            @gray-dim;\n\n//** Global textual link color.\n@link-color:            @brand-link;\n//** Link hover color set via `darken()` function.\n@link-hover-color:      @brand-hover;\n//** Link hover decoration.\n@link-hover-decoration: underline;\n\n\n//== Typography\n//\n//## Font, line-height, and color for body text, headings, and more.\n\n@font-family-sans-serif:  \"Microsoft Yahei\", Arial , Tahoma , Helvetica, sans-serif;\n@font-family-serif:       Georgia, \"Times New Roman\", Times, serif;\n//** Default monospace fonts for `<code>`, `<kbd>`, and `<pre>`.\n@font-family-monospace:   Menlo, Monaco, Consolas, \"Courier New\", monospace;\n@font-family-base:        @font-family-sans-serif;\n\n@font-size-base:          14px;\n@font-size-large:         ceil((@font-size-base * 1.25)); // ~18px\n@font-size-medium:        ceil((@font-size-base * 1.14)); // ~16px \n@font-size-small:         ceil((@font-size-base * 0.85)); // ~12px\n\n@font-size-h1:            floor((@font-size-base * 2.6)); // ~36px\n@font-size-h2:            floor((@font-size-base * 2.15)); // ~30px\n@font-size-h3:            ceil((@font-size-base * 1.7)); // ~24px\n@font-size-h4:            ceil((@font-size-base * 1.25)); // ~18px\n@font-size-h5:            @font-size-base;\n@font-size-h6:            ceil((@font-size-base * 0.85)); // ~12px\n\n//** Unit-less `line-height` for use in components like buttons.\n@line-height-base:        1.428571429; // 20/14\n//** Computed \"line-height\" (`font-size` * `line-height`) for use with `margin`, `padding`, etc.\n@line-height-computed:    floor((@font-size-base * @line-height-base)); // ~20px\n\n//** By default, this inherits from the `<body>`.\n@headings-font-family:    inherit;\n@headings-font-weight:    500;\n@headings-line-height:    1.1;\n@headings-color:          inherit;\n\n\n\n//== Components\n//\n//## Define common padding and border radius sizes and more. Values based on 14px text and 1.428 line-height (~20px to start).\n\n@padding-base-vertical:     6px;\n@padding-base-horizontal:   12px;\n\n@padding-large-vertical:    10px;\n@padding-large-horizontal:  16px;\n\n@padding-small-vertical:    5px;\n@padding-small-horizontal:  10px;\n\n@padding-xs-vertical:       1px;\n@padding-xs-horizontal:     5px;\n\n@line-height-large:         1.3333333; // extra decimals for Win 8.1 Chrome\n@line-height-small:         1.5;\n\n@border-radius-base:        4px;\n@border-radius-large:       6px;\n@border-radius-small:       3px;\n\n\n// Medium screen / desktop\n@screen-md:                  992px;\n@screen-md-min:              @screen-md;\n// Large screen / wide desktop\n@screen-lg:                  1200px;\n@screen-lg-min:              @screen-lg;\n\n\n//\n//==混合变量\n//\n// WebKit-style focus\n.tab-focus() {\n  // WebKit-specific. Other browsers will keep their default outline style.\n  outline: 5px auto -webkit-focus-ring-color;\n  outline-offset: -2px;\n}\n\n// 隐藏文本\n.text-hide() {\n  font: 0/0 a;\n  color: transparent;\n  text-shadow: none;\n  background-color: transparent;\n  border: 0;\n}\n\n//\n//混合工具变量\n//\n\n//去除a和label的虚线\n.remove_dotted(){\n  a,label {blr:~'expression(this.onFocus=this.blur())'}\n  a,label {outline:none;}\n}\n.font(@size:14px){\n    font-size:@size;\n}\n.h100(){\n    height:100%;\n}\n.w100(){\n    width:100%;\n}\n//边框设置\n.border(@w:1px,@c:#eee){\n    border:@w solid @c;\n}\n//定位\n.pos(r){\n    position:relative;\n}\n.pos(a){\n    position:absolute;\n}\n.pos(f){\n    position:fixed;\n}\n//背景图片,.bg(\"..images/1.png\");\n.bg(@url){\n    background:url(@url) no-repeat;\n}\n//浮动,div{.fr;}\n.fl(){\n    float:left;\n}\n.fr(){\n    float:right;\n}\n\n.list-sn(){\n    list-style:none;\n}\n//垂直居中\n.pos-box-cc(@w,@h,@pos:absolute){\n    width:@w;\n    height:@h;\n    left:50%;\n    top:50%;\n    margin:-@w/2 0 0 -@h/2;\n    position: @pos;\n}\n// 居中对齐一个块级元素\n.center-block() {\n  display: block;\n  margin-left: auto;\n  margin-right: auto;\n}\n\n//文字居中\n.tc(){\n    text-align:center;\n}\n//文字垂直居中\n.tcc(@h){\n    text-align:center;\n    line-height:@h;\n}\n.l-h(@h){\n    height:@h;\n    line-height:@h;\n}\n\n// 文本溢出\n// 需要inline-block或块适当的样式\n.text-overflow() {\n  overflow: hidden;\n  text-overflow: ellipsis;\n  white-space: nowrap;\n}\n\n//display\n.d-b(){\n    display:block;\n}\n.d-i(){\n    display:inline;\n}\n.d-ib(){\n    display:inline-block;\n    *display:inline;\n    *zoom:1;\n}\n.d-t(){\n    display:table;\n}\n.d-n(){\n    display:none;\n}\n.t-n(@p:none){\n    text-decoration:@p;\n}\n.tc(){\n    text-align:center;\n}\n.tl(){\n    text-align:left;\n}\n.tr(){\n    text-align:right;\n}\n.va-m(){\n  vertical-align: middle;\n}\n\n//图标距离\n\n.m-icon-left(@l-distance){\n  margin-left:@l-distance;\n}\n.m-icon-right(@r-distance){\n  margin-right:@r-distance;\n}\n\n\n//圆角\n.radius(@r){\n    -webkit-border-radius:@r;\n       -moz-border-radius:@r;\n            border-radius:@r;\n}\n\n//消息圆角\n\n.message(@me-t,@me-r){\n  background-color:#F44336;\n  width: 8px;\n  height: 8px;\n  .pos(a);\n  top:@me-t;\n  right:@me-r;\n}\n\n//三角形\n.triangle(top,@w:5px,@c:#556066){\n    border-width:@w;\n    border-color:transparent transparent @c transparent;\n    border-style:dashed dashed solid dashed;\n}\n\n.triangle(bottom,@w:5px,@c:#556066){\n    border-width:@w;\n    border-color:@c transparent transparent transparent;\n    border-style:solid dashed dashed dashed;\n\n}\n.triangle(left,@w:5px,@c:#556066){\n    border-width:@w;\n    border-color:transparent @c transparent transparent;\n    border-style:dashed dashed dashed solid;\n}\n\n.triangle(right,@w:5px,@c:#556066){\n    border-width:@w;\n    border-color:transparent transparent transparent @c;\n    border-style:dashed solid dashed dashed;\n\n}\n.triangle(@_){\n    width:0;\n    height:0;\n    overflow:hidden;\n    font-size:0;\n}\n.clearfix(){\n  *zoom: 1;\n  &:before,\n  &:after{\n    display:table;\n    content: \"\";\n  }\n  &:after {\n    clear: both;\n  }\n}\n\n.box-sizing(@box){\n  -webkit-box-sizing:@box;\n     -moz-box-sizing:@box;\n      -ms-box-sizing:@box;\n       -o-box-sizing:@box;\n              sizing:@box;\n}\n.box-shadow(@shadow){\n  -webkit-box-shadow:@shadow;\n     -moz-box-shadow:@shadow;\n      -ms-box-shadow:@shadow;\n       -o-box-shadow:@shadow;\n              shadow:@shadow;\n}\n\n//过度\n.transition(@trans){\n    -webkit-transition:@trans;\n       -moz-transition:@trans;\n        -ms-transition:@trans;\n         -o-transition:@trans;\n            transition:@trans;\n}\n.transform-origin(@origin){\n    -webkit-transition-origin:@origin;\n       -moz-transition-origin:@origin;\n        -ms-transition-origin:@origin;\n         -o-transition-origin:@origin;\n            transition-origin:@origin;\n}\n.transform(@transform){\n    -webkit-transform:@transform;\n       -moz-transform:@transform;\n        -ms-transform:@transform;\n         -o-transform:@transform;\n            transform:@transform;\n}\n.create3d(@h){\n  -webkit-perspective:@h;\n          perspective:@h;\n}\n.use3d(){\n    -webkit-transform-style:preserve-3d;\n            transform-style:preserve-3d;\n}\n\n//动画\n.animation(@as){\n    -webkit-animation:@as;\n       -moz-animation:@as;\n         -o-animation:@as;\n            animation:@as;\n}\n.trans3d(){\n    -webkit-transform-style:preserve-3d;\n       -moz-transform-style:preserve-3d;\n            transform-style:preserve-3d;\n}\n.trans-origin(@to){\n  -webkit-transform-origin:@to;\n     -moz-transform-origin:@to;\n          transform-origin:@to;\n}\n\n\n"
  },
  {
    "path": "asset/css/vendor_antd.less",
    "content": ".newAnt {\n  .ant-layout.ant-layout-has-sider {\n    height: 100%;\n  }\n}\n"
  },
  {
    "path": "build/env.js",
    "content": "\nconst ENVS = {\n  PUB: ['pub', 'production'],\n  QA: 'qa',\n  PROFILE: 'profile',\n  DEV: ['dev', 'development'],\n};\n\nconst ENVS_ARR = [];\n\nObject.keys(ENVS).forEach(key => {\n  const value = ENVS[key];\n  if (Array.isArray(value)) {\n    value.forEach(i => ENVS_ARR.push(i));\n  } else {\n    ENVS_ARR.push(value);\n  }\n});\n\nlet currentEnv = process.env.NODE_ENV;\n\n/**\n * 判断当前环境参数是否符合规范\n * @param {string} env\n */\nfunction checkEnv(env) {\n  if (!env) {\n    throw new Error('NODE_ENV is not defined');\n    return false;\n  }\n\n  if (ENVS_ARR.indexOf(env) === -1) {\n    throw new Error(`NODE_ENV must be one of ${JSON.stringify(ENVS_ARR)}，currentEnv is：${env}`);\n  }\n\n  return true;\n}\n\n// 线上环境\nconst isProduction = () => {\n  return checkEnv(currentEnv) && ENVS.PUB.includes(currentEnv);\n};\n\n// 非线上环境\nconst isNotProduction = () => !isProduction();\n\n// 测试环境\nconst isQA = () => {\n  return checkEnv(currentEnv) && currentEnv === ENVS.QA;\n};\n\n// 联调环境\nconst isProfile = () => {\n  return checkEnv(currentEnv) && currentEnv === ENVS.PROFILE;\n};\n\n// 开发环境\n// const isDev = () => !(isProfile() || isProduction() || isQA());\nconst isDev = () => {\n  return checkEnv(currentEnv) && ENVS.DEV.includes(currentEnv);\n};\n\n/**\n * 获取所有环境变量\n * @returns ENVS_ARR\n */\nfunction getEnvs() {\n  return ENVS_ARR;\n}\n\n/**\n * 设置环境变量\n * @param {string} env\n */\nfunction setEnv(env) {\n  if (ENVS_ARR.indexOf(env) === -1) {\n    throw new Error(`NODE_ENV provided must be one of ${JSON.stringify(ENVS_ARR)}，your param is：${env}`);\n  }\n  currentEnv = env;\n  console.log(`NODE_ENV is set successfully，currentEnv is ${env}`);\n}\n\n/**\n * 设置线上环境\n */\nconst setProduction = () => setEnv(ENVS.PUB[0]);\n\n/**\n * 设置测试环境\n */\nconst setQA = () => setEnv(ENVS.QA);\n\n/**\n * 设置联调环境\n */\nconst setProfile = () => setEnv(ENVS.PROFILE);\n\n/**\n * 设置开发环境\n */\nconst setDev = () => setEnv(ENVS.DEV[0]);\n\n/**\n * 开发和线上环境配置，默认为开发环境配置，适用于前后端未分离项目（用hash做缓存）\n * @param  {*} devConfig 开发环境配置\n * @param  {*} pubConfig 线上环境配置\n * @returns (isQA() || isProduction()) ? pubConfig : devConfig\n */\nfunction env(devConfig, pubConfig) {\n  if (!pubConfig && pubConfig !== '') {\n    pubConfig = devConfig;\n  }\n\n  if (isQA() || isProduction()) {\n    return pubConfig;\n  }\n\n  return devConfig;\n}\n\nmodule.exports = {\n  getEnvs,\n  setEnv,\n  setProduction,\n  setQA,\n  setProfile,\n  setDev,\n  env,\n  isProduction,\n  isNotProduction,\n  isQA,\n  isProfile,\n  isDev,\n  PUB: ENVS.PUB[0],\n  QA: ENVS.QA,\n  PROFILE: ENVS.PROFILE,\n  DEV: ENVS.DEV[0],\n};\n"
  },
  {
    "path": "build/postcss.config.js",
    "content": "module.exports = {\n  plugins: [\n    require('autoprefixer'),\n  ],\n};\n"
  },
  {
    "path": "build/publicPath.js",
    "content": "let pkg = require('../package.json');\nconst Env = require('./env');\n\n// 不同环境下的静态资源的url根路径\nlet PRODUCT_STATIC_URL = 'http://static.hello.com';\nlet QA_STATIC_URL = 'http://qa.static.hello.com';\nlet PROFILE_STATIC_URL = 'http://dev.static.hello.com'; // 联调环境静态资源存放路径\nlet DEV_STATIC_URL = '/dist';\n\n// 根据环境来获取不同的静态资源部署的根路径\nlet publicPath = (function getPublicPath() {\n  let staticUrl = DEV_STATIC_URL;\n  if (Env.isDev()) {\n    return staticUrl;\n  }\n\n  if (Env.isProduction()) {\n    staticUrl = PRODUCT_STATIC_URL;\n  }\n\n  if (Env.isQA()) {\n    staticUrl = QA_STATIC_URL;\n  }\n\n  if (Env.isProfile()) {\n    staticUrl = PROFILE_STATIC_URL;\n  }\n\n  return `${staticUrl}/${pkg.name}/${pkg.version}`;\n})();\n\nmodule.exports = publicPath;\n"
  },
  {
    "path": "build/theme.js",
    "content": "module.exports = {\n  'font-family': 'Microsoft Yahei, Arial , Tahoma , Helvetica, sans-serif',\n};\n"
  },
  {
    "path": "build/webpack.base.conf.js",
    "content": "const path = require('path');\nconst webpack = require('webpack');\n\nconst MiniCssExtractPlugin = require('mini-css-extract-plugin');\nconst CleanWebpackPlugin = require('clean-webpack-plugin');\n\nconst resolve = path.resolve;\nconst publicPath = require('./publicPath');\nconst theme = require('./theme.js');\nconst postcssConfigPath = resolve(__dirname, './postcss.config.js');\n\nmodule.exports = {\n  entry: {\n    app: './src/app.jsx',\n  },\n  output: {\n    path: resolve(__dirname, '../dist'),\n    filename: '[name].js',\n    publicPath: publicPath + '/',\n    chunkFilename: '[name].js',\n    crossOriginLoading: 'anonymous',\n  },\n  resolve: {\n    extensions: ['.js', '.jsx', '.less', '.scss', '.css'],\n    alias: {\n      common: resolve('src/common'),\n      components: resolve('src/components'),\n      constants: resolve('src/constants'),\n      modules: resolve('src/modules'),\n      reduxDir: resolve('src/redux'),\n    },\n  },\n  module: {\n    rules: [\n      {\n        test: /\\.(js|jsx)$/i,\n        exclude: /node_modules/,\n        use: {\n          loader: 'babel-loader',\n        },\n      },\n      {\n        test: /\\.css/i,\n        include: [\n          resolve('src'),\n        ],\n        use: [\n          MiniCssExtractPlugin.loader,\n          {\n            loader: 'css-loader?importLoader=1&modules&localIdentName=[path]___[name]__[local]___[hash:base64:5]',\n          },\n          {\n            loader: 'postcss-loader',\n            options: {\n              sourceMap: true,\n              config: {\n                path: postcssConfigPath,\n              },\n            },\n          },\n        ],\n      },\n      {\n        test: /\\.less$/,\n        include: [resolve('src')],\n        use: [\n          MiniCssExtractPlugin.loader,\n          {\n            loader:\n              'css-loader?importLoader=1&modules&localIdentName=[path]___[name]__[local]___[hash:base64:5]',\n            options: {\n              modules: true,\n            },\n          },\n          {\n            loader: 'postcss-loader',\n            options: {\n              sourceMap: true,\n              config: {\n                path: postcssConfigPath,\n              },\n            },\n          },\n          {\n            loader: 'less-loader',\n            options: {\n              javascriptEnabled: true,\n              modifyVars: theme,\n            },\n          },\n        ],\n      },\n      {\n        test: /\\.less/i,\n        include: [\n          resolve('asset/css'),\n          resolve('node_modules/antd/'),\n        ],\n        use: [\n          MiniCssExtractPlugin.loader,\n          {\n            loader: 'css-loader?importLoader=1&modules&localIdentName=[path]___[name]__[local]___[hash:base64:5]',\n            options: {\n              minimize: true, // css压缩\n            },\n          },\n          {\n            loader: 'postcss-loader',\n            options: {\n              sourceMap: true,\n              config: {\n                path: postcssConfigPath,\n              },\n            },\n          },\n          {\n            loader: 'less-loader',\n            options: {\n              javascriptEnabled: true,\n              modifyVars: theme,\n            },\n          },\n        ],\n      },\n      {\n        test: /\\.(png|jpg|gif|svg|eot|ttf|woff|woff2)$/,\n        use: ['file-loader?limit=1000&name=files/[md5:hash:base64:10].[ext]'],\n      },\n      {\n        test: /\\.(html|htm)$/,\n        use: 'html-withimg-loader',\n      },\n    ],\n  },\n  plugins: [\n    new webpack.ContextReplacementPlugin(/moment[\\/\\\\]locale$/, /^\\.\\/zh\\-cn$/),\n    // extract css\n    new MiniCssExtractPlugin({\n      filename: '[name].css',\n      chunkFilename: '[name].css',\n    }),\n    // clean output bundle directory\n    new CleanWebpackPlugin(),\n  ],\n};\n"
  },
  {
    "path": "build/webpack.dev.conf.js",
    "content": "let mockport = 8000;\n\nprocess.argv.forEach(function setMockPort(val, index) {\n  if (val === '--env.mockport') {\n    mockport = process.argv[index + 1];\n    return false;\n  }\n});\n\nmodule.exports = {\n  mode: 'development',\n  devtool: 'source-map',\n  devServer: {\n    // 服务器外部可访问要声明host\n    host: '0.0.0.0',\n    hot: true,\n    inline: true,\n    open: true,\n    proxy: {\n      '**/*.action': {\n        target: 'http://localhost:' + mockport,\n        /* bypass: function bypass(req, res, proxyOptions) {\n          // handle default jsp action\n          if (req.headers.accept.indexOf('html') != -1) {\n            return 'index.html';\n          }\n        }, */\n      },\n    },\n  },\n};\n"
  },
  {
    "path": "build/webpack.prod.conf.js",
    "content": "const path = require('path');\nconst HtmlWebpackPlugin = require('html-webpack-plugin');\n// const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;\n\nmodule.exports = {\n  mode: 'production',\n  plugins: [\n    // analyze bundle size\n    // new BundleAnalyzerPlugin(),\n    new HtmlWebpackPlugin({\n      template: path.resolve(process.cwd(), './template.html'),\n      filename: path.resolve(process.cwd(), './dist', 'index.html'), // 写入的文件\n    }),\n  ],\n  optimization: {\n    splitChunks: {\n      name: true,\n      cacheGroups: {\n        default: {\n          minChunks: 2,\n          priority: -20,\n          reuseExistingChunk: true,\n        },\n        libs: {\n          test: /[\\\\/]node_modules[\\\\/]/,\n          priority: -10,\n        },\n        styles: {\n          test: /(\\.less|\\.css)$/,\n          priority: -10,\n        },\n      },\n    },\n  },\n};\n"
  },
  {
    "path": "index.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en\">\n\n<head>\n  <meta charset=\"utf-8\">\n  <link rel=\"shortcut icon\" href=\"favicon.ico\" />\n  <title>PC端React+redux脚手架</title>\n  <link rel=\"stylesheet\" type=\"text/css\" href=\"/dist/app.css\">\n</head>\n\n<body >\n  <div class=\"asset newAnt\" id=\"app\">\n  </div>\n  <script type=\"text/javascript\" src=\"/dist/app.js\"></script>\n</body>\n\n</html>"
  },
  {
    "path": "mock/config/mockConfig.json",
    "content": "{\n    \"dataSource\": [\"json\", \"template\", \"server\"],\n    \"json\": {\n        \"path\": \"/mock/data/\",\n        \"wrap\": true\n    },\n    \"server\": [{\n        \"host\": \"http://localhost:8080/mock\",\n        \"serverParams\": {\n            \"index\": 1\n        },\n        \"statusCode\": [200],\n        \"rejectUnauthorized\": false,\n        \"secureProtocol\": \"SSLv3_method\",\n        \"cookie\": \"\",\n        \"proxy\": \"\"\n    }, {\n        \"host\": \"http://localhost:8081/\",\n        \"serverParams\": {\n            \"index\": 2\n        },\n        \"statusCode\": [200],\n        \"rejectUnauthorized\": false,\n        \"secureProtocol\": \"SSLv3_method\",\n        \"cookie\": \"\",\n        \"proxy\": \"\"\n    }],\n    \"template\": {\n        \"path\": \"/mock/template/\"\n    }\n\n}"
  },
  {
    "path": "mock/data/skuunit/deleteSkuUnit.json",
    "content": "{\n  \"enabled\": true,\n  \"value\": \"success\",\n  \"success\": {\n    \"flag\": \"0\",\n    \"msg\": [\n      \"\"\n    ],\n    \"data\": []\n  },\n  \"error\": {\n    \"flag\": \"1\",\n    \"msg\": [\n      \"input error\"\n    ],\n    \"data\": []\n  }\n}"
  },
  {
    "path": "mock/data/skuunit/showSkuUnitList.json",
    "content": "{\n  \"enabled\": true,\n  \"value\": \"success\",\n  \"success\": {\n    \"flag\": \"0\",\n    \"msg\": [\"系统繁忙\"],\n    \"data\": {\n      \"dataList\": [\n        {\n          \"skuUnitId\": \"185715847_1\",\n          \"skuUnitName\": \"1单元名称\",\n          \"isPause\": 1,\n          \"skuSetId\": \"23489551\",\n          \"skuSetName\": \"上海鲜花\",\n          \"groupCount\": 5,\n          \"planCount\": 2,\n          \"denyKeyCount\": 9,\n          \"checkStatus\": 1,\n          \"checkOkCount\": 3000,\n          \"checkRejectCount\": 200,\n          \"ideas\": [\n            {\n              \"deveiceType\": 2,\n              \"ideaType\": 2\n            }\n          ]\n        },\n        {\n          \"skuUnitId\": \"185715847_2\",\n          \"skuUnitName\": \"2单元名称2\",\n          \"isPause\": 0,\n          \"skuSetId\": \"23489556\",\n          \"skuSetName\": \"沈阳鲜花\",\n          \"groupCount\": 5,\n          \"planCount\": 2,\n          \"denyKeyCount\": 7,\n          \"checkStatus\": 1,\n          \"checkOkCount\": 3000,\n          \"checkRejectCount\": 200,\n          \"ideas\": [\n            {\n              \"deveiceType\": 1,\n              \"ideaType\": 2\n            },\n            {\n              \"deveiceType\": 2,\n              \"ideaType\": 1\n            }\n          ]\n        },\n        {\n          \"skuUnitId\": \"185715847_3\",\n          \"skuUnitName\": \"3单元名称\",\n          \"isPause\": 0,\n          \"skuSetId\": \"23489555\",\n          \"skuSetName\": \"兰州鲜花\",\n          \"groupCount\": 5,\n          \"planCount\": 2,\n          \"denyKeyCount\": 12,\n          \"checkStatus\": 0,\n          \"checkOkCount\": 3000,\n          \"checkRejectCount\": 200,\n          \"ideas\": [\n            {\n              \"deveiceType\": 2,\n              \"ideaType\": 1\n            }\n          ]\n        },\n        {\n          \"skuUnitId\": \"185715847_4\",\n          \"skuUnitName\": \"4单元名称\",\n          \"isPause\": 1,\n          \"skuSetId\": \"23489552\",\n          \"skuSetName\": \"北京二手车\",\n          \"groupCount\": 5,\n          \"planCount\": 2,\n          \"denyKeyCount\": 4,\n          \"checkStatus\": 1,\n          \"checkOkCount\": 3000,\n          \"checkRejectCount\": 200,\n          \"ideas\": [\n            {\n              \"deveiceType\": 2,\n              \"ideaType\": 2\n            }\n          ]\n        },\n        {\n          \"skuUnitId\": \"185715847_5\",\n          \"skuUnitName\": \"5单元名称\",\n          \"isPause\": 0,\n          \"skuSetId\": \"23489553\",\n          \"skuSetName\": \"香港鲜花\",\n          \"groupCount\": 5,\n          \"planCount\": 2,\n          \"denyKeyCount\": 7,\n          \"checkStatus\": -1,\n          \"checkOkCount\": 3000,\n          \"checkRejectCount\": 200,\n          \"ideas\": [\n            {\n              \"deveiceType\": 1,\n              \"ideaType\": 1\n            }\n          ]\n        }\n      ],\n      \"totalNumber\": 50\n    }\n  },\n  \"error\": {\n    \"flag\": \"1\",\n    \"msg\": [\n      \"系统繁忙\"\n    ],\n    \"data\": []\n  }\n}"
  },
  {
    "path": "mock/data/skuunit/updateSkuUnitPause.json",
    "content": "{\n  \"enabled\": true,\n  \"value\": \"success\",\n  \"success\": {\n    \"flag\": \"0\",\n    \"msg\": [\n      \"修改失败\"\n    ],\n    \"data\": []\n  },\n  \"error\": {\n    \"flag\": \"1\",\n    \"msg\": [\n      \"input error\"\n    ],\n    \"data\": []\n  }\n}"
  },
  {
    "path": "mock/template/query/table.template",
    "content": "{\n    'table|1-10': [\n        {\n            'id|100-1000': 100,\n            'name|1': ['A','B','C','D','E','F','G','H','I','J','K','L','M','N'],\n            'height|50-300': 50,\n            'weight|50-100.1-10': 50,\n            'age|1-100': 1,\n            'email': '@EMAIL'\n        }\n    ]  \n}"
  },
  {
    "path": "package.json",
    "content": "{\n  \"name\": \"react-starter\",\n  \"description\": \"react+redux的脚手架，持续进行更新迭代\",\n  \"version\": \"1.0.1\",\n  \"main\": \"app.js\",\n  \"scripts\": {\n    \"start\": \"cross-env NODE_ENV=development webpack-dev-server --progress --colors\",\n    \"profile\": \"cross-env NODE_ENV=profile webpack --progress --colors --display-modules\",\n    \"qa\": \"cross-env NODE_ENV=qa webpack --progress --colors\",\n    \"pub\": \"cross-env NODE_ENV=production webpack --progress --colors \",\n    \"lint\": \"eslint src\"\n  },\n  \"dependencies\": {\n    \"antd\": \"^3.26.2\",\n    \"axios\": \"^0.18.1\",\n    \"babel-polyfill\": \"^6.26.0\",\n    \"lodash\": \"^4.17.15\",\n    \"moment\": \"^2.24.0\",\n    \"prop-types\": \"^15.7.2\",\n    \"react\": \"^16.12.0\",\n    \"react-dom\": \"^16.12.0\",\n    \"react-redux\": \"^5.1.2\",\n    \"react-router-dom\": \"^5.1.2\",\n    \"redux\": \"^4.0.4\",\n    \"redux-thunk\": \"^2.2.0\"\n  },\n  \"devDependencies\": {\n    \"@babel/cli\": \"^7.7.5\",\n    \"@babel/core\": \"^7.7.5\",\n    \"@babel/plugin-proposal-class-properties\": \"^7.7.4\",\n    \"@babel/plugin-proposal-decorators\": \"^7.7.4\",\n    \"@babel/plugin-proposal-export-default-from\": \"^7.7.4\",\n    \"@babel/plugin-syntax-dynamic-import\": \"^7.7.4\",\n    \"@babel/plugin-transform-runtime\": \"^7.7.6\",\n    \"@babel/preset-env\": \"^7.7.6\",\n    \"@babel/preset-react\": \"^7.7.4\",\n    \"@hot-loader/react-dom\": \"^16.11.0\",\n    \"autoprefixer\": \"^8.6.5\",\n    \"babel-eslint\": \"^10.0.3\",\n    \"babel-loader\": \"^8.0.6\",\n    \"babel-plugin-import\": \"^1.13.0\",\n    \"babel-plugin-transform-imports\": \"^1.5.1\",\n    \"babel-plugin-transform-react-remove-prop-types\": \"^0.4.24\",\n    \"clean-webpack-plugin\": \"^2.0.2\",\n    \"css-loader\": \"^0.28.9\",\n    \"eslint\": \"^5.16.0\",\n    \"eslint-plugin-react\": \"^7.17.0\",\n    \"eslint-plugin-react-hooks\": \"^1.7.0\",\n    \"file-loader\": \"^1.1.11\",\n    \"html-webpack-plugin\": \"^3.2.0\",\n    \"html-withimg-loader\": \"^0.1.16\",\n    \"less\": \"^3.10.3\",\n    \"less-loader\": \"^4.1.0\",\n    \"mini-css-extract-plugin\": \"^0.4.5\",\n    \"postcss-loader\": \"^2.1.6\",\n    \"react-hot-loader\": \"^4.12.18\",\n    \"redux-logger\": \"^3.0.6\",\n    \"url-loader\": \"^1.1.2\",\n    \"cross-env\": \"^6.0.3\",\n    \"webpack\": \"^4.41.2\",\n    \"webpack-bundle-analyzer\": \"^3.6.0\",\n    \"webpack-cli\": \"^3.3.10\",\n    \"webpack-dev-server\": \"^3.9.0\",\n    \"webpack-merge\": \"^4.2.2\"\n  },\n  \"repository\": {\n    \"type\": \"git\",\n    \"url\": \"https://github.com/zhangkun-Jser/react-starter.git\"\n  },\n  \"keywords\": [],\n  \"author\": \"zk\",\n  \"license\": \"ISC\",\n  \"bugs\": {\n    \"url\": \"https://github.com/zhangkun-Jser/react-starter/issues\"\n  },\n  \"homepage\": \"https://github.com/zhangkun-Jser/react-starter\",\n  \"eslintIgnore\": [\n    \"dist/**\"\n  ]\n}\n"
  },
  {
    "path": "src/app.jsx",
    "content": "/*\n * 项目入口\n */\nimport 'babel-polyfill';\nimport React from 'react';\nimport ReactDOM from 'react-dom';\nimport { Provider } from 'react-redux';\nimport { HashRouter } from 'react-router-dom';\n\nimport '../asset/css/app.less';\nimport store from './redux/configureStore';\nimport Root from './routers';\n\nReactDOM.render(\n  <Provider store={store}>\n    <HashRouter>\n      <Root />\n    </HashRouter>\n  </Provider>,\n  document.getElementById('app'),\n);\n"
  },
  {
    "path": "src/common/request.js",
    "content": "// 参考：https://ykloveyxk.github.io/2017/02/25/axios%E5%85%A8%E6%94%BB%E7%95%A5/\n\nimport OriginAxios from 'axios';\nimport { message } from 'antd';\n\nconst axios = OriginAxios.create({\n  timeout: 20000,\n});\n\nexport function get(url, data) {\n  return axios.get(url, {\n    params: data,\n  });\n}\n\n// By default, axios serializes JavaScript objects to JSON\nexport function post(url, data) {\n  return axios({\n    url,\n    method: 'post',\n    data,\n  });\n}\n\n// By default, axios serializes JavaScript objects to JSON\nexport function put(url, data) {\n  return axios({\n    url,\n    method: 'put',\n    data,\n  });\n}\n\nexport function del(url, data) {\n  return axios({\n    url,\n    method: 'delete',\n    data,\n  });\n}\n\n// Add a request interceptor\naxios.interceptors.request.use(\n  function config(config) {\n    // Do something before request is sent\n    return config;\n  },\n  function error(error) {\n    // Do something with request error\n    console.log('request error, HTTP CODE: ', error.response.status);\n    return Promise.reject(error);\n  },\n);\n\n// 返回状态判断(添加响应拦截器)\naxios.interceptors.response.use(\n  res => {\n    if (res.data && res.data.flag === 1) {\n      let errorMsg = res.data.msg;\n      message.error(errorMsg);\n      return Promise.reject(errorMsg);\n    }\n\n    return res;\n  },\n  error => {\n    // 用户登录的时候会拿到一个基础信息,比如用户名,token,过期时间戳\n    // 直接丢localStorage或者sessionStorage\n    if (error.response.status === 401) {\n      // 若是接口访问的时候没有发现有鉴权的基础信息,直接返回登录页\n      // history.push('login');\n      window.location = '/login.html';\n    }\n  },\n);\n\nexport default axios;\n"
  },
  {
    "path": "src/components/ErrorBoundary.jsx",
    "content": "// 错误边界\nimport React, { Component } from 'react';\nimport PropTypes from 'prop-types';\n\nclass ErrorBoundary extends Component {\n  static propTypes = {\n    children: PropTypes.oneOfType([PropTypes.any]),\n  };\n\n  constructor(props) {\n    super(props);\n    this.state = { hasError: false };\n  }\n\n  static getDerivedStateFromError() {\n    // Update state so the next render will show the fallback UI.\n    return { hasError: true };\n  }\n\n  componentDidCatch() {\n    // You can also log the error to an error reporting service\n    // logErrorToMyService(error, info);\n    this.setState({\n      hasError: true,\n    });\n  }\n\n  render() {\n    if (this.state.hasError) {\n      // You can render any custom fallback UI\n      return <h1 className=\"mt30 ml15\">Something went wrong.</h1>;\n    }\n\n    return this.props.children;\n  }\n}\n\nexport default ErrorBoundary;\n"
  },
  {
    "path": "src/components/Layout/Footer.jsx",
    "content": "import React from 'react';\nimport styles from './index.css';\n\nexport default () => (\n  <div className={styles['footer']}>Copyright© 2019 Sogou Biztech. All Rights Reserved.</div>\n);\n"
  },
  {
    "path": "src/components/Layout/Header.jsx",
    "content": "import React from 'react';\nimport PropTypes from 'prop-types';\nimport {\n  Layout, Icon, Menu, Dropdown, Avatar,\n} from 'antd';\n\nimport styles from './index.css';\nimport AVATAR from '../../../asset/img/avatar.png';\n\nconst { Header } = Layout;\n\nconst HeaderComp = ({\n  collapsed = false,\n  onToggle,\n}) => {\n  const menu = (\n    <Menu className={styles['header-menu']}>\n      <Menu.Item key=\"user\"><Icon type=\"user\" />个人中心</Menu.Item>\n      <Menu.Item disabled><Icon type=\"setting\" />设置</Menu.Item>\n      <Menu.Divider />\n      <Menu.Item key=\"logout\"><Icon type=\"logout\" /> 退出登录</Menu.Item>\n    </Menu>\n  );\n\n  const handleToggle = () => {\n    onToggle && onToggle();\n  };\n\n  return (\n    <Header className={styles.header}>\n      <Icon\n        className={styles.trigger}\n        type={collapsed ? 'menu-unfold' : 'menu-fold'}\n        onClick={handleToggle}\n      />\n      <div className={styles['header-right']}>\n        <Dropdown overlay={menu}>\n          <span className={styles['account']}>\n            <Avatar size=\"small\" src={AVATAR} />\n            <span className={styles['user-name']}>dev</span>\n          </span>\n        </Dropdown>\n      </div>\n    </Header>\n  );\n};\n\n\nHeaderComp.propTypes = {\n  collapsed: PropTypes.bool,\n  onToggle: PropTypes.func,\n};\n\nexport default HeaderComp;\n"
  },
  {
    "path": "src/components/Layout/Sider.jsx",
    "content": "import React, { useState } from 'react';\nimport PropTypes from 'prop-types';\nimport { Link } from 'react-router-dom';\nimport { Menu, Layout, Icon } from 'antd';\n\nimport styles from './index.css';\nimport LOGO from '../../../asset/img/logo.svg';\n\nconst { Sider } = Layout;\n\nconst SiderComp = ({\n  history,\n  pathname,\n  collapsed = false,\n}) => {\n  const pathArr = pathname.split('/').filter(i => i);\n  const homeKey = 'home';\n\n  const getDefaultSelectedKeys = () => {\n    const selectKey = pathArr.length ? pathArr[pathArr.length - 1] : '';\n    return selectKey ? [selectKey] : [homeKey];\n  };\n\n  const [selectedKeys, setSelectedKeys] = useState(getDefaultSelectedKeys());\n\n  const handleSelect = ({ selectedKeys }) => {\n    setSelectedKeys(selectedKeys);\n    history.push(`/${selectedKeys}`);\n  };\n\n  window.onhashchange = () => {\n    setSelectedKeys(getDefaultSelectedKeys());\n  };\n\n  return (\n    <Sider\n      trigger={null}\n      collapsible\n      collapsed={collapsed}\n    >\n      <div className={styles['menu-logo']}>\n        <Link to=\"/\">\n          <img src={LOGO} />\n          {!collapsed && <h1>脚手架</h1>}\n        </Link>\n      </div>\n      <Menu\n        theme=\"dark\"\n        mode=\"inline\"\n        selectedKeys={selectedKeys}\n        onSelect={handleSelect}\n      >\n        <Menu.Item key=\"home\">\n          <Icon type=\"smile\" />\n          <span>欢迎</span>\n        </Menu.Item>\n        <Menu.Item key=\"nav2\">\n          <Icon type=\"video-camera\" />\n          <span>nav 2</span>\n        </Menu.Item>\n      </Menu>\n    </Sider>\n  );\n};\n\nSiderComp.propTypes = {\n  history: PropTypes.objectOf(PropTypes.any),\n  pathname: PropTypes.string,\n  collapsed: PropTypes.bool,\n};\n\nexport default SiderComp;\n"
  },
  {
    "path": "src/components/Layout/index.css",
    "content": "/* Header */\n.header {\n  background: #fff;\n  padding: 0 20px;\n  position: relative;\n}\n\n.header-right {\n  position: absolute;\n  top: 0;\n  right: 24px;\n  bottom: 0;\n}\n\n.header-menu {\n  width: 150px;\n}\n\n.user-name {\n  margin-left: 10px;\n}\n\n.account {\n  color: rgba(0, 0, 0, 0.65);\n  cursor: pointer;\n  padding: 0 12px;\n  display: inline-block;\n  transition: all .3s;\n  height: 100%;\n}\n\n.account i {\n  font-size: 16px;\n  vertical-align: middle;\n}\n\n.account:hover {\n  background: #e6f7ff;\n}\n\n/* Sider */\n.trigger {\n  font-size: 18px;\n  line-height: 64px;\n  padding: 0 10px;\n  cursor: pointer;\n  transition: color .3s;\n}\n\n.trigger:hover {\n  color: #1890ff;\n}\n\n.menu-logo {\n  position: relative;\n  height: 64px;\n  padding-left: 24px;\n  overflow: hidden;\n  line-height: 64px;\n  background: #001529;\n  transition: all 0.3s;\n}\n\n.menu-logo a {\n  color: #1890FF;\n  text-decoration: none;\n  background-color: transparent;\n  outline: none;\n  cursor: pointer;\n  transition: color 0.3s;\n}\n\n.menu-logo img {\n  display: inline-block;\n  width: 32px;\n  vertical-align: middle;\n}\n\n.menu-logo h1 {\n  display: inline-block;\n  margin: 0 0 0 12px;\n  color: white;\n  font-weight: 600;\n  font-size: 20px;\n  vertical-align: middle;\n}\n\n\n/* Footer */\n.footer {\n  text-align: center;\n  font-size: 14px;\n  padding: 20px;\n}"
  },
  {
    "path": "src/components/Pages/index.css",
    "content": ".page-wrapper {\n    margin: 16px 0px 16px 16px;\n    line-height: 32px;\n    overflow: hidden;\n}"
  },
  {
    "path": "src/components/Pages/index.jsx",
    "content": "/**\n * 表格分页组件\n */\nimport React, { Component } from 'react';\nimport { Pagination } from 'antd';\nimport PropTypes from 'prop-types';\nimport styles from './index.css';\n\nclass Pages extends Component {\n  static propTypes = {\n    pageSizeOptions: PropTypes.arrayOf(PropTypes.string),\n    defaultCurrent: PropTypes.number,\n    total: PropTypes.number,\n    current: PropTypes.number,\n    pageSize: PropTypes.number,\n    loadTable: PropTypes.func,\n    style: PropTypes.objectOf(PropTypes.any),\n  }\n\n  static defaultProps = {\n    pageSizeOptions: ['10', '20', '30', '40'],\n    defaultCurrent: 1,\n    total: 0,\n    current: 1,\n    pageSize: 10,\n    style: {\n      float: 'right',\n    },\n  };\n\n  state = {\n    current: this.props.current,\n    pageSize: this.props.pageSize,\n  };\n\n  static getDerivedStateFromProps(props) {\n    return {\n      current: props.current,\n      pageSize: props.pageSize,\n      total: props.total,\n    };\n  }\n\n  handlePageChange = (page, pageSize) => {\n    this.loadTable({\n      page,\n      pageSize,\n    });\n  }\n\n  handlePageSizeChange = (current, pageSize) => {\n    this.loadTable({\n      page: this.props.defaultCurrent,\n      pageSize,\n    });\n  }\n\n  loadTable({ page, pageSize }) {\n    this.setState({\n      current: page,\n      pageSize,\n    });\n    this.props.loadTable(false, {\n      page,\n      pageSize,\n    });\n  }\n\n  reset() {\n    const { current, pageSize } = this.defaultProps;\n    this.setState({ current, pageSize });\n  }\n\n  render() {\n    const { total, pageSizeOptions, style } = this.props;\n    const { current, pageSize } = this.state;\n\n    return (\n      <div className={styles['page-wrapper']}>\n        <span>共{total}条</span>\n        <Pagination\n          showSizeChanger\n          pageSizeOptions={pageSizeOptions}\n          total={total}\n          current={current}\n          pageSize={pageSize}\n          onChange={this.handlePageChange}\n          onShowSizeChange={this.handlePageSizeChange}\n          style={style}\n        />\n      </div>\n    );\n  }\n}\n\nexport default Pages;\n"
  },
  {
    "path": "src/components/index.jsx",
    "content": "import Header from './Layout/Header';\nimport Sider from './Layout/Sider';\nimport Footer from './Layout/Footer';\nimport Pages from './Pages';\nimport ErrorBoundary from './ErrorBoundary';\n\nexport {\n  Header,\n  Sider,\n  Footer,\n  Pages,\n  ErrorBoundary,\n};\n"
  },
  {
    "path": "src/constants/constant.js",
    "content": "export const ACTION_TYPE_ADD_ERROR = 'app/ADD_ERROR';\n\n// 分页默认信息\nexport const PAGE_DATA = {\n  pageNo: 1,\n  pageSize: 10,\n};\n"
  },
  {
    "path": "src/constants/url.js",
    "content": "// 单元列表查询展示\nexport const SHOW_UNIT_LIST_URL = '/skuunit/showSkuUnitList.action';\n// 删除单元\nexport const DELETE_UNIT_URL = '/skuunit/deleteSkuUnit.action';\n"
  },
  {
    "path": "src/redux/configureStore.js",
    "content": "import { compose, createStore, applyMiddleware } from 'redux';\nimport logger from 'redux-logger';\nimport thunk from 'redux-thunk';\n\nimport reducer from './reducers';\n\nconst middleware = [thunk];\n\nconst isNotProduction = process.env.NODE_ENV !== 'production';\nif (isNotProduction) {\n  middleware.push(logger);\n}\n\n// 判断当前浏览器是否安装了 REDUX_DEVTOOL 插件\nconst shouldCompose = isNotProduction\n  && typeof window === 'object'\n  && window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__;\n\nconst composeEnhancers = shouldCompose\n  ? window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__({\n    // Specify here name, actionsBlacklist, actionsCreators and other options\n  })\n  : compose;\n\n/*\n   调用 applyMiddleware ，使用 middleware 来增强 createStore\n*/\nconst configureStore = composeEnhancers(applyMiddleware(...middleware))(createStore);\n\nconst store = configureStore(reducer);\nwindow.Store = store;\n\nif (module.hot) {\n  module.hot.accept('./reducers.js', () => {\n    console.log('reducer changed');\n    store.replaceReducer(require('./reducers').default);\n  });\n}\n\nexport default store;\n"
  },
  {
    "path": "src/redux/reducers.js",
    "content": "import { combineReducers } from 'redux';\nimport skuunit from './skuunit/skuunit';\n\nconst reducers = {\n  skuunit,\n};\n\nexport default combineReducers(reducers);\n"
  },
  {
    "path": "src/redux/skuunit/api.js",
    "content": "import { get, post } from 'common/request';\nimport {\n  SHOW_UNIT_LIST_URL,\n  DELETE_UNIT_URL,\n} from 'constants/url';\n\n/**\n * 单元列表查询展示\n * @param {*} params\n */\nexport function showSkuunitListApi(params) {\n  if (!params) {\n    return Promise.reject('params is wrong');\n  }\n  return get(SHOW_UNIT_LIST_URL, params)\n    .then(res => {\n      // 开发时调试等待效果;\n      return new Promise((resolve) => {\n        setTimeout(() => {\n          return resolve(res);\n        }, 1000);\n      });\n    })\n    .then(res => res.data);\n}\n\n/**\n * 删除单元\n * @param {} params\n */\nexport function deleteSkuUnitApi(params) {\n  if (!params) {\n    return Promise.reject('params is wrong');\n  }\n  return post(DELETE_UNIT_URL, params)\n    .then(res => res.data);\n}\n"
  },
  {
    "path": "src/redux/skuunit/skuunit.js",
    "content": "/**\n * author: niuxiaoyu\n * description: 单元\n * date: 2018/6/6\n */\nimport { ACTION_TYPE_ADD_ERROR } from 'constants/constant';\n\nimport {\n  showSkuunitListApi,\n  deleteSkuUnitApi,\n} from './api';\n\nconst SHOW_LOADING = 'skuunit/SHOW_LOADING';\nconst HIDE_LOADING = 'skuunit/HIDE_LOADING';\nconst SHOW_SKU_UNIT_LIST = 'skuunit/SHOW_SKU_UNIT_LIST';\nconst DELETE_SKUUNIT = 'skuunit/DELETE_SKUUNIT';\n\nconst initialState = {\n  dataList: [],\n  isLoading: false,\n  error: '',\n};\n\nexport default function reducer(state = initialState, action = {}) {\n  switch (action.type) {\n    case SHOW_LOADING:\n      return Object.assign({}, state, {\n        isLoading: true,\n      });\n    case HIDE_LOADING:\n      return Object.assign({}, state, {\n        isLoading: false,\n      });\n    case SHOW_SKU_UNIT_LIST:\n      return Object.assign({}, state, {\n        dataList: action.payload.data.dataList,\n        totalNumber: action.payload.data.totalNumber,\n      });\n    case DELETE_SKUUNIT:\n      return Object.assign({}, state, {\n        dataList: _deleteskuUnits(state.dataList, action.payload),\n      });\n    default:\n      return state;\n  }\n}\n\n/**\n * 删除单元\n * @param {*} source\n * @param {*} skuUnitIdList\n */\nconst _deleteskuUnits = (source, params) => {\n  const { skuUnitIdList } = params;\n  const newSource = [];\n  for (let i = 0; i < source.length; i++) {\n    const target = skuUnitIdList.find(skuUnitId => skuUnitId === source[i].skuUnitId);\n    if (!target) {\n      newSource.push(source[i]);\n    }\n  }\n  return newSource;\n};\n\n/**\n * 单元列表查询展示\n * @param {*} params\n */\nexport function showSkuunitList(planName) {\n  return async dispatch => {\n    try {\n      dispatch({ type: SHOW_LOADING });\n      const res = await showSkuunitListApi(planName);\n      await dispatch({ type: SHOW_SKU_UNIT_LIST, payload: res });\n      dispatch({ type: HIDE_LOADING });\n    } catch (err) {\n      dispatch({ type: HIDE_LOADING });\n      console.log(err);\n    }\n  };\n}\n\n/**\n * 删除单元\n * @param {} skuUnitIdList\n */\nexport function deleteSkuUnit(skuUnitIdList) {\n  return async dispatch => {\n    try {\n      const param = { skuUnitIdList };\n      const res = await deleteSkuUnitApi(param);\n      dispatch({ type: DELETE_SKUUNIT, payload: param, res });\n    } catch (err) {\n      dispatch({ type: ACTION_TYPE_ADD_ERROR, payload: { errorMsg: err } });\n    }\n  };\n}\n"
  },
  {
    "path": "src/routers/PrimaryLayout.jsx",
    "content": "import React, { Suspense, useState, lazy } from 'react';\nimport PropTypes from 'prop-types';\nimport { Switch, Route, Redirect } from 'react-router-dom';\nimport { Layout, Spin } from 'antd';\n\nimport styles from './index.less';\nimport {\n  Header, Sider, Footer, ErrorBoundary,\n} from 'components';\nconst { Content } = Layout;\n\nconst Skuunit = lazy(() => import(/* webpackChunkName: \"home\" */ './Skuunit'));\n\nconst PrimaryLayout = (props) => {\n  const [collapsed, setCollapsed] = useState(false);\n\n  const toggleCollapse = () => {\n    setCollapsed(!collapsed);\n  };\n\n  return (\n    <Layout>\n      <Sider\n        history={props.history}\n        pathname={props.location.pathname}\n        collapsed={collapsed}\n      />\n      <Layout>\n        <Header\n          collapsed={collapsed}\n          onToggle={toggleCollapse}\n        />\n        <Content>\n          <div className={styles['main-content']}>\n            <Suspense fallback={<Spin />}>\n              <ErrorBoundary>\n                <Switch>\n                  <Route path=\"/home\" component={Skuunit} />\n                  <Route path=\"/nav2\" component={() => <div>nav2</div>} />\n                  <Redirect to=\"/home\" />\n                </Switch>\n              </ErrorBoundary>\n            </Suspense>\n          </div>\n        </Content>\n        <Footer />\n      </Layout>\n    </Layout>\n  );\n};\n\nPrimaryLayout.propTypes = {\n  history: PropTypes.objectOf(PropTypes.any),\n  location: PropTypes.objectOf(PropTypes.any),\n};\n\nexport default PrimaryLayout;\n"
  },
  {
    "path": "src/routers/Skuunit/columns.jsx",
    "content": "import React from 'react';\n\nconst getColumns = ({\n  handleDelete,\n}) => {\n  return [\n    {\n      title: '名称',\n      dataIndex: 'skuUnitName',\n      width: 230,\n      key: 'skuUnitName',\n    },\n    {\n      title: '集合名称',\n      dataIndex: 'skuSetName',\n      key: 'skuSetName',\n    },\n    {\n      title: '操作',\n      key: 'action',\n      width: 100,\n      render: (text, record) => (\n        <span>\n          <a href=\"javascript:;\" onClick={() => handleDelete(record.skuUnitId, record.skuUnitName)}>删除</a>\n        </span>\n      ),\n    },\n  ];\n};\n\nexport default getColumns;\n"
  },
  {
    "path": "src/routers/Skuunit/index.jsx",
    "content": "import React, { Component } from 'react';\nimport PropTypes from 'prop-types';\nimport {\n  Table, Modal,\n} from 'antd';\nimport { bindActionCreators } from 'redux';\nimport { connect } from 'react-redux';\n\nimport { Pages } from 'components';\nimport getColumns from './columns';\n\nimport { PAGE_DATA } from 'constants/constant';\nimport {\n  showSkuunitList,\n  deleteSkuUnit,\n} from 'reduxDir/skuunit/skuunit';\n\nconst confirm = (msg, onOk, onCancel) => {\n  Modal.confirm({\n    title: msg,\n    onOk() {\n      onOk && onOk();\n    },\n    onCancel() {\n      onCancel && onCancel();\n    },\n  });\n};\n\nclass Skuunit extends Component {\n  static propTypes = {\n    data: PropTypes.shape({\n      dataList: PropTypes.arrayOf(PropTypes.any),\n      isLoading: PropTypes.bool,\n      totalNumber: PropTypes.number,\n    }),\n    showSkuunitList: PropTypes.func,\n    deleteSkuUnit: PropTypes.func,\n  };\n\n  state = {\n    pageNo: PAGE_DATA.pageNo,\n    pageSize: PAGE_DATA.pageSize,\n  };\n\n  componentDidMount() {\n    this.loadTable();\n  }\n\n  getFetchListParams = () => {\n    const {\n      page, pageSize,\n    } = this.state;\n\n    return {\n      page,\n      pageSize,\n    };\n  }\n\n  /**\n   * @resetPage: 表示是否重置Pages组件\n   * @pageInfo: Pages组件的参数\n   */\n  loadTable = (resetPage = true, pageInfo) => {\n    let pageNo;\n    let pageSize;\n    if (!resetPage) {\n      pageNo = pageInfo ? pageInfo.page : this.state.pageNo;\n      pageSize = pageInfo ? pageInfo.pageSize : this.state.pageSize;\n    } else {\n      pageNo = PAGE_DATA.pageNo;\n      pageSize = PAGE_DATA.pageSize;\n    }\n\n    this.setState({\n      pageNo,\n      pageSize,\n    }, () => this.props.showSkuunitList(this.getFetchListParams()));\n  }\n\n  deleteSingleUnit = (unitId, unitName) => {\n    confirm(`删除单元：${unitName} ？`, () => {\n      this.props.deleteSkuUnit([unitId]);\n    });\n  }\n\n  render() {\n    const { dataList, isLoading, totalNumber } = this.props.data;\n    const {\n      pageNo, pageSize,\n    } = this.state;\n\n    const columns = getColumns({\n      handleDelete: this.deleteSingleUnit,\n    });\n\n    return (\n      <div>\n        <Table\n          columns={columns}\n          dataSource={dataList}\n          loading={isLoading}\n          bordered\n          pagination={false}\n          rowKey=\"skuUnitId\"\n        />\n        <Pages\n          total={totalNumber}\n          current={pageNo}\n          pageSize={pageSize}\n          loadTable={this.loadTable}\n        />\n      </div>\n    );\n  }\n}\n\n\nexport default connect(\n  state => ({\n    data: state.skuunit,\n  }),\n  dispatch => bindActionCreators({\n    showSkuunitList,\n    deleteSkuUnit,\n  }, dispatch),\n)(Skuunit);\n"
  },
  {
    "path": "src/routers/index.jsx",
    "content": "/*\n * 路由主入口：可以在该页面进行登录权限控制\n */\nimport React from 'react';\nimport {\n  Route, Switch, withRouter,\n} from 'react-router-dom';\nimport { hot } from 'react-hot-loader';\n\nimport { LocaleProvider } from 'antd';\nimport zhCN from 'antd/lib/locale-provider/zh_CN';\n// UI示范，如果不需要目前的UI框架，直接替换该组件即可\nimport PrimaryLayout from './PrimaryLayout';\n\nconst Root = (props) => {\n  // const { location, history, match } = props;\n  return (\n    <Switch>\n      <Route path=\"/\" render={() => <LocaleProvider locale={zhCN}><PrimaryLayout {...props} /></LocaleProvider>} />\n    </Switch>\n  );\n};\n\nexport default hot(module)(withRouter(Root));\n"
  },
  {
    "path": "src/routers/index.less",
    "content": ".main-content {\n\tmargin: 24px 16px;\n\tpadding: 24px;\n\tbackground: #fff;\n\tmin-height: 280;\n}"
  },
  {
    "path": "template.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en\">\n\n<head>\n  <meta charset=\"utf-8\">\n  <link rel=\"shortcut icon\" href=\"favicon.ico\" />\n  <title>PC端React+redux脚手架</title>\n</head>\n\n<body>\n  <div class=\"asset newAnt\" id=\"app\">\n  </div>\n</body>\n\n</html>"
  },
  {
    "path": "webpack.config.js",
    "content": "const merge = require('webpack-merge');\nconst Env = require('./build/env');\nconst baseConfig = require('./build/webpack.base.conf');\nconst prodConfig = require('./build/webpack.prod.conf');\nconst devConfig = require('./build/webpack.dev.conf');\n\nlet config = devConfig;\n\nif (Env.isQA() || Env.isProduction()) {\n  config = prodConfig;\n}\n\nmodule.exports = merge(baseConfig, config);\n"
  }
]