Переглянути джерело

完善渠道方管理相关模块

zhanghe 7 роки тому
батько
коміт
d8d5d3cdcb
65 змінених файлів з 1642 додано та 3128 видалено
  1. 1 13
      src/common/menu.js
  2. 14 20
      src/common/router.js
  3. 1 1
      src/index.js
  4. 0 119
      src/index.less
  5. 1 1
      src/layouts/BasicLayout.js
  6. 0 1
      src/models/campus.js
  7. 2 2
      src/models/login.js
  8. 3 3
      src/models/merchant.js
  9. 3 47
      src/models/mproduct/detail.js
  10. 3 18
      src/models/mproduct/mproduct.js
  11. 55 22
      src/models/order/detail.js
  12. 11 9
      src/models/order/order.js
  13. 0 1
      src/models/product.js
  14. 0 63
      src/models/recommend.js
  15. 57 0
      src/models/soldProduct.js
  16. 0 10
      src/routes/About/index.js
  17. 1 3
      src/routes/Campus/index.js
  18. 4 2
      src/routes/Campus/modal.js
  19. 3 14
      src/routes/Campus/search.js
  20. 8 21
      src/routes/Campus/table.js
  21. 0 74
      src/routes/Campus/table.less
  22. 0 170
      src/routes/CmsUser/index.js
  23. 0 123
      src/routes/CmsUser/modal.js
  24. 0 43
      src/routes/CmsUser/search.js
  25. 0 119
      src/routes/CmsUser/table.js
  26. 0 90
      src/routes/CmsUser/table.less
  27. 0 191
      src/routes/Dashboard/index.js
  28. 0 104
      src/routes/Dashboard/index.less
  29. 0 139
      src/routes/Frontend/index.js
  30. 0 137
      src/routes/Frontend/recommend.js
  31. 0 127
      src/routes/Frontend/recommend.less
  32. 6 52
      src/routes/MProduct/List/index.js
  33. 5 16
      src/routes/MProduct/List/search.js
  34. 11 47
      src/routes/MProduct/List/table.js
  35. 0 82
      src/routes/MProduct/List/table.less
  36. 11 86
      src/routes/MProduct/Edit/index.js
  37. 0 0
      src/routes/MProduct/View/price.js
  38. 0 0
      src/routes/MProduct/View/tags.js
  39. 1 26
      src/routes/Merchant/index.js
  40. 499 145
      src/routes/Order/Add/index.js
  41. 0 66
      src/routes/Order/Add/index.less
  42. 18 55
      src/routes/Order/Add/product.js
  43. 4 12
      src/routes/Order/Add/terminal.js
  44. 361 0
      src/routes/Order/Edit/SubOrderProfile.js
  45. 0 87
      src/routes/Order/Edit/modal.js
  46. 0 119
      src/routes/Order/Edit/modal.less
  47. 242 54
      src/routes/Order/Edit/orderProfile.js
  48. 0 8
      src/routes/Order/Edit/orderProfile.less
  49. 26 59
      src/routes/Order/List/index.js
  50. 2 16
      src/routes/Order/List/search.js
  51. 53 159
      src/routes/Order/List/table.js
  52. 0 82
      src/routes/Order/List/table.less
  53. 100 119
      src/routes/SoldProduct/index.js
  54. 10 40
      src/routes/SoldProduct/index.less
  55. 5 9
      src/routes/Terminal/Edit/campus.js
  56. 28 16
      src/routes/Terminal/Edit/index.js
  57. 0 7
      src/routes/Terminal/List/index.js
  58. 9 17
      src/routes/Terminal/List/table.js
  59. 1 6
      src/services/merchant.js
  60. 28 5
      src/services/order.js
  61. 2 2
      src/services/sales.js
  62. 3 0
      src/theme.js
  63. 3 28
      src/utils/api.js
  64. 40 14
      src/utils/config.js
  65. 7 7
      src/utils/request.js

+ 1 - 13
src/common/menu.js

@@ -1,12 +1,8 @@
 const menuData = [
   {
-    name: '主页',
-    icon: 'dashboard',
-    path: 'dashboard',
-  }, {
     name: '产品库',
     icon: 'shop',
-    path: 'goods',
+    path: 'products',
   }, {
     name: '订单管理',
     icon: 'trademark',
@@ -24,17 +20,9 @@ const menuData = [
     icon: 'desktop',
     path: 'terminal',
   }, {
-    name: '前端展现',
-    icon: 'setting',
-    path: 'frontend',
-  }, {
     name: '账户信息',
     icon: 'user',
     path: 'merchant',
-  }, {
-    name: '使用说明',
-    icon: 'question-circle-o',
-    path: 'help',
   },
 ];
 

+ 14 - 20
src/common/router.js

@@ -36,20 +36,10 @@ export const getRouterData = (app) => {
     '/': {
       component: dynamicWrapper(app, ['user', 'login'], () => import('../layouts/BasicLayout')),
     },
-    '/help': {
-      component: dynamicWrapper(app, [], () => import('../routes/About')),
-    },
-    '/dashboard': {
-      component: dynamicWrapper(app, [], () => import('../routes/Dashboard')),
-    },
     '/merchant': {
       component: dynamicWrapper(app, ['merchant'], () => import('../routes/Merchant')),
       name: '修改厂商信息',
     },
-    '/frontend': {
-      component: dynamicWrapper(app, ['recommend', 'mproduct/mproduct'], () => import('../routes/Frontend')),
-      name: '前端展现配置',
-    },
     '/campus': {
       component: dynamicWrapper(app, ['campus'], () => import('../routes/Campus')),
     },
@@ -64,9 +54,13 @@ export const getRouterData = (app) => {
       component: dynamicWrapper(app, ['terminal/detail', 'campus'], () => import('../routes/Terminal/Edit')),
       name: '编辑终端',
     },
-    '/goods': {
+    '/products': {
       component: dynamicWrapper(app, ['mproduct/mproduct'], () => import('../routes/MProduct/List')),
     },
+    '/products/view': {
+      component: dynamicWrapper(app, ['mproduct/detail'], () => import('../routes/MProduct/View')),
+      name: '产品详情',
+    },
     '/order': {
       component: dynamicWrapper(app, ['order/order'], () => import('../routes/Order/List')),
     },
@@ -74,16 +68,16 @@ export const getRouterData = (app) => {
       component: dynamicWrapper(app, ['order/detail', 'terminal/terminal', 'mproduct/mproduct'], () => import('../routes/Order/Add')),
       name: '新建订单',
     },
-    // '/order/edit/:id': {
-    //   component: dynamicWrapper(app, ['order/detail'], () => import('../routes/Order/detail')),
-    //   name: '修改订单',
-    // },
-    // '/order/profile/:id': {
-    //   component: dynamicWrapper(app, ['order/detail'], () => import('../routes/Order/detail/orderProfile')),
-    //   name: '订单详情',
-    // },
+    '/order/profile/:id': {
+      component: dynamicWrapper(app, ['order/detail'], () => import('../routes/Order/Edit/OrderProfile')),
+      name: '订单详情',
+    },
+    '/order/sub/profile/:id': {
+      component: dynamicWrapper(app, ['order/detail'], () => import('../routes/Order/Edit/SubOrderProfile')),
+      name: '订单详情',
+    },
     '/sold': {
-      component: dynamicWrapper(app, [], () => import('../routes/SoldProduct')),
+      component: dynamicWrapper(app, ['soldProduct', 'merchant'], () => import('../routes/SoldProduct')),
     },
     '/user': {
       component: dynamicWrapper(app, [], () => import('../layouts/UserLayout')),

+ 1 - 1
src/index.js

@@ -1,9 +1,9 @@
 import 'babel-polyfill';
 import dva from 'dva';
+import browserHistory from 'history/createBrowserHistory';
 import 'moment/locale/zh-cn';
 import './g2';
 // import './rollbar';
-import browserHistory from 'history/createBrowserHistory';
 import './index.less';
 
 // 1. Initialize

+ 0 - 119
src/index.less

@@ -17,125 +17,6 @@ body {
   height: 8px;
 }
 
-:global {
-  .ant-breadcrumb {
-    & > span {
-      &:last-child {
-        color: #999;
-        font-weight: normal;
-      }
-    }
-  }
-
-  .ant-breadcrumb-link {
-    .anticon + span {
-      margin-left: 4px;
-    }
-  }
-
-  .ant-table {
-    .ant-table-thead > tr > th {
-      text-align: center;
-    }
-
-    .ant-table-tbody > tr > td {
-      text-align: center;
-    }
-
-    &.ant-table-small {
-      .ant-table-thead > tr > th {
-        background: #f7f7f7;
-      }
-
-      .ant-table-body > table {
-        padding: 0;
-      }
-    }
-  }
-
-  .ant-table-pagination {
-    float: none!important;
-    display: table;
-    margin: 16px auto !important;
-  }
-
-  .ant-popover-inner {
-    border: none;
-    border-radius: 0;
-    box-shadow: 0 0 20px rgba(100, 100, 100, 0.2);
-  }
-
-  .vertical-center-modal {
-    display: flex;
-    align-items: center;
-    justify-content: center;
-
-    .ant-modal {
-      top: 0;
-
-      .ant-modal-body {
-        max-height: 80vh;
-        overflow-y: auto;
-      }
-    }
-  }
-
-  .ant-form-item-control {
-    vertical-align: middle;
-  }
-
-  .ant-modal-mask {
-    background-color: rgba(55, 55, 55, 0.2);
-  }
-
-  .ant-modal-content {
-    box-shadow: none;
-  }
-
-  .ant-select-dropdown-menu-item {
-    padding: 12px 16px !important;
-  }
-
-  .margin-right {
-    margin-right: 16px;
-  }
-
-  a:focus {
-    text-decoration: none;
-  }
-}
-@media (min-width: 1600px) {
-  :global {
-    .ant-col-xl-48 {
-      width: 20%;
-    }
-
-    .ant-col-xl-96 {
-      width: 40%;
-    }
-  }
-}
-@media (max-width: 767px) {
-  :global {
-    .ant-pagination-item,
-    .ant-pagination-next,
-    .ant-pagination-options,
-    .ant-pagination-prev {
-      margin-bottom: 8px;
-    }
-
-    .ant-card {
-      .ant-card-head {
-        padding: 0 12px;
-      }
-
-      .ant-card-body {
-        padding: 12px;
-      }
-    }
-  }
-}
-
 .globalSpin {
   width: 100%;
   margin: 40px 0 !important;

+ 1 - 1
src/layouts/BasicLayout.js

@@ -130,7 +130,7 @@ class BasicLayout extends React.PureComponent {
                     />
                   ))
                 }
-                <Redirect exact from="/" to="/dashboard" />
+                <Redirect exact from="/" to="/sold" />
                 <Route render={NotFound} />
               </Switch>
             </div>

+ 0 - 1
src/models/campus.js

@@ -21,7 +21,6 @@ export default modelExtend(pageModel, {
     setup({ dispatch, history }) {
       history.listen((location) => {
         if (location.pathname === '/campus') {
-          const { merchantId } = getLocalUser();
           const params = checkSearchParams(queryString.parse(location.search));
           dispatch({
             type: 'query',

+ 2 - 2
src/models/login.js

@@ -13,9 +13,9 @@ export default {
       yield put({ type: 'save', payload: { loading: true } });
       const { data, success } = yield call(login, payload);
       if (success) {
-        addLocalUser(data);
         message.success('登录成功');
-        yield put(routerRedux.push('/'));
+        addLocalUser(data);
+        yield put(routerRedux.push('/sold'));
       }
       yield put({ type: 'save', payload: { loading: false } });
     },

+ 3 - 3
src/models/merchant.js

@@ -12,7 +12,7 @@ export default {
 
   subscriptions: {
     setup({ dispatch, history }) {
-      history.listen(({ pathname, state }) => {
+      history.listen(({ pathname }) => {
         if (pathname === '/merchant') {
           const { merchantId } = getLocalUser();
           dispatch({ type: 'query', payload: { id: merchantId } });
@@ -30,8 +30,8 @@ export default {
       }
       yield put({ type: 'changeLoading', payload: { loading: false } });
     },
-    * update({ payload, callback }, { call, put }) {
-      const { data, success } = yield call(update, payload);
+    * update({ payload, callback }, { call }) {
+      const { success } = yield call(update, payload);
       if (success) {
         message.success('修改成功!');
         if (callback) callback();

+ 3 - 47
src/models/mproduct/detail.js

@@ -1,7 +1,6 @@
-import { queryOne, createMerchantProduct } from '../../services/mproduct';
 import pathToRegexp from 'path-to-regexp';
 import queryString from 'query-string';
-import { Codes } from '../../utils/config';
+import { queryOne } from '../../services/mproduct';
 
 export default {
   namespace: 'mproductDetail',
@@ -14,16 +13,13 @@ export default {
 
   subscriptions: {
     setup({ dispatch, history }) {
-      history.listen(({ pathname, state, search, ...rest }) => {
-        const match = pathToRegexp('/goods/edit').exec(pathname);
+      history.listen(({ pathname, state, search }) => {
+        const match = pathToRegexp('/products/view').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 });
-        }
       });
     },
   },
@@ -38,57 +34,17 @@ export default {
       }
       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();
-      }
-    },
   },
 
   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 };
-    },
   },
 };

+ 3 - 18
src/models/mproduct/mproduct.js

@@ -1,9 +1,8 @@
-import { query, update, createMerchantProduct } from '../../services/mproduct';
 import modelExtend from 'dva-model-extend';
 import queryString from 'query-string';
-import { message } from 'antd';
+import { query } from '../../services/mproduct';
 import { pageModel } from '../common';
-import { pageSize, Codes } from '../../utils/config';
+import { pageSize } from '../../utils/config';
 import { checkSearchParams } from '../../utils/utils';
 import { getLocalUser } from '../../utils/helper';
 
@@ -15,7 +14,7 @@ export default modelExtend(pageModel, {
   subscriptions: {
     setup({ dispatch, history }) {
       history.listen((location) => {
-        if (location.pathname === '/goods') {
+        if (location.pathname === '/products') {
           const payload = checkSearchParams(queryString.parse(location.search));
           dispatch({
             type: 'query',
@@ -46,20 +45,6 @@ export default modelExtend(pageModel, {
       }
       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: {

+ 55 - 22
src/models/order/detail.js

@@ -1,6 +1,6 @@
-import { queryOne, create, update } from '../../services/order';
 import pathToRegexp from 'path-to-regexp';
-import { Codes } from '../../utils/config';
+import { message } from 'antd';
+import { queryOne, querySubOrder, create, remove, confirmPay, confirmSend, confirmReceive } from '../../services/order';
 
 export default {
   namespace: 'orderDetail',
@@ -12,16 +12,22 @@ export default {
     itemLoading: false,
     terminalModalShow: false,
     productModalShow: false,
+    deliveryModalShow: false,
   },
 
   subscriptions: {
     setup({ dispatch, history }) {
-      history.listen(({ pathname, state, ...rest }) => {
+      history.listen(({ pathname, state }) => {
         const match = pathToRegexp('/order/profile/:id').exec(pathname);
+        const sub = pathToRegexp('/order/sub/profile/:id').exec(pathname);
         if (match) {
           dispatch({ type: 'query', payload: { id: match[1] } });
           dispatch({ type: 'saveFilters', payload: state });
           dispatch({ type: 'saveOperType', payload: { operType: 'view' } });
+        } else if (sub) {
+          dispatch({ type: 'querySubOrder', payload: { id: sub[1] } });
+          dispatch({ type: 'saveFilters', payload: state });
+          dispatch({ type: 'saveOperType', payload: { operType: 'view' } });
         }
       });
     },
@@ -36,17 +42,49 @@ export default {
       }
       yield put({ type: 'changeLoading', payload: { itemLoading: false } });
     },
+    * querySubOrder({ payload }, { call, put }) {
+      yield put({ type: 'changeLoading', payload: { itemLoading: true } });
+      const { data, success } = yield call(querySubOrder, payload);
+      if (success) {
+        yield put({ type: 'querySuccess', payload: { ...data } });
+      }
+      yield put({ type: 'changeLoading', payload: { itemLoading: false } });
+    },
     * create({ payload, callback }, { call, put }) {
-      const { data, success } = yield call(create, { ...payload });
+      const { success } = yield call(create, { ...payload });
+      if (success) {
+        message.success('提交成功!');
+        yield put({ type: 'initState' });
+        if (callback) callback();
+      }
+    },
+    * remove({ payload, callback }, { call }) {
+      console.log(payload);
+      const { success } = yield call(remove, payload);
       if (success) {
-        yield put({ type: 'cacheCleaner' });
+        message.success('操作成功!');
         if (callback) callback();
       }
     },
-    * update({ payload, callback }, { call, put }) {
-      const { data, success } = yield call(update, payload);
+    * orderPay({ payload, callback }, { call }) {
+      const { success } = yield call(confirmPay, payload);
       if (success) {
-        yield put({ type: 'cacheCleaner' });
+        message.success('支付成功!');
+        if (callback) callback();
+      }
+    },
+    * orderSend({ payload, callback }, { call, put }) {
+      const { success } = yield call(confirmSend, payload);
+      if (success) {
+        message.success('发货成功!');
+        yield put({ type: 'hideDeliveryModal' });
+        if (callback) callback();
+      }
+    },
+    * orderReceive({ payload, callback }, { call }) {
+      const { success } = yield call(confirmReceive, payload);
+      if (success) {
+        message.success('收货成功!');
         if (callback) callback();
       }
     },
@@ -62,33 +100,28 @@ export default {
     saveFilters(state, { payload: filters }) {
       return { ...state, filters };
     },
+    saveOuterParams(state, action) {
+      return { ...state, ...action.payload };
+    },
     showTerminalModal(state, action) {
       return { ...state, ...action.payload, terminalModalShow: true };
     },
     hideTerminalModal(state, action) {
       return { ...state, ...action.payload, terminalModalShow: false };
     },
-    savePickedTerminal(state, action) {
-      return {
-        ...state,
-        currentItem: { ...state.currentItem, ...action.payload },
-        terminalModalShow: false,
-      };
-    },
     showProductModal(state, action) {
       return { ...state, ...action.payload, productModalShow: true };
     },
     hideProductModal(state, action) {
       return { ...state, ...action.payload, productModalShow: false };
     },
-    savePickedProducts(state, action) {
-      return {
-        ...state,
-        currentItem: { goods: action.payload },
-        productModalShow: false,
-      };
+    showDeliveryModal(state, action) {
+      return { ...state, ...action.payload, deliveryModalShow: true };
+    },
+    hideDeliveryModal(state, action) {
+      return { ...state, ...action.payload, deliveryModalShow: false };
     },
-    cacheCleaner(state) {
+    initState(state) {
       return { ...state, currentItem: {}, itemLoading: false };
     },
   },

+ 11 - 9
src/models/order/order.js

@@ -1,9 +1,10 @@
-import { query, create, update, remove } from '../../services/order';
 import modelExtend from 'dva-model-extend';
 import queryString from 'query-string';
+import { query, create, update, remove } from '../../services/order';
 import { pageModel } from '../common';
 import { pageSize } from '../../utils/config';
 import { checkSearchParams } from '../../utils/utils';
+import { getLocalUser } from '../../utils/helper';
 
 export default modelExtend(pageModel, {
   namespace: 'order',
@@ -33,7 +34,8 @@ export default modelExtend(pageModel, {
   effects: {
     * query({ payload = {} }, { call, put }) {
       yield put({ type: 'changeLoading', payload: { listLoading: true } });
-      const { data, success } = yield call(query, payload);
+      const { merchantId } = getLocalUser();
+      const { data, success } = yield call(query, { ...payload, merchantId });
       if (success) {
         yield put({
           type: 'querySuccess',
@@ -49,27 +51,27 @@ export default modelExtend(pageModel, {
       }
       yield put({ type: 'changeLoading', payload: { listLoading: false } });
     },
-    * create({ payload, callback }, { call, put }) {
-      const { data, success } = yield call(create, payload);
+    * create({ payload, callback }, { call }) {
+      const { success } = yield call(create, payload);
       if (success) {
         if (callback) callback();
       }
     },
     * update({ payload, callback }, { call, put }) {
-      const { data, success } = yield call(update, payload);
+      const { success } = yield call(update, payload);
       if (success) {
         yield put({ type: 'hideModal' });
         if (callback) callback();
       }
     },
-    * delete({ payload, callback }, { call, put }) {
-      const { data, success } = yield call(remove, payload);
+    * delete({ payload, callback }, { call }) {
+      const { success } = yield call(remove, payload);
       if (success) {
         if (callback) callback();
       }
     },
-    * recover({ payload, callback }, { call, put }) {
-      const { data, success } = yield call(update, payload);
+    * recover({ payload, callback }, { call }) {
+      const { success } = yield call(update, payload);
       if (success) {
         if (callback) callback();
       }

+ 0 - 1
src/models/product.js

@@ -1,6 +1,5 @@
 import { query } from '../services/product';
 import modelExtend from 'dva-model-extend';
-import queryString from 'query-string';
 import { pageModel } from './common';
 import { pageSize } from '../utils/config';
 import { checkSearchParams } from '../utils/utils';

+ 0 - 63
src/models/recommend.js

@@ -1,63 +0,0 @@
-import { message } from 'antd';
-import { queryMerchantRecommend, updateMerchantRecommend } from '../services/merchant';
-import { getLocalUser } from '../utils/helper';
-
-export default {
-  namespace: 'recommend',
-
-  state: {
-    item: [],
-    loading: false,
-    modalShow: false,
-  },
-
-  subscriptions: {
-    setup({ dispatch, history }) {
-      history.listen(({ pathname, state }) => {
-        if (pathname === '/frontend') {
-          const { merchantId } = getLocalUser();
-          dispatch({
-            type: 'queryMerchantRecommend',
-            payload: { id: merchantId },
-          });
-        }
-      });
-    },
-  },
-
-  effects: {
-    * queryMerchantRecommend({ payload }, { call, put }) {
-      yield put({ type: 'changeLoading', payload: { loading: true } });
-      const { data, success } = yield call(queryMerchantRecommend, payload);
-      if (success) {
-        yield put({ type: 'querySuccess', payload: data });
-      }
-      yield put({ type: 'changeLoading', payload: { loading: false } });
-    },
-    * updateMerchantRecommend({ payload, callback }, { call, put }) {
-      const { data, success } = yield call(updateMerchantRecommend, payload);
-      if (success) {
-        message.success('修改成功!');
-        if (callback) callback();
-      }
-    },
-  },
-
-  reducers: {
-    changeLoading(state, action) {
-      return { ...state, ...action.payload };
-    },
-    querySuccess(state, action) {
-      return { ...state, item: action.payload };
-    },
-    showModal(state, action) {
-      return { ...state, ...action.payload, modalShow: true };
-    },
-    hideModal(state, action) {
-      return { ...state, ...action.payload, modalShow: false };
-    },
-    saveSortResult(state, action) {
-      return { ...state, item: [...action.payload], modalShow: false };
-    },
-  },
-};

+ 57 - 0
src/models/soldProduct.js

@@ -0,0 +1,57 @@
+import modelExtend from 'dva-model-extend';
+import queryString from 'query-string';
+import { query } from '../services/soldProduct';
+import { pageModel } from './common';
+import { checkSearchParams } from '../utils/utils';
+import { pageSize } from '../utils/config';
+import { getLocalUser } from '../utils/helper';
+
+export default modelExtend(pageModel, {
+  namespace: 'snapshot',
+
+  state: {
+    listLoading: false,
+  },
+
+  subscriptions: {
+    setup({ dispatch, history }) {
+      history.listen((location) => {
+        if (location.pathname === '/sold') {
+          const payload = checkSearchParams(queryString.parse(location.search));
+          dispatch({
+            type: 'query',
+            payload,
+          });
+        }
+      });
+    },
+  },
+
+  effects: {
+    * query({ payload }, { call, put }) {
+      yield put({ type: 'changeLoading', payload: { listLoading: true } });
+      const { merchantId } = getLocalUser();
+      const { data, success } = yield call(query, { ...payload, merchantId });
+      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 } });
+    },
+  },
+
+  reducers: {
+    changeLoading(state, action) {
+      return { ...state, ...action.payload };
+    },
+  },
+});

+ 0 - 10
src/routes/About/index.js

@@ -1,10 +0,0 @@
-import React, { PureComponent } from 'react';
-import { Card, Icon } from 'antd';
-
-export default class About extends PureComponent {
-  render() {
-    return (
-      <Card title="使用说明" />
-    );
-  }
-}

+ 1 - 3
src/routes/Campus/index.js

@@ -1,5 +1,4 @@
 import React, { Component } from 'react';
-import PropTypes from 'prop-types';
 import queryString from 'query-string';
 import { connect } from 'dva';
 import { routerRedux } from 'dva/router';
@@ -8,7 +7,6 @@ import CampusTableList from './table';
 import CampusModalForm from './modal';
 import Search from './search';
 import PageHeaderLayout from '../../layouts/PageHeaderLayout';
-import { Codes } from '../../utils/config';
 
 @connect(state => ({
   campus: state.campus,
@@ -49,7 +47,7 @@ export default class CampusList extends Component {
           payload: data,
           callback: () => {
             dispatch(routerRedux.push({
-              pathname: '/terminal/campus',
+              pathname: '/campus',
               search: queryString.stringify(filters),
             }));
           },

+ 4 - 2
src/routes/Campus/modal.js

@@ -1,5 +1,5 @@
 import React, { Component } from 'react';
-import { Form, Cascader, Select, Input, Modal, Icon } from 'antd';
+import { Form, Cascader, Input, Modal } from 'antd';
 import * as city from '../../utils/city';
 
 @Form.create()
@@ -14,7 +14,9 @@ export default class CampusModalForm extends Component {
       data.provinceCode = city.provNameToCode(data.cascader[0]);
       data.cityName = data.cascader.slice(1, 3).join(' ');
       delete data.cascader;
-      item.id ? data.id = item.id : null;
+      if (item.id) {
+        data.id = item.id;
+      }
 
       onOk(data);
     });

+ 3 - 14
src/routes/Campus/search.js

@@ -1,5 +1,4 @@
-import react, { PureComponent } from 'react';
-import PropTypes from 'prop-types';
+import React from 'react';
 import { Button, Form, Row, Col, Icon } from 'antd';
 import DataSearch from '../../components/DataSearch';
 
@@ -20,11 +19,9 @@ const Search = ({
       value: 'code', name: '校区编号',
     }],
     selectProps: {
-      defaultValue: field || 'name',
-    },
-    onSearch: (value) => {
-      onSearch(value);
+      defaultValue: field || 'code',
     },
+    onSearch: value => onSearch(value),
   };
   return (
     <Row gutter={24}>
@@ -38,12 +35,4 @@ const Search = ({
   );
 };
 
-Search.propTypes = {
-  form: PropTypes.object.isRequired,
-  onSearch: PropTypes.func,
-  onAdd: PropTypes.func,
-  field: PropTypes.string,
-  keyword: PropTypes.string,
-};
-
 export default Form.create()(Search);

+ 8 - 21
src/routes/Campus/table.js

@@ -1,20 +1,8 @@
 import React, { Component } from 'react';
 import moment from 'moment';
-import classnames from 'classnames';
-import { Modal, Table } from 'antd';
-import AnimTableBody from '../../components/Animation/AnimTableBody';
-import styles from './table.less';
+import { Table } from 'antd';
 
 export default class CampusTableList extends Component {
-  handleDeleteItem = (record) => {
-    Modal.confirm({
-      title: '您确定要删除该校区吗?',
-      onOk() {
-        onDeleteItem(record.id);
-      },
-    });
-  }
-
   render() {
     const {
       onDeleteItem,
@@ -28,25 +16,30 @@ export default class CampusTableList extends Component {
       title: '校区编号',
       dataIndex: 'code',
       key: 'code',
+      width: '15%',
     }, {
       title: '校区名称',
       dataIndex: 'name',
       key: 'name',
+      width: '40%',
     }, {
       title: '联系人',
       dataIndex: 'contactName',
       key: 'contactName',
+      width: '10%',
     }, {
       title: '电话',
       dataIndex: 'mobile',
       key: 'mobile',
+      width: '13%',
     }, {
       title: '修改时间',
       dataIndex: 'gmtModified',
       key: 'gmtModified',
-      render: (text, record) => (
+      render: text => (
         <div>{moment(text).format('YYYY-MM-DD HH:mm:ss')}</div>
       ),
+      width: '17%',
     }, {
       title: '操作',
       dataIndex: 'operation',
@@ -56,25 +49,19 @@ export default class CampusTableList extends Component {
           <a onClick={() => onEditItem(record)}>编辑</a>
         </div>
       ),
+      width: '5%',
     }];
 
     // 配置分页
     tableProps.pagination = !!pagination && { ...pagination, showSizeChanger: true, showQuickJumper: true, showTotal: total => `共 ${total} 条` };
 
-    // 添加动画
-    const AnimationTableBody = props => (<AnimTableBody {...props} />);
-
     return (
       <Table
         simple
         bordered
         {...tableProps}
         columns={columns}
-        className={classnames({ [styles.table]: true, [styles.motion]: true })}
         rowKey={record => record.id}
-        components={{
-          body: { wrapper: AnimationTableBody },
-        }}
       />
     );
   }

+ 0 - 74
src/routes/Campus/table.less

@@ -1,74 +0,0 @@
-@import "~antd/lib/style/themes/default.less";
-@import "../../utils/utils.less";
-
-.table {
-  .ant-table-tbody > tr > td,
-  .ant-table-thead > tr > th {
-    height: 62px;
-  }
-
-  &.motion {
-    .ant-table-tbody > tr > td,
-    .ant-table-thead > tr > th {
-      &:nth-child(1) {
-        width: 16%;
-      }
-
-      &:nth-child(2) {
-        width: 20%;
-      }
-
-      &:nth-child(3) {
-        width: 10%;
-      }
-
-      &:nth-child(4) {
-        width: 10%;
-      }
-
-      &:nth-child(5) {
-        width: 16%;
-      }
-
-      &:nth-child(6) {
-        width: 14%;
-      }
-
-      &:nth-child(7) {
-        width: 14%;
-      }
-    }
-
-    .ant-table-thead {
-      & > tr {
-        transition: none;
-        display: block;
-
-        & > th {
-          display: inline-flex;
-          align-items: center;
-          justify-content: center;
-        }
-      }
-    }
-
-    .ant-table-tbody {
-      & > tr {
-        transition: none;
-        display: block;
-        border-bottom: 1px solid #f5f5f5;
-
-        & > td {
-          border-bottom: none;
-          display: inline-flex;
-          align-items: center;
-          justify-content: center;
-        }
-
-        &.ant-table-expanded-row-level-1 > td {
-          height: auto;
-        }
-      }
-    }
-  }
-}

+ 0 - 170
src/routes/CmsUser/index.js

@@ -1,170 +0,0 @@
-import React, { Component } from 'react';
-import PropTypes from 'prop-types';
-import queryString from 'query-string';
-import { connect } from 'dva';
-import { routerRedux } from 'dva/router';
-import { Card } from 'antd';
-import CMSUserTableList from './table';
-import CMSUserModalForm from './modal';
-import CMSUserSearch from './search';
-import PageHeaderLayout from '../../layouts/PageHeaderLayout';
-
-@connect(state => ({
-  cmsUser: state.cmsUser,
-  merchant: state.merchant,
-}))
-export default class CMSUser extends Component {
-  static propTypes = {
-    cmsUser: PropTypes.object,
-    location: PropTypes.object,
-    dispatch: PropTypes.func,
-  };
-
-  componentDidMount() {
-    this.props.dispatch({
-      type: 'merchant/query',
-      payload: {
-        pageNo: 1,
-        pageSize: 1000,
-      },
-    });
-  }
-
-  render() {
-    const { location, dispatch, cmsUser, 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 } = cmsUser;
-
-    Object.keys(filters).map((key) => { filters[key] ? null : delete filters[key]; });
-    if (field && keyword) {
-      filters.field = field;
-      filters.keyword = keyword;
-    }
-
-    const modalProps = {
-      item: modalType === 'create' ? {} : currentItem,
-      visible: modalVisible,
-      merchantList: merchant.list,
-      maskClosable: false,
-      modalType,
-      title: `${modalType === 'create' ? '添加CMS用户' : '编辑CMS用户'}`,
-      wrapClassName: 'vertical-center-modal',
-      onOk(data) {
-        dispatch({
-          type: `cmsUser/${modalType}`,
-          payload: data,
-        });
-      },
-      onCancel() {
-        dispatch({
-          type: 'cmsUser/hideModal',
-        });
-      },
-    };
-
-    const searchProps = {
-      field,
-      keyword,
-      onSearch: (payload) => {
-        if (!payload.keyword.length) {
-          delete payload.field;
-          delete payload.keyword;
-        }
-        dispatch(routerRedux.push({
-          pathname,
-          search: queryString.stringify({
-            ...payload,
-          }),
-        }));
-      },
-      onAdd: () => {
-        dispatch({
-          type: 'cmsUser/showModal',
-          payload: {
-            modalType: 'create',
-          },
-        });
-      },
-    };
-
-    const listProps = {
-      location,
-      pagination,
-      dataSource: list,
-      loading: listLoading,
-      curStatus: filters.status,
-      onChange: (pagination, filterArgs) => {
-        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 = { ...filters, ...tableFilters };
-        Object.keys(data).map(key => (data[key] ? null : delete data[key]));
-        dispatch(routerRedux.push({
-          pathname,
-          search: queryString.stringify({
-            ...data,
-            pageNo: pagination.current,
-            pageSize: pagination.pageSize,
-          }),
-        }));
-      },
-      // 编辑
-      onEditItem: (item) => {
-        dispatch({
-          type: 'cmsUser/showModal',
-          payload: {
-            modalType: 'update',
-            currentItem: item,
-          },
-        });
-      },
-      // 封禁
-      onDeleteItem: (id) => {
-        dispatch({
-          type: 'cmsUser/delete',
-          payload: id,
-          callback: () => {
-            dispatch(
-              routerRedux.push({
-                pathname,
-                search: queryString.stringify(filters),
-              })
-            );
-          },
-        });
-      },
-      // 解禁
-      onRecoverItem: (payload) => {
-        dispatch({
-          type: 'cmsUser/recover',
-          payload,
-          callback: () => {
-            dispatch(
-              routerRedux.push({
-                pathname,
-                search: queryString.stringify(filters),
-              })
-            );
-          },
-        });
-      },
-    };
-
-    return (
-      <PageHeaderLayout>
-        <Card>
-          <CMSUserSearch {...searchProps} />
-          <CMSUserTableList {...listProps} />
-          {modalVisible && <CMSUserModalForm {...modalProps} />}
-        </Card>
-      </PageHeaderLayout>
-    );
-  }
-}

+ 0 - 123
src/routes/CmsUser/modal.js

@@ -1,123 +0,0 @@
-import React, { Component } from 'react';
-import { Form, Select, Switch, Radio, Input, Modal, Icon } from 'antd';
-import { Codes, domains } from '../../utils/config';
-
-@Form.create()
-export default class CMSUserModalForm extends Component {
-  handleModalOk = () => {
-    const { form, onOk, modalType, item } = this.props;
-    const { validateFields, getFieldsValue } = form;
-
-    validateFields((errors) => {
-      if (errors) return;
-      const data = { ...getFieldsValue() };
-
-      data.status ? data.status = Codes.CODE_NORMAL : data.status = Codes.CODE_DELETE;
-
-      if (modalType === 'update') {
-        data.id = item.id;
-      }
-
-      onOk(data);
-    });
-  }
-
-  render() {
-    const { item, merchantList, onOk, form, modalType, ...modalProps } = this.props;
-    const { getFieldDecorator } = form;
-
-    const modalOpts = { ...modalProps, onOk: this.handleModalOk };
-    const formItemLayout = {
-      labelCol: { span: 6 },
-      wrapperCol: { span: 15 },
-    };
-
-    return (
-      <Modal {...modalOpts}>
-        <Form layout="horizontal">
-          <Form.Item label="选择厂商" hasFeedback {...formItemLayout}>
-            {getFieldDecorator('merchantId', {
-              rules: [{ required: true, type: 'string', message: '该项为必选项!' }],
-              initialValue: item.merchantId,
-            })(
-              <Select
-                showSearch
-                allowClear
-                placeholder="请选择"
-                optionFilterProp="children"
-                filterOption={(input, option) => option.props.children.toLowerCase().indexOf(input.toLowerCase()) >= 0}
-              >
-                {merchantList.map(selItem => <Select.Option value={selItem.id} key={selItem.id}>{`${selItem.code}/${selItem.name}`}</Select.Option>)}
-              </Select>
-            )}
-          </Form.Item>
-          <Form.Item label="用户名称:" hasFeedback {...formItemLayout}>
-            {getFieldDecorator('name', {
-              rules: [{ required: true, type: 'string', message: '用户名为必填项!' }],
-              initialValue: item.name,
-            })(<Input placeholder="请输入" />)}
-          </Form.Item>
-          {modalType === 'create' ? (
-            <Form.Item label="用户密码:" hasFeedback {...formItemLayout}>
-              {getFieldDecorator('password', {
-                rules: [{ required: true, type: 'string', message: '密码为必填项!' }],
-                initialValue: item.password,
-              })(<Input placeholder="请输入" />)}
-            </Form.Item>
-) : (
-  <Form.Item label="用户密码:" hasFeedback {...formItemLayout}>
-    {getFieldDecorator('password', {
-                initialValue: item.password,
-              })(<Input placeholder="修改密码时填写" />)}
-  </Form.Item>
-)}
-          <Form.Item label="用户昵称:" hasFeedback {...formItemLayout}>
-            {getFieldDecorator('nickName', {
-              initialValue: item.nickName,
-            })(<Input />)}
-          </Form.Item>
-          <Form.Item label="用户性别:" {...formItemLayout}>
-            {getFieldDecorator('gender', {
-              initialValue: item.gender || 'MALE',
-            })(
-              <Radio.Group>
-                <Radio value="MALE" key="MALE">男</Radio>
-                <Radio value="FEMALE" key="FEMALE">女</Radio>
-              </Radio.Group>
-            )}
-          </Form.Item>
-          <Form.Item label="出生日期:" hasFeedback {...formItemLayout}>
-            {getFieldDecorator('birthday', {
-              initialValue: item.birthday,
-            })(<Input />)}
-          </Form.Item>
-          <Form.Item label="电话:" hasFeedback {...formItemLayout}>
-            {getFieldDecorator('mobile', {
-              initialValue: item.mobile,
-            })(<Input />)}
-          </Form.Item>
-          <Form.Item label="邮箱:" hasFeedback {...formItemLayout}>
-            {getFieldDecorator('mail', {
-              initialValue: item.mail,
-            })(<Input />)}
-          </Form.Item>
-          <Form.Item label="QQ:" hasFeedback {...formItemLayout}>
-            {getFieldDecorator('qq', {
-              initialValue: item.qq,
-            })(<Input />)}
-          </Form.Item>
-          <Form.Item label="微信:" hasFeedback {...formItemLayout}>
-            {getFieldDecorator('weChat', {
-              initialValue: item.weChat,
-            })(<Input />)}
-          </Form.Item>
-          <Form.Item label="账号状态:" {...formItemLayout}>
-            {getFieldDecorator('status', {
-              valuePropsName: 'checked',
-          })(<Switch defaultChecked={item.status === Codes.CODE_NORMAL} checkedChildren="使用中" unCheckedChildren="禁用中" />)}
-          </Form.Item>
-        </Form>
-      </Modal>
-    );
-  }
-}

+ 0 - 43
src/routes/CmsUser/search.js

@@ -1,43 +0,0 @@
-import react, { Component } from 'react';
-import PropTypes from 'prop-types';
-import { Button, Form, Row, Col, Icon } from 'antd';
-import DataSearch from '../../components/DataSearch';
-
-export default class CMSUserSearch extends Component {
-  static propTypes = {
-    onSearch: PropTypes.func,
-    onAdd: PropTypes.func,
-    field: PropTypes.string,
-    keyword: PropTypes.string,
-  };
-
-  render() {
-    const { field, keyword, onSearch, onAdd } = this.props;
-    const searchGroupProps = {
-      field,
-      keyword,
-      size: 'default',
-      select: true,
-      selectOptions: [{
-        value: 'name', name: '用户名称', type: 'input',
-      }],
-      selectProps: {
-        defaultValue: field || 'name',
-      },
-      onSearch: (value) => {
-        onSearch(value);
-      },
-    };
-
-    return (
-      <Row gutter={24}>
-        <Col lg={10} md={12} sm={16} xs={24} style={{ marginBottom: 16 }}>
-          <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>
-        </Col>
-      </Row>
-    );
-  }
-}

+ 0 - 119
src/routes/CmsUser/table.js

@@ -1,119 +0,0 @@
-import React, { Component } from 'react';
-import PropTypes from 'prop-types';
-import moment from 'moment';
-import classnames from 'classnames';
-import queryString from 'query-string';
-import { Avatar, Modal, Badge, Table, Popconfirm, Menu, Icon } from 'antd';
-import AnimTableBody from '../../components/Animation/AnimTableBody';
-import { Codes, statuses, domains } from '../../utils/config';
-import styles from './table.less';
-
-export default class CMSUserTableList extends Component {
-  handleOperateItem = (record) => {
-    Modal.confirm({
-      title: `您确定要${record.status === Codes.CODE_NORMAL ? '禁用' : '解禁'}该账户?`,
-      onOk() {
-        if (record.status === Codes.CODE_NORMAL) {
-          onDeleteItem({ id: record.id });
-        } else {
-          onRecoverItem({ id: record.id, status: Codes.CODE_NORMAL });
-        }
-      },
-    });
-  }
-
-  render() {
-    const {
-      onDeleteItem,
-      onEditItem,
-      onRecoverItem,
-      location,
-      curStatus,
-      pagination,
-      ...tableProps
-    } = this.props;
-
-    const colorList = ['#f56a00', '#7265e6', '#ffbf00', '#00a2ae'];
-
-    const columns = [{
-      title: '头像',
-      dataIndex: 'avatar',
-      key: 'avatar',
-      render: (text, record) => (
-        <Avatar
-          src={text}
-          style={{ backgroundColor: colorList[Math.floor(Math.random() * (colorList.length - 1))], verticalAlign: 'middle' }}
-        >
-          {record.nickname}
-        </Avatar>
-      ),
-    }, {
-      title: '用户名称',
-      dataIndex: 'name',
-      key: 'name',
-    }, {
-      title: '用户昵称',
-      dataIndex: 'nickName',
-      key: 'nickName',
-    }, {
-      title: '厂商名称',
-      dataIndex: 'merchantId',
-      key: 'merchantId',
-      render: (text, record) => record.merchantName,
-    }, {
-      title: '厂商类型',
-      dataIndex: 'domain',
-      key: 'domain',
-      render: (text, record) => domains[record.domain],
-    }, {
-      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]} />);
-      },
-      filters: Object.keys(statuses).map(key => ({ text: statuses[key], value: key })),
-      filterMultiple: false,
-      filteredValue: curStatus,
-    }, {
-      title: '修改时间',
-      dataIndex: 'gmtModified',
-      key: 'gmtModified',
-      render: (text, record) => (
-        <div>{moment(text).format('YYYY-MM-DD HH:mm:ss')}</div>
-      ),
-    }, {
-      title: '操作',
-      dataIndex: 'operation',
-      key: 'operation',
-      render: (text, record) => (
-        <div>
-          <a onClick={() => onEditItem(record)}>编辑</a>
-          <span className={styles.splitLine} />
-          <a onClick={() => this.handleOperateItem(record)}>{record.status === Codes.CODE_NORMAL ? '禁用' : '解禁'}</a>
-        </div>
-      ),
-    }];
-
-    // 配置分页
-    tableProps.pagination = !!pagination && { ...pagination, showSizeChanger: true, showQuickJumper: true, showTotal: total => `共 ${total} 条` };
-
-    // 添加动画
-    const AnimationTableBody = props => (<AnimTableBody {...props} />);
-
-    return (
-      <Table
-        simple
-        bordered
-        {...tableProps}
-        columns={columns}
-        className={classnames({ [styles.table]: true, [styles.motion]: true })}
-        rowKey={record => record.id}
-        components={{
-          body: { wrapper: AnimationTableBody },
-        }}
-      />
-    );
-  }
-}

+ 0 - 90
src/routes/CmsUser/table.less

@@ -1,90 +0,0 @@
-@import "~antd/lib/style/themes/default.less";
-@import "../../utils/utils.less";
-
-.table {
-  :global {
-    .ant-table-tbody > tr > td,
-    .ant-table-thead > tr > th {
-      height: 50px;
-    }
-  }
-
-  &.motion {
-    :global {
-      .ant-table-tbody > tr > td,
-      .ant-table-thead > tr > th {
-        &:nth-child(1) {
-          width: 8%;
-        }
-
-        &:nth-child(2) {
-          width: 14%;
-        }
-
-        &:nth-child(3) {
-          width: 13%;
-        }
-
-        &:nth-child(4) {
-          width: 13%;
-        }
-
-        &:nth-child(5) {
-          width: 10%;
-        }
-
-        &:nth-child(6) {
-          width: 10%;
-        }
-
-        &:nth-child(7) {
-          width: 19%;
-        }
-
-        &:nth-child(8) {
-          width: 13%;
-        }
-      }
-
-      .ant-table-thead {
-        & > tr {
-          transition: none;
-          display: block;
-
-          & > th {
-            display: inline-flex;
-            align-items: center;
-            justify-content: center;
-          }
-        }
-      }
-
-      .ant-table-tbody {
-        & > tr {
-          transition: none;
-          display: block;
-          border-bottom: 1px solid #f5f5f5;
-
-          & > td {
-            border-bottom: none;
-            display: inline-flex;
-            align-items: center;
-            justify-content: center;
-          }
-
-          &.ant-table-expanded-row-level-1 > td {
-            height: auto;
-          }
-        }
-      }
-    }
-  }
-}
-
-.splitLine {
-  background: @border-color-split;
-  display: inline-block;
-  margin: 0 8px;
-  width: 1px;
-  height: 12px;
-}

+ 0 - 191
src/routes/Dashboard/index.js

@@ -1,191 +0,0 @@
-import React, { Component } from 'react';
-import numeral from 'numeral';
-import moment from 'moment';
-import {
-  Row,
-  Col,
-  Icon,
-  Card,
-  Tabs,
-  Table,
-  Radio,
-  DatePicker,
-  Tooltip,
-  Menu,
-  Dropdown,
-} from 'antd';
-import {
-  ChartCard,
-  yuan,
-  MiniArea,
-  MiniBar,
-  MiniProgress,
-  Field,
-  Bar,
-  Pie,
-  TimelineChart,
-} from '../../components/Charts';
-import Trend from '../../components/Trend';
-import NumberInfo from '../../components/NumberInfo';
-import styles from './index.less';
-
-const { TabPane } = Tabs;
-const { RangePicker } = DatePicker;
-
-export default class Dashboard extends Component {
-  render() {
-    // mock order card data
-    const orderData = [];
-    const beginDay = new Date().getTime();
-    const fakeY = [23, 44, 32, 55, 66, 89, 100, 200, 300, 400, 112, 500, 1000];
-    for (let i = 0; i < fakeY.length; i++) {
-      orderData.push({
-        x: moment(new Date(beginDay + (1000 * 60 * 60 * 24 * i))).format('YYYY-MM-DD'),
-        y: fakeY[i],
-      });
-    }
-    // mock rank list data
-    const rankingListData = [];
-    for (let i = 0; i < 7; i++) {
-      rankingListData.push({
-        title: `渠道商${i + 1} - 贝尔安亲${i + 1}`,
-        total: 25894.8,
-      });
-    }
-
-    // 顶部的4个卡片,响应式配置
-    const topColResponsiveProps = {
-      xs: 24,
-      sm: 12,
-      md: 12,
-      lg: 12,
-      xl: 6,
-      style: { marginBotton: 24 },
-    };
-
-    // 底部的销售额与用户量趋势图,日期筛选
-    const salesExtra = (
-      <div className={styles.salesExtraWrap}>
-        <div className={styles.salesExtra}>
-          <a>今日</a>
-          <a>本周</a>
-          <a>本月</a>
-          <a>全年</a>
-        </div>
-        <RangePicker
-          style={{ width: 256 }}
-        />
-      </div>
-    );
-
-    return (
-      <div>
-        <Row gutter={24}>
-          <Col {...topColResponsiveProps}>
-            <ChartCard
-              bordered={false}
-              title="总销售额"
-              action={<Tooltip title="指标说明"><Icon type="info-circle-o" /></Tooltip>}
-              total={yuan(1247238957)}
-              footer={<Field label="日均销售额" value={`¥${numeral(23343).format('0,0')}`} />}
-              contentHeight={46}
-            >
-              <Trend flag="up" style={{ marginRight: 16 }}>
-                周同比<span className={styles.trendText}>12%</span>
-              </Trend>
-              <Trend flag="down">
-                日环比<span className={styles.trendText}>11%</span>
-              </Trend>
-            </ChartCard>
-          </Col>
-          <Col {...topColResponsiveProps}>
-            <ChartCard
-              bordered={false}
-              title="支付笔数"
-              action={<Tooltip title="指标说明"><Icon type="info-circle-o" /></Tooltip>}
-              total={100000}
-              footer={<Field label="日订单量" value={5000} />}
-              contentHeight={46}
-            >
-              <MiniBar
-                height={46}
-                data={orderData}
-              />
-            </ChartCard>
-          </Col>
-          <Col {...topColResponsiveProps}>
-            <ChartCard
-              bordered={false}
-              title="用户量"
-              action={<Tooltip title="指标说明"><Icon type="info-circle-o" /></Tooltip>}
-              total={4000}
-              footer={<Field label="日增长量" value={200} />}
-              contentHeight={46}
-            >
-              <MiniArea
-                color="#975FE4"
-                height={46}
-                data={orderData}
-              />
-            </ChartCard>
-          </Col>
-          <Col {...topColResponsiveProps}>
-            <ChartCard
-              bordered={false}
-              title="活跃量"
-              action={<Tooltip title="指标说明"><Icon type="info-circle-o" /></Tooltip>}
-              total="2037个"
-              footer={<Field label="日活增长" value={200} />}
-              contentHeight={46}
-            >
-              <MiniArea
-                line
-                height={46}
-                data={orderData}
-              />
-            </ChartCard>
-          </Col>
-        </Row>
-        <Card
-          bordered={false}
-          bodyStyle={{ padding: 0 }}
-          style={{ marginTop: 20 }}
-        >
-          <div className={styles.salesCard}>
-            <Tabs tabBarExtraContent={salesExtra} size="large" tabBarStyle={{ marginBottom: 24 }}>
-              <TabPane tab="销售额" key="sales">
-                <Row>
-                  <Col xl={16} lg={12} md={12} sm={24} xs={24}>
-                    <div className={styles.salesBar}>
-                      <Bar
-                        height={295}
-                        title="销售额趋势"
-                        data={orderData}
-                      />
-                    </div>
-                  </Col>
-                  <Col xl={8} lg={12} md={12} sm={24} xs={24}>
-                    <div className={styles.salesRank}>
-                      <h4 className={styles.rankingTitle}>渠道商销售额排名</h4>
-                      <ul className={styles.rankingList}>
-                        {
-                          rankingListData.map((item, i) => (
-                            <li key={item.title}>
-                              <span className={(i < 3) ? styles.active : ''}>{i + 1}</span>
-                              <span>{item.title}</span>
-                              <span>{numeral(item.total).format('0,0')}</span>
-                            </li>
-                          ))
-                        }
-                      </ul>
-                    </div>
-                  </Col>
-                </Row>
-              </TabPane>
-            </Tabs>
-          </div>
-        </Card>
-      </div>
-    );
-  }
-}

+ 0 - 104
src/routes/Dashboard/index.less

@@ -1,104 +0,0 @@
-@import "~antd/lib/style/themes/default.less";
-@import "../../utils/utils.less";
-
-.salesExtra {
-  display: inline-block;
-  margin-right: 24px;
-  a {
-    color: @text-color;
-    margin-left: 24px;
-    &:hover {
-      color: @primary-color;
-    }
-    &.currentDate {
-      color: @primary-color;
-    }
-  }
-}
-
-.salesCard {
-  .salesBar {
-    padding: 0 0 32px 32px;
-  }
-  .salesRank {
-    padding: 0 32px 32px 72px;
-  }
-  :global {
-    .ant-tabs-bar {
-      padding-left: 16px;
-      .ant-tabs-nav .ant-tabs-tab {
-        padding-top: 16px;
-        padding-bottom: 14px;
-        line-height: 24px;
-      }
-    }
-    .ant-tabs-extra-content {
-      padding-right: 24px;
-      line-height: 55px;
-    }
-  }
-}
-
-.salesCard {
-  :global {
-    .ant-card-head {
-      position: relative;
-    }
-  }
-}
-
-.salesCardExtra {
-  height: 68px;
-}
-
-.rankingList {
-  margin: 25px 0 0;
-  padding: 0;
-  list-style: none;
-  li {
-    .clearfix();
-    margin-top: 16px;
-    span {
-      color: @text-color;
-      font-size: 14px;
-      line-height: 22px;
-    }
-    span:first-child {
-      background-color: @background-color-base;
-      border-radius: 20px;
-      display: inline-block;
-      font-size: 12px;
-      font-weight: 600;
-      margin-right: 24px;
-      height: 20px;
-      line-height: 20px;
-      width: 20px;
-      text-align: center;
-    }
-    span.active {
-      //background-color: @primary-color;
-      background-color: #314659;
-      color: #fff;
-    }
-    span:last-child {
-      float: right;
-    }
-  }
-}
-
-.trendText {
-  margin-left: 8px;
-  color: @heading-color;
-}
-
-@media screen and (max-width: @screen-lg) {
-  .salesExtra {
-    display: none;
-  }
-}
-
-@media screen and (max-width: @screen-sm) {
-  .salesExtraWrap {
-    display: none;
-  }
-}

+ 0 - 139
src/routes/Frontend/index.js

@@ -1,139 +0,0 @@
-import React, { PureComponent } from 'react';
-import { connect } from 'dva';
-import { routerRedux } from 'dva/router';
-
-import PageHeaderLayout from '../../layouts/PageHeaderLayout';
-import RecommendList from './recommend';
-import { getLocalUser } from '../../utils/config';
-import { pageSize } from '../../utils/config';
-
-@connect(state => ({
-  recommend: state.recommend,
-  mproduct: state.mproduct,
-}))
-export default class FrontendConfiger extends PureComponent {
-  state = { curTab: 'recommend' };
-
-  handleTabChange = (key) => {
-    this.setState({ curTab: key });
-  }
-
-  // 点击调整推荐位按钮 - 弹出模态框
-  handleRecommendAdjustClick = () => {
-    this.props.dispatch({ type: 'recommend/showModal' });
-  }
-
-  // 点击模态框的取消按钮 - 隐藏
-  handleRecommendModalCancel = () => {
-    this.props.dispatch({ type: 'recommend/hideModal' });
-  }
-
-  // 点击模态框的确定按钮 -> 更新recommend model state -> 隐藏
-  handleRecommendModalOk = (data) => {
-    this.props.dispatch({
-      type: 'recommend/saveSortResult',
-      payload: data,
-    });
-  }
-
-  // 点击模态框的搜索按钮 -> 查询相关产品
-  handleRecommendModalSearch = (data) => {
-    const { merchantId } = getLocalUser();
-    const newData = { ...data };
-    if (newData.keyword) {
-      newData[newData.field] = newData.keyword;
-    }
-    delete newData.field;
-    delete newData.keyword;
-    this.props.dispatch({
-      type: 'mproduct/query',
-      payload: {
-        merchantId,
-        pageNo: 1,
-        pageSize,
-      },
-    });
-  }
-
-  // 模态框中表格翻页
-  handleRecommendModalTableChange = (pagination, filterArgs, filters) => {
-    const { merchantId } = getLocalUser();
-    const newFilters = { ...filters };
-    if (newFilters.keyword) {
-      newFilters[newFilters.field] = newFilters.keyword;
-    }
-    delete newFilters.field;
-    delete newFilters.keyword;
-
-    const data = {
-      ...newFilters,
-      merchantId,
-      pageNo: pagination.current,
-      pageSize: pagination.pageSize,
-    };
-
-    Object.keys(data).map(key => (data[key] ? null : delete data[key]));
-    dispatch({ type: 'mproduct/query', payload: data });
-  }
-
-  // 页面退出 - 跳首页
-  handleRecommendPageCancel = () => {
-    const { dispatch } = this.props;
-    dispatch(routerRedux.push('/'));
-  }
-
-  // 页面提交[params: merchantId, idList] - 成功跳首页
-  handleRecommendPageSubmit = () => {
-    const { merchantId } = getLocalUser();
-    const { dispatch, recommend } = this.props;
-    const { item } = recommend;
-    const idList = item.map(one => one.id);
-
-    dispatch({
-      type: 'recommend/updateMerchantRecommend',
-      payload: { merchantId, idList },
-      callback: () => {
-        dispatch(routerRedux.push('/'));
-      },
-    });
-  }
-
-  render() {
-    const { recommend, mproduct } = this.props;
-    const { item, loading, modalShow } = recommend;
-
-    const tabList = [{
-      key: 'recommend',
-      tab: '推荐位设置',
-    }];
-
-    const contentMap = {
-      recommend:
-  <RecommendList
-    rowKeyName="id"
-    loading={loading}
-    selTableData={item}
-    modalVisible={modalShow}
-    fsTableDataSource={mproduct.list}
-    fsTablePagination={mproduct.pagination}
-    fsTableLoading={mproduct.listLoading}
-    fsTableOnChange={this.handleRecommendModalTableChange}
-    onOk={this.handleRecommendModalOk}
-    onCancel={this.handleRecommendModalCancel}
-    onSearch={this.handleRecommendModalSearch}
-    modalShowController={this.handleRecommendAdjustClick}
-    onPageCancel={this.handleRecommendPageCancel}
-    onPageSubmit={this.handleRecommendPageSubmit}
-  />,
-    };
-
-    return (
-      <PageHeaderLayout
-        tabList={tabList}
-        onTabChange={this.handleTabChange}
-      >
-        {contentMap[this.state.curTab]}
-      </PageHeaderLayout>
-    );
-  }
-}

+ 0 - 137
src/routes/Frontend/recommend.js

@@ -1,137 +0,0 @@
-import React, { PureComponent } from 'react';
-import PropTypes from 'prop-types';
-import { Card, Spin, Table, Button } from 'antd';
-import SelectModal from '../../components/SelectModal';
-import styles from './recommend.less';
-import { productType } from '../../utils/config';
-
-export default class RecommendList extends PureComponent {
-  render() {
-    const {
-      loading,
-      modalVisible,
-      modalShowController,
-      rowKeyName,
-      selTableData,
-      recModalShow,
-      onCancel,
-      onOk,
-      onSearch,
-      onPageCancel,
-      onPageSubmit,
-      ...fsTableOpts
-    } = this.props;
-
-    const modalProps = {
-      title: '选择产品',
-      maskClosable: false,
-      visible: modalVisible,
-      onCancel,
-      onOk,
-    };
-
-    const searchProps = {
-      searchField: 'name',
-      searchKeyWord: '',
-      searchSize: 'default',
-      searchSelect: true,
-      searchSelectOptions: [{
-        value: 'name', name: '产品名称', mode: 'input',
-      }, {
-        value: 'code', name: '产品编号', mode: 'input',
-      }],
-      searchSelectProps: {
-        defaultValue: 'name',
-      },
-      onSearch: (value) => {
-        onSearch(value);
-      },
-    };
-
-    const selTableProps = {
-      operSort: true,
-      operDel: true,
-      tableClassName: styles.sTable,
-      tablePagination: false,
-      tableDataSource: selTableData,
-      rowKeyName,
-      tableColumns: [{
-        title: '产品编号',
-        dataIndex: 'code',
-        key: 'code',
-      }, {
-        title: '产品名称',
-        dataIndex: 'name',
-        key: 'name',
-      }, {
-        title: '类型',
-        dataIndex: 'type',
-        key: 'type',
-        render: (text, record) => productType[text],
-      }],
-    };
-
-    const fsTableProps = {
-      fsTableClassName: styles.fsTable,
-      fsTableColumns: [{
-        title: '产品编号',
-        dataIndex: 'code',
-        key: 'code',
-      }, {
-        title: '产品名称',
-        dataIndex: 'name',
-        key: 'name',
-      }, {
-        title: '类型',
-        dataIndex: 'type',
-        key: 'type',
-        render: (text, record) => productType[text],
-      }],
-      ...fsTableOpts,
-    };
-
-    const columns = [{
-      title: '产品编号',
-      dataIndex: 'code',
-      key: 'code',
-    }, {
-      title: '产品名称',
-      dataIndex: 'name',
-      key: 'name',
-    }, {
-      title: '类型',
-      dataIndex: 'type',
-      key: 'type',
-      render: (text, record) => productType[text],
-    }, {
-      title: '位置',
-      dataIndex: 'sort',
-      key: 'sort',
-      render: (text, record, index) => index + 1,
-    }];
-
-    return (
-      <Spin spinning={loading}>
-        <Card title={<Button onClick={modalShowController} type="primary" size="small" icon="edit">调整</Button>}>
-          <Table
-            bordered
-            columns={columns}
-            dataSource={selTableData}
-            rowKey={record => record.id}
-            pagination={false}
-          />
-          <Button onClick={onPageCancel} style={{ marginTop: 20 }}>退出</Button>
-          <Button onClick={onPageSubmit} type="primary" style={{ marginLeft: 50, marginTop: 20 }}>保存</Button>
-        </Card>
-        {/* 渠道产品选择模态框 */}
-        <SelectModal
-          mode="multiple"
-          {...modalProps}
-          {...searchProps}
-          {...selTableProps}
-          {...fsTableProps}
-        />
-      </Spin>
-    );
-  }
-}

+ 0 - 127
src/routes/Frontend/recommend.less

@@ -1,127 +0,0 @@
-.fsTable {
-  :global {
-    .ant-table-tbody > tr > td,
-    .ant-table-thead > tr > th {
-      height: 50px;
-    }
-  }
-
-  :global {
-    .ant-table-tbody > tr > td,
-    .ant-table-thead > tr > th {
-      &:nth-child(1) {
-        width: 30%;
-      }
-
-      &:nth-child(2) {
-        width: 30%;
-      }
-
-      &:nth-child(3) {
-        width: 20%;
-      }
-
-      &:nth-child(4) {
-        width: 20%;
-      }
-    }
-
-    .ant-table-thead {
-      & > tr {
-        transition: none;
-        display: block;
-
-        & > th {
-          display: inline-flex;
-          align-items: center;
-          justify-content: center;
-        }
-      }
-    }
-
-    .ant-table-tbody {
-      & > tr {
-        transition: none;
-        display: block;
-        border-bottom: 1px solid #f5f5f5;
-
-        & > td {
-          border-bottom: none;
-          display: inline-flex;
-          align-items: center;
-          justify-content: center;
-        }
-
-        &.ant-table-expanded-row-level-1 > td {
-          height: auto;
-        }
-      }
-    }
-  }
-}
-
-.sTable {
-  :global {
-    .ant-table-tbody > tr > td,
-    .ant-table-thead > tr > th {
-      height: 50px;
-    }
-  }
-
-  :global {
-    .ant-table-tbody > tr > td,
-    .ant-table-thead > tr > th {
-      &:nth-child(1) {
-        width: 25%;
-      }
-
-      &:nth-child(2) {
-        width: 25%;
-      }
-
-      &:nth-child(3) {
-        width: 14%;
-      }
-
-      &:nth-child(4) {
-        width: 22%;
-      }
-
-      &:nth-child(5) {
-        width: 14%;
-      }
-    }
-
-    .ant-table-thead {
-      & > tr {
-        transition: none;
-        display: block;
-
-        & > th {
-          display: inline-flex;
-          align-items: center;
-          justify-content: center;
-        }
-      }
-    }
-
-    .ant-table-tbody {
-      & > tr {
-        transition: none;
-        display: block;
-        border-bottom: 1px solid #f5f5f5;
-
-        & > td {
-          border-bottom: none;
-          display: inline-flex;
-          align-items: center;
-          justify-content: center;
-        }
-
-        &.ant-table-expanded-row-level-1 > td {
-          height: auto;
-        }
-      }
-    }
-  }
-}

+ 6 - 52
src/routes/MProduct/List/index.js

@@ -1,5 +1,4 @@
 import React, { PureComponent } from 'react';
-import PropTypes from 'prop-types';
 import queryString from 'query-string';
 import { connect } from 'dva';
 import { routerRedux } from 'dva/router';
@@ -7,18 +6,9 @@ import { Card } from 'antd';
 import TableList from './table';
 import Search from './search';
 import PageHeaderLayout from '../../../layouts/PageHeaderLayout';
-import { Codes } from '../../../utils/config';
 
-@connect(state => ({
-  mproduct: state.mproduct,
-}))
+@connect(state => ({ mproduct: state.mproduct }))
 export default class MerchantProductList extends PureComponent {
-  static propTypes = {
-    mproduct: PropTypes.object,
-    location: PropTypes.object,
-    dispatch: PropTypes.func,
-  };
-
   render() {
     const { location, dispatch, mproduct } = this.props;
     location.query = queryString.parse(location.search);
@@ -49,14 +39,6 @@ export default class MerchantProductList extends PureComponent {
           }),
         }));
       },
-      onAdd: () => {
-        dispatch(
-          routerRedux.push({
-            pathname: '/goods/add',
-            state: filters,
-          })
-        );
-      },
     };
 
     const listProps = {
@@ -65,7 +47,7 @@ export default class MerchantProductList extends PureComponent {
       dataSource: list,
       loading: listLoading,
       curStatus: filters.status,
-      onChange: (pagination, filterArgs) => {
+      onChange: (page, filterArgs) => {
         const getValue = obj => Object.keys(obj).map(key => obj[key]).join(',');
         const tableFilters = Object.keys(filterArgs).reduce((obj, key) => {
           const newObj = { ...obj };
@@ -79,15 +61,15 @@ export default class MerchantProductList extends PureComponent {
           pathname,
           search: queryString.stringify({
             ...data,
-            pageNo: pagination.current,
-            pageSize: pagination.pageSize,
+            pageNo: page.current,
+            pageSize: page.pageSize,
           }),
         }));
       },
-      onEditItem: (item) => {
+      onViewItem: (item) => {
         dispatch(
           routerRedux.push({
-            pathname: '/goods/edit',
+            pathname: '/products/view',
             search: queryString.stringify({
               merchantId: item.merchantId,
               pid: item.pid,
@@ -96,34 +78,6 @@ export default class MerchantProductList extends PureComponent {
           })
         );
       },
-      onDeleteItem: (id) => {
-        dispatch({
-          type: 'mproduct/putOffSale',
-          payload: id,
-          callback: () => {
-            dispatch(
-              routerRedux.push({
-                pathname,
-                search: queryString.stringify(filters),
-              })
-            );
-          },
-        });
-      },
-      onRecoverItem: (payload) => {
-        dispatch({
-          type: 'mproduct/putOnSale',
-          payload,
-          callback: () => {
-            dispatch(
-              routerRedux.push({
-                pathname,
-                search: queryString.stringify(filters),
-              })
-            );
-          },
-        });
-      },
     };
 
     return (

+ 5 - 16
src/routes/MProduct/List/search.js

@@ -1,20 +1,11 @@
-import react, { PureComponent } from 'react';
-import PropTypes from 'prop-types';
-import { Button, Form, Row, Col, Icon } from 'antd';
+import React, { PureComponent } from 'react';
+import { Form, Row, Col } from 'antd';
 import DataSearch from '../../../components/DataSearch';
 
 @Form.create()
 export default class Search extends PureComponent {
-  static propTypes = {
-    form: PropTypes.object.isRequired,
-    onSearch: PropTypes.func,
-    onAdd: PropTypes.func,
-    field: PropTypes.string,
-    keyword: PropTypes.string,
-  };
-
   render() {
-    const { field, keyword, onSearch, onAdd } = this.props;
+    const { field, keyword, onSearch } = this.props;
 
     const searchGroupProps = {
       field,
@@ -27,11 +18,9 @@ export default class Search extends PureComponent {
         value: 'code', name: '产品编号', mode: 'input',
       }],
       selectProps: {
-        defaultValue: field || 'name',
-      },
-      onSearch: (value) => {
-        onSearch(value);
+        defaultValue: field || 'code',
       },
+      onSearch: value => onSearch(value),
     };
 
     return (

+ 11 - 47
src/routes/MProduct/List/table.js

@@ -1,35 +1,12 @@
 import React, { PureComponent } from 'react';
-import PropTypes from 'prop-types';
 import moment from 'moment';
-import classnames from 'classnames';
-import queryString from 'query-string';
-import { Divider, Modal, Table, Menu, Icon, Badge } from 'antd';
-import AnimTableBody from '../../../components/Animation/AnimTableBody';
+import { Table, Badge } from 'antd';
 import { itemStatuses, Codes, productType } from '../../../utils/config';
-import styles from './table.less';
 
 export default class TableList extends PureComponent {
-  handleOperateItem = (record) => {
-    const { onDeleteItem, onRecoverItem } = this.props;
-    Modal.confirm({
-      title: `您确定要${record.status === Codes.CODE_NORMAL ? '下架' : '出售'}该产品?`,
-      onOk() {
-        if (record.status === Codes.CODE_NORMAL) {
-          onDeleteItem({ pid: record.pid, merchantId: record.merchantId, status: Codes.CODE_DELETE });
-        } else if (record.status === Codes.CODE_DELETE) {
-          onRecoverItem({ pid: record.pid, merchantId: record.merchantId, status: Codes.CODE_NORMAL });
-        }
-      },
-    });
-  }
-
   render() {
     const {
-      curStatus,
-      onDeleteItem,
-      onRecoverItem,
-      onEditItem,
-      location,
+      onViewItem,
       pagination,
       ...tableProps
     } = this.props;
@@ -37,15 +14,18 @@ export default class TableList extends PureComponent {
       title: '产品编号',
       dataIndex: 'code',
       key: 'code',
+      width: '25%',
     }, {
       title: '产品名称',
       dataIndex: 'name',
       key: 'name',
+      width: '30%',
     }, {
       title: '产品类型',
       dataIndex: 'type',
       key: 'type',
       render: (text, record) => productType[record.type],
+      width: '10%',
     }, {
       title: '状态',
       dataIndex: 'status',
@@ -54,51 +34,35 @@ export default class TableList extends PureComponent {
         const statusMap = { [Codes.CODE_NORMAL]: 'success', [Codes.CODE_DELETE]: 'error' };
         return (<Badge status={statusMap[record.status]} text={itemStatuses[record.status]} />);
       },
-      filters: Object.keys(itemStatuses).map(key => ({ text: itemStatuses[key], value: key })),
-      filterMultiple: false,
-      filteredValue: [curStatus],
+      width: '10%',
     }, {
-      title: '创建时间',
+      title: '修改时间',
       dataIndex: 'gmtModified',
       key: 'gmtModified',
-      render: (text, record) => (
+      render: text => (
         <div>{moment(text).format('YYYY-MM-DD HH:mm:ss')}</div>
       ),
+      width: '20%',
     }, {
       title: '操作',
       dataIndex: 'operation',
       key: 'operation',
       render: (text, record) => (
-        <div>
-          <a onClick={() => onEditItem(record)}>编辑</a>
-          <Divider type="vertical" />
-          <a onClick={() => this.handleOperateItem(record)}>{record.status === Codes.CODE_NORMAL ? '下架' : '出售'}</a>
-        </div>
+        <a onClick={() => onViewItem(record)}>查看</a>
       ),
+      width: '5%',
     }];
 
-    // 数据table列表表头的筛选按钮点击重置后status值为空,此时删除该参数
-    columns.map((item) => {
-      item.dataIndex === 'status' && !curStatus ? delete item.filteredValue : null;
-    });
-
     // 配置分页
     tableProps.pagination = !!pagination && { ...pagination, showSizeChanger: true, showQuickJumper: true, showTotal: total => `共 ${total} 条` };
 
-    // 添加动画
-    const AnimationTableBody = props => (<AnimTableBody {...props} />);
-
     return (
       <Table
         simple
         bordered
         {...tableProps}
         columns={columns}
-        className={classnames({ [styles.table]: true, [styles.motion]: true })}
         rowKey={record => record.id}
-        components={{
-          body: { wrapper: AnimationTableBody },
-        }}
       />
     );
   }

+ 0 - 82
src/routes/MProduct/List/table.less

@@ -1,82 +0,0 @@
-@import "~antd/lib/style/themes/default.less";
-@import "../../../utils/utils.less";
-
-.table {
-  :global {
-    .ant-table-tbody > tr > td,
-    .ant-table-thead > tr > th {
-      height: 50px;
-    }
-  }
-
-  &.motion {
-    :global {
-      .ant-table-tbody > tr > td,
-      .ant-table-thead > tr > th {
-        &:nth-child(1) {
-          width: 20%;
-        }
-
-        &:nth-child(2) {
-          width: 20%;
-        }
-
-        &:nth-child(3) {
-          width: 15%;
-        }
-
-        &:nth-child(4) {
-          width: 10%;
-        }
-
-        &:nth-child(5) {
-          width: 20%;
-        }
-
-        &:nth-child(6) {
-          width: 15%;
-        }
-      }
-
-      .ant-table-thead {
-        & > tr {
-          transition: none;
-          display: block;
-
-          & > th {
-            display: inline-flex;
-            align-items: center;
-            justify-content: center;
-          }
-        }
-      }
-
-      .ant-table-tbody {
-        & > tr {
-          transition: none;
-          display: block;
-          border-bottom: 1px solid #f5f5f5;
-
-          & > td {
-            border-bottom: none;
-            display: inline-flex;
-            align-items: center;
-            justify-content: center;
-          }
-
-          &.ant-table-expanded-row-level-1 > td {
-            height: auto;
-          }
-        }
-      }
-    }
-  }
-}
-
-.splitLine {
-  background: @border-color-split;
-  display: inline-block;
-  margin: 0 8px;
-  width: 1px;
-  height: 12px;
-}

+ 11 - 86
src/routes/MProduct/Edit/index.js

@@ -1,5 +1,5 @@
 import React, { PureComponent } from 'react';
-import { Divider, Modal, Table, Form, Card, Button, Tag } from 'antd';
+import { Divider, Table, Form, Card, Button, Tag } from 'antd';
 import { connect } from 'dva';
 import { routerRedux } from 'dva/router';
 import queryString from 'query-string';
@@ -11,14 +11,9 @@ import TagSelectModal from './tags';
 import { pageSize, productType, Codes } from '../../../utils/config';
 
 const { Description } = DescriptionList;
-const confirm = Modal.confirm;
 
 @Form.create()
-@connect(state => ({
-  mproductDetail: state.mproductDetail,
-  goodsModel: state.goodsModel,
-  tagModel: state.tagModel,
-}))
+@connect(state => ({ mproductDetail: state.mproductDetail }))
 export default class MerchantProductEdit extends PureComponent {
   handleAddPriceClick = () => {
     this.props.dispatch({
@@ -124,7 +119,7 @@ export default class MerchantProductEdit extends PureComponent {
     const { dispatch, mproductDetail } = this.props;
     const { filters } = mproductDetail;
     dispatch(routerRedux.push({
-      pathname: '/goods',
+      pathname: '/products',
       search: queryString.stringify(filters),
     }));
   }
@@ -155,38 +150,10 @@ export default class MerchantProductEdit extends PureComponent {
   }
 
   render() {
-    const { mproductDetail, goodsModel, tagModel, location } = this.props;
+    const { mproductDetail } = this.props;
     const { currentItem } = mproductDetail;
-    const { modalShow, tagModalShow, operation, goodsItem } = goodsModel;
-    const { list, listLoading, pagination } = tagModel;
     const { goods, tags, type, code, name, merchantName } = currentItem;
 
-    const { search } = location;
-    const { merchantId, pid } = queryString.parse(search);
-
-    const priceModalProps = {
-      data: operation === 'createItem' ? {} : goodsItem,
-      title: operation === 'createItem' ? '添加价格' : '编辑价格',
-      visible: modalShow,
-      maskClosable: false,
-      onCancel: () => {
-        this.props.dispatch({ type: 'goodsModel/hideModal' });
-      },
-      onSubmit: (data) => {
-        const newData = { ...data, merchantId, pid, productType: type };
-        this.props.dispatch({
-          type: `goodsModel/${operation}`,
-          payload: newData,
-          callback: () => {
-            this.props.dispatch(routerRedux.push({
-              pathname: '/goods/edit',
-              search,
-            }));
-          },
-        });
-      },
-    };
-
     const listTableProps = {
       simple: true,
       bordered: true,
@@ -198,27 +165,13 @@ export default class MerchantProductEdit extends PureComponent {
         dataIndex: 'chargeUnit',
         key: 'chargeUnit',
       }, {
-        title: '供应商价格(¥)',
-        dataIndex: 'cpPrice',
-        key: 'cpPrice',
-      }, {
-        title: '渠道方价格(¥)',
+        title: '渠道售卖价格(¥)',
         dataIndex: 'merchantPrice',
         key: 'merchantPrice',
       }, {
-        title: '终端价格(¥)',
+        title: '终端显示价格(¥)',
         dataIndex: 'terminalPrice',
         key: 'terminalPrice',
-      }, {
-        title: '操作',
-        key: 'action',
-        render: (text, record) => (
-          <span>
-            <a onClick={() => this.handleEditPriceClick(record)}>编辑</a>
-            <Divider type="vertical" />
-            <a onClick={() => this.handleDelPriceClick(record)}>删除</a>
-          </span>
-        ),
       }],
     };
 
@@ -226,59 +179,31 @@ export default class MerchantProductEdit extends PureComponent {
       <PageHeaderLayout>
         <Card
           bordered={false}
-          title="详情"
+          title="产品详情"
           style={{ marginBottom: 15 }}
         >
-          <DescriptionList size="large" col={2} style={{ marginBottom: 32 }}>
+          <DescriptionList size="large" col={3} style={{ marginBottom: 32 }}>
             <Description term="商品编号">{code}</Description>
             <Description term="商品名称">{name}</Description>
             <Description term="商品类型">{productType[type]}</Description>
-            <Description term="渠道名称">{merchantName}</Description>
           </DescriptionList>
         </Card>
         <Card
           bordered={false}
-          title={
-            <div>定价
-              <Divider type="vertical" />
-              <Button onClick={this.handleAddPriceClick} size="small" type="primary">添加</Button>
-            </div>
-          }
+          title="价格类型"
           style={{ marginBottom: 15 }}
         >
           <Table {...listTableProps} />
-          <NewPriceModal {...priceModalProps} />
         </Card>
         <Card
           bordered={false}
-          title={
-            <div>标签
-              <Divider type="vertical" />
-              <Button onClick={this.handleEditTagClick} size="small" type="primary">选择</Button>
-            </div>
-          }
+          title="已上架标签"
           style={{ marginBottom: 15 }}
         >
-          {/* 标签的模态选择框 */}
-          <TagSelectModal
-            rowKeyName="id"
-            modalVisible={tagModalShow}
-            selTableData={tags || []}
-            style={{ top: 20 }}
-            fsTableDataSource={list}
-            fsTableLoading={listLoading}
-            fsTablePagination={pagination}
-            onOk={this.handleTagModalOk}
-            onCancel={this.handleTagModalCancel}
-            onSearch={this.handleTagModalSearch}
-            fsTableOnChange={this.handleTagModalTableChange}
-          />
           {tags ? tags.map(item => <Tag color="#f50">{item.name}</Tag>) : null}
         </Card>
         <FooterToolbar>
-          <Button onClick={this.handlePageExit} type="primary">
-            完成
-          </Button>
+          <Button onClick={this.handlePageExit} type="primary">返回产品列表</Button>
         </FooterToolbar>
       </PageHeaderLayout>
     );

src/routes/MProduct/Edit/price.js → src/routes/MProduct/View/price.js


src/routes/MProduct/Edit/tags.js → src/routes/MProduct/View/tags.js


+ 1 - 26
src/routes/Merchant/index.js

@@ -1,7 +1,7 @@
 import React, { PureComponent } from 'react';
 import { connect } from 'dva';
 import { routerRedux } from 'dva/router';
-import { Spin, Card, Form, Switch, Radio, Button, Input } from 'antd';
+import { Spin, Card, Form, Radio, Button, Input } from 'antd';
 import PageHeaderLayout from '../../layouts/PageHeaderLayout';
 import { getLocalUser } from '../../utils/helper';
 
@@ -61,31 +61,6 @@ export default class MerchantInfo extends PureComponent {
         <Spin spinning={loading}>
           <Card>
             <Form layout="horizontal" onSubmit={this.handlePageSubmit}>
-              {/*
-              <Form.Item label="厂商编号" hasFeedback {...formItemLayout}>
-                {getFieldDecorator('code',{
-                  rules: [{ required: true, type: 'string', message: '编号为必填写!' }],
-                  initialValue: item.code,
-                })(<Input />)}
-              </Form.Item>
-              <Form.Item label="厂商名称" hasFeedback {...formItemLayout}>
-                {getFieldDecorator('name',{
-                  rules: [{ required: true, type: 'string', message: '名称为必填项!' }],
-                  initialValue: item.name,
-                })(<Input />)}
-              </Form.Item>
-              <Form.Item label="厂商类型" {...formItemLayout}>
-                {getFieldDecorator('domain',{
-                  initialValue: item.domain || Codes.CODE_PJ,
-                })(
-                  <Radio.Group>
-                    {Object.keys(domains).map(key =>
-                      <Radio value={Number(key)} key={`domain-${key}`}>{domains[Number(key)]}</Radio>
-                    )}
-                  </Radio.Group>
-                )}
-              </Form.Item>
-            */}
               <Form.Item label="开户银行" hasFeedback {...formItemLayout}>
                 {getFieldDecorator('depositBank', {
                   rules: [{ required: true, type: 'string', message: '开户银行为必填项!' }],

+ 499 - 145
src/routes/Order/Add/index.js

@@ -1,23 +1,40 @@
 import React, { Component } from 'react';
-import { Popover, Card, List, Form, Table, Button, Input, InputNumber, Select, Dropdown, Menu, Icon } from 'antd';
 import { connect } from 'dva';
 import { routerRedux } from 'dva/router';
+import queryString from 'query-string';
+import {
+  Tooltip,
+  Modal,
+  Card,
+  List,
+  Form,
+  Table,
+  Button,
+  Input,
+  InputNumber,
+  Select,
+} from 'antd';
 
 import PageHeaderLayout from '../../../layouts/PageHeaderLayout';
 import FooterToolbar from '../../../components/FooterToolbar';
 import TerminalSelectModal from './terminal';
 import MerchantProductSelectModal from './product';
-import { productType, pageSize } from '../../../utils/config';
-import styles from './index.less';
+import { productType, pageSize, Codes } from '../../../utils/config';
 
 @Form.create()
 @connect(state => ({
   terminal: state.terminal,
-  orderItem: state.orderDetail,
+  orderDetail: state.orderDetail,
   mproduct: state.mproduct,
 }))
 export default class CreateOrder extends Component {
-  // 终端选择弹框相关的回调方法
+  state = {
+    userInfo: {}, // 记录终端用户信息
+    products: [], // 记录选择的产品
+    tableDatas: [], // 记录选择的商品
+  };
+
+  // 终端选择弹框,显示 -> 加载数据
   handleTerminalSelectBtnClick = () => {
     this.props.dispatch({ type: 'orderDetail/showTerminalModal' });
     this.props.dispatch({
@@ -29,36 +46,64 @@ export default class CreateOrder extends Component {
     });
   }
 
+  // 选择终端
   handleTerminalModalOk = (record) => {
-    this.props.dispatch({
-      type: 'orderDetail/savePickedTerminal',
-      payload: {
+    this.setState({
+      userInfo: {
+        uid: record.id,
         userCode: record.code,
         userName: record.name,
         campusName: record.campusName,
         merchantName: record.merchantName,
         merchantId: record.merchantId,
+        contactName: record.contactName,
+        mobile: record.mobile,
+        address: record.address,
       },
     });
+    this.props.dispatch({ type: 'orderDetail/hideTerminalModal' });
   }
 
   handleTerminalModalCancel = () => {
     this.props.dispatch({ type: 'orderDetail/hideTerminalModal' });
   }
 
-  handleTerminalModalSearch = () => {
-
+  handleTerminalModalSearch = (data) => {
+    const newData = { ...data };
+    if (newData.keyword) {
+      newData[newData.field] = newData.keyword;
+    }
+    delete newData.field;
+    delete newData.keyword;
+    this.props.dispatch({
+      type: 'terminal/query',
+      payload: { ...newData, pageNo: 1, pageSize },
+    });
   }
 
-  handleTerminalModalTableChange = () => {
+  handleTerminalModalTableChange = (pagination, filterArgs, filters) => {
+    const newFilters = { ...filters };
+    if (newFilters.keyword) {
+      newFilters[newFilters.field] = newFilters.keyword;
+    }
+    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]));
+    this.props.dispatch({ type: 'terminal/query', payload: data });
   }
 
-  // 产品选择弹框相关的回调方法
+  // 产品选择弹框
   handleProductSelectBtnClick = () => {
-    const { orderItem } = this.props;
-    const { currentItem } = orderItem;
-    const { merchantId } = currentItem;
+    const { userInfo } = this.state;
+    const { merchantId } = userInfo;
     this.props.dispatch({ type: 'orderDetail/showProductModal' });
     this.props.dispatch({
       type: 'mproduct/query',
@@ -70,71 +115,289 @@ export default class CreateOrder extends Component {
     });
   }
 
-  handleProductModalSearch = () => {
-
+  handleProductModalSearch = (data) => {
+    const { userInfo } = this.state;
+    const { merchantId } = userInfo;
+    const newData = { ...data };
+    if (newData.keyword) {
+      newData[newData.field] = newData.keyword;
+    }
+    delete newData.field;
+    delete newData.keyword;
+    this.props.dispatch({
+      type: 'mproduct/query',
+      payload: { ...newData, merchantId, pageNo: 1, pageSize },
+    });
   }
 
-  handleProductModalTableChange = () => {
+  handleProductModalTableChange = (pagination, filterArgs, filters) => {
+    const { userInfo } = this.state;
+    const { merchantId } = userInfo;
+    const newFilters = { ...filters };
+    if (newFilters.keyword) {
+      newFilters[newFilters.field] = newFilters.keyword;
+    }
+    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, merchantId };
+    Object.keys(data).map(key => (data[key] ? null : delete data[key]));
+    this.props.dispatch({ type: 'mporduct/query', payload: data });
   }
 
+  // 选择产品
   handleProductModalOk = (data) => {
-    this.props.dispatch({
-      type: 'orderDetail/savePickedProducts',
-      payload: data,
+    const formatedData = this.tableDataConventer(data);
+    this.setState({
+      products: data || [],
+      tableDatas: formatedData,
     });
+    this.props.dispatch({ type: 'orderDetail/hideProductModal' });
   }
 
   handleProductModalCancel = () => {
     this.props.dispatch({ type: 'orderDetail/hideProductModal' });
   }
 
-  render() {
-    const { orderItem, terminal, mproduct, form } = this.props;
-    const { terminalModalShow, productModalShow } = orderItem;
-    const currentItem = {
-      userCode: '15790324883489',
-      userName: '教室六',
-      campusName: '北京-北京市 西城区-科贸A座',
-      merchantName: '贝尔安亲',
-      merchantId: '555',
-      products: [{
-        id: 'p1',
-        code: 'P-01',
-        name: '语文一年级上册',
-        type: 'COURSE',
-        coverUrl: 'https://os.alipayobjects.com/rmsportal/QBnOOoLaAfKPirc.png',
-        goods: [{
-          id: '1',
-          chargeUnit: '年',
-          cpPrice: 2000,
-          merchantPrice: 3000,
-          terminalPrice: 4000,
-        }, {
-          id: '2',
-          chargeUnit: '半年',
-          cpPrice: 1000,
-          merchantPrice: 2000,
-          terminalPrice: 3000,
-        }, {
-          id: '3',
-          chargeUnit: '季',
-          cpPrice: 500,
-          merchantPrice: 800,
-          terminalPrice: 1000,
-        }],
-      }, {
-        id: 'package-1',
-        code: '课程包-01',
-        name: '小学一年级语文课程包',
-        type: 'PACKAGE',
-        coverUrl: 'https://os.alipayobjects.com/rmsportal/QBnOOoLaAfKPirc.png',
-        goods: [{
+  // 对待选的产品进行过滤,如果没有goods则不可选
+  productListFilter = (list) => {
+    const newList = [...list];
+    newList.map(item =>
+      ((!item.goods || item.goods.length == 0) ? item.selectable = true : item.selectable = false));
+    return newList;
+  }
+
+  handleListItemDel = (record) => {
+    const { products, tableDatas } = this.state;
+    Modal.confirm({
+      title: '确定从清单中删除此商品?',
+      cancelText: '取消',
+      okText: '确认',
+      onOk: () => {
+        const newProducts = products.filter(item => item.id !== record.key);
+        const newTableDatas = tableDatas.filter(item => item.key.indexOf(record.key) == -1);
+        this.setState({
+          products: newProducts,
+          tableDatas: newTableDatas,
+        });
+      },
+    });
+  }
+
+  PKGPriceCalculator = (item, tableDatas) => {
+    // 根据key找到该课程包内的所有配套
+    const parentId = item.key;
+    const subSupports = tableDatas.filter(row =>
+      row.key.indexOf(`${parentId}[sub]`) !== -1 && row.type == Codes.CODE_SUPPORT
+    );
+    // 计算该课程包的价格
+    let sumSupportsPrice = 0;
+    subSupports.map((one) => {
+      sumSupportsPrice += one.price2 * one.quantity;
+    });
+    item.rowSum = item.quantity * item.price2 + sumSupportsPrice;
+  }
+
+  handleInputNumberChange = (record, value) => {
+    const { tableDatas } = this.state;
+    const newTableDatas = [...tableDatas];
+    newTableDatas.map((item) => {
+      if (item.key == record.key) {
+        item.quantity = value;
+        if (record.type == Codes.CODE_PACKAGE) {
+          this.PKGPriceCalculator(item, newTableDatas);
+        } else if (record.type == Codes.CODE_SUPPORT && item.parent) {
+          const parentId = item.key.split('[sub]')[0];
+          newTableDatas.map(item => (item.key == parentId ? this.PKGPriceCalculator(item, newTableDatas) : null));
+        } else {
+          item.rowSum = item.quantity * item.price2;
+        }
+      }
+    });
+    this.setState({ tableDatas: newTableDatas });
+  }
+
+  handleSelectChange = (record, value) => {
+    const { tableDatas } = this.state;
+    const newTableDatas = [...tableDatas];
+    newTableDatas.map((item) => {
+      if (item.key === record.key) {
+        const match = item.options.filter(one => one.goodsId === value)[0];
+        item.price1 = match.price1;
+        item.price2 = match.price2;
+        item.price3 = match.price3;
+        item.chargeUnit = match.chargeUnit;
+        item.goodsId = value;
+        if (record.type === Codes.CODE_PACKAGE) {
+          this.PKGPriceCalculator(item, newTableDatas);
+        } else if (record.type === Codes.CODE_SUPPORT && item.parent) {
+          const parentId = item.key.split('[sub]')[0];
+          newTableDatas.map(item => (item.key === parentId ? this.PKGPriceCalculator(item, newTableDatas) : null));
+        } else {
+          item.rowSum = item.quantity * item.price2;
+        }
+      }
+    });
+    this.setState({ tableDatas: newTableDatas });
+  }
+
+  handlePageCancel = () => {
+    const { orderDetail, dispatch } = this.props;
+    const { filters } = orderDetail;
+    dispatch(routerRedux.push({
+      pathname: '/order',
+      search: queryString.stringify(filters),
+    }));
+  }
+
+  handlePageSubmit = () => {
+    const { form, dispatch, orderDetail } = this.props;
+    const { userInfo, tableDatas } = this.state;
+    const { getFieldsValue, validateFields } = form;
+    const { filters } = orderDetail;
+    const { uid } = userInfo;
+    validateFields((errors) => {
+      if (errors) return;
+      const postData = getFieldsValue();
+      const detailList = [];
+      tableDatas.map((item) => {
+        const { goodsId, quantity } = item;
+        if (!item.parent || item.type === Codes.CODE_SUPPORT) {
+          detailList.push({ goodsId, quantity });
+        }
+      });
+      postData.uid = uid;
+      postData.goods = detailList;
+      postData.adjustPrice = 0;
+      postData.orderStatus = Codes.CODE_UNPAID;
+      dispatch({
+        type: 'orderDetail/create',
+        payload: postData,
+        callback: () => {
+          dispatch(routerRedux.push({
+            pathname: '/order',
+            search: queryString.stringify(filters),
+          }));
+        },
+      });
+    });
+  }
 
-        }],
-      }],
+  /**
+   * @desc 对选择的产品列表进行加工,以适应table的展示样式,并进行数量及价格调整
+   * @param {[json]} data
+   * @return {[json]}
+   */
+  tableDataConventer = (data) => {
+    let rowSort = 1;
+    const formatedData = [];
+    const rowDataMaker = (item) => {
+      const first = item.goods[0];
+      return {
+        name: item.name,
+        code: item.code,
+        type: item.type,
+        goodsId: first.id,
+        price1: first.cpPrice,
+        price2: first.merchantPrice,
+        price3: first.terminalPrice,
+        rowSum: first.merchantPrice,
+        chargeUnit: first.chargeUnit,
+      };
     };
-    const { userCode, userName, campusName, merchantName, merchantId, products } = currentItem;
+    data.map((item) => {
+      if (!item.goods || item.goods.length == 0) return;
+      if (item.type === Codes.CODE_COURSE) {
+        const newObj = rowDataMaker(item);
+        newObj.sumRows = 1;
+        newObj.quantity = 1;
+        newObj.key = item.id; // table row, type course, key is id
+        newObj.options = item.goods.map(
+          one => ({
+            goodsId: one.id,
+            price1: one.cpPrice,
+            price2: one.merchantPrice,
+            price3: one.terminalPrice,
+            chargeUnit: one.chargeUnit,
+          })
+        );
+        newObj.rowSort = rowSort;
+        rowSort += 1;
+        formatedData.push(newObj);
+      } else if (item.type === Codes.CODE_SUPPORT) {
+        const newObj = rowDataMaker(item);
+        newObj.sumRows = 1;
+        newObj.quantity = 1;
+        newObj.key = item.id; // table row, type support, key is id
+        newObj.rowSort = rowSort;
+        rowSort += 1;
+        formatedData.push(newObj);
+      } else if (item.type === Codes.CODE_PACKAGE) {
+        const { products, specials } = item;
+        // 产品包为一行
+        const newObj = rowDataMaker(item);
+        newObj.sumRows = products ? products.length + 1 : 1;
+        newObj.quantity = 1;
+        newObj.key = item.id; // table row, type package, key is id
+        newObj.options = item.goods.map(
+          one => ({
+            goodsId: one.id,
+            price1: one.cpPrice,
+            price2: one.merchantPrice,
+            price3: one.terminalPrice,
+            chargeUnit: one.chargeUnit,
+          })
+        );
+        newObj.rowSort = rowSort;
+        rowSort += 1;
+        formatedData.push(newObj);
+        // 产品包中products每一项为一行
+        products.map((subItem) => {
+          const matchGoods = specials.filter(specialsItem => specialsItem.pid == subItem.pid)[0];
+          const newObj = {};
+          newObj.sumRows = 0;
+          newObj.name = subItem.name;
+          newObj.code = subItem.code;
+          newObj.type = subItem.type;
+          newObj.key = `${item.id}[sub]${subItem.id}`; // table row, type package subPro, key is pkgId[sub]pid
+          // 产品包中的配套 - 显示价格及支持数量调整
+          if (subItem.type == Codes.CODE_SUPPORT) {
+            newObj.parent = true;
+            newObj.goodsId = matchGoods.id;
+            newObj.chargeUnit = '件';
+            newObj.price1 = matchGoods.cpPrice;
+            newObj.price2 = matchGoods.merchantPrice;
+            newObj.price3 = '-';
+            newObj.quantity = 0;
+          // 产品包中的课程 - 不显示价格并且不支持数量调整
+          } else if (subItem.type === Codes.CODE_COURSE) {
+            newObj.parent = true;
+            newObj.chargeUnit = '-';
+            newObj.price1 = '-';
+            newObj.price2 = '-';
+            newObj.price3 = '-';
+            newObj.quantity = '-';
+          }
+          formatedData.push(newObj);
+        });
+      }
+    });
+    return formatedData;
+  }
+
+  render() {
+    const { orderDetail, terminal, mproduct, form } = this.props;
+    const { userInfo, products, tableDatas } = this.state;
+    const { getFieldDecorator } = form;
+    const { terminalModalShow, productModalShow } = orderDetail;
+    const listData = tableDatas;
+    const productList = this.productListFilter(mproduct.list);
 
     const formItemLayout = {
       labelCol: {
@@ -149,102 +412,201 @@ export default class CreateOrder extends Component {
     };
 
     const columns = [{
-      title: '产品信息',
-      dataIndex: 'coverUrl',
+      title: '序号',
+      dataIndex: 'rowSort',
+      width: '6%',
       key: 0,
-      render: (text, record) => (
-        <Popover
-          content={
-            <Card hoverable style={{ width: 350, height: 550 }} cover={<img alt="" src={text} />}>
-              <Card.Meta
-                title={`${record.name}/${record.code}`}
-                description={`类型: ${productType[record.type]}`}
-              />
-            </Card>
-          }
-        >
-          <img src={text} width={50} />
-        </Popover>
-      ),
+      render: (text, row) => ({ children: text, props: { rowSpan: row.sumRows } }),
     }, {
-      title: '价格类型',
-      dataIndex: 'goods',
+      title: '编号',
+      dataIndex: 'code',
       key: 1,
-      render: (text, record) => (
-        <Dropdown
-          overlay={
-            <Menu>
-              {record.goods.map(item => <Menu.Item key={item.id}>{`${item.terminalPrice}¥/${item.chargeUnit}`}</Menu.Item>)}
-            </Menu>
-          }
-        ><a>付费方式<Icon type="down" /></a>
-        </Dropdown>
-      ),
+      width: '10%',
     }, {
-      title: '优惠价格',
-      dataIndex: 'adjustPrice',
+      title: '名称',
+      dataIndex: 'name',
       key: 2,
-      render: (text, record) => (
-        <InputNumber
-          min={0}
-          defaultValue={0}
-          formatter={value => `¥ ${value}`.replace(/\B(?=(\d{3})+(?!\d))/g, ',')}
-          parser={value => value.replace(/\¥\s?|(,*)/g, '')}
-        />
-      ),
+      width: '10%',
     }, {
-      title: '实际售价',
-      dataIndex: 'finalPrice',
+      title: '类型',
+      dataIndex: 'type',
       key: 3,
+      render: text => productType[text],
+      width: '10%',
     }, {
-      title: '数量',
-      dataIndex: 'quantity',
+      title: '价格类型',
+      dataIndex: 'goods',
       key: 4,
-      render: (text, record) => (
-        <InputNumber
-          min={1}
-          defaultValue={1}
-          formatter={value => `x ${value}`.replace(/\B(?=(\d{3})+(?!\d))/g, ',')}
-          parser={value => value.replace(/\x\s?|(,*)/g, '')}
-        />
-      ),
+      render: (text, row) => {
+        // 既不是单独课程也不是课程包里的配套
+        if (!row.options && row.type !== Codes.CODE_SUPPORT) {
+          return '-';
+        // 单独的课程
+        } else if (row.options) {
+          return (
+            <Select style={{ width: '100%' }} value={row.goodsId} onChange={value => this.handleSelectChange(row, value)}>
+              {row.options.map(item => <Select.Option key={item.goodsId} value={item.goodsId}>{`¥${item.price2} / ${item.chargeUnit}`}</Select.Option>)}
+            </Select>
+          );
+        // 课程包里的配套(显示价格和数量)
+        } else if (!row.options && row.type === Codes.CODE_SUPPORT) {
+          return `¥${row.price1} / ${row.chargeUnit}`;
+        }
+      },
+      width: '13%',
     }, {
-      title: '单位',
-      dataIndex: 'chargeUnit',
+      title: '供应商售价(¥)',
+      dataIndex: 'price1',
       key: 5,
+      width: '10%',
+    }, {
+      title: '领教售价(¥)',
+      dataIndex: 'price2',
+      key: 6,
+      width: '10%',
+    }, {
+      title: '渠道售价(¥)',
+      dataIndex: 'price3',
+      key: 7,
+      width: '10%',
+    }, {
+      title: '数量',
+      dataIndex: 'quantity',
+      key: 8,
+      render: (text, row) => {
+        // 课程
+        if (row.type === Codes.CODE_COURSE && !row.parent) {
+          return (
+            <InputNumber
+              value={text}
+              onChange={value => this.handleInputNumberChange(row, value)}
+              min={1}
+              placeholder="请填写"
+            />
+          );
+        // 配套
+        } else if (row.type === Codes.CODE_SUPPORT && !row.parent) {
+          return (
+            <InputNumber
+              value={text}
+              onChange={value => this.handleInputNumberChange(row, value)}
+              min={1}
+              placeholder="请填写"
+            />
+          );
+        // 课程包
+        } else if (row.type === Codes.CODE_PACKAGE) {
+          return (
+            <InputNumber
+              value={text}
+              onChange={value => this.handleInputNumberChange(row, value)}
+              min={1}
+              placeholder="请填写"
+            />
+          );
+        // 课程包内的配套
+        } else if (row.type === Codes.CODE_SUPPORT && row.parent) {
+          return (
+            <InputNumber
+              value={text}
+              onChange={value => this.handleInputNumberChange(row, value)}
+              min={0}
+              placeholder="请填写"
+            />
+          );
+        } else {
+          return text;
+        }
+      },
+      width: '6%',
+    }, {
+      title: '小计(¥)',
+      dataIndex: 'rowSum',
+      key: 9,
+      render: (text, row) => ({ children: text, props: { rowSpan: row.sumRows } }),
+      width: '8%',
+    }, {
+      title: '操作',
+      dataIndex: 'operation',
+      key: 10,
+      render: (text, row) => ({ children: <a onClick={() => this.handleListItemDel(row)}>删除</a>, props: { rowSpan: row.sumRows } }),
+      width: '7%',
     }];
 
+    let total = 0;
+    listData.map(item => (item.rowSum ? total += item.rowSum : null));
+
     return (
       <PageHeaderLayout>
         <Card>
           <Form>
             <Form.Item label="选择终端" {...formItemLayout}>
               <Button onClick={this.handleTerminalSelectBtnClick} type="primary" size="small" icon="plus-circle-o">选择</Button>
-              {userCode ? (
-                <List
-                  size="small"
-                  bordered
-                  style={{ width: '50%' }}
-                  dataSource={[`终端编号: ${userCode}`, `终端名称: ${userName}`, `所属校区: ${campusName}`, `所属渠道: ${merchantName}`]}
-                  renderItem={item => <List.Item>{item}</List.Item>}
-                />
-) : null}
+              {userInfo.userCode ?
+                (
+                  <List
+                    size="small"
+                    bordered
+                    style={{ width: '50%' }}
+                    dataSource={[
+                    `终端编号: ${userInfo.userCode}`,
+                    `终端名称: ${userInfo.userName}`,
+                    `所属校区: ${userInfo.campusName}`,
+                    `所属渠道: ${userInfo.merchantName}`,
+                  ]}
+                    renderItem={item => <List.Item>{item}</List.Item>}
+                  />
+)
+              : null}
+            </Form.Item>
+            <Form.Item label="收货人" {...formItemLayout}>
+              {getFieldDecorator('name', {
+                rules: [{ required: true, type: 'string', message: '请填写收货人!' }],
+                initialValue: userInfo.contactName,
+              })(
+                <Input style={{ width: '50%' }} placeholder="请填写或使用默认" />
+              )}
+            </Form.Item>
+            <Form.Item label="联系电话" {...formItemLayout}>
+              {getFieldDecorator('mobile', {
+                rules: [{ required: true, type: 'string', message: '请填写联系电话!' }],
+                initialValue: userInfo.mobile,
+              })(
+                <Input style={{ width: '50%' }} placeholder="请填写或使用默认" />
+              )}
             </Form.Item>
             <Form.Item label="收货地址" {...formItemLayout}>
-              <Input.TextArea style={{ width: '50%' }} placeholder="请填写或使用默认地址" />
+              {getFieldDecorator('address', {
+                rules: [{ required: true, type: 'string', message: '请填写收货地址!' }],
+                initialValue: userInfo.address,
+              })(
+                <Input.TextArea style={{ width: '50%' }} placeholder="请填写或使用默认" />
+              )}
             </Form.Item>
             <Form.Item label="添加备注" {...formItemLayout}>
-              <Input.TextArea style={{ width: '50%' }} placeholder="请输入(选填)" />
+              {getFieldDecorator('note', {
+                initialValue: userInfo.note,
+              })(
+                <Input.TextArea style={{ width: '50%' }} placeholder="请输入(选填)" />
+              )}
             </Form.Item>
             <Form.Item label="添加商品" {...formItemLayout}>
-              <Button onClick={this.handleProductSelectBtnClick} disabled={!merchantId} type="primary" size="small" icon="plus-circle-o">添加</Button>
+              {userInfo.merchantId ?
+                <Button onClick={this.handleProductSelectBtnClick} type="primary" size="small" icon="plus-circle-o">添加</Button>
+              :
+                (
+                  <Tooltip title="先选择终端">
+                    <Button onClick={this.handleProductSelectBtnClick} disabled type="primary" size="small" icon="plus-circle-o">添加</Button>
+                  </Tooltip>
+                )
+              }
               <Table
-                size="large"
-                className={styles.table}
-                rowKey={record => record.id}
+                bordered
+                scroll={{ x: 1250 }}
                 pagination={false}
                 columns={columns}
-                dataSource={products}
+                dataSource={listData}
+                footer={() => <strong>{`价格总计:  ${total}元`}</strong>}
               />
             </Form.Item>
           </Form>
@@ -266,10 +628,10 @@ export default class CreateOrder extends Component {
           <MerchantProductSelectModal
             rowKeyName="id"
             modalVisible={productModalShow}
-            selTableData={products || []}
+            selTableData={products}
             style={{ top: 20 }}
             width={660}
-            fsTableDataSource={mproduct.list}
+            fsTableDataSource={productList}
             fsTableLoading={mproduct.listLoading}
             fsTablePagination={mproduct.pagination}
             onOk={this.handleProductModalOk}
@@ -279,16 +641,8 @@ export default class CreateOrder extends Component {
           />
         </Card>
         <FooterToolbar>
-          <Dropdown.Button
-            overlay={
-              <Menu>
-                <Menu.Item>¥1000 / 年</Menu.Item>
-                <Menu.Item>¥2000 / 季</Menu.Item>
-              </Menu>
-            }
-          />
-          <Button>取消</Button>
-          <Button type="primary">完成</Button>
+          <Button onClick={this.handlePageCancel}>取消</Button>
+          <Button onClick={this.handlePageSubmit} type="primary">提交</Button>
         </FooterToolbar>
       </PageHeaderLayout>
     );

+ 0 - 66
src/routes/Order/Add/index.less

@@ -1,66 +0,0 @@
-@import "~antd/lib/style/themes/default.less";
-@import "../../../utils/utils.less";
-
-.table {
-  :global {
-    .ant-table-tbody > tr > td,
-    .ant-table-thead > tr > th {
-
-      &:nth-child(1) {
-        width: 15%;
-      }
-
-      &:nth-child(2) {
-        width: 20%;
-      }
-
-      &:nth-child(3) {
-        width: 20%;
-      }
-
-      &:nth-child(4) {
-        width: 20%;
-      }
-
-      &:nth-child(5) {
-        width: 15%;
-      }
-
-      &:nth-child(6) {
-        width: 10%;
-      }
-    }
-
-    .ant-table-thead {
-      & > tr {
-        transition: none;
-        display: block;
-
-        & > th {
-          display: inline-flex;
-          align-items: left;
-          justify-content: left;
-        }
-      }
-    }
-
-    .ant-table-tbody {
-      & > tr {
-        transition: none;
-        display: block;
-        border-bottom: 1px solid #f5f5f5;
-
-        & > td {
-          border-bottom: none;
-          display: inline-flex;
-          align-items: left;
-          justify-content: left;
-        }
-
-        &.ant-table-expanded-row-level-1 > td {
-          height: auto;
-        }
-      }
-    }
-  }
-}

+ 18 - 55
src/routes/Order/Add/product.js

@@ -4,7 +4,15 @@ import { productType } from '../../../utils/config';
 
 export default class MerchantProductSelectModal extends PureComponent {
   render() {
-    const { selTableData, modalVisible, rowKeyName, onCancel, onOk, onSearch, ...fsTableOpts } = this.props;
+    const {
+      selTableData,
+      modalVisible,
+      rowKeyName,
+      onCancel,
+      onOk,
+      onSearch,
+      ...fsTableOpts
+    } = this.props;
 
     const modalProps = {
       title: '选则商品',
@@ -25,7 +33,7 @@ export default class MerchantProductSelectModal extends PureComponent {
         value: 'code', name: '产品编号', mode: 'input',
       }],
       searchSelectProps: {
-        defaultValue: 'name',
+        defaultValue: 'code',
       },
       onSearch: (value) => {
         onSearch(value);
@@ -41,23 +49,18 @@ export default class MerchantProductSelectModal extends PureComponent {
         title: '产品编号',
         dataIndex: 'code',
         key: 1,
-        width: '27%',
+        width: '35%',
       }, {
         title: '产品名称',
         dataIndex: 'name',
         key: 2,
-        width: '28%',
+        width: '35%',
       }, {
         title: '产品类型',
         dataIndex: 'type',
         key: 3,
-        render: (text, record) => productType[text],
-        width: '15%',
-      }, {
-        title: '渠道名称',
-        dataIndex: 'merchantName',
-        key: 4,
-        width: '15%',
+        render: text => productType[text],
+        width: '20%',
       }],
     };
 
@@ -66,23 +69,18 @@ export default class MerchantProductSelectModal extends PureComponent {
         title: '产品编号',
         dataIndex: 'code',
         key: 1,
-        width: '27%',
+        width: '32%',
       }, {
         title: '产品名称',
         dataIndex: 'name',
         key: 2,
-        width: '28%',
+        width: '35%',
       }, {
         title: '产品类型',
         dataIndex: 'type',
         key: 3,
-        render: (text, record) => productType[text],
-        width: '15%',
-      }, {
-        title: '渠道名称',
-        dataIndex: 'merchantName',
-        key: 4,
-        width: '15%',
+        render: text => productType[text],
+        width: '20%',
       }],
       ...fsTableOpts,
     };
@@ -98,38 +96,3 @@ export default class MerchantProductSelectModal extends PureComponent {
     );
   }
 }
-
-// import React, { PureComponent } from 'react';
-// import { Modal, List, Collapse, Card, Avatar, Input } from 'antd';
-//
-// const { Panel } = Collapse;
-// export default class GoodsSelectModal extends PureComponent {
-//   render() {
-//     const { modalVisible, onCancel, onOk, pagination, dataSource, ...restProps } = this.props;
-//     return (
-//       <Modal
-//         title="添加商品"
-//         visible={modalVisible}
-//         onOk={onOk}
-//         onCancel={onCancel}
-//         {...restProps}
-//       >
-//         <List
-//           size="large"
-//           bordered
-//           grid={{ gutter: 16, column: 2 }}
-//           pagination={pagination}
-//           dataSource={dataSource}
-//           renderItem={item =>
-//             <List.Item
-//             >
-//               <Card title={item.code}>
-//
-//               </Card>
-//             </List.Item>
-//           }
-//         />
-//       </Modal>
-//     );
-//   }
-// }

+ 4 - 12
src/routes/Order/Add/terminal.js

@@ -1,6 +1,5 @@
 import React, { PureComponent } from 'react';
 import SelectModal from '../../../components/SelectModal';
-import { Codes } from '../../../utils/config';
 
 export default class TerminalSelectModal extends PureComponent {
   render() {
@@ -21,17 +20,15 @@ export default class TerminalSelectModal extends PureComponent {
     };
 
     const searchProps = {
-      searchField: 'name',
+      searchField: 'code',
       searchKeyWord: '',
       searchSize: 'default',
       searchSelect: true,
       searchSelectOptions: [{
-        value: 'name', name: '终端名称', mode: 'input',
-      }, {
         value: 'code', name: '终端编号', mode: 'input',
       }],
       searchSelectProps: {
-        defaultValue: 'name',
+        defaultValue: 'code',
       },
       onSearch: (value) => {
         onSearch(value);
@@ -48,17 +45,12 @@ export default class TerminalSelectModal extends PureComponent {
         title: '终端名称',
         dataIndex: 'name',
         key: 2,
-        width: '15%',
+        width: '18%',
       }, {
         title: '校区名称',
         dataIndex: 'campusName',
         key: 3,
-        width: '30%',
-      }, {
-        title: '渠道名称',
-        dataIndex: 'merchantName',
-        key: 4,
-        width: '15%',
+        width: '45%',
       }],
       ...fsTableOpts,
     };

+ 361 - 0
src/routes/Order/Edit/SubOrderProfile.js

@@ -0,0 +1,361 @@
+import React, { Component } from 'react';
+import { connect } from 'dva';
+import { routerRedux } from 'dva/router';
+import queryString from 'query-string';
+import { Card, Form, Modal, Steps, Button, Table, Input, Icon } from 'antd';
+import PageHeaderLayout from '../../../layouts/PageHeaderLayout';
+import FooterToolbar from '../../../components/FooterToolbar';
+import { Codes, productType } from '../../../utils/config';
+import { provCodeToName } from '../../../utils/city';
+
+@connect(state => ({ orderDetail: state.orderDetail }))
+@Form.create()
+export default class SubOrderProfile extends Component {
+  /**
+   * 返回订单列表
+   */
+  handlePageCancel = () => {
+    const { filters, dispatch } = this.props;
+    dispatch(routerRedux.push({
+      pathname: '/order',
+      search: queryString.stringify(filters),
+    }));
+  }
+
+  /**
+   * 显示发货弹框
+   */
+  showSendModal = () => {
+    this.props.dispatch({
+      type: 'orderDetail/showDeliveryModal',
+    });
+  }
+
+  /**
+   * 隐藏发货弹框
+   */
+  hideSendModal = () => {
+    this.props.dispatch({
+      type: 'orderDetail/hideDeliveryModal',
+    });
+    this.props.form.resetFields();
+  }
+
+  /**
+   * 点击确认发货
+   */
+  confirmSend = () => {
+    const { dispatch, form, orderDetail } = this.props;
+    const { validateFields, getFieldsValue } = form;
+    const { currentItem, filters } = orderDetail;
+    const { id } = currentItem;
+    validateFields((errors) => {
+      if (!errors) {
+        const trackNo = getFieldsValue(['tracKNo']);
+        dispatch({
+          type: 'orderDetail/orderSend',
+          payload: { id, trackNo },
+          callback: () => dispatch(routerRedux.push({
+            pathname: `/order/sub/profile/${id}`,
+            search: queryString.stringify(filters),
+          })),
+        });
+      }
+    });
+  }
+
+  /**
+   * 点击确认收货
+   */
+  confirmReceipt = () => {
+    const { dispatch, orderDetail } = this.props;
+    const { currentItem, filters } = orderDetail;
+    const { id } = currentItem;
+    dispatch({
+      type: 'orderDetail/orderReceive',
+      payload: { id },
+      callback: () => dispatch(routerRedux.push({
+        pathname: `/order/sub/profile/${id}`,
+        search: queryString.stringify(filters),
+      })),
+    });
+  }
+
+  /**
+   * 根据订单状态来生成操作按钮
+   */
+  renderFooter = (status) => {
+    switch (status) {
+      // 待发货订单
+      // case Codes.CODE_FORSEND:
+      //   return (
+      //     <FooterToolbar>
+      //       <Button onClick={this.handlePageCancel}>返回订单列表</Button>
+      //       <Button onClick={this.showSendModal} type="primary">立即发货</Button>
+      //     </FooterToolbar>
+      //   );
+      // // 待收货订单
+      // case Codes.CODE_SENT:
+      //   return (
+      //     <FooterToolbar>
+      //       <Button onClick={this.handlePageCancel}>返回订单列表</Button>
+      //       <Button onClick={this.confirmReceipt} type="primary">确认收货</Button>
+      //     </FooterToolbar>
+      //   );
+      // 已完成订单
+      case Codes.CODE_COMPLETE:
+        return (
+          <FooterToolbar>
+            <Button onClick={this.handlePageCancel}>返回订单列表</Button>
+          </FooterToolbar>
+        );
+      // 已取消订单
+      case Codes.CODE_CANCEL:
+        return (
+          <FooterToolbar>
+            <Button onClick={this.handlePageCancel}>返回订单列表</Button>
+          </FooterToolbar>
+        );
+      default:
+        return (
+          <FooterToolbar>
+            <Button onClick={this.handlePageCancel}>返回订单列表</Button>
+          </FooterToolbar>
+        );
+    }
+  }
+
+  render() {
+    const { orderDetail, form } = this.props;
+    const { getFieldDecorator } = form;
+    const { currentItem, deliveryModalShow } = orderDetail;
+    const {
+      userCode,
+      provinceCode,
+      cityName,
+      zoneName,
+      classroomName,
+      orderStatus,
+      merchantName,
+      // originPrice,
+      // finalPrice,
+      // adjustPrice,
+      name,
+      type,
+      mobile,
+      address,
+      note,
+      trackNo,
+      goods,
+    } = currentItem;
+
+    // simply table columns
+    const simplyTableColumns = [{
+      title: 'field',
+      dataIndex: 'field',
+      key: 1,
+      width: '20%',
+    }, {
+      title: 'value',
+      dataIndex: 'value',
+      key: 2,
+      width: '80%',
+    }];
+    // 收货人信息 - table data
+    const consumerInfoTableData = [{
+      field: '购买终端编号',
+      value: userCode,
+      key: 1,
+    }, {
+      field: '购买终端名称',
+      value: `${classroomName}`,
+      key: 2,
+    }, {
+      field: '终端所属校区',
+      value: `${provCodeToName(provinceCode)}-${cityName}-${zoneName}`,
+      key: 3,
+    }, {
+      field: '终端所属渠道',
+      value: merchantName,
+      key: 4,
+    }, {
+      field: '收货人姓名',
+      value: name,
+      key: 5,
+    }, {
+      field: '收货人电话',
+      value: mobile,
+      key: 6,
+    }, {
+      field: '收货地址',
+      value: address,
+      key: 7,
+    }];
+    // 物流信息 - table datas
+    const deliveryTableData = [{
+      field: '物流单号',
+      value: trackNo,
+      key: 1,
+    }];
+    // 商品清单
+    const goodsTableColumns = [{
+      title: '商品编号',
+      dataIndex: 'code',
+      key: 1,
+      width: '25%',
+    }, {
+      title: '商品名称',
+      dataIndex: 'name',
+      key: 2,
+      width: '25%',
+    }, {
+      title: '商品类型',
+      dataIndex: 'type',
+      render: text => productType[text],
+      key: 3,
+      width: '15%',
+    }, {
+      title: '商品售价(元)',
+      dataIndex: 'merchantPrice',
+      key: 4,
+      width: '25%',
+    }, {
+      title: '商品数量',
+      dataIndex: 'quantity',
+      key: 5,
+      width: '10%',
+    }];
+    // 结算信息 - table data
+    /*
+    const strs = [];
+    (goods || []).forEach(item => strs.push(`${item.merchantPrice}(元) * ${item.quantity}`));
+    const accountTableDatas = [{
+      field: '商品总价',
+      value: `${strs.join(' + ')} = ${originPrice}(元)`,
+      key: 1,
+    }, {
+      field: '支付金额',
+      value:
+  <span>
+    {`总价:${originPrice}(元) - 商品优惠:${adjustPrice}(元) = 订单总金额:`}
+    <strong style={{ color: 'red' }}>{`${finalPrice}(元)`}</strong>
+  </span>,
+      key: 2,
+    }];
+    */
+
+    const entityStepMap = {
+      [Codes.CODE_UNPAID]: 0,
+      [Codes.CODE_PAID]: 1,
+      [Codes.CODE_FORSEND]: 1,
+      [Codes.CODE_SENT]: 2,
+      [Codes.CODE_RECEIVED]: 3,
+      [Codes.CODE_CANCEL]: 4,
+      [Codes.CODE_COMPLETE]: 4,
+    };
+    const virtualStepMap = {
+      [Codes.CODE_UNPAID]: 0,
+      [Codes.CODE_PAID]: 1,
+      [Codes.CODE_FORSEND]: 1,
+      [Codes.CODE_SENT]: 1,
+      [Codes.CODE_RECEIVED]: 1,
+      [Codes.CODE_CANCEL]: 4,
+      [Codes.CODE_COMPLETE]: 4,
+    };
+
+    const formItemLayout = {
+      labelCol: { span: 7 },
+      wrapperCol: { span: 15 },
+    };
+
+    return (
+      <PageHeaderLayout title="子订单详情">
+        {type === Codes.CODE_ENTITY ?
+          (
+            <Card title="订单状态" bordered={false} style={{ marginBottom: 20 }}>
+              <Steps current={entityStepMap[orderStatus]}>
+                <Steps.Step title="提交订单" icon={<Icon type="schedule" />} />
+                <Steps.Step title="付款成功" icon={<Icon type="red-envelope" />} />
+                <Steps.Step title="商品出库" icon={<Icon type="inbox" />} />
+                <Steps.Step title="等待收货" icon={<Icon type="rocket" />} />
+                <Steps.Step title="完成" icon={<Icon type="check" />} />
+              </Steps>
+            </Card>
+)
+        :
+          (
+            <Card title="订单状态" bordered={false} style={{ marginBottom: 20 }}>
+              <Steps current={virtualStepMap[orderStatus]}>
+                <Steps.Step title="提交订单" icon={<Icon type="schedule" />} />
+                <Steps.Step title="付款成功" icon={<Icon type="red-envelope" />} />
+                <Steps.Step title="完成" icon={<Icon type="check" />} />
+              </Steps>
+            </Card>
+)
+        }
+        <Card title="商品清单" bordered={false} style={{ marginBottom: 20 }}>
+          <Table
+            size="small"
+            rowKey={record => record.id}
+            columns={goodsTableColumns}
+            dataSource={goods || []}
+            bordered
+            pagination={false}
+          />
+        </Card>
+        <Card title="收货人信息" bordered={false} style={{ marginBottom: 20 }}>
+          <Table
+            size="small"
+            columns={simplyTableColumns}
+            dataSource={consumerInfoTableData}
+            bordered
+            pagination={false}
+            showHeader={false}
+          />
+        </Card>
+        {orderStatus === Codes.CODE_SENT ?
+          (
+            <Card title="物流信息" bordered={false} style={{ marginBottom: 20 }}>
+              <Table
+                size="small"
+                columns={simplyTableColumns}
+                dataSource={deliveryTableData}
+                bordered
+                pagination={false}
+                showHeader={false}
+              />
+            </Card>
+        ) : null}
+        <Card title="订单备注" bordered={false} style={{ marginBottom: 20 }}>
+          {note || '无'}
+        </Card>
+        {/* <Card title="结算信息" bordered={false}>
+          <Table
+            size="small"
+            columns={simplyTableColumns}
+            dataSource={accountTableDatas}
+            bordered
+            pagination={false}
+            showHeader={false}
+          />
+        </Card> */}
+        <Modal
+          title="发货信息"
+          visible={deliveryModalShow}
+          onOk={this.confirmSend}
+          onCancel={this.hideSendModal}
+        >
+          <Form layout="horizontal">
+            <Form.Item label="物流单号:" hasFeedback {...formItemLayout}>
+              {getFieldDecorator('code', {
+                rules: [{ required: true, type: 'string', message: '请填写正确的物流单号!' }],
+                initialValue: trackNo,
+              })(<Input placeholder="请填写物流单号" />)}
+            </Form.Item>
+          </Form>
+        </Modal>
+        {this.renderFooter(orderStatus)}
+      </PageHeaderLayout>
+    );
+  }
+}

+ 0 - 87
src/routes/Order/Edit/modal.js

@@ -1,87 +0,0 @@
-import React, { PureComponent } from 'react';
-import PropTypes from 'prop-types';
-import { Badge, Popover, Icon } from 'antd';
-import SelectModal from '../../../components/SelectModal';
-import styles from './modal.less';
-import { Codes, resourceType } from '../../../utils/config';
-
-export default class WareSelectSortModal extends PureComponent {
-  static propTypes = {
-    selTableData: PropTypes.array.isRequired,
-    modalVisible: PropTypes.bool.isRequired,
-    rowKeyName: PropTypes.string.isRequired,
-  };
-
-  render() {
-    const { selTableData, modalVisible, rowKeyName, onCancel, onOk, onSearch, ...fsTableOpts } = this.props;
-
-    const modalProps = {
-      title: '选择课件',
-      maskClosable: false,
-      visible: modalVisible,
-      onCancel,
-      onOk,
-    };
-
-    const searchProps = {
-      searchField: 'name',
-      searchKeyWord: '',
-      searchSize: 'default',
-      searchSelect: true,
-      searchSelectOptions: [{
-        value: 'name', name: '课件名称', mode: 'input',
-      }, {
-        value: 'code', name: '课件编号', mode: 'input',
-      }],
-      searchSelectProps: {
-        defaultValue: 'name',
-      },
-      onSearch: (value) => {
-        onSearch(value);
-      },
-    };
-
-    const selTableProps = {
-      operDel: true,
-      operSort: true,
-      tableClassName: styles.sTable,
-      tablePagination: false,
-      tableDataSource: selTableData,
-      rowKeyName,
-      tableColumns: [{
-        title: '课件编号',
-        dataIndex: 'code',
-        key: 'code',
-      }, {
-        title: '课件名称',
-        dataIndex: 'name',
-        key: 'name',
-      }],
-    };
-
-    // 待选资源Table属性
-    const fsTableProps = {
-      fsTableClassName: styles.fsTable,
-      fsTableColumns: [{
-        title: '课件编号',
-        dataIndex: 'code',
-        key: 'code',
-      }, {
-        title: '课件名称',
-        dataIndex: 'name',
-        key: 'name',
-      }],
-      ...fsTableOpts,
-    };
-
-    return (
-      <SelectModal
-        mode="multiple"
-        {...searchProps}
-        {...modalProps}
-        {...selTableProps}
-        {...fsTableProps}
-      />
-    );
-  }
-}

+ 0 - 119
src/routes/Order/Edit/modal.less

@@ -1,119 +0,0 @@
-.fsTable {
-  :global {
-    .ant-table-tbody > tr > td,
-    .ant-table-thead > tr > th {
-      height: 50px;
-    }
-  }
-
-  :global {
-    .ant-table-tbody > tr > td,
-    .ant-table-thead > tr > th {
-      &:nth-child(1) {
-        width: 40%;
-      }
-
-      &:nth-child(2) {
-        width: 40%;
-      }
-
-      &:nth-child(3) {
-        width: 20%;
-      }
-    }
-
-    .ant-table-thead {
-      & > tr {
-        transition: none;
-        display: block;
-
-        & > th {
-          display: inline-flex;
-          align-items: center;
-          justify-content: center;
-        }
-      }
-    }
-
-    .ant-table-tbody {
-      & > tr {
-        transition: none;
-        display: block;
-        border-bottom: 1px solid #f5f5f5;
-
-        & > td {
-          border-bottom: none;
-          display: inline-flex;
-          align-items: center;
-          justify-content: center;
-        }
-
-        &.ant-table-expanded-row-level-1 > td {
-          height: auto;
-        }
-      }
-    }
-  }
-}
-
-.sTable {
-  :global {
-    .ant-table-tbody > tr > td,
-    .ant-table-thead > tr > th {
-      height: 50px;
-    }
-  }
-
-  :global {
-    .ant-table-tbody > tr > td,
-    .ant-table-thead > tr > th {
-      &:nth-child(1) {
-        width: 30%;
-      }
-
-      &:nth-child(2) {
-        width: 30%;
-      }
-
-      &:nth-child(3) {
-        width: 20%;
-      }
-
-      &:nth-child(4) {
-        width: 20%;
-      }
-    }
-
-    .ant-table-thead {
-      & > tr {
-        transition: none;
-        display: block;
-
-        & > th {
-          display: inline-flex;
-          align-items: center;
-          justify-content: center;
-        }
-      }
-    }
-
-    .ant-table-tbody {
-      & > tr {
-        transition: none;
-        display: block;
-        border-bottom: 1px solid #f5f5f5;
-
-        & > td {
-          border-bottom: none;
-          display: inline-flex;
-          align-items: center;
-          justify-content: center;
-        }
-
-        &.ant-table-expanded-row-level-1 > td {
-          height: auto;
-        }
-      }
-    }
-  }
-}

+ 242 - 54
src/routes/Order/Edit/orderProfile.js

@@ -1,87 +1,275 @@
 import React, { Component } from 'react';
 import { connect } from 'dva';
-import { Card, Badge, Table, Divider } from 'antd';
+import { routerRedux } from 'dva/router';
+import queryString from 'query-string';
+import { Card, Modal, Button, Table } from 'antd';
 import moment from 'moment';
 import PageHeaderLayout from '../../../layouts/PageHeaderLayout';
-import DescriptionList from '../../../components/DescriptionList';
-import { Codes, orderStatuses } from '../../../utils/config';
-import styles from './orderProfile.less';
-
-const { Description } = DescriptionList;
+import FooterToolbar from '../../../components/FooterToolbar';
+import { Codes, orderStatuses, productType, orderSplitStatus } from '../../../utils/config';
+import { provCodeToName } from '../../../utils/city';
 
 @connect(state => ({ orderDetail: state.orderDetail }))
 export default class OrderProfile extends Component {
+  /**
+   * 退出页面
+   */
+  handlePageCancel = () => {
+    const { filters, dispatch } = this.props;
+    dispatch(routerRedux.push({
+      pathname: '/order',
+      search: queryString.stringify(filters),
+    }));
+  }
+
+  /**
+   * 取消订单
+   */
+  deleteItem = () => {
+    const { dispatch, orderDetail } = this.props;
+    const { currentItem, filters } = orderDetail;
+    const { id } = currentItem;
+    Modal.confirm({
+      okText: '确认',
+      cancelText: '取消',
+      title: '确定取消该订单?',
+      onOk: () =>
+        dispatch({
+          type: 'orderDetail/remove',
+          payload: { id },
+          callback: () => dispatch(routerRedux.push({
+            pathname: `/order/profile/${id}`,
+            search: queryString.stringify(filters),
+          })),
+        }),
+    });
+  }
+
+  /**
+   * 确认支付
+   */
+  confirmPay = () => {
+    const { dispatch, orderDetail } = this.props;
+    const { currentItem, filters } = orderDetail;
+    const { id } = currentItem;
+    Modal.confirm({
+      okText: '确认支付',
+      cancelText: '取消支付',
+      title: '确定支付该订单?',
+      onOk: () => {
+        dispatch({
+          type: 'orderDetail/orderPay',
+          payload: { id },
+          callback: () => dispatch(routerRedux.push({
+            pathname: `/order/profile/${id}`,
+            search: queryString.stringify(filters),
+          })),
+        });
+      },
+    });
+  }
+
+  /**
+   * 渲染底部操作按钮
+   */
+  renderFooter = (status) => {
+    switch (status) {
+      case Codes.CODE_UNPAID:
+        return (
+          <FooterToolbar>
+            <Button onClick={this.handlePageCancel}>返回订单列表</Button>
+            <Button onClick={this.deleteItem} type="danger">取消订单</Button>
+            <Button onClick={this.confirmPay} type="primary">立即支付</Button>
+          </FooterToolbar>
+        );
+      default:
+        return (
+          <FooterToolbar>
+            <Button onClick={this.handlePageCancel}>返回订单列表</Button>
+          </FooterToolbar>
+        );
+    }
+  }
+
   render() {
     const { orderDetail } = this.props;
     const { currentItem } = orderDetail;
-    const { code, gmtCreated, userCode, status, provinceCode, cityName, zoneName, classroomName } = currentItem;
+    const {
+      id,
+      gmtCreated,
+      userCode,
+      provinceCode,
+      cityName,
+      zoneName,
+      classroomName,
+      orderStatus,
+      splitStatus,
+      merchantName,
+      originPrice,
+      finalPrice,
+      adjustPrice,
+      name,
+      mobile,
+      address,
+      note,
+      goods,
+    } = currentItem;
 
+    // simply table columns
+    const simplyTableColumns = [{
+      title: 'field',
+      dataIndex: 'field',
+      key: 1,
+      width: '20%',
+    }, {
+      title: 'value',
+      dataIndex: 'value',
+      key: 2,
+      width: '80%',
+    }];
+    // 订单信息 - table data
+    const orderInfoTableDatas = [{
+      field: '订单编号',
+      value: id,
+      key: 1,
+    }, {
+      field: '下单时间',
+      value: moment(gmtCreated).format('YYYY-MM-DD HH:mm:ss'),
+      key: 2,
+    }, {
+      field: '订单状态',
+      value: orderStatuses[orderStatus],
+      key: 3,
+    }, {
+      field: '拆分状态',
+      value: orderSplitStatus[splitStatus],
+      key: 4,
+    }, {
+      field: '拆分原因',
+      value: '订单中包含实体物品或物品分属不同商家',
+      key: 5,
+    }];
+    // 收货人信息 - table data
+    const consumerInfoTableData = [{
+      field: '购买终端编号',
+      value: userCode,
+      key: 1,
+    }, {
+      field: '购买终端名称',
+      value: `${classroomName}`,
+      key: 2,
+    }, {
+      field: '终端所属校区',
+      value: `${provCodeToName(provinceCode)}-${cityName}-${zoneName}`,
+      key: 3,
+    }, {
+      field: '终端所属渠道',
+      value: merchantName,
+      key: 4,
+    }, {
+      field: '收货人姓名',
+      value: name,
+      key: 5,
+    }, {
+      field: '收货人电话',
+      value: mobile,
+      key: 6,
+    }, {
+      field: '收货地址',
+      value: address,
+      key: 7,
+    }];
+    // 商品清单
     const goodsTableColumns = [{
       title: '商品编号',
       dataIndex: 'code',
-      key: 'code',
-    }, {
-      title: '商品类型',
-      dataIndex: 'type',
-      key: 'type',
+      key: 1,
+      width: '25%',
     }, {
       title: '商品名称',
       dataIndex: 'name',
-      key: 'name',
+      key: 2,
+      width: '25%',
     }, {
-      title: '领教定价',
-      dataIndex: 'lingjiaoPrice',
-      key: 'lingjiaoPrice',
+      title: '商品类型',
+      dataIndex: 'type',
+      render: text => productType[text],
+      key: 3,
+      width: '15%',
     }, {
-      title: '渠道定价',
+      title: '商品售价(元)',
       dataIndex: 'merchantPrice',
-      key: 'merchantPrice',
-    }, {
-      title: '实际售价',
-      dataIndex: 'finalPrice',
-      key: 'finalPrice',
-    }, {
-      title: '数量',
-      dataIndex: 'quality',
-      key: 'quality',
+      key: 4,
+      width: '20%',
     }, {
-      title: '单位',
-      dataIndex: 'chargeUnit',
-      key: 'chargeUnit',
-    }, {
-      title: '起始时间',
-      dataIndex: 'timeBegin',
-      key: 'timeBegin',
+      title: '商品数量',
+      dataIndex: 'quantity',
+      key: 5,
+      width: '15%',
+    }];
+    // 结算信息 - table data
+    const strs = [];
+    (goods || []).forEach(item => strs.push(`${item.merchantPrice}(元) * ${item.quantity}`));
+    const accountTableDatas = [{
+      field: '商品总价',
+      value: `${strs.join(' + ')} = ${originPrice}(元)`,
+      key: 1,
     }, {
-      title: '结束时间',
-      dataIndex: 'timeEnd',
-      key: 'timeEnd',
+      field: '支付金额',
+      value: (
+        <span>
+          {`总价:${originPrice}(元) - 商品优惠:${adjustPrice}(元) = 订单总金额:`}
+          <strong style={{ color: 'red' }}>{`${finalPrice}(元)`}</strong>
+        </span>),
+      key: 2,
     }];
 
     return (
       <PageHeaderLayout title="订单详情">
-        <Card bordered={false}>
-          <DescriptionList size="large" title="订单信息" col={2} style={{ marginBottom: 32 }}>
-            <Description term="订单编号">{code}</Description>
-            <Description term="创建时间">{moment(gmtCreated).format('YYYY-MM-DD HH:mm:ss')}</Description>
-            <Description term="终端编号">{userCode}</Description>
-            <Description term="终端名称">{`${provinceCode}-${cityName}-${zoneName}-${classroomName}`}</Description>
-            <Description term="支付状态">{orderStatuses[status]}</Description>
-          </DescriptionList>
-          <Divider style={{ marginBottom: 32 }} />
-          <DescriptionList size="large" title="收货信息" col={2} style={{ marginBottom: 32 }}>
-            <Description term="收货人">付小小</Description>
-            <Description term="联系电话">18100000000</Description>
-            <Description term="收货地址">浙江省杭州市西湖区万塘路18号</Description>
-            <Description term="备注">无</Description>
-          </DescriptionList>
-          <Divider style={{ marginBottom: 32 }} />
-          <div className={styles.title}>商品清单</div>
+        <Card title="订单信息" bordered={false} style={{ marginBottom: 20 }}>
           <Table
-            dataSource={[]}
+            size="small"
+            columns={simplyTableColumns}
+            dataSource={orderInfoTableDatas}
+            bordered
+            pagination={false}
+            showHeader={false}
+          />
+        </Card>
+        <Card title="商品清单" bordered={false} style={{ marginBottom: 20 }}>
+          <Table
+            size="small"
+            rowKey={record => record.id}
             columns={goodsTableColumns}
+            dataSource={goods || []}
+            bordered
+            pagination={false}
+          />
+        </Card>
+        <Card title="收货人信息" bordered={false} style={{ marginBottom: 20 }}>
+          <Table
+            size="small"
+            columns={simplyTableColumns}
+            dataSource={consumerInfoTableData}
+            bordered
+            pagination={false}
+            showHeader={false}
+          />
+        </Card>
+        <Card title="订单备注" bordered={false} style={{ marginBottom: 20 }}>
+          {note || '无'}
+        </Card>
+        <Card title="结算信息" bordered={false}>
+          <Table
+            size="small"
+            columns={simplyTableColumns}
+            dataSource={accountTableDatas}
+            bordered
+            pagination={false}
+            showHeader={false}
           />
         </Card>
+        {this.renderFooter(orderStatus)}
       </PageHeaderLayout>
     );
   }

+ 0 - 8
src/routes/Order/Edit/orderProfile.less

@@ -1,8 +0,0 @@
-@import "~antd/lib/style/themes/default.less";
-
-.title {
-  color: @heading-color;
-  font-size: 16px;
-  font-weight: 500;
-  margin-bottom: 16px;
-}

+ 26 - 59
src/routes/Order/List/index.js

@@ -1,5 +1,4 @@
 import React, { PureComponent } from 'react';
-import PropTypes from 'prop-types';
 import queryString from 'query-string';
 import { connect } from 'dva';
 import { routerRedux } from 'dva/router';
@@ -7,28 +6,23 @@ import { Card } from 'antd';
 import TableList from './table';
 import Search from './search';
 import PageHeaderLayout from '../../../layouts/PageHeaderLayout';
-import { Codes } from '../../../utils/config';
 
-@connect(state => ({
-  order: state.order,
-}))
+@connect(state => ({ order: state.order }))
 export default class Order extends PureComponent {
-  static propTypes = {
-    order: PropTypes.object,
-    location: PropTypes.object,
-    dispatch: PropTypes.func,
-  };
-
   render() {
     const { location, dispatch, order } = 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 } = order;
+    const {
+      list,
+      listLoading,
+      pagination,
+    } = order;
 
     // 把携带的参数中空值项删除
-    Object.keys(filters).map((key) => { filters[key] ? null : delete filters[key]; });
+    Object.keys(filters).map(key => (filters[key] ? null : delete filters.key));
     // 如果搜索内容不为空则添加进filters中
     if (field && keyword) {
       filters.field = field;
@@ -67,8 +61,8 @@ export default class Order extends PureComponent {
       loading: listLoading,
       timeBegin: filters.timeBegin,
       timeEnd: filters.timeEnd,
-      curStatus: filters.status,
-      onChange: (pagination, filterArgs) => {
+      curStatus: filters.orderStatus,
+      onChange: (page, filterArgs) => {
         const getValue = obj => Object.keys(obj).map(key => obj[key]).join(',');
         const tableFilters = Object.keys(filterArgs).reduce((obj, key) => {
           const newObj = { ...obj };
@@ -82,54 +76,27 @@ export default class Order extends PureComponent {
           pathname,
           search: queryString.stringify({
             ...data,
-            pageNo: pagination.current,
-            pageSize: pagination.pageSize,
+            pageNo: page.current,
+            pageSize: page.pageSize,
           }),
         }));
       },
       onViewItem: (item) => {
-        dispatch(
-          routerRedux.push({
-            pathname: `/order/profile/${item.id}`,
-            state: filters,
-          })
-        );
-      },
-      onEditItem: (item) => {
-        dispatch(
-          routerRedux.push({
-            pathname: `/order/edit/${item.id}`,
-            state: filters,
-          })
-        );
-      },
-      onDeleteItem: (id) => {
-        dispatch({
-          type: 'order/delete',
-          payload: id,
-          callback: () => {
-            dispatch(
-              routerRedux.push({
-                pathname,
-                search: queryString.stringify(filters),
-              })
-            );
-          },
-        });
-      },
-      onRecoverItem: (payload) => {
-        dispatch({
-          type: 'order/recover',
-          payload,
-          callback: () => {
-            dispatch(
-              routerRedux.push({
-                pathname,
-                search: queryString.stringify(filters),
-              })
-            );
-          },
-        });
+        if (item.flag) {
+          dispatch(
+            routerRedux.push({
+              pathname: `/order/sub/profile/${item.id}`,
+              state: filters,
+            })
+          );
+        } else {
+          dispatch(
+            routerRedux.push({
+              pathname: `/order/profile/${item.id}`,
+              state: filters,
+            })
+          );
+        }
       },
     };
 

+ 2 - 16
src/routes/Order/List/search.js

@@ -1,18 +1,8 @@
-import react, { PureComponent } from 'react';
-import PropTypes from 'prop-types';
-import { Button, Form, Row, Col, Icon } from 'antd';
+import React, { PureComponent } from 'react';
+import { Button, Row, Col, Icon } from 'antd';
 import DataSearch from '../../../components/DataSearch';
 
-@Form.create()
 export default class Search extends PureComponent {
-  static propTypes = {
-    form: PropTypes.object.isRequired,
-    onSearch: PropTypes.func,
-    onAdd: PropTypes.func,
-    field: PropTypes.string,
-    keyword: PropTypes.string,
-  };
-
   render() {
     const { field, keyword, onSearch, onAdd } = this.props;
 
@@ -22,10 +12,6 @@ export default class Search extends PureComponent {
       size: 'default',
       select: true,
       selectOptions: [{
-        value: 'campusName', name: '校区名称', mode: 'input',
-      }, {
-        value: 'campusCode', name: '校区编号', mode: 'input',
-      }, {
         value: 'userCode', name: '终端编号', mode: 'input',
       }],
       selectProps: {

+ 53 - 159
src/routes/Order/List/table.js

@@ -1,127 +1,40 @@
 import React, { PureComponent } from 'react';
-import PropTypes from 'prop-types';
 import moment from 'moment';
-import classnames from 'classnames';
-import queryString from 'query-string';
-import { Divider, Modal, DatePicker, Table, Menu, Icon, Badge, Button } from 'antd';
-import AnimTableBody from '../../../components/Animation/AnimTableBody';
-import styles from './table.less';
+import { Table, Badge } from 'antd';
 import { orderStatuses, Codes } from '../../../utils/config';
-
-const { RangePicker } = DatePicker;
+import { provCodeToName } from '../../../utils/city';
 
 export default class TableList extends PureComponent {
-  state = {
-    filtered: false, // 是否处于过滤状态
-    timeBegin: null, // 起始时间 - 默认当前时间戳
-    timeEnd: null, // 结束时间 - 默认时间戳
-  }
-
-  componentWillReceiveProps(nextProps) {
-    // 如果父组件传进的属性中包含timeBegin和timeEnd并进行合法性校验,通过则更新state
-    const { timeBegin, timeEnd } = nextProps;
-    const nextTimeBegin = Number(timeBegin);
-    const nextTimeEnd = Number(timeEnd);
-    if (nextTimeBegin && nextTimeEnd && moment(nextTimeBegin).isValid() && moment(nextTimeEnd).isValid()) {
-      this.setState({
-        timeBegin: moment(nextTimeBegin),
-        timeEnd: moment(nextTimeEnd),
-        filtered: true,
-      });
-    }
-  }
-
-  // 选择了筛选的时间段,点击确定后触发
-  handleRangePickerOnOk = (value) => {
-    const timeBegin = value[0];
-    const timeEnd = value[1];
-    this.setState({ timeBegin, timeEnd, filtered: true });
-  }
-
-  // 点击清除选中的时间段
-  handleRangePickerOnChange = (value) => {
-    if (value && !value.length) {
-      this.setState({
-        filtered: false,
-        timeBegin: null,
-        timeEnd: null,
-      });
-    } else {
-      this.setState({
-        filtered: true,
-        timeBegin: value[0],
-        timeEnd: value[1],
-      });
-    }
-  }
-
-  // 点击空白区域,隐藏RangePicker,会触发一次查询
-  handleRangePickerFilter = (visible) => {
-    if (!visible) {
-      const { pagination, onChange } = this.props;
-      // 这里构造成数组类型是为了和table自带的过滤参数类型一致
-      const data = { timeBegin: [], timeEnd: [] };
-      if (this.state.timeBegin && this.state.timeEnd) {
-        data.timeBegin = [this.state.timeBegin.format('X')];
-        data.timeEnd = [this.state.timeEnd.format('X')];
-      }
-      onChange(pagination, data);
-    }
-  }
-
-  handleMenuClick = (record, e) => {
-    const { onDeleteItem, onViewItem, onEditItem, onRecoverItem } = this.props;
-    if (e.key === '1') {
-      onViewItem(record);
-    } else if (e.key === '2') {
-      console.log('enter into edit page...');
-    } else if (e.key === '3') {
-      Modal.confirm({
-        title: '你确定要作废该订单?',
-        onOk() {
-          console.log('deleting...');
-          // onDeleteItem(record.id)
-        },
-      });
-    }
-  }
-
-  // 根据订单状态确定下拉菜单的选项
-  renderOperationButton = (record) => {
-    switch (record.status) {
-      case Codes.CODE_PAID:
-        return (
-          <div>
-            <Button type="primary" size="small">查看</Button>
-          </div>
-        );
-        break;
-      case Codes.CODE_UNPAID:
-        return (
-          <div>
-            <a>编辑</a>
-            <Divider type="vertical" />
-            <a>支付</a>
-          </div>
-        );
-        break;
-      case Codes.CODE_CANCEL:
-        return (
-          <div>
-            <a>查看</a>
-          </div>
-        );
-        break;
-      default:
-        break;
-    }
+  handleItemView = (record) => {
+    const { onViewItem } = this.props;
+    onViewItem(record);
   }
 
   render() {
-    const { curStatus, onDeleteItem, onRecoverItem, onEditItem, location, pagination, ...tableProps } = this.props;
-    const { timeBegin, timeEnd, filtered } = this.state;
+    const { dataSource, pagination, ...tableProps } = this.props;
+    const newDataSource = [...dataSource];
+    newDataSource.forEach((item) => {
+      if (Array.isArray(item.detailList) && item.detailList.length) {
+        item.children = item.detailList.map(one => (
+          {
+            ...one,
+            flag: true,
+            userCode: item.userCode,
+            provinceCode: item.provinceCode,
+            cityName: item.cityName,
+            zoneName: item.zoneName,
+          })
+        );
+      }
+    });
 
     const columns = [{
+      title: '订单编号',
+      dataIndex: 'id',
+      key: 'id',
+      fixed: 'left',
+      width: 330,
+    }, {
       title: '终端编号',
       dataIndex: 'userCode',
       key: 'userCode',
@@ -130,83 +43,64 @@ export default class TableList extends PureComponent {
       dataIndex: 'campusName',
       key: 'campusName',
       render: (text, record) => (
-        <span>{`${record.provinceCode}-${record.cityName}-${record.zoneName}`}</span>
+        <span>{`${provCodeToName(record.provinceCode)}-${record.cityName}-${record.zoneName}`}</span>
       ),
     }, {
-      title: '商品标价(¥)',
+      title: '商品总价(元)',
       dataIndex: 'originPrice',
       key: 'originPrice',
     }, {
-      title: '实际售价(¥)',
-      dataIndex: 'finalPrice',
-      key: 'finalPrice',
-    }, {
-      title: '价格调整(¥)',
+      title: '优惠价格(元)',
       dataIndex: 'adjustPrice',
       key: 'adjustPrice',
     }, {
+      title: '实际售价(元)',
+      dataIndex: 'finalPrice',
+      key: 'finalPrice',
+    }, {
       title: '状态',
-      dataIndex: 'status',
-      key: 'status',
+      dataIndex: 'orderStatus',
+      key: 'orderStatus',
       render: (text, record) => {
-        const statusMap = { [Codes.CODE_PAID]: 'success', [Codes.CODE_UNPAID]: 'processing', [Codes.CODE_CANCEL]: 'error' };
-        return (<Badge status={statusMap[record.status]} text={orderStatuses[record.status]} />);
+        const statusMap = {
+          [Codes.CODE_UNPAID]: 'default',
+          [Codes.CODE_COMPLETE]: 'success',
+          [Codes.CODE_CANCEL]: 'error',
+        };
+        return (<Badge status={statusMap[record.orderStatus] || 'processing'} text={orderStatuses[record.orderStatus]} />);
       },
-      filters: Object.keys(orderStatuses).map(key => ({ text: orderStatuses[key], value: key })),
-      filterMultiple: false,
-      filteredValue: [curStatus],
     }, {
       title: '下单时间',
       dataIndex: 'gmtCreated',
       key: 'gmtCreated',
-      render: (text, record) => (
+      render: text => (
         <div>{moment(text).format('YYYY-MM-DD HH:mm:ss')}</div>
       ),
-      filterIcon: <Icon type="clock-circle-o" style={{ color: filtered ? '#108ee9' : '#aaa' }} />,
-      filterDropdown: (
-        <div className="custom-filter-dropdown">
-          <RangePicker
-            value={filtered ? [timeBegin, timeEnd] : []}
-            showTime={{ format: 'HH:mm:ss' }}
-            format="YYYY-MM-DD HH:mm:ss"
-            placeholder={['起始时间', '截止时间']}
-            onChange={this.handleRangePickerOnChange}
-            onOk={this.handleRangePickerOnOk}
-            getCalendarContainer={() => document.querySelector('.custom-filter-dropdown')}
-          />
-        </div>
-      ),
-      onFilterDropdownVisibleChange: this.handleRangePickerFilter,
     }, {
       title: '操作',
       dataIndex: 'operation',
       key: 'operation',
-      render: (text, record) => this.renderOperationButton(record),
+      render: (text, record) => (
+        record.flag ?
+          <a onClick={() => this.handleItemView(record)}>查看子订单</a> :
+          <a onClick={() => this.handleItemView(record)}>查看母订单</a>
+      ),
+      width: 100,
+      fixed: 'right',
     }];
 
-    // 数据table列表表头的筛选按钮点击重置后status值为空,此时删除该参数
-    columns.map((item) => {
-      item.dataIndex === 'status' && !curStatus ? delete item.filteredValue : null;
-    });
-
     // 配置分页
     tableProps.pagination = !!pagination && { ...pagination, showSizeChanger: true, showQuickJumper: true, showTotal: total => `共 ${total} 条` };
 
-    // 添加动画
-    const AnimationTableBody = props => (<AnimTableBody {...props} />);
-
     return (
       <Table
         simple
         bordered
-        {...tableProps}
+        dataSource={newDataSource}
         columns={columns}
-        className={classnames({ [styles.table]: true, [styles.motion]: true })}
-        scroll={{ x: 1200 }}
         rowKey={record => record.id}
-        components={{
-          body: { wrapper: AnimationTableBody },
-        }}
+        {...tableProps}
+        scroll={{ x: 1500 }}
       />
     );
   }

+ 0 - 82
src/routes/Order/List/table.less

@@ -1,82 +0,0 @@
-@import "~antd/lib/style/themes/default.less";
-@import "../../../utils/utils.less";
-
-.table {
-  :global {
-    .ant-table-tbody > tr > td,
-    .ant-table-thead > tr > th {
-      height: 50px;
-    }
-  }
-
-  &.motion {
-    :global {
-      .ant-table-tbody > tr > td,
-      .ant-table-thead > tr > th {
-        &:nth-child(1) {
-          width: 16%;
-        }
-
-        &:nth-child(2) {
-          width: 18%;
-        }
-
-        &:nth-child(3) {
-          width: 10%;
-        }
-
-        &:nth-child(4) {
-          width: 10%;
-        }
-
-        &:nth-child(5) {
-          width: 10%;
-        }
-
-        &:nth-child(6) {
-          width: 10%;
-        }
-
-        &:nth-child(7) {
-          width: 18%;
-        }
-
-        &:nth-child(8) {
-          width: 8%;
-        }
-      }
-
-      .ant-table-thead {
-        & > tr {
-          transition: none;
-          display: block;
-
-          & > th {
-            display: inline-flex;
-            align-items: center;
-            justify-content: center;
-          }
-        }
-      }
-
-      .ant-table-tbody {
-        & > tr {
-          transition: none;
-          display: block;
-          border-bottom: 1px solid #f5f5f5;
-
-          & > td {
-            border-bottom: none;
-            display: inline-flex;
-            align-items: center;
-            justify-content: center;
-          }
-
-          &.ant-table-expanded-row-level-1 > td {
-            height: auto;
-          }
-        }
-      }
-    }
-  }
-}

+ 100 - 119
src/routes/SoldProduct/index.js

@@ -1,48 +1,70 @@
 import React, { Component } from 'react';
-import { Table, Card, Form, Row, Col, Input, Select, DatePicker } from 'antd';
+import { connect } from 'dva';
+import {
+  Row,
+  Col,
+  Table,
+  Form,
+  Card,
+  Input,
+  Button,
+} from 'antd';
 import moment from 'moment';
 import { productType } from '../../utils/config';
 import styles from './index.less';
 
 @Form.create()
+@connect(state => ({ snapshot: state.snapshot }))
 export default class SoldProductList extends Component {
-  render() {
-    // 模拟数据
-    const mockData = [];
-    for (let i = 1; i < 2000; i++) {
-      mockData.push({
-        id: i,
-        productCode: 'C-001',
-        productName: '小学语文二年级上册',
-        productType: 'COURSE',
-        userCode: '1500024398001',
-        userName: '教室六',
-        campusCode: '1500024398',
-        campusName: '湖南省-长沙市天水区-育新小学',
-        merchantName: '贝尔安亲',
-        cpPrice: 1000.85,
-        merchantPrice: 20000.890,
-        terminalPrice: 21000,
-        chargeUnit: '年',
-        quantity: 1,
-        gmtCreated: 1590000134,
-      });
+  getFormValue = () => {
+    const { form } = this.props;
+    const { getFieldsValue } = form;
+    const filters = getFieldsValue();
+    if (filters && filters.time) {
+      filters.timeBegin = filters.time[0].format('X');
+      filters.timeEnd = filters.time[1].format('X');
+      delete filters.time;
+    }
+    if (filters.orderStatus === 'all') {
+      delete filters.orderStatus;
     }
+    return filters;
+  }
+
+  handleFilterSubmit = (e) => {
+    e.preventDefault();
+    const filters = this.getFormValue();
+    this.props.dispatch({ type: 'snapshot/query', payload: filters });
+  }
+
+  handleFilterReset = () => {
+    this.props.form.resetFields();
+    this.props.dispatch({ type: 'snapshot/query', payload: {} });
+  }
+
+  handleTableChange = (pagination) => {
+    const filters = { ...this.getFormValue(), ...pagination };
+    this.props.dispatch({ type: 'snapshot/query', payload: filters });
+  }
+
+  render() {
+    const { snapshot, form } = this.props;
+    const { getFieldDecorator } = form;
+    const { list, listLoading, pagination } = snapshot;
 
-    // 表格-列对象
     const columns = [{
       title: '产品编号',
       dataIndex: 'productCode',
-      key: 1, // 设置了dataIndex可忽略这个属性
+      key: 1,
     }, {
       title: '产品名称',
       dataIndex: 'productName',
       key: 2,
     }, {
-      title: '产品类型',
+      title: '类型',
       dataIndex: 'productType',
       key: 3,
-      render: (text, record) => productType[text],
+      render: text => productType[text],
     }, {
       title: '终端编号',
       dataIndex: 'userCode',
@@ -60,117 +82,76 @@ export default class SoldProductList extends Component {
       dataIndex: 'campusName',
       key: 7,
     }, {
-      title: '渠道名称',
-      dataIndex: 'merchantName',
-      key: 8,
-    }, {
-      title: '供应商价格(¥)',
-      dataIndex: 'cpPrice',
-      key: 9,
-    }, {
       title: '渠道价格(¥)',
       dataIndex: 'merchantPrice',
-      key: 10,
+      key: 8,
     }, {
       title: '终端价格(¥)',
       dataIndex: 'terminalPrice',
-      key: 11,
+      key: 9,
     }, {
       title: '数量',
       dataIndex: 'quantity',
-      key: 12,
+      key: 10,
     }, {
-      title: '计价单位',
+      title: '单位',
       dataIndex: 'chargeUnit',
-      key: 13,
+      key: 11,
     }, {
       title: '创建时间',
       dataIndex: 'gmtCreated',
-      key: 14,
-      render: (text, record) => moment(text).format('YYYY-MM-DD HH:mm:ss'),
+      key: 12,
+      render: text => moment(text).format('YYYY-MM-DD HH:mm:ss'),
     }];
 
-    const { getFieldDecorator } = this.props.form;
+    const newPagination = !!pagination && { ...pagination, showSizeChanger: true, showQuickJumper: true, showTotal: total => `共 ${total} 条` };
 
     return (
       <Card>
-        <div className={styles.tableList}>
-          <div className={styles.tableListForm}>
-            <Form layout="inline">
-              <Row gutter={{ md: 8, lg: 24, xl: 48 }}>
-                <Col md={8} sm={24}>
-                  <Form.Item label="渠道">
-                    {getFieldDecorator('merchantId')(
-                      <Input placeholder="请选择" />
-                    )}
-                  </Form.Item>
-                </Col>
-                <Col md={8} sm={24}>
-                  <Form.Item label="校区">
-                    {getFieldDecorator('campusId')(
-                      <Input placeholder="请选择" />
-                    )}
-                  </Form.Item>
-                </Col>
-                <Col md={8} sm={24}>
-                  <Form.Item label="终端">
-                    {getFieldDecorator('uid')(
-                      <Input placeholder="请选择" />
-                    )}
-                  </Form.Item>
-                </Col>
-              </Row>
-              <Row gutter={{ md: 8, lg: 24, xl: 48 }}>
-                <Col md={8} sm={24}>
-                  <Form.Item label="产品类型">
-                    {getFieldDecorator('productType')(
-                      <Input placeholder="请选择" />
-                    )}
-                  </Form.Item>
-                </Col>
-                <Col md={8} sm={24}>
-                  <Form.Item label="产品名称">
-                    {getFieldDecorator('productName')(
-                      <Input placeholder="请输入" />
-                    )}
-                  </Form.Item>
-                </Col>
-                <Col md={8} sm={24}>
-                  <Form.Item label="产品编号">
-                    {getFieldDecorator('productCode')(
-                      <Input placeholder="请输入" />
-                    )}
-                  </Form.Item>
-                </Col>
-              </Row>
-              <Row gutter={{ md: 8, lg: 24, xl: 48 }}>
-                <Col md={8} sm={24}>
-                  <Form.Item label="时间范围">
-                    {getFieldDecorator('time')(
-                      <DatePicker />
-                    )}
-                  </Form.Item>
-                </Col>
-                <Col md={8} sm={24}>
-                  <Form.Item label="至">
-                    {getFieldDecorator('time')(
-                      <DatePicker />
-                    )}
-                  </Form.Item>
-                </Col>
-              </Row>
-            </Form>
-          </div>
-          <div>
-            <Table
-              bordered
-              rowKey={record => record.id}
-              columns={columns}
-              dataSource={mockData}
-              scroll={{ x: 1800 }}
-            />
-          </div>
-        </div>
+        <Form layout="vertical" className={styles.tableSearchForm} onSubmit={this.handleFilterSubmit}>
+          <Row gutter={24}>
+            <Col span={6}>
+              <Form.Item label="校区名称">
+                {getFieldDecorator('campusName', {
+                })(<Input placeholder="请输入" />)}
+              </Form.Item>
+            </Col>
+            <Col span={6}>
+              <Form.Item label="校区编号">
+                {getFieldDecorator('campusCode', {
+                })(<Input placeholder="请输入" />)}
+              </Form.Item>
+            </Col>
+            <Col span={6}>
+              <Form.Item label="产品编号">
+                {getFieldDecorator('productCode', {
+                })(<Input placeholder="请输入" />)}
+              </Form.Item>
+            </Col>
+            <Col span={6}>
+              <Form.Item label="终端编号">
+                {getFieldDecorator('userCode', {
+                })(<Input placeholder="请输入" />)}
+              </Form.Item>
+            </Col>
+          </Row>
+          <Row>
+            <Col span={24} style={{ textAlign: 'left' }}>
+              <Button type="primary" htmlType="submit">搜索</Button>
+              <Button onClick={this.handleFilterReset} style={{ marginLeft: 8 }}>重置</Button>
+            </Col>
+          </Row>
+        </Form>
+        <Table
+          bordered
+          loading={listLoading}
+          onChange={this.handleTableChange}
+          rowKey={record => record.id}
+          columns={columns}
+          dataSource={list}
+          pagination={newPagination}
+          scroll={{ x: 1800 }}
+        />
       </Card>
     );
   }

+ 10 - 40
src/routes/SoldProduct/index.less

@@ -1,48 +1,18 @@
 @import "~antd/lib/style/themes/default.less";
 @import "../../utils/utils.less";
 
-.tableList {
-  .tableListOperator {
-    margin-bottom: 16px;
-    button {
-      margin-right: 8px;
-    }
-  }
-}
+.tableSearchForm {
+  padding: 24px;
+  background: #fbfbfb;
+  border: 1px solid #d9d9d9;
+  border-radius: 6px;
+  margin-bottom: 20px;
 
-.tableListForm {
-  :global {
-    .ant-form-item {
-      margin-bottom: 24px;
-      margin-right: 0;
-      display: flex;
-      > .ant-form-item-label {
-        width: auto;
-        line-height: 32px;
-        padding-right: 8px;
-      }
-      .ant-form-item-control {
-        line-height: 32px;
-      }
-    }
-    .ant-form-item-control-wrapper {
-      flex: 1;
-    }
+  .ant-form-item {
+    display: flex;
   }
-  .submitButtons {
-    white-space: nowrap;
-    margin-bottom: 24px;
-  }
-}
-
-@media screen and (max-width: @screen-lg) {
-  .tableListForm :global(.ant-form-item) {
-    margin-right: 24px;
-  }
-}
 
-@media screen and (max-width: @screen-md) {
-  .tableListForm :global(.ant-form-item) {
-    margin-right: 8px;
+  .ant-form-item-label {
+    overflow: visible;
   }
 }

+ 5 - 9
src/routes/Terminal/Edit/campus.js

@@ -1,7 +1,5 @@
 import React, { PureComponent } from 'react';
-import PropTypes from 'prop-types';
 import SelectModal from '../../../components/SelectModal';
-import { Codes } from '../../../utils/config';
 
 export default class CampusSelectModal extends PureComponent {
   render() {
@@ -32,11 +30,9 @@ export default class CampusSelectModal extends PureComponent {
         value: 'code', name: '校区编号', mode: 'input',
       }],
       searchSelectProps: {
-        defaultValue: 'name',
-      },
-      onSearch: (value) => {
-        onSearch(value);
+        defaultValue: 'code',
       },
+      onSearch: value => onSearch(value),
     };
 
     const fsTableProps = {
@@ -44,17 +40,17 @@ export default class CampusSelectModal extends PureComponent {
         title: '校区编号',
         dataIndex: 'code',
         key: 'code',
-        width: '30%',
+        width: '20%',
       }, {
         title: '校区名称',
         dataIndex: 'name',
         key: 'name',
-        width: '35%',
+        width: '53%',
       }, {
         title: '渠道名称',
         dataIndex: 'merchantName',
         key: 'merchantName',
-        width: '20%',
+        width: '15%',
       }],
       ...fsTableOpts,
     };

+ 28 - 16
src/routes/Terminal/Edit/index.js

@@ -1,12 +1,11 @@
 import React, { Component } from 'react';
-import { Card, Form, Input, Select, Switch, Tooltip, Button, message } from 'antd';
+import { Card, Form, List, Input, Switch, Tooltip, Button, message } from 'antd';
 import { routerRedux } from 'dva/router';
 import { connect } from 'dva';
 import queryString from 'query-string';
 import PageHeaderLayout from '../../../layouts/PageHeaderLayout';
 import CampusSelectModal from './campus';
 import { Codes, pageSize } from '../../../utils/config';
-import { getLocalUser } from '../../../utils/helper';
 
 @Form.create()
 @connect(state => ({
@@ -15,12 +14,10 @@ import { getLocalUser } from '../../../utils/helper';
 }))
 export default class TerminalProfile extends Component {
   handleCampusSelectClick = () => {
-    const { merchantId } = getLocalUser();
     this.props.dispatch({ type: 'terminalDetail/showModal' });
-    // 获取该渠道下的所属校区
     this.props.dispatch({
       type: 'campus/query',
-      payload: { pageNo: 1, pageSize, merchantId },
+      payload: { pageNo: 1, pageSize },
     });
   }
 
@@ -91,7 +88,7 @@ export default class TerminalProfile extends Component {
   render() {
     const { form, terminalDetail, campus } = this.props;
     const { currentItem, modalShow, operType } = terminalDetail;
-    const { name, password, status, campusId, campusName, campusCode } = currentItem;
+    const { name, password, status, campusId, campusName, merchantName } = currentItem;
     const { getFieldDecorator } = form;
 
     const formItemLayout = {
@@ -122,27 +119,42 @@ export default class TerminalProfile extends Component {
               <Tooltip placement="top" title="点击选择校区">
                 <Button disabled={operType === 'update'} style={{ marginRight: 20 }} type="primary" size="small" icon="select" onClick={this.handleCampusSelectClick}>选择</Button>
               </Tooltip>
-              {campusName && <strong>{`${campusName}`}</strong>}
+              {operType === 'update' ?
+                (
+                  <List
+                    size="small"
+                    bordered
+                    dataSource={[
+                    `所属校区: ${campusName}`,
+                    `所属渠道: ${merchantName}`,
+                  ]}
+                    renderItem={item => <List.Item>{item}</List.Item>}
+                  />
+                ) : (
+                  campusId ? <List size="small" bordered dataSource={[`${campusName}`]} renderItem={item => <List.Item>{item}</List.Item>} /> : null)
+              }
             </Form.Item>
             <Form.Item label="终端名称:" hasFeedback {...formItemLayout}>
               {getFieldDecorator('name', {
                 initialValue: name,
               })(<Input placeholder="请输入(例: 教室三/教室四...)" />)}
             </Form.Item>
-            {operType === 'create' ? (
-              <Form.Item label="终端密码:" hasFeedback {...formItemLayout}>
-                {getFieldDecorator('password', {
+            {operType === 'create' ?
+              (
+                <Form.Item label="终端密码:" hasFeedback {...formItemLayout}>
+                  {getFieldDecorator('password', {
                   rules: [{ required: true, type: 'string', message: '密码为必填项!' }],
                   initialValue: password,
                 })(<Input placeholder="请输入" />)}
-              </Form.Item>
-) : (
-  <Form.Item label="终端密码:" hasFeedback {...formItemLayout}>
-    {getFieldDecorator('password', {
+                </Form.Item>
+              ) : (
+                <Form.Item label="终端密码:" hasFeedback {...formItemLayout}>
+                  {getFieldDecorator('password', {
                   initialValue: password,
                 })(<Input placeholder="修改密码时填写" />)}
-  </Form.Item>
-)}
+                </Form.Item>
+              )
+            }
             <Form.Item label="账号状态:" {...formItemLayout}>
               {getFieldDecorator('status', {
                 valuePropsName: 'checked',

+ 0 - 7
src/routes/Terminal/List/index.js

@@ -1,5 +1,4 @@
 import React, { Component } from 'react';
-import PropTypes from 'prop-types';
 import queryString from 'query-string';
 import { connect } from 'dva';
 import { routerRedux } from 'dva/router';
@@ -10,12 +9,6 @@ import PageHeaderLayout from '../../../layouts/PageHeaderLayout';
 
 @connect(state => ({ terminal: state.terminal }))
 export default class TerminalList extends Component {
-  static propTypes = {
-    Terminal: PropTypes.object,
-    location: PropTypes.object,
-    dispatch: PropTypes.func,
-  };
-
   render() {
     const { dispatch, location, terminal } = this.props;
 

+ 9 - 17
src/routes/Terminal/List/table.js

@@ -1,11 +1,6 @@
 import React, { Component } from 'react';
-import PropTypes from 'prop-types';
 import moment from 'moment';
-import classnames from 'classnames';
-import queryString from 'query-string';
 import { Divider, Table, Modal, Badge } from 'antd';
-import AnimTableBody from '../../../components/Animation/AnimTableBody';
-import styles from './table.less';
 import { Codes, terminalStatuses } from '../../../utils/config';
 
 export default class TerminalTableList extends Component {
@@ -24,28 +19,29 @@ export default class TerminalTableList extends Component {
   }
 
   render() {
-    const { onDeleteItem, onEditItem, curStatus, location, pagination, ...tableProps } = this.props;
-
-    // 从url中提取查询参数
-    location.query = queryString.parse(location.search);
+    const { onDeleteItem, onEditItem, curStatus, pagination, ...tableProps } = this.props;
 
     const columns = [{
       title: '终端编号',
       dataIndex: 'code',
       key: 'code',
+      width: '14%',
     }, {
       title: '终端名称',
       dataIndex: 'name',
       key: 'name',
+      width: '11%',
     }, {
       title: '校区',
       dataIndex: 'campusName',
       key: 'campusName',
+      width: '30%',
     }, {
       title: '渠道名称',
       dataIndex: 'merchantId',
       key: 'merchantId',
       render: (text, record) => record.merchantName,
+      width: '10%',
     }, {
       title: '状态',
       dataIndex: 'status',
@@ -57,13 +53,15 @@ export default class TerminalTableList extends Component {
       filters: Object.keys(terminalStatuses).map(key => ({ text: terminalStatuses[key], value: key })),
       filterMultiple: false,
       filteredValue: [curStatus],
+      width: '8%',
     }, {
       title: '修改时间',
       dataIndex: 'gmtModified',
       key: 'gmtModified',
-      render: (text, record) => (
+      render: text => (
         <div>{moment(text).format('YYYY-MM-DD HH:mm:ss')}</div>
       ),
+      width: '17%',
     }, {
       title: '操作',
       dataIndex: 'operation',
@@ -75,6 +73,7 @@ export default class TerminalTableList extends Component {
           <a onClick={() => this.handleOperateItem(record)}>{record.status === Codes.CODE_NORMAL ? '禁用' : '解禁'}</a>
         </div>
       ),
+      width: '10%',
     }];
 
     columns.map((item) => {
@@ -84,20 +83,13 @@ export default class TerminalTableList extends Component {
     // 配置分页
     tableProps.pagination = !!pagination && { ...pagination, showSizeChanger: true, showQuickJumper: true, showTotal: total => `共 ${total} 条` };
 
-    // 添加动画
-    const AnimationTableBody = props => (<AnimTableBody {...props} />);
-
     return (
       <Table
         simple
         bordered
         {...tableProps}
         columns={columns}
-        className={classnames({ [styles.table]: true, [styles.motion]: true })}
         rowKey={record => record.id}
-        components={{
-          body: { wrapper: AnimationTableBody },
-        }}
       />
     );
   }

+ 1 - 6
src/services/merchant.js

@@ -1,10 +1,5 @@
-import { stringify } from 'qs';
 import request from '../utils/request';
-import { merchants, merchant, recommend } from '../utils/api';
-
-export async function query(params) {
-  return request(`${merchants}?${stringify(params)}`);
-}
+import { merchant, recommend } from '../utils/api';
 
 export async function queryOne({ id }) {
   return request(`${merchant}/${id}`);

+ 28 - 5
src/services/order.js

@@ -1,13 +1,17 @@
 import { stringify } from 'qs';
 import request from '../utils/request';
-import { order } from '../utils/api';
+import { order, subOrder, orderPay, orderSend, orderReceive } from '../utils/api';
 
 export async function query(params) {
   return request(`${order}?${stringify(params)}`);
 }
 
 export async function queryOne({ id }) {
-  return request(`${order.replace('/:id', `/${id}`)}`);
+  return request(`${order}/${id}`);
+}
+
+export async function querySubOrder({ id }) {
+  return request(`${subOrder}/${id}`);
 }
 
 export async function create(params) {
@@ -15,7 +19,7 @@ export async function create(params) {
     method: 'POST',
     body: JSON.stringify(params),
   };
-  return request(`${order.replace('/:id', '')}`, options);
+  return request(`${order}`, options);
 }
 
 export async function update(params) {
@@ -23,10 +27,29 @@ export async function update(params) {
     method: 'PUT',
     body: JSON.stringify(params),
   };
-  return request(`${order.replace('/:id', '')}`, options);
+  return request(`${order}`, options);
 }
 
+// 未支付订单可取消
 export async function remove({ id }) {
   const options = { method: 'DELETE' };
-  return request(`${order.replace('/:id', `/${id}`)}`, options);
+  return request(`${order}/${id}`, options);
+}
+
+export async function confirmPay({ id }) {
+  const options = { method: 'POST' };
+  return request(`${orderPay}/${id}`, options);
+}
+
+export async function confirmSend({ id, ...rest }) {
+  const options = {
+    method: 'POST',
+    body: JSON.stringify(rest),
+  };
+  return request(`${orderSend}/${id}`, options);
+}
+
+export async function confirmReceive({ id }) {
+  const options = { method: 'POST' };
+  return request(`${orderReceive}/${id}`, options);
 }

+ 2 - 2
src/services/sales.js

@@ -1,7 +1,7 @@
 import { stringify } from 'qs';
 import request from '../utils/request';
-import { soldProduct } from '../utils/api';
+import { snapShot } from '../utils/api';
 
 export async function query(params) {
-  return request(`${soldProduct}?${stringify(params)}`);
+  return request(`${snapShot}?${stringify(params)}`);
 }

+ 3 - 0
src/theme.js

@@ -3,4 +3,7 @@ module.exports = {
   'layout-header-background': '#333744',
   'primary-color': '#00c1de',
   'card-actions-background': '#f5f8fa',
+  'table-header-bg': '#3eabe8',
+  'table-padding-vertical': '6px',
+  'table-padding-horizontal': '6px',
 };

+ 3 - 28
src/utils/api.js

@@ -1,9 +1,6 @@
 import config from './config';
 
 module.exports = {
-  resources: `${config.apiHost}/resource/list`,
-  resource: `${config.apiHost}/resource`,
-  signature: `${config.apiHost}/oss/signature`,
   cmsUsers: `${config.apiHost}/cms/user/list`,
   cmsUser: `${config.apiHost}/cms/user`,
   userLogin: `${config.apiHost}/login`,
@@ -12,34 +9,12 @@ module.exports = {
   campus: `${config.apiHost}/campus`,
   terminals: `${config.apiHost}/user/list`,
   terminal: `${config.apiHost}/user`,
-  merchants: `${config.apiHost}/merchant/list`,
   merchant: `${config.apiHost}/merchant`,
-  recommend: `${config.apiHost}/merchant/recommend`,
-  // 标签组及标签
-  groups: `${config.apiHost}/group/list`,
-  group: `${config.apiHost}/group`,
-  tags: `${config.apiHost}/tag/list`,
-  tag: `${config.apiHost}/tag`,
-  // 课件/课
-  wares: `${config.apiHost}/ware/list`,
-  ware: `${config.apiHost}/ware`,
-  lessons: `${config.apiHost}/lesson/list`,
-  lesson: `${config.apiHost}/lesson/:id`,
-  // 产品接口,包括:课程、周边、课程包
-  product: `${config.apiHost}/product`,
-  course: `${config.apiHost}/product/course`,
-  support: `${config.apiHost}/product/support`,
-  packageList: `${config.apiHost}/product/package`,
-  savePackage: `${config.apiHost}/package`,
-  // 渠道产品接口
   merchantProducts: `${config.apiHost}/merchant/product`,
   merchantProduct: `${config.apiHost}/merchant/product/detail`,
   merchantProductCreate: `${config.apiHost}/merchant/product/status`,
-  bundleTag: `${config.apiHost}/merchant/product/tags`,
-  // 商品接口
-  goods: `${config.apiHost}/goods`,
-  // 订单接口
   order: `${config.apiHost}/order`,
-  // 销售统计
-  soldProduct: `${config.apiHost}/soldProduct`,
+  subOrder: `${config.apiHost}/order/detail`,
+  orderPay: `${config.apiHost}/order/pay`,
+  snapShot: `${config.apiHost}/order/snapshot`,
 };

+ 40 - 14
src/utils/config.js

@@ -17,9 +17,23 @@ Codes.CODE_SUPPORT = 'SUPPORT';
 Codes.CODE_PACKAGE = 'PACKAGE';
 
 Codes.CODE_SALE = 'SALE';
-Codes.CODE_UNPAID = 'PAYWAIT';
+Codes.CODE_UNPAID = 'UNPAID';
 Codes.CODE_PAID = 'PAYOK';
-Codes.CODE_CANCEL = 'PAYCANCEL';
+Codes.CODE_CANCEL = 'CANCEL';
+Codes.CODE_REFUND = 'REFUND';
+Codes.CODE_COMPLETE = 'COMPLETE';
+Codes.CODE_FORSEND = 'FORSEND';
+Codes.CODE_SENT = 'SENT';
+Codes.CODE_RECEIVED = 'RECEIVED';
+
+// 订单的拆分状态
+Codes.CODE_ORDER_SPLIT_YES = 'YES';
+Codes.CODE_ORDER_SPLIT_NO = 'NO';
+Codes.CODE_ORDER_SPLIT_UN = 'UN';
+
+Codes.CODE_ENTITY = 'ENTITY';
+Codes.CODE_VIRTUAL = 'VIRTUAL';
+
 Codes.CODE_CP = 1010;
 Codes.CODE_LJ = 2010;
 Codes.CODE_PJ = 3010;
@@ -31,9 +45,10 @@ Codes.CODE_ITEM = '件';
 
 module.exports = {
   apiHost: 'http://tt-cms.api.ai160.com',
+  ossHost: 'http://efunimgs.oss-cn-beijing.aliyuncs.com',
   // apiHost: '/api',
   // 每页返回数据量
-  pageSize: 10,
+  pageSize: 20,
   // 标识码
   Codes,
   // 视频质量
@@ -43,8 +58,8 @@ module.exports = {
   },
   // 状态类型
   statuses: {
-    [Codes.CODE_NORMAL]: '使用中',
-    [Codes.CODE_DELETE]: '删除',
+    [Codes.CODE_NORMAL]: '正常',
+    [Codes.CODE_DELETE]: '删除',
   },
   // 商品出售状态
   itemStatuses: {
@@ -53,8 +68,8 @@ module.exports = {
   },
   // 终端账号状态
   terminalStatuses: {
-    [Codes.CODE_NORMAL]: '使用中',
-    [Codes.CODE_DISABLE]: '禁用',
+    [Codes.CODE_NORMAL]: '正常',
+    [Codes.CODE_DISABLE]: '禁用',
   },
   // 产品类型
   productType: {
@@ -75,16 +90,27 @@ module.exports = {
     [Codes.CODE_LIVE]: '直播',
     [Codes.CODE_IMAGE]: '图片',
   },
-  // 标签类型
-  tagType: {
-    [Codes.CODE_COURSE]: '课程',
-    [Codes.CODE_SUPPORT]: '配套',
-  },
   // 订单状态
   orderStatuses: {
-    [Codes.CODE_UNPAID]: '支付',
+    [Codes.CODE_UNPAID]: '待支付',
     [Codes.CODE_PAID]: '已支付',
-    [Codes.CODE_CANCEL]: '已作废',
+    [Codes.CODE_CANCEL]: '已取消',
+    [Codes.CODE_REFUND]: '已退款',
+    [Codes.CODE_COMPLETE]: '已完成',
+    [Codes.CODE_FORSEND]: '待发货',
+    [Codes.CODE_SENT]: '已发货',
+    [Codes.CODE_RECEIVED]: '已收货',
+  },
+  // 订单拆分状态
+  orderSplitStatus: {
+    [Codes.CODE_ORDER_SPLIT_YES]: '已拆分',
+    [Codes.CODE_ORDER_SPLIT_NO]: '未拆分',
+    [Codes.CODE_ORDER_SPLIT_UN]: '不需要拆分',
+  },
+  // 商品类型
+  goodsType: {
+    [Codes.CODE_ENTITY]: '实体物品',
+    [Codes.CODE_VIRTUAL]: '虚拟物品',
   },
   // 计价单位 名字: 时长
   chargeUnit: {

+ 7 - 7
src/utils/request.js

@@ -25,8 +25,8 @@ const httpCodeMessage = {
 // 自定义响应状态码
 const customCodeMessage = {
   10004: 'Token认证失败',
+  10003: 'Token失效',
   10002: '账号或密码错误',
-  800: '数据不存在',
 };
 
 /**
@@ -53,14 +53,13 @@ function httpErrorHandler(response) {
 function apiErrorHandler(data) {
   if (!data.success) {
     const errortext = customCodeMessage[data.code] || data.message;
-    // Token认证失败, 跳转到登录界面
-    if (data.code === 10004) {
-      message.error('登录失效,请重新登录!');
-      const { dispatch } = store;
-      dispatch(routerRedux.push('/user/login'));
     // 登录密码错误
-    } else if (data.code === 10002) {
+    if (data.code === 10002) {
       message.error(errortext);
+    // Token失效,跳转到登录界面
+    } else if (data.code === 10003 || data.code === 10004) {
+      const { dispatch } = store;
+      dispatch(routerRedux.push('/user/login'));
     // 其它错误打出错误代码
     } else {
       message.error(`请求错误 错误代码:${data.code} 错误信息:${errortext}`);
@@ -109,3 +108,4 @@ export default function request(url, options) {
     .then(apiErrorHandler)
     .catch(e => ({ e }));
 }
+