Browse Source

1.调整视频分页;
2.新增商品;

zhanghe 6 years ago
parent
commit
82ea6c7125
81 changed files with 1480 additions and 927 deletions
  1. 42 5
      .roadhogrc.mock.js
  2. 0 27
      mock/goods.js
  3. 1 1
      mock/merchant.js
  4. 83 0
      mock/mproduct.js
  5. 59 0
      mock/product.js
  6. 3 1
      mock/resource.js
  7. 29 36
      src/common/menu.js
  8. 32 17
      src/common/router.js
  9. 5 4
      src/components/CardValuation/index.js
  10. 1 1
      src/components/Uploader/index.js
  11. 5 2
      src/components/VideoPlayer/index.js
  12. 2 17
      src/index.js
  13. 4 25
      src/models/cmsUser.js
  14. 6 27
      src/models/combo/combo.js
  15. 2 12
      src/models/combo/detail.js
  16. 8 56
      src/models/course/course.js
  17. 9 20
      src/models/course/detail.js
  18. 110 0
      src/models/goods/detail.js
  19. 68 0
      src/models/goods/goods.js
  20. 13 19
      src/models/group/detail.js
  21. 6 51
      src/models/group/group.js
  22. 2 14
      src/models/lesson/detail.js
  23. 4 25
      src/models/lesson/lesson.js
  24. 4 25
      src/models/merchant/merchant.js
  25. 2 13
      src/models/order/detail.js
  26. 4 25
      src/models/order/order.js
  27. 4 25
      src/models/resource.js
  28. 7 18
      src/models/support/detail.js
  29. 7 48
      src/models/support/support.js
  30. 11 18
      src/models/tag/detail.js
  31. 5 50
      src/models/tag/tag.js
  32. 2 14
      src/models/ware/detail.js
  33. 4 25
      src/models/ware/ware.js
  34. 1 1
      src/routes/CmsUser/table.js
  35. 94 20
      src/routes/Course/detail/index.js
  36. 1 1
      src/routes/Course/detail/lessonModal.js
  37. 0 0
      src/routes/Course/Edit/lesson.less
  38. 1 1
      src/routes/Course/detail/resourceModal.js
  39. 0 0
      src/routes/Course/Edit/resource.less
  40. 1 1
      src/routes/Course/detail/supportModal.js
  41. 0 0
      src/routes/Course/Edit/support.less
  42. 2 3
      src/routes/Course/index.js
  43. 1 1
      src/routes/Course/search.js
  44. 2 2
      src/routes/Course/table.js
  45. 1 1
      src/routes/Course/table.less
  46. 12 11
      src/routes/Course/detail/resourceModal.js
  47. 374 0
      src/routes/Goods/Add/index.js
  48. 11 0
      src/routes/Goods/Add/index.less
  49. 12 11
      src/routes/Course/detail/resourceModal.js
  50. 9 11
      src/routes/Course/detail/resourceModal.js
  51. 40 0
      src/routes/Goods/Edit/index.js
  52. 21 26
      src/routes/Tag/index.js
  53. 4 4
      src/routes/Course/search.js
  54. 39 23
      src/routes/Tag/table.js
  55. 8 8
      src/routes/Tag/table.less
  56. 1 1
      src/routes/Resource/gallery/search.js
  57. 2 2
      src/routes/Resource/video/modal.js
  58. 14 4
      src/routes/Resource/video/table.js
  59. 10 6
      src/routes/Resource/video/table.less
  60. 52 7
      src/routes/Support/detail/index.js
  61. 1 4
      src/routes/Support/index.js
  62. 2 8
      src/routes/Tag/detail/index.js
  63. 0 0
      src/routes/Tag/Edit/modal.js
  64. 6 4
      src/routes/Tag/index.js
  65. 2 4
      src/routes/Tag/search.js
  66. 19 14
      src/routes/Tag/table.js
  67. 9 13
      src/routes/TagGroup/table.less
  68. 2 2
      src/routes/TagGroup/detail/index.js
  69. 0 0
      src/routes/TagGroup/Edit/modal.js
  70. 9 15
      src/routes/TagGroup/index.js
  71. 2 4
      src/routes/TagGroup/search.js
  72. 17 12
      src/routes/TagGroup/table.js
  73. 3 11
      src/routes/TagGroup/table.less
  74. 41 0
      src/services/goods.js
  75. 4 4
      src/services/group.js
  76. 49 0
      src/services/product.js
  77. 0 3
      src/services/resource.js
  78. 1 1
      src/theme.js
  79. 20 7
      src/utils/api.js
  80. 22 5
      src/utils/config.js
  81. 14 50
      src/utils/request.js

+ 42 - 5
.roadhogrc.mock.js

@@ -11,6 +11,8 @@ import { lessonList } from './mock/lesson';
 import { courseList } from './mock/course';
 import { supportList } from './mock/support';
 import { comboList } from './mock/combo';
+import { productList } from './mock/product';
+import { merchantProductList } from './mock/mproduct';
 import { orderList } from './mock/order';
 import { signature } from './mock/signature';
 import { cmsUserList } from './mock/cmsUser';
@@ -25,17 +27,19 @@ global.groupList = groupList;
 global.tagList = tagList;
 global.wareList = wareList;
 global.lessonList = lessonList;
-global.courseList = courseList;
-global.supportList = supportList;
-global.comboList = comboList;
+global.courseList = courseList;     //课程数据,已整合到product接口,废弃
+global.supportList = supportList;   //配套数据,已整合到product接口,废弃
+global.comboList = comboList;       //课程包数据,已整合到product接口,废弃
+global.productList = productList;
 global.orderList = orderList;
+global.merchantProductList = merchantProductList;
 global.signature = signature;
 global.cmsUserList = cmsUserList;
 
 // 操作成功响应内容
 const SUCCESS = { code: 200, success: true, message: null };
 // 资源未找到响应内容
-const NOTFOUND = { code: 404, message: 'Not Found!' };
+const NOTFOUND = { code: 404, message: '资源未找到!' };
 
 // 查询
 const query = (dataset, params) => {
@@ -127,6 +131,11 @@ const remove = (dataset, id, res) => {
 
 // mock数据
 const proxy = {
+  // 渠道-产品
+  [`GET ${api.mproducts}`]: (req, res) => {
+    console.log(`[GET][${api.mproducts}]`, req.query);
+    res.send(query(global.merchantProductList, req.query));
+  },
   // 资源
   [`POST ${api.resource.replace('/:id', '')}`]: (req, res) => {
     console.log(`[POST][${api.resource}]`, req.body);
@@ -276,7 +285,10 @@ const proxy = {
     const { id } = req.params;
     queryOne(global.lessonList, id, res);
   },
+
+  // ################## 已被product接口替代 ###################
   // 课程
+  /*
   [`POST ${api.course.replace('/:id', '')}`]: (req, res) => {
     console.log(`[POST][${api.course}]`, req.body);
     res.send(create(global.courseList, req.body));
@@ -345,6 +357,32 @@ const proxy = {
     const { id } = req.params;
     queryOne(global.comboList, id, res);
   },
+  */
+  // ##############################################
+
+  // 产品接口
+  [`POST ${api.product}`]: (req, res) => {
+    console.log(`[POST][${api.product}]`, req.body);
+    res.send(create(global.productList, req.body));
+  },
+  [`DELETE ${api.product}/:id`]: (req, res) => {
+    console.log(`[DELETE][${api.product}]`, req.params);
+    const { id } = req.params;
+    remove(global.productList, id, res);
+  },
+  [`PUT ${api.product}/:id`]: (req, res) => {
+    console.log(`[PUT][${api.product}]`, req.body);
+    res.send(update(global.productList, req.body));
+  },
+  [`GET ${api.product}`]: (req, res) => {
+    console.log(`[GET][${api.product}]`, req.query);
+    res.send(query(global.productList, req.query));
+  },
+  [`GET ${api.product}/:id`]: (req, res) => {
+    console.log(`[GET][${api.product}]`, req.params);
+    const { id } = req.params;
+    queryOne(global.productList, id, res);
+  },
   // 订单
   [`POST ${api.order.replace('/:id', '')}`]: (req, res) => {
     console.log(`[POST][${api.order}]`, req.body);
@@ -393,4 +431,3 @@ const noProxy = process.env.NO_PROXY === 'true';
 
 // 根据是否禁用代理来选择是mock数据还是真实接口
 export default noProxy ? {} : delay(proxy, 500);
-// export default noProxy ? {} : proxy;

+ 0 - 27
mock/goods.js

@@ -1,27 +0,0 @@
-let goodsList = [];
-let types = ['COURSE', 'SUPPORT', 'PACKAGE'];
-for(let i = 1; i < 500; i++) {
-  goodsList.push({
-    id: String(i),
-    code: 'Goods-test-' + i,
-    type: types[i % 3],
-    merchantId: '87',
-    goods: [{
-      id: '39489198994523',
-      duration: 12000,
-      chargeUnit: '年',
-      cpPrice: 1000.89,
-      merchantPrice: 2000.11,
-      terminalPrice: 3000.22,
-    },{
-      id: '43578294325898',
-      duration: 258998,
-      chargeUnit: '季',
-      cpPrice: 45928,
-      merchantPrice: 777665,
-      terminalPrice: 987343,
-    }],
-  })
-}
-
-module.exports = { goodsList };

+ 1 - 1
mock/merchant.js

@@ -3,7 +3,7 @@ for (let i = 1; i < 48; i++) {
   merchantList.push({
     id: String(i),
     name: `厂商-${i}`,
-    domain: 2010,
+    domain: '2010',
     code: '0000' + i,
     licenseId: '79123765223900316',
     taxNumber: '00130',

+ 83 - 0
mock/mproduct.js

@@ -0,0 +1,83 @@
+let merchantProductList = [];
+
+merchantProductList = merchantProductList.concat(
+  [{
+    id: '387201987',
+    name: '急用先学汉字',
+    code: 'course-08-01',
+    type: 0,
+    status: 'NORMAL',
+    merchantId: '87',
+    merchantName: '贝尔安亲',
+    goods: [{
+      id: '39489198994523',
+      duration: 12000,
+      chargeUnit: '年',
+      cpPrice: 1000.89,
+      merchantPrice: 2000.11,
+      terminalPrice: 3000.22,
+    },{
+      id: '43578294325898',
+      duration: 258998,
+      chargeUnit: '季',
+      cpPrice: 45928,
+      merchantPrice: 777665,
+      terminalPrice: 987343,
+    },{
+      id: '9987654321',
+      duration: 180,
+      chargeUnit: '半年',
+      cpPrice: 10000,
+      merchantPrice: 20000,
+      terminalPrice: 30000,
+    }],
+  },{
+    id: '213456798',
+    name: '急用先学汉字练习册',
+    code: 'support-01-09',
+    status: 'DEL',
+    type: 1,
+    merchantId: '88',
+    merchantName: '好托管',
+    goods: [{
+      id: '9090908989',
+      duration: null,
+      chargeUnit: '件',
+      cpPrice: 35.5,
+      merchantPrice: 45.5,
+      terminalPrice: 50,
+    }]
+  },{
+    id: '452942539',
+    name: '汉字书写练习',
+    status: 'NORMAL',
+    code: 'package-01-09',
+    type: 2,
+    merchantId: '89',
+    merchantName: '昂乐',
+    goods: [{
+      id: '34893498',
+      duration: 365,
+      chargeUnit: '年',
+      cpPrice: 2000,
+      merchantPrice: 3000,
+      terminalPrice: 5000,
+    },{
+      id: '78314988',
+      duration: 180,
+      chargeUnit: '半年',
+      cpPrice: 1000,
+      merchantPrice: 2000,
+      terminalPrice: 3000,
+    },{
+      id: '42578097',
+      duration: 30,
+      chargeUnit: '月',
+      cpPrice: 500,
+      merchantPrice: 1000,
+      terminalPrice: 2000,
+    }]
+  }]
+);
+
+module.exports = { merchantProductList };

+ 59 - 0
mock/product.js

@@ -0,0 +1,59 @@
+let productList = [];
+const statuses = ['NORMAL', 'DEL'];
+for (let i = 1; i < 300; i++) {
+  productList.push({
+    id: String(i),
+    code: 'course-code-' + i,
+    name: '小学语文二年级上册' + i,
+    title: '小学语文二年级上册' + i,
+    digest: '这段是课程描述,很长很长很长很长很长很长很长很长很长很长很长很长...',
+    detail: '这段是课程详情,这门课程讲的是小学语文,有很多很多很多很多很多很多很多很多的故事...',
+    keyword: null,
+    coverUrl: 'http://efunimgs.oss-cn-beijing.aliyuncs.com/resources/J/02/01/612102.jpg',
+    bgUrl: 'http://efunimgs.oss-cn-beijing.aliyuncs.com/resources/J/02/01/612102.jpg',
+    type: 'COURSE',
+    status: statuses[i % 2],
+    subItemList: null,
+    supportList: null,
+    gmtCreated: 1512981450000,
+    gmtModified: 1512981450000,
+  })
+}
+
+for (let i = 300; i < 600; i++) {
+  productList.push({
+    id: String(i),
+    code: 'support-code-' + i,
+    name: '小学语文二年级上册练习册' + i,
+    title: '小学语文二年级上册练习册' + i,
+    digest: '这段是周边描述,很长很长很长很长很长很长很长很长很长很长很长很长...',
+    detail: '这段是周边详情,这个周边是小学语文练习册,有很多很多很多很多很多很多很多很多的题目...',
+    keyword: null,
+    subItemList: null,
+    supportList: null,
+    type: 'SUPPORT',
+    status: statuses[i % 2],
+    gmtCreated: 1512981450000,
+    gmtModified: 1512981450000,
+  })
+}
+
+for (let i = 600; i < 1000; i++) {
+  productList.push({
+    id: String(i),
+    code: 'package-code-' + i,
+    name: '小学语文二年级课程包-' + i,
+    title: '小学语文二年级课程包',
+    digest: '小学二年级语文的全部课程,包括相关的联系册,文具,教具...',
+    detail: '该课程包的详情描述,很长很长很长很长很长很长很长很长很长很长...',
+    keyword: null,
+    subItemList: null,
+    supportList: null,
+    type: 'PACKAGE',
+    status: statuses[i % 2],
+    gmtCreated: 15129876650000,
+    gmtModified: 1512289450000,
+  })
+}
+
+module.exports = { productList };

+ 3 - 1
mock/resource.js

@@ -30,7 +30,9 @@ for (let i = 1; i < 100; i++) {
       type: '0',
       gmtCreated: (new Date()).getTime(),
       gmtModified: (new Date()).getTime(),
-      url: 'http://efunvideo.ai160.com/vs2m/015/01503009/01503009031/01503009031.m3u8',
+      // url: 'http://efunvideo.ai160.com/vs2m/015/01503009/01503009031/01503009031.m3u8',
+      //url: 'http://ljvideo.ai160.com/vs2m/001/00103024/00103024026/00103024026.m3u8',
+      url: 'http://ljvideo.ai160.com/vs2m/001/00103024/00103024026/00103024026.m3u8',
     }
   ]);
 }

+ 29 - 36
src/common/menu.js

@@ -1,5 +1,9 @@
 const menuData = [
   {
+    name: '主页',
+    icon: 'dashboard',
+    path: 'dashboard',
+  },{
     name: '资源管理',
     icon: 'folder',
     path: 'resource',
@@ -16,10 +20,10 @@ const menuData = [
     path: 'tag',
     children: [{
       name: '标签组',
-      path: 'group',
+      path: 'tagGroup',
     },{
       name: '标签',
-      path: 'tag',
+      path: 'tagItem',
     }]
   },{
     name: '产品管理',
@@ -45,10 +49,29 @@ const menuData = [
     name: '商品管理',
     icon: 'shop',
     path: 'goods',
-    children: [{
-      name: '商品列表',
-      path: 'item',
-    }]
+  },{
+    name: '订单管理',
+    icon: 'trademark',
+    path: 'order',
+  },{
+    name: '销售统计',
+    icon: 'area-chart',
+    path: 'sold',
+  // },{
+  //   name: '行为统计',
+  //   icon: 'scan',
+  //   path: 'behavior',
+  //   children: [{
+  //     name: '概览',
+  //     path: 'overview',
+  //   },{
+  //     name: '统计详情',
+  //     path: 'detail',
+  //   }]
+  },{
+    name: '厂商管理',
+    icon: 'team',
+    path: 'merchant',
   },{
     name: '终端管理',
     icon: 'desktop',
@@ -61,14 +84,6 @@ const menuData = [
       path: 'campus',
     }],
   },{
-    name: '订单管理',
-    icon: 'trademark',
-    path: 'order',
-  },{
-    name: '厂商管理',
-    icon: 'team',
-    path: 'merchant',
-  },{
     name: '账户管理',
     icon: 'user-add',
     path: 'cms',
@@ -76,28 +91,6 @@ const menuData = [
       name: 'CMS用户',
       path: 'user',
     }]
-  },{
-    name: '销售统计',
-    icon: 'area-chart',
-    path: 'sold',
-    children: [{
-      name: '概览',
-      path: 'overview'
-    },{
-      name: '销售详情',
-      path: 'detail',
-    }]
-  },{
-    name: '行为统计',
-    icon: 'scan',
-    path: 'behavior',
-    children: [{
-      name: '概览',
-      path: 'overview',
-    },{
-      name: '统计详情',
-      path: 'detail',
-    }]
   },
 ];
 

+ 32 - 17
src/common/router.js

@@ -59,26 +59,26 @@ export const getRouterData = (app) => {
       component: dynamicWrapper(app, ['merchant/detail'], () => import('../routes/Merchant/detail')),
       name: '修改厂商',
     },
-    '/tag/group': {
-      component: dynamicWrapper(app, ['group/group', 'merchant/merchant'], () => import('../routes/TagGroup')),
+    '/tag/tagGroup': {
+      component: dynamicWrapper(app, ['group/group', 'merchant/merchant'], () => import('../routes/TagGroup/List')),
     },
-    '/tag/group/add': {
-      component: dynamicWrapper(app, ['group/detail', 'merchant/merchant'], () => import('../routes/TagGroup/detail')),
+    '/tag/tagGroup/add': {
+      component: dynamicWrapper(app, ['group/detail', 'merchant/merchant'], () => import('../routes/TagGroup/Edit')),
       name: '添加标签组',
     },
-    '/tag/group/edit/:id': {
-      component: dynamicWrapper(app, ['group/detail', 'merchant/merchant'], () => import('../routes/TagGroup/detail')),
+    '/tag/tagGroup/edit/:id': {
+      component: dynamicWrapper(app, ['group/detail', 'merchant/merchant'], () => import('../routes/TagGroup/Edit')),
       name: '修改标签组',
     },
-    '/tag/tag': {
-      component: dynamicWrapper(app, ['tag/tag', 'merchant/merchant'], () => import('../routes/Tag')),
+    '/tag/tagItem': {
+      component: dynamicWrapper(app, ['tag/tag', 'merchant/merchant'], () => import('../routes/Tag/List')),
     },
-    '/tag/tag/add': {
-      component: dynamicWrapper(app, ['tag/detail', 'group/group'], () => import('../routes/Tag/detail')),
+    '/tag/tagItem/add': {
+      component: dynamicWrapper(app, ['tag/detail', 'group/group'], () => import('../routes/Tag/Edit')),
       name: '添加标签',
     },
-    '/tag/tag/edit/:id': {
-      component: dynamicWrapper(app, ['tag/detail', 'group/group'], () => import('../routes/Tag/detail')),
+    '/tag/tagItem/edit/:id': {
+      component: dynamicWrapper(app, ['tag/detail', 'group/group'], () => import('../routes/Tag/Edit')),
       name: '修改标签',
     },
     '/product/ware': {
@@ -104,30 +104,45 @@ export const getRouterData = (app) => {
       name: '修改课',
     },
     '/product/course': {
-      component: dynamicWrapper(app, ['course/course'], () => import('../routes/Course')),
+      component: dynamicWrapper(app, ['course/course'], () => import('../routes/Course/List')),
     },
     '/product/course/add': {
-      component: dynamicWrapper(app, ['course/detail', 'resource', 'lesson/lesson'], () => import('../routes/Course/detail')),
+      component: dynamicWrapper(app, ['course/detail', 'resource', 'lesson/lesson', 'support/support', 'merchant/merchant'], () => import('../routes/Course/Edit')),
       name: '添加课程',
     },
     '/product/course/edit/:id': {
-      component: dynamicWrapper(app, ['course/detail', 'resource', 'lesson/lesson'], () => import('../routes/Course/detail')),
+      component: dynamicWrapper(app, ['course/detail', 'resource', 'lesson/lesson', 'support/support', 'merchant/merchant'], () => import('../routes/Course/Edit')),
       name: '修改课程',
     },
     '/product/support': {
       component: dynamicWrapper(app, ['support/support'], () => import('../routes/Support')),
     },
     '/product/support/add': {
-      component: dynamicWrapper(app, ['support/detail', 'resource', 'support/support'], () => import('../routes/Support/detail')),
+      component: dynamicWrapper(app, ['support/detail', 'resource', 'support/support', 'merchant/merchant'], () => import('../routes/Support/detail')),
       name: '添加配套',
     },
     '/product/support/edit/:id': {
-      component: dynamicWrapper(app, ['support/detail', 'resource', 'support/support'], () => import('../routes/Support/detail')),
+      component: dynamicWrapper(app, ['support/detail', 'resource', 'support/support', 'merchant/merchant'], () => import('../routes/Support/detail')),
       name: '修改配套',
     },
     '/product/package': {
       component: dynamicWrapper(app, ['combo/combo'], () => import('../routes/Combo')),
     },
+    '/goods': {
+      component: dynamicWrapper(app, ['goods/goods', 'merchant/merchant'], () => import('../routes/Goods/List')),
+    },
+    '/goods/add': {
+      component: dynamicWrapper(app, ['course/course', 'support/support', 'combo/combo', 'merchant/merchant', 'goods/detail'], () => import('../routes/Goods/Add')),
+      name: '创建商品',
+    },
+    '/goods/edit': {
+      component: dynamicWrapper(app, ['goods/detail'], () => import('../routes/Goods/Edit')),
+      name: '修改商品',
+    },
+    // '/goods/add': {
+    //   component: dynamicWrapper(app, [], () => import('../routes/Goods/GoodsProfile')),
+    //   name: '新建商品'
+    // },
     // '/product/package/add': {
     //   component: dynamicWrapper(app, ['combo/detail', 'support/support'], () => import('../routes/Combo/detail')),
     //   name: '添加课程包',

+ 5 - 4
src/components/CardValuation/index.js

@@ -124,6 +124,7 @@ export default class CardValuation extends PureComponent {
   }
 
   render() {
+    const { cardTitle } = this.props;
     const columns = [{
       title: '计价单位',
       dataIndex: 'chargeUnit',
@@ -141,7 +142,7 @@ export default class CardValuation extends PureComponent {
         return text;
       },
     },{
-      title: '供应商价格',
+      title: '供应商价格(¥)',
       dataIndex: 'cpPrice',
       key: 'cpPrice',
       render: (text, record) => {
@@ -157,7 +158,7 @@ export default class CardValuation extends PureComponent {
         return text;
       }
     },{
-      title: '渠道方价格',
+      title: '渠道方价格(¥)',
       dataIndex: 'merchantPrice',
       key: 'merchantPrice',
       render: (text, record) => {
@@ -173,7 +174,7 @@ export default class CardValuation extends PureComponent {
         return text;
       },
     },{
-      title: '终端价格',
+      title: '终端价格(¥)',
       dataIndex: 'terminalPrice',
       key: 'terminalPrice',
       render: (text, record) => {
@@ -234,10 +235,10 @@ export default class CardValuation extends PureComponent {
           }}
         />
         <Button
+          onClick={this.newMember}
           style={{ width: '100%', marginTop: 16, marginBottom: 8 }}
           type="dashed"
           icon="plus"
-          disabled={true}
         >新增价格类型
         </Button>
       </Card>

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

@@ -100,7 +100,7 @@ export default class Uploader extends PureComponent {
           fileName: file.name.replace(/-/g, '/')
         });
         // 进行签名校验,失效则刷新签名
-        return getSignature().then(res => this.setState({signature: { ...res.data }}));
+        return getSignature({ fileName: file.name }).then(res => this.setState({signature: { ...res.data }}));
       },
       onChange: ({ file, fileList }) => {
         // 检查图片大小,不能超过5M

+ 5 - 2
src/components/VideoPlayer/index.js

@@ -41,7 +41,9 @@ export default class VideoPlayer extends PureComponent {
       this.hls.destroy();
     }
 
-    let { url, autoplay, hlsConfig } = this.props;
+    let { url, autoplay, hlsConfig, m3u8 } = this.props;
+    if (!m3u8) return;
+
     let { video : $video } = this.refs;
     let hls = new Hls(hlsConfig);
 
@@ -65,7 +67,7 @@ export default class VideoPlayer extends PureComponent {
 
   render() {
     let { isMuted, isPlaying, playerId } = this.state;
-    let { controls, width, height } = this.props;
+    let { controls, width, height, url } = this.props;
 
     return (
       <div key={playerId}>
@@ -74,6 +76,7 @@ export default class VideoPlayer extends PureComponent {
         }
         <video ref="video"
           id={`react-hls-${playerId}`}
+          src={url}
           controls={controls}
           width={width}
           height={height}

+ 2 - 17
src/index.js

@@ -1,31 +1,14 @@
 import 'babel-polyfill';
-import { message, notification } from 'antd';
 import dva from 'dva';
-import { routerRedux } from 'dva/router';
 import 'moment/locale/zh-cn';
 import './g2';
 // import './rollbar';
 import browserHistory from 'history/createBrowserHistory';
 import './index.less';
 
-const globalErrorHandler = (err) => {
-  if (err.response && err.response.code === 10004) {
-    message.error('登录失效,请重新登录!');
-    app._store.dispatch(routerRedux.push('/user/login'));
-  }
-  else {
-    console.error(err);
-    notification.error({
-      message: '程序错误',
-      description: '应用内部发生未知错误,请查看日志或联系管理员!',
-    });
-  }
-}
-
 // 1. Initialize
 const app = dva({
   history: browserHistory(),
-  onError: globalErrorHandler,
 });
 
 // 2. Plugins
@@ -39,3 +22,5 @@ app.router(require('./router'));
 
 // 5. Start
 app.start('#root');
+
+export default app._store;

+ 4 - 25
src/models/cmsUser.js

@@ -1,7 +1,6 @@
 import { query, create, update, remove } from '../services/cmsUser';
 import modelExtend from 'dva-model-extend';
 import queryString from 'query-string';
-import { message } from 'antd';
 import { pageModel } from './common';
 import config from '../utils/config';
 import { checkSearchParams } from '../utils/utils';
@@ -53,46 +52,26 @@ export default modelExtend(pageModel, {
     * create ({ payload, callback }, { call, put }) {
       const { data, success } = yield call(create, payload);
       if (success) {
-        message.success('创建成功!');
-        if (callback) {
-          callback();
-        }
-      } else {
-        message.error('创建失败!');
+        if (callback) callback();
       }
     },
     * update ({ payload, callback }, { call, put }) {
       const { data, success } = yield call(update, payload);
       if (success) {
         yield put({ type: 'hideModal' });
-        message.success('更新成功!');
-        if (callback) {
-          callback();
-        }
-      } else {
-        message.error('更新失败!');
+        if (callback) callback();
       }
     },
     * delete ({ payload, callback }, { call, put }) {
       const { data, success } = yield call(remove, payload);
       if (success) {
-        message.success('删除成功!');
-        if (callback) {
-          callback();
-        }
-      } else {
-        message.error('删除失败!');
+        if (callback) callback();
       }
     },
     * recover ({ payload, callback }, { call, put }) {
       const { data, success } = yield call(update, payload);
       if (success) {
-        message.success('恢复成功!');
-        if (callback) {
-          callback();
-        }
-      } else {
-        message.error('恢复失败!');
+        if (callback) callback();
       }
     },
   },

+ 6 - 27
src/models/combo/combo.js

@@ -1,9 +1,8 @@
-import { query, create, update, remove } from '../../services/combo';
+import { query, create, update, remove } from '../../services/product';
 import modelExtend from 'dva-model-extend';
 import queryString from 'query-string';
-import { message } from 'antd';
 import { pageModel } from '../common';
-import { pageSize } from '../../utils/config';
+import { pageSize, Codes } from '../../utils/config';
 import { checkSearchParams } from '../../utils/utils';
 
 export default modelExtend(pageModel, {
@@ -53,46 +52,26 @@ export default modelExtend(pageModel, {
     * create ({ payload, callback }, { call, put }) {
       const { data, success } = yield call(create, payload);
       if (success) {
-        message.success('创建成功!');
-        if (callback) {
-          callback();
-        }
-      } else {
-        message.error('创建失败!');
+        if (callback) callback();
       }
     },
     * update ({ payload, callback }, { call, put }) {
       const { data, success } = yield call(update, payload);
       if (success) {
         yield put({ type: 'hideModal' });
-        message.success('更新成功!');
-        if (callback) {
-          callback();
-        }
-      } else {
-        message.error('更新失败!');
+        if (callback) callback();
       }
     },
     * delete ({ payload, callback }, { call, put }) {
       const { data, success } = yield call(remove, payload);
       if (success) {
-        message.success('删除成功!');
-        if (callback) {
-          callback();
-        }
-      } else {
-        message.error('删除失败!');
+        if (callback) callback();
       }
     },
     * recover ({ payload, callback }, { call, put }) {
       const { data, success } = yield call(update, payload);
       if (success) {
-        message.success('恢复成功!');
-        if (callback) {
-          callback();
-        }
-      } else {
-        message.error('恢复失败!');
+        if (callback) callback();
       }
     },
   },

+ 2 - 12
src/models/combo/detail.js

@@ -46,25 +46,15 @@ export default {
       // 创建课程包,默认状态为NORMAL
       const { data, success } = yield call(create, { ...payload, status: Codes.CODE_NORMAL });
       if (success) {
-        message.success('创建成功!');
         yield put({ type: 'clearPage' });
-        if (callback) {
-          callback();
-        }
-      } else {
-        message.error('创建失败!');
+        if (callback) callback();
       }
     },
     * update ({ payload, callback }, { call, put }) {
       const { data, success } = yield call(update, payload);
       if (success) {
-        message.success('更新成功!');
         yield put({ type: 'clearPage' });
-        if (callback) {
-          callback();
-        }
-      } else {
-        message.error('更新失败!');
+        if (callback) callback();
       }
     }
   },

+ 8 - 56
src/models/course/course.js

@@ -1,20 +1,16 @@
-import { query, create, update, remove } from '../../services/course';
+import { query, update, remove } from '../../services/product';
 import modelExtend from 'dva-model-extend';
 import queryString from 'query-string';
-import { message } from 'antd';
 import { pageModel } from '../common';
 import { pageSize } from '../../utils/config';
 import { checkSearchParams } from '../../utils/utils';
+import { Codes } from '../../utils/config';
 
 export default modelExtend(pageModel, {
   namespace: 'course',
 
   state: {
-    currentItem: {},
-    itemLoading: false,
     listLoading: false,
-    modalVisible: false,
-    modalType: 'create',
   },
 
   subscriptions: {
@@ -22,10 +18,7 @@ export default modelExtend(pageModel, {
       history.listen((location) => {
         if (location.pathname === '/product/course') {
           const payload = checkSearchParams(queryString.parse(location.search));
-          dispatch({
-            type: 'query',
-            payload,
-          });
+          dispatch({ type: 'query', payload });
         }
       });
     }
@@ -34,7 +27,7 @@ export default modelExtend(pageModel, {
   effects: {
     * query ({ payload = {} }, { call, put }) {
       yield put({ type: 'changeLoading', payload: { listLoading: true }});
-      const { data, success } = yield call(query, payload);
+      const { data, success } = yield call(query, { ...payload, type: Codes.CODE_COURSE });
       if (success) {
         yield put({
           type: 'querySuccess',
@@ -50,64 +43,23 @@ export default modelExtend(pageModel, {
       }
       yield put({ type: 'changeLoading', payload: { listLoading: false }});
     },
-    * create ({ payload, callback }, { call, put }) {
-      const { data, success } = yield call(create, payload);
-      if (success) {
-        message.success('创建成功!');
-        if (callback) {
-          callback();
-        }
-      } else {
-        message.error('创建失败!');
-      }
-    },
-    * update ({ payload, callback }, { call, put }) {
-      const { data, success } = yield call(update, payload);
-      if (success) {
-        yield put({ type: 'hideModal' });
-        message.success('更新成功!');
-        if (callback) {
-          callback();
-        }
-      } else {
-        message.error('更新失败!');
-      }
-    },
     * delete ({ payload, callback }, { call, put }) {
       const { data, success } = yield call(remove, payload);
       if (success) {
-        message.success('删除成功!');
-        if (callback) {
-          callback();
-        }
-      } else {
-        message.error('删除失败!');
+        if (callback) callback();
       }
     },
     * recover ({ payload, callback }, { call, put }) {
       const { data, success } = yield call(update, payload);
       if (success) {
-        message.success('恢复成功!');
-        if (callback) {
-          callback();
-        }
-      } else {
-        message.error('恢复失败!');
+        if (callback) callback();
       }
     },
   },
 
   reducers: {
-    changeLoading(state, { payload }) {
-      return { ...state, ...payload };
-    },
-
-    showModal(state, { payload }) {
-      return { ...state, ...payload, modalVisible: true };
-    },
-
-    hideModal(state) {
-      return { ...state, modalVisible: false };
+    changeLoading(state, action) {
+      return { ...state, ...action.payload };
     },
   }
 })

+ 9 - 20
src/models/course/detail.js

@@ -1,5 +1,4 @@
-import { queryOne, create, update } from '../../services/course';
-import { message } from 'antd';
+import { queryOne, create, update } from '../../services/product';
 import pathToRegexp from 'path-to-regexp';
 import { Codes } from '../../utils/config';
 
@@ -47,25 +46,15 @@ export default {
       // 创建课程,默认状态为NORMAL
       const { data, success } = yield call(create, { ...payload, status: Codes.CODE_NORMAL });
       if (success) {
-        message.success('创建成功!');
         yield put({ type: 'clearPage' });
-        if (callback) {
-          callback();
-        }
-      } else {
-        message.error('创建失败!');
+        if (callback) callback();
       }
     },
     * update ({ payload, callback }, { call, put }) {
       const { data, success } = yield call(update, payload);
       if (success) {
-        message.success('更新成功!');
         yield put({ type: 'clearPage' });
-        if (callback) {
-          callback();
-        }
-      } else {
-        message.error('更新失败!');
+        if (callback) callback();
       }
     }
   },
@@ -111,18 +100,18 @@ export default {
       return { ...state, ...payload };
     },
 
-    saveCoverImg(state, { payload: { cvImg } }) {
-      const currentItem = { ...state.currentItem, cvImg };
+    saveCoverImg(state, { payload: { coverUrl } }) {
+      const currentItem = { ...state.currentItem, coverUrl };
       return { ...state, resourceModalVisible: false, currentItem };
     },
 
-    saveBackgroundImg(state, { payload: { bgImg } }) {
-      const currentItem = { ...state.currentItem, bgImg };
+    saveBackgroundImg(state, { payload: { bgUrl } }) {
+      const currentItem = { ...state.currentItem, bgUrl };
       return { ...state, resourceModalVisible: false, currentItem };
     },
 
-    saveLessonList(state, { payload: { lessonList } }) {
-      const currentItem = { ...state.currentItem, lessonList };
+    saveLessonList(state, { payload: { subItemList } }) {
+      const currentItem = { ...state.currentItem, subItemList };
       return { ...state, lessonModalVisible: false, currentItem };
     },
 

+ 110 - 0
src/models/goods/detail.js

@@ -0,0 +1,110 @@
+import { queryOne, createMerchantProduct, update } from '../../services/goods';
+import pathToRegexp from 'path-to-regexp';
+import queryString from 'query-string';
+import { Codes } from '../../utils/config';
+
+export default {
+  namespace: 'goodsDetail',
+
+  state: {
+    filters: {},
+    currentItem: {},
+    itemLoading: false,
+  },
+
+  subscriptions: {
+    setup({ dispatch, history }) {
+      history.listen(({ pathname, state, search, ...rest }) => {
+        const match = pathToRegexp('/goods/edit').exec(pathname);
+        if (match) {
+          const params = queryString.parse(search);
+          dispatch({ type: 'queryOne', payload: { ...params } });
+          dispatch({ type: 'saveFilters', payload: state });
+        }
+        if (pathname === '/goods/add') {
+          dispatch({ type: 'saveFilters', payload: state });
+        }
+      });
+    }
+  },
+
+  effects: {
+    // 查询一条渠道方产品详情 /merchant/product/detail?pid=xxx&merchantId=xxx
+    * queryOne ({ payload }, { call, put }) {
+      yield put({ type: 'changeLoading', payload: { itemLoading: true } });
+      const { data, success } = yield call(queryOne, payload);
+      if (success) {
+        yield put({ type: 'querySuccess', payload: { ...data } });
+      }
+      yield put({ type: 'changeLoading', payload: { itemLoading: false } });
+    },
+    // 创建渠道方产品 {pid:'xxx', merchantId:'xxx', status:'NORMAL'}
+    * createMerchantProduct ({ payload, callback }, { call, put }) {
+      const { data, success } = yield call(createMerchantProduct, payload);
+      if (success) {
+        yield put({ type: 'clearPage' });
+        if (callback) callback();
+      }
+    },
+    // 给产品挂载标签
+    * addTagsToMerchantProduct () {
+
+    },
+    // 定价过程 - 创建多个商品
+    * createGoods () {
+
+    },
+    // // 更新该产品,挂载标签/产品定价
+    // * updateItem ({ payload, callback }, { call, put }) {
+    //   const { data, success } = yield call(update, payload);
+    //   if (success) {
+    //     yield put({ type: 'clearPage' });
+    //     if (callback) callback();
+    //   }
+    // },
+  },
+
+  reducers: {
+    changeLoading(state, { payload }) {
+      return { ...state, ...payload };
+    },
+
+    querySuccess(state, { payload }) {
+      return { ...state, currentItem: payload };
+    },
+
+    saveFilters(state, { payload: filters }) {
+      return { ...state, filters };
+    },
+
+    showSupportModal(state, { payload }) {
+      return { ...state, ...payload, supportModalVisible: true };
+    },
+
+    hideSupportModal(state) {
+      return { ...state, supportModalVisible: false };
+    },
+
+    showResourceModal(state, { payload }) {
+      return { ...state, ...payload, resourceModalVisible: true };
+    },
+
+    hideResourceModal(state) {
+      return { ...state, resourceModalVisible: false };
+    },
+
+    saveSupportList(state, { payload: { supportList } }) {
+      const currentItem = { ...state.currentItem, supportList };
+      return { ...state, supportModalVisible: false, currentItem };
+    },
+
+    saveImgList(state, { payload: { imgList } }) {
+      const currentItem = { ...state.currentItem, imgList };
+      return { ...state, resourceModalVisible: false, currentItem };
+    },
+
+    clearPage(state) {
+      return { ...state, currentItem: {}, itemLoading: false };
+    }
+  }
+}

+ 68 - 0
src/models/goods/goods.js

@@ -0,0 +1,68 @@
+import { query, update, createMerchantProduct } from '../../services/goods';
+import modelExtend from 'dva-model-extend';
+import queryString from 'query-string';
+import { message } from 'antd';
+import { pageModel } from '../common';
+import { pageSize, Codes } from '../../utils/config';
+import { checkSearchParams } from '../../utils/utils';
+
+export default modelExtend(pageModel, {
+  namespace: 'goods',
+
+  state: { listLoading: false },
+
+  subscriptions: {
+    setup({ dispatch, history }) {
+      history.listen((location) => {
+        if (location.pathname === '/goods') {
+          const payload = checkSearchParams(queryString.parse(location.search));
+          dispatch({
+            type: 'query',
+            payload,
+          });
+        }
+      });
+    }
+  },
+
+  effects: {
+    * query ({ payload = {} }, { call, put }) {
+      yield put({ type: 'changeLoading', payload: { listLoading: true }});
+      const { data, success } = yield call(query, payload);
+      if (success) {
+        yield put({
+          type: 'querySuccess',
+          payload: {
+            list: data.list,
+            pagination: {
+              current: Number(payload.pageNo) || 1,
+              pageSize: Number(payload.pageSize) || pageSize,
+              total: data.totalSize,
+            }
+          }
+        });
+      }
+      yield put({ type: 'changeLoading', payload: { listLoading: false }});
+    },
+    // 商品上架 - {status: NORMAL, merchantId: 'xxx', pid: 'xxx'}
+    * putOnSale ({ payload, callback }, { call, put }) {
+      const { data, success } = yield call(createMerchantProduct, { ...payload, status: Codes.CODE_NORMAL });
+      if (success) {
+        if (callback) callback();
+      }
+    },
+    // 商品下架 - {status: 'DEL', merchantId: 'xxx', pid: 'xxx'}
+    * putOffSale ({ payload, callback }, { call, put }) {
+      const { data, success } = yield call(createMerchantProduct, { ...payload, status: Codes.CODE_DELETE });
+      if (success) {
+        if (callback) callback();
+      }
+    },
+  },
+
+  reducers: {
+    changeLoading(state, { payload }) {
+      return { ...state, ...payload };
+    },
+  }
+})

+ 13 - 19
src/models/group/detail.js

@@ -1,6 +1,7 @@
 import { queryOne, create, update } from '../../services/group';
-import { message } from 'antd';
 import pathToRegexp from 'path-to-regexp';
+import { message } from 'antd';
+import { Codes } from '../../utils/config';
 
 export default {
   namespace: 'groupDetail',
@@ -16,14 +17,15 @@ export default {
   subscriptions: {
     setup({ dispatch, history }) {
       history.listen(({ pathname, state }) => {
-        const match = pathToRegexp('/tag/group/edit/:id').exec(pathname);
+        const match = pathToRegexp('/tag/tagGroup/edit/:id').exec(pathname);
         if (match) {
           dispatch({ type: 'query', payload: { id: match[1] } });
           dispatch({ type: 'saveFilters', payload: state });
           dispatch({ type: 'saveOperType', payload: { operType: 'update' } });
         }
-        if (pathname === '/tag/group/add') {
+        if (pathname === '/tag/tagGroup/add') {
           dispatch({ type: 'saveFilters', payload: state });
+          dispatch({ type: 'saveOperType', payload: { operType: 'create' } });
         }
       });
     }
@@ -39,29 +41,21 @@ export default {
       yield put({ type: 'changeLoading', payload: { itemLoading: false } });
     },
     * create ({ payload, callback }, { call, put }) {
-      const { data, success } = yield call(create, payload);
+      const { data, success } = yield call(create, { ...payload, status: Codes.CODE_NORMAL });
       if (success) {
         message.success('创建成功!');
-        yield put({ type: 'clearPage' });
-        if (callback) {
-          callback();
-        }
-      } else {
-        message.error('创建失败!');
+        yield put({ type: 'initState' });
+        if (callback) callback();
       }
     },
     * update ({ payload, callback }, { call, put }) {
       const { data, success } = yield call(update, payload);
       if (success) {
-        message.success('更新成功!');
-        yield put({ type: 'clearPage' });
-        if (callback) {
-          callback();
-        }
-      } else {
-        message.error('更新失败!');
+        message.success('修改成功!');
+        yield put({ type: 'initState' });
+        if (callback) callback();
       }
-    },
+    }
   },
 
   reducers: {
@@ -94,7 +88,7 @@ export default {
       return { ...state, modalVisible: false, currentItem };
     },
 
-    clearPage(state) {
+    initState(state) {
       return { ...state, currentItem: {}, itemLoading: false };
     }
   }

+ 6 - 51
src/models/group/group.js

@@ -3,24 +3,18 @@ import modelExtend from 'dva-model-extend';
 import queryString from 'query-string';
 import { message } from 'antd';
 import { pageModel } from '../common';
-import config from '../../utils/config';
+import { pageSize } from '../../utils/config';
 import { checkSearchParams } from '../../utils/utils';
 
 export default modelExtend(pageModel, {
   namespace: 'group',
 
-  state: {
-    currentItem: {},
-    itemLoading: false,
-    listLoading: false,
-    modalVisible: false,
-    modalType: 'create',
-  },
+  state: { listLoading: false },
 
   subscriptions: {
     setup({ dispatch, history }) {
       history.listen((location) => {
-        if (location.pathname === '/tag/group') {
+        if (location.pathname === '/tag/tagGroup') {
           const payload = checkSearchParams(queryString.parse(location.search));
           dispatch({
             type: 'query',
@@ -42,7 +36,7 @@ export default modelExtend(pageModel, {
             list: data.list,
             pagination: {
               current: Number(payload.pageNo) || 1,
-              pageSize: Number(payload.pageSize) || config.pageSize,
+              pageSize: Number(payload.pageSize) || pageSize,
               total: data.totalSize,
             }
           }
@@ -50,49 +44,18 @@ export default modelExtend(pageModel, {
       }
       yield put({ type: 'changeLoading', payload: { listLoading: false }});
     },
-    * create ({ payload, callback }, { call, put }) {
-      const { data, success } = yield call(create, payload);
-      if (success) {
-        message.success('创建成功!');
-        if (callback) {
-          callback();
-        }
-      } else {
-        message.error('创建失败!');
-      }
-    },
-    * update ({ payload, callback }, { call, put }) {
-      const { data, success } = yield call(update, payload);
-      if (success) {
-        yield put({ type: 'hideModal' });
-        message.success('更新成功!');
-        if (callback) {
-          callback();
-        }
-      } else {
-        message.error('更新失败!');
-      }
-    },
     * delete ({ payload, callback }, { call, put }) {
       const { data, success } = yield call(remove, payload);
       if (success) {
         message.success('删除成功!');
-        if (callback) {
-          callback();
-        }
-      } else {
-        message.error('删除失败!');
+        if (callback) callback();
       }
     },
     * recover ({ payload, callback }, { call, put }) {
       const { data, success } = yield call(update, payload);
       if (success) {
         message.success('恢复成功!');
-        if (callback) {
-          callback();
-        }
-      } else {
-        message.error('恢复失败!');
+        if (callback) callback();
       }
     },
   },
@@ -101,13 +64,5 @@ export default modelExtend(pageModel, {
     changeLoading(state, { payload }) {
       return { ...state, ...payload };
     },
-
-    showModal(state, { payload }) {
-      return { ...state, ...payload, modalVisible: true };
-    },
-
-    hideModal(state) {
-      return { ...state, modalVisible: false };
-    },
   }
 })

+ 2 - 14
src/models/lesson/detail.js

@@ -1,5 +1,4 @@
 import { queryOne, create, update } from '../../services/lesson';
-import { message } from 'antd';
 import pathToRegexp from 'path-to-regexp';
 import { Codes } from '../../utils/config';
 
@@ -45,25 +44,15 @@ export default {
       // 创建课,默认状态为NORMAL
       const { data, success } = yield call(create, { ...payload, status: Codes.CODE_NORMAL });
       if (success) {
-        message.success('创建成功!');
         yield put({ type: 'clearPage' });
-        if (callback) {
-          callback();
-        }
-      } else {
-        message.error('创建失败!');
+        if (callback) callback();
       }
     },
     * update ({ payload, callback }, { call, put }) {
       const { data, success } = yield call(update, payload);
       if (success) {
-        message.success('更新成功!');
         yield put({ type: 'clearPage' });
-        if (callback) {
-          callback();
-        }
-      } else {
-        message.error('更新失败!');
+        if (callback) callback();
       }
     }
   },
@@ -94,7 +83,6 @@ export default {
     },
 
     saveSortResult(state, { payload: { wareList } }) {
-      console.log(wareList);
       const currentItem = { ...state.currentItem, wareList };
       return { ...state, modalVisible: false, currentItem };
     },

+ 4 - 25
src/models/lesson/lesson.js

@@ -1,7 +1,6 @@
 import { query, create, update, remove } from '../../services/lesson';
 import modelExtend from 'dva-model-extend';
 import queryString from 'query-string';
-import { message } from 'antd';
 import { pageModel } from '../common';
 import { pageSize } from '../../utils/config';
 import { checkSearchParams } from '../../utils/utils';
@@ -53,46 +52,26 @@ export default modelExtend(pageModel, {
     * create ({ payload, callback }, { call, put }) {
       const { data, success } = yield call(create, payload);
       if (success) {
-        message.success('创建成功!');
-        if (callback) {
-          callback();
-        }
-      } else {
-        message.error('创建失败!');
+        if (callback) callback();
       }
     },
     * update ({ payload, callback }, { call, put }) {
       const { data, success } = yield call(update, payload);
       if (success) {
         yield put({ type: 'hideModal' });
-        message.success('更新成功!');
-        if (callback) {
-          callback();
-        }
-      } else {
-        message.error('更新失败!');
+        if (callback) callback();
       }
     },
     * delete ({ payload, callback }, { call, put }) {
       const { data, success } = yield call(remove, payload);
       if (success) {
-        message.success('删除成功!');
-        if (callback) {
-          callback();
-        }
-      } else {
-        message.error('删除失败!');
+        if (callback) callback();
       }
     },
     * recover ({ payload, callback }, { call, put }) {
       const { data, success } = yield call(update, payload);
       if (success) {
-        message.success('恢复成功!');
-        if (callback) {
-          callback();
-        }
-      } else {
-        message.error('恢复失败!');
+        if (callback) callback();
       }
     },
   },

+ 4 - 25
src/models/merchant/merchant.js

@@ -1,7 +1,6 @@
 import { query, create, update, remove } from '../../services/merchant';
 import modelExtend from 'dva-model-extend';
 import queryString from 'query-string';
-import { message } from 'antd';
 import { pageModel } from '../common';
 import config from '../../utils/config';
 import { checkSearchParams } from '../../utils/utils';
@@ -53,46 +52,26 @@ export default modelExtend(pageModel, {
     * create ({ payload, callback }, { call, put }) {
       const { data, success } = yield call(create, payload);
       if (success) {
-        message.success('创建成功!');
-        if (callback) {
-          callback();
-        }
-      } else {
-        message.error('创建失败!');
+        if (callback) callback();
       }
     },
     * update ({ payload, callback }, { call, put }) {
       const { data, success } = yield call(update, payload);
       if (success) {
         yield put({ type: 'hideModal' });
-        message.success('更新成功!');
-        if (callback) {
-          callback();
-        }
-      } else {
-        message.error('更新失败!');
+        if (callback) callback();
       }
     },
     * delete ({ payload, callback }, { call, put }) {
       const { data, success } = yield call(remove, payload);
       if (success) {
-        message.success('删除成功!');
-        if (callback) {
-          callback();
-        }
-      } else {
-        message.error('删除失败!');
+        if (callback) callback();
       }
     },
     * recover ({ payload, callback }, { call, put }) {
       const { data, success } = yield call(update, payload);
       if (success) {
-        message.success('恢复成功!');
-        if (callback) {
-          callback();
-        }
-      } else {
-        message.error('恢复失败!');
+        if (callback) callback();
       }
     },
   },

+ 2 - 13
src/models/order/detail.js

@@ -1,5 +1,4 @@
 import { queryOne, create, update } from '../../services/order';
-import { message } from 'antd';
 import pathToRegexp from 'path-to-regexp';
 import { Codes } from '../../utils/config';
 
@@ -39,25 +38,15 @@ export default {
     * create ({ payload, callback }, { call, put }) {
       const { data, success } = yield call(create, { ...payload });
       if (success) {
-        message.success('创建成功!');
         yield put({ type: 'clearPage' });
-        if (callback) {
-          callback();
-        }
-      } else {
-        message.error('创建失败!');
+        if (callback) callback();
       }
     },
     * update ({ payload, callback }, { call, put }) {
       const { data, success } = yield call(update, payload);
       if (success) {
-        message.success('更新成功!');
         yield put({ type: 'clearPage' });
-        if (callback) {
-          callback();
-        }
-      } else {
-        message.error('更新失败!');
+        if (callback) callback();
       }
     }
   },

+ 4 - 25
src/models/order/order.js

@@ -1,7 +1,6 @@
 import { query, create, update, remove } from '../../services/order';
 import modelExtend from 'dva-model-extend';
 import queryString from 'query-string';
-import { message } from 'antd';
 import { pageModel } from '../common';
 import { pageSize } from '../../utils/config';
 import { checkSearchParams } from '../../utils/utils';
@@ -53,46 +52,26 @@ export default modelExtend(pageModel, {
     * create ({ payload, callback }, { call, put }) {
       const { data, success } = yield call(create, payload);
       if (success) {
-        message.success('创建成功!');
-        if (callback) {
-          callback();
-        }
-      } else {
-        message.error('创建失败!');
+        if (callback) callback();
       }
     },
     * update ({ payload, callback }, { call, put }) {
       const { data, success } = yield call(update, payload);
       if (success) {
         yield put({ type: 'hideModal' });
-        message.success('更新成功!');
-        if (callback) {
-          callback();
-        }
-      } else {
-        message.error('更新失败!');
+        if (callback) callback();
       }
     },
     * delete ({ payload, callback }, { call, put }) {
       const { data, success } = yield call(remove, payload);
       if (success) {
-        message.success('作废成功!');
-        if (callback) {
-          callback();
-        }
-      } else {
-        message.error('作废失败!');
+        if (callback) callback();
       }
     },
     * recover ({ payload, callback }, { call, put }) {
       const { data, success } = yield call(update, payload);
       if (success) {
-        message.success('恢复成功!');
-        if (callback) {
-          callback();
-        }
-      } else {
-        message.error('恢复失败!');
+        if (callback) callback();
       }
     },
   },

+ 4 - 25
src/models/resource.js

@@ -1,5 +1,4 @@
 import { query, remove, update, create, getSignature } from '../services/resource';
-import { message } from 'antd';
 import modelExtend from 'dva-model-extend';
 import queryString from 'query-string';
 import { pageModel } from './common';
@@ -64,46 +63,26 @@ export default modelExtend(pageModel, {
       const { data, success } = yield call(create, payload);
       if (success) {
         yield put({ type: 'hideModal' });
-        message.success('创建成功!');
-        if (callback) {
-          callback();
-        }
-      } else {
-        message.error('创建失败!');
+        if (callback) callback();
       }
     },
     * update ({ payload, callback }, { call, put }) {
       const { data, success } = yield call(update, payload);
       if (success) {
         yield put({ type: 'hideModal' });
-        message.success('更新成功!');
-        if (callback) {
-          callback();
-        }
-      } else {
-        message.error('更新失败!');
+        if (callback) callback();
       }
     },
     * delete ({ payload, callback }, { call, put }) {
       const { data, success } = yield call(remove, payload);
       if (success) {
-        message.success('删除成功!');
-        if (callback) {
-          callback();
-        }
-      } else {
-        message.error('删除失败!');
+        if (callback) callback();
       }
     },
     * recover ({ payload, callback }, { call, put }) {
       const { data, success } = yield call(update, payload);
       if (success) {
-        message.success('恢复成功!');
-        if (callback) {
-          callback();
-        }
-      } else {
-        message.error('恢复失败!');
+        if (callback) callback();
       }
     },
   },

+ 7 - 18
src/models/support/detail.js

@@ -1,5 +1,4 @@
 import { queryOne, create, update } from '../../services/support';
-import { message } from 'antd';
 import pathToRegexp from 'path-to-regexp';
 import { Codes } from '../../utils/config';
 
@@ -43,28 +42,18 @@ export default {
       yield put({ type: 'changeLoading', payload: { itemLoading: false } });
     },
     * create ({ payload, callback }, { call, put }) {
-      // 创建课程,默认状态为NORMAL
+      // 创建配套,默认状态为NORMAL
       const { data, success } = yield call(create, { ...payload, status: Codes.CODE_NORMAL });
       if (success) {
-        message.success('创建成功!');
         yield put({ type: 'clearPage' });
-        if (callback) {
-          callback();
-        }
-      } else {
-        message.error('创建失败!');
+        if (callback) callback();
       }
     },
     * update ({ payload, callback }, { call, put }) {
       const { data, success } = yield call(update, payload);
       if (success) {
-        message.success('更新成功!');
         yield put({ type: 'clearPage' });
-        if (callback) {
-          callback();
-        }
-      } else {
-        message.error('更新失败!');
+        if (callback) callback();
       }
     }
   },
@@ -98,10 +87,6 @@ export default {
       return { ...state, resourceModalVisible: false };
     },
 
-    saveOperType(state, { payload }) {
-      return { ...state, ...payload };
-    },
-
     saveSupportList(state, { payload: { supportList } }) {
       const currentItem = { ...state.currentItem, supportList };
       return { ...state, supportModalVisible: false, currentItem };
@@ -112,6 +97,10 @@ export default {
       return { ...state, resourceModalVisible: false, currentItem };
     },
 
+    saveOperType(state, { payload }) {
+      return { ...state, ...payload };
+    },
+
     clearPage(state) {
       return { ...state, currentItem: {}, itemLoading: false };
     }

+ 7 - 48
src/models/support/support.js

@@ -1,30 +1,22 @@
-import { query, create, update, remove } from '../../services/support';
+import { query, update, remove } from '../../services/product';
 import modelExtend from 'dva-model-extend';
 import queryString from 'query-string';
-import { message } from 'antd';
 import { pageModel } from '../common';
 import { pageSize } from '../../utils/config';
 import { checkSearchParams } from '../../utils/utils';
+import { Codes } from '../../utils/config';
 
 export default modelExtend(pageModel, {
   namespace: 'support',
 
-  state: {
-    currentItem: {},
-    itemLoading: false,
-    listLoading: false,
-    modalType: 'create',
-  },
+  state: { listLoading: false },
 
   subscriptions: {
     setup({ dispatch, history }) {
       history.listen((location) => {
         if (location.pathname === '/product/support') {
           const payload = checkSearchParams(queryString.parse(location.search));
-          dispatch({
-            type: 'query',
-            payload,
-          });
+          dispatch({ type: 'query', payload });
         }
       });
     }
@@ -33,7 +25,7 @@ export default modelExtend(pageModel, {
   effects: {
     * query ({ payload = {} }, { call, put }) {
       yield put({ type: 'changeLoading', payload: { listLoading: true }});
-      const { data, success } = yield call(query, payload);
+      const { data, success } = yield call(query, { ...payload, type: Codes.CODE_SUPPORT });
       if (success) {
         yield put({
           type: 'querySuccess',
@@ -49,49 +41,16 @@ export default modelExtend(pageModel, {
       }
       yield put({ type: 'changeLoading', payload: { listLoading: false }});
     },
-    * create ({ payload, callback }, { call, put }) {
-      const { data, success } = yield call(create, payload);
-      if (success) {
-        message.success('创建成功!');
-        if (callback) {
-          callback();
-        }
-      } else {
-        message.error('创建失败!');
-      }
-    },
-    * update ({ payload, callback }, { call, put }) {
-      const { data, success } = yield call(update, payload);
-      if (success) {
-        yield put({ type: 'hideModal' });
-        message.success('更新成功!');
-        if (callback) {
-          callback();
-        }
-      } else {
-        message.error('更新失败!');
-      }
-    },
     * delete ({ payload, callback }, { call, put }) {
       const { data, success } = yield call(remove, payload);
       if (success) {
-        message.success('删除成功!');
-        if (callback) {
-          callback();
-        }
-      } else {
-        message.error('删除失败!');
+        if (callback) callback();
       }
     },
     * recover ({ payload, callback }, { call, put }) {
       const { data, success } = yield call(update, payload);
       if (success) {
-        message.success('恢复成功!');
-        if (callback) {
-          callback();
-        }
-      } else {
-        message.error('恢复失败!');
+        if (callback) callback();
       }
     },
   },

+ 11 - 18
src/models/tag/detail.js

@@ -1,6 +1,6 @@
 import { queryOne, create, update } from '../../services/tag';
-import { message } from 'antd';
 import pathToRegexp from 'path-to-regexp';
+import { message } from 'antd';
 import { Codes } from '../../utils/config';
 
 export default {
@@ -17,14 +17,15 @@ export default {
   subscriptions: {
     setup({ dispatch, history }) {
       history.listen(({ pathname, state, ...rest }) => {
-        const match = pathToRegexp('/tag/tag/edit/:id').exec(pathname);
+        const match = pathToRegexp('/tag/tagItem/edit/:id').exec(pathname);
         if (match) {
           dispatch({ type: 'query', payload: { id: match[1] } });
           dispatch({ type: 'saveFilters', payload: state });
           dispatch({ type: 'saveOperType', payload: { operType: 'update' } });
         }
-        if (pathname === '/tag/tag/add') {
+        if (pathname === '/tag/tagItem/add') {
           dispatch({ type: 'saveFilters', payload: state });
+          dispatch({ type: 'saveOperType', payload: { operType: 'create' } });
         }
       });
     }
@@ -40,27 +41,19 @@ export default {
       yield put({ type: 'changeLoading', payload: { itemLoading: false } });
     },
     * create ({ payload, callback }, { call, put }) {
-      const { data, success } = yield call(create, payload);
+      const { data, success } = yield call(create, { ...payload, status: Codes.CODE_NORMAL });
       if (success) {
         message.success('创建成功!');
-        yield put({ type: 'clearPage' });
-        if (callback) {
-          callback();
-        }
-      } else {
-        message.error('创建失败!');
+        yield put({ type: 'initState' });
+        if (callback) callback();
       }
     },
     * update ({ payload, callback }, { call, put }) {
       const { data, success } = yield call(update, payload);
       if (success) {
-        message.success('更新成功!');
-        yield put({ type: 'clearPage' });
-        if (callback) {
-          callback();
-        }
-      } else {
-        message.error('更新失败!');
+        message.success('修改成功!');
+        yield put({ type: 'initState' });
+        if (callback) callback();
       }
     },
   },
@@ -95,7 +88,7 @@ export default {
       return { ...state, modalVisible: false, currentItem };
     },
 
-    clearPage(state) {
+    initState(state) {
       return { ...state, currentItem: {}, itemLoading: false };
     }
   }

+ 5 - 50
src/models/tag/tag.js

@@ -1,4 +1,4 @@
-import { query, create, update, remove } from '../../services/tag';
+import { query, update, remove } from '../../services/tag';
 import modelExtend from 'dva-model-extend';
 import queryString from 'query-string';
 import { message } from 'antd';
@@ -9,18 +9,12 @@ import { checkSearchParams } from '../../utils/utils';
 export default modelExtend(pageModel, {
   namespace: 'tag',
 
-  state: {
-    currentItem: {},
-    itemLoading: false,
-    listLoading: false,
-    modalVisible: false,
-    modalType: 'create',
-  },
+  state: { listLoading: false },
 
   subscriptions: {
     setup({ dispatch, history }) {
       history.listen((location) => {
-        if (location.pathname === '/tag/tag') {
+        if (location.pathname === '/tag/tagItem') {
           const payload = checkSearchParams(queryString.parse(location.search));
           dispatch({
             type: 'query',
@@ -50,49 +44,18 @@ export default modelExtend(pageModel, {
       }
       yield put({ type: 'changeLoading', payload: { listLoading: false }});
     },
-    * create ({ payload, callback }, { call, put }) {
-      const { data, success } = yield call(create, payload);
-      if (success) {
-        message.success('创建成功!');
-        if (callback) {
-          callback();
-        }
-      } else {
-        message.error('创建失败!');
-      }
-    },
-    * update ({ payload, callback }, { call, put }) {
-      const { data, success } = yield call(update, payload);
-      if (success) {
-        yield put({ type: 'hideModal' });
-        message.success('更新成功!');
-        if (callback) {
-          callback();
-        }
-      } else {
-        message.error('更新失败!');
-      }
-    },
     * delete ({ payload, callback }, { call, put }) {
       const { data, success } = yield call(remove, payload);
       if (success) {
         message.success('删除成功!');
-        if (callback) {
-          callback();
-        }
-      } else {
-        message.error('删除失败!');
+        if (callback) callback();
       }
     },
     * recover ({ payload, callback }, { call, put }) {
       const { data, success } = yield call(update, payload);
       if (success) {
         message.success('恢复成功!');
-        if (callback) {
-          callback();
-        }
-      } else {
-        message.error('恢复失败!');
+        if (callback) callback();
       }
     },
   },
@@ -101,13 +64,5 @@ export default modelExtend(pageModel, {
     changeLoading(state, { payload }) {
       return { ...state, ...payload };
     },
-
-    showModal(state, { payload }) {
-      return { ...state, ...payload, modalVisible: true };
-    },
-
-    hideModal(state) {
-      return { ...state, modalVisible: false };
-    },
   }
 })

+ 2 - 14
src/models/ware/detail.js

@@ -1,5 +1,4 @@
 import { queryOne, create, update } from '../../services/ware';
-import { message } from 'antd';
 import pathToRegexp from 'path-to-regexp';
 import { Codes } from '../../utils/config';
 
@@ -42,28 +41,17 @@ export default {
       yield put({ type: 'changeLoading', payload: { itemLoading: false } });
     },
     * create ({ payload, callback }, { call, put }) {
-      // 创建课件,默认状态为NORMAL
       const { data, success } = yield call(create, { ...payload, status: Codes.CODE_NORMAL });
       if (success) {
-        message.success('创建成功!');
         yield put({ type: 'clearPage' });
-        if (callback) {
-          callback();
-        }
-      } else {
-        message.error('创建失败!');
+        if (callback) callback();
       }
     },
     * update ({ payload, callback }, { call, put }) {
       const { data, success } = yield call(update, payload);
       if (success) {
-        message.success('更新成功!');
         yield put({ type: 'clearPage' });
-        if (callback) {
-          callback();
-        }
-      } else {
-        message.error('更新失败!');
+        if (callback) callback();
       }
     }
   },

+ 4 - 25
src/models/ware/ware.js

@@ -1,7 +1,6 @@
 import { query, create, update, remove } from '../../services/ware';
 import modelExtend from 'dva-model-extend';
 import queryString from 'query-string';
-import { message } from 'antd';
 import { pageModel } from '../common';
 import { pageSize } from '../../utils/config';
 import { checkSearchParams } from '../../utils/utils';
@@ -53,46 +52,26 @@ export default modelExtend(pageModel, {
     * create ({ payload, callback }, { call, put }) {
       const { data, success } = yield call(create, payload);
       if (success) {
-        message.success('创建成功!');
-        if (callback) {
-          callback();
-        }
-      } else {
-        message.error('创建失败!');
+        if (callback) callback();
       }
     },
     * update ({ payload, callback }, { call, put }) {
       const { data, success } = yield call(update, payload);
       if (success) {
         yield put({ type: 'hideModal' });
-        message.success('更新成功!');
-        if (callback) {
-          callback();
-        }
-      } else {
-        message.error('更新失败!');
+        if (callback) callback();
       }
     },
     * delete ({ payload, callback }, { call, put }) {
       const { data, success } = yield call(remove, payload);
       if (success) {
-        message.success('删除成功!');
-        if (callback) {
-          callback();
-        }
-      } else {
-        message.error('删除失败!');
+        if (callback) callback();
       }
     },
     * recover ({ payload, callback }, { call, put }) {
       const { data, success } = yield call(update, payload);
       if (success) {
-        message.success('恢复成功!');
-        if (callback) {
-          callback();
-        }
-      } else {
-        message.error('恢复失败!');
+        if (callback) callback();
       }
     },
   },

+ 1 - 1
src/routes/CmsUser/table.js

@@ -52,7 +52,7 @@ const TableList = ({ onDeleteItem, onEditItem, onRecoverItem, location, curStatu
     dataIndex: 'mobile',
     key: 'mobile',
   },{
-    title: '平台',
+    title: '类型',
     dataIndex: 'domain',
     key: 'domain',
     render: (text, record) => domains[record.domain]

+ 94 - 20
src/routes/Course/detail/index.js

@@ -5,9 +5,9 @@ import queryString from 'query-string';
 import { connect } from 'dva';
 import { Spin, Popover, Badge, Table, Radio, Card, Form, Input, Icon, Button, Select } from 'antd';
 import PageHeaderLayout from '../../../layouts/PageHeaderLayout';
-import LessonSelectSortModal from './lessonModal';
-import ResourceSelectModal from './resourceModal';
-import SupportSelectSortModal from './supportModal';
+import LessonSelectSortModal from './lesson';
+import ResourceSelectModal from './resource';
+import SupportSelectSortModal from './support';
 import { Codes } from '../../../utils/config';
 
 const FormItem = Form.Item;
@@ -21,12 +21,25 @@ const { TextArea } = Input;
   resource: state.resource,
   lesson: state.lesson,
   support: state.support,
+  merchant: state.merchant,
 }))
 export default class CourseDetail extends PureComponent {
   state = {
     curClickedBtn: null,       //记录点击的按钮
   };
 
+  componentDidMount() {
+    const { dispatch } = this.props;
+    dispatch({
+      type: 'merchant/query',
+      payload: {
+        pageSize: 1000,
+        pageNo: 1,
+        domain: Codes.CODE_CP,
+      }
+    });
+  }
+
   // 展示选择模态框 - 加载第一页数据
   handleModalShow = (btnName) => {
     this.setState({
@@ -88,17 +101,17 @@ export default class CourseDetail extends PureComponent {
     if (curClickedBtn === 'lessonBtn') {
       dispatch({
         type: 'courseDetail/saveLessonList',
-        payload: { lessonList: data }
+        payload: { subItemList: data }
       });
     } else if (curClickedBtn === 'cvImgBtn') {
       dispatch({
         type: 'courseDetail/saveCoverImg',
-        payload: { cvImg: data },
+        payload: { coverUrl: data.url },
       });
     } else if (curClickedBtn === 'bgImgBtn') {
       dispatch({
         type: 'courseDetail/saveBackgroundImg',
-        payload: { bgImg: data },
+        payload: { bgUrl: data.url },
       });
     } else if (curClickedBtn === 'supportBtn') {
       dispatch({
@@ -183,10 +196,33 @@ export default class CourseDetail extends PureComponent {
     } = this.props;
     validateFields((errors) => {
       if (errors) { return; }
+
+      // 过滤掉gmtCreated, gmtModified, status三个字段
+      const { subItemList, supportList, gmtCreated, gmtModified, status, ...rest } = currentItem;
+      let newSubItemList;
+      let newSupportList;
+      // 如果subItemList是个list且内容不为空
+      if (Array.isArray(subItemList) && subItemList.length) {
+        newSubItemList = subItemList.map(item => ({ id: item.id, type: Codes.CODE_LESSON }));
+      } else {
+        newSubItemList = [];
+      }
+      // 如果supportList是个list且内容不为空
+      if (Array.isArray(supportList) && supportList.length) {
+        newSupportList = supportList.map(item => ({ id: item.id, type: Codes.CODE_SUPPORT}));
+      } else {
+        newSupportList = [];
+      }
+
+      // 最终要提交的数据
       const data = {
-        ...currentItem,
+        ...rest,
         ...getFieldsValue(),
+        subItemList: newSubItemList,
+        supportList: newSupportList,
       };
+
+      // 操作成功要返回列表页,同时要保持列表页的过滤数据
       dispatch({
         type: `courseDetail/${operType}`,
         payload: data,
@@ -205,7 +241,6 @@ export default class CourseDetail extends PureComponent {
 
   handlePageCancel = () => {
     const { dispatch, courseDetail: { filters } } = this.props;
-    dispatch({ type: 'courseDetail/clearPage' });
     dispatch(
       routerRedux.push({
         pathname: '/product/course',
@@ -215,9 +250,36 @@ export default class CourseDetail extends PureComponent {
   }
 
   render() {
-    const { dispatch, form: { getFieldDecorator }, courseDetail, lesson, resource, support } = this.props;
-    const { itemLoading, currentItem, filters, lessonModalVisible, supportModalVisible, resourceModalVisible } = courseDetail;
-    const { lessonList = [], supportList = [], name, code, digest, cvImg = {}, bgImg = {} } = currentItem;
+    const {
+      dispatch,
+      courseDetail,
+      lesson,
+      resource,
+      support,
+      merchant,
+      form: {
+        getFieldDecorator
+      },
+    } = this.props;
+    const {
+      itemLoading,
+      currentItem,
+      filters,
+      lessonModalVisible,
+      supportModalVisible,
+      resourceModalVisible
+    } = courseDetail;
+    const {
+      name,
+      code,
+      digest,
+      detail,
+      cpId,
+      coverUrl,
+      bgUrl,
+      subItemList = [],
+      supportList = [],
+    } = currentItem;
 
     // 待选表格去掉分页的跳转及变换页码
     if (resource && resource.pagination) {
@@ -277,34 +339,46 @@ export default class CourseDetail extends PureComponent {
                   initialValue: name,
                 })(<Input />)}
               </FormItem>
-              <FormItem label="课程简述:" hasFeedback {...formItemLayout}>
+              <FormItem label="课程概要:" hasFeedback {...formItemLayout}>
                 {getFieldDecorator('digest', {
                   initialValue: digest,
                 })(<TextArea />)}
               </FormItem>
+              <FormItem label="课程详情:" hasFeedback {...formItemLayout}>
+                {getFieldDecorator('detail', {
+                  initialValue: detail,
+                })(<TextArea />)}
+              </FormItem>
+              <FormItem label="所属供应商:" {...formItemLayout}>
+                {getFieldDecorator('cpId', {
+                  initialValue: cpId,
+                })(
+                  <Select placeholder="请选择">{merchant.list.map(item => <Option value={item.id} key={item.id}>{item.name}</Option>)}</Select>
+                )}
+              </FormItem>
               <FormItem label="封面图片" {...formItemLayout}>
                 <Button onClick={() => this.handleModalShow('cvImgBtn')} type="primary" icon="select" size="small">选择</Button>
-                {cvImg.url === undefined ? null :
+                {coverUrl === undefined ? null :
                   <Card
                     hoverable
                     bordered
-                    cover={<img alt="图片加载失败" src={cvImg.url} />}
+                    cover={<img alt="" src={coverUrl} />}
                     style={{ width: 240, marginTop: 20 }}
                   >
                   </Card>}
               </FormItem>
               <FormItem label="背景图片" {...formItemLayout}>
                 <Button onClick={() => this.handleModalShow('bgImgBtn')} type="primary" icon="select" size="small">选择</Button>
-                {bgImg.url === undefined ? null :
+                {bgUrl === undefined ? null :
                   <Card
                   hoverable
                   bordered
-                  cover={<img alt="图片加载失败" src={bgImg.url} />}
+                  cover={<img alt="" src={bgUrl} />}
                   style={{ width: 240, marginTop: 20 }}
                 >
                 </Card>}
               </FormItem>
-              <FormItem label="选择课" {...formItemLayout}>
+              <FormItem label="相关课" {...formItemLayout}>
                 <Button onClick={() => this.handleModalShow('lessonBtn')} type="primary" size="small" icon="edit">编辑</Button>
               </FormItem>
               <FormItem wrapperCol={{ offset: 7, span: 12 }}>
@@ -313,7 +387,7 @@ export default class CourseDetail extends PureComponent {
                     emptyText: <span style={{ color: "#C6D0D6" }}>&nbsp;&nbsp;<Icon type="frown-o"/>
                       该课程下不包含任何课,请选择!</span>
                   }}
-                  dataSource={lessonList}
+                  dataSource={subItemList}
                   columns={lessonTableColumns}
                   rowKey={record => record.id}
                   bordered
@@ -350,7 +424,7 @@ export default class CourseDetail extends PureComponent {
               onCancel={this.handleModalCancel}
               onOk={this.handleModalOk}
               onSearch={this.handleModalSearch}
-              selTableData={lessonList}
+              selTableData={subItemList || []}
               fsTableDataSource={lesson.list || []}
               fsTableLoading={lesson.listLoading}
               fsTablePagination={lesson.pagination}
@@ -379,7 +453,7 @@ export default class CourseDetail extends PureComponent {
               onCancel={this.handleModalCancel}
               onOk={this.handleModalOk}
               onSearch={this.handleModalSearch}
-              selTableData={supportList}
+              selTableData={supportList || []}
               fsTableDataSource={support.list || []}
               fsTableLoading={support.listLoading}
               fsTablePagination={support.pagination}

+ 1 - 1
src/routes/Course/detail/lessonModal.js

@@ -2,7 +2,7 @@ import React, { PureComponent } from 'react';
 import PropTypes from 'prop-types';
 import { Badge, Popover, Icon } from 'antd';
 import SelectModal from '../../../components/SelectModal';
-import styles from './lessonModal.less';
+import styles from './lesson.less';
 import { Codes, resourceType } from '../../../utils/config';
 
 export default class LessonSelectSortModal extends PureComponent {

src/routes/Course/detail/lessonModal.less → src/routes/Course/Edit/lesson.less


+ 1 - 1
src/routes/Course/detail/resourceModal.js

@@ -2,7 +2,7 @@ import React, { PureComponent } from 'react';
 import PropTypes from 'prop-types';
 import { Badge, Popover, Icon } from 'antd';
 import SelectModal from '../../../components/SelectModal';
-import styles from './resourceModal.less';
+import styles from './resource.less';
 import { Codes } from '../../../utils/config';
 
 export default class ResourceSelectModal extends PureComponent {

src/routes/Course/detail/resourceModal.less → src/routes/Course/Edit/resource.less


+ 1 - 1
src/routes/Course/detail/supportModal.js

@@ -2,7 +2,7 @@ import React, { PureComponent } from 'react';
 import PropTypes from 'prop-types';
 import { Badge, Popover, Icon } from 'antd';
 import SelectModal from '../../../components/SelectModal';
-import styles from './supportModal.less';
+import styles from './support.less';
 import { Codes } from '../../../utils/config';
 
 export default class SupportSelectSortModal extends PureComponent {

src/routes/Course/detail/supportModal.less → src/routes/Course/Edit/support.less


+ 2 - 3
src/routes/Course/index.js

@@ -6,8 +6,8 @@ import { routerRedux } from 'dva/router';
 import { Card } from 'antd';
 import TableList from './table';
 import Search from './search';
-import PageHeaderLayout from '../../layouts/PageHeaderLayout';
-import { Codes } from '../../utils/config';
+import PageHeaderLayout from '../../../layouts/PageHeaderLayout';
+import { Codes } from '../../../utils/config';
 
 @connect(state => ({ course: state.course }))
 export default class Course extends PureComponent {
@@ -19,7 +19,6 @@ export default class Course extends PureComponent {
 
   render() {
     const { location, dispatch, course } = this.props;
-
     location.query = queryString.parse(location.search);
     const { query, pathname } = location;
     const { field, keyword, ...filters } = query;

+ 1 - 1
src/routes/Course/search.js

@@ -1,7 +1,7 @@
 import react, { PureComponent } from 'react';
 import PropTypes from 'prop-types';
 import { Button, Form, Row, Col, Icon } from 'antd';
-import DataSearch from '../../components/DataSearch';
+import DataSearch from '../../../components/DataSearch';
 
 @Form.create()
 export default class Search extends PureComponent {

+ 2 - 2
src/routes/Course/table.js

@@ -4,9 +4,9 @@ import moment from 'moment';
 import classnames from 'classnames';
 import queryString from 'query-string';
 import { Modal, Table, Menu, Icon, Badge } from 'antd';
-import AnimTableBody from '../../components/Animation/AnimTableBody';
+import AnimTableBody from '../../../components/Animation/AnimTableBody';
 import styles from './table.less';
-import { statuses, Codes } from '../../utils/config';
+import { statuses, Codes } from '../../../utils/config';
 
 const confirm = Modal.confirm;
 

+ 1 - 1
src/routes/Course/table.less

@@ -1,5 +1,5 @@
 @import "~antd/lib/style/themes/default.less";
-@import "../../utils/utils.less";
+@import "../../../utils/utils.less";
 
 .table {
   :global {

+ 12 - 11
src/routes/Course/detail/resourceModal.js

@@ -2,10 +2,9 @@ import React, { PureComponent } from 'react';
 import PropTypes from 'prop-types';
 import { Badge, Popover, Icon } from 'antd';
 import SelectModal from '../../../components/SelectModal';
-import styles from './resourceModal.less';
 import { Codes } from '../../../utils/config';
 
-export default class ResourceSelectModal extends PureComponent {
+export default class CourseSelectModal extends PureComponent {
   static propTypes = {
     modalVisible: PropTypes.bool.isRequired,
     rowKeyName: PropTypes.string.isRequired,
@@ -15,7 +14,7 @@ export default class ResourceSelectModal extends PureComponent {
     const { modalVisible, onCancel, onOk, onSearch, ...fsTableOpts } = this.props;
 
     const modalProps = {
-      title: '选择图片',
+      title: '选择课程',
       maskClosable: false,
       visible: modalVisible,
       onCancel,
@@ -28,9 +27,9 @@ export default class ResourceSelectModal extends PureComponent {
       searchSize: 'default',
       searchSelect: true,
       searchSelectOptions: [{
-        value: 'name', name: '图片名称', mode: 'input',
+        value: 'name', name: '课程名称', mode: 'input',
       },{
-        value: 'code', name: '图片编号', mode: 'input',
+        value: 'code', name: '课程编号', mode: 'input',
       }],
       searchSelectProps: {
         defaultValue: 'name',
@@ -42,27 +41,29 @@ export default class ResourceSelectModal extends PureComponent {
 
     //待选资源Table属性
     const fsTableProps = {
-      fsTableClassName: styles.fsTable,
       fsTableColumns: [{
-        title: '缩略图',
+        title: '封面图',
         dataIndex: 'url',
         key: 'url',
         render: (text, record) => (
           <Popover
-            content={<img alt="" src={record.url} width={350} />}
+            content={<img alt="" src={record.coverUrl} width={350} />}
             title={record.name}
           >
-            <img alt="" src={record.url} width={70} />
+            <img alt="" src={record.coverUrl} width={70} />
           </Popover>
         ),
+        width: '26%',
       },{
-        title: '图片编号',
+        title: '课程编号',
         dataIndex: 'code',
         key: 'code',
+        width: '27%',
       },{
-        title: '图片名称',
+        title: '课程名称',
         dataIndex: 'name',
         key: 'name',
+        width: '27%',
       }],
       ...fsTableOpts,
     }

+ 374 - 0
src/routes/Goods/Add/index.js

@@ -0,0 +1,374 @@
+import React, { PureComponent } from 'react';
+import { routerRedux } from 'dva/router';
+import PropTypes from 'prop-types';
+import queryString from 'query-string';
+import { connect } from 'dva';
+import { Upload, Spin, Switch, Popover, Badge, Table, Radio, Card, Form, Input, Icon, Button, Select } from 'antd';
+import PageHeaderLayout from '../../../layouts/PageHeaderLayout';
+import CourseSelectModal from './course';
+import SupportSelectModal from './support';
+import PackageSelectModal from './package';
+import { Codes, productType } from '../../../utils/config';
+import styles from './index.less';
+
+const RadioGroup = Radio.Group;
+const FormItem = Form.Item;
+const Option = Select.Option;
+const { Meta } = Card;
+const { TextArea } = Input;
+
+@Form.create()
+@connect(state => ({
+  course: state.course,
+  support: state.support,
+  combo: state.combo,
+  merchant: state.merchant,
+  goodsDetail: state.goodsDetail,
+}))
+export default class GoodsAdd extends PureComponent {
+  state = {
+    radioType: Codes.CODE_COURSE,
+    curClickedBtn: null,
+    courseModalVisible: false,
+    supportModalVisible: false,
+    packageModalVisible: false,
+    selectItem: null,
+  };
+
+  componentDidMount() {
+    const { dispatch } = this.props;
+    dispatch({
+      type: 'merchant/query',
+      payload: {
+        pageNo: 1,
+        pageSize: 1000,
+        status: Codes.CODE_NORMAL,
+        domain: Codes.CODE_PJ,
+      }
+    })
+  }
+
+  handleRadioOnChange = (e) => {
+    this.setState({ radioType: e.target.value });
+  }
+
+  handleModalSearch = (data) => {
+    const { curClickedBtn } = this.state;
+    const { dispatch } = this.props;
+    const newData = { ...data };
+    if (newData.keyword) {
+      newData[newData.field] = newData.keyword;
+      delete newData.field;
+      delete newData.keyword;
+    } else {
+      delete newData.field;
+      delete newData.keyword;
+    }
+    if (curClickedBtn === Codes.CODE_COURSE) {
+      dispatch({
+        type: 'course/query',
+        payload: { ...newData, pageNo: 1, pageSize: 10, status: Codes.CODE_NORMAL },
+      });
+    }
+  }
+
+  handleModalOk = (data) => {
+    const { curClickedBtn } = this.state;
+    const { dispatch } = this.props;
+    if (curClickedBtn === Codes.CODE_COURSE) {
+      this.setState({
+        selectItem: data,
+        courseModalVisible: false,
+      });
+    } else if(curClickedBtn === Codes.CODE_SUPPORT) {
+      this.setState({
+        selectItem: data,
+        supportModalVisible: false,
+      });
+    } else if (curClickedBtn === Codes.CODE_PACKAGE) {
+      this.setState({
+        selectItem: data,
+        packageModalVisible: false,
+      });
+    }
+  }
+
+  handleModalCancel = () => {
+    const { curClickedBtn } = this.state;
+    const { dispatch } = this.props;
+    if (curClickedBtn === Codes.CODE_COURSE) {
+      this.setState({ courseModalVisible: false });
+    } else if (curClickedBtn === Codes.CODE_SUPPORT) {
+      this.setState({ supportModalVisible: false });
+    } else if (curClickedBtn === Codes.CODE_PACKAGE) {
+      this.setState({ packageModalVisible: false });
+    }
+  }
+
+  handleModalShow = (name) => {
+    const { dispatch } = this.props;
+    if (name === Codes.CODE_COURSE) {
+      this.setState({
+        curClickedBtn: name,
+        courseModalVisible: true,
+      }, () => {
+        dispatch({
+          type: 'course/query',
+          payload: {
+            pageNo: 1,
+            pageSize: 10,
+            status: Codes.CODE_NORMAL,
+          }
+        });
+      })
+    } else if (name === Codes.CODE_SUPPORT) {
+      this.setState({
+        curClickedBtn: name,
+        supportModalVisible: true,
+      }, () => {
+        dispatch({
+          type: 'support/query',
+          payload: {
+            pageNo: 1,
+            pageSize: 10,
+            status: Codes.CODE_NORMAL,
+          }
+        });
+      })
+    } else if (name === Codes.CODE_PACKAGE) {
+      this.setState({
+        curClickedBtn: name,
+        packageModalVisible: true,
+      }, () => {
+        dispatch({
+          type: 'combo/query',
+          payload: {
+            pageNo: 1,
+            pageSize: 10,
+            status: Codes.CODE_NORMAL,
+          }
+        });
+      })
+    }
+  }
+
+  handleModalTableChange = () => {
+    const { curClickedBtn } = this.state;
+    const { dispatch } = this.props;
+    const newFilters = { ...filters };
+    if (newFilters.keyword) {
+      newFilters[newFilters.field] = newFilters.keyword;
+      delete newFilters.field;
+      delete newFilters.keyword;
+    } else {
+      delete newFilters.field;
+      delete newFilters.keyword;
+    }
+    const getValue = obj => Object.keys(obj).map(key => obj[key]).join(',');
+    const tableFilters = Object.keys(filterArgs).reduce((obj, key) => {
+      const newObj = { ...obj };
+      newObj[key] = getValue(filterArgs[key]);
+      return newObj;
+    }, {});
+
+    const data = { ...newFilters, ...tableFilters, pageNo: pagination.current, pageSize: pagination.pageSize };
+    Object.keys(data).map(key => data[key] ? null : delete data[key]);
+    if (curClickedBtn === Codes.CODE_COURSE) {
+      dispatch({ type: 'course/query', payload: { ...data, status: Codes.CODE_NORMAL } });
+    } else if (curClickedBtn === Codes.CODE_SUPPORT) {
+      dispatch({ type: 'support/query', payload: { ...data, status: Codes.CODE_NORMAL } });
+    } else if (curClickedBtn === Codes.CODE_PACKAGE) {
+      dispatch({ type: 'combo/query', payload: { ...data, status: Codes.CODE_PACKAGE } });
+    }
+  }
+
+  handlePageSubmit = (e) => {
+    e.preventDefault();
+    const {
+      dispatch,
+      form: {
+        validateFields,
+        getFieldsValue,
+        resetFields,
+      },
+      goodsDetail: {
+        filters,
+      }
+    } = this.props;
+    const {
+      selectItem,
+      radioType,
+    } = this.state;
+    validateFields(errors => {
+      if (errors) return;
+      const data = {
+        ...getFieldsValue(['merchantId', 'status']),
+        pid: selectItem.pid,
+      };
+      data.status ? data.status = Codes.CODE_NORMAL : data.status = Codes.CODE_DELETE;
+      dispatch({
+        type: `goodsDetail/createMerchantProduct`,
+        payload: data,
+        callback: () => {
+          dispatch(
+            routerRedux.push({
+              pathname: '/goods',
+              search: queryString.stringify(filters),
+            })
+          );
+          resetFields();
+        }
+      });
+    });
+  }
+
+  handlePageCancel = () => {
+    const { dispatch, goodsDetail: { filters } } = this.props;
+    dispatch(
+      routerRedux.push({
+        pathname: '/goods',
+        search: queryString.stringify(filters),
+      })
+    );
+  }
+
+  renderLabelName = () => {
+    const { radioType } = this.state;
+    switch (radioType) {
+      case Codes.CODE_COURSE:
+        return '选择课程';
+        break;
+      case Codes.CODE_SUPPORT:
+        return '选择配套';
+        break;
+      case Codes.CODE_PACKAGE:
+        return '选择课程包';
+        break;
+      default:
+        break;
+    }
+  }
+
+  render() {
+    const formItemLayout = {
+      labelCol: {
+        span: 7,
+      },
+      wrapperCol: {
+        span: 12,
+      },
+    };
+    const submitFormLayout = {
+      wrapperCol: {
+        xs: { span: 24, offset: 0 },
+        sm: { span: 10, offset: 7 },
+      },
+    };
+    const {
+      form: {
+        getFieldDecorator
+      },
+      item = {},
+      course,
+      merchant,
+      support,
+      combo
+    } = this.props;
+    const {
+      radioType,
+      selectItem,
+      courseModalVisible,
+      supportModalVisible,
+      packageModalVisible,
+    } = this.state;
+
+    return (
+      <PageHeaderLayout>
+        <Card>
+          <Form layout="horizontal" onSubmit={this.handlePageSubmit}>
+            <FormItem label="选择渠道:" {...formItemLayout}>
+              {getFieldDecorator('merchantId', {
+                rules: [{ required: true, type: 'string', message: '此项为必选项!' }],
+              })(<Select placeholder="请选择" style={{ width: '43%' }}>{merchant.list.map(item => <Option value={item.id} key={item.id}>{item.name}</Option>)}</Select>)}
+            </FormItem>
+            <FormItem label="产品类型:" {...formItemLayout}>
+              {getFieldDecorator('type', {
+                rules: [{ required: true, type: 'string', message: '请选择产品类型!' }],
+                initialValue: radioType,
+              })(
+                <RadioGroup onChange={this.handleRadioOnChange}>
+                  {Object.keys(productType).map(key =>
+                    <Radio value={key} key={key}>{productType[key]}</Radio>)
+                  }
+                </RadioGroup>
+              )}
+            </FormItem>
+            <FormItem label={this.renderLabelName()} {...formItemLayout}>
+              <Button size="small" type="primary" icon="select" onClick={() => this.handleModalShow(radioType)}>选择</Button>
+            </FormItem>
+            <FormItem {...submitFormLayout}>
+              {selectItem ?
+                <Card
+                  title={selectItem.name}
+                  hoverable
+                  cover={<img alt="暂无封面图片" src={selectItem.coverUrl} />}
+                  style={{ width: '50%' }}
+                >
+                </Card> :
+                <Button type="dashed" className={styles.newButton}>未选择</Button>
+              }
+            </FormItem>
+            <FormItem label="上架状态:" {...formItemLayout}>
+              {getFieldDecorator('status', {
+                valuePropsName: 'checked',
+            })(<Switch defaultChecked={false} checkedChildren="出售" unCheckedChildren="下架" />)}
+            </FormItem>
+            <FormItem {...submitFormLayout} style={{ marginTop: 32 }}>
+              <Button onClick={this.handlePageCancel}>取消</Button>
+              <Button type="primary" style={{ marginLeft: 35 }} htmlType="submit">提交</Button>
+            </FormItem>
+          </Form>
+          {/*查询课程的模态选择框*/}
+          <CourseSelectModal
+            rowKeyName="id"
+            modalVisible={courseModalVisible}
+            style={{ top: 30 }}
+            onOk={this.handleModalOk}
+            onCancel={this.handleModalCancel}
+            onSearch={this.handleModalSearch}
+            fsTableDataSource={course.list || []}
+            fsTableLoading={course.listLoading}
+            fsTablePagination={course.pagination}
+            fsTableOnChange={this.handleModalTableChange}
+          />
+          {/*查询配套的模态选择框*/}
+          <SupportSelectModal
+            rowKeyName="id"
+            modalVisible={supportModalVisible}
+            style={{ top: 30 }}
+            onOk={this.handleModalOk}
+            onCancel={this.handleModalCancel}
+            onSearch={this.handleModalSearch}
+            fsTableDataSource={support.list || []}
+            fsTableLoading={support.listLoading}
+            fsTablePagination={support.pagination}
+            fsTableOnChange={this.handleModalTableChange}
+          />
+          {/*查询课程包的模态选择框*/}
+          <PackageSelectModal
+            rowKeyName="id"
+            modalVisible={packageModalVisible}
+            style={{ top: 30 }}
+            onOk={this.handleModalOk}
+            onCancel={this.handleModalCancel}
+            onSearch={this.handleModalSearch}
+            fsTableDataSource={combo.list || []}
+            fsTableLoading={combo.listLoading}
+            fsTablePagination={combo.pagination}
+            fsTableOnChange={this.handleModalTableChange}
+          />
+        </Card>
+      </PageHeaderLayout>
+    );
+  }
+}

+ 11 - 0
src/routes/Goods/Add/index.less

@@ -0,0 +1,11 @@
+@import "~antd/lib/style/themes/default.less";
+@import "../../../utils/utils.less";
+
+.newButton {
+  background-color: #fff;
+  border-color: @border-color-base;
+  border-radius: @border-radius-sm;
+  color: @text-color-secondary;
+  width: 50%;
+  height: 250px;
+}

+ 12 - 11
src/routes/Course/detail/resourceModal.js

@@ -2,10 +2,9 @@ import React, { PureComponent } from 'react';
 import PropTypes from 'prop-types';
 import { Badge, Popover, Icon } from 'antd';
 import SelectModal from '../../../components/SelectModal';
-import styles from './resourceModal.less';
 import { Codes } from '../../../utils/config';
 
-export default class ResourceSelectModal extends PureComponent {
+export default class PackageSelectModal extends PureComponent {
   static propTypes = {
     modalVisible: PropTypes.bool.isRequired,
     rowKeyName: PropTypes.string.isRequired,
@@ -15,7 +14,7 @@ export default class ResourceSelectModal extends PureComponent {
     const { modalVisible, onCancel, onOk, onSearch, ...fsTableOpts } = this.props;
 
     const modalProps = {
-      title: '选择图片',
+      title: '选择课程包',
       maskClosable: false,
       visible: modalVisible,
       onCancel,
@@ -28,9 +27,9 @@ export default class ResourceSelectModal extends PureComponent {
       searchSize: 'default',
       searchSelect: true,
       searchSelectOptions: [{
-        value: 'name', name: '图片名称', mode: 'input',
+        value: 'name', name: '课程包名称', mode: 'input',
       },{
-        value: 'code', name: '图片编号', mode: 'input',
+        value: 'code', name: '课程包编号', mode: 'input',
       }],
       searchSelectProps: {
         defaultValue: 'name',
@@ -42,27 +41,29 @@ export default class ResourceSelectModal extends PureComponent {
 
     //待选资源Table属性
     const fsTableProps = {
-      fsTableClassName: styles.fsTable,
       fsTableColumns: [{
-        title: '缩略图',
+        title: '封面图',
         dataIndex: 'url',
         key: 'url',
         render: (text, record) => (
           <Popover
-            content={<img alt="" src={record.url} width={350} />}
+            content={<img alt="" src={record.coverUrl} width={350} />}
             title={record.name}
           >
-            <img alt="" src={record.url} width={70} />
+            <img alt="" src={record.coverUrl} width={70} />
           </Popover>
         ),
+        width: '30%',
       },{
-        title: '图片编号',
+        title: '课程包编号',
         dataIndex: 'code',
         key: 'code',
+        width: '25%',
       },{
-        title: '图片名称',
+        title: '课程包名称',
         dataIndex: 'name',
         key: 'name',
+        width: '25%',
       }],
       ...fsTableOpts,
     }

+ 9 - 11
src/routes/Course/detail/resourceModal.js

@@ -2,10 +2,9 @@ import React, { PureComponent } from 'react';
 import PropTypes from 'prop-types';
 import { Badge, Popover, Icon } from 'antd';
 import SelectModal from '../../../components/SelectModal';
-import styles from './resourceModal.less';
 import { Codes } from '../../../utils/config';
 
-export default class ResourceSelectModal extends PureComponent {
+export default class SupportSelectModal extends PureComponent {
   static propTypes = {
     modalVisible: PropTypes.bool.isRequired,
     rowKeyName: PropTypes.string.isRequired,
@@ -15,7 +14,7 @@ export default class ResourceSelectModal extends PureComponent {
     const { modalVisible, onCancel, onOk, onSearch, ...fsTableOpts } = this.props;
 
     const modalProps = {
-      title: '选择图片',
+      title: '选择配套',
       maskClosable: false,
       visible: modalVisible,
       onCancel,
@@ -28,9 +27,9 @@ export default class ResourceSelectModal extends PureComponent {
       searchSize: 'default',
       searchSelect: true,
       searchSelectOptions: [{
-        value: 'name', name: '图片名称', mode: 'input',
+        value: 'name', name: '配套名称', mode: 'input',
       },{
-        value: 'code', name: '图片编号', mode: 'input',
+        value: 'code', name: '配套编号', mode: 'input',
       }],
       searchSelectProps: {
         defaultValue: 'name',
@@ -42,25 +41,24 @@ export default class ResourceSelectModal extends PureComponent {
 
     //待选资源Table属性
     const fsTableProps = {
-      fsTableClassName: styles.fsTable,
       fsTableColumns: [{
-        title: '缩略图',
+        title: '封面图',
         dataIndex: 'url',
         key: 'url',
         render: (text, record) => (
           <Popover
-            content={<img alt="" src={record.url} width={350} />}
+            content={<img alt="" src={record.coverUrl} width={350} />}
             title={record.name}
           >
-            <img alt="" src={record.url} width={70} />
+            <img alt="" src={record.coverUrl} width={70} />
           </Popover>
         ),
       },{
-        title: '图片编号',
+        title: '配套编号',
         dataIndex: 'code',
         key: 'code',
       },{
-        title: '图片名称',
+        title: '配套名称',
         dataIndex: 'name',
         key: 'name',
       }],

+ 40 - 0
src/routes/Goods/Edit/index.js

@@ -0,0 +1,40 @@
+import React, { Component } from 'react';
+import { Card, Select } from 'antd';
+import { connect } from 'dva';
+import PageHeaderLayout from '../../../layouts/PageHeaderLayout';
+import DescriptionList from '../../../components/DescriptionList';
+import CardValuation from '../../../components/CardValuation';
+
+const { Description } = DescriptionList;
+
+@connect(state => ({ goodsDetail: state.goodsDetail}))
+export default class GoodsEdit extends Component {
+  render(){
+    return (
+      <PageHeaderLayout>
+        <Card
+          bordered={false}
+          title="商品详情"
+          style={{ marginBottom: 15 }}
+        >
+          <DescriptionList size="large" style={{ marginBottom: 32 }}>
+            <Description term="产品编号">{"COURSE-math-001"}</Description>
+            <Description term="产品名称">小学一年级数学</Description>
+            <Description term="产品类型">课程</Description>
+          </DescriptionList>
+        </Card>
+        <Card
+          bordered={false}
+          title="标签"
+          style={{ marginBottom: 15 }}
+        >
+          <Select style={{ width: 200 }}></Select>
+        </Card>
+        <CardValuation
+          cardTitle="商品定价"
+          value={[]}
+        />
+      </PageHeaderLayout>
+    );
+  }
+}

+ 21 - 26
src/routes/Tag/index.js

@@ -6,39 +6,40 @@ import { routerRedux } from 'dva/router';
 import { Card } from 'antd';
 import TableList from './table';
 import Search from './search';
-import PageHeaderLayout from '../../layouts/PageHeaderLayout';
-import { Codes } from '../../utils/config';
+import PageHeaderLayout from '../../../layouts/PageHeaderLayout';
+import { Codes } from '../../../utils/config';
 
 @connect(state => ({
-  tag: state.tag,
+  goods: state.goods,
   merchant: state.merchant,
 }))
-export default class Tag extends PureComponent {
+export default class GoodsList extends PureComponent {
   static propTypes = {
-    group: PropTypes.object,
+    goods: PropTypes.object,
     location: PropTypes.object,
     dispatch: PropTypes.func,
   };
 
+  // 组件挂载完成,加载1000条渠道商信息用于渠道筛选
   componentDidMount() {
-    const { dispatch } = this.props;
+    const {dispatch} = this.props;
     dispatch({
       type: 'merchant/query',
       payload: {
         pageNo: 1,
+        pageSize: 1000,
+        domain: Codes.CODE_PJ,
         status: Codes.CODE_NORMAL,
-        pageSize: 20,
       }
     });
   }
 
   render() {
-    const { location, dispatch, tag, merchant } = this.props;
-
+    const { location, dispatch, goods, merchant } = this.props;
     location.query = queryString.parse(location.search);
     const { query, pathname } = location;
     const { field, keyword, ...filters } = query;
-    const { list, listLoading, pagination, currentItem, itemLoading, modalVisible, modalType } = tag;
+    const { list, listLoading, pagination  } = goods;
 
     // 把携带的参数中空值项删除
     Object.keys(filters).map(key => { filters[key] ? null : delete filters[key] });
@@ -51,18 +52,6 @@ export default class Tag extends PureComponent {
     const searchProps = {
       field,
       keyword,
-      filterSelectProps: {
-        data: merchant.list.map(item => { return { value: item.id, name: item.name } }),
-        onSelectSearch: (value) => {
-          dispatch({
-            type: 'merchant/query',
-            payload: {
-              name: value,
-              status: Codes.CODE_NORMAL
-            }
-          });
-        },
-      },
       onSearch: (payload) => {
         if (!payload.keyword.length) {
           delete payload.field;
@@ -78,7 +67,7 @@ export default class Tag extends PureComponent {
       onAdd: () => {
         dispatch(
           routerRedux.push({
-            pathname: '/tag/tag/add',
+            pathname: '/goods/add',
             state: filters,
           })
         );
@@ -91,6 +80,8 @@ export default class Tag extends PureComponent {
       dataSource: list,
       loading: listLoading,
       curStatus: filters.status,
+      merchantList: merchant.list,
+      curMid: filters.merchantId,
       onChange: (pagination, filterArgs) => {
         const getValue = obj => Object.keys(obj).map(key => obj[key]).join(',');
         const tableFilters = Object.keys(filterArgs).reduce((obj, key) => {
@@ -113,14 +104,18 @@ export default class Tag extends PureComponent {
       onEditItem: (item) => {
         dispatch(
           routerRedux.push({
-            pathname: `/tag/tag/edit/${item.id}`,
+            pathname: `/goods/edit`,
+            search: queryString.stringify({
+              merchantId: item.merchantId,
+              pid: item.pid,
+            }),
             state: filters,
           })
         );
       },
       onDeleteItem: (id) => {
         dispatch({
-          type: 'tag/delete',
+          type: 'goods/putOffSale',
           payload: id,
           callback: () => {
             dispatch(
@@ -134,7 +129,7 @@ export default class Tag extends PureComponent {
       },
       onRecoverItem: (payload) => {
         dispatch({
-          type: 'tag/recover',
+          type: 'goods/putOnSale',
           payload,
           callback: () => {
             dispatch(

+ 4 - 4
src/routes/Course/search.js

@@ -1,7 +1,7 @@
 import react, { PureComponent } from 'react';
 import PropTypes from 'prop-types';
 import { Button, Form, Row, Col, Icon } from 'antd';
-import DataSearch from '../../components/DataSearch';
+import DataSearch from '../../../components/DataSearch';
 
 @Form.create()
 export default class Search extends PureComponent {
@@ -22,9 +22,9 @@ export default class Search extends PureComponent {
       size: 'default',
       select: true,
       selectOptions: [{
-        value: 'name', name: '课程名称', mode: 'input',
+        value: 'name', name: '产品名称', mode: 'input',
       },{
-        value: 'code', name: '课程编号', mode: 'input',
+        value: 'code', name: '产品编号', mode: 'input',
       }],
       selectProps: {
         defaultValue: field || 'name',
@@ -40,7 +40,7 @@ export default class Search extends PureComponent {
           <DataSearch { ...searchGroupProps } />
         </Col>
         <Col lg={{ offset: 7, span: 7 }} md={12} sm={8} xs={24} style={{ marginBottom: 16, textAlign: 'right' }}>
-          <Button type="primary" onClick={onAdd}><Icon type="plus-circle" />新建课程</Button>
+          <Button type="primary" onClick={onAdd}><Icon type="plus-circle" />新建商品</Button>
         </Col>
       </Row>
     );

+ 39 - 23
src/routes/Tag/table.js

@@ -3,10 +3,10 @@ import PropTypes from 'prop-types';
 import moment from 'moment';
 import classnames from 'classnames';
 import queryString from 'query-string';
-import { Modal, Table, Menu, Icon, Badge } from 'antd';
-import AnimTableBody from '../../components/Animation/AnimTableBody';
+import { Divider, Modal, Table, Menu, Icon, Badge } from 'antd';
+import AnimTableBody from '../../../components/Animation/AnimTableBody';
+import { itemStatuses, Codes, productType } from '../../../utils/config';
 import styles from './table.less';
-import { statuses, Codes } from '../../utils/config';
 
 const confirm = Modal.confirm;
 
@@ -21,53 +21,67 @@ export default class TableList extends PureComponent {
   handleOperateItem = (record) => {
     const { onDeleteItem, onRecoverItem } = this.props;
     confirm({
-      title: `您确定要${record.status === Codes.CODE_NORMAL ? '删除' : '恢复'}该条记录?`,
+      title: `您确定要${record.status === Codes.CODE_NORMAL ? '下架' : '出售'}该产品?`,
       onOk () {
         if (record.status === Codes.CODE_NORMAL) {
-          onDeleteItem({id: record.id});
+          onDeleteItem({ pid: record.pid, merchantId: record.merchantId, status: Codes.CODE_DELETE });
         } else if (record.status === Codes.CODE_DELETE) {
-          onRecoverItem({ id: record.id, status: Codes.CODE_NORMAL });
+          onRecoverItem({ pid: record.pid, merchantId: record.merchantId, status: Codes.CODE_NORMAL });
         }
       },
     })
   }
 
   render() {
-    const { curStatus, onDeleteItem, onRecoverItem, onEditItem, location, pagination, ...tableProps } = this.props;
-
+    const {
+      curStatus,
+      curMid,
+      onDeleteItem,
+      onRecoverItem,
+      onEditItem,
+      location,
+      pagination,
+      merchantList,
+      ...tableProps
+    } = this.props;
     const columns = [{
-      title: '标签编号',
+      title: '产品编号',
       dataIndex: 'code',
       key: 'code',
     },{
-      title: '标签名称',
+      title: '产品名称',
       dataIndex: 'name',
       key: 'name',
     },{
-      title: '所属标签组',
-      dataIndex: 'groupName',
-      key: 'groupName',
+      title: '产品类型',
+      dataIndex: 'type',
+      key: 'type',
+      render: (text, record) => productType[record.type],
     },{
-      title: '渠道平台',
+      title: '渠道名称',
       dataIndex: 'merchantId',
       key: 'merchantId',
+      render: (text, record) => record.merchantName,
+      filters: merchantList.map(item => ({ text: item.name, value: item.id })),
+      filterMultiple: false,
+      filteredValue: [curMid],
     },{
       title: '状态',
       dataIndex: 'status',
       key: 'status',
       render: (text, record) => {
         const statusMap = {[Codes.CODE_NORMAL]: 'success', [Codes.CODE_DELETE]: 'error'};
-        return (<Badge status={statusMap[record.status]} text={statuses[record.status]} />);
+        return (<Badge status={statusMap[record.status]} text={itemStatuses[record.status]} />);
       },
-      filters: Object.keys(statuses).map(key => ({ text: statuses[key], value: key })),
+      filters: Object.keys(itemStatuses).map(key => ({ text: itemStatuses[key], value: key })),
       filterMultiple: false,
       filteredValue: [curStatus],
     },{
-      title: '添加时间',
-      dataIndex: 'gmtCreated',
-      key: 'gmtCreated',
+      title: '创建时间',
+      dataIndex: 'gmtModified',
+      key: 'gmtModified',
       render: (text, record) => (
-        <div>{moment(text).format('YYYY-MM-DD')}</div>
+        <div>{moment(text).format('YYYY-MM-DD HH:mm:ss')}</div>
       )
     },{
       title: '操作',
@@ -76,17 +90,19 @@ export default class TableList extends PureComponent {
       render: (text, record) => (
         <div>
           <a onClick={() => onEditItem(record)}>编辑</a>
-          <span className={styles.splitLine} />
-            <a onClick={() => this.handleOperateItem(record)}>{record.status === Codes.CODE_NORMAL ? '删除' : '恢复'}</a>
+          <Divider type="vertical" />
+          <a onClick={() => this.handleOperateItem(record)}>{record.status === Codes.CODE_NORMAL ? '下架' : '出售'}</a>
         </div>
       )
     }];
 
-    // 数据table列表表头的筛选按钮点击重置后status与domain值为空,此时删除该参数
+    // 数据table列表表头的筛选按钮点击重置后status与merchantName值为空,此时删除该参数
     columns.map(item => {
       item.dataIndex === 'status' && !curStatus ? delete item.filteredValue : null;
+      item.dataIndex === 'merchantId' && !curMid ? delete item.filteredValue : null;
     });
 
+    // 覆盖表格的默认样式,添加动画
     tableProps.pagination = !!pagination && { ...pagination, showSizeChanger: true, showQuickJumper: true, showTotal: total => `共 ${total} 条`};
     const getBodyWrapperProps = {
       page: location.query.page,

+ 8 - 8
src/routes/Tag/table.less

@@ -1,5 +1,5 @@
 @import "~antd/lib/style/themes/default.less";
-@import "../../utils/utils.less";
+@import "../../../utils/utils.less";
 
 .table {
   :global {
@@ -14,31 +14,31 @@
       .ant-table-tbody > tr > td,
       .ant-table-thead > tr > th {
         &:nth-child(1) {
-          width: 14%;
+          width: 15%;
         }
 
         &:nth-child(2) {
-          width: 14%;
+          width: 15%;
         }
 
         &:nth-child(3) {
-          width: 14%;
+          width: 15%;
         }
 
         &:nth-child(4) {
-          width: 14%;
+          width: 15%;
         }
 
         &:nth-child(5) {
-          width: 14%;
+          width: 10%;
         }
 
         &:nth-child(6) {
-          width: 14%;
+          width: 18%;
         }
 
         &:nth-child(7) {
-          width: 16%;
+          width: 12%;
         }
       }
 

+ 1 - 1
src/routes/Resource/gallery/search.js

@@ -40,7 +40,7 @@ export default class Search extends PureComponent {
           <DataSearch { ...searchGroupProps } />
         </Col>
         <Col lg={{ offset: 7, span: 7 }} md={12} sm={8} xs={24} style={{ marginBottom: 16, textAlign: 'right' }}>
-          <Button type="ghost" onClick={onAdd}><Icon type="plus-circle" />添加</Button>
+          <Button type="primary" onClick={onAdd}><Icon type="plus-circle" />添加图片</Button>
         </Col>
       </Row>
     );

+ 2 - 2
src/routes/Resource/video/modal.js

@@ -14,11 +14,11 @@ export default class ModalForm extends PureComponent {
       ...modalProps,
       key: item.id,
     }
-    const playerProps = { url: item.url, isPaused, hlsConfig: {}, width:'100%', height: '100%', controls: true };
+    const playerProps = { m3u8: item.format === 'm3u8', url: item.url, isPaused, hlsConfig: {}, width:'100%', height: '100%', controls: true };
 
     return (
       <Modal {...newModalProps}>
-        <VideoPlayer { ...playerProps } />
+          <VideoPlayer { ...playerProps } />
       </Modal>
     );
   }

+ 14 - 4
src/routes/Resource/video/table.js

@@ -5,7 +5,7 @@ import classnames from 'classnames';
 import queryString from 'query-string';
 import { Popover, Modal, Table, Menu, Icon, Badge } from 'antd';
 import AnimTableBody from '../../../components/Animation/AnimTableBody';
-import { statuses, Codes } from '../../../utils/config'
+import { statuses, quality, Codes } from '../../../utils/config'
 import styles from './table.less';
 
 const confirm = Modal.confirm;
@@ -40,9 +40,14 @@ export default class TableList extends PureComponent {
       dataIndex: 'name',
       key: 'name',
     },{
-      title: '视频大小(B)',
-      dataIndex: 'size',
-      key: 'size',
+      title: '视频格式',
+      dataIndex: 'format',
+      key: 'format',
+    },{
+      title: '视频质量',
+      dataIndex: 'quality',
+      key: 'quality',
+      render: (text, record) => quality[text],
     },{
       title: '状态',
       dataIndex: 'status',
@@ -86,6 +91,10 @@ export default class TableList extends PureComponent {
     };
     const getBodyWrapper = (body) => (<AnimTableBody {...getBodyWrapperProps} body={body} />);
 
+    // 视频返回的数据量每页不固定,会出现分页bug,这里截取与pageSize相等
+    const pageSize = tableProps.pagination.pageSize;
+    const newSource = tableProps.dataSource.slice(0, pageSize);
+
     return (
       <Table
         simple
@@ -95,6 +104,7 @@ export default class TableList extends PureComponent {
         className={classnames({ [styles.table]: true, [styles.motion]: true })}
         rowKey={record => record.id}
         getBodyWrapper={getBodyWrapper}
+        dataSource={newSource}
       />
     );
   }

+ 10 - 6
src/routes/Resource/video/table.less

@@ -14,27 +14,31 @@
       .ant-table-tbody > tr > td,
       .ant-table-thead > tr > th {
         &:nth-child(1) {
-          width: 17%;
+          width: 20%;
         }
 
         &:nth-child(2) {
-          width: 17%;
+          width: 20%;
         }
 
         &:nth-child(3) {
-          width: 16%;
+          width: 15%;
         }
 
         &:nth-child(4) {
-          width: 16%;
+          width: 10%;
         }
 
         &:nth-child(5) {
-          width: 16%;
+          width: 10%;
         }
 
         &:nth-child(6) {
-          width: 18%;
+          width: 12%;
+        }
+
+        &:nth-child(7) {
+          width: 13%;
         }
       }
 

+ 52 - 7
src/routes/Support/detail/index.js

@@ -15,6 +15,7 @@ const { TextArea } = Input;
 
 @Form.create()
 @connect(state => ({
+  merchant: state.merchant,
   resource: state.resource,
   support: state.support,
   supportDetail: state.supportDetail,
@@ -24,9 +25,19 @@ export default class SupportDetail extends PureComponent {
     supportDetail: PropTypes.object,
   };
 
-  state = {
-    curClickedBtn: null,       //记录点击的按钮
-  };
+  state = { curClickedBtn: null };
+
+  componentDidMount() {
+    const { dispatch } = this.props;
+    dispatch({
+      type: 'merchant/query',
+      payload: {
+        pageSize: 1000,
+        pageNo: 1,
+        domain: Codes.CODE_CP,
+      }
+    })
+  }
 
   // 展示选择模态框 - 加载第一页数据
   handleModalShow = (btnName) => {
@@ -194,9 +205,31 @@ export default class SupportDetail extends PureComponent {
   }
 
   render() {
-    const { dispatch, form: { getFieldDecorator }, resource, support, supportDetail } = this.props;
-    const { itemLoading, currentItem, filters, supportModalVisible, resourceModalVisible } = supportDetail;
-    const { imgList = [], supportList = [], name, code, digest } = currentItem;
+    const {
+      merchant,
+      resource,
+      support,
+      supportDetail,
+      form: {
+        getFieldDecorator
+      },
+    } = this.props;
+    const {
+      itemLoading,
+      currentItem,
+      filters,
+      supportModalVisible,
+      resourceModalVisible
+    } = supportDetail;
+    const {
+      cpId,
+      name,
+      code,
+      digest,
+      detail,
+      imgList = [],
+      supportList = [],
+    } = currentItem;
 
     // 待选表格去掉分页的跳转及变换页码
     if (resource && resource.pagination) {
@@ -268,11 +301,23 @@ export default class SupportDetail extends PureComponent {
                   initialValue: name,
                 })(<Input />)}
               </FormItem>
-              <FormItem label="配套简述:" hasFeedback {...formItemLayout}>
+              <FormItem label="配套概要:" hasFeedback {...formItemLayout}>
                 {getFieldDecorator('digest', {
                   initialValue: digest,
                 })(<TextArea />)}
               </FormItem>
+              <FormItem label="配套详情:" hasFeedback {...formItemLayout}>
+                {getFieldDecorator('detail', {
+                  initialValue: detail,
+                })(<TextArea />)}
+              </FormItem>
+              <FormItem label="所属供应商:" {...formItemLayout}>
+                {getFieldDecorator('cpId', {
+                  initialValue: cpId,
+                })(
+                  <Select placeholder="请选择">{merchant.list.map(item => <Option value={item.id} key={item.id}>{item.name}</Option>)}</Select>
+                )}
+              </FormItem>
               <FormItem label="选择图片" {...formItemLayout}>
                 <Button onClick={() => this.handleModalShow('imgBtn')} type="primary" size="small" icon="edit">编辑</Button>
               </FormItem>

+ 1 - 4
src/routes/Support/index.js

@@ -9,9 +9,7 @@ import Search from './search';
 import PageHeaderLayout from '../../layouts/PageHeaderLayout';
 import { Codes } from '../../utils/config';
 
-@connect(state => ({
-  support: state.support,
-}))
+@connect(state => ({ support: state.support }))
 export default class Support extends PureComponent {
   static propTypes = {
     support: PropTypes.object,
@@ -21,7 +19,6 @@ export default class Support extends PureComponent {
 
   render() {
     const { location, dispatch, support } = this.props;
-
     location.query = queryString.parse(location.search);
     const { query, pathname } = location;
     const { field, keyword, ...filters } = query;

+ 2 - 8
src/routes/Tag/detail/index.js

@@ -79,7 +79,7 @@ export default class TagDetail extends PureComponent {
         callback: () => {
           dispatch(
             routerRedux.push({
-              pathname: '/tag/tag',
+              pathname: '/tag/tagItem',
               search: queryString.stringify(filters),
             })
           );
@@ -94,7 +94,7 @@ export default class TagDetail extends PureComponent {
     dispatch({ type: 'tagDetail/clearPage' });
     dispatch(
       routerRedux.push({
-        pathname: '/tag/tag',
+        pathname: '/tag/tagItem',
         search: queryString.stringify(filters),
       })
     );
@@ -149,12 +149,6 @@ export default class TagDetail extends PureComponent {
         <Spin spinning={itemLoading}>
           <Card>
             <Form layout="horizontal" onSubmit={this.handlePageSubmit}>
-              <FormItem label="标签编号:" hasFeedback {...formItemLayout}>
-                {getFieldDecorator('code', {
-                  rules: [{ required: true, type: 'string', message: "编号为必填项!" }],
-                  initialValue: code,
-                })(<Input />)}
-              </FormItem>
               <FormItem label="标签名称:" hasFeedback {...formItemLayout}>
                 {getFieldDecorator('name', {
                   rules: [{ required: true, type: 'string', message: "名称为必填项!" }],

src/routes/Tag/detail/modal.js → src/routes/Tag/Edit/modal.js


+ 6 - 4
src/routes/Tag/index.js

@@ -6,8 +6,8 @@ import { routerRedux } from 'dva/router';
 import { Card } from 'antd';
 import TableList from './table';
 import Search from './search';
-import PageHeaderLayout from '../../layouts/PageHeaderLayout';
-import { Codes } from '../../utils/config';
+import PageHeaderLayout from '../../../layouts/PageHeaderLayout';
+import { Codes } from '../../../utils/config';
 
 @connect(state => ({
   tag: state.tag,
@@ -78,7 +78,7 @@ export default class Tag extends PureComponent {
       onAdd: () => {
         dispatch(
           routerRedux.push({
-            pathname: '/tag/tag/add',
+            pathname: '/tag/tagItem/add',
             state: filters,
           })
         );
@@ -89,8 +89,10 @@ export default class Tag extends PureComponent {
       pagination,
       location,
       dataSource: list,
+      merchantList: merchant.list,
       loading: listLoading,
       curStatus: filters.status,
+      curMerchant: filters.merchantId,
       onChange: (pagination, filterArgs) => {
         const getValue = obj => Object.keys(obj).map(key => obj[key]).join(',');
         const tableFilters = Object.keys(filterArgs).reduce((obj, key) => {
@@ -113,7 +115,7 @@ export default class Tag extends PureComponent {
       onEditItem: (item) => {
         dispatch(
           routerRedux.push({
-            pathname: `/tag/tag/edit/${item.id}`,
+            pathname: `/tag/tagItem/edit/${item.id}`,
             state: filters,
           })
         );

+ 2 - 4
src/routes/Tag/search.js

@@ -1,7 +1,7 @@
 import react, { PureComponent } from 'react';
 import PropTypes from 'prop-types';
 import { Button, Form, Row, Col, Icon } from 'antd';
-import DataSearch from '../../components/DataSearch';
+import DataSearch from '../../../components/DataSearch';
 
 @Form.create()
 export default class Search extends PureComponent {
@@ -26,8 +26,6 @@ export default class Search extends PureComponent {
         value: 'name', name: '标签名称', mode: 'input',
       },{
         value: 'code', name: '标签编号', mode: 'input',
-      },{
-        value: 'merchantId', name: '渠道名称', mode: 'select',
       }],
       selectProps: {
         defaultValue: field || 'name',
@@ -43,7 +41,7 @@ export default class Search extends PureComponent {
           <DataSearch { ...searchGroupProps } />
         </Col>
         <Col lg={{ offset: 7, span: 7 }} md={12} sm={8} xs={24} style={{ marginBottom: 16, textAlign: 'right' }}>
-          <Button type="ghost" onClick={onAdd}><Icon type="plus-circle" />添加</Button>
+          <Button type="primary" onClick={onAdd}><Icon type="plus-circle" />新建标签</Button>
         </Col>
       </Row>
     );

+ 19 - 14
src/routes/Tag/table.js

@@ -3,10 +3,10 @@ import PropTypes from 'prop-types';
 import moment from 'moment';
 import classnames from 'classnames';
 import queryString from 'query-string';
-import { Modal, Table, Menu, Icon, Badge } from 'antd';
-import AnimTableBody from '../../components/Animation/AnimTableBody';
+import { Divider, Modal, Table, Menu, Icon, Badge } from 'antd';
+import AnimTableBody from '../../../components/Animation/AnimTableBody';
 import styles from './table.less';
-import { statuses, Codes } from '../../utils/config';
+import { statuses, Codes } from '../../../utils/config';
 
 const confirm = Modal.confirm;
 
@@ -33,13 +33,13 @@ export default class TableList extends PureComponent {
   }
 
   render() {
-    const { curStatus, onDeleteItem, onRecoverItem, onEditItem, location, pagination, ...tableProps } = this.props;
+    const { curStatus, curMerchant, merchantList, onDeleteItem, onRecoverItem, onEditItem, location, pagination, ...tableProps } = this.props;
 
     const columns = [{
-      title: '标签编号',
-      dataIndex: 'code',
-      key: 'code',
-    },{
+    //   title: '标签编号',
+    //   dataIndex: 'code',
+    //   key: 'code',
+    // },{
       title: '标签名称',
       dataIndex: 'name',
       key: 'name',
@@ -51,6 +51,10 @@ export default class TableList extends PureComponent {
       title: '渠道平台',
       dataIndex: 'merchantId',
       key: 'merchantId',
+      render: (text, record) => record.merchantName,
+      filters: merchantList.map(item => ({ text: item.name, value: item.id })),
+      filterMultiple: false,
+      filteredValue: [curMerchant],
     },{
       title: '状态',
       dataIndex: 'status',
@@ -63,11 +67,11 @@ export default class TableList extends PureComponent {
       filterMultiple: false,
       filteredValue: [curStatus],
     },{
-      title: '添加时间',
-      dataIndex: 'gmtCreated',
-      key: 'gmtCreated',
+      title: '修改时间',
+      dataIndex: 'gmtModified',
+      key: 'gmtModified',
       render: (text, record) => (
-        <div>{moment(text).format('YYYY-MM-DD')}</div>
+        <div>{moment(text).format('YYYY-MM-DD HH:mm:ss')}</div>
       )
     },{
       title: '操作',
@@ -76,8 +80,8 @@ export default class TableList extends PureComponent {
       render: (text, record) => (
         <div>
           <a onClick={() => onEditItem(record)}>编辑</a>
-          <span className={styles.splitLine} />
-            <a onClick={() => this.handleOperateItem(record)}>{record.status === Codes.CODE_NORMAL ? '删除' : '恢复'}</a>
+          <Divider type="vertical" />
+          <a onClick={() => this.handleOperateItem(record)}>{record.status === Codes.CODE_NORMAL ? '删除' : '恢复'}</a>
         </div>
       )
     }];
@@ -85,6 +89,7 @@ export default class TableList extends PureComponent {
     // 数据table列表表头的筛选按钮点击重置后status与domain值为空,此时删除该参数
     columns.map(item => {
       item.dataIndex === 'status' && !curStatus ? delete item.filteredValue : null;
+      item.dataIndex === 'merchantId' && !curMerchant ? delete item.filteredValue : null;
     });
 
     tableProps.pagination = !!pagination && { ...pagination, showSizeChanger: true, showQuickJumper: true, showTotal: total => `共 ${total} 条`};

+ 9 - 13
src/routes/TagGroup/table.less

@@ -1,5 +1,5 @@
 @import "~antd/lib/style/themes/default.less";
-@import "../../utils/utils.less";
+@import "../../../utils/utils.less";
 
 .table {
   :global {
@@ -22,20 +22,24 @@
         }
 
         &:nth-child(3) {
-          width: 17%;
+          width: 15%;
         }
 
         &:nth-child(4) {
-          width: 17%;
+          width: 14%;
         }
 
         &:nth-child(5) {
-          width: 17%;
+          width: 19%;
         }
 
         &:nth-child(6) {
-          width: 15%;
+          width: 18%;
         }
+
+        // &:nth-child(7) {
+        //   width: 14%;
+        // }
       }
 
       .ant-table-thead {
@@ -72,11 +76,3 @@
     }
   }
 }
-
-.splitLine {
-  background: @border-color-split;
-  display: inline-block;
-  margin: 0 8px;
-  width: 1px;
-  height: 12px;
-}

+ 2 - 2
src/routes/TagGroup/detail/index.js

@@ -81,7 +81,7 @@ export default class GroupDetail extends PureComponent {
         callback: () => {
           dispatch(
             routerRedux.push({
-              pathname: '/tag/group',
+              pathname: '/tag/tagGroup',
               search: queryString.stringify(filters),
             })
           );
@@ -96,7 +96,7 @@ export default class GroupDetail extends PureComponent {
     dispatch({ type: 'groupDetail/clearPage' });
     dispatch(
       routerRedux.push({
-        pathname: '/tag/group',
+        pathname: '/tag/tagGroup',
         search: queryString.stringify(filters),
       })
     );

src/routes/TagGroup/detail/modal.js → src/routes/TagGroup/Edit/modal.js


+ 9 - 15
src/routes/TagGroup/index.js

@@ -6,13 +6,12 @@ import { routerRedux } from 'dva/router';
 import { Card } from 'antd';
 import TableList from './table';
 import Search from './search';
-import PageHeaderLayout from '../../layouts/PageHeaderLayout';
-import { Codes } from '../../utils/config';
+import PageHeaderLayout from '../../../layouts/PageHeaderLayout';
+import { Codes } from '../../../utils/config';
 
 @connect(state => ({
   group: state.group,
-  merchant: state.merchant,
-}))
+  merchant: state.merchant, }))
 export default class TagGroup extends PureComponent {
   static propTypes = {
     group: PropTypes.object,
@@ -26,8 +25,9 @@ export default class TagGroup extends PureComponent {
       type: 'merchant/query',
       payload: {
         pageNo: 1,
-        status: Codes.CODE_NORMAL,
         pageSize: 20,
+        domain: Codes.CODE_PJ,
+        status: Codes.CODE_NORMAL,
       }
     });
   }
@@ -78,7 +78,7 @@ export default class TagGroup extends PureComponent {
       onAdd: () => {
         dispatch(
           routerRedux.push({
-            pathname: '/tag/group/add',
+            pathname: '/tag/tagGroup/add',
             state: filters,
           })
         );
@@ -91,6 +91,8 @@ export default class TagGroup extends PureComponent {
       dataSource: list,
       loading: listLoading,
       curStatus: filters.status,
+      curMerchant: filters.merchantId,
+      merchantList: merchant.list,
       onChange: (pagination, filterArgs) => {
         const getValue = obj => Object.keys(obj).map(key => obj[key]).join(',');
         const tableFilters = Object.keys(filterArgs).reduce((obj, key) => {
@@ -99,14 +101,6 @@ export default class TagGroup extends PureComponent {
           return newObj;
         }, {});
 
-        // // 如果filters中包含field,keyword整理成k:v形式
-        // const newFilters = { ...filters };
-        // if (newFilters.field && newFilters.keyword) {
-        //   newFilters[newFilters.field] = newFilters.keyword;
-        //   delete newFilters.field;
-        //   delete newFilters.keyword;
-        // }
-        //
         const data = { ...filters, ...tableFilters };
         Object.keys(data).map(key => data[key] ? null : delete data[key]);
         dispatch(routerRedux.push({
@@ -121,7 +115,7 @@ export default class TagGroup extends PureComponent {
       onEditItem: (item) => {
         dispatch(
           routerRedux.push({
-            pathname: `/tag/group/edit/${item.id}`,
+            pathname: `/tag/tagGroup/edit/${item.id}`,
             state: filters,
           })
         );

+ 2 - 4
src/routes/TagGroup/search.js

@@ -1,7 +1,7 @@
 import react, { PureComponent } from 'react';
 import PropTypes from 'prop-types';
 import { Button, Form, Row, Col, Icon } from 'antd';
-import DataSearch from '../../components/DataSearch';
+import DataSearch from '../../../components/DataSearch';
 
 @Form.create()
 export default class Search extends PureComponent {
@@ -26,8 +26,6 @@ export default class Search extends PureComponent {
         value: 'name', name: '标签组名称', mode: 'input',
       },{
         value: 'code', name: '标签组编号', mode: 'input',
-      },{
-        value: 'merchantId', name: '渠道名称', mode: 'select',
       }],
       selectProps: {
         defaultValue: field || 'name',
@@ -43,7 +41,7 @@ export default class Search extends PureComponent {
           <DataSearch { ...searchGroupProps } />
         </Col>
         <Col lg={{ offset: 7, span: 7 }} md={12} sm={8} xs={24} style={{ marginBottom: 16, textAlign: 'right' }}>
-          <Button type="ghost" onClick={onAdd}><Icon type="plus-circle" />添加</Button>
+          <Button type="primary" onClick={onAdd}><Icon type="plus-circle" />新建标签组</Button>
         </Col>
       </Row>
     );

+ 17 - 12
src/routes/TagGroup/table.js

@@ -3,10 +3,10 @@ import PropTypes from 'prop-types';
 import moment from 'moment';
 import classnames from 'classnames';
 import queryString from 'query-string';
-import { Modal, Table, Menu, Icon, Badge } from 'antd';
-import AnimTableBody from '../../components/Animation/AnimTableBody';
+import { Divider, Modal, Table, Menu, Icon, Badge } from 'antd';
+import AnimTableBody from '../../../components/Animation/AnimTableBody';
 import styles from './table.less';
-import { statuses, Codes } from '../../utils/config';
+import { statuses, Codes } from '../../../utils/config';
 
 const confirm = Modal.confirm;
 
@@ -21,7 +21,7 @@ export default class TableList extends PureComponent {
   handleOperateItem = (record) => {
     const { onDeleteItem, onRecoverItem } = this.props;
     confirm({
-      title: `您确定要${record.status === Codes.CODE_NORMAL ? '删除' : '恢复'}该条记录?`,
+      title: `您确定要${record.status === Codes.CODE_NORMAL ? '删除' : '恢复'}该标签组?`,
       onOk () {
         if (record.status === Codes.CODE_NORMAL) {
           onDeleteItem({id: record.id});
@@ -33,7 +33,7 @@ export default class TableList extends PureComponent {
   }
 
   render() {
-    const { curStatus, onDeleteItem, onRecoverItem, onEditItem, location, pagination, ...tableProps } = this.props;
+    const { curStatus, curMerchant, merchantList, onDeleteItem, onRecoverItem, onEditItem, location, pagination, ...tableProps } = this.props;
 
     const columns = [{
       title: '标签组编号',
@@ -44,9 +44,13 @@ export default class TableList extends PureComponent {
       dataIndex: 'name',
       key: 'name',
     },{
-      title: '渠道平台',
+      title: '渠道名称',
       dataIndex: 'merchantId',
       key: 'merchantId',
+      render: (text, record) => record.merchantName,
+      filters: merchantList.map(item => ({ text: item.name, value: item.id })),
+      filterMultiple: false,
+      filteredValue: [curMerchant],
     },{
       title: '状态',
       dataIndex: 'status',
@@ -59,11 +63,11 @@ export default class TableList extends PureComponent {
       filterMultiple: false,
       filteredValue: [curStatus],
     },{
-      title: '添加时间',
-      dataIndex: 'gmtCreated',
-      key: 'gmtCreated',
+      title: '修改时间',
+      dataIndex: 'gmtModified',
+      key: 'gmtModified',
       render: (text, record) => (
-        <div>{moment(text).format('YYYY-MM-DD')}</div>
+        <div>{moment(text).format('YYYY-MM-DD HH:mm:ss')}</div>
       )
     },{
       title: '操作',
@@ -72,8 +76,8 @@ export default class TableList extends PureComponent {
       render: (text, record) => (
         <div>
           <a onClick={() => onEditItem(record)}>编辑</a>
-          <span className={styles.splitLine} />
-            <a onClick={() => this.handleOperateItem(record)}>{record.status === Codes.CODE_NORMAL ? '删除' : '恢复'}</a>
+          <Divider type="vertical" />
+          <a onClick={() => this.handleOperateItem(record)}>{record.status === Codes.CODE_NORMAL ? '删除' : '恢复'}</a>
         </div>
       )
     }];
@@ -81,6 +85,7 @@ export default class TableList extends PureComponent {
     // 数据table列表表头的筛选按钮点击重置后status与domain值为空,此时删除该参数
     columns.map(item => {
       item.dataIndex === 'status' && !curStatus ? delete item.filteredValue : null;
+      item.dataIndex === 'merchantId' && !curMerchant ? delete item.filteredValue : null;
     });
 
     tableProps.pagination = !!pagination && { ...pagination, showSizeChanger: true, showQuickJumper: true, showTotal: total => `共 ${total} 条`};

+ 3 - 11
src/routes/TagGroup/table.less

@@ -1,5 +1,5 @@
 @import "~antd/lib/style/themes/default.less";
-@import "../../utils/utils.less";
+@import "../../../utils/utils.less";
 
 .table {
   :global {
@@ -26,11 +26,11 @@
         }
 
         &:nth-child(4) {
-          width: 17%;
+          width: 16%;
         }
 
         &:nth-child(5) {
-          width: 17%;
+          width: 18%;
         }
 
         &:nth-child(6) {
@@ -72,11 +72,3 @@
     }
   }
 }
-
-.splitLine {
-  background: @border-color-split;
-  display: inline-block;
-  margin: 0 8px;
-  width: 1px;
-  height: 12px;
-}

+ 41 - 0
src/services/goods.js

@@ -0,0 +1,41 @@
+import { stringify } from 'qs';
+import request from '../utils/request';
+import { mproducts, merchantProductDetailAPI, createMerchantProductAPI } from '../utils/api';
+
+export async function query(params) {
+  return request(`${mproducts}?${stringify(params)}`);
+}
+
+export async function queryOne(params) {
+  return request(`${merchantProductDetailAPI}?${stringify(params)}`);
+}
+
+export async function createMerchantProduct(params) {
+  const options = {
+    method: 'PUT',
+    body: JSON.stringify(params),
+  }
+  return request(`${createMerchantProductAPI}`, options);
+}
+
+
+// export async function create(params) {
+//   const options = {
+//     method: 'POST',
+//     body: JSON.stringify(params),
+//   };
+//   return request(`${merchantProduct.replace('/:id', '')}`, options);
+// }
+//
+// export async function update(params) {
+//   const options = {
+//     method: 'PUT',
+//     body: JSON.stringify(params),
+//   };
+//   return request(`${merchantProduct.replace('/:id', '')}`, options);
+// }
+//
+// export async function remove({ id }) {
+//   const options = { method: 'DELETE' }
+//   return request(`${merchantProduct.replace('/:id', `/${id}`)}`, options);
+// }

+ 4 - 4
src/services/group.js

@@ -7,7 +7,7 @@ export async function query(params) {
 }
 
 export async function queryOne({ id }) {
-  return request(`${group.replace('/:id', `/${id}`)}`);
+  return request(`${group}/${id}`);
 }
 
 export async function create(params) {
@@ -15,7 +15,7 @@ export async function create(params) {
     method: 'POST',
     body: JSON.stringify(params),
   };
-  return request(`${group.replace('/:id', '')}`, options);
+  return request(`${group}`, options);
 }
 
 export async function update(params) {
@@ -23,10 +23,10 @@ export async function update(params) {
     method: 'PUT',
     body: JSON.stringify(params),
   };
-  return request(`${group.replace('/:id', '')}`, options);
+  return request(`${group}`, options);
 }
 
 export async function remove({ id }) {
   const options = { method: 'DELETE' }
-  return request(`${group.replace('/:id', `/${id}`)}`, options);
+  return request(`${group}/${id}`, options);
 }

+ 49 - 0
src/services/product.js

@@ -0,0 +1,49 @@
+import { stringify } from 'qs';
+import request from '../utils/request';
+import { product } from '../utils/api';
+
+/**
+ * @desc 获取全部产品 /product
+ */
+export async function query(params) {
+  return request(`${product}?${stringify(params)}`);
+}
+
+/**
+ * @desc 获取一个产品 /product/<productId>
+ */
+export async function queryOne({ id }) {
+  return request(`${product}/${id}`);
+}
+
+/**
+ * @desc 创建一个产品
+ */
+export async function create(params) {
+  const { type, ...rest } = params;
+  const options = {
+    method: 'POST',
+    body: JSON.stringify(rest),
+  };
+  return request(`${product}?type=${type}`, options);
+}
+
+/**
+ * @desc 修改一个产品 put /product/<productId>
+ */
+export async function update(params) {
+  const { id, ...rest } = params;
+  const options = {
+    method: 'PUT',
+    body: JSON.stringify(rest),
+  };
+  return request(`${product}/${id}`, options);
+}
+
+/**
+ * @desc 下架一个产品 delete /product/<productId>
+ */
+export async function remove({ id }) {
+  const options = { method: 'DELETE' }
+  return request(`${product}/${id}`, options);
+}

+ 0 - 3
src/services/resource.js

@@ -10,9 +10,6 @@ export async function getSignature (params) {
   let newSignature;
   if (!localSignature || localSignature.expire <= expireTime) {
     return request(`${signature}?${stringify(params)}`);
-    // request(`${signature}?${stringify(params)}`).then(res => { newSignature = res.data });
-    // saveLocalSignature(newSignature);
-    // return { data: newSignature };
   } else {
     return { data: localSignature };
   }

+ 1 - 1
src/theme.js

@@ -2,5 +2,5 @@
 module.exports = {
   // 'primary-color': '#10e99b',
   'card-actions-background': '#f5f8fa',
-  'border-radius-base': '1px',
+  // 'border-radius-base': '4px',
 };

+ 20 - 7
src/utils/api.js

@@ -14,20 +14,33 @@ module.exports = {
   terminal: `${config.apiHost}/user/:id`,
   merchants: `${config.apiHost}/merchant/list`,
   merchant: `${config.apiHost}/merchant/:id`,
+
+  // 标签组及标签
   groups: `${config.apiHost}/group/list`,
-  group: `${config.apiHost}/group/:id`,
+  group: `${config.apiHost}/group`,
   tags: `${config.apiHost}/tag/list`,
   tag: `${config.apiHost}/tag/:id`,
+
   wares: `${config.apiHost}/ware/list`,
   ware: `${config.apiHost}/ware/:id`,
   lessons: `${config.apiHost}/lesson/list`,
   lesson: `${config.apiHost}/lesson/:id`,
-  courses: `${config.apiHost}/course/list`,
-  course: `${config.apiHost}/course/:id`,
-  supports: `${config.apiHost}/support/list`,
-  support: `${config.apiHost}/support/:id`,
-  combos: `${config.apiHost}/combo/list`,
-  combo: `${config.apiHost}/combo/:id`,
+
+  // 产品接口,包括:课程、周边、课程包
+  product: `${config.apiHost}/product`,
+  course: `${config.apiHost}/product/course`,
+  support: `${config.apiHost}/product/support`,
+  combo: `${config.apiHost}/product/package`,
+
+  // 渠道产品接口
+  mproducts: `${config.apiHost}/merchant/product`,
+  merchantProductDetailAPI: `${config.apiHost}/merchant/product/detail`,
+  createMerchantProductAPI: `${config.apiHost}/merchant/product/status`,
+
+  // 订单接口
   orders: `${config.apiHost}/orders`,
   order: `${config.apiHost}/order/:id`,
+
+  // 商品挂载标签
+  goodsWithTag: `${config.apiHost}/merchant/product/tags`,
 };

+ 22 - 5
src/utils/config.js

@@ -5,10 +5,16 @@ Codes.CODE_VIDEO = 0;
 Codes.CODE_AUDIO = 1;
 Codes.CODE_LIVE  = 2;
 Codes.CODE_IMAGE = 3;
+
 Codes.CODE_NORMAL = 'NORMAL';
 Codes.CODE_DELETE = 'DEL';
+
+Codes.CODE_LESSON = 'WARE';
+Codes.CODE_LESSON = 'LESSON';
 Codes.CODE_COURSE = 'COURSE';
 Codes.CODE_SUPPORT = 'SUPPORT';
+Codes.CODE_PACKAGE = 'PACKAGE';
+
 Codes.CODE_SALE = 'SALE';
 Codes.CODE_UNPAID = 0;
 Codes.CODE_PAID = 1;
@@ -18,12 +24,17 @@ Codes.CODE_CP = 2010;
 Codes.CODE_PJ = 3010;
 
 module.exports = {
-  // apiHost: 'http://lj.dev.cms.api.com:8500',
-  apiHost: '/api',
+  apiHost: 'http://lj.dev.cms.api.com:8500',
+  // apiHost: '/api',
   // 每页返回数据量
   pageSize: 10,
   // 标识码
   Codes,
+  // 视频质量
+  quality: {
+    high: '高清',
+    standard: '标清',
+  },
   // 状态类型
   statuses: {
     [Codes.CODE_NORMAL]: '使用中',
@@ -31,8 +42,14 @@ module.exports = {
   },
   // 商品出售状态
   itemStatuses: {
-    [Codes.CODE_SALE]: '出售中',
-    [Codes.CODE_DELETE]: '已下架',
+    [Codes.CODE_NORMAL]: '在售',
+    [Codes.CODE_DELETE]: '下架',
+  },
+  // 产品类型
+  productType: {
+    [Codes.CODE_COURSE] : '课程',
+    [Codes.CODE_SUPPORT]: '配套',
+    [Codes.CODE_PACKAGE]: '课程包',
   },
   // 平台代号
   domains: {
@@ -50,7 +67,7 @@ module.exports = {
   // 标签类型
   tagType: {
     [Codes.CODE_COURSE] : '课程',
-    [Codes.CODE_SUPPORT]: '周边',
+    [Codes.CODE_SUPPORT]: '配套',
   },
   // 订单状态
   orderStatuses: {

+ 14 - 50
src/utils/request.js

@@ -1,6 +1,8 @@
 import fetch from 'dva/fetch';
 import { stringify } from 'qs';
 import { message, notification } from 'antd';
+import { routerRedux } from 'dva/router';
+import store from '../index';
 
 // HTTP响应状态码
 const httpCodeMessage = {
@@ -24,6 +26,7 @@ const httpCodeMessage = {
 // 自定义响应状态码
 const customCodeMessage = {
   10004: 'Token认证失败',
+  10002: '账号或密码错误',
   800  : '数据不存在',
 };
 
@@ -42,7 +45,6 @@ function httpErrorHandler(response) {
   const error = new Error(errortext);
   error.response = response;
   throw error;
-  return { error };
 }
 
 /**
@@ -52,11 +54,15 @@ function httpErrorHandler(response) {
 function apiErrorHandler(data) {
   if (!data.success) {
     const errortext = customCodeMessage[data.code] || data.message;
-    // Token认证失败,错误继续外抛,让全局app对象捕获,跳转到登录界面
+    // Token认证失败, 跳转到登录界面
     if (data.code === 10004) {
-      const error = new Error(errortext);
-      error.response = data;
-      throw error;
+      message.error('登录失效,请重新登录!');
+      const { dispatch } = store;
+      dispatch(routerRedux.push('/user/login'));
+    // 登录密码错误
+    } else if (data.code === 10002) {
+      message.error(errortext);
+    // 其它错误打出错误代码
     } else {
       message.error(`请求错误 错误代码:${data.code} 错误信息:${errortext}`);
     }
@@ -65,36 +71,6 @@ function apiErrorHandler(data) {
 }
 
 /**
- * @desc 处理未预知到的网络错误
- * @return {[object]} {error}
- */
-function unpredictableErrorHandler(error) {
-  if (!error.response) {
-    console.error(error);
-    message.error('出现未知的网络问题,请检查日志或联系管理员');
-  }
-  if (error.response && error.response.code === 10004) {
-    throw error;
-  }
-  return { error };
-};
-
-/**
- * @desc 处理请求超时错误
- * @return {[object]} {error}
- */
-function timeoutErrorHandler(error) {
-  // 这里只会捕获两种错误,一是超时错误,一是认证失效错误,认证失效错误继续外抛
-  if (!error.response) {
-    message.error('请求超时,请检查网络状态是否可用!');
-  }
-  else if (error && error.response.code === 10004) {
-    throw error;
-  }
-  return { error };
-}
-
-/**
  * response为promise对象,转换为json
  */
 function promise2Json(response) {
@@ -102,18 +78,6 @@ function promise2Json(response) {
 }
 
 /**
- * fetch api不支持超时设置,进行浅度加工,变相实现超时设定
- */
-function _fetch(requestPromise, timeout=8000) {
-  let timeoutAction = null;
-  const timerPromise = new Promise((resolve, reject) => {
-    timeoutAction = () => { reject() }
-  });
-  setTimeout(() => { timeoutAction() }, timeout);
-  return Promise.race([requestPromise,timerPromise]);
-};
-
-/**
  * Requests a URL, returning an object or none.
  *
  * @param  {string} url       The URL we want to request
@@ -140,10 +104,10 @@ export default function request(url, options) {
       ...newOptions.headers,
     }
   }
-  const originRequest = fetch(url, newOptions)
+
+  return fetch(url, newOptions)
     .then(httpErrorHandler)
     .then(promise2Json)
     .then(apiErrorHandler)
-    .catch(unpredictableErrorHandler)
-  return _fetch(originRequest).catch(timeoutErrorHandler);
+    .catch(e => ({ e }));
 }