Quellcode durchsuchen

Merge branch 'master' into channel

zhanghe vor 6 Jahren
Ursprung
Commit
4486a2ae2e

+ 600 - 0
src/routes/Product/Course/CourseCreate.js

@@ -0,0 +1,600 @@
+import React, { Component } from 'react';
+import pathToRegexp from 'path-to-regexp';
+import { connect } from 'dva';
+import { routerRedux } from 'dva/router';
+import {
+  Form, Modal, Card, Button, Input, Switch, Select, Row, Col,
+} from 'antd';
+import {
+  renderStatus, statusToBool, boolToStatus, genAbsolutePicUrl,
+} from '../../../utils/utils';
+import { Hotax } from '../../../utils/config';
+import AXDragSortTable from '../../../components/AXDragSortTable';
+import Selector from '../../../components/AXTableSelector/Selector';
+import FooterToolbar from '../../../components/FooterToolbar';
+import styles from './CourseCreate.less';
+
+const fieldLabels = {
+  code: '课程编号',
+  title: '课程标题',
+  subTitle: '课程副标题',
+  name: '课程全称',
+  breadCrumb: '顶部导航名',
+  merchant: '内容提供商',
+  digest: '课程概要',
+  detail: '课程详情',
+  coverUrl: '课程封面图',
+  bgUrl: '课程背景图',
+  status: '课程状态',
+  courseType: '阅读历史过滤',
+};
+const formItemLayout = {
+  labelCol: {
+    xs: { span: 24 },
+    sm: { span: 3 },
+  },
+  wrapperCol: {
+    xs: { span: 24 },
+    sm: { span: 14 },
+    md: { span: 12 },
+  },
+};
+
+@connect(({ loading, product, lesson, resource, merchant }) => ({
+  lesson,
+  product,
+  resource,
+  merchant,
+  lLoading: loading.models.lesson,
+  pLoading: loading.models.product,
+  rLoading: loading.models.resource,
+  submitting: loading.models.product,
+}))
+@Form.create()
+export default class CourseItemCreatePage extends Component {
+  state = {
+    bgSelectorDestroy: true,
+    coverSelectorDestroy: true,
+    lessonSelectorDestroy: true,
+    supportSelectorDestroy: true,
+  };
+  componentWillMount() {
+    const match = pathToRegexp('/product/course/create').exec(this.props.location.pathname);
+    if (match) {
+      this.cleanPageState();
+    }
+  }
+  componentDidMount() {
+    const matchId = this.isEdit();
+    if (matchId) {
+      this.props.dispatch({
+        type: 'product/fetchProductItem',
+        payload: { pid: matchId },
+      });
+    }
+    this.props.dispatch({
+      type: 'merchant/fetchMerchantList',
+      payload: { pageSize: 1000 }, // TODO 以后商户多了需要改写交互样式
+    });
+  }
+  isEdit = () => {
+    const { location } = this.props;
+    const match = pathToRegexp('/product/course/edit/:id').exec(location.pathname);
+    if (match) {
+      return match[1];
+    }
+    return false;
+  };
+  cleanPageState = () => {
+    this.props.dispatch({
+      type: 'product/cleanItemState',
+      payload: {},
+    });
+  };
+  selectorDataFetcher = (name, params) => {
+    switch (name) {
+      case 'cover':
+        this.props.dispatch({
+          type: 'resource/fetchImageList',
+          payload: params,
+        });
+        break;
+      case 'bg':
+        this.props.dispatch({
+          type: 'resource/fetchImageList',
+          payload: params,
+        });
+        break;
+      case 'lesson':
+        this.props.dispatch({
+          type: 'lesson/fetchLessonList',
+          payload: params,
+        });
+        break;
+      case 'support':
+        this.props.dispatch({
+          type: 'product/fetchSupportList',
+          payload: params,
+        });
+        break;
+      default:
+        break;
+    }
+  };
+  currentItemFormatter = (name, rows) => {
+    let payload;
+    switch (name) {
+      case 'cover':
+        payload = { coverUrl: rows[0].path };
+        break;
+      case 'bg':
+        payload = { bgUrl: rows[0].path };
+        break;
+      case 'lesson':
+        payload = { subItemList: rows };
+        break;
+      case 'support':
+        payload = { supportList: rows };
+        break;
+      default:
+        break;
+    }
+    return payload;
+  };
+  handleSelectorModalShow = (name) => {
+    this.setState({
+      [`${name}SelectorDestroy`]: false,
+    });
+    this.selectorDataFetcher(name);
+  };
+  handleSelectorChange = (name, params) => {
+    this.selectorDataFetcher(name, params);
+  };
+  handleSelectorFinish = (name, rows) => {
+    this.setState({
+      [`${name}SelectorDestroy`]: true,
+    });
+    const payload = this.currentItemFormatter(name, rows);
+    this.props.dispatch({
+      payload,
+      type: 'product/fixCurrentItem',
+    });
+  };
+  handleDragSortTableChange = (name, rows) => {
+    const payload = this.currentItemFormatter(name, rows);
+    this.props.dispatch({
+      payload,
+      type: 'product/fixCurrentItem',
+    });
+  };
+  handleSelectorCancel = (name) => {
+    this.setState({
+      [`${name}SelectorDestroy`]: true,
+    });
+  };
+  handlePageBack = () => {
+    this.props.dispatch(routerRedux.push({
+      pathname: '/product/course/list',
+      state: this.props.location.state,
+    }));
+  };
+  handlePageSubmit = (e) => {
+    e.preventDefault();
+    this.props.form.validateFieldsAndScroll((err, values) => {
+      if (!err) {
+        // 提取表单字段
+        const { title, subTitle, name, status, ...rest } = values;
+        const newName = `${title}_${subTitle}`;
+        const newValues = { title, subTitle, name: newName, status: boolToStatus(status), ...rest };
+
+        const { product } = this.props;
+        const { currentItem } = product;
+        const { bgUrl, coverUrl, subItemList, supportList } = currentItem;
+
+        // 构造subList,supportIdList
+        let subList;
+        let supportIdList;
+        if (subItemList) {
+          subList = subItemList.map(
+            item => ({ id: item.id, type: Hotax.PRODUCT_LESSON })
+          );
+        }
+        if (supportList) {
+          supportIdList = supportList.map(item => item.id);
+        }
+
+        const matchId = this.isEdit();
+        if (matchId) {
+          const params = {
+            bgUrl,
+            coverUrl,
+            id: matchId,
+            subItemList: subList,
+            supportList: supportIdList,
+            ...newValues,
+          };
+          this.props.dispatch({
+            type: 'product/updateCourseItem',
+            payload: params,
+            states: this.props.location.state,
+          });
+        } else {
+          const params = {
+            bgUrl,
+            coverUrl,
+            subItemList: subList,
+            supportList: supportIdList,
+            ...newValues,
+          };
+          this.props.dispatch({
+            type: 'product/createCourseItem',
+            payload: params,
+            states: this.props.location.state,
+          });
+        }
+      }
+    });
+  };
+
+  render() {
+    const {
+      form, submitting, rLoading, lLoading, pLoading, lesson, product, resource, merchant,
+    } = this.props;
+    const {
+      lessonSelectorDestroy, supportSelectorDestroy, coverSelectorDestroy, bgSelectorDestroy,
+    } = this.state;
+    const { currentItem } = product;
+    const {
+      code, title, subTitle, name, digest, detail, status, coverUrl, bgUrl, cpId,
+      courseType, breadCrumb, subItemList = [], supportList = [],
+    } = currentItem;
+    const { getFieldDecorator } = form;
+
+    const lessonColumns = [{
+      title: '课编号',
+      dataIndex: 'code',
+      key: 1,
+      width: '20%',
+      render: (text, record) => (
+        <a
+          className="a-link"
+          target="_blank"
+          rel="noopener noreferrer"
+          href={`/product/lesson/edit/${record.id}`}
+        >
+          {text}
+        </a>
+      ),
+    }, {
+      title: '课名称',
+      dataIndex: 'title',
+      key: 2,
+      width: '30%',
+      render: (text, record) => (
+        <a
+          className="a-link"
+          target="_blank"
+          rel="noopener noreferrer"
+          href={`/product/lesson/edit/${record.id}`}
+        >
+          {text}
+        </a>
+      ),
+    }, {
+      title: '状态',
+      dataIndex: 'status',
+      key: 3,
+      render: text => renderStatus(text),
+    }];
+    const supportColumns = [{
+      title: '配套编号',
+      dataIndex: 'code',
+      key: 1,
+      width: '20%',
+      render: (text, record) => (
+        <a
+          className="a-link"
+          target="_blank"
+          rel="noopener noreferrer"
+          href={`/product/support/edit/${record.id}`}
+        >
+          {text}
+        </a>
+      ),
+    }, {
+      title: '配套名称',
+      dataIndex: 'name',
+      key: 2,
+      width: '30%',
+      render: (text, record) => (
+        <a
+          className="a-link"
+          target="_blank"
+          rel="noopener noreferrer"
+          href={`/product/support/edit/${record.id}`}
+        >
+          {text}
+        </a>
+      ),
+    }, {
+      title: '配套状态',
+      dataIndex: 'status',
+      key: 3,
+      render: text => renderStatus(text),
+    }];
+
+    const getMerchants = () => {
+      const { list } = merchant;
+      return list.map(item => ({
+        text: item.name,
+        key: item.id,
+      }));
+    };
+
+    const getResourceModal = (isCover) => {
+      return (
+        <Modal
+          width={1100}
+          footer={null}
+          visible
+          title="图片资源"
+          maskClosable={false}
+          onCancel={() => this.handleSelectorCancel(isCover ? 'cover' : 'bg')}
+        >
+          <Selector
+            multiple={false}
+            loading={rLoading}
+            selectorName="PictureSingle"
+            list={resource.list}
+            pageNo={resource.pageNo}
+            pageSize={resource.pageSize}
+            totalSize={resource.totalSize}
+            onCancel={() => this.handleSelectorCancel(isCover ? 'cover' : 'bg')}
+            onChange={data => this.handleSelectorChange(isCover ? 'cover' : 'bg', data)}
+            onFinish={rows => this.handleSelectorFinish(isCover ? 'cover' : 'bg', rows)}
+          />
+        </Modal>
+      );
+    };
+    const getLessonModal = () => {
+      return (
+        <Modal
+          width={1100}
+          footer={null}
+          visible
+          title="课资源"
+          maskClosable={false}
+          onCancel={() => this.handleSelectorCancel('lesson')}
+        >
+          <Selector
+            multiple
+            loading={lLoading}
+            selectorName="Lesson"
+            selectedRows={subItemList}
+            list={lesson.list}
+            pageNo={lesson.pageNo}
+            pageSize={lesson.pageSize}
+            totalSize={lesson.totalSize}
+            onCancel={() => this.handleSelectorCancel('lesson')}
+            onChange={data => this.handleSelectorChange('lesson', data)}
+            onFinish={rows => this.handleSelectorFinish('lesson', rows)}
+          />
+        </Modal>
+      );
+    };
+    const getSupportModal = () => {
+      return (
+        <Modal
+          width={1100}
+          footer={null}
+          visible
+          title="配套资源"
+          maskClosable={false}
+          onCancel={() => this.handleSelectorCancel('support')}
+        >
+          <Selector
+            multiple
+            loading={pLoading}
+            selectorName="Support"
+            selectedRows={supportList}
+            list={product.list}
+            pageNo={product.pageNo}
+            pageSize={product.pageSize}
+            totalSize={product.totalSize}
+            onCancel={() => this.handleSelectorCancel('support')}
+            onChange={data => this.handleSelectorChange('support', data)}
+            onFinish={rows => this.handleSelectorFinish('support', rows)}
+          />
+        </Modal>
+      );
+    };
+    const renderLessonCardName = () => {
+      return (
+        <Button
+          type="primary"
+          onClick={() => this.handleSelectorModalShow('lesson')}
+        >课列表
+        </Button>
+      );
+    };
+    const renderSupportCardName = () => {
+      return (
+        <Button
+          type="primary"
+          onClick={() => this.handleSelectorModalShow('support')}
+        >配套列表
+        </Button>
+      );
+    };
+    return (
+      <div>
+        {/* 基础信息Card */}
+        <Card title="基础信息" style={{ marginBottom: 16 }}>
+          <Form>
+            <Form.Item hasFeedback label={fieldLabels.code} {...formItemLayout}>
+              {getFieldDecorator('code', {
+                rules: [
+                  {
+                    required: true, message: '请填写课程编号',
+                  }, {
+                    pattern: /^[a-zA-Z0-9|-]+$/g, message: '编号包含非法字符',
+                  },
+                ],
+                initialValue: code,
+              })(
+                <Input placeholder="请输入" />
+              )}
+            </Form.Item>
+            {this.isEdit() && (
+              <Form.Item label={fieldLabels.name} {...formItemLayout}>
+                {getFieldDecorator('name', {
+                  initialValue: name,
+                })(
+                  <Input placeholder="根据标题及副标题自动生成完整名称,不必填写" disabled />
+                )}
+              </Form.Item>
+            )}
+            <Form.Item hasFeedback label={fieldLabels.title} {...formItemLayout}>
+              {getFieldDecorator('title', {
+                rules: [{ required: true, message: '请填写课程标题' }],
+                initialValue: title,
+              })(
+                <Input placeholder="请输入" />
+              )}
+            </Form.Item>
+            <Form.Item hasFeedback label={fieldLabels.subTitle} {...formItemLayout}>
+              {getFieldDecorator('subTitle', {
+                rules: [{ required: true, message: '请填写课程副标题' }],
+                initialValue: subTitle,
+              })(
+                <Input placeholder="请输入" />
+              )}
+            </Form.Item>
+            <Form.Item hasFeedback label={fieldLabels.breadCrumb} {...formItemLayout}>
+              {getFieldDecorator('breadCrumb', {
+                rules: [{ required: true, message: '请填写顶部导航名' }],
+                initialValue: breadCrumb,
+              })(
+                <Input placeholder="请输入" />
+              )}
+            </Form.Item>
+            <Form.Item hasFeedback label={fieldLabels.merchant} {...formItemLayout}>
+              {getFieldDecorator('cpId', {
+                rules: [{ required: true, message: '请选择供应商' }],
+                initialValue: cpId,
+              })(
+                <Select placeholder="请选择">
+                  {
+                    getMerchants().map(item => (
+                      <Select.Option key={item.key} value={item.key}>
+                        {item.text}
+                      </Select.Option>
+                    ))
+                  }
+                </Select>
+              )}
+            </Form.Item>
+            <Form.Item hasFeedback label={fieldLabels.courseType} {...formItemLayout}>
+              {getFieldDecorator('courseType', {
+                initialValue: courseType || Hotax.FILTER_NO,
+              })(
+                <Select placeholder="请选择">
+                  <Select.Option value={Hotax.FILTER_YES}>是</Select.Option>
+                  <Select.Option value={Hotax.FILTER_NO}>否</Select.Option>
+                </Select>
+              )}
+            </Form.Item>
+            <Form.Item label={fieldLabels.digest} {...formItemLayout}>
+              {getFieldDecorator('digest', {
+                initialValue: digest,
+              })(
+                <Input.TextArea rows={4} placeholder="请输入" />
+              )}
+            </Form.Item>
+            <Form.Item label={fieldLabels.detail} {...formItemLayout}>
+              {getFieldDecorator('detail', {
+                initialValue: detail,
+              })(
+                <Input.TextArea rows={6} placeholder="请输入" />
+              )}
+            </Form.Item>
+            <Form.Item label={fieldLabels.status} {...formItemLayout}>
+              {getFieldDecorator('status', {
+                valuePropName: 'checked',
+                initialValue: statusToBool(status),
+              })(
+                <Switch
+                  checkedChildren="正常"
+                  unCheckedChildren="删除"
+                />
+              )}
+            </Form.Item>
+          </Form>
+        </Card>
+        <Row gutter={16} style={{ marginBottom: 16 }}>
+          <Col span={12}>
+            <Card
+              className={styles.picCard}
+              title={
+                <Button
+                  type="primary"
+                  onClick={() => this.handleSelectorModalShow('cover')}
+                >课程封面
+                </Button>
+              }
+            >
+              {coverUrl && <img src={genAbsolutePicUrl(coverUrl)} alt="" />}
+              {!coverSelectorDestroy && getResourceModal(true)}
+            </Card>
+          </Col>
+          <Col span={12}>
+            <Card
+              className={styles.picCard}
+              title={
+                <Button
+                  type="primary"
+                  onClick={() => this.handleSelectorModalShow('bg')}
+                >课程背景
+                </Button>
+              }
+            >
+              {bgUrl && <img src={genAbsolutePicUrl(bgUrl)} alt="" />}
+              {!bgSelectorDestroy && getResourceModal(false)}
+            </Card>
+          </Col>
+        </Row>
+        {/* 课列表选择Card */}
+        <Card title={renderLessonCardName()} style={{ marginBottom: 16 }}>
+          <AXDragSortTable
+            columns={lessonColumns}
+            data={subItemList}
+            onChange={rows => this.handleDragSortTableChange('lesson', rows)}
+          />
+          {!lessonSelectorDestroy && getLessonModal()}
+        </Card>
+        {/* 周边配套选择Card */}
+        <Card title={renderSupportCardName()} style={{ marginBottom: 70 }}>
+          <AXDragSortTable
+            columns={supportColumns}
+            data={supportList}
+            onChange={rows => this.handleDragSortTableChange('support', rows)}
+          />
+          {!supportSelectorDestroy && getSupportModal()}
+        </Card>
+        <FooterToolbar style={{ width: '100%' }}>
+          <Button
+            onClick={this.handlePageBack}
+            style={{ marginRight: 10 }}
+          >取消
+          </Button>
+          <Button
+            type="primary"
+            loading={submitting}
+            onClick={this.handlePageSubmit}
+          >提交
+          </Button>
+        </FooterToolbar>
+      </div>
+    );
+  }
+}

+ 1 - 1
src/routes/Terminal/User/TerminalEdit.js

@@ -173,7 +173,7 @@ export default class TerminalEditPage extends PureComponent {
       align: 'center',
     }, {
       title: '最后登录时间',
-      dataIndex: 'gmtCreated',
+      dataIndex: 'gmtModified',
       render: text => moment(text).format('YYYY-MM-DD HH:mm:ss'),
       width: '25%',
       align: 'center',

+ 4 - 0
src/utils/config.js

@@ -30,6 +30,10 @@ Hotax.PRODUCT_PACKAGE = 'PACKAGE';
 Hotax.STATUS_NORMAL = 'NORMAL';
 Hotax.STATUS_DELETE = 'DEL';
 
+// 推荐位过滤标记值
+Hotax.FILTER_NO = 0;
+Hotax.FILTER_YES = 1;
+
 // 订单状态 <待支付|已作废|已支付|待发货|已发货|待收货|已收货|待退款|已完成>
 Hotax.ORDER_UNPAID = 'UNPAID';
 Hotax.ORDER_CANCEL = 'CANCEL';