Selaa lähdekoodia

:fire: 渠道管理平台多余代码移除及部分模块修改

zhanghe 6 vuotta sitten
vanhempi
commit
af0bd66f1c
79 muutettua tiedostoa jossa 2525 lisäystä ja 3798 poistoa
  1. 1 1
      .webpackrc
  2. 4 4
      package.json
  3. 11 17
      src/common/menu.js
  4. 19 33
      src/common/router.js
  5. 95 0
      src/components/AXItem/CourseItem.js
  6. 78 0
      src/components/AXItem/CourseItem.less
  7. 40 0
      src/components/AXItem/PackageItem.js
  8. 75 0
      src/components/AXItem/SupportItem.js
  9. 78 0
      src/components/AXItem/SupportItem.less
  10. 33 0
      src/components/AXItem/TrainingItem.js
  11. 80 0
      src/components/AXItem/TrainingItem.less
  12. 5 1
      src/components/AXItem/index.js
  13. 12 12
      src/components/AXList/StandardTableList.js
  14. 36 8
      src/components/AXTableSelector/MultipleSelectTable.js
  15. 5 2
      src/components/AXTableSelector/MultipleSelectTable.less
  16. 8 8
      src/components/AXTableSelector/Selector.js
  17. 19 31
      src/components/AXTableSelector/SingleSelectTable.js
  18. 49 1
      src/components/AXTableSelector/columnsMap.js
  19. 0 1
      src/components/AXTableSelector/columnsMap.less
  20. 1 1
      src/components/AXUpload/index.js
  21. 7 0
      src/components/AXUpload/index.less
  22. 5 5
      src/components/GlobalHeader/index.less
  23. 1 0
      src/components/HeaderSearch/index.less
  24. 1 1
      src/components/SiderMenu/SiderMenu.js
  25. 7 7
      src/components/SiderMenu/index.less
  26. 1 1
      src/index.ejs
  27. 1 1
      src/index.less
  28. 0 28
      src/models/activities.js
  29. 0 61
      src/models/chart.js
  30. 0 103
      src/models/cmsUser.js
  31. 0 197
      src/models/configUser.js
  32. 0 103
      src/models/courseware.js
  33. 0 103
      src/models/lesson.js
  34. 0 225
      src/models/merchant.js
  35. 0 28
      src/models/monitor.js
  36. 0 28
      src/models/project.js
  37. 0 32
      src/models/register.js
  38. 0 47
      src/models/rule.js
  39. 0 103
      src/models/tagGroup.js
  40. 154 1
      src/models/terminal.js
  41. 1 1
      src/router.js
  42. 2 96
      src/routes/Campus/CampusCreate.js
  43. 28 12
      src/routes/Campus/CampusList.js
  44. 0 156
      src/routes/Frontend/ConfigUser/ConfigRecommendCourse.js
  45. 0 11
      src/routes/Frontend/ConfigUser/ConfigRecommendPoster.js
  46. 0 564
      src/routes/Frontend/ConfigUser/ConfigTag.js
  47. 0 67
      src/routes/Frontend/ConfigUser/ConfigTag.less
  48. 0 188
      src/routes/Frontend/ConfigUser/ConfigUserLists.js
  49. 688 0
      src/routes/Frontend/Personalize/PersonalizeEdit.js
  50. 26 0
      src/routes/Frontend/Personalize/PersonalizeEdit.less
  51. 263 0
      src/routes/Frontend/Personalize/PersonalizeList.js
  52. 2 2
      src/routes/Frontend/ConfigUser/index.js
  53. 35 0
      src/routes/Resource/Picture/PictureTableList.less
  54. 36 2
      src/routes/Shelves/ShelvesEdit.js
  55. 7 16
      src/routes/Shelves/ShelvesList.js
  56. 0 347
      src/routes/System/CmsUser/CmsUserCreate.js
  57. 0 72
      src/routes/System/CmsUser/CmsUserCreate.less
  58. 0 339
      src/routes/System/CmsUser/CmsUserEdit.js
  59. 0 233
      src/routes/System/CmsUser/CmsUserList.js
  60. 240 0
      src/routes/Terminal/Auth/AuthList.js
  61. 10 0
      src/routes/Terminal/Auth/AuthList.less
  62. 2 3
      src/routes/System/CmsUser/index.js
  63. 1 2
      src/routes/Terminal/User/TerminalCreate.js
  64. 20 31
      src/routes/Terminal/User/TerminalEdit.js
  65. 60 43
      src/routes/Terminal/User/TerminalList.js
  66. 17 16
      src/routes/Trade/Order/OrderCreate.js
  67. 24 22
      src/routes/Trade/ShopCart/ShopCartDetail.js
  68. 41 21
      src/routes/Trade/ShopCart/ShopCartList.js
  69. 5 2
      src/services/campus.js
  70. 0 34
      src/services/cmsUser.js
  71. 0 104
      src/services/configUser.js
  72. 0 38
      src/services/courseware.js
  73. 0 38
      src/services/lesson.js
  74. 0 89
      src/services/merchant.js
  75. 0 38
      src/services/tagGroup.js
  76. 165 0
      src/services/terminal.js
  77. 2 9
      src/theme.js
  78. 7 5
      src/utils/config.js
  79. 17 3
      src/utils/utils.js

+ 1 - 1
.webpackrc

@@ -17,5 +17,5 @@
   },
   "publicPath": "/",
   "disableDynamicImport": true,
-  "hash": false
+  "hash": true
 }

+ 4 - 4
package.json

@@ -5,9 +5,9 @@
   "private": true,
   "scripts": {
     "precommit": "npm run lint-staged",
-    "start": "cross-env DISABLE_ESLINT=true roadhog dev",
-    "start:no-proxy": "cross-env NO_PROXY=true DISABLE_ESLINT=true roadhog dev",
-    "build": "cross-env NODE_ENV=production DISABLE_ESLINT=true roadhog build",
+    "start": "cross-env ESLINT=none roadhog dev",
+    "start:no-proxy": "cross-env NO_PROXY=true ESLINT=none roadhog dev",
+    "build": "cross-env NODE_ENV=production ESLINT=none roadhog build",
     "site": "roadhog-api-doc static && gh-pages -d dist",
     "analyze": "cross-env ANALYZE=true roadhog build",
     "lint:style": "stylelint \"src/**/*.less\" --syntax less",
@@ -22,7 +22,7 @@
   "dependencies": {
     "@antv/data-set": "^0.8.0",
     "@babel/polyfill": "^7.0.0-beta.36",
-    "antd": "^3.3.0",
+    "antd": "^3.6.3",
     "babel-runtime": "^6.9.2",
     "bizcharts": "^3.1.3-beta.1",
     "bizcharts-plugin-slider": "^2.0.1",

+ 11 - 17
src/common/menu.js

@@ -16,7 +16,7 @@ const menuData = () => {
       path: 'sold',
       icon: <AXIcon type="action" />,
     }],
-    authority: ['admin', 'platform'],
+    authority: ['admin', 'channel'],
   }, {
     name: '产品库',
     icon: 'shop',
@@ -34,16 +34,16 @@ const menuData = () => {
       name: '套餐包',
       path: 'package',
     }],
-    authority: ['admin', 'platform'],
+    authority: ['admin', 'channel'],
   }, {
     name: '前端配置',
     icon: 'android-o',
     path: 'frontend',
     children: [{
-      name: '用户配置',
-      path: 'ConfigUser',
+      name: '个性化配置',
+      path: 'personalize',
     }],
-    authority: ['admin', 'platform'],
+    authority: ['admin', 'channel'],
   }, {
     name: '交易管理',
     icon: 'trademark',
@@ -57,7 +57,7 @@ const menuData = () => {
       icon: <AXIcon type="order" />,
       path: 'order',
     }],
-    authority: ['admin', 'platform'],
+    authority: ['admin', 'channel'],
   }, {
     name: '校区管理',
     icon: <AXIcon type="campus" />,
@@ -66,7 +66,7 @@ const menuData = () => {
       name: '校区列表',
       path: 'list',
     }],
-    authority: ['admin', 'platform', 'channel'],
+    authority: ['admin', 'channel'],
   }, {
     name: '终端管理',
     path: 'terminal',
@@ -74,17 +74,11 @@ const menuData = () => {
     children: [{
       name: '终端用户',
       path: 'user',
+    }, {
+      name: '权限列表',
+      path: 'auth',
     }],
-    authority: ['admin', 'platform', 'channel'],
-  }, {
-    name: '系统管理',
-    path: 'system',
-    icon: <AXIcon type="systemuser" />,
-    children: [{
-      name: '系统用户',
-      path: 'cms-user',
-    }],
-    authority: 'admin',
+    authority: ['admin', 'channel'],
   }];
 };
 

+ 19 - 33
src/common/router.js

@@ -75,36 +75,23 @@ export const getRouterData = (app) => {
     },
     // 校区管理相关路由注册
     '/campus/list': {
-      component: dynamicWrapper(app, ['campus', 'merchant'], () => import('../routes/Campus/CampusList')),
+      component: dynamicWrapper(app, ['campus'], () => import('../routes/Campus/CampusList')),
       name: '校区列表',
     },
     '/campus/create': {
-      component: dynamicWrapper(app, ['campus', 'merchant'], () => import('../routes/Campus/CampusCreate')),
+      component: dynamicWrapper(app, ['campus'], () => import('../routes/Campus/CampusCreate')),
       name: '添加校区',
     },
     '/campus/edit/:id': {
-      component: dynamicWrapper(app, ['campus', 'merchant'], () => import('../routes/Campus/CampusCreate')),
+      component: dynamicWrapper(app, ['campus'], () => import('../routes/Campus/CampusCreate')),
       name: '编辑校区',
     },
-    // 系统管理相关路由注册
-    '/system/cms-user': {
-      component: dynamicWrapper(app, ['cmsUser'], () => import('../routes/System/CmsUser')),
-    },
-    '/system/cms-user/list': {
-      component: dynamicWrapper(app, ['cmsUser'], () => import('../routes/System/CmsUser/CmsUserList')),
-    },
-    '/system/cms-user/create': {
-      component: dynamicWrapper(app, ['cmsUser', 'merchant'], () => import('../routes/System/CmsUser/CmsUserCreate')),
-    },
-    '/system/cms-user/edit/:id': {
-      component: dynamicWrapper(app, ['cmsUser'], () => import('../routes/System/CmsUser/CmsUserEdit')),
-    },
     // 终端管理相关路由注册
     '/terminal/user': {
-      component: dynamicWrapper(app, ['terminal', 'campus', 'merchant'], () => import('../routes/Terminal/User')),
+      component: dynamicWrapper(app, ['terminal', 'campus'], () => import('../routes/Terminal/User')),
     },
     '/terminal/user/list': {
-      component: dynamicWrapper(app, ['terminal', 'campus', 'merchant'], () => import('../routes/Terminal/User/TerminalList')),
+      component: dynamicWrapper(app, ['terminal', 'campus'], () => import('../routes/Terminal/User/TerminalList')),
     },
     '/terminal/user/create': {
       component: dynamicWrapper(app, ['terminal', 'campus'], () => import('../routes/Terminal/User/TerminalCreate')),
@@ -112,6 +99,12 @@ export const getRouterData = (app) => {
     '/terminal/user/edit/:id': {
       component: dynamicWrapper(app, ['terminal'], () => import('../routes/Terminal/User/TerminalEdit')),
     },
+    '/terminal/auth': {
+      component: dynamicWrapper(app, ['terminal'], () => import('../routes/Terminal/Auth')),
+    },
+    '/terminal/auth/list': {
+      component: dynamicWrapper(app, ['terminal'], () => import('../routes/Terminal/Auth/AuthList')),
+    },
     // 产品出售相关路由注册
     '/shelves/course': {
       component: dynamicWrapper(app, ['shelves'], () => import('../routes/Shelves')),
@@ -124,7 +117,7 @@ export const getRouterData = (app) => {
     },
     // 查看页面
     '/shelves/course/edit': {
-      component: dynamicWrapper(app, ['shelves', 'resource'], () => import('../routes/Shelves/ShelvesEdit')),
+      component: dynamicWrapper(app, ['shelves', 'product', 'resource'], () => import('../routes/Shelves/ShelvesEdit')),
     },
     '/shelves/support': {
       component: dynamicWrapper(app, ['shelves'], () => import('../routes/Shelves')),
@@ -163,27 +156,20 @@ export const getRouterData = (app) => {
       component: dynamicWrapper(app, ['shelves', 'resource'], () => import('../routes/Shelves/ShelvesEdit')),
     },
     // 前端管理相关路由注册
-    '/frontend/ConfigUser': {
-      component: dynamicWrapper(app, ['configUser'], () => import('../routes/Frontend/ConfigUser')),
-    },
-    '/frontend/ConfigUser/list': {
-      component: dynamicWrapper(app, ['configUser', 'terminal'], () => import('../routes/Frontend/ConfigUser/ConfigUserLists')),
-    },
-    '/frontend/ConfigUser/tag/:id': {
-      component: dynamicWrapper(app, ['configUser', 'tag', 'tagType'], () => import('../routes/Frontend/ConfigUser/ConfigTag')),
+    '/frontend/personalize': {
+      component: dynamicWrapper(app, ['terminal'], () => import('../routes/Frontend/Personalize')),
     },
-    '/frontend/ConfigUser/course-edit/:id': {
-      component: dynamicWrapper(app, ['configUser', 'tag', 'tagType'], () => import('../routes/Frontend/ConfigUser/ConfigRecommendCourse')),
+    '/frontend/personalize/list': {
+      component: dynamicWrapper(app, ['terminal', 'campus'], () => import('../routes/Frontend/Personalize/PersonalizeList')),
     },
-    '/frontend/ConfigUser/poster-edit/:id': {
-      component: dynamicWrapper(app, ['configUser', 'tag', 'tagType'], () => import('../routes/Frontend/ConfigUser/ConfigRecommendPoster')),
+    '/frontend/personalize/edit/:id': {
+      component: dynamicWrapper(app, ['terminal', 'tag', 'tagType', 'shelves'], () => import('../routes/Frontend/Personalize/PersonalizeEdit')),
     },
-    // 交易管理
     '/trade/shopcart': {
       component: dynamicWrapper(app, [], () => import('../routes/Trade/ShopCart')),
     },
     '/trade/shopcart/list': {
-      component: dynamicWrapper(app, ['terminal', 'campus', 'merchant'], () => import('../routes/Trade/ShopCart/ShopCartList')),
+      component: dynamicWrapper(app, ['terminal', 'campus'], () => import('../routes/Trade/ShopCart/ShopCartList')),
     },
     '/trade/shopcart/detail/:id': {
       component: dynamicWrapper(app, ['trade'], () => import('../routes/Trade/ShopCart/ShopCartDetail')),

+ 95 - 0
src/components/AXItem/CourseItem.js

@@ -0,0 +1,95 @@
+import React, { Component } from 'react';
+import { Row, Col, Card, Table, Radio } from 'antd';
+import { genAbsolutePicUrl } from '../../utils/utils';
+import styles from './CourseItem.less';
+
+function CourseTpl({ title, subTitle, coverUrl }) {
+  return (
+    <div className={styles.content}>
+      <div className={styles.courseWrapper}>
+        <img src={genAbsolutePicUrl(coverUrl)} alt="" />
+        <div className={styles.desc}>
+          <div className={styles.title}>{title}</div>
+          <div className={styles.subTitle}>{subTitle}</div>
+        </div>
+      </div>
+    </div>
+  );
+}
+
+class CourseItem extends Component {
+  state = {
+    openKey: 'lesson',
+  };
+  handleRadioChange = (e) => {
+    this.setState({ openKey: e.target.value });
+  };
+  render() {
+    const { subItemList, supportList } = this.props;
+    const lessonColumns = [{
+      title: '课编号',
+      dataIndex: 'code',
+      key: 1,
+      width: '50%',
+    }, {
+      title: '课名称',
+      dataIndex: 'title',
+      key: 2,
+      width: '50%',
+    }];
+    const supportColumns = [{
+      title: '配套编号',
+      dataIndex: 'code',
+      key: 1,
+      width: '50%',
+    }, {
+      title: '配套名称',
+      dataIndex: 'name',
+      key: 2,
+      width: '50%',
+    }];
+    return (
+      <Row gutter={16} style={{ marginBottom: 16 }}>
+        <Col span={9}>
+          <Card title="课程信息" style={{ height: 540 }} className={styles.coverCard}>
+            <CourseTpl {...this.props} />
+          </Card>
+        </Col>
+        <Col span={15}>
+          <Card
+            title={
+              <Radio.Group
+                value={this.state.openKey}
+                onChange={this.handleRadioChange}
+              >
+                <Radio.Button value="lesson">课列表</Radio.Button>
+                <Radio.Button value="support">周边配套</Radio.Button>
+              </Radio.Group>
+            }
+            style={{ height: 540 }}
+          >{this.state.openKey === 'lesson' ? (
+            <Table
+              pagination={false}
+              rowKey={record => record.id}
+              dataSource={subItemList}
+              columns={lessonColumns}
+              scroll={{ y: 400 }}
+              className={styles.dataTable}
+            />
+          ) : (
+            <Table
+              pagination={false}
+              rowKey={record => record.id}
+              dataSource={supportList}
+              columns={supportColumns}
+              scroll={{ y: 400 }}
+              className={styles.dataTable}
+            />
+          )}
+          </Card>
+        </Col>
+      </Row>
+    );
+  }
+}
+export default CourseItem;

+ 78 - 0
src/components/AXItem/CourseItem.less

@@ -0,0 +1,78 @@
+@import "~antd/lib/style/themes/default.less";
+
+.coverCard {
+  background: #d9d9d9;
+  :global(.ant-card) {
+    height: 500px;
+  }
+  :global(.ant-card-body) {
+    position: relative;
+    text-align: center;
+    height: 480px;
+    overflow: hidden;
+    .content {
+      width: 320px;
+      height: 458px;
+      position: absolute;
+      top: 0;
+      bottom: 0;
+      left: 0;
+      right: 0;
+      margin: auto;
+    }
+  }
+}
+.dataTable {
+  :global {
+    .ant-table-title {
+      padding: 0 0 16px 0;
+    }
+    .ant-table-footer {
+      padding: 10px;
+    }
+    .ant-table-tbody > tr > td {
+      padding: 5px;
+    }
+    .ant-table-thead > tr > th {
+      padding: 10px 5px;
+    }
+  }
+}
+
+.courseWrapper {
+  width: 320px;
+  height: 458px;
+  border: solid 5px #36cfc9;
+  border-radius: 29px;
+  position: relative;
+  img {
+    position: inherit;
+    width: 100%;
+    height: 100%;
+    border-radius: 25px;
+  }
+  .desc {
+    position: absolute;
+    bottom: 0;
+    width: 100%;
+    height: 112px;
+    background: #fff;
+    border-radius: 0 0 20px 20px;
+    .title {
+      width: 100%;
+      margin-left: 6px;
+      font-size: 36px;
+      white-space: nowrap;
+      overflow: hidden;
+      text-align: center;
+    }
+    .subTitle {
+      width: 100%;
+      margin-left: 6px;
+      font-size: 30px;
+      white-space: nowrap;
+      overflow: hidden;
+      text-align: center;
+    }
+  }
+}

+ 40 - 0
src/components/AXItem/PackageItem.js

@@ -0,0 +1,40 @@
+import React, { Component } from 'react';
+import { Card, Table } from 'antd';
+import { renderProductType } from '../../utils/utils';
+import styles from './CourseItem.less';
+
+class PackageItem extends Component {
+  render() {
+    const { products } = this.props;
+    const packageColumns = [{
+      title: '产品编号',
+      dataIndex: 'code',
+      key: 1,
+      width: '40%',
+    }, {
+      title: '产品名称',
+      dataIndex: 'name',
+      key: 2,
+      width: '40%',
+    }, {
+      title: '产品类型',
+      dataIndex: 'type',
+      key: 3,
+      width: '20%',
+      render: text => renderProductType(text),
+    }];
+    return (
+      <Card title="套餐包内容" style={{ height: 450, marginBottom: 16 }}>
+        <Table
+          pagination={false}
+          rowKey={record => record.id}
+          dataSource={products}
+          columns={packageColumns}
+          scroll={{ y: 300 }}
+          className={styles.dataTable}
+        />
+      </Card>
+    );
+  }
+}
+export default PackageItem;

+ 75 - 0
src/components/AXItem/SupportItem.js

@@ -0,0 +1,75 @@
+import React, { Component } from 'react';
+import { Row, Col, Card, Table, Radio } from 'antd';
+import { genAbsolutePicUrl } from '../../utils/utils';
+import styles from './SupportItem.less';
+
+function SupportTpl({ title, subTitle, coverUrl }) {
+  return (
+    <div className={styles.content}>
+      <div className={styles.courseWrapper}>
+        <img src={genAbsolutePicUrl(coverUrl)} alt="" />
+        <div className={styles.desc}>
+          <div className={styles.title}>{title}</div>
+          <div className={styles.subTitle}>{subTitle}</div>
+        </div>
+      </div>
+    </div>
+  );
+}
+
+class SupportItem extends Component {
+  state = {
+    openKey: 'support',
+  };
+  handleRadioChange = (e) => {
+    this.setState({ openKey: e.target.value });
+  };
+  render() {
+    const { supportList } = this.props;
+    const supportColumns = [{
+      title: '配套编号',
+      dataIndex: 'code',
+      key: 1,
+      width: '50%',
+    }, {
+      title: '配套名称',
+      dataIndex: 'name',
+      key: 2,
+      width: '50%',
+    }];
+    return (
+      <Row gutter={16} style={{ marginBottom: 16 }}>
+        <Col span={9}>
+          <Card title="配套信息" style={{ height: 540 }} className={styles.coverCard}>
+            <SupportTpl {...this.props} />
+          </Card>
+        </Col>
+        <Col span={15}>
+          <Card
+            title={
+              <Radio.Group
+                value={this.state.openKey}
+                onChange={this.handleRadioChange}
+              >
+                <Radio.Button value="support">相关配套</Radio.Button>
+              </Radio.Group>
+            }
+            style={{ height: 540 }}
+          >
+            {this.state.openKey === 'support' ? (
+              <Table
+                pagination={false}
+                rowKey={record => record.id}
+                dataSource={supportList}
+                columns={supportColumns}
+                scroll={{ y: 400 }}
+                className={styles.dataTable}
+              />
+            ) : null}
+          </Card>
+        </Col>
+      </Row>
+    );
+  }
+}
+export default SupportItem;

+ 78 - 0
src/components/AXItem/SupportItem.less

@@ -0,0 +1,78 @@
+@import "~antd/lib/style/themes/default.less";
+
+.coverCard {
+  background: #d9d9d9;
+  :global(.ant-card) {
+    height: 500px;
+  }
+  :global(.ant-card-body) {
+    position: relative;
+    text-align: center;
+    height: 480px;
+    overflow: hidden;
+    .content {
+      width: 320px;
+      height: 458px;
+      position: absolute;
+      top: 0;
+      bottom: 0;
+      left: 0;
+      right: 0;
+      margin: auto;
+    }
+  }
+}
+.dataTable {
+  :global {
+    .ant-table-title {
+      padding: 0 0 16px 0;
+    }
+    .ant-table-footer {
+      padding: 10px;
+    }
+    .ant-table-tbody > tr > td {
+      padding: 5px;
+    }
+    .ant-table-thead > tr > th {
+      padding: 10px 5px;
+    }
+  }
+}
+
+.courseWrapper {
+  width: 320px;
+  height: 458px;
+  border: solid 5px #36cfc9;
+  border-radius: 29px;
+  position: relative;
+  img {
+    position: inherit;
+    width: 100%;
+    height: 100%;
+    border-radius: 25px;
+  }
+  .desc {
+    position: absolute;
+    bottom: 0;
+    width: 100%;
+    height: 112px;
+    background: #fff;
+    border-radius: 0 0 20px 20px;
+    .title {
+      width: 100%;
+      margin-left: 6px;
+      font-size: 36px;
+      white-space: nowrap;
+      overflow: hidden;
+      text-align: center;
+    }
+    .subTitle {
+      width: 100%;
+      margin-left: 6px;
+      font-size: 30px;
+      white-space: nowrap;
+      overflow: hidden;
+      text-align: center;
+    }
+  }
+}

+ 33 - 0
src/components/AXItem/TrainingItem.js

@@ -0,0 +1,33 @@
+import React, { Component } from 'react';
+import { Card } from 'antd';
+import { genAbsolutePicUrl } from '../../utils/utils';
+import styles from './TrainingItem.less';
+
+function TrainingTpl({ title, dateDesc, coverUrl }) {
+  return (
+    <div className={styles.content}>
+      <div className={styles.courseWrapper}>
+        <img src={genAbsolutePicUrl(coverUrl)} alt="" />
+        <div className={styles.desc}>
+          <div className={styles.title}>{title}</div>
+          <div className={styles.date}>{dateDesc}</div>
+        </div>
+      </div>
+    </div>
+  );
+}
+
+class TrainingItem extends Component {
+  render() {
+    return (
+      <Card
+        title="师训信息"
+        style={{ height: 540, marginBottom: 16 }}
+        className={styles.coverCard}
+      >
+        <TrainingTpl {...this.props} />
+      </Card>
+    );
+  }
+}
+export default TrainingItem;

+ 80 - 0
src/components/AXItem/TrainingItem.less

@@ -0,0 +1,80 @@
+@import "~antd/lib/style/themes/default.less";
+
+.coverCard {
+  background: #d9d9d9;
+  :global(.ant-card) {
+    height: 500px;
+  }
+  :global(.ant-card-body) {
+    position: relative;
+    text-align: center;
+    height: 480px;
+    overflow: hidden;
+    .content {
+      width: 650px;
+      height: 400px;
+      position: absolute;
+      top: 0;
+      bottom: 0;
+      left: 0;
+      right: 0;
+      margin: auto;
+    }
+  }
+}
+.dataTable {
+  :global {
+    .ant-table-title {
+      padding: 0 0 16px 0;
+    }
+    .ant-table-footer {
+      padding: 10px;
+    }
+    .ant-table-tbody > tr > td {
+      padding: 5px;
+    }
+    .ant-table-thead > tr > th {
+      padding: 10px 5px;
+    }
+  }
+}
+
+.courseWrapper {
+  width: 655px;
+  height: 400px;
+  border: solid 5px #36cfc9;
+  border-radius: 30px;
+  position: relative;
+  img {
+    position: inherit;
+    width: 100%;
+    height: 100%;
+    border-radius: 25px;
+  }
+  .desc {
+    position: absolute;
+    bottom: 0;
+    width: 100%;
+    height: 96px;
+    background: #fff;
+    border-radius: 0 0 20px 20px;
+    .title {
+      width: 100%;
+      margin-top: 5px;
+      margin-left: 32px;
+      font-size: 28px;
+      white-space: nowrap;
+      overflow: hidden;
+      text-align: left;
+    }
+    .date {
+      width: 100%;
+      margin-top: 5px;
+      margin-left: 32px;
+      font-size: 28px;
+      white-space: nowrap;
+      overflow: hidden;
+      text-align: left;
+    }
+  }
+}

+ 5 - 1
src/components/AXItem/index.js

@@ -1,3 +1,7 @@
 import PictureItem from './PictureItem';
+import CourseItem from './CourseItem';
+import SupportItem from './SupportItem';
+import TrainingItem from './TrainingItem';
+import PackageItem from './PackageItem';
 
-export { PictureItem };
+export { PictureItem, CourseItem, SupportItem, TrainingItem, PackageItem };

+ 12 - 12
src/components/AXList/StandardTableList.js

@@ -10,16 +10,6 @@ function getSearchField(options) {
 }
 
 export default class StandardTableList extends PureComponent {
-  static defaultProps = {
-    keepUIState: {},
-    loading: false,
-    dataSource: [],
-    columns: [],
-    header: false,
-    footer: false,
-    showStatusSelect: true,
-    rowSelectable: true,
-  };
   static propTypes = {
     loading: PropTypes.bool,
     dataSource: PropTypes.array,
@@ -36,6 +26,16 @@ export default class StandardTableList extends PureComponent {
     rowSelectable: PropTypes.bool,
     keepUIState: PropTypes.object,
   };
+  static defaultProps = {
+    keepUIState: {},
+    loading: false,
+    dataSource: [],
+    columns: [],
+    header: false,
+    footer: false,
+    showStatusSelect: true,
+    rowSelectable: true,
+  };
   constructor(props) {
     super(props);
     const {
@@ -141,7 +141,7 @@ export default class StandardTableList extends PureComponent {
               总计 <a style={{ fontWeight: 600 }}>{pagination.totalSize}</a> 项&nbsp;&nbsp;
             </Fragment>
           )}
-          type="info"
+          type="success"
           showIcon
         />
       </div>
@@ -262,7 +262,7 @@ export default class StandardTableList extends PureComponent {
     this.setState({
       searchInputValue: value,
     }, () =>
-      this.handleFilterOperation({ [searchSelectKey]: value })
+      this.handleFilterOperation({ [searchSelectKey]: value, pageNo: 1 })
     );
   };
   // 刷新操作

+ 36 - 8
src/components/AXTableSelector/MultipleSelectTable.js

@@ -4,12 +4,6 @@ import { Table, Pagination, Button } from 'antd';
 import styles from './MultipleSelectTable.less';
 
 export default class MultipleSelectTable extends Component {
-  static defaultProps = {
-    loading: false,
-    columns: [],
-    dataSource: [],
-    pagination: false,
-  };
   static propTypes = {
     loading: PropTypes.bool,
     columns: PropTypes.array,
@@ -19,11 +13,17 @@ export default class MultipleSelectTable extends Component {
       PropTypes.bool,
     ]),
   };
+  static defaultProps = {
+    loading: false,
+    columns: [],
+    dataSource: [],
+    pagination: false,
+  };
   constructor(props) {
     super(props);
     this.state = {
-      selectedRows: props.selectedRows,
-      selectedRowKeys: props.selectedRowKeys,
+      selectedRows: props.selectedRows || [],
+      selectedRowKeys: props.selectedRowKeys || [],
     };
   }
 
@@ -70,18 +70,45 @@ export default class MultipleSelectTable extends Component {
   handleTableChange = (page, pageSize) => {
     this.props.onChange(page, pageSize);
   };
+  /**
+   * 响应行的点击动作
+   * @param record
+   */
+  handleOnRowClick = (record) => {
+    const { selectedRows, selectedRowKeys } = this.state;
+    const newSelectedRowKeys = [...selectedRowKeys];
+    const newSelectedRows = [...selectedRows];
+    const targetIndex = selectedRowKeys.indexOf(record.key);
+    if (targetIndex !== -1) {
+      newSelectedRowKeys.splice(targetIndex, 1);
+      newSelectedRows.splice(targetIndex, 1);
+    } else {
+      newSelectedRowKeys.push(record.key);
+      newSelectedRows.push(record);
+    }
+    this.setState({
+      selectedRowKeys: newSelectedRowKeys,
+      selectedRows: newSelectedRows,
+    });
+  };
 
   render() {
     const { loading, columns, dataSource, pagination, tableType } = this.props;
     const { selectedRowKeys } = this.state;
 
     const rowSelection = {
+      type: 'checkbox',
       selectedRowKeys,
       onChange: this.handleRowSelectChange,
       getCheckboxProps: record => ({
         disabled: record.disabled,
       }),
     };
+    const onRowClick = (record) => {
+      return {
+        onClick: () => this.handleOnRowClick(record),
+      };
+    };
 
     const renderTableFooter = (paginationProps) => {
       if (paginationProps) {
@@ -109,6 +136,7 @@ export default class MultipleSelectTable extends Component {
             footer={() => renderTableFooter(pagination)}
             rowKey={record => record.key}
             rowSelection={rowSelection}
+            onRow={onRowClick}
             onChange={this.handleTableChange}
             scroll={{ y: 350 }}
           />

+ 5 - 2
src/components/AXTableSelector/MultipleSelectTable.less

@@ -1,8 +1,6 @@
 @import "~antd/lib/style/themes/default.less";
 
 .container {
-  .tableWrapper {
-  }
   .buttonWrapper {
     height: 50px;
     line-height: 50px;
@@ -28,5 +26,10 @@
       height: 46px;
       padding: 5px;
     }
+    .ant-table-row {
+      &:hover {
+        cursor: pointer;
+      }
+    }
   }
 }

+ 8 - 8
src/components/AXTableSelector/Selector.js

@@ -16,18 +16,12 @@ export default class Selector extends PureComponent {
     const rows = addRowKey(props.selectedRows || []);
     const rowKeys = getRowKeys(rows || []);
     this.state = {
-      searchKey: 'name',
-      searchValue: '',
+      searchKey: 'code',
+      searchValue: undefined,
       selectedRows: rows,
       selectedRowKeys: rowKeys,
     };
   }
-  componentWillReceiveProps(nextProps) {
-    this.setState({
-      selectedRows: addRowKey(nextProps.selectedRows),
-      selectedRowKeys: getRowKeys(addRowKey(nextProps.selectedRows) || []),
-    });
-  }
   /**
    * 选择搜索字段
    * @param value <code|name>
@@ -243,6 +237,12 @@ export default class Selector extends PureComponent {
             onClick={this.handleFinishOperation}
           >完成
           </Button>
+          {multiple && (
+            <Button
+              style={{ marginLeft: 20, background: '#5cdbd3' }}
+            >{`已选择【${(selectedRows || []).length}】项`}
+            </Button>
+          )}
         </div>
       </div>
     );

+ 19 - 31
src/components/AXTableSelector/SingleSelectTable.js

@@ -1,15 +1,9 @@
 import React, { Component } from 'react';
 import PropTypes from 'prop-types';
-import { Table, Radio, Pagination } from 'antd';
+import { Table, Pagination } from 'antd';
 import styles from './SingleSelectTable.less';
 
 export default class SingleSelectTable extends Component {
-  static defaultProps = {
-    loading: false,
-    columns: [],
-    dataSource: [],
-    pagination: false,
-  };
   static propTypes = {
     loading: PropTypes.bool,
     columns: PropTypes.array,
@@ -19,13 +13,19 @@ export default class SingleSelectTable extends Component {
       PropTypes.bool,
     ]),
   };
+  static defaultProps = {
+    loading: false,
+    columns: [],
+    dataSource: [],
+    pagination: false,
+  };
   state = {
-    selectedRowKey: null,
+    selectedRowKeys: null,
   };
 
-  handleRowClick = (record) => {
+  handleRowSelectChange = (record) => {
     this.setState({
-      selectedRowKey: record.key,
+      selectedRowKeys: [record.key],
     });
     this.props.onSingleTransfer(record);
   };
@@ -35,29 +35,16 @@ export default class SingleSelectTable extends Component {
 
   render() {
     const { loading, columns, dataSource, pagination } = this.props;
-    const { selectedRowKey } = this.state;
+    const { selectedRowKeys } = this.state;
 
-    // 给首列加入单选按钮
-    const addColumnOnFirst = (cols) => {
-      const newColumns = [...cols];
-      newColumns.unshift({
-        key: '-1',
-        dataIndex: 'key',
-        width: 40,
-        render: (text) => {
-          return (
-            <Radio
-              key={text}
-              checked={selectedRowKey === text}
-            />
-          );
-        },
-      });
-      return newColumns;
+    const rowSelection = {
+      type: 'radio',
+      selectedRowKeys,
+      onChange: (_, rows) => this.handleRowSelectChange(rows[0]),
     };
     const onRowClick = (record) => {
       return {
-        onClick: () => this.handleRowClick(record),
+        onClick: () => this.handleRowSelectChange(record),
       };
     };
     const renderTableFooter = (paginationProps) => {
@@ -77,12 +64,13 @@ export default class SingleSelectTable extends Component {
         bordered
         loading={loading}
         footer={() => renderTableFooter(pagination)}
-        columns={addColumnOnFirst(columns)}
+        columns={columns}
         dataSource={dataSource}
         pagination={false}
         rowKey={record => record.key}
-        onRow={onRowClick}
+        rowSelection={rowSelection}
         onChange={this.handleTableChange}
+        onRow={onRowClick}
         className={styles.table}
         scroll={{ y: 400 }}
       />

+ 49 - 1
src/components/AXTableSelector/columnsMap.js

@@ -1,10 +1,14 @@
 import React from 'react';
+import { Icon } from 'antd';
 import {
   genAbsolutePicUrl,
   renderProductType,
   renderVideoQuality,
-  renderCategory, renderStatus,
+  renderCategory,
+  renderStatus,
+  getResourceTypeName,
 } from '../../utils/utils';
+import { Hotax } from '../../utils/config';
 import styles from './columnsMap.less';
 
 function renderPicture(path) {
@@ -25,6 +29,48 @@ function renderPictureData(record) {
 }
 
 const clMap = {
+  Resource: {
+    columns: [{
+      title: '缩略图',
+      key: 1,
+      render: (_, record) => {
+        const { type } = record;
+        if (type === Hotax.RESOURCE_IMAGE) {
+          const { path } = record;
+          return (
+            <div className={styles.thumb}>
+              <img src={genAbsolutePicUrl(path)} alt="" />
+            </div>
+          );
+        } else if (type === Hotax.RESOURCE_AUDIOBOOK) {
+          const { img } = record;
+          const { path } = img || {};
+          return (
+            <div className={styles.thumb}>
+              <img src={genAbsolutePicUrl(path)} alt="" />
+            </div>
+          );
+        } else {
+          return (
+            <Icon style={{ fontSize: 50 }} type="video-camera" />
+          );
+        }
+      },
+      width: '25%',
+      align: 'center',
+    }, {
+      title: '编号/名称',
+      key: 2,
+      render: (_, record) => renderPictureData(record),
+      width: '40%',
+    }, {
+      title: '资源类型',
+      key: 3,
+      dataIndex: 'type',
+      render: text => getResourceTypeName(text),
+      align: 'center',
+    }],
+  },
   Picture: {
     columns: [{
       title: '图片',
@@ -32,6 +78,7 @@ const clMap = {
       dataIndex: 'path',
       render: path => renderPicture(path),
       width: '25%',
+      align: 'center',
     }, {
       title: '编号/名称',
       key: 2,
@@ -46,6 +93,7 @@ const clMap = {
       dataIndex: 'path',
       render: path => renderPicture(path),
       width: '12%',
+      align: 'center',
     }, {
       title: '图片编号',
       key: 2,

+ 0 - 1
src/components/AXTableSelector/columnsMap.less

@@ -4,7 +4,6 @@
   position: relative;
   vertical-align: middle;
   text-align: center;
-  width: 90px;
   height: 100px;
   line-height: 100px;
   img {

+ 1 - 1
src/components/AXUpload/index.js

@@ -177,7 +177,7 @@ class Uploader extends Component {
 
     return (
       <div>
-        <Upload.Dragger {...uploadProps}>
+        <Upload.Dragger {...uploadProps} className={styles.uploader}>
           <p className={styles.dragIcon}>
             <Icon type="inbox" />
           </p>

+ 7 - 0
src/components/AXUpload/index.less

@@ -1,5 +1,12 @@
 @import "~antd/lib/style/themes/default.less";
 
+.uploader {
+  :global(.anticon-cross) {
+    font-size: 20px !important;
+    color: #f5222d !important;
+    opacity: unset !important;
+  }
+}
 .dragIcon {
   margin-bottom: 10px !important;
   :global {

+ 5 - 5
src/components/GlobalHeader/index.less

@@ -1,12 +1,12 @@
 @import "~antd/lib/style/themes/default.less";
 @ease-in-out-circ: cubic-bezier(.78, .14, .15, .86);
 
-@hover-bg: #ffe58f;
+@hover-bg: #008e3e;
 
 .header {
   height: 50px;
   padding: 0;
-  background: #f49b13;
+  background: #00a65a;
   position: relative;
 }
 
@@ -30,7 +30,7 @@
     border-right: 1px solid @hover-bg;
     font-size: 20px;
     margin: 0;
-    padding: 0 21px 0 21px;
+    padding: 0 10px 0 10px;
     font-family: 'Myriad Pro', 'Helvetica Neue', Arial, Helvetica, sans-serif;
     font-weight: 600;
     &:hover {
@@ -44,7 +44,7 @@ i.trigger {
   height: 50px;
   width: 50px;
   line-height: 50px;
-  color: #000;
+  color: #fff;
   font-size: 20px;
   cursor: pointer;
   transition: all .3s, padding 0s;
@@ -86,7 +86,7 @@ i.trigger {
     > i {
       font-size: 20px;
       vertical-align: middle;
-      color: #000;
+      color: #fff;
     }
     &:hover {
       background: @hover-bg;

+ 1 - 0
src/components/HeaderSearch/index.less

@@ -5,6 +5,7 @@
   :global(.anticon-search) {
     cursor: pointer;
     font-size: 16px;
+    color: #fff;
   }
   .input {
     transition: width .3s, margin-left .3s;

+ 1 - 1
src/components/SiderMenu/SiderMenu.js

@@ -232,7 +232,7 @@ export default class SiderMenu extends PureComponent {
       >
         <Menu
           key="Menu"
-          theme="dark"
+          theme="light"
           mode="inline"
           {...menuProps}
           onOpenChange={this.handleOpenChange}

+ 7 - 7
src/components/SiderMenu/index.less

@@ -14,15 +14,15 @@
       background: #f9fafc;
       color: #000 !important;
     }
-    .ant-menu-dark {
+    .ant-menu-light {
       background: #f9fafc;
       color: #000 !important;
     }
-    .ant-menu-dark .ant-menu-inline.ant-menu-sub {
+    .ant-menu-light .ant-menu-inline.ant-menu-sub {
       background: #f4f5f5;
       color: #777 !important;
     }
-    .ant-menu.ant-menu-dark .ant-menu-item-selected {
+    .ant-menu.ant-menu-light .ant-menu-item-selected {
       background-color: #f4f5f5 !important;
       color: #000 !important;
     }
@@ -33,7 +33,7 @@
     .ant-menu-inline-collapsed .ant-menu-item {
       padding: 0 16px !important;
       span {
-        opacity: 1;
+        opacity: 0;
       }
     }
   }
@@ -43,14 +43,14 @@
   .ant-menu-inline-collapsed > .ant-menu-submenu > .ant-menu-submenu-title {
     padding: 0 16px !important;
     .realbull + span {
-      opacity: 1;
+      opacity: 0;
     }
   }
-  .ant-menu-submenu-popup.ant-menu-dark .ant-menu-item-selected {
+  .ant-menu-submenu-popup.ant-menu-light .ant-menu-item-selected {
     background-color: #f4f4f5 !important;
     color: #000 !important;
   }
-  .ant-menu-dark .ant-menu-sub {
+  .ant-menu-light .ant-menu-sub {
     background: #f9fafc;
     color: #000 !important;
   }

+ 1 - 1
src/index.ejs

@@ -5,7 +5,7 @@
   <meta charset="UTF-8">
   <meta http-equiv="X-UA-Compatible" content="IE=edge">
   <meta name="viewport" content="width=device-width, initial-scale=1">
-  <title>Hotax</title>
+  <title>Taurus</title>
   <link rel="icon" href="/favicon.png" type="image/x-icon">
   <!-- 引入业务图标库 -->
   <link rel="stylesheet" href="//at.alicdn.com/t/font_583229_e33lmw33ougl23xr.css" type="text/css">

+ 1 - 1
src/index.less

@@ -55,7 +55,7 @@ body {
   }
   */
   .ant-table-tbody > tr.ant-table-row-selected td {
-    background: #ffffb8 !important;
+    background: #f6ffed !important;
   }
   .ant-tag {
     width: 137px;

+ 0 - 28
src/models/activities.js

@@ -1,28 +0,0 @@
-import { queryActivities } from '../services/api';
-
-export default {
-  namespace: 'activities',
-
-  state: {
-    list: [],
-  },
-
-  effects: {
-    *fetchList(_, { call, put }) {
-      const response = yield call(queryActivities);
-      yield put({
-        type: 'saveList',
-        payload: Array.isArray(response) ? response : [],
-      });
-    },
-  },
-
-  reducers: {
-    saveList(state, action) {
-      return {
-        ...state,
-        list: action.payload,
-      };
-    },
-  },
-};

+ 0 - 61
src/models/chart.js

@@ -1,61 +0,0 @@
-import { fakeChartData } from '../services/api';
-
-export default {
-  namespace: 'chart',
-
-  state: {
-    visitData: [],
-    visitData2: [],
-    salesData: [],
-    searchData: [],
-    offlineData: [],
-    offlineChartData: [],
-    salesTypeData: [],
-    salesTypeDataOnline: [],
-    salesTypeDataOffline: [],
-    radarData: [],
-    loading: false,
-  },
-
-  effects: {
-    *fetch(_, { call, put }) {
-      const response = yield call(fakeChartData);
-      yield put({
-        type: 'save',
-        payload: response,
-      });
-    },
-    *fetchSalesData(_, { call, put }) {
-      const response = yield call(fakeChartData);
-      yield put({
-        type: 'save',
-        payload: {
-          salesData: response.salesData,
-        },
-      });
-    },
-  },
-
-  reducers: {
-    save(state, { payload }) {
-      return {
-        ...state,
-        ...payload,
-      };
-    },
-    clear() {
-      return {
-        visitData: [],
-        visitData2: [],
-        salesData: [],
-        searchData: [],
-        offlineData: [],
-        offlineChartData: [],
-        salesTypeData: [],
-        salesTypeDataOnline: [],
-        salesTypeDataOffline: [],
-        radarData: [],
-      };
-    },
-  },
-};

+ 0 - 103
src/models/cmsUser.js

@@ -1,103 +0,0 @@
-import { message } from 'antd';
-import { routerRedux } from 'dva/router';
-import {
-  queryCmsUserList,
-  createCmsUserItem,
-  updateCmsUserItem,
-  deleteCmsUserItem,
-} from '../services/cmsUser';
-
-export default {
-  namespace: 'cmsUser',
-
-  state: {
-    list: [],
-    pageNo: 1,
-    pageSize: 15,
-    totalSize: 0,
-    currentItem: {},
-  },
-
-  effects: {
-    *fetchCmsUserList({ payload }, { call, put }) {
-      const response = yield call(queryCmsUserList, payload);
-      if (response.success) {
-        yield put({
-          type: 'querySuccess',
-          payload: {
-            list: response.data.list || [],
-            pageSize: response.data.pageSize,
-            totalSize: response.data.totalSize,
-            pageNo: response.data.pageNo,
-          },
-        });
-      }
-    },
-    *createCmsUserItem({ payload, state }, { call, put }) {
-      const response = yield call(createCmsUserItem, payload);
-      if (response.success) {
-        message.success('创建系统用户成功');
-        yield put(routerRedux.push({
-          state,
-          pathname: '/system/cms-user/list',
-        }));
-      }
-    },
-    *deleteCmsUserItem({ payload, states }, { call, put }) {
-      const response = yield call(deleteCmsUserItem, payload);
-      if (response.success) {
-        message.success('禁用系统用户成功');
-        yield put({
-          type: 'fetchCmsUserList',
-          payload: states.Queryers,
-        });
-      }
-    },
-    *recoverCmsUserItem({ payload, states }, { call, put }) {
-      const response = yield call(updateCmsUserItem, payload);
-      if (response.success) {
-        message.success('解禁系统用户成功');
-        yield put({
-          type: 'fetchCmsUserList',
-          payload: states.Queryers,
-        });
-      }
-    },
-    *updateCmsUserItem({ payload, states }, { call, put }) {
-      const response = yield call(updateCmsUserItem, payload);
-      if (response.success) {
-        message.success('修改系统用户成功');
-        yield put(routerRedux.push({
-          pathname: '/system/cms-user/list',
-          state: states,
-        }));
-      }
-    },
-  },
-
-  reducers: {
-    querySuccess(state, action) {
-      return {
-        ...state,
-        ...action.payload,
-      };
-    },
-    fixCurrentItem(state, action) {
-      const { currentItem } = state;
-      return {
-        ...state,
-        currentItem: {
-          ...currentItem,
-          ...action.payload,
-        },
-      };
-    },
-    cleanState(state) {
-      return {
-        ...state,
-        list: [],
-        currentItem: {},
-      };
-    },
-  },
-};

+ 0 - 197
src/models/configUser.js

@@ -1,197 +0,0 @@
-import { message } from 'antd';
-import { routerRedux } from 'dva/router';
-import { Hotax } from '../utils/config';
-import { query as queryUsers } from '../services/user';
-import { getLocalUser } from '../utils/authority';
-import {
-  queryUserTags,
-  createConfigUserTagItem,
-  queryTagDetail,
-  updateConfigUserTagItem,
-  deleteConfigUserTagItem,
-  queryConfigCourse,
-  updateConfigCourse,
-  copyTag,
-} from '../services/configUser';
-
-
-export default {
-  namespace: 'configUser',
-  state: {
-    list: [],
-    currentUser: {},
-    TagList: [],
-    userTagLists: [],
-    TagDetails: [],
-    courseList: [],
-  },
-  effects: {
-    *fetch(_, { call, put }) {
-      const response = yield call(queryUsers);
-      yield put({
-        type: 'save',
-        payload: response,
-      });
-    },
-    *fetchCurrent(_, { put }) {
-      // TODO 这里从本地读取用户信息,后期改成请求
-      const userInfo = getLocalUser();
-      yield put({
-        type: 'saveCurrentUser',
-        payload: userInfo,
-      });
-    },
-    // 用户终端下用户推荐课程相关;
-    *fetchConfigCourse({ payload }, { call, put }) {
-      const response = yield call(queryConfigCourse, payload);
-      if (response.success) {
-        yield put({
-          type: 'querySuccess',
-          payload: {
-            courseList: response.data || [],
-          },
-        });
-      }
-    },
-    *updateConfigCourse({ payload, states }, { call, put }) {
-      const response = yield call(updateConfigCourse, payload);
-      if (response.success) {
-        message.success('推荐课程列表更新成功');
-        yield put(routerRedux.push({
-          pathname: '/frontend/ConfigUser',
-          state: states,
-        }));
-      }
-    },
-    // 用户标签的相关
-    *fetchTagDetail({ payload }, { call, put }) {
-      const response = yield call(queryTagDetail, payload);
-      if (response.success) {
-        yield put({
-          type: 'querySuccess',
-          payload: {
-            TagDetails: response.data || [],
-          },
-        });
-      }
-    },
-    *fetchUserTags({ payload }, { call, put }) {
-      const response = yield call(queryUserTags, payload);
-      if (response.success) {
-        yield put({
-          type: 'querySuccess',
-          payload: {
-            userTagLists: response.data || [],
-          },
-        });
-      } else {
-        yield put({
-          type: 'querySuccess',
-          payload: {
-            userTagLists: [],
-          },
-        });
-      }
-    },
-    *createConfigUserTagItem({ payload, TagId }, { call, put, select }) {
-      const response = yield call(createConfigUserTagItem, payload);
-      if (response.success) {
-        message.success('创建终端标签成功');
-        const originalData = yield select(state => state.configUser.userTagLists);
-        const newData = originalData.map((data) => {
-          const { id, isNew, isEdit, ...rest } = data;
-          if (id === TagId) {
-            return { ...rest, id: response.data.id };
-          }
-          return { ...data };
-        });
-        yield put({
-          type: 'fixUserTagLists',
-          payload: newData,
-        });
-      }
-    },
-    *updateConfigUserTagItem({ payload, TagId }, { call, put, select }) {
-      const response = yield call(updateConfigUserTagItem, payload);
-      if (response.success) {
-        message.success('修改终端标签成功');
-        const originalData = yield select(state => state.configUser.userTagLists);
-        const newData = originalData.map((data) => {
-          const { isEdit, ...rest } = data;
-          if (data.id === TagId) {
-            return { ...rest };
-          }
-          return { ...data };
-        });
-        yield put({
-          type: 'fixUserTagLists',
-          payload: newData,
-        });
-      }
-    },
-    *deleteConfigUserTagItem({ payload }, { call, put, select }) {
-      const response = yield call(deleteConfigUserTagItem, payload);
-      if (response.success) {
-        message.success('删除终端标签成功');
-        const originalData = yield select(state => state.configUser.userTagLists);
-        const newData = originalData.map((data) => {
-          if (data.id === payload) {
-            return { ...data, status: Hotax.STATUS_DELETE };
-          }
-          return { ...data };
-        });
-        yield put({
-          type: 'fixUserTagLists',
-          payload: newData,
-        });
-      }
-    },
-    *copyTag({ payload }, { call }) {
-      const response = yield call(copyTag, payload);
-      if (response.success) {
-        message.success('成功复制标签关联产品到当前标签');
-      }
-    },
-  },
-  reducers: {
-    querySuccess(state, action) {
-      return {
-        ...state,
-        ...action.payload,
-      };
-    },
-    fixCourseList(state, action) {
-      return {
-        ...state,
-        courseList: action.payload,
-      };
-    },
-    save(state, action) {
-      return {
-        ...state,
-        list: action.payload,
-      };
-    },
-    fixUserTagLists(state, action) {
-      return {
-        ...state,
-        userTagLists: action.payload,
-      };
-    },
-    saveCurrentUser(state, action) {
-      return {
-        ...state,
-        currentUser: action.payload,
-      };
-    },
-    changeNotifyCount(state, action) {
-      return {
-        ...state,
-        currentUser: {
-          ...state.currentUser,
-          notifyCount: action.payload,
-        },
-      };
-    },
-  },
-};

+ 0 - 103
src/models/courseware.js

@@ -1,103 +0,0 @@
-import { message } from 'antd';
-import { routerRedux } from 'dva/router';
-import {
-  queryCoursewareList,
-  queryCoursewareItem,
-  createCoursewareItem,
-  updateCoursewareItem,
-  deleteCoursewareItem,
-} from '../services/courseware';
-
-export default {
-  namespace: 'courseware',
-
-  state: {
-    list: [],
-    pageNo: 1,
-    pageSize: 15,
-    totalSize: 0,
-    currentItem: {},
-  },
-
-  effects: {
-    *fetchCoursewareList({ payload }, { call, put }) {
-      const response = yield call(queryCoursewareList, payload);
-      if (response.success) {
-        yield put({
-          type: 'querySuccess',
-          payload: {
-            list: response.data.list || [],
-            pageSize: response.data.pageSize,
-            totalSize: response.data.totalSize,
-            pageNo: response.data.pageNo,
-          },
-        });
-      }
-    },
-    *fetchCoursewareItem({ payload }, { call, put }) {
-      const response = yield call(queryCoursewareItem, payload);
-      if (response.success) {
-        yield put({
-          type: 'querySuccess',
-          payload: {
-            currentItem: response.data || {},
-          },
-        });
-      }
-    },
-    *createCoursewareItem({ payload, state }, { call, put }) {
-      const response = yield call(createCoursewareItem, payload);
-      if (response.success) {
-        message.success('创建课件成功');
-        yield put(routerRedux.push({
-          state,
-          pathname: '/product/courseware/list',
-        }));
-      }
-    },
-    *deleteCoursewareItem({ payload, states }, { call, put }) {
-      const response = yield call(deleteCoursewareItem, payload);
-      if (response.success) {
-        message.success('删除课件成功');
-        yield put({
-          type: 'fetchCoursewareList',
-          payload: states.Queryers,
-        });
-      }
-    },
-    *updateCoursewareItem({ payload, states }, { call, put }) {
-      const response = yield call(updateCoursewareItem, payload);
-      if (response.success) {
-        message.success('修改课件成功');
-        yield put(routerRedux.push({
-          pathname: '/product/courseware/list',
-          state: states,
-        }));
-      }
-    },
-  },
-
-  reducers: {
-    querySuccess(state, action) {
-      return {
-        ...state,
-        ...action.payload,
-      };
-    },
-    fixResourceList(state, action) {
-      return {
-        ...state,
-        currentItem: {
-          ...state.currentItem,
-          resourceList: action.payload,
-        },
-      };
-    },
-    cleanItemState(state) {
-      return {
-        ...state,
-        currentItem: {},
-      };
-    },
-  },
-};

+ 0 - 103
src/models/lesson.js

@@ -1,103 +0,0 @@
-import { message } from 'antd';
-import { routerRedux } from 'dva/router';
-import {
-  queryLessonList,
-  queryLessonItem,
-  createLessonItem,
-  updateLessonItem,
-  deleteLessonItem,
-} from '../services/lesson';
-
-export default {
-  namespace: 'lesson',
-
-  state: {
-    list: [],
-    pageNo: 1,
-    pageSize: 15,
-    totalSize: 0,
-    currentItem: {},
-  },
-
-  effects: {
-    *fetchLessonList({ payload }, { call, put }) {
-      const response = yield call(queryLessonList, payload);
-      if (response.success) {
-        yield put({
-          type: 'querySuccess',
-          payload: {
-            list: response.data.list || [],
-            pageSize: response.data.pageSize,
-            totalSize: response.data.totalSize,
-            pageNo: response.data.pageNo,
-          },
-        });
-      }
-    },
-    *fetchLessonItem({ payload }, { call, put }) {
-      const response = yield call(queryLessonItem, payload);
-      if (response.success) {
-        yield put({
-          type: 'querySuccess',
-          payload: {
-            currentItem: response.data || {},
-          },
-        });
-      }
-    },
-    *createLessonItem({ payload, state }, { call, put }) {
-      const response = yield call(createLessonItem, payload);
-      if (response.success) {
-        message.success('创建课成功');
-        yield put(routerRedux.push({
-          state,
-          pathname: '/product/lesson/list',
-        }));
-      }
-    },
-    *deleteLessonItem({ payload, states }, { call, put }) {
-      const response = yield call(deleteLessonItem, payload);
-      if (response.success) {
-        message.success('删除课成功');
-        yield put({
-          type: 'fetchLessonList',
-          payload: states.Queryers,
-        });
-      }
-    },
-    *updateLessonItem({ payload, states }, { call, put }) {
-      const response = yield call(updateLessonItem, payload);
-      if (response.success) {
-        message.success('修改课成功');
-        yield put(routerRedux.push({
-          pathname: '/product/lesson/list',
-          state: states,
-        }));
-      }
-    },
-  },
-
-  reducers: {
-    querySuccess(state, action) {
-      return {
-        ...state,
-        ...action.payload,
-      };
-    },
-    fixCoursewareList(state, action) {
-      return {
-        ...state,
-        currentItem: {
-          ...state.currentItem,
-          wareList: action.payload,
-        },
-      };
-    },
-    cleanItemState(state) {
-      return {
-        ...state,
-        currentItem: {},
-      };
-    },
-  },
-};

+ 0 - 225
src/models/merchant.js

@@ -1,225 +0,0 @@
-import { message } from 'antd';
-import { routerRedux } from 'dva/router';
-import {
-  queryMerchantList,
-  queryMerchantItem,
-  createMerchantItem,
-  updateMerchantItem,
-  deleteMerchantItem,
-  depositMerchantItem,
-  queryMerchantRecommend,
-  updateMerchantRecommend,
-  queryMerchantPoster,
-  createMerchantPosterItem,
-  updateMerchantPosterItem,
-  deleteMerchantPosterItem, queryMerchantPosterItem,
-} from '../services/merchant';
-import { Hotax } from '../utils/config';
-
-export default {
-  namespace: 'merchant',
-
-  state: {
-    list: [],
-    pageNo: 1,
-    pageSize: 15,
-    totalSize: 0,
-    currentItem: {},
-    recommendList: [],
-  },
-
-  effects: {
-    *fetchMerchantList({ payload }, { call, put }) {
-      const response = yield call(queryMerchantList, payload);
-      if (response.success) {
-        yield put({
-          type: 'querySuccess',
-          payload: {
-            list: response.data.list || [],
-            pageSize: response.data.pageSize,
-            totalSize: response.data.totalSize,
-            pageNo: response.data.pageNo,
-          },
-        });
-      }
-    },
-    *fetchMerchantItem({ payload }, { call, put }) {
-      const response = yield call(queryMerchantItem, payload);
-      if (response.success) {
-        yield put({
-          type: 'querySuccess',
-          payload: {
-            currentItem: response.data || {},
-          },
-        });
-      }
-    },
-    *createMerchantItem({ payload, state }, { call, put }) {
-      const response = yield call(createMerchantItem, payload);
-      if (response.success) {
-        message.success('创建商户成功');
-        yield put(routerRedux.push({
-          state,
-          pathname: '/merchant/list',
-        }));
-      }
-    },
-    *deleteMerchantItem({ payload, states }, { call, put }) {
-      const response = yield call(deleteMerchantItem, payload);
-      if (response.success) {
-        message.success('删除商户成功');
-        yield put({
-          type: 'fetchMerchantList',
-          payload: states.Queryers,
-        });
-      }
-    },
-    *updateMerchantItem({ payload, states }, { call, put }) {
-      const response = yield call(updateMerchantItem, payload);
-      if (response.success) {
-        message.success('修改商户成功');
-        yield put(routerRedux.push({
-          pathname: '/merchant/list',
-          state: states,
-        }));
-      }
-    },
-    *depositMerchantItem({ payload, states }, { call, put }) {
-      const response = yield call(depositMerchantItem, payload);
-      if (response.success) {
-        message.success('账户充值成功');
-        yield put(routerRedux.push({
-          pathname: '/merchant/list',
-          state: states,
-        }));
-      }
-    },
-    *queryMerchantRecommend({ payload }, { call, put }) {
-      const response = yield call(queryMerchantRecommend, payload);
-      if (response.success) {
-        yield put({
-          type: 'querySuccess',
-          payload: {
-            recommendList: response.data || [],
-          },
-        });
-      }
-    },
-    *updateMerchantRecommend({ payload, states }, { call, put }) {
-      const response = yield call(updateMerchantRecommend, payload);
-      if (response.success) {
-        message.success('推荐课程列表更新成功');
-        yield put(routerRedux.push({
-          pathname: '/frontend/recommend',
-          state: states,
-        }));
-      }
-    },
-    *fetchMerchantPoster({ payload }, { call, put }) {
-      const response = yield call(queryMerchantPoster, payload);
-      if (response.success) {
-        yield put({
-          type: 'querySuccess',
-          payload: {
-            posterList: response.data || [],
-          },
-        });
-      }
-    },
-    *fetchMerchantPosterItem({ payload }, { call, put }) {
-      const response = yield call(queryMerchantPosterItem, payload);
-      if (response.success) {
-        yield put({
-          type: 'querySuccess',
-          payload: {
-            posterItem: response.data || {},
-          },
-        });
-      }
-    },
-    *createMerchantPosterItem({ payload, posterId }, { call, put, select }) {
-      const response = yield call(createMerchantPosterItem, payload);
-      if (response.success) {
-        message.success('创建海报成功');
-        // 创建成功,更新该条数据id
-        const originalData = yield select(state => state.merchant.posterList);
-        const newData = originalData.map((data) => {
-          const { id, isNew, isEdit, ...rest } = data;
-          if (id === posterId) {
-            return { ...rest, id: response.data.id };
-          }
-          return { ...data };
-        });
-        yield put({
-          type: 'fixPosterList',
-          payload: newData,
-        });
-      }
-    },
-    *updateMerchantPosterItem({ payload, posterId }, { call, put, select }) {
-      const response = yield call(updateMerchantPosterItem, payload);
-      if (response.success) {
-        message.success('修改海报成功');
-        // 修改成功,去掉isEdit字段
-        const originalData = yield select(state => state.merchant.posterList);
-        const newData = originalData.map((data) => {
-          const { isEdit, ...rest } = data;
-          if (data.id === posterId) {
-            return { ...rest };
-          }
-          return { ...data };
-        });
-        yield put({
-          type: 'fixPosterList',
-          payload: newData,
-        });
-      }
-    },
-    *deleteMerchantPosterItem({ payload }, { call, put, select }) {
-      const response = yield call(deleteMerchantPosterItem, payload);
-      if (response.success) {
-        message.success('删除海报成功');
-        // 删除成功后修改该条的status字段为删除状态
-        const { posterId } = payload;
-        const originalData = yield select(state => state.merchant.posterList);
-        const newData = originalData.map((data) => {
-          if (data.id === posterId) {
-            return { ...data, status: Hotax.STATUS_DELETE };
-          }
-          return { ...data };
-        });
-        yield put({
-          type: 'fixPosterList',
-          payload: newData,
-        });
-      }
-    },
-  },
-
-  reducers: {
-    querySuccess(state, action) {
-      return {
-        ...state,
-        ...action.payload,
-      };
-    },
-    fixRecommendList(state, action) {
-      return {
-        ...state,
-        recommendList: action.payload,
-      };
-    },
-    fixPosterList(state, action) {
-      return {
-        ...state,
-        posterList: action.payload,
-      };
-    },
-    cleanItemState(state) {
-      return {
-        ...state,
-        currentItem: {},
-      };
-    },
-  },
-};

+ 0 - 28
src/models/monitor.js

@@ -1,28 +0,0 @@
-import { queryTags } from '../services/api';
-
-export default {
-  namespace: 'monitor',
-
-  state: {
-    tags: [],
-  },
-
-  effects: {
-    *fetchTags(_, { call, put }) {
-      const response = yield call(queryTags);
-      yield put({
-        type: 'saveTags',
-        payload: response.list,
-      });
-    },
-  },
-
-  reducers: {
-    saveTags(state, action) {
-      return {
-        ...state,
-        tags: action.payload,
-      };
-    },
-  },
-};

+ 0 - 28
src/models/project.js

@@ -1,28 +0,0 @@
-import { queryProjectNotice } from '../services/api';
-
-export default {
-  namespace: 'project',
-
-  state: {
-    notice: [],
-  },
-
-  effects: {
-    *fetchNotice(_, { call, put }) {
-      const response = yield call(queryProjectNotice);
-      yield put({
-        type: 'saveNotice',
-        payload: Array.isArray(response) ? response : [],
-      });
-    },
-  },
-
-  reducers: {
-    saveNotice(state, action) {
-      return {
-        ...state,
-        notice: action.payload,
-      };
-    },
-  },
-};

+ 0 - 32
src/models/register.js

@@ -1,32 +0,0 @@
-import { fakeRegister } from '../services/api';
-import { setAuthority } from '../utils/authority';
-import { reloadAuthorized } from '../utils/Authorized';
-
-export default {
-  namespace: 'register',
-
-  state: {
-    status: undefined,
-  },
-
-  effects: {
-    *submit(_, { call, put }) {
-      const response = yield call(fakeRegister);
-      yield put({
-        type: 'registerHandle',
-        payload: response,
-      });
-    },
-  },
-
-  reducers: {
-    registerHandle(state, { payload }) {
-      setAuthority('user');
-      reloadAuthorized();
-      return {
-        ...state,
-        status: payload.status,
-      };
-    },
-  },
-};

+ 0 - 47
src/models/rule.js

@@ -1,47 +0,0 @@
-import { queryRule, removeRule, addRule } from '../services/api';
-
-export default {
-  namespace: 'rule',
-
-  state: {
-    data: {
-      list: [],
-      pagination: {},
-    },
-  },
-
-  effects: {
-    *fetch({ payload }, { call, put }) {
-      const response = yield call(queryRule, payload);
-      yield put({
-        type: 'save',
-        payload: response,
-      });
-    },
-    *add({ payload, callback }, { call, put }) {
-      const response = yield call(addRule, payload);
-      yield put({
-        type: 'save',
-        payload: response,
-      });
-      if (callback) callback();
-    },
-    *remove({ payload, callback }, { call, put }) {
-      const response = yield call(removeRule, payload);
-      yield put({
-        type: 'save',
-        payload: response,
-      });
-      if (callback) callback();
-    },
-  },
-
-  reducers: {
-    save(state, action) {
-      return {
-        ...state,
-        data: action.payload,
-      };
-    },
-  },
-};

+ 0 - 103
src/models/tagGroup.js

@@ -1,103 +0,0 @@
-import { message } from 'antd';
-import { routerRedux } from 'dva/router';
-import {
-  queryTagGroupList,
-  queryTagGroupItem,
-  createTagGroupItem,
-  updateTagGroupItem,
-  deleteTagGroupItem,
-} from '../services/tagGroup';
-
-export default {
-  namespace: 'tagGroup',
-
-  state: {
-    list: [],
-    pageNo: 1,
-    pageSize: 15,
-    totalSize: 0,
-    currentItem: {},
-  },
-
-  effects: {
-    *fetchTagGroupList({ payload }, { call, put }) {
-      const response = yield call(queryTagGroupList, payload);
-      if (response.success) {
-        yield put({
-          type: 'querySuccess',
-          payload: {
-            list: response.data.list || [],
-            pageSize: response.data.pageSize,
-            totalSize: response.data.totalSize,
-            pageNo: response.data.pageNo,
-          },
-        });
-      }
-    },
-    *fetchTagGroupItem({ payload }, { call, put }) {
-      const response = yield call(queryTagGroupItem, payload);
-      if (response.success) {
-        yield put({
-          type: 'querySuccess',
-          payload: {
-            currentItem: response.data || {},
-          },
-        });
-      }
-    },
-    *createTagGroupItem({ payload, state }, { call, put }) {
-      const response = yield call(createTagGroupItem, payload);
-      if (response.success) {
-        message.success('创建标签组成功');
-        yield put(routerRedux.push({
-          state,
-          pathname: '/frontend/tagGroup',
-        }));
-      }
-    },
-    *deleteTagGroupItem({ payload, states }, { call, put }) {
-      const response = yield call(deleteTagGroupItem, payload);
-      if (response.success) {
-        message.success('删除标签组成功');
-        yield put({
-          type: 'fetchTagGroupList',
-          payload: states.Queryers,
-        });
-      }
-    },
-    *updateTagGroupItem({ payload, states }, { call, put }) {
-      const response = yield call(updateTagGroupItem, payload);
-      if (response.success) {
-        message.success('修改标签组成功');
-        yield put(routerRedux.push({
-          pathname: '/frontend/tagGroup',
-          state: states,
-        }));
-      }
-    },
-  },
-
-  reducers: {
-    querySuccess(state, action) {
-      return {
-        ...state,
-        ...action.payload,
-      };
-    },
-    fixCurrentItem(state, action) {
-      return {
-        ...state,
-        currentItem: {
-          ...state.currentItem,
-          ...action.payload,
-        },
-      };
-    },
-    cleanItemState(state) {
-      return {
-        ...state,
-        currentItem: {},
-      };
-    },
-  },
-};

+ 154 - 1
src/models/terminal.js

@@ -11,6 +11,16 @@ import {
   createSpecialTerminalItem,
   updateSpecialTerminalItem,
   deleteSpecialTerminalItem,
+  queryTerminalAuthList,
+  updateTerminalAuth,
+  queryTerminalTagList,
+  queryTerminalTagItem,
+  createTerminalTagItem,
+  updateTerminalTagItem,
+  deleteTerminalTagItem,
+  queryTerminalRecommendCourse,
+  updateTerminalRecommendCourse,
+  copyMerchantTag,
 } from '../services/terminal';
 
 export default {
@@ -22,6 +32,9 @@ export default {
     pageSize: 15,
     totalSize: 0,
     currentItem: {},
+    userTagList: [],
+    userRecCourse: [],
+    currentUserTagItem: {},
   },
 
   effects: {
@@ -144,6 +157,118 @@ export default {
         });
       }
     },
+    *fetchTerminalAuthList({ payload }, { call, put }) {
+      const response = yield call(queryTerminalAuthList, payload);
+      if (response.success) {
+        yield put({
+          type: 'querySuccess',
+          payload: {
+            list: response.data.list || [],
+            pageSize: response.data.pageSize,
+            totalSize: response.data.totalSize,
+            pageNo: response.data.pageNo,
+          },
+        });
+      }
+    },
+    *updateTerminalAuth({ payload, states }, { call, put }) {
+      const response = yield call(updateTerminalAuth, payload);
+      if (response.success) {
+        message.success('修改权限时长成功');
+        yield put({
+          type: 'fetchTerminalAuthList',
+          payload: states,
+        });
+      }
+    },
+    *fetchTerminalTagList({ payload }, { call, put }) {
+      const response = yield call(queryTerminalTagList, payload);
+      if (response.success) {
+        yield put({
+          type: 'querySuccess',
+          payload: {
+            userTagList: response.data || [],
+          },
+        });
+      }
+    },
+    *fetchTerminalTagItem({ payload }, { call, put }) {
+      const response = yield call(queryTerminalTagItem, payload);
+      if (response.success) {
+        yield put({
+          type: 'querySuccess',
+          payload: {
+            currentUserTagItem: response.data || {},
+          },
+        });
+      }
+    },
+    *createTerminalTagItem({ payload }, { call, put }) {
+      const response = yield call(createTerminalTagItem, payload);
+      if (response.success) {
+        message.success('终端用户标签创建成功');
+        const { uid } = payload;
+        yield put({
+          type: 'fetchTerminalTagList',
+          payload: { uid },
+        });
+      }
+    },
+    *updateTerminalTagItem({ payload }, { call, put }) {
+      const response = yield call(updateTerminalTagItem, payload);
+      if (response.success) {
+        message.success('终端用户标签修改成功');
+        const { uid } = payload;
+        yield put({
+          type: 'fetchTerminalTagList',
+          payload: { uid },
+        });
+      }
+    },
+    *deleteTerminalTagItem({ payload }, { call, put }) {
+      const response = yield call(deleteTerminalTagItem, payload);
+      if (response.success) {
+        message.success('终端用户标签删除成功');
+        const { uid } = payload;
+        yield put({
+          type: 'fetchTerminalTagList',
+          payload: { uid },
+        });
+      }
+    },
+    *copyMerchantTagToUser({ payload }, { call, put }) {
+      const { uid, ...rest } = payload;
+      const response = yield call(copyMerchantTag, rest);
+      if (response.success) {
+        message.success('复制渠道标签成功');
+        yield put({
+          type: 'fetchTerminalTagList',
+          payload: { uid },
+        });
+      }
+    },
+    *fetchTerminalRecommendCourse({ payload }, { call, put }) {
+      const response = yield call(queryTerminalRecommendCourse, payload);
+      if (response.success) {
+        yield put({
+          type: 'querySuccess',
+          payload: {
+            userRecCourse: response.data || [],
+          },
+        });
+      }
+    },
+    *updateTerminalRecommendCourse({ payload }, { call, put }) {
+      const response = yield call(updateTerminalRecommendCourse, payload);
+      if (response.success) {
+        message.success('修改用户推荐课程成功');
+        const { uid } = payload;
+        yield put({
+          type: 'fetchTerminalRecommendCourse',
+          payload: { uid },
+        });
+      }
+    },
   },
 
   reducers: {
@@ -163,11 +288,39 @@ export default {
         },
       };
     },
+    fixCurrentUserTagItem(state, action) {
+      const { currentUserTagItem } = state;
+      return {
+        ...state,
+        currentUserTagItem: {
+          ...currentUserTagItem,
+          ...action.payload,
+        },
+      };
+    },
+    fixUserRecCourse(state, action) {
+      return {
+        ...state,
+        userRecCourse: action.payload,
+      };
+    },
     cleanState(state) {
       return {
         ...state,
-        currentItem: {},
         list: [],
+        pageNo: 1,
+        pageSize: 15,
+        totalSize: 0,
+        userTagList: [],
+        userRecCourse: [],
+        currentItem: {},
+        currentUserTagItem: {},
+      };
+    },
+    resetUserTagItem(state) {
+      return {
+        ...state,
+        currentUserTagItem: {},
       };
     },
   },

+ 1 - 1
src/router.js

@@ -28,7 +28,7 @@ function RouterConfig({ history, app }) {
           <AuthorizedRoute
             path="/"
             render={props => <BasicLayout {...props} />}
-            authority={['admin', 'platform']}
+            authority={['admin', 'channel']}
             redirectPath="/user/login"
           />
         </Switch>

+ 2 - 96
src/routes/Campus/CampusCreate.js

@@ -1,10 +1,9 @@
 /* eslint-disable no-trailing-spaces */
 import React, { PureComponent } from 'react';
 import pathToRegexp from 'path-to-regexp';
-import { message, Card, Modal, List, Form, Input, Button, Popover, Icon } from 'antd';
+import { Card, Form, Input, Button, Popover, Icon } from 'antd';
 import { connect } from 'dva';
 import { routerRedux } from 'dva/router';
-import Selector from '../../components/AXTableSelector/Selector';
 import AXCityCascader from '../../components/AXCityCascader';
 import FooterToolbar from '../../components/FooterToolbar';
 import PageHeaderLayout from '../../layouts/PageHeaderLayout';
@@ -14,10 +13,7 @@ import {
 } from '../../utils/utils';
 import styles from './CampusCreate.less';
 
-const Message = message;
-
 const fieldLabels = {
-  merchant: '所属商户',
   cityName: '所在城市',
   zoneName: '校区名称',
   contactName: '校区联系人',
@@ -47,9 +43,6 @@ const formItemLayout = {
   loading: loading.models.merchant,
 }))
 export default class CampusCreatePage extends PureComponent {
-  state = {
-    merchantSelectorDestroy: true,
-  };
   componentDidMount() {
     // 如果是编辑校区,加载校区详情
     const matchId = this.isEdit();
@@ -74,58 +67,13 @@ export default class CampusCreatePage extends PureComponent {
     }
     return false;
   };
-  handleMerchantSelectorModalShow = () => {
-    this.setState({
-      merchantSelectorDestroy: false,
-    });
-    this.props.dispatch({
-      type: 'merchant/fetchMerchantList',
-      payload: {},
-    });
-  };
-  handleMerchantSelectorFinish = (rows) => {
-    this.setState({
-      merchantSelectorDestroy: true,
-    });
-    if (!rows || !rows.length) {
-      return;
-    }
-    const { id, name } = rows[0];
-    this.props.dispatch({
-      type: 'campus/fixCurrentItem',
-      payload: {
-        merchantId: id,
-        merchantName: name,
-      },
-    });
-  };
-  handleMerchantSelectorCancel = () => {
-    this.setState({
-      merchantSelectorDestroy: true,
-    });
-  };
-  handleMerchantSelectorChange = (params) => {
-    this.props.dispatch({
-      type: 'merchant/fetchMerchantList',
-      payload: params,
-    });
-  };
   handlePageSubmit = () => {
     this.props.form.validateFieldsAndScroll((error, values) => {
       if (!error) {
-        const { campus } = this.props;
-        const { currentItem } = campus;
-        const { merchantId } = currentItem;
-        if (!merchantId) {
-          Message.error('请选择该校区所属厂商');
-          return;
-        }
-
         const { cityName, ...restProps } = values;
         const [province, city] = cityName;
         restProps.provinceCode = provinceNameToCode(province);
         restProps.cityName = city;
-        restProps.merchantId = merchantId;
         const matchId = this.isEdit();
         if (matchId) {
           restProps.id = matchId;
@@ -152,11 +100,9 @@ export default class CampusCreatePage extends PureComponent {
   };
 
   render() {
-    const { merchantSelectorDestroy } = this.state;
-    const { form, merchant, mLoading, campus, submitting } = this.props;
+    const { form, campus, submitting } = this.props;
     const { getFieldDecorator, getFieldsError } = form;
     const { currentItem } = campus;
-    const { merchantName } = currentItem;
 
     const renderCityName = () => {
       const { provinceCode, cityName } = currentItem;
@@ -210,49 +156,10 @@ export default class CampusCreatePage extends PureComponent {
       );
     };
 
-    const getMerchantModal = () => {
-      return (
-        <Modal
-          visible
-          width={1100}
-          footer={null}
-          title="厂商列表"
-          maskClosable={false}
-          onCancel={this.handleMerchantSelectorCancel}
-        >
-          <Selector
-            multiple={false}
-            loading={mLoading}
-            selectorName="Merchant"
-            list={merchant.list}
-            pageNo={merchant.pageNo}
-            pageSize={merchant.pageSize}
-            totalSize={merchant.totalSize}
-            onCancel={this.handleMerchantSelectorCancel}
-            onChange={this.handleMerchantSelectorChange}
-            onFinish={this.handleMerchantSelectorFinish}
-          />
-        </Modal>
-      );
-    };
-
     return (
       <PageHeaderLayout>
         <Card style={{ marginBottom: 70 }}>
           <Form>
-            <Form.Item
-              {...formItemLayout}
-              label={!this.isEdit() ? <Button size="small" type="primary" onClick={this.handleMerchantSelectorModalShow}>所属厂商</Button> : '所属厂商'}
-            >
-              <List
-                bordered
-                size="small"
-                dataSource={[
-                  `${merchantName || '请选择'}`,
-                ]}
-                renderItem={item => <List.Item>{item}</List.Item>}
-              />
-            </Form.Item>
             <Form.Item label={fieldLabels.cityName} {...formItemLayout}>
               {getFieldDecorator('cityName', {
                 rules: [{ required: true, message: '请选择校区地址!' }],
@@ -312,7 +219,6 @@ export default class CampusCreatePage extends PureComponent {
               )}
             </Form.Item>
           </Form>
-          {!merchantSelectorDestroy && getMerchantModal()}
         </Card>
         <FooterToolbar style={{ width: '100%' }}>
           {getErrorInfo()}

+ 28 - 12
src/routes/Campus/CampusList.js

@@ -1,8 +1,9 @@
+/* eslint-disable prefer-destructuring */
 import React, { Component } from 'react';
 import moment from 'moment';
 import { connect } from 'dva';
 import { routerRedux } from 'dva/router';
-import { Card, Form, Button, message } from 'antd';
+import { Card, Button, message } from 'antd';
 import { StandardTableList } from '../../components/AXList';
 import Ellipsis from '../../components/Ellipsis';
 import PageHeaderLayout from '../../layouts/PageHeaderLayout';
@@ -10,7 +11,6 @@ import { addRowKey } from '../../utils/utils';
 
 const Message = message;
 
-@Form.create()
 @connect(({ loading, campus, merchant }) => ({
   campus,
   merchant,
@@ -27,9 +27,14 @@ export default class CampusListPage extends Component {
     };
   }
   componentDidMount() {
+    const { merchants } = this.state;
+    let merchantId;
+    if (merchants && merchants.length) {
+      merchantId = merchants[0].split('||')[1];
+    }
     this.props.dispatch({
       type: 'campus/fetchCampusList',
-      payload: { ...this.state.Queryers },
+      payload: { merchantId, ...this.state.Queryers },
     });
   }
   handleCreateOperation = () => {
@@ -37,37 +42,48 @@ export default class CampusListPage extends Component {
       pathname: '/campus/create',
       state: this.state,
     }));
-  }
+  };
   handleEditOperation = (item) => {
     this.props.dispatch(routerRedux.push({
       pathname: `/campus/edit/${item.id}`,
       state: this.state,
     }));
-  }
+  };
   handleFilterOperation = (params, states) => {
+    const { merchants } = this.state;
+    let merchantId;
+    if (merchants && merchants.length) {
+      merchantId = merchants[0].split('||')[1];
+    }
     this.props.dispatch({
       type: 'campus/fetchCampusList',
-      payload: params,
+      payload: { merchantId, ...params },
     });
     this.setState({
       UIParams: states,
       Queryers: params,
     });
-  }
+  };
   handleModalFilterOperation = () => {
     const { getFieldValue } = this.props.form;
-    const value = getFieldValue('merchantId');
+    const merchants = getFieldValue('merchants');
+    let merchantId;
+    if (merchants && merchants.length) {
+      merchantId = merchants[0].split('||')[1];
+    }
     this.props.dispatch({
       type: 'campus/fetchCampusList',
       payload: {
+        merchantId,
         ...this.state.Queryers,
-        merchantId: value[0],
       },
     });
-  }
+    this.setState({ merchants });
+    this.handleFilterModalDestroy();
+  };
   handleBatchOperation = () => {
     Message.info('暂不支持批量操作!');
-  }
+  };
   handleRemoteSelectSearch = (value) => {
     this.props.dispatch({
       type: 'merchant/fetchMerchantList',
@@ -76,7 +92,7 @@ export default class CampusListPage extends Component {
         name: value,
       },
     });
-  }
+  };
 
   render() {
     const { loading, campus } = this.props;

+ 0 - 156
src/routes/Frontend/ConfigUser/ConfigRecommendCourse.js

@@ -1,156 +0,0 @@
-import React, { Component } from 'react';
-import pathToRegexp from 'path-to-regexp';
-import { connect } from 'dva';
-import { routerRedux } from 'dva/router';
-import { Card, Modal, Button } from 'antd';
-import AXDragSortTable from '../../../components/AXDragSortTable';
-import Selector from '../../../components/AXTableSelector/Selector';
-import FooterToolbar from '../../../components/FooterToolbar/index';
-
-@connect(({ loading, merchant, shelves, configUser }) => ({
-  shelves,
-  merchant,
-  configUser,
-  sLoading: loading.models.product,
-  submitting: loading.models.merchant,
-}))
-
-export default class ConfigCourse extends Component {
-  state = {
-    productSelectorDestroy: true,
-  };
-  componentDidMount() {
-    this.props.dispatch({
-      type: 'configUser/fetchConfigCourse',
-      payload: { courseId: this.getMerchantId() },
-    });
-  }
-  getMerchantId = () => {
-    const match = pathToRegexp('/frontend/ConfigUser/course-edit/:id')
-      .exec(this.props.location.pathname);
-    return match[1];
-  }
-  handleSelectorModalShow = () => {
-    const { itemData } = this.props.location.state;
-    const { merchantId: id } = itemData;
-    this.setState({ productSelectorDestroy: false });
-    this.props.dispatch({
-      type: 'shelves/fetchCourseItemList',
-      payload: { merchantId: id },
-    });
-  }
-  handleSelectorChange = (params) => {
-    const { itemData } = this.props.location.state;
-    const { merchantId: id } = itemData;
-    this.props.dispatch({
-      type: 'shelves/fetchCourseItemList',
-      payload: {
-        merchantId: id,
-        ...params,
-      },
-    });
-  }
-  handleSelectorFinish = (rows) => {
-    this.setState({ productSelectorDestroy: true });
-    this.props.dispatch({
-      type: 'configUser/fixCourseList',
-      payload: rows,
-    });
-  }
-  handleSelectorCancel = () => {
-    this.setState({ productSelectorDestroy: true });
-  }
-  handleDragSortTableChange = (rows) => {
-    this.props.dispatch({
-      type: 'configUser/fixCourseList',
-      payload: rows,
-    });
-  }
-  handlePageBack = () => {
-    this.props.dispatch(routerRedux.push({
-      pathname: '/frontend/ConfigUser',
-      state: this.props.location.state,
-    }));
-  }
-  handlePageSubmit = () => {
-    const { configUser } = this.props;
-    const { courseList } = configUser;
-    const idList = courseList.map(item => item.pid);
-    this.props.dispatch({
-      type: 'configUser/updateConfigCourse',
-      payload: {
-        idList,
-        courseId: this.getMerchantId(),
-      },
-    });
-  }
-
-  render() {
-    const { productSelectorDestroy } = this.state;
-    const { shelves, configUser } = this.props;
-    const { courseList } = configUser;
-    // recommend事件
-    const productColumns = [{
-      title: '课程编号',
-      key: 1,
-      dataIndex: 'code',
-      width: '40%',
-    }, {
-      title: '课程名称',
-      key: 2,
-      dataIndex: 'name',
-    }];
-    const getProductModal = () => {
-      return (
-        <Modal
-          visible
-          width={1100}
-          footer={null}
-          title="课程资源"
-          maskClosable={false}
-          onCancel={this.handleSelectorCancel}
-        >
-          <Selector
-            multiple
-            selectorName="Course"
-            selectedRows={courseList}
-            list={shelves.list}
-            pageNo={shelves.pageNo}
-            pageSize={shelves.pageSize}
-            totalSize={shelves.totalSize}
-            onCancel={this.handleSelectorCancel}
-            onChange={this.handleSelectorChange}
-            onFinish={this.handleSelectorFinish}
-          />
-        </Modal>
-      );
-    };
-    return (
-      <div>
-        <Card
-          title={<a onClick={this.handleSelectorModalShow}>选择课程</a>}
-          style={{ marginBottom: 70 }}
-        >
-          <AXDragSortTable
-            columns={productColumns}
-            data={courseList}
-            onChange={this.handleDragSortTableChange}
-          />
-          {!productSelectorDestroy && getProductModal()}
-        </Card>
-        <FooterToolbar style={{ width: '100%' }}>
-          <Button
-            onClick={this.handlePageBack}
-            style={{ marginRight: 10 }}
-          >取消
-          </Button>
-          <Button
-            type="primary"
-            onClick={this.handlePageSubmit}
-          >提交
-          </Button>
-        </FooterToolbar>
-      </div>
-    );
-  }
-}

+ 0 - 11
src/routes/Frontend/ConfigUser/ConfigRecommendPoster.js

@@ -1,11 +0,0 @@
-import React, { Component } from 'react';
-
-export default class ConfigPoster extends Component {
-  render() {
-    return (
-      <div>
-        <h2>用户终端海报接口暂未开放</h2>
-      </div>
-    );
-  }
-}

+ 0 - 564
src/routes/Frontend/ConfigUser/ConfigTag.js

@@ -1,564 +0,0 @@
-import React, { Component } from 'react';
-import pathToRegexp from 'path-to-regexp';
-import { connect } from 'dva';
-import { routerRedux } from 'dva/router';
-import { Card, Table, Modal, Popconfirm, Switch, Button, Input, Icon } from 'antd';
-import Selector from '../../../components/AXTableSelector/Selector';
-import FooterToolbar from '../../../components/FooterToolbar';
-import { boolToStatus, renderStatus, statusToBool } from '../../../utils/utils';
-import styles from './ConfigTag.less';
-
-@connect(({ loading, merchant, shelves, resource, configUser, tagType, tag }) => ({
-  shelves,
-  merchant,
-  resource,
-  configUser,
-  tagType,
-  tag,
-  loading: loading.models.tagType,
-  rLoading: loading.models.resource,
-  sLoading: loading.models.shelves,
-  mLoading: loading.models.merchant,
-}))
-export default class ConfigTag extends Component {
-  state = {
-    productSelectorDestroy: true,
-    resourceSelectorDestroy: true,
-    allTagSelectorDestroy: true,
-    productType: 'Course',
-    currentEditTagId: '',
-  };
-  componentDidMount() {
-    this.props.dispatch({
-      type: 'configUser/fetchUserTags',
-      payload: { configUserId: this.getConfigUserId() },
-    });
-    this.props.dispatch({
-      type: 'tagType/fetchTagTypeList',
-      payload: {},
-    });
-    this.props.dispatch({
-      type: 'tagType/fetchMerchantPoster',
-      payload: { merchantId: this.getConfigUserId() },
-    });
-  }
-  /**
-   * 1.从URL中提取configUserId
-   * @returns {String}
-   */
-  getConfigUserId = () => {
-    const match = pathToRegexp('/frontend/ConfigUser/tag/:id')
-      .exec(this.props.location.pathname);
-    return match[1];
-  };
-  /**
-   * 2.终端用户新建标签
-   */
-  handleTagItemCreate = () => {
-    const newData = [...this.props.configUser.userTagLists];
-    newData.push({
-      id: `new-poster-${newData.length + 1}`,
-      isNew: true,
-      isEdit: true,
-    });
-    this.props.dispatch({
-      type: 'configUser/fixUserTagLists',
-      payload: newData,
-    });
-  };
-  /**
-   * 3.删除一跳标签信息
-   * @param TagId
-   * @param isNew
-   */
-  handleTagItemDelete = (TagId, isNew) => {
-    if (isNew) {
-      const originalData = [...this.props.configUser.userTagLists];
-      const newData = originalData.filter(data => data.id !== TagId);
-      this.props.dispatch({
-        type: 'configUser/fixUserTagLists',
-        payload: newData,
-      });
-      return;
-    }
-    this.props.dispatch({
-      type: 'configUser/deleteConfigUserTagItem',
-      payload: TagId,
-    });
-  }
-  /**
-   * 4.编辑一条标签
-   * @param TagId
-   */
-  handleTagItemEdit = (TagId) => {
-    const newData = [...this.props.configUser.userTagLists];
-    for (const index in newData) {
-      if (newData[index].id === TagId) {
-        newData[index].isEdit = true;
-      }
-    }
-    this.props.dispatch({
-      type: 'configUser/fixUserTagLists',
-      payload: newData,
-    });
-  };
-  /**
-   * 5.根据flag,控制模态框的展现
-   * @param flag
-   * @param TagId
-   */
-  handleSelectorModalShow = (flag, TagId) => {
-    this.setState({
-      [`${flag}SelectorDestroy`]: false,
-      currentEditTagId: TagId,
-    });
-    if (flag === 'product') {
-      this.props.dispatch({
-        type: 'configUser/fetchTagDetail',
-        payload: TagId,
-      });
-      return;
-    }
-    if (flag === 'resource') {
-      this.props.dispatch({
-        type: 'tagType/fetchTagTypeList',
-        payload: {},
-      });
-    }
-    if (flag === 'allTag') {
-      this.props.dispatch({
-        type: 'tag/fetchTagList',
-      });
-    }
-  };
-  /**
-   * 6.控制模态框的销毁
-   */
-  handleSelectorCancel = (flag) => {
-    this.setState({ [`${flag}SelectorDestroy`]: true });
-  };
-  /**
-   * 7.模态框内的查询操作 完成
-   * @param {String} flag
-   * @param {Object} params
-   */
-  handleSelectorChange = (flag, params) => {
-    if (flag === 'product') {
-      const { productType } = this.state;
-      this.props.dispatch({
-        type: `shelves/fetch${productType}ItemList`,
-        payload: { ...params, merchantId: this.getConfigUserId() },
-      });
-      return;
-    }
-    if (flag === 'allTag') {
-      this.props.dispatch({
-        type: 'tag/fetchTagList',
-        payload: params,
-      });
-    }
-    if (flag === 'resource') {
-      this.props.dispatch({
-        type: 'tagType/fetchTagTypeList',
-        payload: params,
-      });
-    }
-  };
-  /**
-   * 8.响应选择完成操作 数据的回显 模态框的处理
-   * @param {String} flag
-   * @param {Array} rows
-   */
-  handleSelectorFinish = (flag, rows) => {
-    this.setState({ [`${flag}SelectorDestroy`]: true });
-    const { currentEditTagId } = this.state;
-    const originalData = [...this.props.configUser.userTagLists];
-    if (flag !== 'allTag') {
-      const newData = originalData.map((data) => {
-        if (flag === 'resource' && data.id === currentEditTagId) {
-          return { ...data, typeCode: rows[0].code };
-        }
-        return { ...data };
-      });
-      this.props.dispatch({
-        type: 'configUser/fixUserTagLists',
-        payload: newData,
-      });
-    } else {
-      this.props.dispatch({
-        type: 'configUser/copyTag',
-        payload: {
-          userTagId: currentEditTagId,
-          tagId: rows[0].id,
-        },
-      });
-    }
-  };
-  /**
-   * 9.修改排序值
-   * @param e
-   * @param TagId
-   */
-  handleSortInputChange = (e, TagId) => {
-    const originalData = [...this.props.configUser.userTagLists];
-    const newData = originalData.map((data) => {
-      if (data.id === TagId) {
-        return { ...data, sort: parseInt(e.target.value, 10) || 0 };
-      }
-      return { ...data };
-    });
-    this.props.dispatch({
-      type: 'configUser/fixUserTagLists',
-      payload: newData,
-    });
-  };
-  /**
-   * 10.修改标签名称
-   * @param e
-   * @param TagId
-   */
-  handleNameInputChange = (e, TagId) => {
-    const originalData = [...this.props.configUser.userTagLists];
-    const newData = originalData.map((data) => {
-      if (data.id === TagId) {
-        return { ...data, name: e.target.value };
-      }
-      return { ...data };
-    });
-    this.props.dispatch({
-      type: 'configUser/fixUserTagLists',
-      payload: newData,
-    });
-  };
-  /**
-   * 11.修改状态
-   * @param checked
-   * @param TagId
-   */
-  handleStatusSwitchChange = (checked, TagId) => {
-    const originalData = [...this.props.configUser.userTagLists];
-    const newData = originalData.map((data) => {
-      if (data.id === TagId) {
-        return { ...data, status: boolToStatus(checked) };
-      }
-      return { ...data };
-    });
-    this.props.dispatch({
-      type: 'configUser/fixUserTagLists',
-      payload: newData,
-    });
-  };
-  /**
-   * 12.提交标签的内容
-   * @param TagId
-   */
-  handleSaveOperation = (TagId) => {
-    const originalData = [...this.props.configUser.userTagLists];
-    const targetData = originalData.filter(data => data.id === TagId)[0];
-    const { id, sort, name, typeCode, status, isNew } = targetData;
-    if (isNew) {
-      this.props.dispatch({
-        type: 'configUser/createConfigUserTagItem',
-        payload: {
-          name, typeCode, sort, status: boolToStatus(status), uid: this.getConfigUserId(),
-        },
-        TagId: id,
-      });
-      return;
-    }
-    this.props.dispatch({
-      type: 'configUser/updateConfigUserTagItem',
-      payload: {
-        id, name, typeCode, sort, status: boolToStatus(status), uid: this.getConfigUserId(),
-      },
-      TagId: id,
-    });
-  };
-  /**
-   * 13.返回上一页 finished
-   */
-  handlePageBack = () => {
-    this.props.dispatch(routerRedux.push({
-      pathname: '/frontend/ConfigUser/list',
-      state: this.props.location.state,
-    }));
-  };
-  render() {
-    const { productSelectorDestroy, resourceSelectorDestroy, allTagSelectorDestroy } = this.state;
-    const { configUser, tagType, tag, loading } = this.props;
-    const { userTagLists } = configUser;
-    /* 海报列表格式设定 */
-    const userTagColumns = [{
-      title: '位置',
-      key: 1,
-      dataIndex: 'sort',
-      width: '10%',
-      render: (text, record) => {
-        const { id, isEdit } = record;
-        if (isEdit) {
-          return (
-            <Input
-              value={text}
-              onChange={e => this.handleSortInputChange(e, id)}
-              placeholder="必填项"
-              style={{ width: 100 }}
-            />
-          );
-        }
-        return text;
-      },
-      align: 'center',
-    }, {
-      title: '标签名称',
-      key: 2,
-      dataIndex: 'name',
-      width: '15%',
-      render: (text, record) => {
-        const { id, isEdit } = record;
-        if (isEdit) {
-          return (
-            <Input
-              value={text}
-              onChange={e => this.handleNameInputChange(e, id)}
-              placeholder="必填项"
-              style={{ width: 100 }}
-            />
-          );
-        }
-        return text;
-      },
-      align: 'center',
-    }, {
-      title: '标签类型',
-      key: 3,
-      dataIndex: 'typeCode',
-      width: '15%',
-      render: (text, record) => {
-        // 将标签类型更换,编辑状态下可更换
-        const { id, isNew, isEdit, typeCode = '标签类型选择(必选项)' } = record;
-        return (
-          <div className={styles.product}>
-            {isEdit && (
-              <div className={styles.mongolian}>
-                <a onClick={() => this.handleSelectorModalShow('resource', id)}>{isNew ? '选择' : '更换'}</a>
-              </div>
-            )}
-            {typeCode}
-          </div>
-        );
-      },
-      align: 'center',
-    }, {
-      title: '标签状态',
-      key: 4,
-      dataIndex: 'status',
-      width: '15%',
-      render: (text, record) => {
-        const { id, isEdit } = record;
-        if (isEdit) {
-          return (
-            <Switch
-              checked={statusToBool(text)}
-              checkedChildren="正常"
-              unCheckedChildren="删除"
-              onChange={checked => this.handleStatusSwitchChange(checked, id)}
-            />
-          );
-        }
-        return renderStatus(text);
-      },
-      align: 'center',
-    }, {
-      title: '标签关联产品',
-      key: 5,
-      render: (_, record) => {
-        const { id, isEdit } = record;
-        if (isEdit) {
-          return (
-            <div>
-              <p>此处暂时无法编辑,只做查看</p>
-            </div>
-          );
-        }
-        return (
-          <div>
-            <a onClick={() => this.handleSelectorModalShow('product', id)}>
-              <Icon type="double-right" />查看详情
-            </a>
-          </div>
-        );
-      },
-      width: '20%',
-      align: 'center',
-    }, {
-      title: '操作',
-      key: 6,
-      width: '30%',
-      render: (_, record) => {
-        const { id, isNew, isEdit } = record;
-        const getPopconfirmBtn = () => {
-          return (
-            <Popconfirm
-              placement="top"
-              title="确定要删除该标签?"
-              okText="确定"
-              cancelText="取消"
-              onConfirm={() => this.handleTagItemDelete(id, isNew)}
-            >
-              <Button
-                size="small"
-                className="delBtn"
-              >删除
-              </Button>
-            </Popconfirm>
-          );
-        };
-        if (isEdit) {
-          return (
-            <div>
-              <Button
-                size="small"
-                className="editBtn"
-                onClick={() => this.handleSaveOperation(id)}
-              >保存
-              </Button>
-              {getPopconfirmBtn()}
-              <Button
-                size="small"
-                className="depositBtn"
-                onClick={() => this.handleSelectorModalShow('allTag', id)}
-              >复制
-              </Button>
-            </div>
-          );
-        }
-        return (
-          <div>
-            <Button
-              size="small"
-              className="editBtn"
-              onClick={() => this.handleTagItemEdit(id)}
-            >编辑
-            </Button>
-            {getPopconfirmBtn()}
-            <Button
-              size="small"
-              className="depositBtn"
-              onClick={() => this.handleSelectorModalShow('allTag', id)}
-            >复制
-            </Button>
-          </div>
-        );
-      },
-      align: 'right',
-    }];
-    const getProductModal = () => {
-      const columns = [
-        {
-          title: '产品编号',
-          dataIndex: 'code',
-        },
-        {
-          title: '产品名称',
-          dataIndex: 'name',
-        },
-      ];
-      const { productList = [] } = this.props.configUser.TagDetails;
-      return (
-        <Modal
-          visible
-          width={1100}
-          title="关联产品详情"
-          footer={null}
-          maskClosable={false}
-          onCancel={() => this.handleSelectorCancel('product')}
-        >
-          <Table
-            columns={columns}
-            dataSource={productList}
-            fixedName="Product"
-            onCancel={() => this.handleSelectorCancel('product')}
-          />
-        </Modal>
-      );
-    };
-    /* 标签模态框选择器 */
-    const getResourceModal = () => {
-      return (
-        <Modal
-          width={1100}
-          footer={null}
-          visible
-          title="标签类型"
-          maskClosable={false}
-          onCancel={() => this.handleSelectorCancel('resource')}
-        >
-          <Selector
-            multiple={false}
-            loading={loading}
-            selectorName="TagType"
-            list={tagType.list}
-            pageNo={tagType.pageNo}
-            pageSize={tagType.pageSize}
-            totalSize={tagType.totalSize}
-            onCancel={() => this.handleSelectorCancel('resource')}
-            onChange={data => this.handleSelectorChange('resource', data)}
-            onFinish={rows => this.handleSelectorFinish('resource', rows)}
-          />
-        </Modal>
-      );
-    };
-    /* 选择标签的对应的模态框 */
-    const getAllTagModal = () => {
-      return (
-        <Modal
-          width={1100}
-          footer={null}
-          visible
-          title="可选择的标签"
-          maskClosable={false}
-          onCancel={() => this.handleSelectorCancel('allTag')}
-        >
-          <Selector
-            multiple={false}
-            loading={loading}
-            selectorName="allTag"
-            list={tag.list}
-            pageNo={tag.pageNo}
-            pageSize={tag.pageSize}
-            totalSize={tag.totalSize}
-            onCancel={() => this.handleSelectorCancel('allTag')}
-            onChange={data => this.handleSelectorChange('allTag', data)}
-            onFinish={rows => this.handleSelectorFinish('allTag', rows)}
-          />
-        </Modal>
-      );
-    };
-    return (
-      <div>
-        <Card style={{ marginBottom: 70 }}>
-          <Table
-            pagination={false}
-            dataSource={userTagLists}
-            columns={userTagColumns}
-            rowKey={record => record.id}
-            className={styles.posterTable}
-          />
-          <Button
-            type="dashed"
-            icon="plus"
-            style={{ width: '100%', marginTop: 16, marginBottom: 8 }}
-            onClick={this.handleTagItemCreate}
-          >新建
-          </Button>
-          {!resourceSelectorDestroy && getResourceModal()}
-          {!productSelectorDestroy && getProductModal()}
-          {!allTagSelectorDestroy && getAllTagModal()}
-        </Card>
-        <FooterToolbar style={{ width: '100%' }}>
-          <Button type="primary" onClick={this.handlePageBack}>返回上一页</Button>
-        </FooterToolbar>
-      </div>
-    );
-  }
-}

+ 0 - 67
src/routes/Frontend/ConfigUser/ConfigTag.less

@@ -1,67 +0,0 @@
-@import "../../../../node_modules/antd/lib/style/themes/default.less";
-
-.posterTable {
-  :global {
-    .ant-table-title {
-      padding: 0 0 16px 0;
-    }
-    .ant-table-footer {
-      padding: 10px;
-    }
-    .ant-table-tbody > tr > td {
-      padding: 5px 10px;
-    }
-    .ant-table-thead > tr > th {
-      padding: 10px 5px;
-    }
-  }
-}
-
-.mongolian {
-  z-index: 10;
-  display: none;
-  position: absolute;
-  top: 0;
-  left: 0;
-  width: 100%;
-  height: 100%;
-  a {
-    position: relative;
-    top: 45%;
-    transform: translateY(-45%);
-    padding: 5px 15px;
-    background: #fff;
-    color: #73777a;
-  }
-}
-
-.cover {
-  position: relative;
-  margin: 0;
-  width: 100%;
-  height: 100%;
-  img {
-    width: 100%;
-    height: 100%;
-  }
-  &:hover {
-    .mongolian {
-      display: block;
-      background: rgba(0, 193, 222, .8);
-    }
-  }
-}
-
-.product {
-  position: relative;
-  &:hover {
-    .mongolian {
-      display: block;
-      background: rgba(0, 193, 222, .8);
-    }
-  }
-}
-.copyBtn {
-  margin-left: 10px;
-  font-weight: 500;
-}

+ 0 - 188
src/routes/Frontend/ConfigUser/ConfigUserLists.js

@@ -1,188 +0,0 @@
-import React, { Component } from 'react';
-import { connect } from 'dva';
-import { routerRedux } from 'dva/router';
-import { Card, Form, Menu, Dropdown, Icon } from 'antd';
-import { StandardTableList } from '../../../components/AXList/index';
-import Ellipsis from '../../../components/Ellipsis/index';
-import { addRowKey, renderStatus } from '../../../utils/utils';
-
-@Form.create()
-@connect(({ loading, campus, merchant, terminal }) => ({
-  campus,
-  merchant,
-  terminal,
-  fetching1: loading.models.merchant,
-  fetching2: loading.models.campus,
-  loading: loading.models.terminal,
-}))
-
-export default class ConfigUserLists extends Component {
-  constructor(props) {
-    super(props);
-    const { state } = props.location;
-    this.state = {
-      UIParams: (state || {}).UIParams, // 组件的状态参数
-      Queryers: (state || {}).Queryers, // 查询的条件参数
-      filterModalDestroy: true,
-      itemData: {},
-    };
-  }
-  componentDidMount() {
-    this.props.dispatch({
-      type: 'terminal/fetchTerminalList',
-      payload: { ...this.state.Queryers },
-    });
-  }
-  // 跳转到配置标签的page
-  handleEditOperation = (item) => {
-    this.props.dispatch(routerRedux.push({
-      pathname: `/frontend/ConfigUser/tag/${item.id}`,
-      state: {
-        currentItem: item,
-        ...this.state,
-      },
-    }));
-  }
-  // 跳转到配置课程页面
-  handleDeviceCourseOperation = (item) => {
-    const { id } = item;
-    this.props.dispatch(routerRedux.push({
-      pathname: `/frontend/ConfigUser/course-edit/${id}`,
-      state: this.state,
-    }));
-  }
-  // 跳到配置海报的页面
-  handleDevicePosterOperation = (item) => {
-    const { id } = item;
-    this.props.dispatch(routerRedux.push({
-      pathname: `/frontend/ConfigUser/poster-edit/${id}`,
-      state: this.state,
-    }));
-  }
-  handleFilterOperation = (params, states) => {
-    this.props.dispatch({
-      type: 'terminal/fetchTerminalList',
-      payload: params,
-    });
-    this.setState({
-      UIParams: states,
-      Queryers: params,
-    });
-  }
-  // 将item中的数据传送给子栏目
-  transferData = (item) => {
-    this.setState(() => {
-      return {
-        itemData: item,
-      };
-    });
-  }
-  render() {
-    const { loading, terminal } = this.props;
-    const { list, totalSize, pageSize, pageNo } = terminal;
-    const { itemData } = this.state;
-
-    const renderCampusName = (name) => {
-      return (
-        <Ellipsis tooltip lines={1}>{name}</Ellipsis>
-      );
-    };
-    const menu = (
-      <Menu>
-        <Menu.Item>
-          <a onClick={() => this.handleDevicePosterOperation(itemData)}>
-            推荐海报
-          </a>
-        </Menu.Item>
-        <Menu.Item>
-          <a onClick={() => this.handleDeviceCourseOperation(itemData)}>
-            推荐课程
-          </a>
-        </Menu.Item>
-      </Menu>
-    );
-    const renderOperation = (item) => {
-      return (
-        <div>
-          <a
-            onClick={() => this.handleEditOperation(item)}
-          >标签配置
-          </a>
-          &nbsp;&nbsp;&nbsp;
-          <Dropdown overlay={menu} trigger={['click']}>
-            <a
-              onClick={() => this.transferData(item)}
-            >推荐配置 <Icon type="down" />
-            </a>
-          </Dropdown>
-        </div>
-      );
-    };
-    const basicSearch = {
-      keys: [{
-        name: '终端编号',
-        field: 'code',
-      }, {
-        name: '终端名称',
-        field: 'name',
-      }],
-    };
-    const pagination = {
-      pageNo,
-      pageSize,
-      totalSize,
-    };
-    const columns = [{
-      title: '终端编号',
-      key: 1,
-      dataIndex: 'code',
-      width: '20%',
-    }, {
-      title: '终端名称',
-      key: 2,
-      dataIndex: 'name',
-      width: '20%',
-    }, {
-      title: '所属校区',
-      key: 3,
-      dataIndex: 'campusName',
-      render: text => renderCampusName(text),
-      width: '22%',
-    }, {
-      title: '所属渠道',
-      key: 4,
-      dataIndex: 'merchantName',
-      width: '12%',
-    }, {
-      title: '账号状态',
-      key: 5,
-      dataIndex: 'status',
-      render: text => renderStatus(text, '已禁用'),
-      width: '8%',
-    }, {
-      title: '操作',
-      key: 6,
-      dataIndex: 'operation',
-      render: (_, record) => renderOperation(record),
-      width: '20%',
-      align: 'right',
-    }];
-    return (
-      <Card>
-        <StandardTableList
-          columns={columns}
-          loading={loading}
-          dataSource={addRowKey(list)}
-          header={{
-            basicSearch,
-            onFilterClick: this.handleFilterOperation,
-          }}
-          footer={{
-            pagination,
-          }}
-          keepUIState={{ ...this.state.UIParams }}
-        />
-      </Card>
-    );
-  }
-}

+ 688 - 0
src/routes/Frontend/Personalize/PersonalizeEdit.js

@@ -0,0 +1,688 @@
+/* eslint-disable no-trailing-spaces */
+import React, { Component } from 'react';
+import pathToRegexp from 'path-to-regexp';
+import { routerRedux } from 'dva/router';
+import { connect } from 'dva';
+import { Card, Table, Modal, Form, Tooltip, Popconfirm, Switch, Button, Input } from 'antd';
+import Selector from '../../../components/AXTableSelector/Selector';
+import AXDragSortTable from '../../../components/AXDragSortTable';
+import FooterToolbar from '../../../components/FooterToolbar';
+import { renderStatus, statusToBool, boolToStatus, renderProductType } from '../../../utils/utils';
+import { Hotax } from '../../../utils/config';
+import styles from './PersonalizeEdit.less';
+
+const formItemLayout = {
+  labelCol: {
+    xs: { span: 24 },
+    sm: { span: 2 },
+    md: { span: 2 },
+  },
+  wrapperCol: {
+    xs: { span: 24 },
+    sm: { span: 14 },
+    md: { span: 22 },
+  },
+};
+
+@Form.create()
+@connect(({ loading, terminal, shelves, tagType, tag }) => ({
+  tag,
+  shelves,
+  terminal,
+  tagType,
+  loading,
+  sLoading: loading.models.shelves,
+  tLoading: loading.models.tagType,
+  mtLoading: loading.models.tag,
+}))
+export default class PersonalizeEditPage extends Component {
+  constructor(props) {
+    super(props);
+    const { location } = props;
+    const match = pathToRegexp('/frontend/personalize/edit/:id').exec(location.pathname);
+    this.state = {
+      uid: match[1],
+      tagModalDestroy: true,
+      tagModalName: '新建用户标签',
+      tagTypeSelecting: false, // 标签类型处于选择状态
+      recModalDestroy: true,
+      productSelecting: false, // 产品处于选择状态
+      merchantTagModalDestroy: true,
+      targetRow: {},
+    };
+  }
+  componentDidMount() {
+    // 加载用户标签数据
+    this.props.dispatch({
+      type: 'terminal/fetchTerminalTagList',
+      payload: { uid: this.state.uid },
+    });
+    // 加载用户推荐课程
+    this.props.dispatch({
+      type: 'terminal/fetchTerminalRecommendCourse',
+      payload: { uid: this.state.uid },
+    });
+    // 加载全部
+    this.props.dispatch({
+      type: 'tagType/fetchTagTypeList',
+      payload: { pageSize: 1000 },
+    });
+  }
+
+  /**
+   * 用户标签创建/编辑模态框展现及数据加载控制
+   * @param userTagId
+   */
+  handleUserTagModalShow = (userTagId) => {
+    // 展现模态框前清空下currentUserTagItem内容
+    this.props.dispatch({
+      type: 'terminal/resetUserTagItem',
+    });
+    // 展现模态框
+    this.setState({ tagModalDestroy: false });
+    // 如果编辑标签则发获取详情请求
+    if (userTagId) {
+      this.props.dispatch({
+        type: 'terminal/fetchTerminalTagItem',
+        payload: { userTagId },
+      });
+      this.setState({ tagModalName: '编辑用户标签' });
+    }
+  };
+  /**
+   * 用户推荐课程模态框的展现
+   */
+  handleUserRecModalShow = () => {
+    this.setState({ recModalDestroy: false });
+  };
+  handleMerchantTagModalShow = (record) => {
+    this.setState({
+      targetRow: record,
+      merchantTagModalDestroy: false,
+    });
+    this.props.dispatch({
+      type: 'tag/fetchTagList',
+      payload: {},
+    });
+  };
+  /**
+   * 模态框的取消操作
+   * @param modalName
+   */
+  handleModalHide = (modalName) => {
+    this.setState({ [modalName]: true });
+  };
+  /**
+   * 用户标签内卡片切换操作
+   * @param name
+   */
+  handleCardSwitch = (name) => {
+    if (name === 'tagType') {
+      this.props.dispatch({
+        type: 'tagType/fetchTagTypeList',
+        payload: {},
+      });
+      this.setState({ tagTypeSelecting: true });
+    }
+    if (name === 'product') {
+      this.props.dispatch({
+        type: 'shelves/fetchItemList',
+        payload: {},
+      });
+      this.setState({ productSelecting: true });
+    }
+    if (name === 'course') {
+      this.props.dispatch({
+        type: 'shelves/fetchCourseItemList',
+        payload: {},
+      });
+      this.setState({ productSelecting: true });
+    }
+  };
+  /**
+   * 标签类型/关联产品取消选择操作
+   * @param name
+   */
+  handleSelectingCardCancel = (name) => {
+    if (name === 'tagType') {
+      this.setState({ tagTypeSelecting: false });
+    }
+    if (name === 'product' || name === 'course') {
+      this.setState({ productSelecting: false });
+    }
+  };
+  /**
+   * 标签类型/关联产品筛选操作
+   * @param name
+   * @param data
+   */
+  handleSelectingCardChange = (name, data) => {
+    if (name === 'tagType') {
+      this.props.dispatch({
+        type: 'tagType/fetchTagTypeList',
+        payload: data,
+      });
+    }
+    if (name === 'product') {
+      this.props.dispatch({
+        type: 'shelves/fetchItemList',
+        payload: data,
+      });
+    }
+    if (name === 'course') {
+      this.props.dispatch({
+        type: 'shelves/fetchCourseItemList',
+        payload: data,
+      });
+    }
+    if (name === 'tag') {
+      this.props.dispatch({
+        type: 'tag/fetchTagList',
+        payload: data,
+      });
+    }
+  };
+  /**
+   * 标签类型/关联产品完成操作
+   * @param cardName
+   * @param data
+   */
+  handleSelectingCardFinish = (cardName, data) => {
+    if (cardName === 'tagType') {
+      const tagType = data[0] || {};
+      const { code, name } = tagType;
+      this.props.dispatch({
+        type: 'terminal/fixCurrentUserTagItem',
+        payload: {
+          typeCode: code,
+          typeName: name,
+        },
+      });
+      this.setState({ tagTypeSelecting: false });
+    }
+    if (cardName === 'product') {
+      this.props.dispatch({
+        type: 'terminal/fixCurrentUserTagItem',
+        payload: { productList: data },
+      });
+      this.setState({ productSelecting: false });
+    }
+    if (cardName === 'course') {
+      this.props.dispatch({
+        type: 'terminal/fixUserRecCourse',
+        payload: (data || []).slice(0, 5),
+      });
+      this.setState({ productSelecting: false });
+    }
+  };
+  /**
+   * 用户标签内关联产品/推荐课程排序操作
+   * @param rows
+   * @param tabName
+   */
+  handleDragSortTableChange = (rows, tabName) => {
+    if (tabName === 'tag') {
+      this.props.dispatch({
+        type: 'terminal/fixCurrentUserTagItem',
+        payload: { productList: rows },
+      });
+    }
+    if (tabName === 'rec') {
+      this.props.dispatch({
+        type: 'terminal/fixUserRecCourse',
+        payload: rows,
+      });
+    }
+  };
+  handlePageBack = () => {
+    this.props.dispatch(routerRedux.push({
+      pathname: '/frontend/personalize/list',
+      state: this.props.location.state,
+    }));
+  };
+  handleUserTagSubmit = () => {
+    this.props.form.validateFieldsAndScroll((err, values) => {
+      if (!err) {
+        const { sort, name, status } = values;
+        const { uid } = this.state;
+        const { terminal } = this.props;
+        const { currentUserTagItem } = terminal;
+        const { id, typeCode, productList } = currentUserTagItem;
+        const pidList = (productList || []).map(product => product.pid);
+        const newStatus = boolToStatus(status);
+        if (!id) {
+          this.props.dispatch({
+            type: 'terminal/createTerminalTagItem',
+            payload: { sort, uid, name, typeCode, status: newStatus, productList: pidList },
+          });
+        } else {
+          this.props.dispatch({
+            type: 'terminal/updateTerminalTagItem',
+            payload: { id, sort, uid, name, typeCode, status: newStatus, productList: pidList },
+          });
+        }
+        this.setState({ tagModalDestroy: true });
+      }
+    });
+  };
+  handleUserTagCopyOperation = (data) => {
+    const { targetRow } = this.state;
+    const merchantTag = (data || [])[0] || {};
+    this.props.dispatch({
+      type: 'terminal/copyMerchantTagToUser',
+      payload: { uid: this.state.uid, userTagId: targetRow.id, tagId: merchantTag.id },
+    });
+    this.setState({ merchantTagModalDestroy: true });
+  };
+  handleUserTagDelete = (userTagId) => {
+    this.props.dispatch({
+      type: 'terminal/deleteTerminalTagItem',
+      payload: { uid: this.state.uid, userTagId },
+    });
+  };
+  handleUserRecCourseSubmit = () => {
+    const { uid } = this.state;
+    const { terminal } = this.props;
+    const { userRecCourse } = terminal;
+    const idList = (userRecCourse || []).map(product => product.pid);
+    this.props.dispatch({
+      type: 'terminal/updateTerminalRecommendCourse',
+      payload: { uid, idList },
+    });
+    this.setState({ recModalDestroy: true });
+  };
+  render() {
+    const {
+      tagModalDestroy,
+      tagModalName,
+      tagTypeSelecting,
+      productSelecting,
+      recModalDestroy,
+      merchantTagModalDestroy,
+    } = this.state;
+    const {
+      sLoading, tLoading, mtLoading, loading, terminal, shelves, tagType, tag, form,
+    } = this.props;
+    const { getFieldDecorator } = form;
+    const { userTagList, userRecCourse, currentUserTagItem } = terminal;
+    const { sort, name, status, productList, typeCode, typeName } = currentUserTagItem;
+
+    // 用户标签列表表头
+    const tagColumns = [{
+      title: '标签位置',
+      dataIndex: 'sort',
+      width: '10%',
+    }, {
+      title: '标签名称',
+      dataIndex: 'name',
+      width: '25%',
+    }, {
+      title: '标签类型',
+      dataIndex: 'typeCode',
+      width: '25%',
+      filters: (tagType.list || []).map(item => ({ text: item.name, value: item.code })),
+      onFilter: (value, record) => record.typeCode.indexOf(value) === 0,
+    }, {
+      title: '标签状态',
+      dataIndex: 'status',
+      width: '15%',
+      render: text => renderStatus(text),
+      filters: [{
+        text: '正常',
+        value: Hotax.STATUS_NORMAL,
+      }, {
+        text: '删除',
+        value: Hotax.STATUS_DELETE,
+      }],
+      onFilter: (value, record) => record.status === value,
+      align: 'center',
+    }, {
+      title: '操作',
+      width: '25%',
+      align: 'right',
+      render: (_, record) => (
+        <div>
+          <Button
+            size="small"
+            className="editBtn"
+            onClick={() => this.handleUserTagModalShow(record.id)}
+          >编辑
+          </Button>
+          <Tooltip title="复制渠道标签进行关联产品">
+            <Button
+              size="small"
+              className="recBtn"
+              onClick={() => this.handleMerchantTagModalShow(record)}
+            >复制
+            </Button>
+          </Tooltip>
+          <Popconfirm
+            placement="top"
+            title="确定要删除该用户标签?"
+            okText="确定"
+            cancelText="取消"
+            onConfirm={() => this.handleUserTagDelete(record.id)}
+          >
+            <Button
+              size="small"
+              className="delBtn"
+            >删除
+            </Button>
+          </Popconfirm>
+        </div>
+      ),
+    }];
+    // 推荐课程
+    const courseColumns = [{
+      title: '课程编号',
+      dataIndex: 'code',
+      key: 1,
+      width: '40%',
+    }, {
+      title: '课程名称',
+      dataIndex: 'name',
+      key: 2,
+      width: '40%',
+    }, {
+      title: '课程状态',
+      dataIndex: 'status',
+      key: 3,
+      render: text => renderStatus(text),
+      width: '20%',
+    }];
+
+    /* ************************ modal1: 用户标签创建 ************************* */
+    const getTagCreateModal = () => {
+      const tagTypeColumns = [{
+        title: '标签类型编号',
+        dataIndex: 'code',
+        key: 1,
+        width: '50%',
+      }, {
+        title: '标签类型名称',
+        dataIndex: 'name',
+        key: 2,
+        width: '50%',
+      }];
+      const tagTypeData = typeCode ? [{ key: 'row-1', name: typeName || '-', code: typeCode }] : undefined;
+      const tagTypeSelectCard = (
+        <Card title="选择标签类型">
+          <Selector
+            multiple={false}
+            loading={tLoading}
+            selectorName="TagType"
+            list={tagType.list}
+            pageNo={tagType.pageNo}
+            pageSize={tagType.pageSize}
+            totalSize={tagType.totalSize}
+            onCancel={() => this.handleSelectingCardCancel('tagType')}
+            onChange={data => this.handleSelectingCardChange('tagType', data)}
+            onFinish={data => this.handleSelectingCardFinish('tagType', data)}
+          />
+        </Card>
+      );
+      const tagTypeShowCard = (
+        <Card title="标签类型信息">
+          <Table
+            pagination={false}
+            dataSource={tagTypeData}
+            columns={tagTypeColumns}
+            className={styles.tagTable}
+            onChange={this.handleUserTagTableChange}
+          />
+        </Card>
+      );
+      const productColumns = [{
+        title: '产品编号',
+        dataIndex: 'code',
+        key: 1,
+        width: '20%',
+      }, {
+        title: '产品名称',
+        dataIndex: 'name',
+        key: 2,
+        width: '30%',
+      }, {
+        title: '产品类型',
+        dataIndex: 'type',
+        key: 3,
+        render: text => renderProductType(text),
+      }];
+      const productSelectCard = (
+        <Card title="选择产品">
+          <Selector
+            multiple
+            loading={sLoading}
+            selectorName="Product"
+            list={shelves.list}
+            pageNo={shelves.pageNo}
+            pageSize={shelves.pageSize}
+            totalSize={shelves.totalSize}
+            selectedRows={productList}
+            onCancel={() => this.handleSelectingCardCancel('product')}
+            onChange={data => this.handleSelectingCardChange('product', data)}
+            onFinish={data => this.handleSelectingCardFinish('product', data)}
+          />
+        </Card>
+      );
+      const productShowCard = (
+        <Card title="已关联产品列表">
+          <AXDragSortTable
+            data={productList}
+            columns={productColumns}
+            onChange={rows => this.handleDragSortTableChange(rows, 'tag')}
+          />
+        </Card>
+      );
+      return (
+        <Modal
+          visible
+          width={1100}
+          title={tagModalName}
+          maskClosable={false}
+          cancelText="取消"
+          okText="提交"
+          onCancel={() => this.handleModalHide('tagModalDestroy')}
+          onOk={this.handleUserTagSubmit}
+        >
+          <Form>
+            <Form.Item hasFeedback label="标签名称" {...formItemLayout}>
+              {getFieldDecorator('name', {
+                rules: [{ required: true, message: '请填写标签名称' }],
+                initialValue: name,
+              })(
+                <Input placeholder="请输入" />
+              )}
+            </Form.Item>
+            <Form.Item hasFeedback label="标签位置" {...formItemLayout}>
+              {getFieldDecorator('sort', {
+                initialValue: sort,
+              })(
+                <Input placeholder="请输入" />
+              )}
+            </Form.Item>
+            <Form.Item label="标签类型" {...formItemLayout}>
+              <Button
+                disabled={tagTypeSelecting}
+                type="primary"
+                size="small"
+                icon="search"
+                onClick={() => this.handleCardSwitch('tagType')}
+              >选择
+              </Button>
+            </Form.Item>
+            <Form.Item wrapperCol={{ offset: 2, span: 22 }}>
+              {tagTypeSelecting ? tagTypeSelectCard : tagTypeShowCard}
+            </Form.Item>
+            <Form.Item label="关联产品" {...formItemLayout}>
+              <Button
+                disabled={productSelecting}
+                type="primary"
+                size="small"
+                icon="form"
+                onClick={() => this.handleCardSwitch('product')}
+              >编辑
+              </Button>
+            </Form.Item>
+            <Form.Item wrapperCol={{ offset: 2, span: 22 }}>
+              {productSelecting ? productSelectCard : productShowCard}
+            </Form.Item>
+            <Form.Item label="标签状态" {...formItemLayout}>
+              {getFieldDecorator('status', {
+                valuePropName: 'checked',
+                initialValue: statusToBool(status),
+              })(
+                <Switch checkedChildren="正常" unCheckedChildren="删除" />
+              )}
+            </Form.Item>
+          </Form>
+        </Modal>
+      );
+    };
+
+    /* ************************ modal2: 用户推荐课程修改 ************************* */
+    const getRecCourseModal = () => {
+      const cColumns = [{
+        title: '课程编号',
+        dataIndex: 'code',
+        key: 1,
+        width: '30%',
+      }, {
+        title: '课程名称',
+        dataIndex: 'name',
+        key: 2,
+        width: '30%',
+      }];
+      const recCourseSelectCard = (
+        <Card title="选择课程">
+          <Selector
+            multiple
+            loading={sLoading}
+            selectorName="Course"
+            list={shelves.list}
+            pageNo={shelves.pageNo}
+            pageSize={shelves.pageSize}
+            totalSize={shelves.totalSize}
+            selectedRows={userRecCourse}
+            onCancel={() => this.handleSelectingCardCancel('course')}
+            onChange={data => this.handleSelectingCardChange('course', data)}
+            onFinish={data => this.handleSelectingCardFinish('course', data)}
+          />
+        </Card>
+      );
+      const recCourseShowCard = (
+        <Card title="推荐课程列表">
+          <AXDragSortTable
+            data={userRecCourse}
+            columns={cColumns}
+            onChange={rows => this.handleDragSortTableChange(rows, 'rec')}
+          />
+        </Card>
+      );
+      return (
+        <Modal
+          visible
+          width={1100}
+          title="用户推荐课程"
+          maskClosable={false}
+          cancelText="取消"
+          okText="提交"
+          onCancel={() => this.handleModalHide('recModalDestroy')}
+          onOk={this.handleUserRecCourseSubmit}
+        >
+          <Form>
+            <Form.Item label="推荐课程" {...formItemLayout}>
+              <Button
+                disabled={productSelecting}
+                type="primary"
+                size="small"
+                icon="search"
+                onClick={() => this.handleCardSwitch('course')}
+              >选择
+              </Button>
+            </Form.Item>
+            <Form.Item wrapperCol={{ offset: 2, span: 22 }}>
+              {productSelecting ? recCourseSelectCard : recCourseShowCard}
+            </Form.Item>
+          </Form>
+        </Modal>
+      );
+    };
+
+    /* ************************ modal3: 渠道标签选择 ************************* */
+    const getMerchantTagModal = () => {
+      return (
+        <Modal
+          width={1100}
+          footer={null}
+          visible
+          title="渠道标签列表"
+          maskClosable={false}
+          onCancel={() => this.handleModalHide('merchantTagModalDestroy')}
+        >
+          <Selector
+            multiple={false}
+            loading={mtLoading}
+            selectorName="Tag"
+            list={tag.list}
+            pageNo={tag.pageNo}
+            pageSize={tag.pageSize}
+            totalSize={tag.totalSize}
+            onCancel={() => this.handleModalHide('merchantTagModalDestroy')}
+            onChange={data => this.handleSelectingCardChange('tag', data)}
+            onFinish={this.handleUserTagCopyOperation}
+          />
+        </Modal>
+      );
+    };
+
+    return (
+      <div>
+        <Card
+          title={
+            <div>
+              用户标签
+              <Button onClick={() => this.handleUserTagModalShow()} style={{ float: 'right' }} type="primary">新建标签</Button>
+            </div>
+          }
+          loading={loading.effects['terminal/fetchTerminalTagList']}
+          style={{ marginBottom: 16 }}
+          className={styles.tagCard}
+        >
+          <Table
+            pagination={{ pageSize: 15 }}
+            dataSource={userTagList}
+            columns={tagColumns}
+            rowKey={record => record.id}
+            className={styles.tagTable}
+          />
+          {!tagModalDestroy && getTagCreateModal()}
+          {!merchantTagModalDestroy && getMerchantTagModal()}
+        </Card>
+        <Card
+          title={
+            <div>
+              推荐课程
+              <Button onClick={this.handleUserRecModalShow} style={{ float: 'right' }} type="primary">更换课程</Button>
+            </div>
+          }
+          loading={loading.effects['terminal/fetchTerminalRecommendCourse']}
+          style={{ marginBottom: 70 }}
+          className={styles.tagCard}
+        >
+          <Table
+            pagination={false}
+            dataSource={userRecCourse}
+            columns={courseColumns}
+            rowKey={record => record.id}
+            className={styles.tagTable}
+          />
+          {!recModalDestroy && getRecCourseModal()}
+        </Card>
+        <FooterToolbar style={{ width: '100%' }}>
+          <Button type="primary" onClick={this.handlePageBack}>返回上一页</Button>
+        </FooterToolbar>
+      </div>
+    );
+  }
+}

+ 26 - 0
src/routes/Frontend/Personalize/PersonalizeEdit.less

@@ -0,0 +1,26 @@
+@import "../../../../node_modules/antd/lib/style/themes/default.less";
+
+.tagTable {
+  :global {
+    .ant-table-title {
+      padding: 0 0 16px 0;
+    }
+    .ant-table-footer {
+      padding: 10px;
+    }
+    .ant-table-tbody > tr > td {
+      padding: 5px;
+    }
+    .ant-table-thead > tr > th {
+      padding: 10px 5px;
+    }
+  }
+}
+.tagCard {
+  :global {
+    .ant-card-head {
+      padding: 0 16px !important;
+    }
+  }
+}
+

+ 263 - 0
src/routes/Frontend/Personalize/PersonalizeList.js

@@ -0,0 +1,263 @@
+/* eslint-disable prefer-destructuring */
+import React, { Component } from 'react';
+import moment from 'moment';
+import { connect } from 'dva';
+import { routerRedux } from 'dva/router';
+import { Card, Modal, Form, Button, message } from 'antd';
+import { StandardTableList } from '../../../components/AXList';
+import AXRemoteSelect from '../../../components/AXRemoteSelect';
+import Ellipsis from '../../../components/Ellipsis';
+import { addRowKey, renderStatus, renderBindStatus } from '../../../utils/utils';
+
+const Message = message;
+
+const formItemLayout = {
+  labelCol: {
+    xs: { span: 24 },
+    sm: { span: 7 },
+  },
+  wrapperCol: {
+    xs: { span: 24 },
+    sm: { span: 15 },
+    md: { span: 13 },
+  },
+};
+function arrayDataFormatter(data) {
+  return data.map((item) => {
+    return {
+      text: item.name,
+      value: `${item.name}||${item.id}`,
+    };
+  });
+}
+
+@Form.create()
+@connect(({ loading, campus, terminal }) => ({
+  campus,
+  terminal,
+  fetching: loading.models.campus,
+  loading: loading.models.terminal,
+}))
+export default class PersonalizeListPage extends Component {
+  constructor(props) {
+    super(props);
+    const { state } = props.location;
+    this.state = {
+      UIParams: (state || {}).UIParams, // 组件的状态参数
+      Queryers: (state || {}).Queryers, // 查询的条件参数
+      campuses: (state || {}).campuses || [], // 记录筛选的校区
+      filterModalDestroy: true,
+    };
+  }
+  componentWillMount() {
+    this.props.dispatch({
+      type: 'terminal/cleanState',
+    });
+  }
+  componentDidMount() {
+    const { campuses } = this.state;
+    let campusId;
+    if (campuses && campuses.length) {
+      campusId = campuses[0].split('||')[1];
+    }
+    this.props.dispatch({
+      type: 'terminal/fetchTerminalList',
+      payload: {
+        campusId,
+        ...this.state.Queryers,
+      },
+    });
+  }
+  handleEditOperation = (item) => {
+    this.props.dispatch(routerRedux.push({
+      pathname: `/frontend/personalize/edit/${item.id}`,
+      state: { ...this.state },
+    }));
+  };
+  handleFilterOperation = (params, states) => {
+    this.setState({
+      UIParams: states,
+      Queryers: params,
+    });
+    const { campuses } = this.state;
+    let campusId;
+    if (campuses && campuses.length) {
+      campusId = campuses[0].split('||')[1];
+    }
+    this.props.dispatch({
+      type: 'terminal/fetchTerminalList',
+      payload: {
+        campusId,
+        ...params,
+      },
+    });
+  };
+  handleModalFilterOperation = () => {
+    const { getFieldsValue } = this.props.form;
+    const { campuses } = getFieldsValue();
+    let campusId;
+    if (campuses && campuses.length) {
+      campusId = campuses[0].split('||')[1];
+    }
+    this.props.dispatch({
+      type: 'terminal/fetchTerminalList',
+      payload: {
+        ...this.state.Queryers,
+        campusId,
+      },
+    });
+    this.setState({ campuses });
+    this.handleFilterModalDestroy();
+  };
+  handleBatchOperation = () => {
+    Message.info('暂不支持批量操作!');
+  };
+  handleFilterModalShow = () => {
+    this.setState({ filterModalDestroy: false });
+  };
+  handleFilterModalDestroy = () => {
+    this.setState({ filterModalDestroy: true });
+  };
+  handleMerchantRemoteSelectSearch = (value) => {
+    this.props.dispatch({
+      type: 'merchant/fetchMerchantList',
+      payload: {
+        pageSize: 50,
+        name: value,
+      },
+    });
+  };
+  handleCampusRemoteSelectSearch = (value) => {
+    this.props.dispatch({
+      type: 'campus/fetchCampusList',
+      payload: {
+        pageSize: 50,
+        name: value,
+      },
+    });
+  };
+
+  render() {
+    const { campuses } = this.state;
+    const { loading, fetching, form, campus, terminal } = this.props;
+    const { list, totalSize, pageSize, pageNo } = terminal;
+    const { getFieldDecorator } = form;
+    const renderCampusName = (name) => {
+      return (
+        <Ellipsis tooltip lines={1}>{name}</Ellipsis>
+      );
+    };
+    const renderOperation = (item) => {
+      return (
+        <div>
+          <Button
+            size="small"
+            className="editBtn"
+            onClick={() => this.handleEditOperation(item)}
+          >用户配置
+          </Button>
+        </div>
+      );
+    };
+    const batchActions = [{
+      key: 'config',
+      name: '批量配置',
+    }];
+    const basicSearch = {
+      keys: [{
+        name: '终端编号',
+        field: 'code',
+      }, {
+        name: '终端名称',
+        field: 'name',
+      }],
+    };
+    const pagination = {
+      pageNo,
+      pageSize,
+      totalSize,
+    };
+    const columns = [{
+      title: '终端编号',
+      dataIndex: 'code',
+      width: '15%',
+    }, {
+      title: '终端名称',
+      dataIndex: 'name',
+      width: '15%',
+    }, {
+      title: '所属校区',
+      dataIndex: 'campusName',
+      render: text => renderCampusName(text),
+      width: '25%',
+    }, {
+      title: '账号状态',
+      dataIndex: 'status',
+      render: text => renderStatus(text, '已禁用'),
+      width: '8%',
+    }, {
+      title: '绑定状态',
+      dataIndex: 'deviceStatus',
+      render: text => renderBindStatus(text),
+      width: '9%',
+    }, {
+      title: '更新时间',
+      dataIndex: 'gmtModified',
+      render: text => moment(text).format('YYYY-MM-DD HH:mm:ss'),
+      width: '15%',
+    }, {
+      title: '操作',
+      dataIndex: 'operation',
+      render: (_, record) => renderOperation(record),
+      width: '13%',
+      align: 'right',
+    }];
+    return (
+      <Card>
+        <StandardTableList
+          columns={columns}
+          loading={loading}
+          dataSource={addRowKey(list)}
+          header={{
+            basicSearch,
+            onAdvanceFilterClick: this.handleFilterModalShow,
+            onFilterClick: this.handleFilterOperation,
+            onCreateClick: this.handleCreateOperation,
+          }}
+          footer={{
+            pagination,
+            batchActions,
+            onBatchClick: this.handleBatchOperation,
+          }}
+          keepUIState={{ ...this.state.UIParams }}
+        />
+        {!this.state.filterModalDestroy && (
+          <Modal
+            width={600}
+            visible
+            title="高级筛选"
+            okText="筛选"
+            cancelText="取消"
+            maskClosable={false}
+            onCancel={this.handleFilterModalDestroy}
+            onOk={this.handleModalFilterOperation}
+          >
+            <Form>
+              <Form.Item label="所属校区" {...formItemLayout}>
+                {getFieldDecorator('campuses', {
+                  initialValue: campuses,
+                })(
+                  <AXRemoteSelect
+                    fetching={fetching}
+                    dataSource={arrayDataFormatter(campus.list)}
+                    onSearch={this.handleCampusRemoteSelectSearch}
+                  />
+                )}
+              </Form.Item>
+            </Form>
+          </Modal>
+        )}
+      </Card>
+    );
+  }
+}

+ 2 - 2
src/routes/Frontend/ConfigUser/index.js

@@ -5,7 +5,7 @@ import PageHeaderLayout from '../../../layouts/PageHeaderLayout';
 import { getRoutes } from '../../../utils/utils';
 
 @connect()
-export default class ConfigTag extends Component {
+export default class Personalize extends Component {
   render() {
     const { match, routerData } = this.props;
     const routes = getRoutes(match.path, routerData);
@@ -24,7 +24,7 @@ export default class ConfigTag extends Component {
               )
             )
           }
-          <Redirect exact from="/frontend/ConfigUser" to="/frontend/ConfigUser/list" />
+          <Redirect exact from="/frontend/personalize" to="/frontend/personalize/list" />
         </Switch>
       </PageHeaderLayout>
     );

+ 35 - 0
src/routes/Resource/Picture/PictureTableList.less

@@ -0,0 +1,35 @@
+@import "../../../../node_modules/antd/lib/style/themes/default.less";
+
+.thumbPic {
+  position: relative;
+  vertical-align: middle;
+  text-align: center;
+  width: 90px;
+  height: 128px;
+  line-height: 128px;
+  img {
+    max-height: 100%;
+    max-width: 100%;
+    vertical-align: middle;
+    height: auto;
+  }
+}
+
+.meta {
+  p {
+    font-weight: 500;
+  }
+}
+
+.editBtn {
+  margin-right: 10px;
+  background: @primary-5;
+  color: #fff;
+  font-weight: 500;
+}
+.delBtn {
+  margin-right: 10px;
+  background: #f5222d;
+  color: #fff;
+  font-weight: 500;
+}

+ 36 - 2
src/routes/Shelves/ShelvesEdit.js

@@ -1,18 +1,21 @@
+/* eslint-disable no-trailing-spaces */
 import React, { Component } from 'react';
 import pathToRegexp from 'path-to-regexp';
 import { Card, Modal, Form, Button, Tag } from 'antd';
 import { connect } from 'dva';
 import { routerRedux } from 'dva/router';
 import TableForm from './TableForm';
+import { CourseItem, SupportItem, TrainingItem, PackageItem } from '../../components/AXItem';
 import FooterToolbar from '../../components/FooterToolbar';
 import Selector from '../../components/AXTableSelector/Selector';
 import { addRowKey } from '../../utils/utils';
 import { Hotax } from '../../utils/config';
 
-@connect(({ loading, shelves, resource, tag }) => ({
+@connect(({ loading, shelves, product, resource, tag }) => ({
   tag,
   shelves,
   resource,
+  product,
   rLoading: loading.models.resource,
   tLoading: loading.models.tag,
   submitting: loading.models.shelves,
@@ -48,6 +51,10 @@ export default class ShelvesEdit extends Component {
         merchantId,
       },
     });
+    this.props.dispatch({
+      type: 'product/fetchProductItem',
+      payload: { pid },
+    });
   }
   handleGoodsCreate = (data) => {
     const { pid, merchantId, scene } = this.state;
@@ -177,13 +184,40 @@ export default class ShelvesEdit extends Component {
   };
   render() {
     const { tagSelectorDestroy, resourceSelectorDestroy } = this.state;
-    const { submitting, tLoading, rLoading, resource, shelves, tag, form } = this.props;
+    const { submitting, tLoading, rLoading, resource, product, shelves, tag, form } = this.props;
     const { getFieldDecorator } = form;
     const { currentItem } = shelves;
     const { goods, tags } = currentItem;
 
+    // render product show card
+    const renderProductCard = (proInfo) => {
+      if (!proInfo) return;
+      const { type } = proInfo;
+      switch (type) {
+        case Hotax.PRODUCT_COURSE:
+          return (
+            <CourseItem {...proInfo} />
+          );
+        case Hotax.PRODUCT_SUPPORT:
+          return (
+            <SupportItem {...proInfo} />
+          );
+        case Hotax.PRODUCT_TRAINING:
+          return (
+            <TrainingItem {...proInfo} />
+          );
+        case Hotax.PRODUCT_PACKAGE:
+          return (
+            <PackageItem {...proInfo} />
+          );
+        default:
+          return null;
+      }
+    };
+
     return (
       <div>
+        {renderProductCard(product ? product.currentItem : {})}
         <Card title="价格管理" style={{ marginBottom: 16 }}>
           {getFieldDecorator('goods', {
             initialValue: (!goods || !goods.length) ? null : addRowKey(goods),

+ 7 - 16
src/routes/Shelves/ShelvesList.js

@@ -153,11 +153,11 @@ export default class ShelvesListPage extends Component {
     }];
     const basicSearch = {
       keys: [{
-        name: `${productNameMap[scene]}名称`,
-        field: 'name',
-      }, {
         name: `${productNameMap[scene]}编号`,
         field: 'code',
+      }, {
+        name: `${productNameMap[scene]}名称`,
+        field: 'name',
       }],
     };
     const pagination = {
@@ -167,40 +167,31 @@ export default class ShelvesListPage extends Component {
     };
     const columns = [{
       title: `${productNameMap[scene]}编号`,
-      key: 1,
       dataIndex: 'code',
-      width: '15%',
+      width: '20%',
     }, {
       title: `${productNameMap[scene]}名称`,
-      key: 2,
       dataIndex: 'name',
       render: text => (
         <Ellipsis tooltip lines={1}>{text}</Ellipsis>
       ),
       width: '30%',
     }, {
-      title: '上架渠道',
-      key: 3,
-      dataIndex: 'merchantName',
-      width: '14%',
-    }, {
       title: '上架状态',
-      key: 4,
       dataIndex: 'status',
       render: text => renderStatus(text, '已下架', '已上架'),
       width: '10%',
     }, {
       title: '更新时间',
-      key: 5,
       dataIndex: 'gmtModified',
       render: text => moment(text).format('YYYY-MM-DD HH:mm:ss'),
-      width: '17%',
+      width: '20%',
+      align: 'center',
     }, {
       title: '操作',
-      key: 6,
       dataIndex: 'operation',
       render: (_, record) => renderOperation(record),
-      width: '13%',
+      width: '10%',
       align: 'right',
     }];
     return (

+ 0 - 347
src/routes/System/CmsUser/CmsUserCreate.js

@@ -1,347 +0,0 @@
-/* eslint-disable no-trailing-spaces */
-import React, { PureComponent } from 'react';
-import { message, Card, Modal, List, Form, Input, Button, Radio, Popover, Icon, Switch } from 'antd';
-import { connect } from 'dva';
-import { routerRedux } from 'dva/router';
-import Selector from '../../../components/AXTableSelector/Selector';
-import FooterToolbar from '../../../components/FooterToolbar';
-import { boolToStatus } from '../../../utils/utils';
-import styles from './CmsUserCreate.less';
-
-const Message = message;
-
-const genders = [{
-  title: '男',
-  value: 'MALE',
-}, {
-  title: '女',
-  value: 'FEMALE',
-}];
-const fieldLabels = {
-  campus: '所属商户',
-  name: '用户名称',
-  nickName: '用户昵称',
-  gender: '用户性别',
-  birthday: '出生日期',
-  mobile: '电话',
-  mail: '邮箱',
-  qq: 'QQ号码',
-  weChat: '微信号码',
-  password: '用户密码',
-  confirm: '确认密码',
-  status: '初始状态',
-};
-const formItemLayout = {
-  labelCol: {
-    xs: { span: 24 },
-    sm: { span: 6 },
-  },
-  wrapperCol: {
-    xs: { span: 24 },
-    sm: { span: 14 },
-    md: { span: 12 },
-  },
-};
-
-@Form.create()
-@connect(({ loading, merchant, cmsUser }) => ({
-  merchant,
-  cmsUser,
-  mLoading: loading.models.merchant,
-  submitting: loading.models.cmsUser,
-}))
-export default class CmsUserCreatePage extends PureComponent {
-  state = {
-    merchantSelectorDestroy: true,
-  };
-  componentWillUnmount() {
-    this.props.dispatch({
-      type: 'cmsUser/cleanState',
-    });
-  }
-  checkPassword = (rule, value, callback) => {
-    if (value && value !== this.props.form.getFieldValue('password')) {
-      callback('两次输入的密码不一致');
-    } else {
-      callback();
-    }
-  }
-  handleMerchantSelectorModalShow = () => {
-    this.setState({
-      merchantSelectorDestroy: false,
-    });
-    this.props.dispatch({
-      type: 'merchant/fetchMerchantList',
-      payload: {},
-    });
-  }
-  handleMerchantSelectorFinish = (rows) => {
-    this.setState({
-      merchantSelectorDestroy: true,
-    });
-    if (!rows || !rows.length) {
-      return;
-    }
-    const { id, code, name } = rows[0];
-    this.props.dispatch({
-      type: 'cmsUser/fixCurrentItem',
-      payload: {
-        code,
-        merchantId: id,
-        merchantName: name,
-      },
-    });
-  };
-  handleMerchantSelectorCancel = () => {
-    this.setState({
-      merchantSelectorDestroy: true,
-    });
-  };
-  handleMerchantSelectorChange = (params) => {
-    this.props.dispatch({
-      type: 'merchant/fetchMerchantList',
-      payload: params,
-    });
-  }
-  handlePageSubmit = () => {
-    this.props.form.validateFieldsAndScroll((error, values) => {
-      const { currentItem } = this.props.cmsUser;
-      const { merchantId } = currentItem;
-      if (!merchantId) {
-        Message.error('请选择厂商');
-        return;
-      }
-      const { status, ...restProps } = values;
-      restProps.status = boolToStatus(status);
-      restProps.merchantId = merchantId;
-      if (!error) {
-        this.props.dispatch({
-          type: 'cmsUser/createCmsUserItem',
-          payload: restProps,
-          states: this.props.location.state,
-        });
-      }
-    });
-  }
-  handlePageBack = () => {
-    this.props.dispatch(routerRedux.push({
-      pathname: '/system/cms-user/list',
-      state: this.props.location.state,
-    }));
-  }
-
-  render() {
-    const { merchantSelectorDestroy } = this.state;
-    const { form, mLoading, submitting, merchant, cmsUser } = this.props;
-    const { getFieldDecorator, getFieldsError } = form;
-    const { currentItem } = cmsUser;
-    const { code, merchantName } = currentItem;
-
-    const errors = getFieldsError();
-    const getErrorInfo = () => {
-      const errorCount = Object.keys(errors).filter(key => errors[key]).length;
-      if (!errors || errorCount === 0) {
-        return null;
-      }
-      const scrollToField = (fieldKey) => {
-        const labelNode = document.querySelector(`label[for="${fieldKey}"]`);
-        if (labelNode) {
-          labelNode.scrollIntoView(true);
-        }
-      };
-      const errorList = Object.keys(errors).map((key) => {
-        if (!errors[key]) {
-          return null;
-        }
-        return (
-          <li key={key} className={styles.errorListItem} onClick={() => scrollToField(key)}>
-            <Icon type="cross-circle-o" className={styles.errorIcon} />
-            <div className={styles.errorMessage}>{errors[key][0]}</div>
-            <div className={styles.errorField}>{fieldLabels[key]}</div>
-          </li>
-        );
-      });
-      return (
-        <span className={styles.errorIcon}>
-          <Popover
-            title="表单校验信息"
-            content={errorList}
-            overlayClassName={styles.errorPopover}
-            trigger="click"
-            getPopupContainer={trigger => trigger.parentNode}
-          >
-            <Icon type="exclamation-circle" />
-          </Popover>
-          {errorCount}
-        </span>
-      );
-    };
-
-    const getMerchantModal = () => {
-      return (
-        <Modal
-          visible
-          width={1100}
-          footer={null}
-          title="厂商列表"
-          maskClosable={false}
-          onCancel={this.handleMerchantSelectorCancel}
-        >
-          <Selector
-            multiple={false}
-            loading={mLoading}
-            selectorName="Merchant"
-            list={merchant.list}
-            pageNo={merchant.pageNo}
-            pageSize={merchant.pageSize}
-            totalSize={merchant.totalSize}
-            onCancel={this.handleMerchantSelectorCancel}
-            onChange={this.handleMerchantSelectorChange}
-            onFinish={this.handleMerchantSelectorFinish}
-          />
-        </Modal>
-      );
-    };
-
-    return (
-      <div>
-        <Card title="创建用户" style={{ marginBottom: 70 }}>
-          <Form>
-            <Form.Item
-              {...formItemLayout}
-              label={<Button size="small" type="primary" onClick={this.handleMerchantSelectorModalShow}>选择厂商</Button>}
-            >
-              <List
-                bordered
-                size="small"
-                dataSource={[
-                  `厂商编号: ${code || ''}`,
-                  `厂商名称: ${merchantName || ''}`,
-                ]}
-                renderItem={item => <List.Item>{item}</List.Item>}
-              />
-            </Form.Item>
-            <Form.Item hasFeedback label={fieldLabels.name} {...formItemLayout}>
-              {getFieldDecorator('name', {
-                rules: [{ required: true, message: '请填写用户名' }],
-              })(
-                <Input placeholder="请输入" />
-              )}
-            </Form.Item>
-            <Form.Item hasFeedback label={fieldLabels.nickName} {...formItemLayout}>
-              {getFieldDecorator('nickName', {
-                rules: [{ required: true, message: '请填写用户昵称' }],
-              })(
-                <Input placeholder="请输入" />
-              )}
-            </Form.Item>
-            <Form.Item hasFeedback label={fieldLabels.password} {...formItemLayout}>
-              {getFieldDecorator('password', {
-                rules: [
-                  {
-                    required: true, message: '密码不能为空!',
-                  }, {
-                    pattern: /^[a-zA-Z0-9|-]+$/ig, message: '密码格式错误!',
-                  }],
-              })(
-                <Input
-                  type="password"
-                  placeholder="请输入"
-                  addonBefore={<Icon type="lock" />}
-                />
-              )}
-            </Form.Item>
-            <Form.Item hasFeedback label={fieldLabels.confirm} {...formItemLayout}>
-              {getFieldDecorator('confirm', {
-                rules: [
-                  {
-                    required: true, message: '请确认密码',
-                  }, {
-                    validator: this.checkPassword,
-                  }],
-              })(
-                <Input
-                  type="password"
-                  placeholder="请输入"
-                  addonBefore={<Icon type="lock" />}
-                />
-              )}
-            </Form.Item>
-            <Form.Item label={fieldLabels.birthday} {...formItemLayout}>
-              {getFieldDecorator('birthday')(
-                <Input placeholder="请输入" />
-              )}
-            </Form.Item>
-            <Form.Item label={fieldLabels.mobile} {...formItemLayout}>
-              {getFieldDecorator('mobile', {
-                rules: [{ pattern: /^[1][34578][0-9]{9}$/g, message: '请输入11位有效手机号!' }],
-              })(
-                <Input placeholder="请输入" />
-              )}
-            </Form.Item>
-            <Form.Item label={fieldLabels.mail} {...formItemLayout}>
-              {getFieldDecorator('mail')(
-                <Input placeholder="请输入" />
-              )}
-            </Form.Item>
-            <Form.Item label={fieldLabels.qq} {...formItemLayout}>
-              {getFieldDecorator('qq')(
-                <Input placeholder="请输入" />
-              )}
-            </Form.Item>
-            <Form.Item label={fieldLabels.weChat} {...formItemLayout}>
-              {getFieldDecorator('weChat')(
-                <Input placeholder="请输入" />
-              )}
-            </Form.Item>
-            <Form.Item label={fieldLabels.gender} {...formItemLayout}>
-              {getFieldDecorator('gender', {
-                initialValue: 'MALE',
-              })(
-                <Radio.Group className={styles.radio}>
-                  {
-                    genders.map(item =>
-                      (
-                        <Radio.Button
-                          key={item.value}
-                          value={item.value}
-                        >{item.title}
-                        </Radio.Button>
-                      )
-                    )
-                  }
-                </Radio.Group>
-              )}
-            </Form.Item>
-            <Form.Item label={fieldLabels.status} {...formItemLayout}>
-              {getFieldDecorator('status', {
-                valuePropName: 'checked',
-                initialValue: true,
-              })(
-                <Switch
-                  checkedChildren="启用"
-                  unCheckedChildren="禁用"
-                />
-              )}
-            </Form.Item>
-          </Form>
-          {!merchantSelectorDestroy && getMerchantModal()}
-        </Card>
-        <FooterToolbar style={{ width: '100%' }}>
-          {getErrorInfo()}
-          <Button
-            onClick={this.handlePageBack}
-            style={{ marginRight: 10 }}
-          >取消
-          </Button>
-          <Button
-            type="primary"
-            loading={submitting}
-            onClick={this.handlePageSubmit}
-          >提交
-          </Button>
-        </FooterToolbar>
-      </div>
-    );
-  }
-}

+ 0 - 72
src/routes/System/CmsUser/CmsUserCreate.less

@@ -1,72 +0,0 @@
-@import "../../../../node_modules/antd/lib/style/themes/default.less";
-
-.cardItem {
-  :global {
-    .ant-card-head {
-      padding-left: 16px;
-    }
-  }
-}
-.infoTable {
-  :global {
-    .ant-table-body > table {
-      padding: 0 !important;
-    }
-    .ant-table-tbody > tr > td {
-      &:first-child {
-        font-weight: 500;
-      }
-    }
-  }
-}
-.radio {
-  :global {
-    .ant-radio-button-wrapper-checked {
-      background-color: @primary-5;
-      color: #fff;
-    }
-  }
-}
-.errorIcon {
-  cursor: pointer;
-  color: @error-color;
-  margin-right: 24px;
-  i {
-    margin-right: 4px;
-  }
-}
-.errorPopover {
-  :global {
-    .ant-popover-inner-content {
-      padding: 0;
-      max-height: 290px;
-      overflow: auto;
-      min-width: 256px;
-    }
-  }
-}
-.errorListItem {
-  list-style: none;
-  border-bottom: 1px solid @border-color-split;
-  padding: 8px 16px;
-  cursor: pointer;
-  transition: all .3s;
-  &:hover {
-    background: @primary-1;
-  }
-  &:last-child {
-    border: 0;
-  }
-  .errorIcon {
-    color: @error-color;
-    float: left;
-    margin-top: 4px;
-    margin-right: 12px;
-    padding-bottom: 22px;
-  }
-  .errorField {
-    font-size: 12px;
-    color: @text-color-secondary;
-    margin-top: 2px;
-  }
-}

+ 0 - 339
src/routes/System/CmsUser/CmsUserEdit.js

@@ -1,339 +0,0 @@
-import React, { PureComponent } from 'react';
-import { Card, Form, Table, Input, Button, Radio, Icon, Switch } from 'antd';
-import { connect } from 'dva';
-import { routerRedux } from 'dva/router';
-import FooterToolbar from '../../../components/FooterToolbar';
-import {renderAvatar, renderGender, renderStatus, boolToStatus, statusToBool} from '../../../utils/utils';
-import styles from './CmsUserCreate.less';
-
-const genders = [{
-  title: '男',
-  value: 'MALE',
-}, {
-  title: '女',
-  value: 'FEMALE',
-}];
-const fieldLabels = {
-  nickName: '用户昵称',
-  gender: '用户性别',
-  birthday: '出生日期',
-  mobile: '电话',
-  mail: '邮箱',
-  qq: 'QQ号码',
-  weChat: '微信号码',
-  password: '用户密码',
-  confirm: '确认密码',
-  status: '账号状态',
-};
-const formItemLayout = {
-  labelCol: {
-    xs: { span: 24 },
-    sm: { span: 6 },
-  },
-  wrapperCol: {
-    xs: { span: 24 },
-    sm: { span: 14 },
-    md: { span: 12 },
-  },
-};
-
-function cmsUserDataFormatter(item) {
-  const {
-    qq,
-    name,
-    mail,
-    weChat,
-    mobile,
-    status,
-    gender,
-    avatar,
-    birthday,
-    nickName,
-    merchantName,
-  } = item;
-  return [{
-    key: 1,
-    field: '用户头像',
-    text: renderAvatar(avatar, nickName),
-  }, {
-    key: 2,
-    field: '用户名称',
-    text: name,
-  }, {
-    key: 3,
-    field: '用户昵称',
-    text: nickName,
-  }, {
-    key: 4,
-    field: '出生日期',
-    text: birthday,
-  }, {
-    key: 5,
-    field: '用户性别',
-    text: renderGender(gender),
-  }, {
-    key: 6,
-    field: '所属商户',
-    text: merchantName,
-  }, {
-    key: 7,
-    field: '联系电话',
-    text: mobile,
-  }, {
-    key: 8,
-    field: 'QQ号码',
-    text: qq,
-  }, {
-    key: 9,
-    field: '微信',
-    text: weChat,
-  }, {
-    key: 10,
-    field: '邮箱',
-    text: mail,
-  }, {
-    key: 11,
-    field: '账号状态',
-    text: renderStatus(status),
-  }];
-}
-
-@Form.create()
-@connect(({ loading, cmsUser }) => ({
-  cmsUser,
-  submitting: loading.models.cmsUser,
-}))
-export default class CmsUserEditPage extends PureComponent {
-  state = {
-    currentItem: {},
-    passwordEdit: false,
-  };
-  componentWillMount() {
-    this.setState({
-      currentItem: this.props.location.state.currentItem,
-    });
-  }
-  checkPassword = (rule, value, callback) => {
-    if (value && value !== this.props.form.getFieldValue('password')) {
-      callback('两次输入的密码不一致');
-    } else if (!value) {
-      callback('请再次输入密码进行确认');
-    } else {
-      callback();
-    }
-  };
-  handlePasswordEdit = () => {
-    this.setState({
-      passwordEdit: true,
-    });
-  };
-  handlePasswordSave = () => {
-    this.props.form.validateFieldsAndScroll((error) => {
-      if (!error) {
-        this.setState({
-          passwordEdit: false,
-        });
-      }
-    });
-  };
-  handlePageSubmit = () => {
-    this.props.form.validateFieldsAndScroll((error, values) => {
-      const { currentItem, passwordEdit } = this.state;
-      const { id } = currentItem;
-      const { status, ...restProps } = values;
-      restProps.status = boolToStatus(status);
-      restProps.id = id;
-      if (!error && !passwordEdit) {
-        this.props.dispatch({
-          type: 'cmsUser/updateCmsUserItem',
-          payload: restProps,
-          states: this.props.location.state,
-        });
-      }
-    });
-  };
-  handlePageBack = () => {
-    this.props.dispatch(routerRedux.push({
-      pathname: '/system/cms-user/list',
-      state: this.props.location.state,
-    }));
-  };
-
-  render() {
-    const { form, submitting } = this.props;
-    const { currentItem, passwordEdit } = this.state;
-    const { getFieldDecorator } = form;
-    const {
-      qq,
-      mail,
-      gender,
-      weChat,
-      status,
-      birthday,
-      nickName,
-      mobile,
-    } = currentItem;
-
-    const campusColumns = [{
-      title: '字段名',
-      key: 1,
-      dataIndex: 'field',
-      width: '15%',
-    }, {
-      title: '字段值',
-      key: 2,
-      dataIndex: 'text',
-      width: '85%',
-    }];
-
-    return (
-      <div>
-        <Card title="用户详情" style={{ marginBottom: 16 }}>
-          <Form>
-            <Form.Item wrapperCol={{ span: 13, offset: 5 }}>
-              <Table
-                bordered
-                size="small"
-                showHeader={false}
-                pagination={false}
-                className={styles.infoTable}
-                columns={campusColumns}
-                dataSource={cmsUserDataFormatter(currentItem)}
-              />
-            </Form.Item>
-          </Form>
-        </Card>
-        <Card title="修改信息" style={{ marginBottom: 70 }}>
-          <Form hideRequiredMark>
-            <Form.Item label={fieldLabels.nickName} {...formItemLayout}>
-              {getFieldDecorator('nickName', {
-                initialValue: nickName,
-              })(
-                <Input placeholder="请输入" />
-              )}
-            </Form.Item>
-            <Form.Item label={fieldLabels.password} {...formItemLayout}>
-              {getFieldDecorator('password', {
-                rules: [
-                  {
-                    pattern: /^[a-zA-Z0-9|-]+$/ig, message: '密码格式错误!',
-                  }],
-              })(
-                <Input
-                  type="password"
-                  placeholder="请输入"
-                  disabled={!passwordEdit}
-                  addonBefore={<Icon type="lock" />}
-                  addonAfter={
-                    passwordEdit ? (
-                      <Icon type="save" onClick={this.handlePasswordSave} />
-                    ) : (
-                      <Icon type="edit" onClick={this.handlePasswordEdit} />
-                    )
-                  }
-                />
-              )}
-            </Form.Item>
-            {passwordEdit && (
-              <Form.Item label={fieldLabels.confirm} {...formItemLayout}>
-                {getFieldDecorator('confirm', {
-                  rules: [
-                    {
-                      validator: this.checkPassword,
-                    }],
-                })(
-                  <Input
-                    type="password"
-                    placeholder="请输入"
-                    addonBefore={<Icon type="lock" />}
-                  />
-                )}
-              </Form.Item>
-            )}
-            <Form.Item label={fieldLabels.birthday} {...formItemLayout}>
-              {getFieldDecorator('birthday', {
-                initialValue: birthday,
-              })(
-                <Input placeholder="请输入" />
-              )}
-            </Form.Item>
-            <Form.Item label={fieldLabels.mobile} {...formItemLayout}>
-              {getFieldDecorator('mobile', {
-                rules: [{
-                  pattern: /^[1][34578][0-9]{9}$/g, message: '请输入11位有效手机号!',
-                }],
-                initialValue: mobile,
-              })(
-                <Input placeholder="请输入" />
-              )}
-            </Form.Item>
-            <Form.Item label={fieldLabels.mail} {...formItemLayout}>
-              {getFieldDecorator('mail', {
-                initialValue: mail,
-              })(
-                <Input placeholder="请输入" />
-              )}
-            </Form.Item>
-            <Form.Item label={fieldLabels.qq} {...formItemLayout}>
-              {getFieldDecorator('qq', {
-                initialValue: qq,
-              })(
-                <Input placeholder="请输入" />
-              )}
-            </Form.Item>
-            <Form.Item label={fieldLabels.weChat} {...formItemLayout}>
-              {getFieldDecorator('weChat', {
-                initialValue: weChat,
-              })(
-                <Input placeholder="请输入" />
-              )}
-            </Form.Item>
-            <Form.Item label={fieldLabels.gender} {...formItemLayout}>
-              {getFieldDecorator('gender', {
-                initialValue: gender,
-              })(
-                <Radio.Group className={styles.radio}>
-                  {
-                    genders.map(item =>
-                      (
-                        <Radio.Button
-                          key={item.value}
-                          value={item.value}
-                        >{item.title}
-                        </Radio.Button>
-                      )
-                    )
-                  }
-                </Radio.Group>
-              )}
-            </Form.Item>
-            <Form.Item label={fieldLabels.status} {...formItemLayout}>
-              {getFieldDecorator('status', {
-                valuePropName: 'checked',
-                initialValue: statusToBool(status),
-              })(
-                <Switch
-                  checkedChildren="启用"
-                  unCheckedChildren="禁用"
-                />
-              )}
-            </Form.Item>
-          </Form>
-        </Card>
-        <FooterToolbar style={{ width: '100%' }}>
-          <Button
-            onClick={this.handlePageBack}
-            style={{ marginRight: 10 }}
-          >取消
-          </Button>
-          <Button
-            type="primary"
-            loading={submitting}
-            onClick={this.handlePageSubmit}
-          >提交
-          </Button>
-        </FooterToolbar>
-      </div>
-    );
-  }
-}

+ 0 - 233
src/routes/System/CmsUser/CmsUserList.js

@@ -1,233 +0,0 @@
-import React, { Component } from 'react';
-import moment from 'moment';
-import { connect } from 'dva';
-import { routerRedux } from 'dva/router';
-import { Card, Modal, Button, message } from 'antd';
-import Ellipsis from '../../../components/Ellipsis';
-import { StandardTableList } from '../../../components/AXList';
-import {
-  addRowKey,
-  renderStatus,
-  renderAvatar,
-  renderGender,
-  renderCategory,
-} from '../../../utils/utils';
-import { Hotax } from '../../../utils/config';
-
-const Message = message;
-
-@connect(({ loading, cmsUser }) => ({
-  cmsUser,
-  loading: loading.models.cmsUser,
-}))
-export default class CmsUserListPage extends Component {
-  constructor(props) {
-    super(props);
-    const { state } = props.location;
-    this.state = {
-      UIParams: (state || {}).UIParams, // 组件的状态参数
-      Queryers: (state || {}).Queryers, // 查询的条件参数
-    };
-  }
-  componentDidMount() {
-    this.props.dispatch({
-      type: 'cmsUser/fetchCmsUserList',
-      payload: { ...this.state.Queryers },
-    });
-  }
-  handleCreateOperation = () => {
-    this.props.dispatch(routerRedux.push({
-      pathname: '/system/cms-user/create',
-      state: this.state,
-    }));
-  }
-  handleDisableOperation = (item) => {
-    Modal.confirm({
-      okText: '禁用',
-      cancelText: '取消',
-      title: '你确定要禁用该系统账户吗?',
-      content: '禁用该账户后将不能使用该账户登录管理系统',
-      onOk: () => {
-        this.props.dispatch({
-          type: 'cmsUser/deleteCmsUserItem',
-          payload: { id: item.id },
-          states: this.state,
-        });
-      },
-    });
-  }
-  handleRecoverOperation = (item) => {
-    Modal.confirm({
-      okText: '确定',
-      cancelText: '取消',
-      title: '你确定要解除对该系统账号的禁用状态吗?',
-      onOk: () => {
-        this.props.dispatch({
-          type: 'cmsUser/recoverCmsUserItem',
-          payload: {
-            id: item.id,
-            status: Hotax.STATUS_NORMAL,
-          },
-          states: this.state,
-        });
-      },
-    });
-  }
-  handleEditOperation = (item) => {
-    this.props.dispatch(routerRedux.push({
-      pathname: `/system/cms-user/edit/${item.id}`,
-      state: {
-        currentItem: item,
-        ...this.state,
-      },
-    }));
-  }
-  handleFilterOperation = (params, states) => {
-    this.props.dispatch({
-      type: 'cmsUser/fetchCmsUserList',
-      payload: params,
-    });
-    this.setState({
-      UIParams: states,
-      Queryers: params,
-    });
-  }
-  handleBatchOperation = () => {
-    Message.info('暂不支持批量操作!');
-  }
-
-  render() {
-    const { loading, cmsUser } = this.props;
-    const { list, totalSize, pageSize, pageNo } = cmsUser;
-
-    const renderOperation = (item) => {
-      return (
-        <div>
-          <Button
-            size="small"
-            className="editBtn"
-            onClick={() => this.handleEditOperation(item)}
-          >编辑
-          </Button>
-          {
-            item.status === Hotax.STATUS_NORMAL ? (
-              <Button
-                size="small"
-                className="delBtn"
-                onClick={() => this.handleDisableOperation(item)}
-              >禁用
-              </Button>
-            ) : (
-              <Button
-                size="small"
-                className="delBtn"
-                onClick={() => this.handleRecoverOperation(item)}
-              >解禁
-              </Button>
-            )
-          }
-        </div>
-      );
-    };
-
-    const batchActions = [{
-      key: 'delete',
-      name: '批量禁用',
-    }, {
-      key: 'recovery',
-      name: '批量解禁',
-    }];
-    const basicSearch = {
-      keys: [{
-        name: '用户名称',
-        field: 'name',
-      }],
-    };
-    const pagination = {
-      pageNo,
-      pageSize,
-      totalSize,
-    };
-    const columns = [{
-      title: '用户头像',
-      key: 1,
-      dataIndex: 'avatar',
-      render: (text, record) => {
-        if (record.nickName) {
-          return renderAvatar(text, record.nickName);
-        }
-        return renderAvatar(text, record.name);
-      },
-      width: '10%',
-    }, {
-      title: '用户名称',
-      key: 2,
-      dataIndex: 'name',
-      width: '10%',
-      render: text => (
-        <Ellipsis tooltip lines={1}>{text}</Ellipsis>
-      ),
-    }, {
-      title: '用户昵称',
-      key: 3,
-      dataIndex: 'nickName',
-      width: '10%',
-    }, {
-      title: '用户性别',
-      key: 4,
-      dataIndex: 'gender',
-      render: text => renderGender(text),
-      width: '10%',
-    }, {
-      title: '所属商户',
-      key: 5,
-      dataIndex: 'merchantName',
-      width: '11%',
-    }, {
-      title: '商户类型',
-      key: 6,
-      dataIndex: 'domain',
-      render: text => renderCategory(text),
-      width: '10%',
-    }, {
-      title: '账号状态',
-      key: 7,
-      dataIndex: 'status',
-      render: text => renderStatus(text, '已禁用'),
-      width: '11%',
-    }, {
-      title: '更新时间',
-      key: 8,
-      dataIndex: 'gmtModified',
-      render: text => moment(text).format('YYYY-MM-DD HH:mm:ss'),
-      width: '16%',
-    }, {
-      title: '操作',
-      key: 9,
-      dataIndex: 'operation',
-      render: (_, record) => renderOperation(record),
-      width: '12%',
-      align: 'right',
-    }];
-    return (
-      <Card>
-        <StandardTableList
-          columns={columns}
-          loading={loading}
-          dataSource={addRowKey(list)}
-          header={{
-            basicSearch,
-            onFilterClick: this.handleFilterOperation,
-            onCreateClick: this.handleCreateOperation,
-          }}
-          footer={{
-            pagination,
-            batchActions,
-            onBatchClick: this.handleBatchOperation,
-          }}
-          keepUIState={{ ...this.state.UIParams }}
-        />
-      </Card>
-    );
-  }
-}

+ 240 - 0
src/routes/Terminal/Auth/AuthList.js

@@ -0,0 +1,240 @@
+/* eslint-disable prefer-destructuring */
+import React, { Component } from 'react';
+import moment from 'moment';
+import { connect } from 'dva';
+import { Modal, Form, Card, Badge, Button, DatePicker, message } from 'antd';
+import { StandardTableList } from '../../../components/AXList';
+import Ellipsis from '../../../components/Ellipsis';
+import { addRowKey } from '../../../utils/utils';
+import styles from './AuthList.less';
+
+const Message = message;
+
+const formItemLayout = {
+  labelCol: {
+    md: { span: 6 },
+  },
+  wrapperCol: {
+    md: { span: 18 },
+  },
+};
+const timestamp2Str = ts => moment(ts).format('YYYY-MM-DD HH:mm:ss');
+
+@Form.create()
+@connect(({ terminal, loading }) => ({
+  terminal,
+  loading: loading.models.terminal,
+}))
+export default class TerminalAuthListPage extends Component {
+  constructor(props) {
+    super(props);
+    const { state } = props.location;
+    this.state = {
+      UIParams: (state || {}).UIParams, // 组件的状态参数
+      Queryers: (state || {}).Queryers, // 查询的条件参数
+      authEditModalDestroy: true,
+      targetRow: {},
+    };
+  }
+  componentWillMount() {
+    this.props.dispatch({
+      type: 'terminal/cleanState',
+    });
+  }
+  componentDidMount() {
+    this.props.dispatch({
+      type: 'terminal/fetchTerminalAuthList',
+      payload: { ...this.state.Queryers },
+    });
+  }
+  handleFilterOperation = (params, states) => {
+    this.props.dispatch({
+      type: 'terminal/fetchTerminalAuthList',
+      payload: params,
+    });
+    this.setState({
+      UIParams: states,
+      Queryers: params,
+    });
+  };
+  handleBatchOperation = () => {
+    Message.info('暂不支持批量操作!');
+  };
+  handleEditAuthModalShow = (record) => {
+    this.setState({
+      authEditModalDestroy: false,
+      targetRow: record,
+    });
+  };
+  handleEditAuthModalHide = () => {
+    this.setState({
+      authEditModalDestroy: true,
+    });
+  };
+  handleEditAuthModalFinish = () => {
+    this.props.form.validateFieldsAndScroll((err, values) => {
+      if (!err) {
+        const { startTime, endTime } = values;
+        const stStr = moment(startTime).format('YYYY-MM-DD');
+        const etStr = moment(endTime).format('YYYY-MM-DD');
+        const { targetRow } = this.state;
+        this.props.dispatch({
+          type: 'terminal/updateTerminalAuth',
+          payload: { ...targetRow, startTime: stStr, endTime: etStr },
+          states: { ...this.state.Queryers },
+        });
+      }
+    });
+    this.setState({
+      authEditModalDestroy: true,
+    });
+  };
+  render() {
+    const { targetRow, authEditModalDestroy } = this.state;
+    const { terminal, loading, form } = this.props;
+    const { getFieldDecorator } = form;
+    const { pageNo, pageSize, totalSize, list } = terminal;
+    const basicSearch = {
+      keys: [{
+        name: '终端编号',
+        field: 'code',
+      }],
+    };
+    const batchActions = [{
+      key: 'delete',
+      name: '批量删除',
+    }];
+    const pagination = {
+      pageNo,
+      pageSize,
+      totalSize,
+    };
+    const columns = [{
+      title: '终端编号',
+      key: 1,
+      dataIndex: 'ucode',
+      width: '15%',
+      render: text => (
+        <Ellipsis tooltip lines={1}>{text}</Ellipsis>
+      ),
+    }, {
+      title: '产品编号',
+      key: 2,
+      dataIndex: 'pcode',
+      width: '12%',
+      render: text => (
+        <Ellipsis tooltip lines={1}>{text}</Ellipsis>
+      ),
+    }, {
+      title: '产品名称',
+      key: 3,
+      dataIndex: 'pname',
+      width: '13%',
+      render: text => (
+        <Ellipsis tooltip lines={1}>{text}</Ellipsis>
+      ),
+    }, {
+      title: '权限有效期',
+      key: 4,
+      render: (_, record) => {
+        const { startTime, endTime } = record;
+        return (
+          <div className={styles.authDesc}>
+            <p><span>起始时间:&nbsp;&nbsp;</span>{`${timestamp2Str(startTime)}`}</p>
+            <p><span>到期时间:&nbsp;&nbsp;</span>{`${timestamp2Str(endTime)}`}</p>
+          </div>
+        );
+      },
+      width: '25%',
+      align: 'center',
+    }, {
+      title: '权限有效时长',
+      key: 5,
+      dataIndex: 'endTime',
+      render: (text) => {
+        const day = moment(text).diff(moment(), 'days');
+        if (day < 0) {
+          return <Badge status="error" text="已到期" />;
+        }
+        return <span><span style={{ color: '#52c41a', fontWeight: 'bold' }}>{day}</span>天到期</span>;
+      },
+      width: '12%',
+      align: 'center',
+    }, {
+      title: '修改时间',
+      key: 6,
+      dataIndex: 'updateTime',
+      render: text => timestamp2Str(text),
+      width: '15%',
+      align: 'center',
+    }, {
+      title: '操作',
+      key: 7,
+      render: (_, record) => (
+        <Button onClick={() => this.handleEditAuthModalShow(record)} size="small" className="editBtn">修改权限</Button>
+      ),
+      width: '8%',
+      align: 'right',
+    }];
+    return (
+      <Card>
+        <StandardTableList
+          columns={columns}
+          loading={loading}
+          dataSource={addRowKey(list)}
+          header={{
+            basicSearch,
+            onFilterClick: this.handleFilterOperation,
+          }}
+          footer={{
+            pagination,
+            batchActions,
+            onBatchClick: this.handleBatchOperation,
+          }}
+          keepUIState={{ ...this.state.UIParams }}
+          showStatusSelect={false}
+        />
+        {!authEditModalDestroy && (
+          <Modal
+            visible
+            title="修改权限时间"
+            cancelText="取消"
+            okText="确定"
+            maskClosable={false}
+            onCancel={this.handleEditAuthModalHide}
+            onOk={this.handleEditAuthModalFinish}
+          >
+            <Form>
+              <Form.Item label="起始日期" {...formItemLayout}>
+                {getFieldDecorator('startTime', {
+                  rules: [{ required: true, message: '请选择起始日期' }],
+                  initialValue: targetRow && targetRow.startTime && moment(targetRow.startTime),
+                })(
+                  <DatePicker
+                    showTime
+                    placeholder="选择日期时间"
+                    format="YYYY-MM-DD HH:mm:ss"
+                    style={{ width: 280 }}
+                  />
+                )}
+              </Form.Item>
+              <Form.Item label="截止日期" {...formItemLayout}>
+                {getFieldDecorator('endTime', {
+                  rules: [{ required: true, message: '请选择结束日期' }],
+                  initialValue: targetRow && targetRow.endTime && moment(targetRow.endTime),
+                })(
+                  <DatePicker
+                    showTime
+                    placeholder="选择日期时间"
+                    format="YYYY-MM-DD HH:mm:ss"
+                    style={{ width: 280 }}
+                  />
+                )}
+              </Form.Item>
+            </Form>
+          </Modal>
+        )}
+      </Card>
+    );
+  }
+}

+ 10 - 0
src/routes/Terminal/Auth/AuthList.less

@@ -0,0 +1,10 @@
+@import "../../../../node_modules/antd/lib/style/themes/default.less";
+
+.authDesc {
+  p {
+    margin-bottom: unset !important;
+  }
+  & > p > span {
+    font-weight: bold;
+  }
+}

+ 2 - 3
src/routes/System/CmsUser/index.js

@@ -5,11 +5,10 @@ import PageHeaderLayout from '../../../layouts/PageHeaderLayout';
 import { getRoutes } from '../../../utils/utils';
 
 @connect()
-export default class WhiteList extends Component {
+export default class Index extends Component {
   render() {
     const { match, routerData } = this.props;
     const routes = getRoutes(match.path, routerData);
-
     return (
       <PageHeaderLayout>
         <Switch>
@@ -25,7 +24,7 @@ export default class WhiteList extends Component {
               )
             )
           }
-          <Redirect exact from="/system/cms-user" to="/system/cms-user/list" />
+          <Redirect exact from="/terminal/auth" to="/terminal/auth/list" />
         </Switch>
       </PageHeaderLayout>
     );

+ 1 - 2
src/routes/Terminal/User/TerminalCreate.js

@@ -120,7 +120,7 @@ export default class TerminalCreatePage extends PureComponent {
     const { form, cLoading, submitting, campus, terminal } = this.props;
     const { getFieldDecorator } = form;
     const { currentItem } = terminal;
-    const { campusCode, campusName, merchantName } = currentItem;
+    const { campusCode, campusName } = currentItem;
 
     const getCampusModal = () => {
       return (
@@ -162,7 +162,6 @@ export default class TerminalCreatePage extends PureComponent {
                 dataSource={[
                   `校区编号: ${campusCode || ''}`,
                   `校区名称: ${campusName || ''}`,
-                  `所属厂商: ${merchantName || ''}`,
                 ]}
                 renderItem={item => <List.Item>{item}</List.Item>}
               />

+ 20 - 31
src/routes/Terminal/User/TerminalEdit.js

@@ -1,5 +1,5 @@
 import React, { PureComponent } from 'react';
-import { Card, Form, Table, Input, Button, Icon, Switch } from 'antd';
+import { Card, Form, Table, Input, Button, Icon, Switch, message } from 'antd';
 import { connect } from 'dva';
 import { routerRedux } from 'dva/router';
 import FooterToolbar from '../../../components/FooterToolbar';
@@ -33,9 +33,6 @@ function campusDataFormatter(item) {
     status,
     campusName,
     contactName,
-    merchantName,
-    merchantContactName,
-    merchantContactMobile,
   } = item;
   return [{
     key: 1,
@@ -61,18 +58,6 @@ function campusDataFormatter(item) {
     key: 6,
     field: '校区联系电话',
     text: mobile,
-  }, {
-    key: 7,
-    field: '所属渠道',
-    text: merchantName,
-  }, {
-    key: 8,
-    field: '渠道联系人',
-    text: merchantContactName,
-  }, {
-    key: 9,
-    field: '渠道联系电话',
-    text: merchantContactMobile,
   }];
 }
 
@@ -82,14 +67,13 @@ function campusDataFormatter(item) {
   submitting: loading.models.terminal,
 }))
 export default class TerminalEditPage extends PureComponent {
-  state = {
-    currentItem: {},
-    passwordEdit: false,
-  };
-  componentDidMount() {
-    this.setState({
-      currentItem: this.props.location.state.currentItem,
-    });
+  constructor(props) {
+    super(props);
+    const { location } = props;
+    const { state } = location;
+    this.state = {
+      currentItem: (state || {}).currentItem || {},
+    };
   }
   checkPassword = (rule, value, callback) => {
     if (value && value !== this.props.form.getFieldValue('password')) {
@@ -117,17 +101,22 @@ export default class TerminalEditPage extends PureComponent {
   handlePageSubmit = () => {
     this.props.form.validateFieldsAndScroll((error, values) => {
       const { currentItem, passwordEdit } = this.state;
+      if (error) {
+        return;
+      }
+      if (passwordEdit) {
+        message.error('请保存密码!');
+        return;
+      }
       const { id } = currentItem;
       const { status, ...restProps } = values;
       restProps.id = id;
       restProps.status = boolToStatus(status);
-      if (!error && !passwordEdit) {
-        this.props.dispatch({
-          type: 'terminal/updateTerminalItem',
-          payload: restProps,
-          states: this.props.location.state,
-        });
-      }
+      this.props.dispatch({
+        type: 'terminal/updateTerminalItem',
+        payload: restProps,
+        states: this.props.location.state,
+      });
     });
   };
   handlePageBack = () => {

+ 60 - 43
src/routes/Terminal/User/TerminalList.js

@@ -1,11 +1,12 @@
+/* eslint-disable prefer-destructuring */
 import React, { Component } from 'react';
 import moment from 'moment';
 import { connect } from 'dva';
 import { routerRedux } from 'dva/router';
 import { Card, Modal, Form, Button, message } from 'antd';
-import { StandardTableList } from '../../../components/AXList/index';
-import AXRemoteSelect from '../../../components/AXRemoteSelect/index';
-import Ellipsis from '../../../components/Ellipsis/index';
+import { StandardTableList } from '../../../components/AXList';
+import AXRemoteSelect from '../../../components/AXRemoteSelect';
+import Ellipsis from '../../../components/Ellipsis';
 import { addRowKey, renderStatus, renderBindStatus } from '../../../utils/utils';
 import { Hotax } from '../../../utils/config';
 
@@ -26,17 +27,15 @@ function arrayDataFormatter(data) {
   return data.map((item) => {
     return {
       text: item.name,
-      value: item.id,
+      value: `${item.name}||${item.id}`,
     };
   });
 }
 
 @Form.create()
-@connect(({ loading, campus, merchant, terminal }) => ({
+@connect(({ loading, campus, terminal }) => ({
   campus,
-  merchant,
   terminal,
-  fetching1: loading.models.merchant,
   fetching2: loading.models.campus,
   loading: loading.models.terminal,
 }))
@@ -47,13 +46,27 @@ export default class TerminalListPage extends Component {
     this.state = {
       UIParams: (state || {}).UIParams, // 组件的状态参数
       Queryers: (state || {}).Queryers, // 查询的条件参数
+      campuses: (state || {}).campuses || [], // 记录筛选的校区
       filterModalDestroy: true,
     };
   }
+  componentWillMount() {
+    this.props.dispatch({
+      type: 'terminal/cleanState',
+    });
+  }
   componentDidMount() {
+    const { campuses } = this.state;
+    let campusId;
+    if (campuses && campuses.length) {
+      campusId = campuses[0].split('||')[1];
+    }
     this.props.dispatch({
       type: 'terminal/fetchTerminalList',
-      payload: { ...this.state.Queryers },
+      payload: {
+        campusId,
+        ...this.state.Queryers,
+      },
     });
   }
   handleCreateOperation = () => {
@@ -61,7 +74,7 @@ export default class TerminalListPage extends Component {
       pathname: '/terminal/user/create',
       state: this.state,
     }));
-  }
+  };
   handleDeviceUnboundOperation = (item) => {
     Modal.confirm({
       okText: '确定',
@@ -76,7 +89,7 @@ export default class TerminalListPage extends Component {
         });
       },
     });
-  }
+  };
   handleDisableOperation = (item) => {
     Modal.confirm({
       okText: '禁用',
@@ -91,7 +104,7 @@ export default class TerminalListPage extends Component {
         });
       },
     });
-  }
+  };
   handleRecoverOperation = (item) => {
     Modal.confirm({
       okText: '确定',
@@ -108,7 +121,7 @@ export default class TerminalListPage extends Component {
         });
       },
     });
-  }
+  };
   handleEditOperation = (item) => {
     this.props.dispatch(routerRedux.push({
       pathname: `/terminal/user/edit/${item.id}`,
@@ -117,48 +130,51 @@ export default class TerminalListPage extends Component {
         ...this.state,
       },
     }));
-  }
+  };
   handleFilterOperation = (params, states) => {
-    this.props.dispatch({
-      type: 'terminal/fetchTerminalList',
-      payload: params,
-    });
     this.setState({
       UIParams: states,
       Queryers: params,
     });
-  }
+    const { campuses } = this.state;
+    let campusId;
+    if (campuses && campuses.length) {
+      campusId = campuses[0].split('||')[1];
+    }
+    this.props.dispatch({
+      type: 'terminal/fetchTerminalList',
+      payload: {
+        campusId,
+        ...params,
+      },
+    });
+  };
   handleModalFilterOperation = () => {
     const { getFieldsValue } = this.props.form;
-    const { merchantIds, campusIds } = getFieldsValue();
+    const { campuses } = getFieldsValue();
+    let campusId;
+    if (campuses && campuses.length) {
+      campusId = campuses[0].split('||')[1];
+    }
     this.props.dispatch({
       type: 'terminal/fetchTerminalList',
       payload: {
         ...this.state.Queryers,
-        merchantId: merchantIds[0],
-        campusId: campusIds[0],
+        campusId,
       },
     });
+    this.setState({ campuses });
     this.handleFilterModalDestroy();
-  }
+  };
   handleBatchOperation = () => {
     Message.info('暂不支持批量操作!');
-  }
+  };
   handleFilterModalShow = () => {
     this.setState({ filterModalDestroy: false });
-  }
+  };
   handleFilterModalDestroy = () => {
     this.setState({ filterModalDestroy: true });
-  }
-  handleMerchantRemoteSelectSearch = (value) => {
-    this.props.dispatch({
-      type: 'merchant/fetchMerchantList',
-      payload: {
-        pageSize: 50,
-        name: value,
-      },
-    });
-  }
+  };
   handleCampusRemoteSelectSearch = (value) => {
     this.props.dispatch({
       type: 'campus/fetchCampusList',
@@ -167,9 +183,10 @@ export default class TerminalListPage extends Component {
         name: value,
       },
     });
-  }
+  };
 
   render() {
+    const { campuses } = this.state;
     const { loading, fetching2, form, campus, terminal } = this.props;
     const { list, totalSize, pageSize, pageNo } = terminal;
     const { getFieldDecorator } = form;
@@ -242,12 +259,12 @@ export default class TerminalListPage extends Component {
       title: '终端编号',
       key: 1,
       dataIndex: 'code',
-      width: '13%',
+      width: '15%',
     }, {
       title: '终端名称',
       key: 2,
       dataIndex: 'name',
-      width: '13%',
+      width: '15%',
     }, {
       title: '所属校区',
       key: 3,
@@ -259,25 +276,25 @@ export default class TerminalListPage extends Component {
       key: 4,
       dataIndex: 'status',
       render: text => renderStatus(text, '已禁用'),
-      width: '7%',
+      width: '8%',
     }, {
       title: '绑定状态',
       key: 5,
       dataIndex: 'deviceStatus',
       render: text => renderBindStatus(text),
-      width: '7%',
+      width: '8%',
     }, {
       title: '更新时间',
       key: 6,
       dataIndex: 'gmtModified',
       render: text => moment(text).format('YYYY-MM-DD HH:mm:ss'),
-      width: '20%',
+      width: '16%',
     }, {
       title: '操作',
       key: 7,
       dataIndex: 'operation',
       render: (_, record) => renderOperation(record),
-      width: '15%',
+      width: '18%',
       align: 'right',
     }];
     return (
@@ -312,8 +329,8 @@ export default class TerminalListPage extends Component {
           >
             <Form>
               <Form.Item label="所属校区" {...formItemLayout}>
-                {getFieldDecorator('campusIds', {
-                    initialValue: [],
+                {getFieldDecorator('campuses', {
+                    initialValue: campuses,
                   })(
                     <AXRemoteSelect
                       fetching={fetching2}

+ 17 - 16
src/routes/Trade/Order/OrderCreate.js

@@ -1,7 +1,7 @@
 /* eslint-disable no-trailing-spaces */
 import React, { Component } from 'react';
 import { connect } from 'dva';
-import { Tooltip, Card, Modal, Form, Table, List, Steps, Select, Button, Input, InputNumber, Icon } from 'antd';
+import { Tooltip, Card, Modal, Form, Table, List, Steps, Select, Button, Input, Icon } from 'antd';
 import Ellipsis from '../../../components/Ellipsis';
 import Selector from '../../../components/AXTableSelector/Selector';
 import FooterToolbar from '../../../components/FooterToolbar';
@@ -76,11 +76,11 @@ export default class OrderCreatePage extends Component {
       return;
     }
     const {
-      id, code, name, campusName, merchantId, merchantName, contactName, mobile, address,
+      id, code, name, campusName, merchantId, contactName, mobile, address,
     } = rows[0];
     this.setState({
       deliveryInfo: {
-        id, code, name, campusName, merchantId, merchantName, contactName, mobile, address,
+        id, code, name, campusName, merchantId, contactName, mobile, address,
       },
     });
   };
@@ -239,7 +239,7 @@ export default class OrderCreatePage extends Component {
       terminalSelectorDestroy, goodsSelectorDestroy, packageFixModalDestroy,
     } = this.state;
     const {
-      code, name, campusName, merchantName, contactName, mobile, address,
+      code, name, campusName, contactName, mobile, address,
     } = deliveryInfo;
     const { terminal, tLoading, shelves, sLoading, form } = this.props;
     const { getFieldDecorator } = form;
@@ -285,7 +285,6 @@ export default class OrderCreatePage extends Component {
                   `终端编号: ${code || ''}`,
                   `终端名称: ${name || ''}`,
                   `所属校区: ${campusName || ''}`,
-                  `所属渠道: ${merchantName || ''}`,
                 ]}
                 renderItem={item => <List.Item>{item}</List.Item>}
               />
@@ -714,18 +713,20 @@ export default class OrderCreatePage extends Component {
                 </span>
                 &nbsp;件商品
               </span>
-              <span className={styles.adjustPrice}>
-                <span style={{ color: '#2f7d0d' }}>
-                  优惠:&nbsp;&nbsp;¥&nbsp;
+              {/*
+                <span className={styles.adjustPrice}>
+                  <span style={{ color: '#2f7d0d' }}>
+                    优惠:&nbsp;&nbsp;¥&nbsp;
+                  </span>
+                  <InputNumber
+                    min={0}
+                    max={totalPrice}
+                    size="small"
+                    value={adjustPrice}
+                    onChange={this.handleAdjustPriceChange}
+                  />
                 </span>
-                <InputNumber
-                  min={0}
-                  max={totalPrice}
-                  size="small"
-                  value={adjustPrice}
-                  onChange={this.handleAdjustPriceChange}
-                />
-              </span>
+              */}
               <span className={styles.totalPrice}>
                 总价:&nbsp;&nbsp;&nbsp;
                 <span style={{ color: '#f60', fontSize: 24 }}>

+ 24 - 22
src/routes/Trade/ShopCart/ShopCartDetail.js

@@ -2,7 +2,7 @@
 import React, { Component } from 'react';
 import pathToRegexp from 'path-to-regexp';
 import { connect } from 'dva';
-import { Card, Form, Table, List, Steps, Select, Button, Input, InputNumber, Icon } from 'antd';
+import { Card, Form, Table, List, Steps, Select, Button, Input, Icon } from 'antd';
 import FooterToolbar from '../../../components/FooterToolbar';
 import { checkProductType, toDecimal2 } from '../../../utils/utils';
 import { Hotax } from '../../../utils/config';
@@ -25,13 +25,16 @@ const formItemLayout = {
 }))
 @Form.create()
 export default class ShopCartDetail extends Component {
-  state = {
-    currentStep: 0,
-    selectedRowKeys: [],
-    adjustPrice: 0,
-    deliveryInfo: {},
-    width: '100%',
-  };
+  constructor(props) {
+    super(props);
+    this.state = {
+      currentStep: 0,
+      selectedRowKeys: [],
+      adjustPrice: 0,
+      width: '100%',
+      deliveryInfo: props.location.state || {},
+    };
+  }
   componentDidMount() {
     // 监听视图窗口变化
     window.addEventListener('resize', this.resizeFooterToolbar);
@@ -45,9 +48,6 @@ export default class ShopCartDetail extends Component {
         payload: { userId: match[1] },
       });
     }
-    // 获取收货信息
-    const { deliveryInfo } = this.props.location.state;
-    this.setState({ deliveryInfo });
   }
   componentWillUnmount() {
     window.removeEventListener('resize', this.resizeFooterToolbar);
@@ -489,18 +489,20 @@ export default class ShopCartDetail extends Component {
                 </span>
                 &nbsp;件商品
               </span>
-              <span className={styles.adjustPrice}>
-                <span style={{ color: '#2f7d0d' }}>
-                  优惠:&nbsp;&nbsp;¥&nbsp;
+              {/*
+                <span className={styles.adjustPrice}>
+                  <span style={{ color: '#2f7d0d' }}>
+                    优惠:&nbsp;&nbsp;¥&nbsp;
+                  </span>
+                  <InputNumber
+                    min={0}
+                    max={totalPrice}
+                    size="small"
+                    value={adjustPrice}
+                    onChange={this.handleAdjustPriceChange}
+                  />
                 </span>
-                <InputNumber
-                  min={0}
-                  max={totalPrice}
-                  size="small"
-                  value={adjustPrice}
-                  onChange={this.handleAdjustPriceChange}
-                />
-              </span>
+              */}
               <span className={styles.totalPrice}>
                 总价:&nbsp;&nbsp;&nbsp;
                 <span style={{ color: '#f60', fontSize: 24 }}>

+ 41 - 21
src/routes/Trade/ShopCart/ShopCartList.js

@@ -1,3 +1,4 @@
+/* eslint-disable prefer-destructuring */
 import React, { Component } from 'react';
 import moment from 'moment';
 import { connect } from 'dva';
@@ -25,18 +26,16 @@ function arrayDataFormatter(data) {
   return data.map((item) => {
     return {
       text: item.name,
-      value: item.id,
+      value: `${item.name}||${item.id}`,
     };
   });
 }
 
 @Form.create()
-@connect(({ loading, campus, merchant, terminal }) => ({
+@connect(({ loading, campus, terminal }) => ({
   campus,
-  merchant,
   terminal,
-  fetching1: loading.models.merchant,
-  fetching2: loading.models.campus,
+  fetching: loading.models.campus,
   loading: loading.models.terminal,
 }))
 export default class ShopCartListPage extends Component {
@@ -46,36 +45,57 @@ export default class ShopCartListPage extends Component {
     this.state = {
       UIParams: (state || {}).UIParams, // 组件的状态参数
       Queryers: (state || {}).Queryers, // 查询的条件参数
+      campuses: (state || {}).campuses || [], // 记录筛选的校区
       filterModalDestroy: true,
     };
   }
   componentDidMount() {
+    const { campuses } = this.state;
+    let campusId;
+    if (campuses && campuses.length) {
+      campusId = campuses[0].split('||')[1];
+    }
     this.props.dispatch({
       type: 'terminal/fetchTerminalList',
-      payload: { ...this.state.Queryers },
+      payload: {
+        campusId,
+        ...this.state.Queryers,
+      },
     });
   }
   handleFilterOperation = (params, states) => {
-    this.props.dispatch({
-      type: 'terminal/fetchTerminalList',
-      payload: params,
-    });
     this.setState({
       UIParams: states,
       Queryers: params,
     });
+    const { campuses } = this.state;
+    let campusId;
+    if (campuses && campuses.length) {
+      campusId = campuses[0].split('||')[1];
+    }
+    this.props.dispatch({
+      type: 'terminal/fetchTerminalList',
+      payload: {
+        campusId,
+        ...params,
+      },
+    });
   };
   handleModalFilterOperation = () => {
     const { getFieldsValue } = this.props.form;
-    const { merchantIds, campusIds } = getFieldsValue();
+    const { campuses } = getFieldsValue();
+    let campusId;
+    if (campuses && campuses.length) {
+      campusId = campuses[0].split('||')[1];
+    }
     this.props.dispatch({
       type: 'terminal/fetchTerminalList',
       payload: {
         ...this.state.Queryers,
-        merchantId: merchantIds[0],
-        campusId: campusIds[0],
+        campusId,
       },
     });
+    this.setState({ campuses });
     this.handleFilterModalDestroy();
   };
   handleBatchOperation = () => {
@@ -116,7 +136,8 @@ export default class ShopCartListPage extends Component {
   };
 
   render() {
-    const { loading, fetching2, form, campus, terminal } = this.props;
+    const { campuses } = this.state;
+    const { loading, fetching, form, campus, terminal } = this.props;
     const { list, totalSize, pageSize, pageNo } = terminal;
     const { getFieldDecorator } = form;
 
@@ -140,7 +161,6 @@ export default class ShopCartListPage extends Component {
     const batchActions = [{
       key: 'delete',
       name: '批量清空',
-    }, {
     }];
     const basicSearch = {
       keys: [{
@@ -174,19 +194,19 @@ export default class ShopCartListPage extends Component {
       width: '25%',
     }, {
       title: '账号状态',
-      key: 5,
+      key: 4,
       dataIndex: 'status',
       render: text => renderStatus(text, '已禁用'),
       width: '10%',
     }, {
       title: '更新时间',
-      key: 6,
+      key: 5,
       dataIndex: 'gmtModified',
       render: text => moment(text).format('YYYY-MM-DD HH:mm:ss'),
       width: '20%',
     }, {
       title: '操作',
-      key: 7,
+      key: 6,
       dataIndex: 'operation',
       render: (_, record) => renderOperation(record),
       width: '10%',
@@ -224,11 +244,11 @@ export default class ShopCartListPage extends Component {
           >
             <Form>
               <Form.Item label="所属校区" {...formItemLayout}>
-                {getFieldDecorator('campusIds', {
-                  initialValue: [],
+                {getFieldDecorator('campuses', {
+                  initialValue: campuses,
                 })(
                   <AXRemoteSelect
-                    fetching={fetching2}
+                    fetching={fetching}
                     dataSource={arrayDataFormatter(campus.list)}
                     onSearch={this.handleCampusRemoteSelectSearch}
                   />

+ 5 - 2
src/services/campus.js

@@ -1,7 +1,10 @@
 import { stringify } from 'qs';
+import { getLocalUser } from '../utils/authority';
 import request from '../utils/request';
 import { api, Hotax } from '../utils/config';
 
+const { merchantId } = getLocalUser();
+
 export async function queryCampusList(params) {
   const newParams = {
     pageSize: Hotax.PAGE_SIZE,
@@ -17,7 +20,7 @@ export async function queryCampusItem({ id }) {
 export async function createCampusItem(params) {
   const options = {
     method: 'POST',
-    body: params,
+    body: { merchantId, ...params },
   };
   return request(`${api.campusItem}`, options);
 }
@@ -25,7 +28,7 @@ export async function createCampusItem(params) {
 export async function updateCampusItem(params) {
   const options = {
     method: 'PUT',
-    body: params,
+    body: { merchantId, ...params },
   };
   return request(`${api.campusItem}`, options);
 }

+ 0 - 34
src/services/cmsUser.js

@@ -1,34 +0,0 @@
-import { stringify } from 'qs';
-import request from '../utils/request';
-import { api, Hotax } from '../utils/config';
-
-export async function queryCmsUserList(params) {
-  const newParams = {
-    pageSize: Hotax.PAGE_SIZE,
-    ...params,
-  };
-  return request(`${api.cmsUser}?${stringify(newParams)}`);
-}
-
-export async function createCmsUserItem(params) {
-  const options = {
-    method: 'POST',
-    body: params,
-  };
-  return request(`${api.cmsUserItem}`, options);
-}
-
-export async function updateCmsUserItem(params) {
-  const options = {
-    method: 'PUT',
-    body: params,
-  };
-  return request(`${api.cmsUserItem}`, options);
-}
-
-export async function deleteCmsUserItem({ id }) {
-  const options = {
-    method: 'DELETE',
-  };
-  return request(`${api.cmsUserItem}/${id}`, options);
-}

+ 0 - 104
src/services/configUser.js

@@ -1,104 +0,0 @@
-import { stringify } from 'qs';
-import request from '../utils/request';
-import { api } from '../utils/config';
-
-export async function queryUserTags(params) {
-  const id = params.configUserId;
-  return request(`${api.configUser}/${id}`);
-}
-
-export async function createConfigUserTagItem(params) {
-  const options = {
-    method: 'POST',
-    body: params,
-  };
-  return request(`${api.configUserTag}`, options);
-}
-
-
-export async function queryTagDetail(id) {
-  return request(`${api.configUserTag}/${id}`);
-}
-
-export async function updateConfigUserTagItem(params) {
-  const options = {
-    method: 'PUT',
-    body: params,
-  };
-  return request(`${api.configUserTag}`, options);
-}
-
-export async function deleteConfigUserTagItem(id) {
-  const options = {
-    method: 'DELETE',
-  };
-  return request(`${api.configUserTag}/${id}`, options);
-}
-
-export async function depositMerchantItem(params) {
-  const options = {
-    method: 'POST',
-    body: params,
-  };
-  return request(`${api.deposit}`, options);
-}
-
-export async function queryMerchantRecommend({ merchantId }) {
-  return request(`${api.recommend}/${merchantId}`);
-}
-
-export async function updateMerchantRecommend({ merchantId, idList }) {
-  const options = {
-    method: 'PUT',
-    body: idList,
-  };
-  return request(`${api.recommend}/${merchantId}`, options);
-}
-
-export async function queryMerchantPoster({ merchantId }) {
-  return request(`${api.poster}/${merchantId}`);
-}
-
-export async function queryMerchantPosterItem({ posterId }) {
-  return request(`${api.posterItem}/${posterId}`);
-}
-
-export async function createMerchantPosterItem(params) {
-  const options = {
-    method: 'POST',
-    body: params,
-  };
-  return request(`${api.posterItem}`, options);
-}
-
-export async function updateMerchantPosterItem(params) {
-  const options = {
-    method: 'PUT',
-    body: params,
-  };
-  return request(`${api.posterItem}`, options);
-}
-
-export async function deleteMerchantPosterItem({ posterId }) {
-  const options = {
-    method: 'DELETE',
-  };
-  return request(`${api.posterItem}/${posterId}`, options);
-}
-
-export async function queryConfigCourse(params) {
-  const id = params.courseId;
-  return request(`${api.configCourse}/${id}`);
-}
-
-export async function copyTag(params) {
-  return request(`${api.copyTag}?${stringify(params)}`);
-}
-
-export async function updateConfigCourse({ courseId, idList }) {
-  const options = {
-    method: 'PUT',
-    body: idList,
-  };
-  return request(`${api.configCourse}/${courseId}`, options);
-}

+ 0 - 38
src/services/courseware.js

@@ -1,38 +0,0 @@
-import { stringify } from 'qs';
-import request from '../utils/request';
-import { api, Hotax } from '../utils/config';
-
-export async function queryCoursewareList(params) {
-  const newParams = {
-    pageSize: Hotax.PAGE_SIZE,
-    ...params,
-  };
-  return request(`${api.courseware}?${stringify(newParams)}`);
-}
-
-export async function queryCoursewareItem({ id }) {
-  return request(`${api.coursewareItem}/${id}`);
-}
-
-export async function createCoursewareItem(params) {
-  const options = {
-    method: 'POST',
-    body: params,
-  };
-  return request(`${api.coursewareItem}`, options);
-}
-
-export async function updateCoursewareItem(params) {
-  const options = {
-    method: 'PUT',
-    body: params,
-  };
-  return request(`${api.coursewareItem}`, options);
-}
-
-export async function deleteCoursewareItem({ id }) {
-  const options = {
-    method: 'DELETE',
-  };
-  return request(`${api.coursewareItem}/${id}`, options);
-}

+ 0 - 38
src/services/lesson.js

@@ -1,38 +0,0 @@
-import { stringify } from 'qs';
-import request from '../utils/request';
-import { api, Hotax } from '../utils/config';
-
-export async function queryLessonList(params) {
-  const newParams = {
-    pageSize: Hotax.PAGE_SIZE,
-    ...params,
-  };
-  return request(`${api.lesson}?${stringify(newParams)}`);
-}
-
-export async function queryLessonItem({ id }) {
-  return request(`${api.lessonItem}/${id}`);
-}
-
-export async function createLessonItem(params) {
-  const options = {
-    method: 'POST',
-    body: params,
-  };
-  return request(`${api.lessonItem}`, options);
-}
-
-export async function updateLessonItem(params) {
-  const options = {
-    method: 'PUT',
-    body: params,
-  };
-  return request(`${api.lessonItem}`, options);
-}
-
-export async function deleteLessonItem({ id }) {
-  const options = {
-    method: 'DELETE',
-  };
-  return request(`${api.lessonItem}/${id}`, options);
-}

+ 0 - 89
src/services/merchant.js

@@ -1,89 +0,0 @@
-import { stringify } from 'qs';
-import request from '../utils/request';
-import { api, Hotax } from '../utils/config';
-
-export async function queryMerchantList(params) {
-  const newParams = {
-    pageSize: Hotax.PAGE_SIZE,
-    ...params,
-  };
-  return request(`${api.merchant}?${stringify(newParams)}`);
-}
-
-export async function queryMerchantItem({ id }) {
-  return request(`${api.merchantItem}/${id}`);
-}
-
-export async function createMerchantItem(params) {
-  const options = {
-    method: 'POST',
-    body: params,
-  };
-  return request(`${api.merchantItem}`, options);
-}
-
-export async function updateMerchantItem(params) {
-  const options = {
-    method: 'PUT',
-    body: params,
-  };
-  return request(`${api.merchantItem}`, options);
-}
-
-export async function deleteMerchantItem({ id }) {
-  const options = {
-    method: 'DELETE',
-  };
-  return request(`${api.merchantItem}/${id}`, options);
-}
-
-export async function depositMerchantItem(params) {
-  const options = {
-    method: 'POST',
-    body: params,
-  };
-  return request(`${api.deposit}`, options);
-}
-
-export async function queryMerchantRecommend({ merchantId }) {
-  return request(`${api.recommend}/${merchantId}`);
-}
-
-export async function updateMerchantRecommend({ merchantId, idList }) {
-  const options = {
-    method: 'PUT',
-    body: idList,
-  };
-  return request(`${api.recommend}/${merchantId}`, options);
-}
-
-export async function queryMerchantPoster({ merchantId }) {
-  return request(`${api.poster}/${merchantId}`);
-}
-
-export async function queryMerchantPosterItem({ posterId }) {
-  return request(`${api.posterItem}/${posterId}`);
-}
-
-export async function createMerchantPosterItem(params) {
-  const options = {
-    method: 'POST',
-    body: params,
-  };
-  return request(`${api.posterItem}`, options);
-}
-
-export async function updateMerchantPosterItem(params) {
-  const options = {
-    method: 'PUT',
-    body: params,
-  };
-  return request(`${api.posterItem}`, options);
-}
-
-export async function deleteMerchantPosterItem({ posterId }) {
-  const options = {
-    method: 'DELETE',
-  };
-  return request(`${api.posterItem}/${posterId}`, options);
-}

+ 0 - 38
src/services/tagGroup.js

@@ -1,38 +0,0 @@
-import { stringify } from 'qs';
-import request from '../utils/request';
-import { api, Hotax } from '../utils/config';
-
-export async function queryTagGroupList(params) {
-  const newParams = {
-    pageSize: Hotax.PAGE_SIZE,
-    ...params,
-  };
-  return request(`${api.tagGroup}?${stringify(newParams)}`);
-}
-
-export async function queryTagGroupItem({ groupId }) {
-  return request(`${api.tagGroupItem}/${groupId}`);
-}
-
-export async function createTagGroupItem(params) {
-  const options = {
-    method: 'POST',
-    body: params,
-  };
-  return request(`${api.tagGroupItem}`, options);
-}
-
-export async function updateTagGroupItem(params) {
-  const options = {
-    method: 'PUT',
-    body: params,
-  };
-  return request(`${api.tagGroupItem}`, options);
-}
-
-export async function deleteTagGroupItem({ groupId }) {
-  const options = {
-    method: 'DELETE',
-  };
-  return request(`${api.tagGroupItem}/${groupId}`, options);
-}

+ 165 - 0
src/services/terminal.js

@@ -2,6 +2,11 @@ import { stringify } from 'qs';
 import request from '../utils/request';
 import { api, Hotax } from '../utils/config';
 
+/**
+ * 查询终端用户列表
+ * @param params
+ * @returns {Promise<Object>}
+ */
 export async function queryTerminalList(params) {
   const newParams = {
     pageSize: Hotax.PAGE_SIZE,
@@ -10,6 +15,11 @@ export async function queryTerminalList(params) {
   return request(`${api.terminal}?${stringify(newParams)}`);
 }
 
+/**
+ * 创建终端用户
+ * @param params
+ * @returns {Promise<Object>}
+ */
 export async function createTerminalItem(params) {
   const options = {
     method: 'POST',
@@ -18,6 +28,11 @@ export async function createTerminalItem(params) {
   return request(`${api.terminalItem}`, options);
 }
 
+/**
+ * 修改终端用户
+ * @param params
+ * @returns {Promise<Object>}
+ */
 export async function updateTerminalItem(params) {
   const options = {
     method: 'PUT',
@@ -26,6 +41,11 @@ export async function updateTerminalItem(params) {
   return request(`${api.terminalItem}`, options);
 }
 
+/**
+ * 删除终端用户
+ * @param id
+ * @returns {Promise<Object>}
+ */
 export async function deleteTerminalItem({ id }) {
   const options = {
     method: 'DELETE',
@@ -33,6 +53,11 @@ export async function deleteTerminalItem({ id }) {
   return request(`${api.terminalItem}/${id}`, options);
 }
 
+/**
+ * 查询白名单用户
+ * @param params
+ * @returns {Promise<Object>}
+ */
 export async function querySpecialTerminalList(params) {
   const newParams = {
     pageSize: Hotax.PAGE_SIZE,
@@ -41,10 +66,20 @@ export async function querySpecialTerminalList(params) {
   return request(`${api.specialTerminal}?${stringify(newParams)}`);
 }
 
+/**
+ * 查询白名单用户详情
+ * @param userId
+ * @returns {Promise<Object>}
+ */
 export async function querySpecialTerminalItem({ userId }) {
   return request(`${api.specialTerminalItem}/${userId}`);
 }
 
+/**
+ * 创建白名单用户
+ * @param params
+ * @returns {Promise<Object>}
+ */
 export async function createSpecialTerminalItem(params) {
   const options = {
     method: 'POST',
@@ -53,6 +88,11 @@ export async function createSpecialTerminalItem(params) {
   return request(`${api.specialTerminalItem}`, options);
 }
 
+/**
+ * 修改白名单用户
+ * @param params
+ * @returns {Promise<Object>}
+ */
 export async function updateSpecialTerminalItem(params) {
   const options = {
     method: 'PUT',
@@ -61,6 +101,11 @@ export async function updateSpecialTerminalItem(params) {
   return request(`${api.specialTerminalItem}`, options);
 }
 
+/**
+ * 删除白名单用户
+ * @param params
+ * @returns {Promise<Object>}
+ */
 export async function deleteSpecialTerminalItem(params) {
   const options = {
     method: 'DELETE',
@@ -69,9 +114,129 @@ export async function deleteSpecialTerminalItem(params) {
   return request(`${api.specialTerminalItem}`, options);
 }
 
+/**
+ * 解除终端绑定
+ * @param id
+ * @returns {Promise<Object>}
+ */
 export async function deviceUnbound({ id }) {
   const options = {
     method: 'DELETE',
   };
   return request(`${api.terminalUnbound}/${id}`, options);
 }
+
+/**
+ * 查询终端权限列表
+ * @param params
+ * @returns {Promise<Object>}
+ */
+export async function queryTerminalAuthList(params) {
+  const newParams = {
+    pageSize: Hotax.PAGE_SIZE,
+    ...params,
+  };
+  return request(`${api.terminalAuth}?${stringify(newParams)}`);
+}
+
+/**
+ * 修改用户权限时长
+ * @param params
+ * @returns {Promise<Object>}
+ */
+export async function updateTerminalAuth(params) {
+  const options = {
+    method: 'POST',
+    body: params,
+  };
+  return request(`${api.terminalAuthEdit}`, options);
+}
+
+/**
+ * 查询终端标签列表
+ * @param uid
+ * @returns {Promise<Object>}
+ */
+export async function queryTerminalTagList({ uid }) {
+  return request(`${api.userTags}/${uid}`);
+}
+
+/**
+ * 获取终端标签详情
+ * @param userTagId
+ * @returns {Promise<Object>}
+ */
+export async function queryTerminalTagItem({ userTagId }) {
+  return request(`${api.userTag}/${userTagId}`);
+}
+
+/**
+ * 创建终端标签
+ * @param params
+ * @returns {Promise<Object>}
+ */
+export async function createTerminalTagItem(params) {
+  const options = {
+    method: 'POST',
+    body: params,
+  };
+  return request(`${api.userTag}`, options);
+}
+
+/**
+ * 修改终端标签
+ * @param params
+ * @returns {Promise<Object>}
+ */
+export async function updateTerminalTagItem(params) {
+  const options = {
+    method: 'PUT',
+    body: params,
+  };
+  return request(`${api.userTag}`, options);
+}
+
+/**
+ * 删除终端标签
+ * @param userTagId
+ * @returns {Promise<Object>}
+ */
+export async function deleteTerminalTagItem({ userTagId }) {
+  const options = {
+    method: 'DELETE',
+  };
+  return request(`${api.userTag}/${userTagId}`, options);
+}
+
+/**
+ * 复制渠道标签
+ * @param params
+ * @returns {Promise<Object>}
+ */
+export async function copyMerchantTag(params) {
+  return request(`${api.userTagCopy}?${stringify(params)}`);
+}
+
+/**
+ * 查询终端的推荐课程
+ * @param params
+ * @returns {Promise<Object>}
+ */
+export async function queryTerminalRecommendCourse({ uid }) {
+  return request(`${api.userRecommend}/${uid}`);
+}
+
+/**
+ * 修改终端的推荐课程
+ * @param uid <用户id>
+ * @param idList <课程的 idList>
+ * @returns {Promise<Object>}
+ */
+export async function updateTerminalRecommendCourse({ uid, idList }) {
+  const options = {
+    method: 'PUT',
+    body: idList,
+  };
+  return request(`${api.userRecommend}/${uid}`, options);
+}
+

+ 2 - 9
src/theme.js

@@ -1,14 +1,7 @@
 // https://github.com/ant-design/ant-design/blob/master/components/style/themes/default.less
 module.exports = {
-  'primary-color': '#faad14',
+  'primary-color': '#00a65a',
   // 'border-radius-base': '0',
   // 'font-family': '"Helvetica Neue", Helvetica, Arial, sans-serif',
-  'table-header-bg': '#ffe58f',
-  'font-size-base': '14px',
-  'badge-font-size': '12px',
-  'btn-font-size-lg': '@font-size-base',
-  'menu-dark-bg': '#faad14',
-  'menu-dark-submenu-bg': '#613400',
-  'layout-sider-background': '#faad14',
-  'layout-body-background': '#f0f2f5',
+  'table-header-bg': '#f5f6fa',
 };

+ 7 - 5
src/utils/config.js

@@ -71,7 +71,7 @@ Hotax.PAGE_SIZE = 20;
 // 上传文件最大尺寸
 Hotax.FILE_MAX_SIZE = 5; // Index max size should below 5M
 // 项目名称
-Hotax.PROJECT_NAME = '渠道管理平台';
+Hotax.PROJECT_NAME = '渠道运维';
 // 版权声明
 Hotax.CopyRight = '2017-2020 领教信息科技有限公司';
 // 接口地址(测试)
@@ -101,6 +101,8 @@ const apiObj = {
   terminal: '/user/list',
   terminalItem: '/user',
   terminalUnbound: '/device/unbind',
+  terminalAuth: '/user/auth/list',
+  terminalAuthEdit: '/user/auth',
   specialTerminal: '/white/user/list',
   specialTerminalItem: '/white/user',
   cmsUser: '/cms/user/list',
@@ -128,10 +130,10 @@ const apiObj = {
   orderSend: '/order/send',
   orderReceive: '/order/receive',
   snapshot: '/order/snapshot',
-  configUser: '/user/userTag/uid',
-  configUserTag: '/userTag',
-  configCourse: '/user/userRecommend/uid',
-  copyTag: '/userTag/copy',
+  userTags: '/user/userTag/uid',
+  userTag: '/userTag',
+  userTagCopy: '/userTag/copy',
+  userRecommend: '/user/userRecommend/uid',
 };
 
 /**

+ 17 - 3
src/utils/utils.js

@@ -282,7 +282,7 @@ export function renderAvatar(avatar, name) {
         verticalAlign: 'middle',
       }}
     >
-      {name[0]}
+      {name && name[0]}
     </Avatar>
   );
 }
@@ -321,6 +321,8 @@ export function renderProductType(type) {
     return '课程';
   } else if (type === Hotax.PRODUCT_SUPPORT) {
     return '配套';
+  } else if (type === Hotax.PRODUCT_TRAINING) {
+    return '师训';
   } else if (type === Hotax.PRODUCT_PACKAGE) {
     return '套餐包';
   } else {
@@ -429,6 +431,11 @@ export function renderOrderSplitStatus(status) {
   }
 }
 
+/**
+ * 根据商品类型返回中文名称
+ * @param status
+ * @returns {string}
+ */
 export function renderGoodsType(status) {
   switch (status) {
     case Hotax.GOODS_VIRTUAL:
@@ -440,6 +447,11 @@ export function renderGoodsType(status) {
   }
 }
 
+/**
+ * 根据资源类型获取资源中文名称
+ * @param type
+ * @returns {string}
+ */
 export function getResourceTypeName(type) {
   switch (type) {
     case Hotax.RESOURCE_VIDEO:
@@ -455,18 +467,19 @@ export function getResourceTypeName(type) {
   }
 }
 
-// 视频相关常量
+// 资源类型 - 中文名称map
 const resourceTypes = {
   [Hotax.RESOURCE_AUDIO]: '音频',
   [Hotax.RESOURCE_VIDEO]: '视频',
 };
+// 资源质量 - 中文名称map
 const resourceQuality = {
   [Hotax.QUALITY_FLUENT]: '流畅',
   [Hotax.QUALITY_STANDARD]: '标清',
   [Hotax.QUALITY_HIGH]: '高清',
   [Hotax.QUALITY_SUPERCLEAR]: '超清',
 };
-// 价格相关常量
+// 价格类型 - 中文名称map
 const chargeUnitMap = {
   [Hotax.CHARGE_UNIT_DAY]: '天',
   [Hotax.CHARGE_UNIT_SEASON]: '季',
@@ -474,6 +487,7 @@ const chargeUnitMap = {
   [Hotax.CHARGE_UNIT_YEAR]: '年',
   [Hotax.CHARGE_UNIT_ITEM]: '件',
 };
+// 计价单位 - 时长map
 const durationMap = {
   [Hotax.CHARGE_UNIT_SEASON]: Hotax.DURATION_SEASON,
   [Hotax.CHARGE_UNIT_HALF_YEAR]: Hotax.DURATION_HALF_YEAR,