Full Code of zhangkun-Jser/react-kit for AI

master df46946e8e9d cached
48 files
89.8 KB
29.3k tokens
42 symbols
1 requests
Download .txt
Repository: zhangkun-Jser/react-kit
Branch: master
Commit: df46946e8e9d
Files: 48
Total size: 89.8 KB

Directory structure:
gitextract_wmw74cji/

├── .babelrc
├── .eslintignore
├── .eslintrc.js
├── .gitignore
├── README.md
├── asset/
│   └── css/
│       ├── app.less
│       ├── base.less
│       ├── icon.less
│       ├── reset.less
│       ├── variables.less
│       └── vendor_antd.less
├── build/
│   ├── env.js
│   ├── postcss.config.js
│   ├── publicPath.js
│   ├── theme.js
│   ├── webpack.base.conf.js
│   ├── webpack.dev.conf.js
│   └── webpack.prod.conf.js
├── index.html
├── mock/
│   ├── config/
│   │   └── mockConfig.json
│   ├── data/
│   │   └── skuunit/
│   │       ├── deleteSkuUnit.json
│   │       ├── showSkuUnitList.json
│   │       └── updateSkuUnitPause.json
│   └── template/
│       └── query/
│           └── table.template
├── package.json
├── src/
│   ├── app.jsx
│   ├── common/
│   │   └── request.js
│   ├── components/
│   │   ├── ErrorBoundary.jsx
│   │   ├── Layout/
│   │   │   ├── Footer.jsx
│   │   │   ├── Header.jsx
│   │   │   ├── Sider.jsx
│   │   │   └── index.css
│   │   ├── Pages/
│   │   │   ├── index.css
│   │   │   └── index.jsx
│   │   └── index.jsx
│   ├── constants/
│   │   ├── constant.js
│   │   └── url.js
│   ├── redux/
│   │   ├── configureStore.js
│   │   ├── reducers.js
│   │   └── skuunit/
│   │       ├── api.js
│   │       └── skuunit.js
│   └── routers/
│       ├── PrimaryLayout.jsx
│       ├── Skuunit/
│       │   ├── columns.jsx
│       │   └── index.jsx
│       ├── index.jsx
│       └── index.less
├── template.html
└── webpack.config.js

================================================
FILE CONTENTS
================================================

================================================
FILE: .babelrc
================================================
{
  "presets": [
    "@babel/preset-env",
    "@babel/preset-react"
  ],
  "plugins": [
    [
      "@babel/plugin-proposal-decorators",
      {
        "legacy": true
      }
    ],
    "@babel/plugin-proposal-class-properties",
    "@babel/plugin-proposal-export-default-from",
    "@babel/plugin-transform-runtime",
    "@babel/plugin-syntax-dynamic-import",
    "react-hot-loader/babel",
    [
      "import",
      {
        "libraryName": "antd",
        "libraryDirectory": "es",
        "style": true
      }
    ],
    [
      "transform-imports",
      {
        "react-router": {
          "transform": "react-router/${member}",
          "preventFullImport": true
        }
      }
    ]
  ],
  "env": {
    "production": {
      "plugins": [
        [
          "transform-react-remove-prop-types",
          {
            "mode": "wrap",
            "ignoreFilenames": [
              "node_modules"
            ]
          }
        ]
      ]
    }
  }
}

================================================
FILE: .eslintignore
================================================
node_modules
dist

================================================
FILE: .eslintrc.js
================================================
module.exports = {
  "env": {
    "node": true,
    "browser": true,
    "es6": true,
    "commonjs": true,
  },
  "parser": "babel-eslint",
  "parserOptions": {
    "ecmaVersion": 6,
    "sourceType": "module",
    "ecmaFeatures": {
      "jsx": true,
      "modules": true,
      "experimentalObjectRestSpread": true
    }
  },
  "plugins": [
    "react",
    "react-hooks"
  ],
  "rules": {
    "react-hooks/rules-of-hooks": "error",
    "react-hooks/exhaustive-deps": "warn",
    // 推荐规则 0="off", 1="warn", 2="error"
    "no-compare-neg-zero": 2,
    "no-cond-assign": 2,
    "no-console": 0,// 不采用 构建时自己去除
    "no-constant-condition": 2,// 采用
    "no-control-regex": 2,// 采用
    "no-debugger": 0,// node端采用,web端不采用
    "no-dupe-args": 2, // 采用
    "no-dupe-keys": 2,// 采用
    "no-duplicate-case": 2,// 采用
    "no-empty": 2, // 采用
    "no-empty-character-class": 2,// 采用
    "no-ex-assign": 2, // 采用
    "no-extra-boolean-cast": 2, // 采用
    "no-extra-parens": 0, // 不采用
    "no-extra-semi": 2,// 采用
    "no-func-assign": 2,// 采用
    "no-inner-declarations": 2, // 采用
    "no-invalid-regexp": 2,// 采用
    "no-irregular-whitespace": 2,// 采用
    "no-obj-calls": 2,// 采用
    "no-regex-spaces": 2,// 采用
    "no-sparse-arrays": 2,// 采用
    "no-unexpected-multiline": 2,// 采用
    "no-unreachable": 2,// 采用
    "no-unsafe-finally": 2,// 采用
    "no-unsafe-negation": 2,// 采用
    "use-isnan": 2,// 采用
    "valid-typeof": 2,// 采用
    "no-case-declarations": 2, // 采用
    "no-empty-pattern": 2,// 采用
    "no-fallthrough": 2,// 采用
    "no-global-assign": 2,// 采用
    "no-octal": 2,// 采用
    "no-redeclare": 2,// 采用
    "no-self-assign": 2,// 采用
    "no-unused-labels": 2,// 采用
    "no-useless-escape": 2,// 采用
    "no-delete-var": 2,// 采用
    "no-undef": 2,// 采用
    "no-unused-vars": 2,// 采用
    "no-mixed-spaces-and-tabs": 2,// 采用
    "constructor-super": 2,// 采用
    "no-class-assign": 2,// 采用
    "no-const-assign": 2,// 采用
    "no-dupe-class-members": 2,// 采用
    "no-new-symbol": 2,// 采用
    "no-this-before-super": 2,// 采用
    "require-yield": 2,// 采用

    // 拓展规则
    "for-direction": 2,// 采用
    "getter-return": 2,// 采用
    "no-await-in-loop": 2,// 采用
    "no-prototype-builtins": 2,// 采用
    "no-template-curly-in-string": 2,// 采用
    // valid-jsdoc 不采用,基础组件强制使用
    "accessor-pairs": 2,// 采用
    "array-callback-return": 2,// 采用
    "block-scoped-var": 2,// 采用
    "class-methods-use-this": 1,//采用(warning,可能报错很多)
    "curly": 1,// 采用 warning
    "default-case": 2,// 采用
    "eqeqeq": 1,// 采用  warning
    "guard-for-in": 2,// 采用
    "no-caller": 2, // 采用
    "no-eval": 2,// 采用
    "no-extend-native": 2,// 采用
    "no-extra-label": 2,// 采用
    "no-floating-decimal": 2,// 采用
    "no-implied-eval": 2,// 采用
    // 临时关闭,有误报
    "no-invalid-this": 0,// 采用
    "no-iterator": 2,// 采用
    "no-labels": 2,// 采用
    "no-lone-blocks": 2,// 采用
    "no-throw-literal": 2,// 采用
    "no-unmodified-loop-condition": 1,// 采用 warning
    "no-useless-concat": 2,// 采用
    "no-useless-return": 2,// 采用
    "radix": 2,// 采用
    "require-await": 2,// 采用

    //关于Node.js或在浏览器中使用CommonJS的相关规则
    "callback-return": 2,// 采用

    // 临时关闭
    "global-require": 0,// 采用
    "handle-callback-err": 2,// 采用
    "no-buffer-constructor": 2,// 采用
    "no-mixed-requires": 2,// 采用
    "no-new-require": 2,// 采用

    // 代码风格
    // 在数组开括号后和闭括号前强制换行
    "array-bracket-newline": 0, // 不采用
    // 禁止或强制在括号内使用空格
    "array-bracket-spacing": [2, "never"], // 采用,禁止在数组括号内出现空格,
    // 强制数组元素间出现换行
    "array-element-newline": 0, // 不采用
    // 禁止或强制在代码块中开括号前和闭括号后有空格
    "block-spacing": [2, "always"], // 采用,要求使用一个或多个空格
    // 强制在代码块中使用一致的大括号风格
    "brace-style": [2, "1tbs", { allowSingleLine: true }], // 强制 one true brace style(一种代码风格,将大括号放在控制语句或声明语句同一行的位置)
    // 可以有例外情况, allowSingleLine允许块的开括号和闭括号在 同一行
    // 要求使用骆驼拼写法 
    "camelcase": [2, { properties: "never" }], // 采用,但不检查属性名称
    // 强制或禁止对注释的第一个字母大写
    "capitalized-comments": 0, // 不采用
    // 要求或禁止使用拖尾逗号
    "comma-dangle": [2, {
      arrays: "always-multiline",
      objects: "always-multiline",
      imports: "always-multiline",
      exports: "always-multiline",
      functions: "always-multiline",
    }], // 采用,当最后一个元素或属性与闭括号 ] 或 } 在 不同的行时,要求使用拖尾逗号;当在 同一行时,禁止使用拖尾逗号。
    // 强制在逗号周围使用空格
    "comma-spacing": [2, { before: false, after: true }], // 采用,禁止在逗号前使用空格,要求在逗号后使用一个或多个空格
    // 逗号风格
    "comma-style": [2, "last", { // last 要求逗号放在数组元素、对象属性或变量声明之后,且在同一行
      exceptions: {             // 额外规则 包含与 JavaScript 代码的抽象语法树 (AST) 的节点类型对应的属性:
        ArrayExpression: false, // 忽略数组字面量的逗号风格
        ArrayPattern: false,    // 忽略数组的解构赋值语句中的逗号风格
        ArrowFunctionExpression: false, // 忽略箭头函数表达式的参数中的逗号风格
        CallExpression: false, // 忽略函数调用的参数中的逗号风格
        FunctionDeclaration: false, // 忽略函数声明的参数中的逗号风格
        FunctionExpression: false,  // 忽略函数表达式的参数中的逗号风格
        ImportDeclaration: false,  // 忽略 import 语句中的逗号风格
        ObjectExpression: false, // 忽略对象字面量的逗号风格
        ObjectPattern: false,   // 忽略对象的解构赋值中的逗号风格
        VariableDeclaration: false, // 忽略变量声明的逗号风格
        NewExpression: false, //  忽略构造函数表达式参数中的逗号风格
      }                       // !!! 注意,以上配置全为false,即不忽略
    }], // 采用
    // 禁止或强制在计算属性中使用空格
    "computed-property-spacing": [2, 'never'], // 采用,禁止在计算属性内使用空格
    // 要求一致的 This
    "consistent-this": 0, // 不采用
    // 要求或禁止文件末尾保留一行空行
    "eol-last": [2, 'always'], // 采用 强制使用换行 (LF)
    // 要求或禁止在函数标识符和其调用之间有空格
    "func-call-spacing": [2, 'never'], // 禁止在函数名和开括号之间有空格
    // 要求函数名与赋值给它们的变量名或属性名相匹配
    "func-name-matching": 0, // 不采用
    // 要求或禁止命名的 function 表达式 
    "func-names": 1, // 警告
    // 强制 function 声明或表达式的一致性
    "func-style": 0, // 不采用
    // 强制在函数括号内使用一致的换行
    "function-paren-newline": [2, 'consistent'], // 采用, 要求每个括号使用一致的换行。如果一个括号有换行,另一个括号没有换行,则报错。
    // 禁止使用指定的标识符
    "id-blacklist": 0, // 不采用
    // 强制标识符的最小和最大长度
    "id-length": 0, // 不采用
    // 要求标识符匹配一个指定的正则表达式
    "id-match": 0, // 不采用
    // 强制隐式返回的箭头函数体的位置
    "implicit-arrow-linebreak": [2, 'beside'], // 采用 禁止在箭头函数体之前出现换行
    // 强制使用一致的缩进
    "indent": [2, 2, {
      SwitchCase: 1,
      VariableDeclarator: 1,
      outerIIFEBody: 1,
      // MemberExpression: null,
      FunctionDeclaration: {
        parameters: 1,
        body: 1
      },
      FunctionExpression: {
        parameters: 1,
        body: 1
      },
      CallExpression: {
        arguments: 1
      },
      ArrayExpression: 1,
      ObjectExpression: 1,
      ImportDeclaration: 1,
      flatTernaryExpressions: false,
      // list derived from https://github.com/benjamn/ast-types/blob/HEAD/def/jsx.js
      ignoredNodes: ['JSXElement', 'JSXElement > *', 'JSXAttribute', 'JSXIdentifier', 'JSXNamespacedName', 'JSXMemberExpression', 'JSXSpreadAttribute', 'JSXExpressionContainer', 'JSXOpeningElement', 'JSXClosingElement', 'JSXText', 'JSXEmptyExpression', 'JSXSpreadChild'],
      ignoreComments: false
    }], // 采用 一般2个缩进,特殊语句见配置
    // 强制在 JSX 属性中一致地使用双引号或单引号
    "jsx-quotes": 0, // 不采用
    // 强制在对象字面量的属性中键和值之间使用一致的间距
    "key-spacing": [2, { beforeColon: false, afterColon: true }], // 采用, 禁止在对象字面量的键和冒号之间存在空格,要求在对象字面量的冒号和值之间存在至少有一个空格
    // 强制在关键字前后使用一致的空格
    "keyword-spacing": [2, {
      before: true, // 要求在关键字之前至少有一个空格
      after: true, // 要求在关键字之后至少有一个空格
      overrides: { // 允许覆盖指定的关键字的空格风格
        return: { after: true },
        throw: { after: true },
        case: { after: true }
      }
    }], // 采用
    // 强制行注释的位置
    "line-comment-position": 0, // 不采用
    // 强制使用一致的换行风格
    "linebreak-style": [2, 'unix'], // 采用,强制使用 Unix 换行符: \n。
    // 要求在注释周围有空行
    "lines-around-comment": 0, //不采用
    // 要求或禁止类成员之间出现空行
    "lines-between-class-members": [2, 'always', { exceptAfterSingleLine: false }],// 采用
    // 强制可嵌套的块的最大深度
    "max-depth": 0, // 不采用
    // 强制一行的最大长度
    "max-len": [2, 100, 2, {
      ignoreUrls: true,
      ignoreComments: false,
      ignoreRegExpLiterals: true,
      ignoreStrings: true,
      ignoreTemplateLiterals: true,
    }],// 采用,最长100,tab字符宽度为2
    // 强制最大行数
    "max-lines": 0, // 不采用
    // 强制回调函数最大嵌套深度
    "max-nested-callbacks": 0, // 不采用
    // 强制函数定义中最多允许的参数数量
    "max-params": 0, //不采用
    // 强制函数块最多允许的的语句数量
    "max-statements": 0, // 不采用
    // 强制每一行中所允许的最大语句数量
    "max-statements-per-line": 0, // 不采用
    // 强制对多行注释使用特定风格
    "multiline-comment-style": 0, // 不采用
    // 要求或禁止在三元操作数中间换行
    "multiline-ternary": 0, // 不采用
    // 要求构造函数首字母大写
    "new-cap": [2, {
      newIsCap: true, // 要求调用 new 操作符时有首字母大小的函数
      newIsCapExceptions: [],
      capIsNew: false, // 要求调用首字母大写的函数时有 new 操作符
      capIsNewExceptions: ['Immutable.Map', 'Immutable.Set', 'Immutable.List'], // 允许调用指定的首字母大写的函数时没有 new 操作符
    }], // 采用
    // 要求调用无参构造函数时有圆括号
    "new-parens": 2, // 采用
    // 要求方法链中每个调用都有一个换行符
    "newline-per-chained-call": [2, { ignoreChainWithDepth: 4 }], // 允许在同一行成链的深度为4
    // 禁用 Array 构造函数
    "no-array-constructor": 2, // 采用
    // 禁用按位运算符
    "no-bitwise": 2, // 采用
    // 禁用 continue 语句
    "no-continue": 2, // 采用
    // 禁止在代码后使用内联注释
    "no-inline-comments": 0, // 不采用
    // 禁止 if 作为唯一的语句出现在 else 语句中
    "no-lonely-if": 2, // 采用
    // 禁止混合使用不同的操作符
    "no-mixed-operators": [2, {
      groups: [
        ['%', '**'],
        ['%', '+'],
        ['%', '-'],
        ['%', '*'],
        ['%', '/'],
        ['**', '+'],
        ['**', '-'],
        ['**', '*'],
        ['**', '/'],
        ['&', '|', '^', '~', '<<', '>>', '>>>'],
        ['==', '!=', '===', '!==', '>', '>=', '<', '<='],
        ['&&', '||'],
        ['in', 'instanceof']
      ],
      allowSamePrecedence: false
    }], // 采用
    // 禁止连续赋值
    "no-multi-assign": 2, // 采用
    // 禁止出现多行空行
    "no-multiple-empty-lines": [2, { max: 2, maxEOF: 0 }], // 采用
    // 禁用否定的表达式
    "no-negated-condition": 0, // 不采用
    // 禁用嵌套的三元表达式
    "no-nested-ternary": 2, // 采用
    // 禁用 Object 的构造函数
    "no-new-object": 2, // 采用
    // 禁用一元操作符 ++ 和 --
    // 临时关闭
    "no-plusplus": 0, // 采用
    // 禁用特定的语法
    "no-restricted-syntax": [
      2,
      {
        selector: 'ForInStatement',
        message: 'for..in loops iterate over the entire prototype chain, which is virtually never what you want. Use Object.{keys,values,entries}, and iterate over the resulting array.',
      },
      {
        selector: 'ForOfStatement',
        message: 'iterators/generators require regenerator-runtime, which is too heavyweight for this guide to allow them. Separately, loops should be avoided in favor of array iterations.',
      },
      {
        selector: 'LabeledStatement',
        message: 'Labels are a form of GOTO; using them makes code confusing and hard to maintain and understand.',
      },
      {
        selector: 'WithStatement',
        message: '`with` is disallowed in strict mode because it makes code impossible to predict and optimize.',
      },
    ], // 采用
    // 禁用 tab
    "no-tabs": 2, // 采用
    // 禁用三元操作符
    "no-ternary": 0, //不采用
    // 禁用行尾空格
    "no-trailing-spaces": [2, {
      skipBlankLines: false,
      ignoreComments: false,
    }], // 采用
    // 禁止标识符中有悬空下划线(临时关闭)
    /* "no-underscore-dangle": [2, {
      allow: [],
      allowAfterThis: false,
      allowAfterSuper: false,
      enforceInMethodNames: false,
    }], */ // 采用
    // 禁止可以在有更简单的可替代的表达式时使用三元操作符
    "no-unneeded-ternary": [2, { defaultAssignment: false }],
    // 禁止属性前有空白
    "no-whitespace-before-property": 2, //采用
    // 强制单个语句的位置
    "nonblock-statement-body-position": [2, 'beside', { overrides: {} }], // 采用
    // 强制大括号内换行符的一致性
    "object-curly-newline": [2, {
      ObjectExpression: { minProperties: 4, multiline: true, consistent: true },
      ObjectPattern: { minProperties: 4, multiline: true, consistent: true },
      ImportDeclaration: { minProperties: 4, multiline: true, consistent: true },
      ExportDeclaration: { minProperties: 4, multiline: true, consistent: true },
    }],// 采用
    // 强制在大括号中使用一致的空格
    "object-curly-spacing": [2, 'always'], // 采用
    // 强制将对象的属性放在不同的行上
    "object-property-newline": [2, {
      allowAllPropertiesOnSameLine: true,
    }],
    // 强制函数中的变量要么一起声明要么分开声明
    "one-var": [2, 'never'], // 采用, 要求每个作用域有多个变量声明
    // 要求或禁止在变量声明周围换行
    "one-var-declaration-per-line": [2, 'always'], // 采用,强制每个变量声明都换行
    // 要求或禁止在可能的情况下使用简化的赋值操作符
    "operator-assignment": [2, 'always'], //采用,要求尽可能地简化赋值操作
    // 强制操作符使用一致的换行符
    "operator-linebreak": [2, 'before', { overrides: { '=': 'none' } }], // 采用, 要求把换行符放在操作符前面
    // 要求或禁止块内填充
    "padded-blocks": [2, { blocks: 'never', classes: 'never', switches: 'never' }], // 采用
    // 要求或禁止在语句间填充空行
    "padding-line-between-statements": 0, // 不采用
    // 要求对象字面量属性名称用引号括起来
    "quote-props": [2, 'as-needed', { keywords: false, unnecessary: true, numbers: false }], //采用
    // 强制使用一致的反勾号、双引号或单引号
    "quotes": [2, 'single', { avoidEscape: true }], // 采用
    // 要求使用 JSDoc 注释
    "require-jsdoc": 0, // 不采用
    // 要求或禁止使用分号代替 ASI
    "semi": [2, 'always'], // 采用,要求在语句末尾使用分号
    // 强制分号之前和之后使用一致的空格
    "semi-spacing": ['error', { before: false, after: true }], // 采用
    // 强制分号的位置
    "semi-style": [2, 'last'], // 采用,强制分号出现在句子末尾。
    // 要求对象属性按序排列
    "sort-keys": 0, // 不采用
    // 要求同一个声明块中的变量按顺序排列
    "sort-vars": 0, // 不采用
    // 强制在块之前使用一致的空格
    "space-before-blocks": 2, // 采用
    // 强制在 function的左括号之前使用一致的空格
    "space-before-function-paren": [2, {
      anonymous: 'always',
      named: 'never',
      asyncArrow: 'always'
    }], // 采用
    // 强制在圆括号内使用一致的空格
    "space-in-parens": [2, 'never'], // 采用,强制圆括号内没有空格
    // 要求操作符周围有空格
    "space-infix-ops": 2, // 采用
    // 强制在一元操作符前后使用一致的空格
    "space-unary-ops": [2, {
      words: true,
      nonwords: false,
      overrides: {
      },
    }], // 采用
    // 强制在注释中 // 或 /* 使用一致的空格
    "spaced-comment": [2, 'always', {
      line: {
        exceptions: ['-', '+'],
        markers: ['=', '!'], // space here to support sprockets directives
      },
      block: {
        exceptions: ['-', '+'],
        markers: ['=', '!'], // space here to support sprockets directives
        balanced: true,
      }
    }], // 采用
    // 强制在 switch 的冒号左右有空格
    "switch-colon-spacing": [2, { after: true, before: false }], // 采用
    // 要求或禁止在模板标记和它们的字面量之间有空格
    "template-tag-spacing": [2, 'never'], // 禁止在一个标记的函数和它的模板字面量之间有空格
    // 要求或禁止 Unicode 字节顺序标记 (BOM)
    "unicode-bom": [2, 'never'], // 采用
    // 要求正则表达式被括号括起来
    "wrap-regex": 0, // 不采用


    // react相关配置
    // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/display-name.md
    'react/display-name': ['off', { ignoreTranspilerName: false }],

    // Forbid certain propTypes (any, array, object)
    // https://github.com/yannickcr/eslint-plugin-react/blob/843d71a432baf0f01f598d7cf1eea75ad6896e4b/docs/rules/forbid-prop-types.md
    'react/forbid-prop-types': ['error', {
      forbid: ['any', 'array', 'object'],
      checkContextTypes: true,
      checkChildContextTypes: true,
    }],

    // Forbid certain props on DOM Nodes
    // https://github.com/yannickcr/eslint-plugin-react/blob/843d71a432baf0f01f598d7cf1eea75ad6896e4b/docs/rules/forbid-dom-props.md
    'react/forbid-dom-props': ['off', { forbid: [] }],

    // Enforce boolean attributes notation in JSX
    // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/jsx-boolean-value.md
    'react/jsx-boolean-value': ['error', 'never', { always: [] }],

    // Validate closing bracket location in JSX
    // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/jsx-closing-bracket-location.md
    'react/jsx-closing-bracket-location': ['error', 'line-aligned'],

    // Validate closing tag location in JSX
    // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/jsx-closing-tag-location.md
    'react/jsx-closing-tag-location': 'error',

    // Enforce or disallow spaces inside of curly braces in JSX attributes
    // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/jsx-curly-spacing.md
    'react/jsx-curly-spacing': ['error', 'never', { allowMultiline: true }],

    // Enforce event handler naming conventions in JSX
    // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/jsx-handler-names.md
    'react/jsx-handler-names': ['off', {
      eventHandlerPrefix: 'handle',
      eventHandlerPropPrefix: 'on',
    }],

    // Validate props indentation in JSX
    // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/jsx-indent-props.md
    'react/jsx-indent-props': ['error', 2],

    // Validate JSX has key prop when in array or iterator
    // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/jsx-key.md
    'react/jsx-key': 'off',

    // Limit maximum of props on a single line in JSX
    // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/jsx-max-props-per-line.md
    'react/jsx-max-props-per-line': ['error', { maximum: 1, when: 'multiline' }],

    // Prevent usage of .bind() in JSX props
    // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/jsx-no-bind.md
    'react/jsx-no-bind': ['error', {
      ignoreRefs: true,
      allowArrowFunctions: true,
      allowBind: false,
    }],

    // Prevent duplicate props in JSX
    // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/jsx-no-duplicate-props.md
    'react/jsx-no-duplicate-props': ['error', { ignoreCase: true }],

    // Prevent usage of unwrapped JSX strings
    // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/jsx-no-literals.md
    'react/jsx-no-literals': ['off', { noStrings: true }],

    // Disallow undeclared variables in JSX
    // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/jsx-no-undef.md
    'react/jsx-no-undef': 'error',

    // Enforce PascalCase for user-defined JSX components
    // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/jsx-pascal-case.md
    'react/jsx-pascal-case': ['error', {
      allowAllCaps: true,
      ignore: [],
    }],

    // Enforce propTypes declarations alphabetical sorting
    // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/sort-prop-types.md
    'react/sort-prop-types': ['off', {
      ignoreCase: true,
      callbacksLast: false,
      requiredFirst: false,
      sortShapeProp: true,
    }],

    // Deprecated in favor of react/jsx-sort-props
    'react/jsx-sort-prop-types': 'off',

    // Enforce props alphabetical sorting
    // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/jsx-sort-props.md
    'react/jsx-sort-props': ['off', {
      ignoreCase: true,
      callbacksLast: false,
      shorthandFirst: false,
      shorthandLast: false,
      noSortAlphabetically: false,
      reservedFirst: true,
    }],

    // Enforce defaultProps declarations alphabetical sorting
    // https://github.com/yannickcr/eslint-plugin-react/blob/843d71a432baf0f01f598d7cf1eea75ad6896e4b/docs/rules/jsx-sort-default-props.md
    'react/jsx-sort-default-props': ['off', {
      ignoreCase: true,
    }],

    // Prevent React to be incorrectly marked as unused
    // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/jsx-uses-react.md
    'react/jsx-uses-react': ['error'],

    // Prevent variables used in JSX to be incorrectly marked as unused
    // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/jsx-uses-vars.md
    'react/jsx-uses-vars': 'error',

    // Prevent usage of dangerous JSX properties
    // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/no-danger.md
    'react/no-danger': 'warn',

    // Prevent usage of deprecated methods
    // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/no-deprecated.md
    'react/no-deprecated': ['error'],

    // Prevent usage of setState in componentDidMount
    // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/no-did-mount-set-state.md
    // this is necessary for server-rendering
    'react/no-did-mount-set-state': 'off',

    // Prevent usage of setState in componentDidUpdate
    // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/no-did-update-set-state.md
    'react/no-did-update-set-state': 'error',

    // Prevent usage of setState in componentWillUpdate
    // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/no-will-update-set-state.md
    'react/no-will-update-set-state': 'error',

    // Prevent direct mutation of this.state
    // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/no-direct-mutation-state.md
    'react/no-direct-mutation-state': 'off',

    // Prevent usage of isMounted
    // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/no-is-mounted.md
    'react/no-is-mounted': 'error',

    // Prevent multiple component definition per file
    // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/no-multi-comp.md
    'react/no-multi-comp': ['error', { ignoreStateless: true }],

    // Prevent usage of setState
    // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/no-set-state.md
    'react/no-set-state': 'off',

    // Prevent using string references
    // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/no-string-refs.md
    'react/no-string-refs': 'error',

    // Prevent usage of unknown DOM property
    // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/no-unknown-property.md
    'react/no-unknown-property': 'error',

    // Require ES6 class declarations over React.createClass
    // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/prefer-es6-class.md
    'react/prefer-es6-class': ['error', 'always'],

    // Require stateless functions when not using lifecycle methods, setState or ref
    // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/prefer-stateless-function.md
    'react/prefer-stateless-function': ['error', { ignorePureComponents: true }],

    // Prevent missing props validation in a React component definition
    // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/prop-types.md
    'react/prop-types': ['error', {
      ignore: [],
      customValidators: [],
      skipUndeclared: false
    }],

    // Prevent missing React when using JSX
    // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/react-in-jsx-scope.md
    'react/react-in-jsx-scope': 'error',

    // Require render() methods to return something
    // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/require-render-return.md
    'react/require-render-return': 'error',

    // Prevent extra closing tags for components without children
    // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/self-closing-comp.md
    'react/self-closing-comp': 'error',

    // Enforce component methods order
    // https://github.com/yannickcr/eslint-plugin-react/blob/843d71a432baf0f01f598d7cf1eea75ad6896e4b/docs/rules/sort-comp.md
    'react/sort-comp': ['error', {
      order: [
        'static-methods',
        'instance-variables',
        'lifecycle',
        '/^on.+$/',
        'getters',
        'setters',
        '/^(get|set)(?!(InitialState$|DefaultProps$|ChildContext$)).+$/',
        'instance-methods',
        'everything-else',
        'rendering',
      ],
      groups: {
        lifecycle: [
          'displayName',
          'propTypes',
          'contextTypes',
          'childContextTypes',
          'mixins',
          'statics',
          'defaultProps',
          'constructor',
          'getDefaultProps',
          'getInitialState',
          'state',
          'getChildContext',
          'componentWillMount',
          'componentDidMount',
          'componentWillReceiveProps',
          'shouldComponentUpdate',
          'componentWillUpdate',
          'componentDidUpdate',
          'componentWillUnmount',
        ],
        rendering: [
          '/^render.+$/',
          'render'
        ],
      },
    }],

    // Prevent missing parentheses around multilines JSX
    // https://github.com/yannickcr/eslint-plugin-react/blob/843d71a432baf0f01f598d7cf1eea75ad6896e4b/docs/rules/jsx-wrap-multilines.md
    'react/jsx-wrap-multilines': ['error', {
      declaration: 'parens-new-line',
      assignment: 'parens-new-line',
      return: 'parens-new-line',
      arrow: 'parens-new-line',
      condition: 'parens-new-line',
      logical: 'parens-new-line',
      prop: 'parens-new-line',
    }],

    // 当元素为多行时,要求JSX元素中的第一个属性位于新行上。
    // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/jsx-first-prop-new-line.md
    'react/jsx-first-prop-new-line': [2, 'multiline-multiprop'], // 采用,如果JSX标签占用多个行并且有多个属性,则第一个属性应该总是放置在一个新行上。这是默认值。

    // 在JSX的等号两侧是否强制留有空格
    // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/jsx-equals-spacing.md
    'react/jsx-equals-spacing': [2, 'never'], // 采用,等号周围的不允许空格

    // 强制jsx的缩进风格
    // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/jsx-indent.md
    'react/jsx-indent': [2, 2], // 采用,两个空格作为缩进

    // 不允许不安全的target='_blank'用法, 具体见连接说明
    // https://github.com/yannickcr/eslint-plugin-react/blob/ac102885765be5ff37847a871f239c6703e1c7cc/docs/rules/jsx-no-target-blank.md
    'react/jsx-no-target-blank': [2, { enforceDynamicLinks: 'always' }], // 采用,如果是动态链接则不强制

    // 只有.jsx文件允许写JSX语法
    // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/jsx-filename-extension.md
    'react/jsx-filename-extension': [2, { extensions: ['.jsx'] }],// 采用

    // 防止JS注释意外的作为文本注入到JSX中
    // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/jsx-no-comment-textnodes.md
    'react/jsx-no-comment-textnodes': 2, // 采用,jsx的注释问题

    // 不允许使用react或者reactdom的render方法的返回值
    // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/no-render-return-value.md
    'react/no-render-return-value': 2, // 采用,建议采用ref

    // 要求有shouldComponentUpdate方法, 或者采用PureRenderMixin
    // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/require-optimization.md
    'react/require-optimization': [0, { allowDecorators: [] }], // 不采用

    // 禁止使用findDOMNode()方法
    // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/no-find-dom-node.md
    'react/no-find-dom-node': 2, // 采用

    // 在Components中禁用一些特定的props
    // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/forbid-component-props.md
    'react/forbid-component-props': 0, // 不采用

    // 禁用特定元素
    // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/forbid-elements.md
    'react/forbid-elements': 0, // 不采用

    // 避免在children和dangerouslySetInnerHTML属性共存时出现问题
    // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/no-danger-with-children.md
    'react/no-danger-with-children': 2, // 采用

    // 防止未使用的propType定义
    // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/no-unused-prop-types.md
    'react/no-unused-prop-types': [2, {
      customValidators: [
      ],
      skipShapeProps: true,
    }], // 采用

    // 要求样式的值为对象或者变量
    // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/style-prop-object.md
    'react/style-prop-object': 2, // 采用

    // 禁止无效字符的出现
    // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/no-unescaped-entities.md
    'react/no-unescaped-entities': 2, // 采用

    // 禁止通过children props来传值
    // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/no-children-prop.md
    'react/no-children-prop': 2, // 采用

    // 在JSX打开和关闭括号内和周围验证空格
    // https://github.com/yannickcr/eslint-plugin-react/blob/843d71a432baf0f01f598d7cf1eea75ad6896e4b/docs/rules/jsx-tag-spacing.md
    'react/jsx-tag-spacing': [2, {
      closingSlash: 'never',
      beforeSelfClosing: 'always',
      afterOpening: 'never',
      beforeClosing: 'never',
    }], // 采用, 不留空格

    // 强制在闭JSX元素标签之前留空格
    // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/jsx-space-before-closing.md
    // Deprecated in favor of jsx-tag-spacing
    'react/jsx-space-before-closing': 0, // 不采用

    // 禁止使用数组的索引作为元素的key属性
    // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/no-array-index-key.md
    'react/no-array-index-key': 2, // 采用, 原因可以见链接

    // 强制给每个不是必须的属性定义一个默认值
    // https://github.com/yannickcr/eslint-plugin-react/blob/843d71a432baf0f01f598d7cf1eea75ad6896e4b/docs/rules/require-default-props.md
    'react/require-default-props': [0, {
      forbidDefaultForRequired: true,
    }], // 采用, 写了isRequired的属性则禁止设置默认值

    // 禁止使用没有被export的prototype
    // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/forbid-foreign-prop-types.md
    'react/forbid-foreign-prop-types': ['warn', { allowInPropTypes: true }], // 设置为警告级别

    // 不要给单标签的jsx元素传入children
    // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/void-dom-elements-no-children.md
    'react/void-dom-elements-no-children': 2, // 采用

    // 强制所有的默认prop都对应一个非必须的proptype
    // https://github.com/yannickcr/eslint-plugin-react/blob/9e13ae2c51e44872b45cc15bf1ac3a72105bdd0e/docs/rules/default-props-match-prop-types.md
    'react/default-props-match-prop-types': [2, { allowRequiredDefaults: false }], // 采用

    // 继承React.PureComponent时,禁止使用shouldComponentUpdate
    // https://github.com/yannickcr/eslint-plugin-react/blob/9e13ae2c51e44872b45cc15bf1ac3a72105bdd0e/docs/rules/no-redundant-should-component-update.md
    'react/no-redundant-should-component-update': 2, // 采用s

    // 禁止存在未使用的state值
    // https://github.com/yannickcr/eslint-plugin-react/pull/1103/
    'react/no-unused-state': 2, // 采用

    // 强制布尔值属性命名的的一致性
    // https://github.com/yannickcr/eslint-plugin-react/blob/843d71a432baf0f01f598d7cf1eea75ad6896e4b/docs/rules/boolean-prop-naming.md
    'react/boolean-prop-naming': 0, // 不采用

    // 防止常见的套管缺陷
    // https://github.com/yannickcr/eslint-plugin-react/blob/73abadb697034b5ccb514d79fb4689836fe61f91/docs/rules/no-typos.md
    'react/no-typos': 2, // 采用

    // 禁止在props和children中使用无意义的大括号
    // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/jsx-curly-brace-presence.md
    'react/jsx-curly-brace-presence': [2, { props: 'never', children: 'never' }], // 采用

    // 每行一个jsx元素
    // https://github.com/yannickcr/eslint-plugin-react/blob/843d71a432baf0f01f598d7cf1eea75ad6896e4b/docs/rules/jsx-one-expression-per-line.md
    // TODO: re-enable when an option for text children is available
    'react/jsx-one-expression-per-line': 0, // 不采用

    // 强制使用desconstruct的一致性
    // https://github.com/yannickcr/eslint-plugin-react/blob/843d71a432baf0f01f598d7cf1eea75ad6896e4b/docs/rules/destructuring-assignment.md
    // TODO: re-enable when component detection is fixed
    'react/destructuring-assignment': 0, // 不采用

    // 禁止在this.setState中使用this.state
    // https://github.com/yannickcr/eslint-plugin-react/blob/843d71a432baf0f01f598d7cf1eea75ad6896e4b/docs/rules/no-access-state-in-setstate.md
    'react/no-access-state-in-setstate': 2, // 采用

    // 禁止使用没有显式type属性的按钮元素
    // https://github.com/yannickcr/eslint-plugin-react/blob/843d71a432baf0f01f598d7cf1eea75ad6896e4b/docs/rules/button-has-type.md
    'react/button-has-type': [2, {
      button: true,
      submit: true,
      reset: false,
    }], // 采用

    // 确保内联标签两边有空格
    'react/jsx-child-element-spacing': 0, // 不采用

    // 禁止在无状态组件中使用this
    // https://github.com/yannickcr/eslint-plugin-react/blob/843d71a432baf0f01f598d7cf1eea75ad6896e4b/docs/rules/no-this-in-sfc.md
    'react/no-this-in-sfc': 2, // 采用

    // 检查jsx最大深度
    // https://github.com/yannickcr/eslint-plugin-react/blob/abe8381c0d6748047224c430ce47f02e40160ed0/docs/rules/jsx-max-depth.md
    'react/jsx-max-depth': 0, // 不采用

    // 禁止在jsx props之间存在多个空格
    // https://github.com/yannickcr/eslint-plugin-react/blob/ac102885765be5ff37847a871f239c6703e1c7cc/docs/rules/jsx-props-no-multi-spaces.md
    'react/jsx-props-no-multi-spaces': 2, // 采用

    // 不允许使用UNSAFE_ methods
    // https://github.com/yannickcr/eslint-plugin-react/blob/157cc932be2cfaa56b3f5b45df6f6d4322a2f660/docs/rules/no-unsafe.md
    'react/no-unsafe': 0, // 不采用
  }
};


================================================
FILE: .gitignore
================================================
node_modules
npm-debug.log
yarn-error.log
target
node
src/_tmp
static
dist
coverage
rekit_temp_app
_book
.nyc_output
.tmp
.DS_Store
Thumbs.db
.vscode

================================================
FILE: README.md
================================================
# react-starter

PC 端 react+redux 的脚手架,持续进行更新迭代。


## 特点

1. 当中集成了当前react生态的几个主流技术栈:
  - react 16.12.0
  - ant design 3.26.2
  - react-router 5.1.2
  - redux 4.0.4
  - redux-thunk 2.2.0
  - webpack 4.41.2

2. 符合目前前后端分离项目的开发、部署、测试、上线各环节及操作的要求。
3. 优化了传统redux-thunk中action、reducer书写上的不便。
4. 根据路由地址,懒加载所需模块,就是说目前脚手架中提供的示例代码无需删除,只需要确保路由无效即可
5. 增加错误边界,捕捉嵌套组件在生命过程中发生的错误
6. 封装 axios(`src/common/request.js`),对失败的请求进行了统一处理,使用方法:

```
import { get, post, put, del } from 'common/request';
post(URL, params).then(res => {});

// 如果需要自行处理请求失败的情况,也可以直接引入 axios 实例
import axios from 'common/request';
axios.post(URL, params)
  .then(res => {})
  .catch(error => {})
```

## 文件目录

> 注意:带 ✎ 号的表示示例配置,可以根据自己的项目配置自行替换;带 +/- 号的表示示例组件,可保留修改或删除

```
├─asset                     // ========================= 静态资源目录
│  ├─css
|  |  ├─app.less            // ========================= 样式文件入口
|  |  ├─base.less           // ========================= 基础样式
|  |  ├─icon.less           // ========================= iconfont 样式(✎)
|  |  ├─reset.less          // ========================= 样式重置文件
|  |  ├─variables.less      // ========================= 变量文件(✎)
|  |  └─vendor_antd.less    // ========================= 专门用于覆盖 antd 样式(✎)
│  ├─font                   // ========================= iconfont文件,在icon.less文件中引用
|  |  ├─iconfont.eot(+/-)
|  |  ├─iconfont.svg(+/-)
|  |  ├─iconfont.ttf(+/-)
|  |  └─iconfont.woff(+/-)
│  └─img                    // ========================= 图片资源存放目录
│
├─build                     //  ========================= webpack 相关配置目录
│   ├─postcss.config.js     //  ========================= postcss 相关配置
│   ├─publicPath.js         //  ========================= 发布路径(✎)
│   ├─theme.js              //  ========================= antd 主题配置(✎)
│   ├─webpack.base.conf.js  //  ========================= webpack 公用项配置
│   ├─webpack.dev.conf.js   //  ========================= webpack 开发/联调环境项配置
│   └─webpack.prod.conf.js  //  ========================= webpack 生产/测试环境项配置
│
├─mock                      //  ========================= mock 文件目录
│  ├─config
│  ├─data
│  └─template
│
├─src                       //  ========================= js 源码目录
│   ├─common                //  ========================= 公共模块目录
│   │  └─request.js         //  ========================= 对axios进行封装
│   │ 
│   ├─components            //  ========================= 纯组件目录
│   │  ├─Layout             //  ========================= 布局相关组件目录
│   │  │  ├─Footer.jsx(+/-)
│   │  │  ├─Header.jsx(+/-)
│   │  │  └─Sider.jsx(+/-)
│   │  ├─Pages(+/-)        //  ========================= 分页组件
│   │  ├─ErrorBoundary.jsx  //  ========================= 错误边界组件
│   │  └─index.js(+/-)
│   │ 
│   ├─constants
│   │  ├─constant.js        //  ========================= 常量集中管理目录(✎)
│   │  └─url.js             //  ========================= url集中管理目录(✎)
│   │ 
│   ├─redux                 //  ========================= redux 处理目录
│   │  ├─skuunit(+/-)
│   │  ├─configureStore.js  //  ========================= store/中间件配置
│   │  └─reducers.js        //  ========================= 所有reducer入口(✎)
│   │ 
│   ├─routers               //  ========================= 按路由划分的业务模块目录
│   │  ├─Skuunit(+/-)
│   │  ├─index.jsx(✎)
│   │  └─PrimaryLayout.jsx(+/-)
│   │ 
│   └─app.jsx               //  ========================= 项目入口
│
├─.babelrc                  //  ========================= babel配置文件
├─.eslintignore
├─.eslintrc.js              //  ========================= eslint规则文件
├─.gitignore
├─index.html
├─package-lock.json
├─package.json
├─README.md(✎)
├─template.html(✎)         //  ========================= html嵌入模板
└─webpack.config.js         //  ========================= webpack配置入口

```

## Changelog

- 2019.07.15
  - [x] src目录支持使用less
  - [x] 兼容IE11

- 2019.06.11
  - [x] 更改UI

- 2019.06.03
  - [x] 升级babel 7
  - [x] 更新示例组件
  - [x] 修复eslint报错
  - [x] 完善README.md

- 2019.05.22
  - [x] 完成基础版本,给出规范目录结构,满足打包构建路由懒加载数据管理错误处理一条龙服务。


## babel7依赖说明

基础依赖:

- @babel/cli: 为babel的脚手架工具
- @babel/core: babel-core是作为babel的核心存在,babel的核心api都在这个模块里面,比如:transform,用于字符串转码得到AST
- @babel/plugin-proposal-class-properties: 解析class类的属性
- @babel/plugin-proposal-decorators: 解析装饰器模式语法,如使用react-redux的@connect
- @babel/plugin-proposal-export-default-from: 解析export xxx from 'xxx'语法
- @babel/plugin-transform-runtime: 防止转换后的代码重复
- @babel/preset-env : 官方解释“用于编写下一代JavaScript的编译器”,编译成浏览器认识的JavaScript标准
- @babel/preset-react: 用于编译react的jsx,开发react应用必备
- babel-loader: 就是用于编译JavaScript代码
- babel-plugin-import: 用于进行按需加载

代码优化相关依赖:

- [babel-plugin-transform-imports](https://www.npmjs.com/package/babel-plugin-transform-imports):去除未使用的模块
- [babel-plugin-transform-react-remove-prop-types](https://www.npmjs.com/package/babel-plugin-transform-react-remove-prop-types): 生产环境下移除不必要的 React propTypes,以减少代码体积


## 参考文章

- 2019.06.03
  - [Webpack4+Babel7优化70%速度](https://juejin.im/post/5c763885e51d457380771ab0#heading-11)
  - [一口(很长的)气了解 babel](https://juejin.im/post/5c19c5e0e51d4502a232c1c6#heading-0)
- 2019.05.22
  - [Web Performance Optimization with webpack](https://developers.google.com/web/fundamentals/performance/webpack/)
  - [Webpack 4 配置最佳实践](https://juejin.im/post/5b304f1f51882574c72f19b0#heading-1)
  - [Webpack 4 默认分包策略](https://panjiachen.github.io/awesome-bookmarks/blog/webpack/webpack4-b.html)
  - [webpack-libs-optimizations](https://github.com/GoogleChromeLabs/webpack-libs-optimizations)


朋友们如果有一些对本项目得建议,或者想法欢迎去 github 提 issues,我将持续改进优化该项目


================================================
FILE: asset/css/app.less
================================================
/* 样式文件入口 */
@import 'reset.less';
@import 'variables.less';
@import 'icon.less';
@import 'base.less';

// 样式覆盖
@import url("vendor_antd.less");


================================================
FILE: asset/css/base.less
================================================
/* 基础样式 */
html, body {
  font-size: 14px;
  height: 100%;
}

#app {
  height: 100%;
}

.fl {
	.fl();
}
.fr {
	.fr();
}

.ml15 {
	margin-left: 5px;
}
.ml10 {
	margin-left: 10px;
}
.ml15 {
	margin-left: 15px;
}
.ml20 {
	margin-left: 20px;
}
.mr5 {
	margin-right: 5px;
}
.mr10 {
	margin-right: 10px;
}
.mr15 {
	margin-right: 15px;
}
.mr20 {
	margin-right: 20px;
}
.m15 {
	margin: 15px;
}
.mt15 {
	margin-top: 15px;
}
.mt30 {
	margin-top: 30px;
}
.mt45 {
	margin-top: 45px;
}
.mb10 {
	margin-bottom: 10px;
}
.mb15 {
	margin-bottom: 15px;
}
.mb20 {
	margin-bottom: 20px;
}

================================================
FILE: asset/css/icon.less
================================================
/* iconfont样式 */
@font-face {
  font-family: "iconfont";
  // src: url('../font/iconfont.eot'); /* IE9*/
  // src: url('../font/iconfont.eot') format('embedded-opentype'), /* IE6-IE8 */
  // url('../font/iconfont.woff') format('woff'), /* chrome, firefox */
  // url('../font/iconfont.ttf') format('truetype'), /* chrome, firefox, opera, Safari, Android, iOS 4.2+*/
  // url('../font/iconfont.svg') format('svg'); /* iOS 4.1- */
}

.iconfont {
  font-family: "iconfont" !important;
  font-size: 16px;
  font-style: normal;
  display: inline-block;
  vertical-align: baseline;
  text-align: center;
  text-transform: none;
  line-height: 1;
  text-rendering: optimizeLegibility;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
}



================================================
FILE: asset/css/reset.less
================================================
/* 样式重置文件 */

//
// 1. Remove default margin padding.
//

html, body, div, ul, li, h1, h2, h3, h4, h5, h6, p, dl, dt, dd, ol, form, input, textarea, th, td, select {
  margin: 0;
  padding: 0;
}

// HTML5 display definitions
// ==========================================================================

//
// Correct `block` display not defined for any HTML5 element in IE 8/9.
// Correct `block` display not defined for `details` or `summary` in IE 10/11
// and Firefox.
// Correct `block` display not defined for `main` in IE 11.
//

article,
aside,
details,
figcaption,
figure,
footer,
header,
hgroup,
main,
menu,
nav,
section,
summary {
  display: block;
}

//
// 1. Correct `inline-block` display not defined in IE 8/9.
// 2. Normalize vertical alignment of `progress` in Chrome, Firefox, and Opera.
//

audio,
canvas,
progress,
video {
  display: inline-block;
  vertical-align: baseline;
}

//
// Prevent modern browsers from displaying `audio` without controls.
// Remove excess height in iOS 5 devices.
//

audio:not([controls]) {
  display: none;
  height: 0;
}

//
// Address `[hidden]` styling not present in IE 8/9/10.
// Hide the `template` element in IE 8/9/10/11, Safari, and Firefox < 22.
//

[hidden],
template {
  display: none;
}

// Links
// ==========================================================================

//
// Remove the gray background color from active links in IE 10.
//

a {
  background-color: transparent;
}

//
// Improve readability of focused elements when they are also in an
// active/hover state.
//

a:active,
a:hover {
  outline: 0;
}
// Text-level semantics
// ==========================================================================

//
// Address styling not present in IE 8/9/10/11, Safari, and Chrome.
//

abbr[title] {
  border-bottom: 1px dotted;
}

//
// Address style set to `bolder` in Firefox 4+, Safari, and Chrome.
//

b,
strong {
  font-weight: bold;
}

//
// Address styling not present in Safari and Chrome.
//

dfn {
  font-style: italic;
}

//
// Unified "h1" - "h6" font weight
//

h1, h2, h3, h4, h5, h6{font-weight:normal;}

//
// Remove ul-ol the default styles
//

ul,ol {
  list-style: none;
}


//
// Address styling not present in IE 8/9.
//

mark {
  background: #ff0;
  color: #000;
}

//
// Address inconsistent and variable font size in all browsers.
//

small {
  font-size: 80%;
}

//
// Prevent `sub` and `sup` affecting `line-height` in all browsers.
//

sub,
sup {
  font-size: 75%;
  line-height: 0;
  position: relative;
  vertical-align: baseline;
}

sup {
  top: -0.5em;
}

sub {
  bottom: -0.25em;
}

// Embedded content
// ==========================================================================

//
// Remove border when inside `a` element in IE 8/9/10.
//

img {
  border: 0;
}

//
// Correct overflow not hidden in IE 9/10/11.
//

svg:not(:root) {
  overflow: hidden;
}

// Grouping content
// ==========================================================================

//
// Address margin not present in IE 8/9 and Safari.
//

figure {
  margin: 1em 40px;
}

//
// Address differences between Firefox and other browsers.
//

hr {
  box-sizing: content-box;
  height: 0;
}

//
// Contain overflow in all browsers.
//

pre {
  overflow: auto;
}

//
// Address odd `em`-unit font size rendering in all browsers.
//

code,
kbd,
pre,
samp {
  font-family: monospace, monospace;
  font-size: 1em;
}

// Forms
// ==========================================================================

//
// Known limitation: by default, Chrome and Safari on OS X allow very limited
// styling of `select`, unless a `border` property is set.
//

//
// 1. Correct color not being inherited.
//    Known issue: affects color of disabled elements.
// 2. Correct font properties not being inherited.
// 3. Address margins set differently in Firefox 4+, Safari, and Chrome.
//

button,
input,
optgroup,
select,
textarea {
  color: inherit; // 1
  font: inherit; // 2
  margin: 0; // 3
}

//
// Address `overflow` set to `hidden` in IE 8/9/10/11.
//

button {
  overflow: visible;
}

//
// Address inconsistent `text-transform` inheritance for `button` and `select`.
// All other form control elements do not inherit `text-transform` values.
// Correct `button` style inheritance in Firefox, IE 8/9/10/11, and Opera.
// Correct `select` style inheritance in Firefox.
//

button,
select {
  text-transform: none;
}

//
// 1. Avoid the WebKit bug in Android 4.0.* where (2) destroys native `audio`
//    and `video` controls.
// 2. Correct inability to style clickable `input` types in iOS.
// 3. Improve usability and consistency of cursor style between image-type
//    `input` and others.
//

button,
html input[type="button"], // 1
input[type="reset"],
input[type="submit"] {
  -webkit-appearance: button; // 2
  cursor: pointer; // 3
}

//
// Re-set default cursor for disabled elements.
//

button[disabled],
html input[disabled] {
  cursor: default;
}

//
// Remove inner padding and border in Firefox 4+.
//

button::-moz-focus-inner,
input::-moz-focus-inner {
  border: 0;
  padding: 0;
}

//
// Address Firefox 4+ setting `line-height` on `input` using `!important` in
// the UA stylesheet.
//

input {
  line-height: normal;
}

//
// It's recommended that you don't attempt to style these elements.
// Firefox's implementation doesn't respect box-sizing, padding, or width.
//
// 1. Address box sizing set to `content-box` in IE 8/9/10.
// 2. Remove excess padding in IE 8/9/10.
//

input[type="checkbox"],
input[type="radio"] {
  box-sizing: border-box; // 1
  padding: 0; // 2
}

//
// Fix the cursor style for Chrome's increment/decrement buttons. For certain
// `font-size` values of the `input`, it causes the cursor style of the
// decrement button to change from `default` to `text`.
//

input[type="number"]::-webkit-inner-spin-button,
input[type="number"]::-webkit-outer-spin-button {
  height: auto;
}

//
// 1. Address `appearance` set to `searchfield` in Safari and Chrome.
// 2. Address `box-sizing` set to `border-box` in Safari and Chrome.
//

input[type="search"] {
  -webkit-appearance: textfield; // 1
  box-sizing: content-box; //2
}

//
// Remove inner padding and search cancel button in Safari and Chrome on OS X.
// Safari (but not Chrome) clips the cancel button when the search input has
// padding (and `textfield` appearance).
//

input[type="search"]::-webkit-search-cancel-button,
input[type="search"]::-webkit-search-decoration {
  -webkit-appearance: none;
}

//
// Define consistent border, margin, and padding.
//

fieldset {
  border: 1px solid #c0c0c0;
  margin: 0 2px;
  padding: 0.35em 0.625em 0.75em;
}

//
// 1. Correct `color` not being inherited in IE 8/9/10/11.
// 2. Remove padding so people aren't caught out if they zero out fieldsets.
//

legend {
  border: 0; // 1
  padding: 0; // 2
}

//
// 1.Don't allow the user to zoom
// 2.Remove default vertical scrollbar in IE 8/9/10/11.
//

textarea {
  resize: none;
  overflow: auto;
}

//
// Don't inherit the `font-weight` (applied by a rule above).
// NOTE: the default cannot safely be changed in Chrome and Safari on OS X.
//

optgroup {
  font-weight: bold;
}

// Tables
// ==========================================================================

//
// Remove most spacing between table cells.
//

table {
  border-collapse: collapse;
  border-spacing: 0;
}

td,
th {
  padding: 0;
}


em, b, i {
  font-style: normal;
  font-weight: normal;
}


/* �������� */
input, select {
    vertical-align:baseline
}








================================================
FILE: asset/css/variables.less
================================================
//
// ==变量
// --------------------------------------------------


//== 颜色
//
//## 灰色及品牌标记颜色引导.

@gray-base:              #000;
@gray-darker:            lighten(@gray-base, 13.5%); // #222
@gray-dark:              lighten(@gray-base, 20%);   // #333
@gray-dim:               lighten(@gray-base, 25%);   // #404040  系统主要文字颜色
@gray:                   lighten(@gray-base, 33.5%); // #555
@gray-light:             lighten(@gray-base, 46.7%); // #777
@gray-minor:             lighten(@gray-base, 59.2%); // #979797  系统次要文字颜色
@gray-lighter:           lighten(@gray-base, 93.5%); // #eee



@brand-link:            #2577FF; //链接
@brand-hover:           #094D86;; //链接hover
@brand-success:         #3BA861; //状态成功、正常
@brand-warning:         #f0ad4e; //提示警告
@brand-danger:          #B01207; //危险、失败
@brand-forbid:          #b4b4b4; //不可用
@title-background:      #E9EBF1; //标题、条幅背景色



//== Scaffolding
//
//## Settings for some of the most global styles.

//** Background color for `<body>`.
@body-bg:               #f1f2f3;
//** Global text color on `<body>`.
@text-color:            @gray-dim;

//** Global textual link color.
@link-color:            @brand-link;
//** Link hover color set via `darken()` function.
@link-hover-color:      @brand-hover;
//** Link hover decoration.
@link-hover-decoration: underline;


//== Typography
//
//## Font, line-height, and color for body text, headings, and more.

@font-family-sans-serif:  "Microsoft Yahei", Arial , Tahoma , Helvetica, sans-serif;
@font-family-serif:       Georgia, "Times New Roman", Times, serif;
//** Default monospace fonts for `<code>`, `<kbd>`, and `<pre>`.
@font-family-monospace:   Menlo, Monaco, Consolas, "Courier New", monospace;
@font-family-base:        @font-family-sans-serif;

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

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

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

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



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

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

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

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

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

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

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


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


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

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

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

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

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

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

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

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

//图标距离

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


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

//消息圆角

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

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

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

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

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

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

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

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

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




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


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

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

const ENVS_ARR = [];

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

let currentEnv = process.env.NODE_ENV;

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

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

  return true;
}

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

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

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

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

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

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

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

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

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

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

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

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

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

  return devConfig;
}

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


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


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

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

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

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

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

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

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

module.exports = publicPath;


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


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

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

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

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


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

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

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


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

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


================================================
FILE: index.html
================================================
<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="utf-8">
  <link rel="shortcut icon" href="favicon.ico" />
  <title>PC端React+redux脚手架</title>
  <link rel="stylesheet" type="text/css" href="/dist/app.css">
</head>

<body >
  <div class="asset newAnt" id="app">
  </div>
  <script type="text/javascript" src="/dist/app.js"></script>
</body>

</html>

================================================
FILE: mock/config/mockConfig.json
================================================
{
    "dataSource": ["json", "template", "server"],
    "json": {
        "path": "/mock/data/",
        "wrap": true
    },
    "server": [{
        "host": "http://localhost:8080/mock",
        "serverParams": {
            "index": 1
        },
        "statusCode": [200],
        "rejectUnauthorized": false,
        "secureProtocol": "SSLv3_method",
        "cookie": "",
        "proxy": ""
    }, {
        "host": "http://localhost:8081/",
        "serverParams": {
            "index": 2
        },
        "statusCode": [200],
        "rejectUnauthorized": false,
        "secureProtocol": "SSLv3_method",
        "cookie": "",
        "proxy": ""
    }],
    "template": {
        "path": "/mock/template/"
    }

}

================================================
FILE: mock/data/skuunit/deleteSkuUnit.json
================================================
{
  "enabled": true,
  "value": "success",
  "success": {
    "flag": "0",
    "msg": [
      ""
    ],
    "data": []
  },
  "error": {
    "flag": "1",
    "msg": [
      "input error"
    ],
    "data": []
  }
}

================================================
FILE: mock/data/skuunit/showSkuUnitList.json
================================================
{
  "enabled": true,
  "value": "success",
  "success": {
    "flag": "0",
    "msg": ["系统繁忙"],
    "data": {
      "dataList": [
        {
          "skuUnitId": "185715847_1",
          "skuUnitName": "1单元名称",
          "isPause": 1,
          "skuSetId": "23489551",
          "skuSetName": "上海鲜花",
          "groupCount": 5,
          "planCount": 2,
          "denyKeyCount": 9,
          "checkStatus": 1,
          "checkOkCount": 3000,
          "checkRejectCount": 200,
          "ideas": [
            {
              "deveiceType": 2,
              "ideaType": 2
            }
          ]
        },
        {
          "skuUnitId": "185715847_2",
          "skuUnitName": "2单元名称2",
          "isPause": 0,
          "skuSetId": "23489556",
          "skuSetName": "沈阳鲜花",
          "groupCount": 5,
          "planCount": 2,
          "denyKeyCount": 7,
          "checkStatus": 1,
          "checkOkCount": 3000,
          "checkRejectCount": 200,
          "ideas": [
            {
              "deveiceType": 1,
              "ideaType": 2
            },
            {
              "deveiceType": 2,
              "ideaType": 1
            }
          ]
        },
        {
          "skuUnitId": "185715847_3",
          "skuUnitName": "3单元名称",
          "isPause": 0,
          "skuSetId": "23489555",
          "skuSetName": "兰州鲜花",
          "groupCount": 5,
          "planCount": 2,
          "denyKeyCount": 12,
          "checkStatus": 0,
          "checkOkCount": 3000,
          "checkRejectCount": 200,
          "ideas": [
            {
              "deveiceType": 2,
              "ideaType": 1
            }
          ]
        },
        {
          "skuUnitId": "185715847_4",
          "skuUnitName": "4单元名称",
          "isPause": 1,
          "skuSetId": "23489552",
          "skuSetName": "北京二手车",
          "groupCount": 5,
          "planCount": 2,
          "denyKeyCount": 4,
          "checkStatus": 1,
          "checkOkCount": 3000,
          "checkRejectCount": 200,
          "ideas": [
            {
              "deveiceType": 2,
              "ideaType": 2
            }
          ]
        },
        {
          "skuUnitId": "185715847_5",
          "skuUnitName": "5单元名称",
          "isPause": 0,
          "skuSetId": "23489553",
          "skuSetName": "香港鲜花",
          "groupCount": 5,
          "planCount": 2,
          "denyKeyCount": 7,
          "checkStatus": -1,
          "checkOkCount": 3000,
          "checkRejectCount": 200,
          "ideas": [
            {
              "deveiceType": 1,
              "ideaType": 1
            }
          ]
        }
      ],
      "totalNumber": 50
    }
  },
  "error": {
    "flag": "1",
    "msg": [
      "系统繁忙"
    ],
    "data": []
  }
}

================================================
FILE: mock/data/skuunit/updateSkuUnitPause.json
================================================
{
  "enabled": true,
  "value": "success",
  "success": {
    "flag": "0",
    "msg": [
      "修改失败"
    ],
    "data": []
  },
  "error": {
    "flag": "1",
    "msg": [
      "input error"
    ],
    "data": []
  }
}

================================================
FILE: mock/template/query/table.template
================================================
{
    'table|1-10': [
        {
            'id|100-1000': 100,
            'name|1': ['A','B','C','D','E','F','G','H','I','J','K','L','M','N'],
            'height|50-300': 50,
            'weight|50-100.1-10': 50,
            'age|1-100': 1,
            'email': '@EMAIL'
        }
    ]  
}

================================================
FILE: package.json
================================================
{
  "name": "react-starter",
  "description": "react+redux的脚手架,持续进行更新迭代",
  "version": "1.0.1",
  "main": "app.js",
  "scripts": {
    "start": "cross-env NODE_ENV=development webpack-dev-server --progress --colors",
    "profile": "cross-env NODE_ENV=profile webpack --progress --colors --display-modules",
    "qa": "cross-env NODE_ENV=qa webpack --progress --colors",
    "pub": "cross-env NODE_ENV=production webpack --progress --colors ",
    "lint": "eslint src"
  },
  "dependencies": {
    "antd": "^3.26.2",
    "axios": "^0.18.1",
    "babel-polyfill": "^6.26.0",
    "lodash": "^4.17.15",
    "moment": "^2.24.0",
    "prop-types": "^15.7.2",
    "react": "^16.12.0",
    "react-dom": "^16.12.0",
    "react-redux": "^5.1.2",
    "react-router-dom": "^5.1.2",
    "redux": "^4.0.4",
    "redux-thunk": "^2.2.0"
  },
  "devDependencies": {
    "@babel/cli": "^7.7.5",
    "@babel/core": "^7.7.5",
    "@babel/plugin-proposal-class-properties": "^7.7.4",
    "@babel/plugin-proposal-decorators": "^7.7.4",
    "@babel/plugin-proposal-export-default-from": "^7.7.4",
    "@babel/plugin-syntax-dynamic-import": "^7.7.4",
    "@babel/plugin-transform-runtime": "^7.7.6",
    "@babel/preset-env": "^7.7.6",
    "@babel/preset-react": "^7.7.4",
    "@hot-loader/react-dom": "^16.11.0",
    "autoprefixer": "^8.6.5",
    "babel-eslint": "^10.0.3",
    "babel-loader": "^8.0.6",
    "babel-plugin-import": "^1.13.0",
    "babel-plugin-transform-imports": "^1.5.1",
    "babel-plugin-transform-react-remove-prop-types": "^0.4.24",
    "clean-webpack-plugin": "^2.0.2",
    "css-loader": "^0.28.9",
    "eslint": "^5.16.0",
    "eslint-plugin-react": "^7.17.0",
    "eslint-plugin-react-hooks": "^1.7.0",
    "file-loader": "^1.1.11",
    "html-webpack-plugin": "^3.2.0",
    "html-withimg-loader": "^0.1.16",
    "less": "^3.10.3",
    "less-loader": "^4.1.0",
    "mini-css-extract-plugin": "^0.4.5",
    "postcss-loader": "^2.1.6",
    "react-hot-loader": "^4.12.18",
    "redux-logger": "^3.0.6",
    "url-loader": "^1.1.2",
    "cross-env": "^6.0.3",
    "webpack": "^4.41.2",
    "webpack-bundle-analyzer": "^3.6.0",
    "webpack-cli": "^3.3.10",
    "webpack-dev-server": "^3.9.0",
    "webpack-merge": "^4.2.2"
  },
  "repository": {
    "type": "git",
    "url": "https://github.com/zhangkun-Jser/react-starter.git"
  },
  "keywords": [],
  "author": "zk",
  "license": "ISC",
  "bugs": {
    "url": "https://github.com/zhangkun-Jser/react-starter/issues"
  },
  "homepage": "https://github.com/zhangkun-Jser/react-starter",
  "eslintIgnore": [
    "dist/**"
  ]
}


================================================
FILE: src/app.jsx
================================================
/*
 * 项目入口
 */
import 'babel-polyfill';
import React from 'react';
import ReactDOM from 'react-dom';
import { Provider } from 'react-redux';
import { HashRouter } from 'react-router-dom';

import '../asset/css/app.less';
import store from './redux/configureStore';
import Root from './routers';

ReactDOM.render(
  <Provider store={store}>
    <HashRouter>
      <Root />
    </HashRouter>
  </Provider>,
  document.getElementById('app'),
);


================================================
FILE: src/common/request.js
================================================
// 参考:https://ykloveyxk.github.io/2017/02/25/axios%E5%85%A8%E6%94%BB%E7%95%A5/

import OriginAxios from 'axios';
import { message } from 'antd';

const axios = OriginAxios.create({
  timeout: 20000,
});

export function get(url, data) {
  return axios.get(url, {
    params: data,
  });
}

// By default, axios serializes JavaScript objects to JSON
export function post(url, data) {
  return axios({
    url,
    method: 'post',
    data,
  });
}

// By default, axios serializes JavaScript objects to JSON
export function put(url, data) {
  return axios({
    url,
    method: 'put',
    data,
  });
}

export function del(url, data) {
  return axios({
    url,
    method: 'delete',
    data,
  });
}

// Add a request interceptor
axios.interceptors.request.use(
  function config(config) {
    // Do something before request is sent
    return config;
  },
  function error(error) {
    // Do something with request error
    console.log('request error, HTTP CODE: ', error.response.status);
    return Promise.reject(error);
  },
);

// 返回状态判断(添加响应拦截器)
axios.interceptors.response.use(
  res => {
    if (res.data && res.data.flag === 1) {
      let errorMsg = res.data.msg;
      message.error(errorMsg);
      return Promise.reject(errorMsg);
    }

    return res;
  },
  error => {
    // 用户登录的时候会拿到一个基础信息,比如用户名,token,过期时间戳
    // 直接丢localStorage或者sessionStorage
    if (error.response.status === 401) {
      // 若是接口访问的时候没有发现有鉴权的基础信息,直接返回登录页
      // history.push('login');
      window.location = '/login.html';
    }
  },
);

export default axios;


================================================
FILE: src/components/ErrorBoundary.jsx
================================================
// 错误边界
import React, { Component } from 'react';
import PropTypes from 'prop-types';

class ErrorBoundary extends Component {
  static propTypes = {
    children: PropTypes.oneOfType([PropTypes.any]),
  };

  constructor(props) {
    super(props);
    this.state = { hasError: false };
  }

  static getDerivedStateFromError() {
    // Update state so the next render will show the fallback UI.
    return { hasError: true };
  }

  componentDidCatch() {
    // You can also log the error to an error reporting service
    // logErrorToMyService(error, info);
    this.setState({
      hasError: true,
    });
  }

  render() {
    if (this.state.hasError) {
      // You can render any custom fallback UI
      return <h1 className="mt30 ml15">Something went wrong.</h1>;
    }

    return this.props.children;
  }
}

export default ErrorBoundary;


================================================
FILE: src/components/Layout/Footer.jsx
================================================
import React from 'react';
import styles from './index.css';

export default () => (
  <div className={styles['footer']}>Copyright© 2019 Sogou Biztech. All Rights Reserved.</div>
);


================================================
FILE: src/components/Layout/Header.jsx
================================================
import React from 'react';
import PropTypes from 'prop-types';
import {
  Layout, Icon, Menu, Dropdown, Avatar,
} from 'antd';

import styles from './index.css';
import AVATAR from '../../../asset/img/avatar.png';

const { Header } = Layout;

const HeaderComp = ({
  collapsed = false,
  onToggle,
}) => {
  const menu = (
    <Menu className={styles['header-menu']}>
      <Menu.Item key="user"><Icon type="user" />个人中心</Menu.Item>
      <Menu.Item disabled><Icon type="setting" />设置</Menu.Item>
      <Menu.Divider />
      <Menu.Item key="logout"><Icon type="logout" /> 退出登录</Menu.Item>
    </Menu>
  );

  const handleToggle = () => {
    onToggle && onToggle();
  };

  return (
    <Header className={styles.header}>
      <Icon
        className={styles.trigger}
        type={collapsed ? 'menu-unfold' : 'menu-fold'}
        onClick={handleToggle}
      />
      <div className={styles['header-right']}>
        <Dropdown overlay={menu}>
          <span className={styles['account']}>
            <Avatar size="small" src={AVATAR} />
            <span className={styles['user-name']}>dev</span>
          </span>
        </Dropdown>
      </div>
    </Header>
  );
};


HeaderComp.propTypes = {
  collapsed: PropTypes.bool,
  onToggle: PropTypes.func,
};

export default HeaderComp;


================================================
FILE: src/components/Layout/Sider.jsx
================================================
import React, { useState } from 'react';
import PropTypes from 'prop-types';
import { Link } from 'react-router-dom';
import { Menu, Layout, Icon } from 'antd';

import styles from './index.css';
import LOGO from '../../../asset/img/logo.svg';

const { Sider } = Layout;

const SiderComp = ({
  history,
  pathname,
  collapsed = false,
}) => {
  const pathArr = pathname.split('/').filter(i => i);
  const homeKey = 'home';

  const getDefaultSelectedKeys = () => {
    const selectKey = pathArr.length ? pathArr[pathArr.length - 1] : '';
    return selectKey ? [selectKey] : [homeKey];
  };

  const [selectedKeys, setSelectedKeys] = useState(getDefaultSelectedKeys());

  const handleSelect = ({ selectedKeys }) => {
    setSelectedKeys(selectedKeys);
    history.push(`/${selectedKeys}`);
  };

  window.onhashchange = () => {
    setSelectedKeys(getDefaultSelectedKeys());
  };

  return (
    <Sider
      trigger={null}
      collapsible
      collapsed={collapsed}
    >
      <div className={styles['menu-logo']}>
        <Link to="/">
          <img src={LOGO} />
          {!collapsed && <h1>脚手架</h1>}
        </Link>
      </div>
      <Menu
        theme="dark"
        mode="inline"
        selectedKeys={selectedKeys}
        onSelect={handleSelect}
      >
        <Menu.Item key="home">
          <Icon type="smile" />
          <span>欢迎</span>
        </Menu.Item>
        <Menu.Item key="nav2">
          <Icon type="video-camera" />
          <span>nav 2</span>
        </Menu.Item>
      </Menu>
    </Sider>
  );
};

SiderComp.propTypes = {
  history: PropTypes.objectOf(PropTypes.any),
  pathname: PropTypes.string,
  collapsed: PropTypes.bool,
};

export default SiderComp;


================================================
FILE: src/components/Layout/index.css
================================================
/* Header */
.header {
  background: #fff;
  padding: 0 20px;
  position: relative;
}

.header-right {
  position: absolute;
  top: 0;
  right: 24px;
  bottom: 0;
}

.header-menu {
  width: 150px;
}

.user-name {
  margin-left: 10px;
}

.account {
  color: rgba(0, 0, 0, 0.65);
  cursor: pointer;
  padding: 0 12px;
  display: inline-block;
  transition: all .3s;
  height: 100%;
}

.account i {
  font-size: 16px;
  vertical-align: middle;
}

.account:hover {
  background: #e6f7ff;
}

/* Sider */
.trigger {
  font-size: 18px;
  line-height: 64px;
  padding: 0 10px;
  cursor: pointer;
  transition: color .3s;
}

.trigger:hover {
  color: #1890ff;
}

.menu-logo {
  position: relative;
  height: 64px;
  padding-left: 24px;
  overflow: hidden;
  line-height: 64px;
  background: #001529;
  transition: all 0.3s;
}

.menu-logo a {
  color: #1890FF;
  text-decoration: none;
  background-color: transparent;
  outline: none;
  cursor: pointer;
  transition: color 0.3s;
}

.menu-logo img {
  display: inline-block;
  width: 32px;
  vertical-align: middle;
}

.menu-logo h1 {
  display: inline-block;
  margin: 0 0 0 12px;
  color: white;
  font-weight: 600;
  font-size: 20px;
  vertical-align: middle;
}


/* Footer */
.footer {
  text-align: center;
  font-size: 14px;
  padding: 20px;
}

================================================
FILE: src/components/Pages/index.css
================================================
.page-wrapper {
    margin: 16px 0px 16px 16px;
    line-height: 32px;
    overflow: hidden;
}

================================================
FILE: src/components/Pages/index.jsx
================================================
/**
 * 表格分页组件
 */
import React, { Component } from 'react';
import { Pagination } from 'antd';
import PropTypes from 'prop-types';
import styles from './index.css';

class Pages extends Component {
  static propTypes = {
    pageSizeOptions: PropTypes.arrayOf(PropTypes.string),
    defaultCurrent: PropTypes.number,
    total: PropTypes.number,
    current: PropTypes.number,
    pageSize: PropTypes.number,
    loadTable: PropTypes.func,
    style: PropTypes.objectOf(PropTypes.any),
  }

  static defaultProps = {
    pageSizeOptions: ['10', '20', '30', '40'],
    defaultCurrent: 1,
    total: 0,
    current: 1,
    pageSize: 10,
    style: {
      float: 'right',
    },
  };

  state = {
    current: this.props.current,
    pageSize: this.props.pageSize,
  };

  static getDerivedStateFromProps(props) {
    return {
      current: props.current,
      pageSize: props.pageSize,
      total: props.total,
    };
  }

  handlePageChange = (page, pageSize) => {
    this.loadTable({
      page,
      pageSize,
    });
  }

  handlePageSizeChange = (current, pageSize) => {
    this.loadTable({
      page: this.props.defaultCurrent,
      pageSize,
    });
  }

  loadTable({ page, pageSize }) {
    this.setState({
      current: page,
      pageSize,
    });
    this.props.loadTable(false, {
      page,
      pageSize,
    });
  }

  reset() {
    const { current, pageSize } = this.defaultProps;
    this.setState({ current, pageSize });
  }

  render() {
    const { total, pageSizeOptions, style } = this.props;
    const { current, pageSize } = this.state;

    return (
      <div className={styles['page-wrapper']}>
        <span>共{total}条</span>
        <Pagination
          showSizeChanger
          pageSizeOptions={pageSizeOptions}
          total={total}
          current={current}
          pageSize={pageSize}
          onChange={this.handlePageChange}
          onShowSizeChange={this.handlePageSizeChange}
          style={style}
        />
      </div>
    );
  }
}

export default Pages;


================================================
FILE: src/components/index.jsx
================================================
import Header from './Layout/Header';
import Sider from './Layout/Sider';
import Footer from './Layout/Footer';
import Pages from './Pages';
import ErrorBoundary from './ErrorBoundary';

export {
  Header,
  Sider,
  Footer,
  Pages,
  ErrorBoundary,
};


================================================
FILE: src/constants/constant.js
================================================
export const ACTION_TYPE_ADD_ERROR = 'app/ADD_ERROR';

// 分页默认信息
export const PAGE_DATA = {
  pageNo: 1,
  pageSize: 10,
};


================================================
FILE: src/constants/url.js
================================================
// 单元列表查询展示
export const SHOW_UNIT_LIST_URL = '/skuunit/showSkuUnitList.action';
// 删除单元
export const DELETE_UNIT_URL = '/skuunit/deleteSkuUnit.action';


================================================
FILE: src/redux/configureStore.js
================================================
import { compose, createStore, applyMiddleware } from 'redux';
import logger from 'redux-logger';
import thunk from 'redux-thunk';

import reducer from './reducers';

const middleware = [thunk];

const isNotProduction = process.env.NODE_ENV !== 'production';
if (isNotProduction) {
  middleware.push(logger);
}

// 判断当前浏览器是否安装了 REDUX_DEVTOOL 插件
const shouldCompose = isNotProduction
  && typeof window === 'object'
  && window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__;

const composeEnhancers = shouldCompose
  ? window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__({
    // Specify here name, actionsBlacklist, actionsCreators and other options
  })
  : compose;

/*
   调用 applyMiddleware ,使用 middleware 来增强 createStore
*/
const configureStore = composeEnhancers(applyMiddleware(...middleware))(createStore);

const store = configureStore(reducer);
window.Store = store;

if (module.hot) {
  module.hot.accept('./reducers.js', () => {
    console.log('reducer changed');
    store.replaceReducer(require('./reducers').default);
  });
}

export default store;


================================================
FILE: src/redux/reducers.js
================================================
import { combineReducers } from 'redux';
import skuunit from './skuunit/skuunit';

const reducers = {
  skuunit,
};

export default combineReducers(reducers);


================================================
FILE: src/redux/skuunit/api.js
================================================
import { get, post } from 'common/request';
import {
  SHOW_UNIT_LIST_URL,
  DELETE_UNIT_URL,
} from 'constants/url';

/**
 * 单元列表查询展示
 * @param {*} params
 */
export function showSkuunitListApi(params) {
  if (!params) {
    return Promise.reject('params is wrong');
  }
  return get(SHOW_UNIT_LIST_URL, params)
    .then(res => {
      // 开发时调试等待效果;
      return new Promise((resolve) => {
        setTimeout(() => {
          return resolve(res);
        }, 1000);
      });
    })
    .then(res => res.data);
}

/**
 * 删除单元
 * @param {} params
 */
export function deleteSkuUnitApi(params) {
  if (!params) {
    return Promise.reject('params is wrong');
  }
  return post(DELETE_UNIT_URL, params)
    .then(res => res.data);
}


================================================
FILE: src/redux/skuunit/skuunit.js
================================================
/**
 * author: niuxiaoyu
 * description: 单元
 * date: 2018/6/6
 */
import { ACTION_TYPE_ADD_ERROR } from 'constants/constant';

import {
  showSkuunitListApi,
  deleteSkuUnitApi,
} from './api';

const SHOW_LOADING = 'skuunit/SHOW_LOADING';
const HIDE_LOADING = 'skuunit/HIDE_LOADING';
const SHOW_SKU_UNIT_LIST = 'skuunit/SHOW_SKU_UNIT_LIST';
const DELETE_SKUUNIT = 'skuunit/DELETE_SKUUNIT';

const initialState = {
  dataList: [],
  isLoading: false,
  error: '',
};

export default function reducer(state = initialState, action = {}) {
  switch (action.type) {
    case SHOW_LOADING:
      return Object.assign({}, state, {
        isLoading: true,
      });
    case HIDE_LOADING:
      return Object.assign({}, state, {
        isLoading: false,
      });
    case SHOW_SKU_UNIT_LIST:
      return Object.assign({}, state, {
        dataList: action.payload.data.dataList,
        totalNumber: action.payload.data.totalNumber,
      });
    case DELETE_SKUUNIT:
      return Object.assign({}, state, {
        dataList: _deleteskuUnits(state.dataList, action.payload),
      });
    default:
      return state;
  }
}

/**
 * 删除单元
 * @param {*} source
 * @param {*} skuUnitIdList
 */
const _deleteskuUnits = (source, params) => {
  const { skuUnitIdList } = params;
  const newSource = [];
  for (let i = 0; i < source.length; i++) {
    const target = skuUnitIdList.find(skuUnitId => skuUnitId === source[i].skuUnitId);
    if (!target) {
      newSource.push(source[i]);
    }
  }
  return newSource;
};

/**
 * 单元列表查询展示
 * @param {*} params
 */
export function showSkuunitList(planName) {
  return async dispatch => {
    try {
      dispatch({ type: SHOW_LOADING });
      const res = await showSkuunitListApi(planName);
      await dispatch({ type: SHOW_SKU_UNIT_LIST, payload: res });
      dispatch({ type: HIDE_LOADING });
    } catch (err) {
      dispatch({ type: HIDE_LOADING });
      console.log(err);
    }
  };
}

/**
 * 删除单元
 * @param {} skuUnitIdList
 */
export function deleteSkuUnit(skuUnitIdList) {
  return async dispatch => {
    try {
      const param = { skuUnitIdList };
      const res = await deleteSkuUnitApi(param);
      dispatch({ type: DELETE_SKUUNIT, payload: param, res });
    } catch (err) {
      dispatch({ type: ACTION_TYPE_ADD_ERROR, payload: { errorMsg: err } });
    }
  };
}


================================================
FILE: src/routers/PrimaryLayout.jsx
================================================
import React, { Suspense, useState, lazy } from 'react';
import PropTypes from 'prop-types';
import { Switch, Route, Redirect } from 'react-router-dom';
import { Layout, Spin } from 'antd';

import styles from './index.less';
import {
  Header, Sider, Footer, ErrorBoundary,
} from 'components';
const { Content } = Layout;

const Skuunit = lazy(() => import(/* webpackChunkName: "home" */ './Skuunit'));

const PrimaryLayout = (props) => {
  const [collapsed, setCollapsed] = useState(false);

  const toggleCollapse = () => {
    setCollapsed(!collapsed);
  };

  return (
    <Layout>
      <Sider
        history={props.history}
        pathname={props.location.pathname}
        collapsed={collapsed}
      />
      <Layout>
        <Header
          collapsed={collapsed}
          onToggle={toggleCollapse}
        />
        <Content>
          <div className={styles['main-content']}>
            <Suspense fallback={<Spin />}>
              <ErrorBoundary>
                <Switch>
                  <Route path="/home" component={Skuunit} />
                  <Route path="/nav2" component={() => <div>nav2</div>} />
                  <Redirect to="/home" />
                </Switch>
              </ErrorBoundary>
            </Suspense>
          </div>
        </Content>
        <Footer />
      </Layout>
    </Layout>
  );
};

PrimaryLayout.propTypes = {
  history: PropTypes.objectOf(PropTypes.any),
  location: PropTypes.objectOf(PropTypes.any),
};

export default PrimaryLayout;


================================================
FILE: src/routers/Skuunit/columns.jsx
================================================
import React from 'react';

const getColumns = ({
  handleDelete,
}) => {
  return [
    {
      title: '名称',
      dataIndex: 'skuUnitName',
      width: 230,
      key: 'skuUnitName',
    },
    {
      title: '集合名称',
      dataIndex: 'skuSetName',
      key: 'skuSetName',
    },
    {
      title: '操作',
      key: 'action',
      width: 100,
      render: (text, record) => (
        <span>
          <a href="javascript:;" onClick={() => handleDelete(record.skuUnitId, record.skuUnitName)}>删除</a>
        </span>
      ),
    },
  ];
};

export default getColumns;


================================================
FILE: src/routers/Skuunit/index.jsx
================================================
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import {
  Table, Modal,
} from 'antd';
import { bindActionCreators } from 'redux';
import { connect } from 'react-redux';

import { Pages } from 'components';
import getColumns from './columns';

import { PAGE_DATA } from 'constants/constant';
import {
  showSkuunitList,
  deleteSkuUnit,
} from 'reduxDir/skuunit/skuunit';

const confirm = (msg, onOk, onCancel) => {
  Modal.confirm({
    title: msg,
    onOk() {
      onOk && onOk();
    },
    onCancel() {
      onCancel && onCancel();
    },
  });
};

class Skuunit extends Component {
  static propTypes = {
    data: PropTypes.shape({
      dataList: PropTypes.arrayOf(PropTypes.any),
      isLoading: PropTypes.bool,
      totalNumber: PropTypes.number,
    }),
    showSkuunitList: PropTypes.func,
    deleteSkuUnit: PropTypes.func,
  };

  state = {
    pageNo: PAGE_DATA.pageNo,
    pageSize: PAGE_DATA.pageSize,
  };

  componentDidMount() {
    this.loadTable();
  }

  getFetchListParams = () => {
    const {
      page, pageSize,
    } = this.state;

    return {
      page,
      pageSize,
    };
  }

  /**
   * @resetPage: 表示是否重置Pages组件
   * @pageInfo: Pages组件的参数
   */
  loadTable = (resetPage = true, pageInfo) => {
    let pageNo;
    let pageSize;
    if (!resetPage) {
      pageNo = pageInfo ? pageInfo.page : this.state.pageNo;
      pageSize = pageInfo ? pageInfo.pageSize : this.state.pageSize;
    } else {
      pageNo = PAGE_DATA.pageNo;
      pageSize = PAGE_DATA.pageSize;
    }

    this.setState({
      pageNo,
      pageSize,
    }, () => this.props.showSkuunitList(this.getFetchListParams()));
  }

  deleteSingleUnit = (unitId, unitName) => {
    confirm(`删除单元:${unitName} ?`, () => {
      this.props.deleteSkuUnit([unitId]);
    });
  }

  render() {
    const { dataList, isLoading, totalNumber } = this.props.data;
    const {
      pageNo, pageSize,
    } = this.state;

    const columns = getColumns({
      handleDelete: this.deleteSingleUnit,
    });

    return (
      <div>
        <Table
          columns={columns}
          dataSource={dataList}
          loading={isLoading}
          bordered
          pagination={false}
          rowKey="skuUnitId"
        />
        <Pages
          total={totalNumber}
          current={pageNo}
          pageSize={pageSize}
          loadTable={this.loadTable}
        />
      </div>
    );
  }
}


export default connect(
  state => ({
    data: state.skuunit,
  }),
  dispatch => bindActionCreators({
    showSkuunitList,
    deleteSkuUnit,
  }, dispatch),
)(Skuunit);


================================================
FILE: src/routers/index.jsx
================================================
/*
 * 路由主入口:可以在该页面进行登录权限控制
 */
import React from 'react';
import {
  Route, Switch, withRouter,
} from 'react-router-dom';
import { hot } from 'react-hot-loader';

import { LocaleProvider } from 'antd';
import zhCN from 'antd/lib/locale-provider/zh_CN';
// UI示范,如果不需要目前的UI框架,直接替换该组件即可
import PrimaryLayout from './PrimaryLayout';

const Root = (props) => {
  // const { location, history, match } = props;
  return (
    <Switch>
      <Route path="/" render={() => <LocaleProvider locale={zhCN}><PrimaryLayout {...props} /></LocaleProvider>} />
    </Switch>
  );
};

export default hot(module)(withRouter(Root));


================================================
FILE: src/routers/index.less
================================================
.main-content {
	margin: 24px 16px;
	padding: 24px;
	background: #fff;
	min-height: 280;
}

================================================
FILE: template.html
================================================
<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="utf-8">
  <link rel="shortcut icon" href="favicon.ico" />
  <title>PC端React+redux脚手架</title>
</head>

<body>
  <div class="asset newAnt" id="app">
  </div>
</body>

</html>

================================================
FILE: webpack.config.js
================================================
const merge = require('webpack-merge');
const Env = require('./build/env');
const baseConfig = require('./build/webpack.base.conf');
const prodConfig = require('./build/webpack.prod.conf');
const devConfig = require('./build/webpack.dev.conf');

let config = devConfig;

if (Env.isQA() || Env.isProduction()) {
  config = prodConfig;
}

module.exports = merge(baseConfig, config);
Download .txt
gitextract_wmw74cji/

├── .babelrc
├── .eslintignore
├── .eslintrc.js
├── .gitignore
├── README.md
├── asset/
│   └── css/
│       ├── app.less
│       ├── base.less
│       ├── icon.less
│       ├── reset.less
│       ├── variables.less
│       └── vendor_antd.less
├── build/
│   ├── env.js
│   ├── postcss.config.js
│   ├── publicPath.js
│   ├── theme.js
│   ├── webpack.base.conf.js
│   ├── webpack.dev.conf.js
│   └── webpack.prod.conf.js
├── index.html
├── mock/
│   ├── config/
│   │   └── mockConfig.json
│   ├── data/
│   │   └── skuunit/
│   │       ├── deleteSkuUnit.json
│   │       ├── showSkuUnitList.json
│   │       └── updateSkuUnitPause.json
│   └── template/
│       └── query/
│           └── table.template
├── package.json
├── src/
│   ├── app.jsx
│   ├── common/
│   │   └── request.js
│   ├── components/
│   │   ├── ErrorBoundary.jsx
│   │   ├── Layout/
│   │   │   ├── Footer.jsx
│   │   │   ├── Header.jsx
│   │   │   ├── Sider.jsx
│   │   │   └── index.css
│   │   ├── Pages/
│   │   │   ├── index.css
│   │   │   └── index.jsx
│   │   └── index.jsx
│   ├── constants/
│   │   ├── constant.js
│   │   └── url.js
│   ├── redux/
│   │   ├── configureStore.js
│   │   ├── reducers.js
│   │   └── skuunit/
│   │       ├── api.js
│   │       └── skuunit.js
│   └── routers/
│       ├── PrimaryLayout.jsx
│       ├── Skuunit/
│       │   ├── columns.jsx
│       │   └── index.jsx
│       ├── index.jsx
│       └── index.less
├── template.html
└── webpack.config.js
Download .txt
SYMBOL INDEX (42 symbols across 10 files)

FILE: build/env.js
  constant ENVS (line 2) | const ENVS = {
  constant ENVS_ARR (line 9) | const ENVS_ARR = [];
  function checkEnv (line 26) | function checkEnv(env) {
  function getEnvs (line 67) | function getEnvs() {
  function setEnv (line 75) | function setEnv(env) {
  function env (line 109) | function env(devConfig, pubConfig) {

FILE: build/publicPath.js
  constant PRODUCT_STATIC_URL (line 5) | let PRODUCT_STATIC_URL = 'http://static.hello.com';
  constant QA_STATIC_URL (line 6) | let QA_STATIC_URL = 'http://qa.static.hello.com';
  constant PROFILE_STATIC_URL (line 7) | let PROFILE_STATIC_URL = 'http://dev.static.hello.com';
  constant DEV_STATIC_URL (line 8) | let DEV_STATIC_URL = '/dist';

FILE: src/common/request.js
  function get (line 10) | function get(url, data) {
  function post (line 17) | function post(url, data) {
  function put (line 26) | function put(url, data) {
  function del (line 34) | function del(url, data) {

FILE: src/components/ErrorBoundary.jsx
  class ErrorBoundary (line 5) | class ErrorBoundary extends Component {
    method constructor (line 10) | constructor(props) {
    method getDerivedStateFromError (line 15) | static getDerivedStateFromError() {
    method componentDidCatch (line 20) | componentDidCatch() {
    method render (line 28) | render() {

FILE: src/components/Pages/index.jsx
  class Pages (line 9) | class Pages extends Component {
    method getDerivedStateFromProps (line 36) | static getDerivedStateFromProps(props) {
    method loadTable (line 58) | loadTable({ page, pageSize }) {
    method reset (line 69) | reset() {
    method render (line 74) | render() {

FILE: src/constants/constant.js
  constant ACTION_TYPE_ADD_ERROR (line 1) | const ACTION_TYPE_ADD_ERROR = 'app/ADD_ERROR';
  constant PAGE_DATA (line 4) | const PAGE_DATA = {

FILE: src/constants/url.js
  constant SHOW_UNIT_LIST_URL (line 2) | const SHOW_UNIT_LIST_URL = '/skuunit/showSkuUnitList.action';
  constant DELETE_UNIT_URL (line 4) | const DELETE_UNIT_URL = '/skuunit/deleteSkuUnit.action';

FILE: src/redux/skuunit/api.js
  function showSkuunitListApi (line 11) | function showSkuunitListApi(params) {
  function deleteSkuUnitApi (line 31) | function deleteSkuUnitApi(params) {

FILE: src/redux/skuunit/skuunit.js
  constant SHOW_LOADING (line 13) | const SHOW_LOADING = 'skuunit/SHOW_LOADING';
  constant HIDE_LOADING (line 14) | const HIDE_LOADING = 'skuunit/HIDE_LOADING';
  constant SHOW_SKU_UNIT_LIST (line 15) | const SHOW_SKU_UNIT_LIST = 'skuunit/SHOW_SKU_UNIT_LIST';
  constant DELETE_SKUUNIT (line 16) | const DELETE_SKUUNIT = 'skuunit/DELETE_SKUUNIT';
  function reducer (line 24) | function reducer(state = initialState, action = {}) {
  function showSkuunitList (line 69) | function showSkuunitList(planName) {
  function deleteSkuUnit (line 87) | function deleteSkuUnit(skuUnitIdList) {

FILE: src/routers/Skuunit/index.jsx
  method onOk (line 21) | onOk() {
  method onCancel (line 24) | onCancel() {
  class Skuunit (line 30) | class Skuunit extends Component {
    method componentDidMount (line 46) | componentDidMount() {
    method render (line 88) | render() {
Condensed preview — 48 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (109K chars).
[
  {
    "path": ".babelrc",
    "chars": 969,
    "preview": "{\n  \"presets\": [\n    \"@babel/preset-env\",\n    \"@babel/preset-react\"\n  ],\n  \"plugins\": [\n    [\n      \"@babel/plugin-propo"
  },
  {
    "path": ".eslintignore",
    "chars": 17,
    "preview": "node_modules\ndist"
  },
  {
    "path": ".eslintrc.js",
    "chars": 32312,
    "preview": "module.exports = {\n  \"env\": {\n    \"node\": true,\n    \"browser\": true,\n    \"es6\": true,\n    \"commonjs\": true,\n  },\n  \"pars"
  },
  {
    "path": ".gitignore",
    "chars": 149,
    "preview": "node_modules\nnpm-debug.log\nyarn-error.log\ntarget\nnode\nsrc/_tmp\nstatic\ndist\ncoverage\nrekit_temp_app\n_book\n.nyc_output\n.tm"
  },
  {
    "path": "README.md",
    "chars": 5401,
    "preview": "# react-starter\n\nPC 端 react+redux 的脚手架,持续进行更新迭代。\n\n\n## 特点\n\n1. 当中集成了当前react生态的几个主流技术栈:\n  - react 16.12.0\n  - ant design 3."
  },
  {
    "path": "asset/css/app.less",
    "chars": 145,
    "preview": "/* 样式文件入口 */\n@import 'reset.less';\n@import 'variables.less';\n@import 'icon.less';\n@import 'base.less';\n\n// 样式覆盖\n@import "
  },
  {
    "path": "asset/css/base.less",
    "chars": 568,
    "preview": "/* 基础样式 */\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"
  },
  {
    "path": "asset/css/icon.less",
    "chars": 758,
    "preview": "/* iconfont样式 */\n@font-face {\n  font-family: \"iconfont\";\n  // src: url('../font/iconfont.eot'); /* IE9*/\n  // src: url('"
  },
  {
    "path": "asset/css/reset.less",
    "chars": 7497,
    "preview": "/* 样式重置文件 */\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"
  },
  {
    "path": "asset/css/variables.less",
    "chars": 8186,
    "preview": "//\n// ==变量\n// --------------------------------------------------\n\n\n//== 颜色\n//\n//## 灰色及品牌标记颜色引导.\n\n@gray-base:            "
  },
  {
    "path": "asset/css/vendor_antd.less",
    "chars": 71,
    "preview": ".newAnt {\n  .ant-layout.ant-layout-has-sider {\n    height: 100%;\n  }\n}\n"
  },
  {
    "path": "build/env.js",
    "chars": 2447,
    "preview": "\nconst ENVS = {\n  PUB: ['pub', 'production'],\n  QA: 'qa',\n  PROFILE: 'profile',\n  DEV: ['dev', 'development'],\n};\n\nconst"
  },
  {
    "path": "build/postcss.config.js",
    "chars": 69,
    "preview": "module.exports = {\n  plugins: [\n    require('autoprefixer'),\n  ],\n};\n"
  },
  {
    "path": "build/publicPath.js",
    "chars": 725,
    "preview": "let pkg = require('../package.json');\nconst Env = require('./env');\n\n// 不同环境下的静态资源的url根路径\nlet PRODUCT_STATIC_URL = 'http"
  },
  {
    "path": "build/theme.js",
    "chars": 98,
    "preview": "module.exports = {\n  'font-family': 'Microsoft Yahei, Arial , Tahoma , Helvetica, sans-serif',\n};\n"
  },
  {
    "path": "build/webpack.base.conf.js",
    "chars": 3702,
    "preview": "const path = require('path');\nconst webpack = require('webpack');\n\nconst MiniCssExtractPlugin = require('mini-css-extrac"
  },
  {
    "path": "build/webpack.dev.conf.js",
    "chars": 679,
    "preview": "let mockport = 8000;\n\nprocess.argv.forEach(function setMockPort(val, index) {\n  if (val === '--env.mockport') {\n    mock"
  },
  {
    "path": "build/webpack.prod.conf.js",
    "chars": 875,
    "preview": "const path = require('path');\nconst HtmlWebpackPlugin = require('html-webpack-plugin');\n// const BundleAnalyzerPlugin = "
  },
  {
    "path": "index.html",
    "chars": 356,
    "preview": "<!DOCTYPE html>\n<html lang=\"en\">\n\n<head>\n  <meta charset=\"utf-8\">\n  <link rel=\"shortcut icon\" href=\"favicon.ico\" />\n  <t"
  },
  {
    "path": "mock/config/mockConfig.json",
    "chars": 727,
    "preview": "{\n    \"dataSource\": [\"json\", \"template\", \"server\"],\n    \"json\": {\n        \"path\": \"/mock/data/\",\n        \"wrap\": true\n  "
  },
  {
    "path": "mock/data/skuunit/deleteSkuUnit.json",
    "chars": 214,
    "preview": "{\n  \"enabled\": true,\n  \"value\": \"success\",\n  \"success\": {\n    \"flag\": \"0\",\n    \"msg\": [\n      \"\"\n    ],\n    \"data\": []\n "
  },
  {
    "path": "mock/data/skuunit/showSkuUnitList.json",
    "chars": 2754,
    "preview": "{\n  \"enabled\": true,\n  \"value\": \"success\",\n  \"success\": {\n    \"flag\": \"0\",\n    \"msg\": [\"系统繁忙\"],\n    \"data\": {\n      \"dat"
  },
  {
    "path": "mock/data/skuunit/updateSkuUnitPause.json",
    "chars": 218,
    "preview": "{\n  \"enabled\": true,\n  \"value\": \"success\",\n  \"success\": {\n    \"flag\": \"0\",\n    \"msg\": [\n      \"修改失败\"\n    ],\n    \"data\": "
  },
  {
    "path": "mock/template/query/table.template",
    "chars": 293,
    "preview": "{\n    'table|1-10': [\n        {\n            'id|100-1000': 100,\n            'name|1': ['A','B','C','D','E','F','G','H','"
  },
  {
    "path": "package.json",
    "chars": 2574,
    "preview": "{\n  \"name\": \"react-starter\",\n  \"description\": \"react+redux的脚手架,持续进行更新迭代\",\n  \"version\": \"1.0.1\",\n  \"main\": \"app.js\",\n  \"s"
  },
  {
    "path": "src/app.jsx",
    "chars": 442,
    "preview": "/*\n * 项目入口\n */\nimport 'babel-polyfill';\nimport React from 'react';\nimport ReactDOM from 'react-dom';\nimport { Provider }"
  },
  {
    "path": "src/common/request.js",
    "chars": 1559,
    "preview": "// 参考:https://ykloveyxk.github.io/2017/02/25/axios%E5%85%A8%E6%94%BB%E7%95%A5/\n\nimport OriginAxios from 'axios';\nimport "
  },
  {
    "path": "src/components/ErrorBoundary.jsx",
    "chars": 850,
    "preview": "// 错误边界\nimport React, { Component } from 'react';\nimport PropTypes from 'prop-types';\n\nclass ErrorBoundary extends Compo"
  },
  {
    "path": "src/components/Layout/Footer.jsx",
    "chars": 182,
    "preview": "import React from 'react';\nimport styles from './index.css';\n\nexport default () => (\n  <div className={styles['footer']}"
  },
  {
    "path": "src/components/Layout/Header.jsx",
    "chars": 1291,
    "preview": "import React from 'react';\nimport PropTypes from 'prop-types';\nimport {\n  Layout, Icon, Menu, Dropdown, Avatar,\n} from '"
  },
  {
    "path": "src/components/Layout/Sider.jsx",
    "chars": 1698,
    "preview": "import React, { useState } from 'react';\nimport PropTypes from 'prop-types';\nimport { Link } from 'react-router-dom';\nim"
  },
  {
    "path": "src/components/Layout/index.css",
    "chars": 1290,
    "preview": "/* Header */\n.header {\n  background: #fff;\n  padding: 0 20px;\n  position: relative;\n}\n\n.header-right {\n  position: absol"
  },
  {
    "path": "src/components/Pages/index.css",
    "chars": 94,
    "preview": ".page-wrapper {\n    margin: 16px 0px 16px 16px;\n    line-height: 32px;\n    overflow: hidden;\n}"
  },
  {
    "path": "src/components/Pages/index.jsx",
    "chars": 2018,
    "preview": "/**\n * 表格分页组件\n */\nimport React, { Component } from 'react';\nimport { Pagination } from 'antd';\nimport PropTypes from 'pr"
  },
  {
    "path": "src/components/index.jsx",
    "chars": 254,
    "preview": "import Header from './Layout/Header';\nimport Sider from './Layout/Sider';\nimport Footer from './Layout/Footer';\nimport P"
  },
  {
    "path": "src/constants/constant.js",
    "chars": 124,
    "preview": "export const ACTION_TYPE_ADD_ERROR = 'app/ADD_ERROR';\n\n// 分页默认信息\nexport const PAGE_DATA = {\n  pageNo: 1,\n  pageSize: 10,"
  },
  {
    "path": "src/constants/url.js",
    "chars": 153,
    "preview": "// 单元列表查询展示\nexport const SHOW_UNIT_LIST_URL = '/skuunit/showSkuUnitList.action';\n// 删除单元\nexport const DELETE_UNIT_URL = "
  },
  {
    "path": "src/redux/configureStore.js",
    "chars": 1047,
    "preview": "import { compose, createStore, applyMiddleware } from 'redux';\nimport logger from 'redux-logger';\nimport thunk from 'red"
  },
  {
    "path": "src/redux/reducers.js",
    "chars": 159,
    "preview": "import { combineReducers } from 'redux';\nimport skuunit from './skuunit/skuunit';\n\nconst reducers = {\n  skuunit,\n};\n\nexp"
  },
  {
    "path": "src/redux/skuunit/api.js",
    "chars": 731,
    "preview": "import { get, post } from 'common/request';\nimport {\n  SHOW_UNIT_LIST_URL,\n  DELETE_UNIT_URL,\n} from 'constants/url';\n\n/"
  },
  {
    "path": "src/redux/skuunit/skuunit.js",
    "chars": 2322,
    "preview": "/**\n * author: niuxiaoyu\n * description: 单元\n * date: 2018/6/6\n */\nimport { ACTION_TYPE_ADD_ERROR } from 'constants/const"
  },
  {
    "path": "src/routers/PrimaryLayout.jsx",
    "chars": 1500,
    "preview": "import React, { Suspense, useState, lazy } from 'react';\nimport PropTypes from 'prop-types';\nimport { Switch, Route, Red"
  },
  {
    "path": "src/routers/Skuunit/columns.jsx",
    "chars": 571,
    "preview": "import React from 'react';\n\nconst getColumns = ({\n  handleDelete,\n}) => {\n  return [\n    {\n      title: '名称',\n      data"
  },
  {
    "path": "src/routers/Skuunit/index.jsx",
    "chars": 2599,
    "preview": "import React, { Component } from 'react';\nimport PropTypes from 'prop-types';\nimport {\n  Table, Modal,\n} from 'antd';\nim"
  },
  {
    "path": "src/routers/index.jsx",
    "chars": 615,
    "preview": "/*\n * 路由主入口:可以在该页面进行登录权限控制\n */\nimport React from 'react';\nimport {\n  Route, Switch, withRouter,\n} from 'react-router-dom"
  },
  {
    "path": "src/routers/index.less",
    "chars": 90,
    "preview": ".main-content {\n\tmargin: 24px 16px;\n\tpadding: 24px;\n\tbackground: #fff;\n\tmin-height: 280;\n}"
  },
  {
    "path": "template.html",
    "chars": 230,
    "preview": "<!DOCTYPE html>\n<html lang=\"en\">\n\n<head>\n  <meta charset=\"utf-8\">\n  <link rel=\"shortcut icon\" href=\"favicon.ico\" />\n  <t"
  },
  {
    "path": "webpack.config.js",
    "chars": 381,
    "preview": "const merge = require('webpack-merge');\nconst Env = require('./build/env');\nconst baseConfig = require('./build/webpack."
  }
]

About this extraction

This page contains the full source code of the zhangkun-Jser/react-kit GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 48 files (89.8 KB), approximately 29.3k tokens, and a symbol index with 42 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!