<|start_filename|>templates/react-admin/src/view/form/redux/selectors.js<|end_filename|>
import { createSelector } from 'reselect';

export const formviewSelector = state => state.formview;
export const stepSelector = createSelector(formviewSelector, formview => (formview.step));
export const advancedSelector = createSelector(formviewSelector, formview => (formview.advanced));


<|start_filename|>templates/react-admin/config/webpack.config.dev.js<|end_filename|>
/**
 * @file webpack 开发模式配置
 * @see https://doc.webpack-china.org/configuration/
 */
const autoprefixer = require('autoprefixer');
const path = require('path');
const webpack = require('webpack');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const CaseSensitivePathsPlugin = require('case-sensitive-paths-webpack-plugin');
const InterpolateHtmlPlugin = require('react-dev-utils/InterpolateHtmlPlugin');
const WatchMissingNodeModulesPlugin = require('react-dev-utils/WatchMissingNodeModulesPlugin');
const eslintFormatter = require('react-dev-utils/eslintFormatter');
const ModuleScopePlugin = require('react-dev-utils/ModuleScopePlugin');
const getClientEnvironment = require('./env');
const paths = require('./paths');

// Webpack 使用 `publicPath` 来确定应用程序在哪里发送。
// 在开发模式中，我们始终从 root 上服务。这使配置更容易。
const publicPath = '/';
// `publicUrl` 就像`publicPath`，但是我们将把它提供给我们的应用程序，
// 就像 ％PUBLIC_URL％ 在 'index.html` 和 'process.env.PUBLIC_URL` 在 JavaScript 中 。
// 省略尾随斜线，%PUBLIC_PATH%/xyz 看起来比 %PUBLIC_PATH%xyz 好。
const publicUrl = '';
// 获取环境变量以注入到应用程序中。
const env = getClientEnvironment(publicUrl);

// 这是开发模式配置。
// 它专注于开发人员的经验和快速的重新构建。
// 与生产模式配置不同，它存在于一个单独的文件中。
module.exports = {

  // 通过在浏览器调试工具(browser devtools)中添加元信息(meta info)增强调试
  devtool: 'cheap-module-source-map',

  // webpack 打包入口
  // 这意味着它们将是 JS 捆绑包中包含的根
  // 前两个入口点启用 "hot" CSS 和自动刷新 JS。
  entry: [

    // 我们默认加载几个 polyfills
    // require.resolve('./polyfills'),

    // 为 WebpackDevServer 添加备用客户端。
    // 客户端的工作是通过 socket 连接到 WebpackDevServer，并获得有关更改的通知。
    // 保存文件时，客户端将应用热更新（在更改CSS的情况下）或刷新页面（如果是JS更改）。
    // 当您发出语法错误时，此客户端将显示语法错误覆盖。
    // 注意：代替默认的 WebpackDevServer 客户端，我们使用自定义的方式为创建 React App 用户带来更好的体验。
    // 如果您喜欢 stock 客户端，您可以用以下两行代替以下行：
    // require.resolve('webpack-dev-server/client') + '?/',
    // require.resolve('webpack/hot/dev-server'),
    require.resolve('react-dev-utils/webpackHotDevClient'),

    // 您的应用程序的代码入口
    // 我们最后包含应用程序代码，以便如果在初始化期间有运行时错误，
    // 它不会破坏 WebpackDevServer 客户端，并且更改 JS 代码仍将触发刷新。
    paths.appIndexJs
  ],

  // webpack 如何输出结果的相关选项
  output: {

    // 所有输出文件的目标路径
    // 必须是绝对路径（使用 Node.js 的 path 模块）
    // 下一行未在 dev 中使用，但是没有它 WebpackDevServer 会崩溃：
    path: paths.appBuild,

    // 在输出中添加 /* filename */ 注释来生成require（）。
    pathinfo: true,

    // 「入口分块(entry chunk)」的文件名模板
    // 这不会产生真实的文件。
    // 这只是WebpackDevServer在开发中提供的虚拟路径。
    // 这是包含所有入口点的代码和Webpack运行时的JS包。
    filename: 'static/js/bundle.js',

    // 「附加分块(additional chunk)」的文件名模板（如果你使用代码分离）
    chunkFilename: 'static/js/[name].chunk.js',

    // 输出解析文件的目录，url 相对于 HTML 页面。开发模式下，我们使用 "/"
    publicPath,

    // 将源映射条目指向原始磁盘位置（格式为Windows上的URL）
    devtoolModuleFilenameTemplate: info =>
      path.resolve(info.absoluteResourcePath).replace(/\\/g, '/')
  },

  // 解析模块请求的选项
  resolve: {

    // 用于查找模块的目录。
    // 我们把这些路径放在第二位，因为我们想要“node_modules”来“赢”
    // 如果有任何冲突。这符合 Node 的解析机制。
    // https://github.com/facebookincubator/create-react-app/issues/253
    modules: ['node_modules', paths.appNodeModules].concat(
      // It is guaranteed to exist because we tweak it in `env.js`
      process.env.NODE_PATH.split(path.delimiter).filter(Boolean)
    ),

    // 使用的扩展名
    extensions: ['.js', '.json', '.jsx', '.css', '.less'],

    // 模块别名列表
    alias: {
      // 支持 React Native Web
      // https://www.smashingmagazine.com/2016/08/a-glimpse-into-the-future-with-react-native-for-web/
      'react-native': 'react-native-web'
    },

    // 附加插件列表
    plugins: [

      // 防止用户从 src /（或node_modules/）外部导入文件。
      // 这通常会导致混乱，因为我们只处理 src/ 中的文件与 babel。
      // 为了解决这个问题，我们阻止你从 src/ 外部导入文件。
      // 如果你非要这么做，请将文件链接到您的 node_modules/，并让 module-resolution 起作用。
      // 确保你的源文件被编译，因为它们不会以任何方式处理。
      new ModuleScopePlugin(paths.appSrc, [paths.appPackageJson])
    ]
  },

  // 模块配置
  module: {
    strictExportPresence: true,

    // 模块规则（配置 loader、解析器等选项）
    rules: [

      // 首先，运行 ESLint
      // 在 Babel 处理 JS 之前做这件事很重要。
      {
        test: /\.(js|jsx)$/,
        enforce: 'pre', // 标识应用这些规则，即使规则覆盖（高级选项）
        use: [
          {
            options: {
              formatter: eslintFormatter,
              eslintPath: require.resolve('eslint'),
              configFile: paths.appConfig.concat('/.eslintrc.js')
            },
            loader: require.resolve('eslint-loader')
          }
        ],
        include: paths.appSrc
      },
      {
        // “oneOf”将遍历所有以下 loader，直到有一个符合要求。
        // 当没有 loader 匹配时，它会返回 loader 列表末尾的 loader。
        oneOf: [
          // url-loader 就像 file-loader 一样工作，但如果文件小于限制，则可以返回 DataURL。
          {
            test: [/\.bmp$/, /\.gif$/, /\.jpe?g$/, /\.png$/],
            loader: require.resolve('url-loader'),
            options: {
              limit: 10000,
              name: 'static/media/[name].[hash:8].[ext]'
            }
          },
          // JS 解析器，使得浏览器可以识别 ES6/React 等语法
          {
            test: /\.(js|jsx)$/,
            include: paths.appSrc,
            loader: require.resolve('babel-loader'),
            options: {
              // 这是 babel-loader（不是Babel本身）的一个功能。
              // 它允许缓存结果在 ./node_modules/.cache/babel-loader/ 目录下，使得重新构建更快。
              cacheDirectory: true
            }
          },
          // 这里的符号有点令人困惑。
          // postcss-loader 将 autoprefixer 应用于我们的CSS。
          // css-loader 解析 CSS 中的路径，并将资源添加为依赖关系。
          // style-loader 通常将 CSS 转换为注入<style>的 JS 模块，
          // 在生产模式中，我们使用一个插件将该 CSS 提取到一个文件中，
          // 但在开发模式中，style-loader 可以实现对 CSS 的热编辑。
          // @see https://github.com/webpack-contrib/style-loader
          // @see https://github.com/webpack-contrib/css-loader
          // @see https://github.com/postcss/postcss-loader
          {
            test: /\.css$/,
            use: [
              require.resolve('style-loader'),
              {
                loader: require.resolve('css-loader'),
                options: {
                  importLoaders: 1
                }
              },
              {
                loader: require.resolve('postcss-loader'),
                options: {
                  // 导入外部 CSS 时必不可少N
                  // https://github.com/facebookincubator/create-react-app/issues/2677
                  ident: 'postcss',
                  plugins: () => [
                    require('postcss-flexbugs-fixes'),
                    autoprefixer({
                      browsers: [
                        '>1%',
                        'last 4 versions',
                        'Firefox ESR',
                        'not ie < 9' // React doesn't support IE8 anyway
                      ],
                      flexbox: 'no-2009'
                    })
                  ]
                }
              }
            ]
          },
          // less 完全兼容 css 语法。对于 less 文件，先交给 style-loader 、css-loader 处理。
          // 如果存在 less 自身的特性，则交给 less-loader 去处理。
          // @see https://github.com/webpack-contrib/less-loader
          {
            test: /\.less$/,
            use: [{
              loader: 'style-loader'
            }, {
              loader: 'css-loader'
            }, {
              loader: 'less-loader',
              options: {
                strictMath: true,
                noIeCompat: true
              }
            }]
          },
          // file-loader 确保资源文件最终在输出文件夹中。
          // 当您导入资产时，您将获得其文件名。
          // 这个装载器不使用“测试”，所以它将捕获所有未匹配其它 loader 的模块
          {
            loader: require.resolve('file-loader'),

            // 排除`js`文件以保持 css-loader 在注入时工作
            // 它是运行时，否则将通过 file-loader 处理。
            // 还可以排除`html`和`json`扩展名，以便它们通过 webpack 内部装载机得到处理
            exclude: [/\.js$/, /\.html$/, /\.json$/],
            options: {
              name: 'static/media/[name].[hash:8].[ext]'
            }
          }
          // 【结束】如果需要添加新的 loader，请确保在 file-loader 之前。
        ]
      }
    ]
  },
  plugins: [
    // 使一些环境变量可用于JS代码。
    // 例如：if（process.env.NODE_ENV ==='production'）{...}。请参阅 `./env.js`。
    // 在这里设置 NODE_ENV 是绝对必要的。否则React将以非常缓慢的开发模式进行编译。
    new webpack.DefinePlugin(env.stringified),

    // Moment.js是一个非常受欢迎的日期相关的库，可以捆绑大型语言环境文件，默认情况下由于Webpack解释其代码。
    // 这是一个实用的解决方案，要求用户选择导入特定区域设置。
    // https://github.com/jmblog/how-to-optimize-momentjs-with-webpack
    // 如果你不使用 Moment.js，可以移除
    new webpack.IgnorePlugin(/^\.\/locale$/, /moment$/),

    // 开启全局的模块热替换(HMR)
    new webpack.HotModuleReplacementPlugin(),

    // 当模块热替换(HMR)时在浏览器控制台输出对用户更友好的模块名字信息
    new webpack.NamedModulesPlugin(),

    // 在 index.html 中提供一些环境变量。
    // 公共 URL 在 index.html 中以 ％PUBLIC_URL％ 的形式提供，
    // 例如：<link rel="shortcut icon" href="%PUBLIC_URL%/favicon.ico">
    // 在生产模式中，在 `package.json` 中指定 "homepage" ，它将是该URL的路径名；否则它将是一个空字符串，
    new InterpolateHtmlPlugin(env.raw),

    // 使用注入的<script>生成一个`index.html`文件。
    // @see https://github.com/jantimon/html-webpack-plugin
    new HtmlWebpackPlugin({
      inject: true,
      template: paths.appHtml
    }),

    // 如果您在路径中输入错误的大小写，则 Watcher 无法正常工作，因此当您尝试执行此操作时，我们使用打印错误的插件。
    // @see https://github.com/facebookincubator/create-react-app/issues/240
    new CaseSensitivePathsPlugin(),

    // 如果您需要一个缺少的模块，然后 `npm install` 它 ，那么您仍然需要重新启动 Webpack 的开发服务器来发现它。
    // 此插件可以自动发现缺少的模块，使您不必重新启动。
    // @see https://github.com/facebookincubator/create-react-app/issues/186
    new WatchMissingNodeModulesPlugin(paths.appNodeModules)
  ],

  // 某些库导入 Node 模块，但不要在浏览器中使用它们。
  // 告诉 Webpack 为他们提供空的模拟，以便导入它们。
  node: {
    dgram: 'empty',
    fs: 'empty',
    net: 'empty',
    tls: 'empty',
    child_process: 'empty'
  },

  // 在开发过程中关闭性能提示，因为我们不会为了追求速度进行代码分离或压缩。这些警告变得很麻烦。
  performance: {
    hints: false
  }
};


<|start_filename|>templates/react-admin/src/util/http/mock/index.js<|end_filename|>
import axios from 'axios';
import MockAdapter from 'axios-mock-adapter';

const mockAxios = axios.create();

// mock 数据
const mockAdapter = new MockAdapter(mockAxios);
mockAdapter.onGet('/menu/list').reply(200, require('./menu'));
mockAdapter.onGet('/ui/table/basic/list').reply(200, require('./ui/table/basic'));
mockAdapter.onPost('/api/form/save').reply(200, require('./form'));

export default mockAxios;


<|start_filename|>templates/react-admin/src/component/index.js<|end_filename|>
export Result from './Result';
export Layout from './Layout';
export FooterToolbar from './FooterToolbar';


<|start_filename|>templates/react-admin/src/component/Layout/Menu.js<|end_filename|>
/**
 * @file 侧边导航栏组件
 * @author <NAME>
 * @see https://ant.design/components/layout-cn/
 */
import { Icon, Menu, Spin } from 'antd';
import React from 'react';
import { connect } from 'react-redux';
import { Link, withRouter } from 'react-router-dom';
import { bindActionCreators } from 'redux';
import getPathsAndKeyPath from './common';
import { onMenuItemSelected, onMenuListSearch } from './redux/actions';

/**
 * 根据查询数据，动态加载菜单项
 */
class CustomMenu extends React.PureComponent {
  state = {
    paths: [],
    keyPath: [],
    mode: 'inline'
  };

  componentWillMount() {
    this.props.onMenuListSearch();
    this.setState({
      mode: this.props.mode
    });
  }

  render() {
    const { menu, router } = this.props;

    // 根据选中菜单项的关键 key 数组获取对应组件
    const paths = [];
    const keyPath = [];
    getPathsAndKeyPath(router, menu, paths, keyPath);
    const getMenuItems = (list) => {
      return Array.isArray(list) && list.map((item) => {
        const menus = getMenuItems(item.children, item.key);
        switch (item.type) {
        case 'SubMenu':
          return (
            <Menu.SubMenu
              key={item.key}
              title={<span><Icon type={item.icon} /><span className="nav-text">{item.title}</span></span>}
            >
              {menus}
            </Menu.SubMenu>
          );
        case 'ItemGroup':
          return (
            <Menu.ItemGroup
              key={item.key}
              title={
                <span>
                  <Icon type={item.icon} />&nbsp;
                  <span className="nav-text">{item.title}</span>
                </span>
              }
            >
              {menus}
            </Menu.ItemGroup>
          );
        case 'Divider':
          return (
            <Menu.Divider key={item.key} />
          );
        case 'Item':
        default:
          return (
            <Menu.Item key={item.key}>
              {
                item.url ? (
                  <Link to={item.url}>
                    {item.icon && <Icon type={item.icon} />}<span className="nav-text">{item.title}</span>
                  </Link>
                ) : (
                  <span>
                    {item.icon && <Icon type={item.icon} />}<span className="nav-text">{item.title}</span>
                  </span>
                )
              }
            </Menu.Item>
          );
        }
      });
    };

    return (
      <Spin spinning={menu.loading} delay={500} size="large" tip="Loading...">
        <Menu
          className="sider-menu"
          mode={this.state.mode}
          theme="dark"
          selectedKeys={keyPath}
          defaultOpenKeys={['ui']}
        >
          {getMenuItems(menu.list)}
        </Menu>
      </Spin>
    );
  }
}
function mapStateToProps(state) {
  return {
    menu: state.menu,
    router: state.router
  };
}
function mapDispatchToProps(dispatch) {
  return {
    onMenuListSearch: bindActionCreators(onMenuListSearch, dispatch),
    onMenuItemSelected: bindActionCreators(onMenuItemSelected, dispatch)
  };
}
export default withRouter(connect(mapStateToProps, mapDispatchToProps)(CustomMenu));


<|start_filename|>templates/react-admin/src/component/Layout/Header.js<|end_filename|>
/**
 * @file 顶部布局组件
 * @author <NAME>
 * @see https://ant.design/components/layout-cn/
 */
import { Layout } from 'antd';
import React from 'react';

const { Header } = Layout;

/**
 * 顶部布局组件
 * @class
 */
class CustomHeader extends React.PureComponent {
  render() {
    return (
      <Header />
    );
  }
}
export default CustomHeader;



<|start_filename|>templates/react-admin/src/util/http/mock/menu.js<|end_filename|>
/**
 * 供非真实环境使用的菜单项仿真数据。
 * 进入应用后，菜单栏会查询 /menu/list 接口获取菜单项数据。
 * 被 axios-mock-adapter 拦截请求，将返回以下的模拟应答数据。
 * @type {{code: number, message: string, data: [*]}}
 */

module.exports = {
  code: 0,
  message: '成功',
  data: [{
    key: 'home',
    title: '首页',
    icon: 'home',
    type: 'Item',
    url: '/home',
    children: []
  }, {
    key: 'form',
    title: '表单',
    icon: 'edit',
    type: 'SubMenu',
    children: [
      { key: '/form/basic', title: '基础表单', type: 'Item', url: '/form/basic', parent: 'form' },
      { key: '/form/advanced', title: '高级表单', type: 'Item', url: '/form/advanced', parent: 'form' },
      { key: '/form/step', title: '分步表单', type: 'Item', url: '/form/step', parent: 'form' }
    ]
  }, {
    key: 'ui',
    title: '组件',
    icon: 'scan',
    type: 'SubMenu',
    children: [{
      key: '/ui/general',
      title: 'General',
      type: 'ItemGroup',
      parent: 'ui',
      children: [
        { key: '/ui/general/button', title: '按钮', type: 'Item', url: '/ui/general/button', parent: '/ui/general' },
        { key: '/ui/general/icon', title: '图标', type: 'Item', url: '/ui/general/icon', parent: '/ui/general' }
      ]
    }, {
      key: '/ui/data',
      title: 'Data',
      type: 'ItemGroup',
      parent: 'ui',
      children: [
        { key: '/ui/data/table', title: '表格', type: 'Item', url: '/ui/data/table', parent: '/ui/data' }
      ]
    }, {
      key: '/ui/feedback',
      title: 'Feedback',
      type: 'ItemGroup',
      parent: 'ui',
      children: [
        { key: '/ui/feedback/alert', title: '警告提示', type: 'Item', url: '/ui/feedback/alert', parent: '/ui/feedback' },
        { key: '/ui/feedback/modal', title: '对话框', type: 'Item', url: '/ui/feedback/modal', parent: '/ui/feedback' }
      ]
    }]
  }, {
    key: 'error',
    title: '错误',
    icon: 'exception',
    type: 'SubMenu',
    children: [
      { key: '/error/403', title: '403', type: 'Item', url: '/error/403', parent: 'error' },
      { key: '/error/404', title: '404', type: 'Item', url: '/error/404', parent: 'error' },
      { key: '/error/500', title: '500', type: 'Item', url: '/error/500', parent: 'error' }
    ]
  }, {
    key: 'general',
    title: '特殊页面',
    icon: 'laptop',
    type: 'SubMenu',
    children: [
      { key: '/general/result', title: '结果', type: 'Item', url: '/general/result', parent: 'general' },
      { key: '/general/welcome', title: '欢迎', type: 'Item', url: '/general/welcome', parent: 'general' }
    ]
  }]
};


<|start_filename|>templates/react-admin/src/component/Layout/redux/constants.js<|end_filename|>
// 查询菜单列表
export const MENU_LIST_SEARCH = 'MENU_LIST_SEARCH';
export const MENU_LIST_SEARCH_SUCCESS = 'MENU_LIST_SEARCH_SUCCESS';
export const MENU_LIST_SEARCH_FAILED = 'MENU_LIST_SEARCH_FAILED';

// 选中菜单列表项
export const MENU_ITEM_SELECTED = 'MENU_ITEM_SELECTED';


<|start_filename|>templates/react-admin/src/component/Layout/redux/reducers.js<|end_filename|>
import _ from 'lodash';
import { MENU_ITEM_SELECTED, MENU_LIST_SEARCH, MENU_LIST_SEARCH_FAILED, MENU_LIST_SEARCH_SUCCESS } from './constants';

const MENU_ITEM_HOME_KEY = '0';

const initState = {
  loading: true,
  list: [],
  selected: {
    key: MENU_ITEM_HOME_KEY,
    keyPath: [MENU_ITEM_HOME_KEY]
  }
};

const menu = (state = initState, action = {}) => {
  switch (action.type) {
  case MENU_LIST_SEARCH: {
    return state;
  }
  case MENU_LIST_SEARCH_SUCCESS: {
    return { ...state, loading: false, list: _.get(action, ['payload'], action) };
  }
  case MENU_LIST_SEARCH_FAILED: {
    return { ...state, loading: false, list: [] };
  }
  case MENU_ITEM_SELECTED: {
    return { ...state, selected: _.get(action, ['payload'], state) };
  }
  default:
    return state;
  }
};
export default menu;


<|start_filename|>templates/react-admin/src/view/form/StepForm/Step3.js<|end_filename|>
import { Button, Col, Row } from 'antd';
import _ from 'lodash';
import React from 'react';
import { push } from 'react-router-redux';
import { store } from '../../../common';
import { Result } from '../../../component';
import './style.less';

export default class Step3 extends React.PureComponent {
  componentWillMount() {
    const { formview } = this.props;
    if (_.isEmpty(formview.step.firstData) || _.isEmpty(formview.step.secondData)) {
      this.onFinish();
    }
  }

  onFinish = () => {
    store.dispatch(push('/form/step'));
  };

  render() {
    const { formview } = this.props;
    const information = (
      <div className="information">
        <Row>
          <Col span={8} className="label">付款账户：</Col>
          <Col span={16}>{formview.step.firstData.payAccount}</Col>
        </Row>
        <Row>
          <Col span={8} className="label">收款账户：</Col>
          <Col span={16}>{formview.step.firstData.receiverAccount}</Col>
        </Row>
        <Row>
          <Col span={8} className="label">收款人姓名：</Col>
          <Col span={16}>{formview.step.firstData.receiverName}</Col>
        </Row>
        <Row>
          <Col span={8} className="label">转账金额：</Col>
          <Col span={16}><span className="money">{formview.step.firstData.amount}</span> 元</Col>
        </Row>
      </div>
    );
    const actions = (
      <div>
        <Button type="primary" onClick={this.onFinish}>
          再转一笔
        </Button>
        <Button>
          查看账单
        </Button>
      </div>
    );
    return (
      <Result
        type="success"
        title="操作成功"
        description="预计两小时内到账"
        extra={information}
        actions={actions}
        className="result"
      />
    );
  }
}


<|start_filename|>templates/react-admin/src/component/Layout/index.js<|end_filename|>
/**
 * @file Sider-Header-Content-Footer 式页面级整体布局
 * @author <NAME>
 * @see https://ant.design/components/layout-cn/
 */
import { Layout } from 'antd';
import PropTypes from 'prop-types';
import React from 'react';
import { connect } from 'react-redux';
import { withRouter } from 'react-router-dom';
import Content from './Content';
import Footer from './Footer';
import Header from './Header';
import './index.less';
import Navpath from './Navpath';
import Sider from './Sider';

class CustomLayout extends React.PureComponent {
  static propTypes = {
    navpath: PropTypes.array
  };
  static defaultProps = {
    navpath: []
  };

  render() {
    const { navpath } = this.props;

    return (
      <Layout className="ant-layout-has-sider">
        <Sider />
        <Layout className="ant-layout-container">
          <Header />
          <Navpath data={navpath} />
          <Content type="SHCF">
            {this.props.children}
          </Content>
          <Footer />
        </Layout>
      </Layout>
    );
  }
}
const mapStateToProps = (state) => {
  const { menu } = state;
  return {
    navpath: menu.navpath
  };
};

const mapDispatchToProps = () => {
  return {};
};
export default withRouter(connect(mapStateToProps, mapDispatchToProps)(CustomLayout));


<|start_filename|>templates/react-admin/src/common/store.js<|end_filename|>
/**
 * @file redux store 构造器
 * @author <NAME>
 * @description redux store 构造器。根据开发环境和非开发环境分别集成所需的 redux 中间件。
 * @see https://github.com/gaearon/redux-devtools/blob/master/docs/Walkthrough.md
 * @see https://github.com/gaearon/redux-thunk
 * @see https://github.com/reactjs/react-router-redux
 * @see https://github.com/evgenyrodionov/redux-logger
 */
import { routerMiddleware } from 'react-router-redux';
import { applyMiddleware, compose, createStore } from 'redux';
import thunk from 'redux-thunk';
import history from './history';
import rootReducer from './rootReducer';

// 本项目所使用的基本的 redux 中间件
const router = routerMiddleware(history);
const middlewares = [thunk, router];

let enhancer;
if (process.env.NODE_ENV === 'development') {
  // 非生产环境额外添加 redux-logger
  const createLogger = require('redux-logger').createLogger;
  const logger = createLogger({ collapsed: true });
  middlewares.push(logger);

  // 支持 redux 开发工具
  // @see https://github.com/zalmoxisus/redux-devtools-extension
  const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose;
  enhancer = composeEnhancers(
    // Middleware you want to use in development:
    applyMiddleware(...middlewares),
  );
} else {
  enhancer = applyMiddleware(...middlewares);
}

// 创建 Redux Store，这是本应用唯一的状态管理容器
const configStore = (initialState) => {
  // Note: only Redux >= 3.1.0 supports passing enhancer as third argument.
  // See https://github.com/reactjs/redux/releases/tag/v3.1.0
  const store = createStore(rootReducer, initialState, enhancer);

  // Hot reload reducers (requires Webpack or Browserify HMR to be enabled)
  if (module.hot) {
    module.hot.accept('./rootReducer', () =>
      store.replaceReducer(require('./rootReducer').default)
    );
  }

  return store;
};
const store = configStore();
export default store;


<|start_filename|>templates/react-admin/src/component/Layout/Footer.js<|end_filename|>
/**
 * @file 底部布局组件
 * @author <NAME>
 * @see https://ant.design/components/layout-cn/
 */
import { Layout } from 'antd';
import React from 'react';

const { Footer } = Layout;

/**
 * 底部布局组件
 * @class
 */
class CustomFooter extends React.PureComponent {
  render() {
    return (
      <Footer>
        Copyright © 2017 <a href="https://github.com/dunwu/" target="_blank"><NAME></a><br />
      </Footer>
    );
  }
}
export default CustomFooter;


<|start_filename|>templates/react-admin/src/component/Layout/redux/actions.js<|end_filename|>
import { message } from 'antd';
import { createAction } from 'redux-actions';
import http from '../../../util/http/index';
import { MENU_ITEM_SELECTED, MENU_LIST_SEARCH, MENU_LIST_SEARCH_FAILED, MENU_LIST_SEARCH_SUCCESS } from './constants';

// 查询菜单列表
const menuListSearch = createAction(MENU_LIST_SEARCH);
const menuListSearchSuccess = createAction(MENU_LIST_SEARCH_SUCCESS);
const menuListSearchFailed = createAction(MENU_LIST_SEARCH_FAILED);
export function onMenuListSearch(params) {
  return (dispatch) => {
    dispatch(menuListSearch());
    http.get('/menu/list', { params })
      .then((response) => {
        dispatch(menuListSearchSuccess(response.data.data));
      })
      .catch((error) => {
        message.error('加载菜单列表失败');
        dispatch(menuListSearchFailed(error));
      });
  };
}

// 选中菜单列表项
const menuItemSelected = createAction(MENU_ITEM_SELECTED);
export const onMenuItemSelected = (key, keyPath) => {
  return (dispatch) => {
    dispatch(menuItemSelected(key, keyPath));
  };
};


<|start_filename|>templates/react-admin/src/util/http/axiosInstance.js<|end_filename|>
import axios from 'axios';
import axiosConfig from './axiosConfig';

// 本项目的默认配置
// 使用默认配置初始化的请求
const http = axios.create(axiosConfig);
export default http;


<|start_filename|>templates/react-admin/src/view/ui/Icon.js<|end_filename|>
import { Card, Col, Icon, Row } from 'antd';
import _ from 'lodash';
import React from 'react';

const directionIcons = ['step-backward', 'step-forward', 'fast-backward', 'fast-forward', 'shrink', 'arrows-alt',
  'down', 'up', 'left', 'right', 'caret-up', 'caret-down', 'caret-left', 'caret-right', 'up-circle', 'down-circle',
  'left-circle', 'right-circle', 'up-circle-o', 'down-circle-o', 'right-circle-o', 'left-circle-o', 'double-right',
  'double-left', 'verticle-left', 'verticle-right', 'forward', 'backward', 'rollback', 'enter', 'retweet', 'swap',
  'swap-left', 'swap-right', 'arrow-up', 'arrow-down', 'arrow-left', 'arrow-right', 'play-circle', 'play-circle-o',
  'up-square', 'down-square', 'left-square', 'right-square', 'up-square-o', 'down-square-o', 'left-square-o',
  'right-square-o', 'login', 'logout', 'menu-fold', 'menu-unfold'];
const suggestionIcons = ['question', 'question-circle-o', 'question-circle', 'plus', 'plus-circle-o', 'plus-circle',
  'pause', 'pause-circle-o', 'pause-circle', 'minus', 'minus-circle-o', 'minus-circle', 'plus-square', 'plus-square-o',
  'minus-square', 'minus-square-o', 'info', 'info-circle-o', 'info-circle', 'exclamation', 'exclamation-circle-o',
  'exclamation-circle', 'close', 'close-circle', 'close-circle-o', 'close-square', 'close-square-o', 'check',
  'check-circle', 'check-circle-o', 'check-square', 'check-square-o', 'clock-circle-o', 'clock-circle'];
const otherIcons = ['lock', 'unlock', 'area-chart', 'pie-chart', 'bar-chart', 'dot-chart', 'bars', 'book', 'calendar',
  'cloud', 'cloud-download', 'code', 'code-o', 'copy', 'credit-card', 'delete', 'desktop', 'download', 'edit',
  'ellipsis', 'file', 'file-text', 'file-unknown', 'file-pdf', 'file-excel', 'file-jpg', 'file-ppt', 'file-add',
  'folder', 'folder-open', 'folder-add', 'hdd', 'frown', 'frown-o', 'meh', 'meh-o', 'smile', 'smile-o', 'inbox',
  'laptop', 'appstore-o', 'appstore', 'line-chart', 'link', 'mail', 'mobile', 'notification', 'paper-clip', 'picture',
  'poweroff', 'reload', 'search', 'setting', 'share-alt', 'shopping-cart', 'tablet', 'tag', 'tag-o', 'tags', 'tags-o',
  'to-top', 'upload', 'user', 'video-camera', 'home', 'loading', 'loading-3-quarters', 'cloud-upload-o',
  'cloud-download-o', 'cloud-upload', 'cloud-o', 'star-o', 'star', 'heart-o', 'heart', 'environment', 'environment-o',
  'eye', 'eye-o', 'camera', 'camera-o', 'save', 'team', 'solution', 'phone', 'filter', 'exception', 'export',
  'customer-service', 'qrcode', 'scan', 'like', 'like-o', 'dislike', 'dislike-o', 'message', 'pay-circle',
  'pay-circle-o', 'calculator', 'pushpin', 'pushpin-o', 'bulb', 'select', 'switcher', 'rocket', 'bell', 'disconnect',
  'database', 'compass', 'barcode', 'hourglass', 'key', 'flag', 'layout', 'printer', 'sound', 'usb', 'skin', 'tool',
  'sync', 'wifi', 'car', 'schedule', 'user-add', 'user-delete', 'usergroup-add', 'usergroup-delete', 'man', 'woman',
  'shop', 'gift', 'idcard', 'medicine-box', 'red-envelope', 'coffee', 'copyright', 'trademark', 'safety', 'wallet',
  'bank', 'trophy', 'contacts', 'global', 'shake', 'api'];
const logoIcons = ['android', 'android-o', 'apple', 'apple-o', 'windows', 'windows-o', 'ie', 'chrome', 'github',
  'aliwangwang', 'aliwangwang-o', 'dingding', 'dingding-o'];

const getIconList = (data, title, colorStr) => {
  const groups = _.chunk(data, 6);
  return (
    <div>
      <span>
        <p style={{ fontSize: 24, fontWeight: 500, color: colorStr }}>
          {title}
        </p>
      </span>
      {groups.map((row, i) => (
        <Row key={`row-${title}-${i}`} gutter={8}>
          {row.map(icon => (
            <Col key={icon} span={4}>
              <Card style={{ textAlign: 'center' }}>
                <Icon type={icon} style={{ fontSize: 24, color: colorStr }} /><br />
                <span style={{ fontSize: 12, color: colorStr }}>{icon}</span>
              </Card>
            </Col>
          ))}
        </Row>
      ))}
      <br />
    </div>
  );
};

const IconView = () => (
  <Card title="图标示例">
    {getIconList(directionIcons, '方向性图标', '#de6f64')}
    {getIconList(suggestionIcons, '提示建议性图标', '#399ab2')}
    {getIconList(otherIcons, '网站通用图标', '#346093')}
    {getIconList(logoIcons, '品牌和标识', '#78943a')}
  </Card>
);
export default IconView;


<|start_filename|>templates/react-admin/src/view/general/route.js<|end_filename|>
import ResultView from './Result';
import WelcomeView from './Welcome';

export default [{
  path: '/home',
  component: WelcomeView
}, {
  path: '/general/welcome',
  component: WelcomeView
}, {
  path: '/general/result',
  component: ResultView
}];


<|start_filename|>templates/react-admin/src/view/form/redux/reducers.js<|end_filename|>
import _ from 'lodash';
import { combineReducers } from 'redux';
import {
  ADVANCED_FORM_SUBMIT,
  ADVANCED_FORM_SUBMIT_FAILED,
  ADVANCED_FORM_SUBMIT_SUCCESS,
  STEP_FORM_STEP1,
  STEP_FORM_STEP2,
  ADVANCED_FORM_POST,
  STEP_FORM_SUBMIT,
  STEP_FORM_SUBMIT_FAILED,
  STEP_FORM_SUBMIT_SUCCESS
} from './constants';

const stepState = {
  firstData: {},
  secondData: {},
  result: false
};
const step = (state = stepState, action = {}) => {
  switch (action.type) {
  case STEP_FORM_STEP1: {
    return { ...state, firstData: _.get(action, ['payload'], action) };
  }
  case STEP_FORM_STEP2: {
    return { ...state, secondData: _.get(action, ['payload'], action) };
  }
  case STEP_FORM_SUBMIT: {
    return state;
  }
  case STEP_FORM_SUBMIT_SUCCESS: {
    const ret = _.get(action, ['payload'], action);
    return { ...state, result: ret.result };
  }
  case STEP_FORM_SUBMIT_FAILED: {
    return { ...state };
  }
  default: {
    return state;
  }
  }
};

const advancedState = {
  data: {},
  result: false
};
const advanced = (state = advancedState, action = {}) => {
  switch (action.type) {
  case ADVANCED_FORM_POST: {
    return { ...state, data: _.get(action, ['payload'], action) };
  }
  case ADVANCED_FORM_SUBMIT: {
    return state;
  }
  case ADVANCED_FORM_SUBMIT_SUCCESS: {
    const ret = _.get(action, ['payload'], action);
    return { ...state, result: ret.result };
  }
  case ADVANCED_FORM_SUBMIT_FAILED: {
    return { ...state };
  }
  default: {
    return state;
  }
  }
};

const formview = combineReducers({
  step, advanced
});
export default formview;


<|start_filename|>templates/react-admin/src/common/rootRouter.js<|end_filename|>
/**
 * @file react router 入口。
 * @description react router 入口。注意：本项目中使用 react router 4.x，它的 API 与以前的版本有很大的不同。
 * @author <NAME>
 * @see https://reacttraining.com/react-router/
 * @see https://reacttraining.cn/
 */
import _ from 'lodash';
import errorRoutes from '../view/error/route';
import formRoutes from '../view/form/route';
import generalRoutes from '../view/general/route';
import uiRoutes from '../view/ui/route';

/**
 * 合并所有子路由
 */
const rootRouter = _.concat(errorRoutes, generalRoutes, formRoutes, uiRoutes);
export default rootRouter;


<|start_filename|>templates/react-admin/src/view/general/Result/index.js<|end_filename|>
import { Card } from 'antd';
import React from 'react';
import Failed from './Failed';
import Success from './Success';

export default () => (
  <div>
    <Card noHovering bordered={false} title="成功示例">
      <Success />
    </Card>
    <Card noHovering bordered={false} title="失败示例">
      <Failed />
    </Card>
  </div>
);


<|start_filename|>templates/react-admin/src/view/general/Welcome/index.js<|end_filename|>
import React from 'react';
import './index.less';
import logo from './logo.svg';

export default () => (
  <div className="App">
    <header className="App-header">
      <img src={logo} className="App-logo" alt="logo" />
      <h1 className="App-title">Welcome to REACT ADMIN</h1>
    </header>
    <p className="App-intro">
      REACT ADMIN is developing。。。
    </p>
  </div>
);


<|start_filename|>templates/react-admin/src/view/form/StepForm/index.js<|end_filename|>
import { Card, Form, Steps } from 'antd';
import React from 'react';
import { connect } from 'react-redux';
import { withRouter } from 'react-router-dom';
import { bindActionCreators } from 'redux';
import { onStepFormFirst, onStepFormSecond, onStepFormSubmit } from '../redux/actions';
import { stepSelector } from '../redux/selectors';
import Step1 from './Step1';
import Step2 from './Step2';
import Step3 from './Step3';
import './style.less';

const { Step } = Steps;

class StepForm extends React.PureComponent {
  getCurrentStep() {
    const { location } = this.props;
    const { pathname } = location;
    const pathList = pathname.split('/');
    switch (pathList[pathList.length - 1]) {
    case 'step':
      return 0;
    case 'second':
      return 1;
    case 'third':
      return 2;
    default:
      return 0;
    }
  }

  getCurrentComponent() {
    const componentMap = {
      0: Step1,
      1: Step2,
      2: Step3
    };
    return componentMap[this.getCurrentStep()];
  }

  render() {
    const { form, formview } = this.props;
    const formItemLayout = {
      labelCol: {
        span: 5
      },
      wrapperCol: {
        span: 19
      }
    };
    const CurrentComponent = this.getCurrentComponent();
    return (
      <Card bordered={false}>
        <div>
          <Steps current={this.getCurrentStep()} className="steps">
            <Step title="填写转账信息" />
            <Step title="确认转账信息" />
            <Step title="完成" />
          </Steps>
          <CurrentComponent
            {...this.props}
            formItemLayout={formItemLayout}
            form={form}
            data={formview.step}
          />
        </div>
      </Card>
    );
  }
}
function mapStateToProps(state) {
  return {
    formview: {
      step: stepSelector(state)
    }
  };
}
function mapDispatchToProps(dispatch) {
  return {
    onStepFormSubmit: bindActionCreators(onStepFormSubmit, dispatch),
    onStepFormFirst: bindActionCreators(onStepFormFirst, dispatch),
    onStepFormSecond: bindActionCreators(onStepFormSecond, dispatch)
  };
}
const WrappedStepForm = Form.create()(StepForm);
export default withRouter(connect(mapStateToProps, mapDispatchToProps)(WrappedStepForm));


<|start_filename|>templates/react-admin/src/component/Layout/Navpath.js<|end_filename|>
/**
 * @file 面包屑组件
 * @author <NAME>
 * @see https://github.com/facebook/prop-types
 * @see https://ant.design/components/breadcrumb-cn/
 * @see https://ant.design/components/icon-cn/
 */
import { Breadcrumb, Col, Icon, Row } from 'antd';
import React from 'react';
import { connect } from 'react-redux';
import { withRouter } from 'react-router-dom';
import getPathsAndKeyPath from './common';

/**
 * 面包屑组件
 * @class
 */
class Navpath extends React.PureComponent {
  render() {
    const { menu, router } = this.props;
    // 根据选中菜单项的关键 key 数组获取对应组件
    const paths = [];
    const keyPath = [];
    getPathsAndKeyPath(router, menu, paths, keyPath);

    const breadcrumbItems = paths.map((item) => {
      return (
        <Breadcrumb.Item key={item.key}>
          <Icon type={item.icon} />
          <span>{item.title}</span>
        </Breadcrumb.Item>
      );
    });

    let tilte;
    if (paths && paths.length > 0) {
      tilte = paths[paths.length - 1].title;
    }
    return (
      <div className="navpath">
        <Row>
          <Col xs={0} sm={12} md={12} lg={12} xl={12}>
            <span className="title">{tilte}</span>
          </Col>
          <Col xs={24} sm={12} md={12} lg={12} xl={12}>
            <Breadcrumb separator=">">
              {breadcrumbItems}
            </Breadcrumb>
          </Col>
        </Row>
      </div>
    );
  }
}
function mapStateToProps(state) {
  return {
    menu: state.menu,
    router: state.router
  };
}
export default withRouter(connect(mapStateToProps)(Navpath));


<|start_filename|>templates/react-admin/config/webpack.config.prod.js<|end_filename|>
/**
 * @file webpack 生产模式配置
 * @see https://doc.webpack-china.org/configuration/
 */
const autoprefixer = require('autoprefixer');
const path = require('path');
const webpack = require('webpack');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const ExtractTextPlugin = require('extract-text-webpack-plugin');
const ManifestPlugin = require('webpack-manifest-plugin');
const InterpolateHtmlPlugin = require('react-dev-utils/InterpolateHtmlPlugin');
const eslintFormatter = require('react-dev-utils/eslintFormatter');
const ModuleScopePlugin = require('react-dev-utils/ModuleScopePlugin');
const paths = require('./paths');
const getClientEnvironment = require('./env');

// Webpack 使用 `publicPath` 来确定应用程序在哪里发送。
// 它需要尾部斜线，否则文件资源将获得不正确的路径。
const publicPath = paths.servedPath;
// 某些应用程序不使用 pushState 的客户端路由。
// 对于这些，"homepage" 可以设置为 "."。以启用相对路径。
const shouldUseRelativeAssetPaths = publicPath === './';
// Source maps 较大，可能会导致因为源文件太大而内存不足的问题。
const shouldUseSourceMap = process.env.GENERATE_SOURCEMAP !== 'false';
// `publicUrl` 就像`publicPath`，但是我们将把它提供给我们的应用程序，
// 就像 ％PUBLIC_URL％ 在 'index.html` 和 'process.env.PUBLIC_URL` 在 JavaScript 中 。
// 省略尾随斜线，%PUBLIC_PATH%/xyz 看起来比 %PUBLIC_PATH%xyz 好。
const publicUrl = publicPath.slice(0, -1);
// 获取环境变量以注入到应用程序中。
const env = getClientEnvironment(publicUrl);

// 这里断言是为了安全。
// React 的开发模式构建比较慢，不适合于生产环境
// if (env.stringified['process.env'].NODE_ENV !== '"production"') {
//   throw new Error('Production builds must have NODE_ENV=production.');
// }

// 注意：在这里定义，因为它将被使用不止一次。
const cssFilename = 'static/css/[name].[contenthash:8].css';

// ExtractTextPlugin 期望构建的输出平稳。
// (See https://github.com/webpack-contrib/extract-text-webpack-plugin/issues/27)
// 但是，我们的输出结构使用 css，js 和 media 文件夹。
// 要使这个结构可以作用于相对路径，我们必须使用自定义选项。
const extractTextPluginOptions = shouldUseRelativeAssetPaths
  ? // 确保 publicPath 返回到构建文件夹。
  { publicPath: Array(cssFilename.split('/').length).join('../') }
  : {};

// 这是生产环境的配置
// 它编译速度较慢并专注于生成一个快速和最小化的包。
module.exports = {
  // 在第一个错误出错时抛出，而不是无视错误。
  bail: true,

  // 我们在生产中生成 sourcemaps。这很慢，但效果不错。
  // 您可以在部署期间从构建中排除* .map文件。
  // 通过在浏览器调试工具(browser devtools)中添加元信息(meta info)增强调试
  devtool: shouldUseSourceMap ? 'source-map' : false,

  // webpack 打包入口
  // 在生产环境中，我们仅仅想加载 polyfills 和 app 的代码.
  entry: [
    // require.resolve('./polyfills'),

    // 您的应用程序的代码入口
    // 我们最后包含应用程序代码，以便如果在初始化期间有运行时错误，
    // 它不会破坏 WebpackDevServer 客户端，并且更改 JS 代码仍将触发刷新。
    paths.appIndexJs
  ],

  // webpack 如何输出结果的相关选项
  output: {

    // 所有输出文件的目标路径
    // 必须是绝对路径（使用 Node.js 的 path 模块）
    path: paths.appBuild,

    // 「入口分块(entry chunk)」的文件名模板
    // 生成的JS文件名（带有嵌套文件夹）将有一个主要的bundle，每个异步块有一个文件。
    // filename: "static/js/[name].js", // 用于多个入口点
    filename: 'static/js/[name].[chunkhash:8].js', // 用于长效缓存

    // 「附加分块(additional chunk)」的文件名模板
    // chunkFilename: "static/js/[name].js", // 用于多个入口点
    chunkFilename: 'static/js/[name].[chunkhash:8].chunk.js', // 用于长效缓存

    // 输出解析文件的目录，url 相对于 HTML 页面。
    publicPath,

    // 「devtool 中模块」的文件名模板
    devtoolModuleFilenameTemplate: info =>
      path.relative(paths.appSrc, info.absoluteResourcePath).replace(/\\/g, '/')
  },

  // 解析模块请求的选项
  resolve: {

    // 用于查找模块的目录。
    // 我们把这些路径放在第二位，因为我们想要“node_modules”来“赢”
    // 如果有任何冲突。这符合 Node 的解析机制。
    // https://github.com/facebookincubator/create-react-app/issues/253
    modules: ['node_modules', paths.appNodeModules].concat(
      // It is guaranteed to exist because we tweak it in `env.js`
      process.env.NODE_PATH.split(path.delimiter).filter(Boolean)
    ),

    // 使用的扩展名
    extensions: ['.js', '.json', '.jsx', '.css', '.less'],

    // 模块别名列表
    alias: {
      // 支持 React Native Web
      // https://www.smashingmagazine.com/2016/08/a-glimpse-into-the-future-with-react-native-for-web/
      'react-native': 'react-native-web'
    },

    // 附加插件列表
    plugins: [

      // 防止用户从 src /（或node_modules/）外部导入文件。
      // 这通常会导致混乱，因为我们只处理 src/ 中的文件与 babel。
      // 为了解决这个问题，我们阻止你从 src/ 外部导入文件。
      // 如果你非要这么做，请将文件链接到您的 node_modules/，并让 module-resolution 起作用。
      // 确保你的源文件被编译，因为它们不会以任何方式处理。
      new ModuleScopePlugin(paths.appSrc, [paths.appPackageJson])
    ]
  },

  // 模块配置
  module: {
    strictExportPresence: true,

    // 模块规则（配置 loader、解析器等选项）
    rules: [

      // 首先，运行 ESLint
      // 在 Babel 处理 JS 之前做这件事很重要。
      {
        test: /\.(js|jsx)$/,
        enforce: 'pre', // 标识应用这些规则，即使规则覆盖（高级选项）
        use: [
          {
            options: {
              formatter: eslintFormatter,
              eslintPath: require.resolve('eslint'),
              configFile: paths.appConfig.concat('/.eslintrc.js')
            },
            loader: require.resolve('eslint-loader')
          }
        ],
        include: paths.appSrc
      },
      {
        // “oneOf”将遍历所有以下 loader，直到有一个符合要求。
        // 当没有 loader 匹配时，它会返回 loader 列表末尾的 loader。
        oneOf: [
          // url-loader 就像 file-loader 一样工作，但如果文件小于限制，则可以返回 DataURL。
          {
            test: [/\.bmp$/, /\.gif$/, /\.jpe?g$/, /\.png$/],
            loader: require.resolve('url-loader'),
            options: {
              limit: 10000,
              name: 'static/media/[name].[hash:8].[ext]'
            }
          },
          // JS 解析器，使得浏览器可以识别 ES6/React 等语法
          {
            test: /\.(js|jsx)$/,
            include: paths.appSrc,
            loader: require.resolve('babel-loader'),
            options: {
              compact: true
            }
          },
          // 这里的符号有点令人困惑。
          // postcss-loader 将 autoprefixer 应用于我们的CSS。
          // css-loader 解析 CSS 中的路径，并将资源添加为依赖关系。
          // style-loader 通常将 CSS 转换为注入<style>的 JS 模块，
          // 但与开发配置不同，我们做的不同。
          // “ExtractTextPlugin”首先应用 postcss-loader 和 css-loader（第二个参数），
          // 然后抓取 CSS 编译结果并将其放入我们构建过程中的一个单独文件。
          // 这样，我们实际上是在生产环境输出一个单独的CSS文件，而不是在JS代码中注入<style>标签。
          // 但是，如果您使用代码分割，则任何异步包仍然使用 style-loader 解析异步代码中的 CSS，
          // 因此它们不会出现在 main CSS 文件中。
          // @see https://github.com/webpack-contrib/style-loader
          // @see https://github.com/webpack-contrib/css-loader
          // @see https://github.com/postcss/postcss-loader
          {
            test: /\.css$/,
            loader: ExtractTextPlugin.extract(
              Object.assign(
                {
                  fallback: require.resolve('style-loader'),
                  use: [
                    {
                      loader: require.resolve('css-loader'),
                      options: {
                        importLoaders: 1,
                        minimize: true,
                        sourceMap: shouldUseSourceMap
                      }
                    },
                    {
                      loader: require.resolve('postcss-loader'),
                      options: {
                        ident: 'postcss',
                        plugins: () => [
                          require('postcss-flexbugs-fixes'),
                          autoprefixer({
                            browsers: [
                              '>1%',
                              'last 4 versions',
                              'Firefox ESR',
                              'not ie < 9' // React doesn't support IE8 anyway
                            ],
                            flexbox: 'no-2009'
                          })
                        ]
                      }
                    }
                  ]
                },
                extractTextPluginOptions
              )
            )
            // 注意：如果`plugins`中没有 ExtractTextPlugin，这将不起作用。
          },
          // less 完全兼容 css 语法。对于 less 文件，先交给 style-loader 、css-loader 处理。
          // 如果存在 less 自身的特性，则交给 less-loader 去处理。
          // 与开发模式不同，这里使用了 ExtractTextPlugin
          // @see https://github.com/webpack-contrib/less-loader
          {
            test: /\.less$/,
            loader: ExtractTextPlugin.extract(
              Object.assign(
                {
                  fallback: require.resolve('style-loader'),
                  use: [
                    {
                      loader: require.resolve('css-loader'),
                      options: {
                        importLoaders: 1,
                        minimize: true,
                        sourceMap: shouldUseSourceMap
                      }
                    },
                    {
                      loader: require.resolve('less-loader')
                    }
                  ]
                },
                extractTextPluginOptions
              )
            )
          },
          // file-loader 确保资源文件最终在输出文件夹中。
          // 当您导入资产时，您将获得其文件名。
          // 这个装载器不使用“测试”，所以它将捕获所有未匹配其它 loader 的模块
          {
            loader: require.resolve('file-loader'),

            // 排除`js`文件以保持 css-loader 在注入时工作
            // 它是运行时，否则将通过 file-loader 处理。
            // 还可以排除`html`和`json`扩展名，以便它们通过 webpack 内部装载机得到处理
            exclude: [/\.js$/, /\.html$/, /\.json$/],
            options: {
              name: 'static/media/[name].[hash:8].[ext]'
            }
          }
          // 【结束】如果需要添加新的 loader，请确保在 file-loader 之前。
        ]
      }
    ]
  },
  plugins: [
    // 使一些环境变量可用于JS代码。
    // 例如：if（process.env.NODE_ENV ==='production'）{...}。请参阅 `./env.js`。
    // 在这里设置 NODE_ENV 是绝对必要的。否则React将以非常缓慢的开发模式进行编译。
    new webpack.DefinePlugin(env.stringified),

    // 压缩代码大小
    new webpack.optimize.UglifyJsPlugin({
      compress: {
        warnings: false,
        // 由于Uglify出现问题而导致看似有效的代码被禁用：
        // https://github.com/facebookincubator/create-react-app/issues/2376
        // 待进一步调查：
        // https://github.com/mishoo/UglifyJS2/issues/2011
        comparisons: false
      },
      output: {
        comments: false,
        // 打开它因为表情符号和正则表达式使用默认值没有正确地进行调整
        // https://github.com/facebookincubator/create-react-app/issues/2488
        ascii_only: true
      },
      sourceMap: shouldUseSourceMap
    }),

    // Moment.js是一个非常受欢迎的日期相关的库，可以捆绑大型语言环境文件，默认情况下由于Webpack解释其代码。
    // 这是一个实用的解决方案，要求用户选择导入特定区域设置。
    // https://github.com/jmblog/how-to-optimize-momentjs-with-webpack
    // 如果你不使用 Moment.js，可以移除
    new webpack.IgnorePlugin(/^\.\/locale$/, /moment$/),

    // 在 index.html 中提供一些环境变量。
    // 公共 URL 在 index.html 中以 ％PUBLIC_URL％ 的形式提供，
    // 例如：<link rel="shortcut icon" href="%PUBLIC_URL%/favicon.ico">
    // 在生产模式中，在 `package.json` 中指定 "homepage" ，它将是该URL的路径名；否则它将是一个空字符串，
    new InterpolateHtmlPlugin(env.raw),

    // 使用注入的<script>生成一个`index.html`文件。
    // @see https://github.com/jantimon/html-webpack-plugin
    new HtmlWebpackPlugin({
      inject: true,
      template: paths.appHtml,
      minify: {
        removeComments: true,
        collapseWhitespace: true,
        removeRedundantAttributes: true,
        useShortDoctype: true,
        removeEmptyAttributes: true,
        removeStyleLinkTypeAttributes: true,
        keepClosingSlash: true,
        minifyJS: true,
        minifyCSS: true,
        minifyURLs: true
      }
    }),

    // 将文本从包中提取到文件中。
    // 注意：如果在“loader”中没有 ExtractTextPlugin.extract（..），这将不起作用。
    // @see https://github.com/webpack-contrib/extract-text-webpack-plugin
    new ExtractTextPlugin({
      filename: cssFilename
    }),

    // 生成一个 manifest 文件，它包含所有资源文件到相应输出文件的映射清单。
    // 这使得工具可以无需解析 `index.html` 就找到资源。
    // @see https://github.com/danethurber/webpack-manifest-plugin
    new ManifestPlugin({
      fileName: 'asset-manifest.json'
    })
  ],

  // 某些库导入 Node 模块，但不要在浏览器中使用它们。
  // 告诉 Webpack 为他们提供空的模拟，以便导入它们。
  node: {
    dgram: 'empty',
    fs: 'empty',
    net: 'empty',
    tls: 'empty',
    child_process: 'empty'
  }
};


<|start_filename|>templates/react-admin/config/webpackDevServer.config.js<|end_filename|>
/**
 * @file webpack 开发服务器配置
 * @see https://doc.webpack-china.org/configuration/dev-server
 */
const errorOverlayMiddleware = require('react-dev-utils/errorOverlayMiddleware');
const noopServiceWorkerMiddleware = require('react-dev-utils/noopServiceWorkerMiddleware');
const config = require('./webpack.config.dev');
const paths = require('./paths');

const protocol = process.env.HTTPS === 'true' ? 'https' : 'http';
const host = process.env.HOST || '0.0.0.0';

module.exports = function (proxy, allowedHost) {
  return {

    // WebpackDevServer 2.4.3引入了一个安全修复程序，可防止远程站点通过DNS重新绑定访问本地内容：
    // @see https://github.com/webpack/webpack-dev-server/issues/887
    // https://medium.com/webpack/webpack-dev-server-middleware-security-issues-1489d950874a
    // 但是，云端环境或子域名开发中的几个现有用例显着更复杂：
    // https://github.com/facebookincubator/create-react-app/issues/2271
    // https://github.com/facebookincubator/create-react-app/issues/2233
    // 我们正考虑更好的解决方案，现在我们将暂时妥协。
    // 由于我们的 WDS 配置只提供 `public` 文件夹中的文件，所以我们不会考虑访问一个漏洞。
    // 但是，如果您使用 `proxy` 功能，它会变得更危险，因为它可能暴露在 Django 和 Rails 等后端的远程代码执行漏洞。
    // 所以我们将正常禁用主机检查，但如果您指定了`proxy`设置，则启用它。
    // 最后，如果你真的知道你在使用一个特殊的环境变量，你可以重写它。
    disableHostCheck: !proxy || process.env.DANGEROUSLY_DISABLE_HOST_CHECK === 'true',

    // 一切服务都启用 gzip 压缩
    compress: true,

    // 当使用内联模式(inline mode)时，在开发工具(DevTools)的控制台(console)将显示消息，
    // 如：在重新加载之前，在一个错误之前，或者模块热替换(Hot Module Replacement)启用时。这可能显得很繁琐。
    // 这里关闭 WebpackDevServer 自己的日志，因为它们通常没什么用。
    // 但是仍然会显示编译警告和错误。
    clientLogLevel: 'none',

    // 告诉服务器从哪里提供内容。只有在你想要提供静态文件时才需要。
    // devServer.publicPath 将用于确定应该从哪里提供 bundle，并且此选项优先。
    // 默认情况下，将使用当前工作目录作为提供内容的目录，但是你可以修改为其他目录：
    // 注意，推荐使用绝对路径。但是也可以从多个目录提供内容。如下：
    // contentBase: [path.join(__dirname, "public"), path.join(__dirname, "assets")]
    // 默认情况下，WebpackDevServer 除了从内存中提供的所有虚拟构建产品之外，还提供来自当前目录的物理文件。
    // 这是令人困惑的，因为这些文件不会自动在生产构建文件夹中可用，除非我们复制它们。
    // 但是，复制整个项目目录是危险的，因为我们可能会暴露敏感文件。
    // 相反，我们建立一个只在 `public` 目录中提供公开文件的约定。
    // 我们的构建脚本会将 `public` 复制到 `build` 文件夹中。
    // 在 `index.html` 中，您可以使用 ％PUBLIC_URL％ 获取 `public` 文件夹的 URL：
    // <link rel="shortcut icon" href="%PUBLIC_URL%/favicon.ico">。
    //  在JavaScript代码中，您可以使用 `process.env.PUBLIC_URL' 访问它。
    //  请注意，我们建议使用`public`文件夹作为`favicon.ico`，`manifest.json` 和由于某些原因从 Webpack 无法导入的库
    //  如果您只想使用图像，请将其放在`src`中，然后从JavaScript导入。
    contentBase: paths.appPublic,

    // 默认情况下，`contentBase` 的文件不会触发页面重新加载。
    watchContentBase: true,

    // 启用 webpack 的模块热替换特性。
    // 它将为 WebpackDevServer 客户端提供 /sockjs-node/ 端点，以便可以了解文件的更新时间。
    // WebpackDevServer 客户端作为 Webpack 开发配置中的入口点。
    // 请注意，只有 CSS 的更改目前正在重新加载。JS 更改将刷新浏览器。
    hot: true,

    // 告诉 WebpackDevServer 使用与我们在 config 中指定的相同的根路径是很重要的。在开发中，我们始终服务于/。
    publicPath: config.output.publicPath,

    // 启用 quiet 后，除了初始启动信息之外的任何内容都不会被打印到控制台。
    // 这也意味着来自 webpack 的错误或警告在控制台不可见。
    quiet: true,

    // 据报道，这避免了某些系统上的CPU过载。
    // https://github.com/facebookincubator/create-react-app/issues/293
    watchOptions: {
      ignored: /node_modules/
    },

    // 如果设置 HTTPS 环境变量为 true，则开启 HTTPS 开关
    https: protocol === 'https',
    host,
    overlay: false,
    historyApiFallback: {
      // 带点的路径应该仍然使用 history 回退。
      // See https://github.com/facebookincubator/create-react-app/issues/387.
      disableDotRule: true
    },
    public: allowedHost,
    proxy,
    setup(app) {
      // 这使我们可以在运行时错误重叠中打开文件。
      app.use(errorOverlayMiddleware());

      // service worker 文件实际上是一个“无操作”，将重置以前为同一主机注册的服务工作者：端口组合。
      // 我们在开发中这样做，以避免在使用相同的主机和端口时冲突生产缓存。
      // https://github.com/facebookincubator/create-react-app/issues/2272#issuecomment-302832432
      app.use(noopServiceWorkerMiddleware());
    }
  };
};


<|start_filename|>templates/react-admin/src/component/Layout/common.js<|end_filename|>
import _ from 'lodash';

const getItemByUrl = (list, url, items) => {
  list.forEach((item) => {
    if (item.url === url) {
      items.push(item);
    }
    if (_.isArray(item.children)) {
      getItemByUrl(item.children, url, items);
    }
  });
};
const getItemByKey = (list, key, items) => {
  list.forEach((item) => {
    if (item.key === key) {
      items.push(item);
    }
    if (_.isArray(item.children)) {
      getItemByKey(item.children, key, items);
    }
  });
};
function getPaths(list, key, paths, keyPath) {
  const tmp = [];
  getItemByKey(list, key, tmp);
  if (_.isEmpty(tmp)) {
    return paths;
  }
  paths.push(tmp[0]);
  keyPath.push(tmp[0].key);
  return getPaths(list, tmp[0].parent, paths, keyPath);
}
function getPathsAndKeyPath(router, menu, paths, keyPath) {
  // 根据选中菜单项的关键 key 数组获取对应组件
  const items = [];
  if (router.location.pathname && menu.list) {
    getItemByUrl(menu.list, router.location.pathname, items);

    if (!_.isEmpty(items)) {
      paths.push(items[0]);
      keyPath.push(items[0].key);
      getPaths(menu.list, items[0].parent, paths, keyPath);
    }
    paths = _.reverse(paths);
    keyPath = _.reverse(keyPath);
  }
}

export default getPathsAndKeyPath;


<|start_filename|>templates/react-admin/src/common/index.js<|end_filename|>
export { default as store } from './store';
export { default as rootReducer } from './rootReducer';
export { default as rootRouter } from './rootRouter';
export { default as history } from './history';


<|start_filename|>templates/react-admin/src/view/ui/Button.js<|end_filename|>
import { Button, Card, Col, Dropdown, Icon, Menu, Radio, Row } from 'antd';
import React from 'react';

class ButtonView extends React.PureComponent {
  state = {
    size: 'large',
    loading: false,
    iconLoading: false
  };

  handleSizeChange = (e) => {
    this.setState({ size: e.target.value });
  };

  enterLoading = () => {
    this.setState({ loading: true });
  };

  enterIconLoading = () => {
    this.setState({ iconLoading: true });
  };

  handleMenuClick = (e) => {
    console.log('click', e);
  };

  render() {
    const size = this.state.size;
    const menu = (
      <Menu onClick={this.handleMenuClick}>
        <Menu.Item key="1">1st item</Menu.Item>
        <Menu.Item key="2">2nd item</Menu.Item>
        <Menu.Item key="3">3rd item</Menu.Item>
      </Menu>
    );
    return (
      <div>
        <Row gutter={16}>
          <Col xs={12}>
            <Card title="按钮类型">
              <Button type="primary">Primary</Button>
              <Button>Default</Button>
              <Button type="dashed">Dashed</Button>
              <Button type="danger">Danger</Button>
            </Card>
            <Card title="图标按钮">
              <Button type="primary" shape="circle" icon="search" />
              <Button type="primary" icon="search">Search</Button>
              <Button shape="circle" icon="search" />
              <Button icon="search">Search</Button>
              <br />
              <Button shape="circle" icon="search" />
              <Button icon="search">Search</Button>
              <Button type="dashed" shape="circle" icon="search" />
              <Button type="dashed" icon="search">Search</Button>
            </Card>
            <Card title="按钮尺寸">
              <Radio.Group value={size} onChange={this.handleSizeChange}>
                <Radio.Button value="large">Large</Radio.Button>
                <Radio.Button value="default">Default</Radio.Button>
                <Radio.Button value="small">Small</Radio.Button>
              </Radio.Group>
              <br /> <br />
              <Button type="primary" size={size}>Primary</Button>
              <Button size={size}>Normal</Button>
              <Button type="dashed" size={size}>Dashed</Button>
              <Button type="danger" size={size}>Danger</Button>
              <br />
              <Button type="primary" shape="circle" icon="download" size={size} />
              <Button type="primary" icon="download" size={size}>Download</Button>
              <br />
              <Button.Group size={size}>
                <Button type="primary">
                  <Icon type="left" />Backward
                </Button>
                <Button type="primary">
                  Forward<Icon type="right" />
                </Button>
              </Button.Group>
            </Card>
            <Card title="多个按钮组合">
              <Button type="primary">primary</Button>
              <Button>secondary</Button>
              <Dropdown overlay={menu}>
                <Button>
                  more <Icon type="down" />
                </Button>
              </Dropdown>
            </Card>
          </Col>
          <Col span={12}>
            <Card title="幽灵按钮" style={{ background: 'rgb(190, 200, 200)' }}>
              <Button type="primary" ghost>Primary</Button>
              <Button ghost>Default</Button>
              <Button type="dashed" ghost>Dashed</Button>
              <Button type="danger" ghost>danger</Button>
            </Card>
            <Card title="不可用状态">
              <Button type="primary">Primary</Button>
              <Button>Default</Button>
              <Button type="dashed">Dashed</Button>
              <Button type="danger">Danger</Button>
              <br /><br />
              <Button type="primary" disabled>Primary(disabled)</Button>
              <Button disabled>Default(disabled)</Button>
              <Button disabled>Ghost(disabled)</Button>
              <Button type="dashed" disabled>Dashed(disabled)</Button>
            </Card>
            <Card title="加载中状态">
              <Button type="primary" loading>
                Loading
              </Button>
              <Button type="primary" size="small" loading>
                Loading
              </Button>
              <br /><br />
              <Button type="primary" loading={this.state.loading} onClick={this.enterLoading}>
                Click me!
              </Button>
              <Button type="primary" icon="poweroff" loading={this.state.iconLoading} onClick={this.enterIconLoading}>
                Click me!
              </Button>
              <br />
              <Button shape="circle" loading />
              <Button type="primary" shape="circle" loading />
            </Card>
            <Card title="按钮组合">
              <h4>Basic</h4>
              <Button.Group>
                <Button>Cancel</Button>
                <Button type="primary">OK</Button>
              </Button.Group>
              <Button.Group>
                <Button disabled>L</Button>
                <Button disabled>M</Button>
                <Button disabled>R</Button>
              </Button.Group>
              <Button.Group>
                <Button type="primary">L</Button>
                <Button>M</Button>
                <Button>M</Button>
                <Button type="dashed">R</Button>
              </Button.Group>

              <h4>With Icon</h4>
              <Button.Group>
                <Button type="primary">
                  <Icon type="left" />Go back
                </Button>
                <Button type="primary">
                  Go forward<Icon type="right" />
                </Button>
              </Button.Group>
              <Button.Group>
                <Button type="primary" icon="cloud" />
                <Button type="primary" icon="cloud-download" />
              </Button.Group>
            </Card>
          </Col>
        </Row>
      </div>
    );
  }
}
export default ButtonView;


<|start_filename|>templates/react-admin/src/component/RootContainer.js<|end_filename|>
/**
 * @file 应用的 Root 容器
 * @author <NAME>
 * @see http://gaearon.github.io/react-hot-loader/getstarted/
 */
import React from 'react';
import { Provider } from 'react-redux';
import { Redirect, Route, Switch } from 'react-router-dom';
import { ConnectedRouter } from 'react-router-redux';
import { store, history, rootRouter } from '../common';

// 返回包裹了路由的 HCFLayout
class WrappedContainer extends React.PureComponent {
  render() {
    const Layout = require('./Layout').default;
    return (
      <Layout>
        <Switch>
          {rootRouter.map((route) => (
            <Route
              key={route.path}
              path={route.path}
              render={props => (
                <route.component {...props} routes={route.routes} />
              )}
            />
          ))}
          <Redirect from="/" to="/home" />
        </Switch>
      </Layout>
    );
  }
}

/**
 * 应用的 Root 容器
 */
export default class RootContainer extends React.PureComponent {
  render() {
    return (
      <Provider store={store}>
        <ConnectedRouter history={history}>
          <Switch>
            <Route path="/" component={WrappedContainer} />
            <Redirect to="/" />
          </Switch>
        </ConnectedRouter>
      </Provider>
    );
  }
}



<|start_filename|>templates/react-admin/config/paths.js<|end_filename|>
const path = require('path');
const fs = require('fs');
const url = require('url');

// 确保解决了项目文件夹中的任何符号链接：
// https://github.com/facebookincubator/create-react-app/issues/637
const appDirectory = fs.realpathSync(process.cwd());
const resolveApp = relativePath => path.resolve(appDirectory, relativePath);

const envPublicUrl = process.env.PUBLIC_URL;

function ensureSlash(servedUrl, needsSlash) {
  const hasSlash = servedUrl.endsWith('/');
  if (hasSlash && !needsSlash) {
    return servedUrl.substr(servedUrl, servedUrl.length - 1);
  } else if (!hasSlash && needsSlash) {
    return `${servedUrl}/`;
  }
  return servedUrl;
}

const getPublicUrl = appPackageJson =>
  envPublicUrl || require(appPackageJson).homepage;

// 我们使用`PUBLIC_URL` 环境变量或 "homepage" 字段推断应用程序的“公共路径”。
// Webpack 需要知道它可以将正确的<script> hrefs 放入 HTML，
// 即使在单页应用程序中，可能为 /todos/42 这样的嵌套URL提供index.html。
// 我们不在 HTML 中使用相对路径，因为我们不想加载类似 /todos/42/static/js/bundle.7289d.js 这样的文件。
// 我们必须知道根路径。
function getServedPath(appPackageJson) {
  const publicUrl = getPublicUrl(appPackageJson);
  const servedUrl =
    envPublicUrl || (publicUrl ? url.parse(publicUrl).pathname : '/');
  return ensureSlash(servedUrl, true);
}

// config after eject: we're in ./config/
module.exports = {
  dotenv: resolveApp('.env'),
  appBuild: resolveApp('build'),
  appConfig: resolveApp('config'),
  appPublic: resolveApp('public'),
  appHtml: resolveApp('public/index.html'),
  appIndexJs: resolveApp('src/index.js'),
  appPackageJson: resolveApp('package.json'),
  appSrc: resolveApp('src'),
  yarnLockFile: resolveApp('yarn.lock'),
  testsSetup: resolveApp('src/setupTests.js'),
  appNodeModules: resolveApp('node_modules'),
  publicUrl: getPublicUrl(resolveApp('package.json')),
  servedPath: getServedPath(resolveApp('package.json'))
};


<|start_filename|>templates/react-admin/src/view/ui/Table/redux/constants.js<|end_filename|>
export const EXAMPLE_BASIC_TABLE_LIST_SEARCH = 'EXAMPLE_BASIC_TABLE_LIST_SEARCH';
export const EXAMPLE_BASIC_TABLE_LIST_SEARCH_SUCCESS = 'EXAMPLE_BASIC_TABLE_LIST_SEARCH_SUCCESS';
export const EXAMPLE_BASIC_TABLE_LIST_SEARCH_FAILED = 'EXAMPLE_BASIC_TABLE_LIST_SEARCH_FAILED';


<|start_filename|>templates/react-admin/src/view/ui/Table/BasicTable.js<|end_filename|>
import { Avatar, Card, Col, Row, Table } from 'antd';
import _ from 'lodash';
import React from 'react';
import { connect } from 'react-redux';
import { withRouter } from 'react-router-dom';
import { bindActionCreators } from 'redux';
import onPersonListSearch from './redux/actions';

class BasicTable extends React.PureComponent {
  state = {
    selectedRowKeys: [],
    filteredInfo: null,
    sortedInfo: null
  };

  componentWillMount() {
    this.props.onPersonListSearch();
  }

  onSelectChange = (selectedRowKeys) => {
    console.log('selectedRowKeys changed: ', selectedRowKeys);
    this.setState({ selectedRowKeys });
  };

  onChange = (pagination, filters, sorter) => {
    console.log('onChange parameters', pagination, filters, sorter);
    this.setState({
      filteredInfo: filters,
      sortedInfo: sorter
    });
  };

  onExpand = (record) => {
    console.log('onExpand', record);
    const nodes = [];
    _.forIn(record, (value, key) => {
      nodes.push({ key, value });
    });

    return (
      <div style={{ marginTop: '12px' }}>
        <Card width="100%" title="详细信息" style={{ fontSize: '16px' }}>
          {nodes.map((node) => {
            return (
              <Row key={node.key}>
                <Col span={6}>
                  <h3 style={{ fontWeight: '600' }}>{node.key}</h3>
                </Col>
                <Col span={6}>
                  <span>{node.value}</span>
                </Col>
              </Row>
            );
          })}
        </Card>
      </div>
    );
  };

  clearAll = () => {
    this.setState({
      filteredInfo: null,
      sortedInfo: null
    });
  };

  render() {
    const { selectedRowKeys } = this.state;
    const { table } = this.props;
    const rowSelection = {
      selectedRowKeys,
      onChange: this.onSelectChange,
      hideDefaultSelections: true,
      selections: [{
        key: 'all-data',
        text: 'Select All Data',
        onSelect: () => {
          this.setState({
            selectedRowKeys: [...Array(46).keys()] // 0...45
          });
        }
      }, {
        key: 'odd',
        text: 'Select Odd Row',
        onSelect: (changableRowKeys) => {
          let newSelectedRowKeys = [];
          newSelectedRowKeys = changableRowKeys.filter((key, index) => {
            if (index % 2 !== 0) {
              return false;
            }
            return true;
          });
          this.setState({ selectedRowKeys: newSelectedRowKeys });
        }
      }, {
        key: 'even',
        text: 'Select Even Row',
        onSelect: (changableRowKeys) => {
          let newSelectedRowKeys = [];
          newSelectedRowKeys = changableRowKeys.filter((key, index) => {
            if (index % 2 !== 0) {
              return true;
            }
            return false;
          });
          this.setState({ selectedRowKeys: newSelectedRowKeys });
        }
      }],
      onSelection: this.onSelection
    };
    let { sortedInfo } = this.state;
    sortedInfo = sortedInfo || {};
    const columns = [{
      title: '头像',
      dataIndex: 'avatar',
      key: 'avatar',
      render(text, record) {
        return (<Avatar shape="square" size="large" src={record.avatar} icon="user" />);
      }
    }, {
      title: '登录名',
      dataIndex: 'loginName',
      key: 'loginName',
      sorter: (a, b) => a.loginName.length - b.loginName.length,
      sortOrder: sortedInfo.columnKey === 'loginName' && sortedInfo.order
    }, {
      title: '姓名',
      dataIndex: 'name',
      key: 'name',
      sorter: (a, b) => a.name.length - b.name.length,
      sortOrder: sortedInfo.columnKey === 'name' && sortedInfo.order
    }, {
      title: '生日',
      dataIndex: 'birthday',
      key: 'birthday',
      sorter: (a, b) => a.birthday - b.birthday,
      sortOrder: sortedInfo.columnKey === 'birthday' && sortedInfo.order
    }, {
      title: '邮件',
      dataIndex: 'email',
      key: 'email'
    }, {
      title: '电话',
      dataIndex: 'tel',
      key: 'tel'
    }, {
      title: '手机',
      dataIndex: 'mobile',
      key: 'mobile'
    }, {
      title: '地址',
      dataIndex: 'address',
      key: 'address'
    }];
    return (
      <div>
        <Card title="基本表格示例">
          <Table
            rowKey="loginName"
            rowSelection={rowSelection}
            columns={columns}
            dataSource={table.list}
            loading={table.loading}
            onChange={this.onChange}
            expandedRowRender={this.onExpand}
          />
        </Card>
      </div>
    );
  }
}
function mapStateToProps(state) {
  return {
    table: state.table
  };
}
function mapDispatchToProps(dispatch) {
  return {
    onPersonListSearch: bindActionCreators(onPersonListSearch, dispatch)
  };
}
export default withRouter(connect(mapStateToProps, mapDispatchToProps)(BasicTable));


<|start_filename|>templates/react-admin/src/view/form/route.js<|end_filename|>
import AdvancedForm from './AdvancedForm';
import FormResult from './AdvancedForm/FormResult';
import BasicForm from './BasicForm';
import StepForm from './StepForm';
import Step1 from './StepForm/Step1';
import Step2 from './StepForm/Step2';
import Step3 from './StepForm/Step3';

export default [{
  path: '/form/basic',
  component: BasicForm
}, {
  path: '/form/advanced',
  component: AdvancedForm
}, {
  path: '/form/result',
  component: FormResult
}, {
  path: '/form/step',
  component: StepForm
}, {
  path: '/form/step/first',
  component: Step1
}, {
  path: '/form/step/second',
  component: Step2
}, {
  path: '/form/step/third',
  component: Step3
}];


<|start_filename|>templates/react-admin/src/util/http/mock/ui/table/basic.js<|end_filename|>
/**
 * `/example/table/basic/list` 的 Mock 数据
 * @see https://github.com/nuysoft/Mock/wiki
 * @see http://mockjs.com/examples.html
 */
const Mock = require('mockjs');

const Random = Mock.Random;

const data = [];
for (let i = 0; i < 46; i += 1) {
  const loginName = Random.name();
  data.push({
    id: i,
    avatar: Random.image('64x64', Random.color(), '#ffffff', loginName[0]),
    loginName,
    password: <PASSWORD>(),
    name: Random.cname(),
    idCard: Random.id(),
    birthday: Random.date('yyyy-MM-dd'),
    email: Random.email(),
    tel: `0${Random.string('number', 2, 3)}-${Random.string('number', 7, 7)}`,
    mobile: Random.string('number', 11, 11),
    address: Random.county(true),
    zip: Random.zip(),
    statu: Random.integer(0, 1),
    createTime: Random.datetime('yyyy-MM-dd HH:mm:ss'),
    updateTime: Random.datetime('yyyy-MM-dd HH:mm:ss'),
    description: Random.cparagraph(3, 10)
  });
}

module.exports = {
  code: 0,
  message: '成功',
  data
};


<|start_filename|>templates/react-admin/src/index.js<|end_filename|>
/**
 * @file App 的总入口
 * @author <NAME>
 * @see http://gaearon.github.io/react-hot-loader/getstarted/
 */
import React from 'react';
import ReactDOM from 'react-dom';
import { AppContainer } from 'react-hot-loader';
import 'react-hot-loader/patch';
import RootContainer from './component/RootContainer';
import './style/index.less';

const render = (Container) => {
  ReactDOM.render(
    <AppContainer>
      <Container />
    </AppContainer>,
    document.getElementById('root')
  );
};

// 初次启动 App
render(RootContainer);

// 热替换启动 App
if (module.hot) {
  module.hot.accept('./component/RootContainer', () => {
    const NextRootContainer = require('./component/RootContainer');
    render(NextRootContainer);
  });
}


<|start_filename|>templates/react-admin/src/view/ui/route.js<|end_filename|>
import AlertView from './Alert';
import ButtonView from './Button';
import IconView from './Icon';
import ModalView from './Modal';
import TableView from './Table';

export default [{
  path: '/ui/general/button',
  component: ButtonView
}, {
  path: '/ui/general/icon',
  component: IconView
}, {
  path: '/ui/data/table',
  component: TableView
}, {
  path: '/ui/feedback/alert',
  component: AlertView
}, {
  path: '/ui/feedback/modal',
  component: ModalView
}];
