Go 5 лет назад
Родитель
Сommit
92c04f79f3
33 измененных файлов с 1257 добавлено и 589 удалено
  1. 3 3
      front/project/h5/stores/order.js
  2. 1 1
      front/project/www/components/Select/index.js
  3. 136 96
      front/project/www/routes/my/answer/page.js
  4. 117 58
      front/project/www/routes/my/collect/page.js
  5. 63 27
      front/project/www/routes/my/course/page.js
  6. 16 164
      front/project/www/routes/my/data/page.js
  7. 97 53
      front/project/www/routes/my/error/page.js
  8. 196 0
      front/project/www/routes/my/index.js
  9. 21 2
      front/project/www/routes/my/message/page.js
  10. 88 37
      front/project/www/routes/my/note/page.js
  11. 72 14
      front/project/www/routes/my/order/page.js
  12. 111 61
      front/project/www/routes/my/report/page.js
  13. 22 13
      front/project/www/routes/my/tools/page.js
  14. 10 13
      front/project/www/stores/my.js
  15. 12 4
      front/project/www/stores/order.js
  16. 3 3
      front/src/containers/Page.js
  17. 35 0
      server/data/src/main/java/com/qxgmat/data/dao/entity/User.java
  18. 35 0
      server/data/src/main/java/com/qxgmat/data/dao/entity/UserTextbookFeedback.java
  19. 3 2
      server/data/src/main/java/com/qxgmat/data/dao/mapping/UserMapper.xml
  20. 3 1
      server/data/src/main/java/com/qxgmat/data/dao/mapping/UserTextbookFeedbackMapper.xml
  21. 1 1
      server/data/src/main/java/com/qxgmat/data/relation/UserOrderRecordRelationMapper.java
  22. 7 7
      server/data/src/main/java/com/qxgmat/data/relation/mapping/UserAskQuestionRelationMapper.xml
  23. 2 2
      server/data/src/main/java/com/qxgmat/data/relation/mapping/UserCollectQuestionRelationMapper.xml
  24. 7 7
      server/data/src/main/java/com/qxgmat/data/relation/mapping/UserNoteQuestionRelationMapper.xml
  25. 25 0
      server/data/src/main/java/com/qxgmat/data/relation/mapping/UserOrderRecordRelationMapper.xml
  26. 12 8
      server/data/src/main/java/com/qxgmat/data/relation/mapping/UserPaperRelationMapper.xml
  27. 2 2
      server/data/src/main/java/com/qxgmat/data/relation/mapping/UserQuestionRelationMapper.xml
  28. 1 0
      server/data/src/main/resources/db/migration/V1__init_table.sql
  29. 45 1
      server/gateway-api/src/main/java/com/qxgmat/controller/api/MyController.java
  30. 59 1
      server/gateway-api/src/main/java/com/qxgmat/controller/api/OrderController.java
  31. 23 0
      server/gateway-api/src/main/java/com/qxgmat/dto/response/UserOrderRecordListDto.java
  32. 8 8
      server/gateway-api/src/main/java/com/qxgmat/service/inline/UserOrderRecordService.java
  33. 21 0
      server/gateway-api/src/main/java/com/qxgmat/service/inline/UserOrderService.java

+ 3 - 3
front/project/h5/stores/order.js

@@ -43,7 +43,7 @@ export default class OrderStore extends BaseStore {
    * @param {*} param0
    */
   listRecord({ page, size }) {
-    return this.apiGet('/my/record/list', { page, size });
+    return this.apiGet('/order/record/list', { page, size });
   }
 
   /**
@@ -51,7 +51,7 @@ export default class OrderStore extends BaseStore {
    * @param {*} id
    */
   getRecord(id) {
-    return this.apiGet('/my/record/detail', { id });
+    return this.apiGet('/order/record/detail', { id });
   }
 
   /**
@@ -59,7 +59,7 @@ export default class OrderStore extends BaseStore {
    * @param {*} id
    */
   useRecord(id, isSubscribe) {
-    return this.apiPost('/my/record/use', { id, isSubscribe });
+    return this.apiPost('/order/record/use', { id, isSubscribe });
   }
 }
 

+ 1 - 1
front/project/www/components/Select/index.js

@@ -23,7 +23,7 @@ export default class Select extends Component {
   render() {
     const { selecting } = this.state;
     const { placeholder, value, list = [], size = 'basic', theme = 'theme', excludeSelf, onChange } = this.props;
-    let index = -1;
+    let index = 0;
     for (let i = 0; i < list.length; i += 1) {
       if (list[i].key === value) {
         index = i;

+ 136 - 96
front/project/www/routes/my/answer/page.js

@@ -2,62 +2,66 @@ import React from 'react';
 import { Link } from 'react-router-dom';
 import './index.less';
 import Page from '@src/containers/Page';
+import { timeRange } from '@src/services/Tools';
 import UserLayout from '../../../layouts/User';
 import UserTable from '../../../components/UserTable';
 import UserAction from '../../../components/UserAction';
+import UserPagination from '../../../components/UserPagination';
 import Switch from '../../../components/Switch';
-import menu from '../index';
+import menu, { refreshQuestionType, refreshStruct } from '../index';
 import Tabs from '../../../components/Tabs';
+import { TimeRange } from '../../../../Constant';
+import { My } from '../../../stores/my';
 
-const columns = [
-  {
-    key: 'title',
-    title: '笔记对象',
-    width: 140,
-    render(text, row) {
-      return row.group ? (
-        <div className="group">
-          <Link to="">{text}</Link>
-        </div>
-      ) : (
-        <div className="sub">{text}</div>
-      );
-    },
+const columns = [{
+  key: 'title',
+  title: '笔记对象',
+  width: 140,
+  render(text, row) {
+    return row.group ? (
+      <div className="group">
+        <Link to="">{text}</Link>
+      </div>
+    ) : (
+      <div className="sub">{text}</div>
+    );
   },
-  {
-    key: 'date',
-    title: '更新时间',
-    width: 100,
-    render(text, row) {
-      return row.group ? (
-        <div className="group">
-          <Link to="">{text}</Link>
-        </div>
-      ) : (
-        <div className="sub">
-          <div className="date">{text.split(' ')[0]}</div>
-          <div className="date">{text.split(' ')[1]}</div>
-        </div>
-      );
-    },
+}, {
+  key: 'date',
+  title: '更新时间',
+  width: 100,
+  render(text, row) {
+    return row.group ? (
+      <div className="group">
+        <Link to="">{text}</Link>
+      </div>
+    ) : (<div className="sub">
+      <div className="date">{text.split(' ')[0]}</div>
+      <div className="date">{text.split(' ')[1]}</div>
+    </div>
+    );
   },
-  {
-    key: 'content',
-    title: '内容',
-    width: 540,
-    render(text, row) {
-      return row.group ? (
-        <div className="group">
-          <Link to="">{text}</Link>
-        </div>
-      ) : (
-        <div className="sub">{text}</div>
-      );
-    },
+}, {
+  key: 'content',
+  title: '内容',
+  width: 540,
+  render(text, row) {
+    return row.group ? (
+      <div className="group">
+        <Link to="">{text}</Link>
+      </div>
+    ) : (
+      <div className="sub">{text}</div>
+    );
   },
-];
+}];
 
 export default class extends Page {
+  constructor(props) {
+    props.size = 10;
+    super(props);
+  }
+
   initState() {
     return {
       filterMap: {},
@@ -109,29 +113,60 @@ export default class extends Page {
         },
       ],
       selectList: [],
-      tab: '1',
-      page: 1,
-      total: 1,
+      tab: 'exercise',
+      timerange: 'today',
     };
   }
 
+  initData() {
+    const data = Object.assign(this.state, this.state.search);
+    data.filterMap = this.state.search;
+    if (data.order) {
+      data.sortMap = { [data.order]: data.direction };
+    }
+    if (data.timerange) {
+      data.filterMap.timerange = data.timerange;
+    }
+    const [startTime, endTime] = timeRange(data.timerange);
+    refreshQuestionType(this, data.subject, data.questionType, { all: true, needSentence: false, allSubject: true })
+      .then(({ questionTypes }) => {
+        return refreshStruct(this, data.tab, data.one, data.two, {
+          all: true, needPreview: false, needTextbook: true,
+        })
+          .then(({ structIds, latest, year }) => {
+            My.listQuestionAsk(Object.assign({ module: data.tab, questionTypes, structIds, latest, year, startTime, endTime }, this.state.search, {
+              order: Object.keys(data.sortMap).map(key => {
+                return `${key} ${data.sortMap[key]}`;
+              }).join(','),
+            })).then(result => {
+              this.setState({ list: result.list, total: result.total, page: data.page });
+            });
+          });
+      });
+  }
+
   onTabChange(tab) {
-    this.setState({ tab });
+    const data = { tab };
+    this.refreshQuery(data);
   }
 
   onFilter(value) {
-    this.setState({ filterMap: value });
+    this.search(value);
   }
 
   onSort(value) {
-    this.setState({ sortMap: value });
+    const keys = Object.keys(value);
+    // this.search({ order: keys.length ? keys.join('|') : null, direction: keys.length ? Object.values(value).join('|') : null });
+    const { sortMap } = this.state;
+    const index = keys.length > 1 && sortMap[keys[0]] ? 1 : 0;
+    this.search({ order: keys.length ? keys[index] : null, direction: keys.length ? value[keys[index]] : null });
   }
 
-  onDataChange(page) {
-    this.setState({ page, allChecked: false, selectList: [] });
+  onChangePage(page) {
+    this.search({ page });
   }
 
-  onAction() {}
+  onAction() { }
 
   onSelect(selectList) {
     this.setState({ selectList });
@@ -143,7 +178,8 @@ export default class extends Page {
   }
 
   renderTable() {
-    const { tab, filterMap = {}, sortMap = {}, data = [] } = this.state;
+    const { tab, questionSubjectSelect, questionSubjectMap = {}, oneSelect, twoSelectMap = {}, filterMap = {}, sortMap = {}, list = [] } = this.state;
+    const { total, page } = this.state;
     return (
       <div className="table-layout">
         <Tabs
@@ -154,68 +190,72 @@ export default class extends Page {
           space={2.5}
           width={100}
           active={tab}
-          tabs={[{ key: '1', title: '练习' }, { key: '2', title: '模考' }]}
+          tabs={[{ key: 'exercise', title: '练习' }, { key: 'examination', title: '模考' }]}
           onChange={key => this.onTabChange(key)}
         />
         <UserAction
           search
-          selectList={[
-            {
-              label: '123',
-              children: [
-                {
-                  key: 'one',
-                  default: '1',
-                  select: [{ title: '123', key: '1' }, { title: '123', key: '2' }, { title: '123', key: '2' }],
-                },
-                {
-                  key: 'two',
-                  be: 'one',
-                  placeholder: '全部',
-                  selectMap: [{ title: '123', key: '1' }, { title: '123', key: '2' }, { title: '123', key: '2' }],
-                },
-              ],
-            },
-            {
-              label: '123',
-              right: true,
-              select: [{ title: '123', key: '1' }, { title: '123', key: '2' }, { title: '123', key: '2' }],
-            },
-          ]}
+          selectList={[{
+            children: [{
+              key: 'subject',
+              placeholder: '学科',
+              select: questionSubjectSelect,
+            }, {
+              placeholder: '题型',
+              key: 'questionType',
+              be: 'subject',
+              selectMap: questionSubjectMap,
+            }],
+          }, {
+            label: '范围',
+            children: [{
+              key: 'one',
+              placeholder: '全部',
+              select: oneSelect,
+            }, {
+              key: 'two',
+              be: 'one',
+              placeholder: '全部',
+              selectMap: twoSelectMap,
+            }],
+          }, {
+            right: true,
+            key: 'timerange',
+            select: TimeRange,
+          }]}
           filterMap={filterMap}
           onFilter={value => this.onFilter(value)}
         />
         <UserAction
-          sortList={[{ right: true, label: '提问时间', key: '1' }, { right: true, label: '回答时间', key: '2' }]}
+          sortList={[{ right: true, label: '提问时间', key: 'create_time' }, { right: true, label: '回答时间', key: 'ask_time' }]}
           sortMap={sortMap}
           left={
             <div className="email">
-              只看已回答 <Switch />
+              只看已回答 <Switch checked={filterMap.askStatus} onChange={() => {
+              filterMap.askStatus = !filterMap.askStatus;
+              this.onFilter(filterMap);
+            }} />
             </div>
           }
           onSort={value => this.onSort(value)}
         />
-        {data.map(item => {
+        {list.map(item => {
           return (
             <div className="group">
               <UserTable border={false} size="small" columns={columns} data={[item]} header={false} />
-              {item.list &&
-                item.list.map(_item => {
-                  return (
-                    <div className="answer-layout">
-                      <div className="title">
-                        提问区域: <b>{_item.title}</b>
-                      </div>
-                      <div className="small-tag">提问</div>
-                      <div className="desc">{_item.ask}</div>
-                      <div className="small-tag">回答</div>
-                      <div className="desc">{_item.answer}</div>
-                    </div>
-                  );
-                })}
+              <div className="answer-layout">
+                <div className="title">
+                  提问区域: <b>{item.target}</b>
+                </div>
+                <div className="small-tag">提问</div>
+                <div className="desc">{item.content}</div>
+                <div className="small-tag">回答</div>
+                <div className="desc">{item.answer}</div>
+              </div>
             </div>
           );
         })}
+        {total && list.length > 0 && <UserPagination total={total} current={page} onChange={(p) => this.onChangePage(p)} />}
       </div>
     );
   }

+ 117 - 58
front/project/www/routes/my/collect/page.js

@@ -2,22 +2,25 @@ import React, { Component } from 'react';
 import './index.less';
 import { Icon, Checkbox } from 'antd';
 import Page from '@src/containers/Page';
+import { timeRange } from '@src/services/Tools';
 import UserLayout from '../../../layouts/User';
 import UserTable from '../../../components/UserTable';
 import UserAction from '../../../components/UserAction';
-import menu from '../index';
+import menu, { refreshQuestionType, refreshStruct } from '../index';
 import Tabs from '../../../components/Tabs';
 import Modal from '../../../components/Modal';
 import Select from '../../../components/Select';
 import GIcon from '../../../components/Icon';
+import { TimeRange } from '../../../../Constant';
+import { My } from '../../../stores/my';
 
 const columns = [
-  { key: '', title: '题型', fixSort: true },
-  { key: '', title: '题目ID', fixSort: true },
-  { key: '', title: '内容' },
-  { key: '', title: '耗时', sort: true },
-  { key: '', title: '错误率', sort: true },
-  { key: '', title: '最近做题' },
+  { key: 'question_type', title: '题型', fixSort: true },
+  { key: 'title', title: '题目ID', fixSort: true },
+  { key: 'description', title: '内容' },
+  { key: 'time', title: '耗时', sort: true },
+  { key: 'correct', title: '错误率', sort: true },
+  { key: 'latest_time', title: '最近做题' },
   {
     key: '',
     title: '',
@@ -37,34 +40,89 @@ const exportType = [
 ];
 
 export default class extends Page {
+  constructor(props) {
+    props.size = 10;
+    super(props);
+  }
+
   initState() {
     return {
-      tab: '1',
+      tab: 'question',
+      timerange: 'today',
       filterMap: {},
       sortMap: {},
       data: [{}, {}],
       selectList: [],
       allChecked: false,
-      page: 1,
-      total: 1,
       showDetail: false,
     };
   }
 
-  onChangeTab(tab) {
-    this.setState({ tab });
+  initData() {
+    const data = Object.assign(this.state, this.state.search);
+    data.filterMap = this.state.search;
+    if (data.order) {
+      data.sortMap = { [data.order]: data.direction };
+    }
+    if (data.timerange) {
+      data.filterMap.timerange = data.timerange;
+    }
+    switch (data.tab) {
+      case 'question':
+        return this.refreshQuestion(data);
+      case 'article':
+        return this.refreshArticle(data);
+      default:
+        return null;
+    }
+  }
+
+  refreshQuestion(data) {
+    const [startTime, endTime] = timeRange(data.timerange);
+    refreshQuestionType(this, data.subject, data.questionType, { all: true, needSentence: true, allSubject: true })
+      .then(({ questionTypes }) => {
+        return refreshStruct(this, data.tab, data.one, data.two, {
+          all: true, needPreview: false, needTextbook: true,
+        })
+          .then(({ structIds, latest, year }) => {
+            My.listQuestionCollect(Object.assign({ module: data.tab, questionTypes, structIds, latest, year, startTime, endTime }, this.state.search, {
+              order: Object.keys(data.sortMap).map(key => {
+                if (key === 'title') return 'pid desc, no desc';
+                return `${key} ${data.sortMap[key]}`;
+              }).join(','),
+            })).then(result => {
+              this.setState({ list: result.list, total: result.total });
+            });
+          });
+      });
+  }
+
+  refreshArticle(data) {
+    const [startTime, endTime] = timeRange(data.timerange);
+    My.listExperienceCollect(Object.assign({ startTime, endTime }, this.state.search)).then(result => {
+      this.setState({ list: result.list, total: result.total });
+    });
+  }
+
+  onTabChange(tab) {
+    const data = { tab };
+    this.refreshQuery(data);
   }
 
   onFilter(value) {
-    this.setState({ filterMap: value });
+    this.search(value);
   }
 
   onSort(value) {
-    this.setState({ sortMap: value });
+    const keys = Object.keys(value);
+    // this.search({ order: keys.length ? keys.join('|') : null, direction: keys.length ? Object.values(value).join('|') : null });
+    const { sortMap } = this.state;
+    const index = keys.length > 1 && sortMap[keys[0]] ? 1 : 0;
+    this.search({ order: keys.length ? keys[index] : null, direction: keys.length ? value[keys[index]] : null });
   }
 
-  onDataChange(page) {
-    this.setState({ page, allChecked: false, selectList: [] });
+  onChangePage(page) {
+    this.search({ page });
   }
 
   onAll(checked) {
@@ -80,7 +138,7 @@ export default class extends Page {
     }
   }
 
-  onAction() {}
+  onAction() { }
 
   onSelect(selectList) {
     this.setState({ selectList });
@@ -103,7 +161,7 @@ export default class extends Page {
           space={2.5}
           width={100}
           active={tab}
-          tabs={[{ key: '1', title: '题目' }, { key: '2', title: '文章' }]}
+          tabs={[{ key: 'question', title: '题目' }, { key: 'article', title: '文章' }]}
           onChange={key => this.onChangeTab(key)}
         />
         {this[`renderTab${tab}`]()}
@@ -112,40 +170,41 @@ export default class extends Page {
     );
   }
 
-  renderTab1() {
-    const { filterMap = {}, sortMap = {}, selectList = [], data = [], allChecked, page, total } = this.state;
+  renderTabquestion() {
+    const { questionSubjectSelect, questionSubjectMap = {}, oneSelect, twoSelectMap = {}, filterMap = {}, sortMap = {}, list = [] } = this.state;
+    const { selectList = [], allChecked, page, total } = this.state;
     return (
       <div className="tab-1-layout">
         <UserAction
-          selectList={[
-            {
-              children: [
-                {
-                  key: '3',
-                  default: '1',
-                  select: [{ title: '123', key: '1' }, { title: '123', key: '2' }, { title: '123', key: '2' }],
-                },
-                {
-                  key: 'two',
-                  be: 'one',
-                  placeholder: '全部',
-                  selectMap: {
-                    1: [{ title: '123', key: '1' }, { title: '123', key: '2' }, { title: '123', key: '2' }],
-                  },
-                },
-              ],
-            },
-            {
-              label: '123',
+          search
+          selectList={[{
+            children: [{
+              key: 'subject',
+              placeholder: '学科',
+              select: questionSubjectSelect,
+            }, {
+              placeholder: '题型',
+              key: 'questionType',
+              be: 'subject',
+              selectMap: questionSubjectMap,
+            }],
+          }, {
+            label: '范围',
+            children: [{
               key: 'one',
-              select: [{ title: '123', key: '1' }, { title: '123', key: '2' }, { title: '123', key: '2' }],
-            },
-            {
-              key: 'th',
-              right: true,
-              select: [{ title: '123', key: '1' }, { title: '123', key: '2' }, { title: '123', key: '2' }],
-            },
-          ]}
+              placeholder: '全部',
+              select: oneSelect,
+            }, {
+              key: 'two',
+              be: 'one',
+              placeholder: '全部',
+              selectMap: twoSelectMap,
+            }],
+          }, {
+            right: true,
+            key: 'timerange',
+            select: TimeRange,
+          }]}
           filterMap={filterMap}
           onFilter={value => this.onFilter(value)}
         />
@@ -170,19 +229,19 @@ export default class extends Page {
           select
           columns={columns}
           sortMap={sortMap}
-          data={data}
+          data={list}
           current={page}
           total={total}
           selectList={selectList}
           onSelect={l => this.onSelect(l)}
           onSort={v => this.onSort(v)}
-          onChange={p => this.onDataChange(p)}
+          onChange={p => this.onChangePage(p)}
         />
       </div>
     );
   }
 
-  renderTab2() {
+  renderTabarticle() {
     const { filterMap = {}, sortMap = {} } = this.state;
     return (
       <div className="tab-1-layout">
@@ -219,7 +278,7 @@ export default class extends Page {
   renderModal() {
     return [
       <ArticleDetail show={this.state.showDetail} onClose={() => this.setState({ showDetail: false })} />,
-      <Modal title="操作提示" confirmText="好的,知道了" btnAlign="center" onConfirm={() => {}}>
+      <Modal title="操作提示" confirmText="好的,知道了" btnAlign="center" onConfirm={() => { }}>
         <div className="flex-layout m-b-2">
           <div className="flex-block">
             <div className="t-1 t-s-18">组卷功能</div>
@@ -239,10 +298,10 @@ export default class extends Page {
           查阅以上信息。
         </div>
       </Modal>,
-      <Modal title="组卷练习" confirmText="好的,知道了" btnAlign="center" onConfirm={() => {}}>
+      <Modal title="组卷练习" confirmText="好的,知道了" btnAlign="center" onConfirm={() => { }}>
         <div className="t-2 t-s-18">不可同时选中语文题和数学题,请重新选择。</div>
       </Modal>,
-      <Modal title="组卷练习" confirmText="开始练习" onConfirm={() => {}} onCancel={() => {}}>
+      <Modal title="组卷练习" confirmText="开始练习" onConfirm={() => { }} onCancel={() => { }}>
         <div className="t-2 t-s-18">
           您共选择了 <span className="t-4">50</span> 道题目,是否开始练习?
         </div>
@@ -256,15 +315,15 @@ export default class extends Page {
           次以上的错题
         </div>
       </Modal>,
-      <Modal title="移出" onConfirm={() => {}} onCancel={() => {}}>
+      <Modal title="移出" onConfirm={() => { }} onCancel={() => { }}>
         <div className="t-2 t-s-18">
           当前选中的 <span className="t-4">50</span> 道题即将被移出错题本,移出后无法恢复,是否继续?
         </div>
       </Modal>,
-      <Modal title="导出" confirmText="好的,知道了" btnAlign="center" onConfirm={() => {}}>
+      <Modal title="导出" confirmText="好的,知道了" btnAlign="center" onConfirm={() => { }}>
         <div className="t-2 t-s-18">正在下载中,请不要关闭当前页面!</div>
       </Modal>,
-      <Modal title="导出" confirmText="导出" onConfirm={() => {}} onCancel={() => {}}>
+      <Modal title="导出" confirmText="导出" onConfirm={() => { }} onCancel={() => { }}>
         <div className="t-2 t-s-18 m-b-5">
           当前共选中 <span className="t-4">50</span> 道题,请确认需要导出的内容:
         </div>
@@ -279,7 +338,7 @@ export default class extends Page {
           })}
         </div>
       </Modal>,
-      <Modal title="导出" confirmText="导出" onConfirm={() => {}} onCancel={() => {}}>
+      <Modal title="导出" confirmText="导出" onConfirm={() => { }} onCancel={() => { }}>
         <div className="t-2 t-s-18 m-b-5">
           当前共选中 <span className="t-4">50</span> 道题,请确认需要导出的内容:
         </div>

+ 63 - 27
front/project/www/routes/my/course/page.js

@@ -16,12 +16,13 @@ import More from '../../../components/More';
 import Modal from '../../../components/Modal';
 import Date from '../../../components/Date';
 import Note from '../../../components/Note';
+import { My } from '../../../stores/my';
 
 export default class extends Page {
   initState() {
     return {
-      tab1: '1',
-      tab2: '1',
+      tab: 'online',
+      status: 'all',
       data: [
         {
           status: 'ing',
@@ -54,7 +55,42 @@ export default class extends Page {
     };
   }
 
-  onAction() {}
+  initData() {
+    const data = Object.assign(this.state, this.state.search);
+    data.filterMap = this.state.search;
+    if (data.order) {
+      data.sortMap = { [data.order]: data.direction };
+    }
+    this.setState(data);
+    let isUsed = null;
+    let isEnd = null;
+    switch (data.status) {
+      case 'learning':
+        isUsed = true;
+        isEnd = false;
+        break;
+      case 'end':
+        isUsed = true;
+        isEnd = true;
+        break;
+      case 'all':
+      default:
+    }
+    My.listCourse(Object.assign({ courseModule: data.tab }, this.state.search, { isUsed, isEnd })).then(result => {
+      this.setState({ list: result.list, total: result.total, page: data.page });
+    });
+  }
+
+  onAction() { }
+
+  onTabChange(tab) {
+    const data = { tab };
+    this.refreshQuery(data);
+  }
+
+  onStatusChange(status) {
+    this.search({ status });
+  }
 
   refresh(tab1, tab2) {
     this.setState({ tab1, tab2 });
@@ -66,7 +102,7 @@ export default class extends Page {
   }
 
   renderDetail() {
-    const { tab1, tab2 } = this.state;
+    const { tab, status, showTime, showRestore, showUploadNote, showNote } = this.state;
     return (
       <div className="detail-layout">
         <div className="tip">
@@ -80,9 +116,9 @@ export default class extends Page {
           size="small"
           space={2.5}
           width={100}
-          active={tab1}
-          tabs={[{ key: '1', title: '在线课程' }, { key: '2', title: '1V1私教' }]}
-          onChange={key => this.refresh(key, tab2)}
+          active={tab}
+          tabs={[{ key: 'online', title: '在线课程' }, { key: 'vs', title: '1V1私教' }]}
+          onChange={key => this.onTabChange(key)}
         />
         <Tabs
           type="tag"
@@ -90,17 +126,17 @@ export default class extends Page {
           size="small"
           space={5}
           width={54}
-          active={tab2}
+          active={status}
           tabs={[
-            { key: '1', title: '全部' },
-            { key: '2', title: '未开通' },
-            { key: '3', title: '学习中' },
-            { key: '4', title: '已结束' },
+            { key: 'all', title: '全部' },
+            { key: 'nouse', title: '未开通' },
+            { key: 'learning', title: '学习中' },
+            { key: 'end', title: '已结束' },
           ]}
-          onChange={key => this.refresh(tab1, key)}
+          onChange={key => this.onStatusChange(key)}
         />
-        {this[`renderTab${tab1}`]()}
-        <Modal className="clock-modal" title="打卡表" width={460} onClose={() => {}}>
+        {this[`renderTab${tab}`]()}
+        <Modal show={showTime} className="clock-modal" title="打卡表" width={460} onClose={() => { }}>
           <div>
             <div className="d-i-b w-3">
               <div className="t-2 t-s-14">听课频率</div>
@@ -148,10 +184,10 @@ export default class extends Page {
           />
           <div className="t-2 t-s-12">*听课频率≤2天/课时,作业完成度≥90%,课程有效期可延长7-10天。</div>
         </Modal>
-        <Modal title="恢复学习" onConfirm={() => {}} onCancel={() => {}}>
+        <Modal show={showRestore} title="恢复学习" onConfirm={() => { }} onCancel={() => { }}>
           <div className="t-2 t-s-18">恢复学习后,本课程的停课功能将无法使用。是否恢复学习?</div>
         </Modal>
-        <Modal title="上传笔记" width={630} confirmText="提交" onConfirm={() => {}} onCancel={() => {}}>
+        <Modal show={showUploadNote} title="上传笔记" width={630} confirmText="提交" onConfirm={() => { }} onCancel={() => { }}>
           <textarea
             className="b-c-1 w-10 p-10 m-b-1"
             rows={6}
@@ -163,7 +199,7 @@ export default class extends Page {
           </div>
           <div className="b-b m-t-2" />
         </Modal>
-        <Modal title="课后笔记" width={500} height={500} onClose={() => {}}>
+        <Modal show={showNote} title="课后笔记" width={500} height={500} onClose={() => { }}>
           <Note />
           <Note />
           <Note />
@@ -173,16 +209,16 @@ export default class extends Page {
     );
   }
 
-  renderTab1() {
-    const { data = [] } = this.state;
-    return data.map(item => {
+  renderTabonline() {
+    const { list = [] } = this.state;
+    return list.map(item => {
       return <Course data={item} />;
     });
   }
 
-  renderTab2() {
-    const { data = [] } = this.state;
-    return data.map(item => {
+  renderTabvs() {
+    const { list = [] } = this.state;
+    return list.map(item => {
       return <Education data={item} />;
     });
   }
@@ -408,10 +444,10 @@ class Education extends Component {
         <div className="class-hour">
           <div className="text">课时 5:解读句子属性</div>
           <div className="right">
-            <GIcon name="prev" onClick={() => {}} />
+            <GIcon name="prev" onClick={() => { }} />
             <span>上一课时</span>
             <span>下一课时</span>
-            <GIcon name="next" onClick={() => {}} />
+            <GIcon name="next" onClick={() => { }} />
           </div>
         </div>
         {tab === '1' ? this.renderTimeLine() : this.renderTable()}
@@ -582,7 +618,7 @@ class TimeLineItem extends Component {
               <span className="link">点此上传</span>
             </div>
             <div className="note-list">
-              <Note onAction={() => {}} actionList={[{ key: '123', label: '123' }]} />
+              <Note onAction={() => { }} actionList={[{ key: '123', label: '123' }]} />
               <Note />
               <Note />
               <Note />

+ 16 - 164
front/project/www/routes/my/data/page.js

@@ -5,16 +5,15 @@ import Page from '@src/containers/Page';
 import BarChart from '@src/components/BarChart';
 import PieChart from '@src/components/PieChart';
 // import { getMap } from '@src/services/Tools';
+import { getMap, formatPercent, formatSeconds, timeRange } from '@src/services/Tools';
 import UserLayout from '../../../layouts/User';
 import UserTable from '../../../components/UserTable';
 import UserAction from '../../../components/UserAction';
 import Select from '../../../components/Select';
-import menu from '../index';
+import menu, { refreshQuestionType, refreshStruct } from '../index';
 import Tabs from '../../../components/Tabs';
 import { My } from '../../../stores/my';
-import { QuestionDifficult, QuestionType, TimeRange, TextbookMinYear, CourseModule } from '../../../../Constant';
-import { getMap, formatPercent, formatSeconds, timeRange, formatTreeData } from '../../../../../src/services/Tools';
-import { Main } from '../../../stores/main';
+import { QuestionDifficult, QuestionType, TimeRange } from '../../../../Constant';
 
 const QuestionDifficultMap = getMap(QuestionDifficult, 'value', 'label');
 const QuestionTypeMap = getMap(QuestionType, 'value', 'label');
@@ -308,15 +307,25 @@ export default class extends Page {
   }
 
   initData() {
+    delete this.state.search.page;
+    delete this.state.search.size;
     const data = Object.assign(this.state, this.state.search);
-    data.filterMap = this.state;
+    data.filterMap = this.state.search;
     if (data.order) {
       data.sortMap = { [data.order]: data.direction };
     }
+    if (data.timerange) {
+      data.filterMap.timerange = data.timerange;
+    }
+    if (data.subject) {
+      data.filterMap.subject = data.subject;
+    }
     const [startTime, endTime] = timeRange(data.timerange);
-    this.refreshQuestionType(data.subject, { needSentence: false, allSubject: false })
+    refreshQuestionType(this, data.subject, data.questionType, { needSentence: false, allSubject: false })
       .then(() => {
-        return this.refreshStruct(data.tab, data.one, data.two, { needPreview: false, needTextbook: false });
+        return refreshStruct(this, data.tab, data.one, data.two, {
+          all: true, needPreview: false, needTextbook: false,
+        });
       })
       .then(({ structIds }) => {
         My.getData(data.tab, data.subject, structIds, startTime, endTime).then(result => {
@@ -332,163 +341,6 @@ export default class extends Page {
       });
   }
 
-  refreshQuestionType(subject, { needSentence, allSubject }) {
-    return Main.getExercise().then(result => {
-      const list = result.filter(row => (needSentence ? true : row.isExamination)).map(row => {
-        row.title = `${row.titleZh}${row.titleEn}`;
-        row.key = row.extend;
-        return row;
-      });
-      const tree = formatTreeData(list, 'id', 'title', 'parentId');
-      this.questionSubjectMap = getMap(tree, 'key', 'children');
-      this.questionSubjectSelect = tree.filter(row => row.level === 1 && (allSubject ? true : row.children.length > 1));
-      this.setState({
-        questionSubjectSelect: this.questionSubjectSelect,
-        questionSubjectMap: this.questionSubjectMap,
-      });
-      return {
-        questionTypes: subject ? this.questionSubjectMap[subject].map(row => row.key) : null,
-      };
-    });
-  }
-
-  refreshStruct(module, one, two, { needTextbook, needPreview }) {
-    switch (module) {
-      case 'exercise':
-        return Main.getExerciseAll().then(result => {
-          const tmp = result.filter(row => row.level > 2).map(row => {
-            row.title = `${row.titleZh}`;
-            row.key = row.titleEn;
-            return row;
-          });
-          const idsMap = getMap(tmp, 'id', 'key');
-          const map = {};
-          tmp.forEach(row => {
-            if (!map[row.key]) {
-              map[row.key] = {
-                title: row.title,
-                key: row.key,
-                structIds: [],
-                parentId: row.level > 3 ? idsMap[row.parentId] : null,
-                subject: [],
-                questionType: [],
-              };
-            }
-            const item = map[row.key];
-            item.structIds.push(row.id);
-            if (item.subject.indexOf(row.subject) < 0) {
-              item.subject.push(row.subject);
-            }
-            if (item.questionType.indexOf(row.questionType) < 0) {
-              item.questionType.push(row.questionType);
-            }
-          });
-          const list = Object.values(map);
-          if (needPreview) {
-            list.push({
-              title: '预习作业',
-              key: 'preview',
-              id: 'preview',
-            });
-            CourseModule.forEach(row => {
-              list.push({
-                title: row.label,
-                key: row.value,
-                parentId: 'preview',
-              });
-            });
-          }
-          let courseModules = null;
-          let structIds = null;
-          if (one === 'preview') {
-            if (!two) {
-              courseModules = CourseModule.map(row => row.value);
-            } else {
-              courseModules = [two];
-            }
-          } else if (one) {
-            const resultMap = getMap(list, 'key', 'structIds');
-            if (!two) {
-              structIds = resultMap[one];
-            } else {
-              structIds = resultMap[two];
-            }
-          }
-
-          const tree = formatTreeData(list, 'key', 'title', 'parentId');
-          const oneSelect = tree;
-          const twoSelectMap = getMap(tree, 'key', 'children');
-          this.setState({ oneSelect, twoSelectMap });
-
-          return {
-            structIds,
-            courseModules,
-          };
-        });
-      case 'examination':
-        return Main.getExamination().then(result => {
-          const list = result.map(row => {
-            row.title = `${row.titleZh}${row.titleEn}`;
-            row.key = `${row.id}`;
-            return row;
-          });
-          if (needTextbook) {
-            list.push({
-              title: '数学机经',
-              key: 'textbook',
-              id: 'textbook',
-            });
-            list.push({
-              title: '最新',
-              key: 'latest',
-              parentId: 'textbook',
-            });
-
-            const nowYear = new Date().getFullYear();
-            for (let i = TextbookMinYear; i <= nowYear; i += 1) {
-              list.push({
-                title: i.toString(),
-                key: i.toString(),
-                parentId: 'textbook',
-              });
-            }
-          }
-          let latest = null;
-          let year = null;
-          let structIds = null;
-          if (one === 'textbook') {
-            if (!two) {
-              latest = true;
-            } else if (two === 'latest') {
-              latest = true;
-            } else {
-              year = two;
-            }
-          } else if (one) {
-            if (!two) {
-              structIds = [Number(one)];
-            } else {
-              structIds = [Number(two)];
-            }
-          }
-
-          const tree = formatTreeData(list, 'key', 'title', 'parentId');
-          const oneSelect = tree;
-          const twoSelectMap = getMap(tree, 'key', 'children');
-
-          this.setState({ oneSelect, twoSelectMap });
-          return {
-            structIds,
-            latest,
-            year,
-          };
-        });
-      default:
-        return Promise.resolve({});
-    }
-  }
-
-
   onTabChange(tab) {
     const data = { tab };
     this.refreshQuery(data);

+ 97 - 53
front/project/www/routes/my/error/page.js

@@ -2,22 +2,25 @@ import React from 'react';
 import './index.less';
 import { Icon, Checkbox } from 'antd';
 import Page from '@src/containers/Page';
+import { timeRange } from '@src/services/Tools';
 import UserLayout from '../../../layouts/User';
 import UserTable from '../../../components/UserTable';
 import UserAction from '../../../components/UserAction';
-import menu from '../index';
+import menu, { refreshQuestionType, refreshStruct } from '../index';
 import Tabs from '../../../components/Tabs';
 import Modal from '../../../components/Modal';
 import Select from '../../../components/Select';
 import GIcon from '../../../components/Icon';
+import { TimeRange } from '../../../../Constant';
+import { My } from '../../../stores/my';
 
 const columns = [
-  { key: '', title: '题型', fixSort: true },
-  { key: '', title: '题目ID', fixSort: true },
-  { key: '', title: '内容' },
-  { key: '', title: '耗时', sort: true },
-  { key: '', title: '错误率', sort: true },
-  { key: '', title: '最近做题' },
+  { key: 'question_type', title: '题型', fixSort: true },
+  { key: 'title', title: '题目ID', fixSort: true },
+  { key: 'description', title: '内容' },
+  { key: 'time', title: '耗时', sort: true },
+  { key: 'correct', title: '错误率', sort: true },
+  { key: 'latest_time', title: '最近做题' },
   {
     key: '',
     title: '',
@@ -37,33 +40,70 @@ const exportType = [
 ];
 
 export default class extends Page {
+  constructor(props) {
+    props.size = 10;
+    super(props);
+  }
+
   initState() {
     return {
-      tab: '1',
+      tab: 'exercise',
+      timerange: 'today',
       filterMap: {},
       sortMap: {},
       data: [{}, {}],
       selectList: [],
       allChecked: false,
-      page: 1,
-      total: 1,
     };
   }
 
-  onChangeTab(tab) {
-    this.setState({ tab });
+  initData() {
+    const data = Object.assign(this.state, this.state.search);
+    data.filterMap = this.state.search;
+    if (data.order) {
+      data.sortMap = { [data.order]: data.direction };
+    }
+    if (data.timerange) {
+      data.filterMap.timerange = data.timerange;
+    }
+    const [startTime, endTime] = timeRange(data.timerange);
+    refreshQuestionType(this, data.subject, data.questionType, { all: true, needSentence: true, allSubject: true })
+      .then(({ questionTypes }) => {
+        return refreshStruct(this, data.tab, data.one, data.two, {
+          all: true, needPreview: false, needTextbook: true,
+        })
+          .then(({ structIds, latest, year }) => {
+            My.listQuestionAsk(Object.assign({ module: data.tab, questionTypes, structIds, latest, year, startTime, endTime }, this.state.search, {
+              order: Object.keys(data.sortMap).map(key => {
+                if (key === 'title') return 'pid desc, no desc';
+                return `${key} ${data.sortMap[key]}`;
+              }).join(','),
+            })).then(result => {
+              this.setState({ list: result.list, total: result.total });
+            });
+          });
+      });
+  }
+
+  onTabChange(tab) {
+    const data = { tab };
+    this.refreshQuery(data);
   }
 
   onFilter(value) {
-    this.setState({ filterMap: value });
+    this.search(value);
   }
 
   onSort(value) {
-    this.setState({ sortMap: value });
+    const keys = Object.keys(value);
+    // this.search({ order: keys.length ? keys.join('|') : null, direction: keys.length ? Object.values(value).join('|') : null });
+    const { sortMap } = this.state;
+    const index = keys.length > 1 && sortMap[keys[0]] ? 1 : 0;
+    this.search({ order: keys.length ? keys[index] : null, direction: keys.length ? value[keys[index]] : null });
   }
 
-  onDataChange(page) {
-    this.setState({ page, allChecked: false, selectList: [] });
+  onChangePage(page) {
+    this.search({ page });
   }
 
   onAll(checked) {
@@ -79,7 +119,7 @@ export default class extends Page {
     }
   }
 
-  onAction() {}
+  onAction() { }
 
   onSelect(selectList) {
     this.setState({ selectList });
@@ -91,7 +131,8 @@ export default class extends Page {
   }
 
   renderTable() {
-    const { tab, filterMap = {}, sortMap = {}, selectList = [], data = [], allChecked, page, total } = this.state;
+    const { tab, questionSubjectSelect, questionSubjectMap = {}, oneSelect, twoSelectMap = {}, filterMap = {}, sortMap = {}, list = [] } = this.state;
+    const { selectList = [], allChecked, page, total } = this.state;
     return (
       <div className="table-layout">
         <Tabs
@@ -102,36 +143,39 @@ export default class extends Page {
           space={2.5}
           width={100}
           active={tab}
-          tabs={[{ key: '1', title: '练习' }, { key: '2', title: '模考' }]}
-          onChange={key => this.onChangeTab(key)}
+          tabs={[{ key: 'exercise', title: '练习' }, { key: 'examination', title: '模考' }]}
+          onChange={key => this.onTabChange(key)}
         />
         <UserAction
           search
-          selectList={[
-            {
-              label: '123',
+          selectList={[{
+            children: [{
+              key: 'subject',
+              placeholder: '学科',
+              select: questionSubjectSelect,
+            }, {
+              placeholder: '题型',
+              key: 'questionType',
+              be: 'subject',
+              selectMap: questionSubjectMap,
+            }],
+          }, {
+            label: '范围',
+            children: [{
               key: 'one',
-              select: [{ title: '123', key: '1' }, { title: '123', key: '2' }, { title: '123', key: '2' }],
-            },
-            {
-              label: '123',
-              children: [
-                {
-                  key: '3',
-                  default: '1',
-                  select: [{ title: '123', key: '1' }, { title: '123', key: '2' }, { title: '123', key: '2' }],
-                },
-                {
-                  key: 'two',
-                  be: 'one',
-                  placeholder: '全部',
-                  selectMap: {
-                    1: [{ title: '123', key: '1' }, { title: '123', key: '2' }, { title: '123', key: '2' }],
-                  },
-                },
-              ],
-            },
-          ]}
+              placeholder: '全部',
+              select: oneSelect,
+            }, {
+              key: 'two',
+              be: 'one',
+              placeholder: '全部',
+              selectMap: twoSelectMap,
+            }],
+          }, {
+            right: true,
+            key: 'timerange',
+            select: TimeRange,
+          }]}
           filterMap={filterMap}
           onFilter={value => this.onFilter(value)}
         />
@@ -157,13 +201,13 @@ export default class extends Page {
           select
           columns={columns}
           sortMap={sortMap}
-          data={data}
+          data={list}
           current={page}
           total={total}
           selectList={selectList}
           onSelect={l => this.onSelect(l)}
           onSort={v => this.onSort(v)}
-          onChange={p => this.onDataChange(p)}
+          onChange={p => this.onChangePage(p)}
         />
         {this.renderModal()}
       </div>
@@ -172,7 +216,7 @@ export default class extends Page {
 
   renderModal() {
     return [
-      <Modal title="操作提示" confirmText="好的,知道了" btnAlign="center" onConfirm={() => {}}>
+      <Modal title="操作提示" confirmText="好的,知道了" btnAlign="center" onConfirm={() => { }}>
         <div className="flex-layout m-b-2">
           <div className="flex-block">
             <div className="t-1 t-s-18">组卷功能</div>
@@ -192,10 +236,10 @@ export default class extends Page {
           查阅以上信息。
         </div>
       </Modal>,
-      <Modal title="组卷练习" confirmText="好的,知道了" btnAlign="center" onConfirm={() => {}}>
+      <Modal title="组卷练习" confirmText="好的,知道了" btnAlign="center" onConfirm={() => { }}>
         <div className="t-2 t-s-18">不可同时选中语文题和数学题,请重新选择。</div>
       </Modal>,
-      <Modal title="组卷练习" confirmText="开始练习" onConfirm={() => {}} onCancel={() => {}}>
+      <Modal title="组卷练习" confirmText="开始练习" onConfirm={() => { }} onCancel={() => { }}>
         <div className="t-2 t-s-18">
           您共选择了 <span className="t-4">50</span> 道题目,是否开始练习?
         </div>
@@ -209,15 +253,15 @@ export default class extends Page {
           次以上的错题
         </div>
       </Modal>,
-      <Modal title="移出" onConfirm={() => {}} onCancel={() => {}}>
+      <Modal title="移出" onConfirm={() => { }} onCancel={() => { }}>
         <div className="t-2 t-s-18">
           当前选中的 <span className="t-4">50</span> 道题即将被移出错题本,移出后无法恢复,是否继续?
         </div>
       </Modal>,
-      <Modal title="导出" confirmText="好的,知道了" btnAlign="center" onConfirm={() => {}}>
+      <Modal title="导出" confirmText="好的,知道了" btnAlign="center" onConfirm={() => { }}>
         <div className="t-2 t-s-18">正在下载中,请不要关闭当前页面!</div>
       </Modal>,
-      <Modal title="导出" confirmText="导出" onConfirm={() => {}} onCancel={() => {}}>
+      <Modal title="导出" confirmText="导出" onConfirm={() => { }} onCancel={() => { }}>
         <div className="t-2 t-s-18 m-b-5">
           当前共选中 <span className="t-4">50</span> 道题,请确认需要导出的内容:
         </div>
@@ -232,7 +276,7 @@ export default class extends Page {
           })}
         </div>
       </Modal>,
-      <Modal title="导出" confirmText="导出" onConfirm={() => {}} onCancel={() => {}}>
+      <Modal title="导出" confirmText="导出" onConfirm={() => { }} onCancel={() => { }}>
         <div className="t-2 t-s-18 m-b-5">
           当前共选中 <span className="t-4">50</span> 道题,请确认需要导出的内容:
         </div>

+ 196 - 0
front/project/www/routes/my/index.js

@@ -1,3 +1,4 @@
+import { getMap, formatTreeData } from '@src/services/Tools';
 import main from './main';
 import course from './course';
 import tools from './tools';
@@ -10,4 +11,199 @@ import collect from './collect';
 import order from './order';
 import message from './message';
 
+import { TextbookMinYear, CourseModule } from '../../../Constant';
+import { Main } from '../../stores/main';
+
 export default [main, course, tools, error, note, answer, report, data, collect, order, message];
+
+
+export function refreshQuestionType(compontent, subject, questionType, { all, needSentence, allSubject }) {
+  return Main.getExercise().then(result => {
+    const list = result.filter(row => (needSentence ? true : row.isExamination)).map(row => {
+      row.title = `${row.titleZh}${row.titleEn}`;
+      row.key = row.extend;
+      return row;
+    });
+    const tree = formatTreeData(list, 'id', 'title', 'parentId');
+    compontent.questionSubjectMap = getMap(tree, 'key', 'children');
+    compontent.questionSubjectSelect = tree.filter(row => row.level === 1 && (allSubject ? true : row.children.length > 1));
+    if (all) {
+      compontent.questionSubjectSelect.forEach(row => {
+        row.children.unshift({
+          title: '全部',
+          key: '',
+        });
+      });
+      compontent.questionSubjectSelect.unshift({
+        title: '全部',
+        key: '',
+      });
+    }
+    compontent.setState({
+      questionSubjectSelect: compontent.questionSubjectSelect,
+      questionSubjectMap: compontent.questionSubjectMap,
+    });
+    return {
+      questionTypes: questionType || (subject ? compontent.questionSubjectMap[subject].map(row => row.key).filter(row => row) : null),
+    };
+  });
+}
+
+export function refreshStruct(compontent, module, one, two, { all, needTextbook, needPreview }) {
+  switch (module) {
+    case 'exercise':
+      return Main.getExerciseAll().then(result => {
+        const tmp = result.filter(row => row.level > 2).map(row => {
+          row.title = `${row.titleZh}`;
+          row.key = row.titleEn;
+          return row;
+        });
+        const idsMap = getMap(tmp, 'id', 'key');
+        const map = {};
+        tmp.forEach(row => {
+          if (!map[row.key]) {
+            map[row.key] = {
+              title: row.title,
+              key: row.key,
+              structIds: [],
+              parentId: row.level > 3 ? idsMap[row.parentId] : null,
+              subject: [],
+              questionType: [],
+            };
+          }
+          const item = map[row.key];
+          item.structIds.push(row.id);
+          if (item.subject.indexOf(row.subject) < 0) {
+            item.subject.push(row.subject);
+          }
+          if (item.questionType.indexOf(row.questionType) < 0) {
+            item.questionType.push(row.questionType);
+          }
+        });
+        const list = Object.values(map);
+        if (needPreview) {
+          list.push({
+            title: '预习作业',
+            key: 'preview',
+            id: 'preview',
+          });
+          CourseModule.forEach(row => {
+            list.push({
+              title: row.label,
+              key: row.value,
+              parentId: 'preview',
+            });
+          });
+        }
+        let courseModules = null;
+        let structIds = null;
+        if (one === 'preview') {
+          if (!two) {
+            courseModules = CourseModule.map(row => row.value);
+          } else {
+            courseModules = [two];
+          }
+        } else if (one) {
+          const resultMap = getMap(list, 'key', 'structIds');
+          if (!two) {
+            structIds = resultMap[one];
+          } else {
+            structIds = resultMap[two];
+          }
+        }
+
+        const tree = formatTreeData(list, 'key', 'title', 'parentId');
+        const oneSelect = tree;
+        const twoSelectMap = getMap(tree, 'key', 'children');
+        if (all) {
+          oneSelect.forEach(row => {
+            row.children.unshift({
+              title: '全部',
+              key: '',
+            });
+          });
+          oneSelect.unshift({
+            title: '全部',
+            key: '',
+          });
+        }
+        compontent.setState({ oneSelect, twoSelectMap });
+
+        return {
+          structIds,
+          courseModules,
+        };
+      });
+    case 'examination':
+      return Main.getExamination().then(result => {
+        const list = result.map(row => {
+          row.title = `${row.titleZh}${row.titleEn}`;
+          row.key = `${row.id}`;
+          return row;
+        });
+        if (needTextbook) {
+          list.push({
+            title: '数学机经',
+            key: 'textbook',
+            id: 'textbook',
+          });
+          list.push({
+            title: '最新',
+            key: 'latest',
+            parentId: 'textbook',
+          });
+
+          const nowYear = new Date().getFullYear();
+          for (let i = TextbookMinYear; i <= nowYear; i += 1) {
+            list.push({
+              title: i.toString(),
+              key: i.toString(),
+              parentId: 'textbook',
+            });
+          }
+        }
+        let latest = null;
+        let year = null;
+        let structIds = null;
+        if (one === 'textbook') {
+          if (!two) {
+            latest = false;
+          } else if (two === 'latest') {
+            latest = true;
+          } else {
+            year = two;
+          }
+        } else if (one) {
+          if (!two) {
+            structIds = [Number(one)];
+          } else {
+            structIds = [Number(two)];
+          }
+        }
+
+        const tree = formatTreeData(list, 'key', 'title', 'parentId');
+        const oneSelect = tree;
+        const twoSelectMap = getMap(tree, 'key', 'children');
+        if (all) {
+          oneSelect.forEach(row => {
+            row.children.unshift({
+              title: '全部',
+              key: '',
+            });
+          });
+          oneSelect.unshift({
+            title: '全部',
+            key: '',
+          });
+        }
+        compontent.setState({ oneSelect, twoSelectMap });
+        return {
+          structIds,
+          latest,
+          year,
+        };
+      });
+    default:
+      return Promise.resolve({});
+  }
+}

+ 21 - 2
front/project/www/routes/my/message/page.js

@@ -16,6 +16,11 @@ const MessageTypeMap = getMap(MessageType, 'value', 'label');
 const columns = [{ title: '消息', key: 'content' }, { title: '类型', key: 'type' }, { title: '发送时间', key: 'date' }];
 
 export default class extends Page {
+  constructor(props) {
+    props.size = 15;
+    super(props);
+  }
+
   initState() {
     return {
       tab: '',
@@ -46,6 +51,7 @@ export default class extends Page {
 
         return row;
       });
+      result.page = data.page;
       this.setState(result);
     });
   }
@@ -59,6 +65,10 @@ export default class extends Page {
     this.refreshQuery(data);
   }
 
+  onChangePage(page) {
+    this.search({ page });
+  }
+
   readAllMessage() {
     My.readAllMessage().then(() => {
       asyncSMessage('操作成功');
@@ -71,7 +81,7 @@ export default class extends Page {
   }
 
   renderTable() {
-    const { tab, list, messageTypeSelect, filterMap } = this.state;
+    const { tab, list, messageTypeSelect, filterMap, page, total } = this.state;
     return (
       <div className="table-layout">
         <UserAction
@@ -99,7 +109,16 @@ export default class extends Page {
           filterMap={filterMap}
           onFilter={value => this.onFilter(value)}
         />
-        <UserTable size="small" columns={columns} data={list} />
+        <UserTable
+          size="small"
+          columns={columns}
+          data={list}
+          current={page}
+          total={total}
+          onChange={(value) => {
+            this.onChangePage(value);
+          }}
+        />
       </div>
     );
   }

+ 88 - 37
front/project/www/routes/my/note/page.js

@@ -2,11 +2,15 @@ import React from 'react';
 import { Link } from 'react-router-dom';
 import './index.less';
 import Page from '@src/containers/Page';
+import { timeRange } from '@src/services/Tools';
 import UserLayout from '../../../layouts/User';
 import UserTable from '../../../components/UserTable';
 import UserAction from '../../../components/UserAction';
-import menu from '../index';
+import UserPagination from '../../../components/UserPagination';
+import menu, { refreshQuestionType, refreshStruct } from '../index';
 import Tabs from '../../../components/Tabs';
+import { TimeRange } from '../../../../Constant';
+import { My } from '../../../stores/my';
 
 const columns = [
   {
@@ -32,11 +36,10 @@ const columns = [
         <div className="group">
           <Link to="">{text}</Link>
         </div>
-      ) : (
-        <div className="sub">
-          <div className="date">{text.split(' ')[0]}</div>
-          <div className="date">{text.split(' ')[1]}</div>
-        </div>
+      ) : (<div className="sub">
+        <div className="date">{text.split(' ')[0]}</div>
+        <div className="date">{text.split(' ')[1]}</div>
+      </div>
       );
     },
   },
@@ -57,6 +60,11 @@ const columns = [
 ];
 
 export default class extends Page {
+  constructor(props) {
+    props.size = 10;
+    super(props);
+  }
+
   initState() {
     return {
       filterMap: {},
@@ -105,26 +113,57 @@ export default class extends Page {
       ],
       selectList: [],
       allChecked: false,
-      tab: '1',
-      page: 1,
-      total: 1,
+      tab: 'exercise',
+      timerange: 'today',
     };
   }
 
+  initData() {
+    const data = Object.assign(this.state, this.state.search);
+    data.filterMap = this.state.search;
+    if (data.order) {
+      data.sortMap = { [data.order]: data.direction };
+    }
+    if (data.timerange) {
+      data.filterMap.timerange = data.timerange;
+    }
+    const [startTime, endTime] = timeRange(data.timerange);
+    refreshQuestionType(this, data.subject, data.questionType, { all: true, needSentence: false, allSubject: true })
+      .then(({ questionTypes }) => {
+        return refreshStruct(this, data.tab, data.one, data.two, {
+          all: true, needPreview: false, needTextbook: false,
+        })
+          .then(({ structIds, latest, year }) => {
+            My.listQuestionNote(Object.assign({ module: data.tab, questionTypes, structIds, latest, year, startTime, endTime }, this.state.search, {
+              order: Object.keys(data.sortMap).map(key => {
+                return `${key} ${data.sortMap[key]}`;
+              }).join(','),
+            })).then(result => {
+              this.setState({ list: result.list, total: result.total, page: data.page });
+            });
+          });
+      });
+  }
+
   onTabChange(tab) {
-    this.setState({ tab });
+    const data = { tab };
+    this.refreshQuery(data);
   }
 
   onFilter(value) {
-    this.setState({ filterMap: value });
+    this.search(value);
   }
 
   onSort(value) {
-    this.setState({ sortMap: value });
+    const keys = Object.keys(value);
+    // this.search({ order: keys.length ? keys.join('|') : null, direction: keys.length ? Object.values(value).join('|') : null });
+    const { sortMap } = this.state;
+    const index = keys.length > 1 && sortMap[keys[0]] ? 1 : 0;
+    this.search({ order: keys.length ? keys[index] : null, direction: keys.length ? value[keys[index]] : null });
   }
 
-  onDataChange(page) {
-    this.setState({ page, allChecked: false, selectList: [] });
+  onChangePage(page) {
+    this.search({ page });
   }
 
   onAll(checked) {
@@ -140,7 +179,7 @@ export default class extends Page {
     }
   }
 
-  onAction() {}
+  onAction() { }
 
   onSelect(selectList) {
     this.setState({ selectList });
@@ -152,7 +191,8 @@ export default class extends Page {
   }
 
   renderTable() {
-    const { tab, filterMap = {}, sortMap = {}, selectList = [], data = [], allChecked } = this.state;
+    const { tab, questionSubjectSelect, questionSubjectMap = {}, oneSelect, twoSelectMap = {}, filterMap = {}, sortMap = {}, list = [] } = this.state;
+    const { selectList = [], allChecked, page, total } = this.state;
     return (
       <div className="table-layout">
         <Tabs
@@ -163,29 +203,39 @@ export default class extends Page {
           space={2.5}
           width={100}
           active={tab}
-          tabs={[{ key: '1', title: '练习' }, { key: '2', title: '模考' }]}
+          tabs={[{ key: 'exercise', title: '练习' }, { key: 'examination', title: '模考' }]}
           onChange={key => this.onTabChange(key)}
         />
         <UserAction
           search
-          selectList={[
-            {
-              label: '123',
-              children: [
-                {
-                  key: 'one',
-                  default: '1',
-                  select: [{ title: '123', key: '1' }, { title: '123', key: '2' }, { title: '123', key: '2' }],
-                },
-                {
-                  key: 'two',
-                  be: 'one',
-                  placeholder: '全部',
-                  selectMap: [{ title: '123', key: '1' }, { title: '123', key: '2' }, { title: '123', key: '2' }],
-                },
-              ],
-            },
-          ]}
+          selectList={[{
+            children: [{
+              key: 'subject',
+              placeholder: '学科',
+              select: questionSubjectSelect,
+            }, {
+              placeholder: '题型',
+              key: 'questionType',
+              be: 'subject',
+              selectMap: questionSubjectMap,
+            }],
+          }, {
+            label: '范围',
+            children: [{
+              key: 'one',
+              placeholder: '全部',
+              select: oneSelect,
+            }, {
+              key: 'two',
+              be: 'one',
+              placeholder: '全部',
+              selectMap: twoSelectMap,
+            }],
+          }, {
+            right: true,
+            key: 'timerange',
+            select: TimeRange,
+          }]}
           filterMap={filterMap}
           onFilter={value => this.onFilter(value)}
         />
@@ -194,13 +244,13 @@ export default class extends Page {
           allChecked={allChecked}
           help
           btnList={[{ title: '删除', key: 'remove' }, { title: '导出', key: 'export', tag: 'vip' }]}
-          sortList={[{ right: true, label: '更新时间', key: '1' }]}
+          sortList={[{ right: true, label: '更新时间', key: 'update_time' }]}
           sortMap={sortMap}
           onSort={value => this.onSort(value)}
           onAll={checked => this.onAll(checked)}
           onAction={key => this.onAction(key)}
         />
-        {data.map((item, index) => {
+        {list.map((item, index) => {
           return (
             <div className="group">
               <UserTable
@@ -226,6 +276,7 @@ export default class extends Page {
             </div>
           );
         })}
+        {total && list.length > 0 && <UserPagination total={total} current={page} onChange={(p) => this.onChangePage(p)} />}
       </div>
     );
   }

+ 72 - 14
front/project/www/routes/my/order/page.js

@@ -1,27 +1,78 @@
 import React from 'react';
 import './index.less';
 import Page from '@src/containers/Page';
+import { getMap, formatMoney, formatDate } from '@src/services/Tools';
 import UserLayout from '../../../layouts/User';
 import menu from '../index';
 import UserTable from '../../../components/UserTable';
+import { Order } from '../../../stores/order';
+import { RecordSource, ServiceKey } from '../../../../Constant';
 
+const RecordSourceMap = getMap(RecordSource, 'value', 'label');
+const ServiceKeyMap = getMap(ServiceKey, 'value', 'label');
 const columns = [
-  { title: '订单编号', key: 'no' },
-  { title: '服务', key: 'title' },
-  { title: '购买时间', key: 'date' },
-  { title: '付款方式', key: 'method' },
-  { title: '付款金额', key: 'money' },
+  { title: '订单编号', key: 'orderId' },
+  {
+    title: '服务',
+    key: 'title',
+    render: (text, record) => {
+      if (record.productType === 'course-package') {
+        return (record.coursePackage || {}).title;
+      }
+      if (record.productType === 'course') {
+        return (record.course || {}).title;
+      }
+      if (record.productType === 'data') {
+        return (record.data || {}).title;
+      }
+      if (record.productType === 'service') {
+        return `${ServiceKeyMap[text]}`;
+      }
+      return '';
+    },
+  },
+  {
+    title: '购买时间',
+    key: 'createTime',
+    render: (text) => {
+      return formatDate(text, 'YYYY-MM-DD\nHH:mm:ss');
+    },
+  },
+  {
+    title: '付款方式',
+    key: 'source',
+    render: (text) => {
+      return RecordSourceMap[text];
+    },
+  },
+  {
+    title: '付款金额',
+    key: 'money',
+    render: (text) => {
+      return formatMoney(text);
+    },
+  },
 ];
 
 export default class extends Page {
+  constructor(props) {
+    props.size = 15;
+    super(props);
+  }
+
   initState() {
-    return {
-      data: [
-        { no: '-', title: 'VIP会员', date: '2019-07-12 \n 11:38:51', method: '实名认证赠送', money: 0 },
-        { no: '12930219321321321', title: '教学资料-01', date: '2019-07-12 \n 11:38:51', method: '支付宝', money: 200 },
-        { no: '12930219321321321', title: '千行课程', date: '2019-07-12 \n 11:38:51', method: '银行付款', money: 0 },
-      ],
-    };
+    return {};
+  }
+
+  initData() {
+    Order.list(this.state.search)
+      .then(result => {
+        this.setState({ list: result.list, total: result.total, page: this.state.search.page });
+      });
+  }
+
+  onChangePage(page) {
+    this.search({ page });
   }
 
   renderView() {
@@ -30,10 +81,17 @@ export default class extends Page {
   }
 
   renderTable() {
-    const { data } = this.state;
+    const { list, total, page } = this.state;
     return (
       <div className="table-layout">
-        <UserTable size="small" columns={columns} data={data} />
+        <UserTable
+          size="small"
+          columns={columns}
+          data={list}
+          onChange={p => this.onChangePage(p)}
+          total={total}
+          current={page}
+        />
       </div>
     );
   }

+ 111 - 61
front/project/www/routes/my/report/page.js

@@ -1,23 +1,25 @@
 import React from 'react';
 import './index.less';
 import Page from '@src/containers/Page';
+import { timeRange } from '@src/services/Tools';
 import UserLayout from '../../../layouts/User';
-import Tabs from '../../../components/Tabs';
 import UserAction from '../../../components/UserAction';
 import UserTable from '../../../components/UserTable';
 import IconButton from '../../../components/IconButton';
-import menu from '../index';
+import menu, { refreshQuestionType, refreshStruct } from '../index';
+import Tabs from '../../../components/Tabs';
+import { TimeRange } from '../../../../Constant';
+import { My } from '../../../stores/my';
 
 const columns = [
-  { key: '', title: '练习册名称', fixSort: true },
-  { key: '', title: '做题时间', fixSort: true },
-  { key: '', title: '正确率', sort: true },
-  { key: '', title: '平均耗时', sort: true },
-  { key: '', title: '完成度' },
-  { key: '', title: '报告' },
+  { key: 'title', title: '练习册名称', fixSort: true },
+  { key: 'create_time', title: '做题时间', fixSort: true },
+  { key: 'correct', title: '正确率', sort: true },
+  { key: 'time', title: '平均耗时', sort: true },
+  { key: 'progress', title: '完成度' },
   {
-    key: '',
-    title: '',
+    key: 'report',
+    title: '报告',
     render() {
       return <IconButton type="report" tip="report" />;
     },
@@ -25,25 +27,84 @@ const columns = [
 ];
 
 export default class extends Page {
+  constructor(props) {
+    props.size = 10;
+    super(props);
+  }
+
   initState() {
     return {
-      tab: '1',
+      tab: 'exercise',
+      timerange: 'today',
       filterMap: {},
       sortMap: {},
       data: [{}, {}],
       selectList: [],
       allChecked: false,
-      page: 1,
-      total: 1,
     };
   }
 
-  onChangeTab(tab) {
-    this.setState({ tab });
+  initData() {
+    const data = Object.assign(this.state, this.state.search);
+    data.filterMap = this.state.search;
+    if (data.order) {
+      data.sortMap = { [data.order]: data.direction };
+    }
+    if (data.timerange) {
+      data.filterMap.timerange = data.timerange;
+    }
+    const [startTime, endTime] = timeRange(data.timerange);
+    switch (data.tab) {
+      case 'error':
+      case 'collect':
+        My.listReport(Object.assign({ origin: data.tab, startTime, endTime }, this.state.search, {
+          order: Object.keys(data.sortMap).map(key => {
+            return `${key} ${data.sortMap[key]}`;
+          }).join(','),
+        })).then(result => {
+          this.setState({ list: result.list, total: result.total, page: data.page });
+        });
+        break;
+      case 'exercise':
+      case 'examination':
+      default:
+        refreshQuestionType(this, data.subject, data.questionType, { all: true, needSentence: true, allSubject: true })
+          .then(({ questionTypes, courseModules }) => {
+            return refreshStruct(this, data.tab, data.one, data.two, {
+              all: true, needPreview: true, needTextbook: true,
+            })
+              .then(({ structIds, latest, year }) => {
+                My.listReport(Object.assign({ module: data.tab, questionTypes, structIds, latest, year, courseModules, startTime, endTime }, this.state.search, {
+                  order: Object.keys(data.sortMap).map(key => {
+                    return `${key} ${data.sortMap[key]}`;
+                  }).join(','),
+                })).then(result => {
+                  this.setState({ list: result.list, total: result.total, page: data.page });
+                });
+              });
+          });
+    }
+  }
+
+  onTabChange(tab) {
+    const data = { tab };
+    this.refreshQuery(data);
   }
 
   onFilter(value) {
-    this.setState({ filterMap: value });
+    this.search(value);
+  }
+
+  onSort(value) {
+    const keys = Object.keys(value);
+    // this.search({ order: keys.length ? keys.join('|') : null, direction: keys.length ? Object.values(value).join('|') : null });
+    const { sortMap } = this.state;
+    const index = keys.length > 1 && sortMap[keys[0]] ? 1 : 0;
+    this.search({ order: keys.length ? keys[index] : null, direction: keys.length ? value[keys[index]] : null });
+  }
+
+  onChangePage(page) {
+    this.search({ page });
   }
 
   renderView() {
@@ -52,7 +113,8 @@ export default class extends Page {
   }
 
   renderTable() {
-    const { tab, filterMap = {}, sortMap = {}, selectList = [], data = [], page, total } = this.state;
+    const { tab, questionSubjectSelect, questionSubjectMap = {}, oneSelect, twoSelectMap = {}, filterMap = {}, sortMap = {}, list = [] } = this.state;
+    const { selectList = [], page, total } = this.state;
     return (
       <div className="table-layout">
         <Tabs
@@ -63,51 +125,39 @@ export default class extends Page {
           space={2.5}
           width={100}
           active={tab}
-          tabs={[
-            { key: '1', title: '练习' },
-            { key: '2', title: '模考' },
-            { key: '3', title: '错题组卷' },
-            { key: '4', title: '收藏组卷' },
-          ]}
-          onChange={key => this.onChangeTab(key)}
+          tabs={[{ key: 'exercise', title: '练习' }, { key: 'examination', title: '模考' }, { key: 'error', title: '错误组卷' }, { key: 'collect', title: '收藏组卷' }]}
+          onChange={key => this.onTabChange(key)}
         />
         <UserAction
           search
-          selectList={[
-            {
-              label: '123',
-              children: [
-                {
-                  key: 'one',
-                  default: '1',
-                  select: [{ title: '123', key: '1' }, { title: '123', key: '2' }, { title: '123', key: '2' }],
-                },
-                {
-                  key: 'two',
-                  be: 'one',
-                  placeholder: '全部',
-                  selectMap: [{ title: '123', key: '1' }, { title: '123', key: '2' }, { title: '123', key: '2' }],
-                },
-              ],
-            },
-            {
-              label: '123',
-              right: true,
-              children: [
-                {
-                  key: 'one',
-                  default: '1',
-                  select: [{ title: '123', key: '1' }, { title: '123', key: '2' }, { title: '123', key: '2' }],
-                },
-                {
-                  key: 'two',
-                  be: 'one',
-                  placeholder: '全部',
-                  selectMap: [{ title: '123', key: '1' }, { title: '123', key: '2' }, { title: '123', key: '2' }],
-                },
-              ],
-            },
-          ]}
+          selectList={[{
+            children: [{
+              key: 'subject',
+              placeholder: '学科',
+              select: questionSubjectSelect,
+            }, {
+              placeholder: '题型',
+              key: 'questionType',
+              be: 'subject',
+              selectMap: questionSubjectMap,
+            }],
+          }, {
+            label: '范围',
+            children: [{
+              key: 'one',
+              placeholder: '全部',
+              select: oneSelect,
+            }, {
+              key: 'two',
+              be: 'one',
+              placeholder: '全部',
+              selectMap: twoSelectMap,
+            }],
+          }, {
+            right: true,
+            key: 'timerange',
+            select: TimeRange,
+          }]}
           filterMap={filterMap}
           onFilter={value => this.onFilter(value)}
           onChange={key => this.onChangeTab(key)}
@@ -115,13 +165,13 @@ export default class extends Page {
         <UserTable
           columns={columns}
           sortMap={sortMap}
-          data={data}
+          data={list}
           current={page}
           total={total}
           selectList={selectList}
           onSelect={l => this.onSelect(l)}
           onSort={v => this.onSort(v)}
-          onChange={p => this.onDataChange(p)}
+          onChange={p => this.onChangePage(p)}
         />
       </div>
     );

+ 22 - 13
front/project/www/routes/my/tools/page.js

@@ -4,7 +4,7 @@ import { Icon } from 'antd';
 import Page from '@src/containers/Page';
 import Assets from '@src/components/Assets';
 import { asyncSMessage } from '@src/services/AsyncTools';
-import { formatDate } from '@src/services/Tools';
+import { formatDate, getMap } from '@src/services/Tools';
 import UserLayout from '../../../layouts/User';
 import UserAction from '../../../components/UserAction';
 import menu from '../index';
@@ -15,14 +15,17 @@ import Switch from '../../../components/Switch';
 import TotalSort from '../../../components/TotalSort';
 import Modal from '../../../components/Modal';
 import UserTable from '../../../components/UserTable';
+import UserPagination from '../../../components/UserPagination';
 import { My } from '../../../stores/my';
 import { User } from '../../../stores/user';
 import { Order } from '../../../stores/order';
 import { Textbook } from '../../../stores/textbook';
-import { DataType } from '../../../../Constant';
+import { DataType, ServiceKey } from '../../../../Constant';
 import { Main } from '../../../stores/main';
 import { Question } from '../../../stores/question';
 
+const ServiceKeyMap = getMap(ServiceKey, 'value', 'label');
+
 const dataHistoryColumns = [
   { title: '更新时间', key: 'time', width: 120 },
   { title: '位置', key: 'position', width: 120 },
@@ -33,8 +36,8 @@ const dataHistoryColumns = [
 
 const textbookHistoryColumns = [
   { title: '更新时间', key: 'createTime', width: 120 },
-  { title: '版本', key: 'version', width: 90 },
-  { title: '内容', key: 'content', width: 360 },
+  { title: '版本', key: 'version', width: 120 },
+  { title: '更新内容', key: 'content', width: 330 },
 ];
 
 const openColumns = [
@@ -110,7 +113,8 @@ export default class extends Page {
             row.createTime = formatDate(row.createTime, 'YYYY-MM-DD\nHH:mm:ss');
             return row;
           }),
-          updateTotal: result.length,
+          // 不显示分页
+          updateTotal: 0,
           updatePage: page,
           updateData: { page, size, subject, columns: textbookHistoryColumns, type: 'textbook' },
         });
@@ -133,11 +137,12 @@ export default class extends Page {
     });
   }
 
-  recordList({ page, size, service, isUse }) {
-    Order.listRecord({ page, size, productType: 'service', service, isUse })
+  recordList({ page, size, service, isUse, isExpire }) {
+    Order.listRecord({ page, size, productType: 'service', service, isUse, isExpire })
       .then(result => {
         this.setState({
           updateList: result.map(row => {
+            row.title = ServiceKeyMap[service];
             row.handler = <a onClick={() => {
               this.open(row.id);
             }}>立即开通</a>;
@@ -193,6 +198,7 @@ export default class extends Page {
             return row;
           }),
           total: result.total,
+          page: this.state.search.page,
         });
       });
   }
@@ -213,8 +219,10 @@ export default class extends Page {
   }
 
   onSort(value) {
+    const { sortMap } = this.state;
     const keys = Object.keys(value);
-    this.search({ order: keys.length ? keys[0] : null, direction: keys.length ? value[keys[0]] : null });
+    const index = keys.length > 1 && sortMap[keys[0]] ? 1 : 0;
+    this.search({ order: keys.length ? keys[index] : null, direction: keys.length ? value[keys[index]] : null });
   }
 
   onTabChange(tab) {
@@ -298,7 +306,7 @@ export default class extends Page {
             columns={updateData.columns}
             data={updateList}
             current={updateData.page}
-            onChangePage={(page) => {
+            onChange={(page) => {
               updateData.page = page;
               if (updateData.type === 'data') {
                 this.dataHistory(updateData);
@@ -329,7 +337,7 @@ export default class extends Page {
   }
 
   renderTabdata() {
-    const { list = [], filterMap = {}, sortMap = {}, structs, dataTypeSelect } = this.state;
+    const { list = [], filterMap = {}, sortMap = {}, structs, dataTypeSelect, total, page } = this.state;
     const { info } = this.props.user;
     return (
       <div className="tab-1-layout">
@@ -397,6 +405,7 @@ export default class extends Page {
             );
           })}
         </div>
+        {total && list.length > 0 && <UserPagination total={total} current={page} onChange={(p) => this.onChangePage(p)} />}
       </div>
     );
   }
@@ -417,7 +426,7 @@ export default class extends Page {
             </div>
           }
           right={!data.hasService && data.unUseRecord && <div className="email" onClick={() => {
-            this.recordList({ page: 1, size: 10, service: 'textbook', isUse: false });
+            this.recordList({ page: 1, size: 10, service: 'textbook', isUse: false, isExpire: false });
           }}>待开通</div>}
         />
         {data.hasService && <div className="data-layout">
@@ -474,8 +483,8 @@ export default class extends Page {
     const { data = {}, service } = this.state;
     return (
       <div className="tab-3-layout">
-        <UserAction right={data.unUseRecord && <div className="email" onClick={() => {
-          this.recordList({ page: 1, size: 10, service: 'qx_cat', isUse: false });
+        <UserAction right={!data.hasService && data.unUseRecord && <div className="email" onClick={() => {
+          this.recordList({ page: 1, size: 10, service: 'qx_cat', isUse: false, isExpire: false });
         }}>待开通</div>} />
         {!data.hasService && !data.unUseRecord && !data.expireTime && <div className="tip-layout">
           <div className="t1">未购买</div>

+ 10 - 13
front/project/www/stores/my.js

@@ -181,10 +181,9 @@ export default class MyStore extends BaseStore {
    * @param {*} startTime
    * @param {*} endTime
    * @param {*} order
-   * @param {*} direction
    */
-  listQuestionCollect({ keyword, module, questionTypes, structIds, latest, year, page, size, startTime, endTime, order, direction }) {
-    return this.apiGet('/my/collect/question/list', { keyword, module, questionTypes, structIds, latest, year, page, size, startTime, endTime, order, direction });
+  listQuestionCollect({ keyword, module, questionTypes, structIds, latest, year, page, size, startTime, endTime, order }) {
+    return this.apiGet('/my/collect/question/list', { keyword, module, questionTypes, structIds, latest, year, page, size, startTime, endTime, order });
   }
 
   /**
@@ -193,8 +192,8 @@ export default class MyStore extends BaseStore {
    * @param {*} page
    * @param {*} size
    */
-  listError({ keyword, module, questionTypes, structIds, latest, year, page, size, startTime, endTime, order, direction }) {
-    return this.apiGet('/my/error/list', { keyword, module, questionTypes, structIds, latest, year, page, size, startTime, endTime, order, direction });
+  listError({ keyword, module, questionTypes, structIds, latest, year, page, size, startTime, endTime, order }) {
+    return this.apiGet('/my/error/list', { keyword, module, questionTypes, structIds, latest, year, page, size, startTime, endTime, order });
   }
 
   /**
@@ -266,10 +265,9 @@ export default class MyStore extends BaseStore {
    * @param {*} startTime
    * @param {*} endTime
    * @param {*} order
-   * @param {*} direction
    */
-  questionNoteList({ keyword, module, questionTypes, structIds, latest, year, page, size, startTime, endTime, order, direction }) {
-    return this.apiGet('/my/note/question/list', { keyword, module, questionTypes, structIds, latest, year, page, size, startTime, endTime, order, direction });
+  listQuestionNote({ keyword, module, questionTypes, structIds, latest, year, page, size, startTime, endTime, order }) {
+    return this.apiGet('/my/note/question/list', { keyword, module, questionTypes, structIds, latest, year, page, size, startTime, endTime, order });
   }
 
   /**
@@ -281,10 +279,9 @@ export default class MyStore extends BaseStore {
    * @param {*} startTime
    * @param {*} endTime
    * @param {*} order
-   * @param {*} direction
    */
-  reportList({ keyword, module, origin, questionTypes, structIds, latest, year, page, size, startTime, endTime, order, direction }) {
-    return this.apiGet('/my/report/list', { keyword, module, origin, questionTypes, structIds, latest, year, page, size, startTime, endTime, order, direction });
+  listReport({ keyword, module, origin, questionTypes, structIds, latest, year, courseModules, page, size, startTime, endTime, order }) {
+    return this.apiGet('/my/report/list', { keyword, module, origin, questionTypes, structIds, latest, year, courseModules, page, size, startTime, endTime, order });
   }
 
   /**
@@ -307,8 +304,8 @@ export default class MyStore extends BaseStore {
     return this.apiDel('/my/ask/question/delete', { id });
   }
 
-  listQuestionAsk({ keyword, module, questionTypes, structIds, latest, year, askStatus, page, size, startTime, endTime, order, direction }) {
-    return this.apiGet('/my/ask/question/list', { keyword, module, questionTypes, structIds, latest, year, askStatus, page, size, startTime, endTime, order, direction });
+  listQuestionAsk({ keyword, module, questionTypes, structIds, latest, year, askStatus, page, size, startTime, endTime, order }) {
+    return this.apiGet('/my/ask/question/list', { keyword, module, questionTypes, structIds, latest, year, askStatus, page, size, startTime, endTime, order });
   }
 
   /**

+ 12 - 4
front/project/www/stores/order.js

@@ -38,12 +38,20 @@ export default class OrderStore extends BaseStore {
     return this.apiGet('/order/pay/query', { orderId });
   }
 
+  list({ page, size }) {
+    return this.apiGet('/order/list', { page, size });
+  }
+
+  getOrder(id) {
+    return this.apiGet('/order/detail', { id });
+  }
+
   /**
    * 获取所有已购记录
    * @param {*} param0
    */
-  listRecord({ page, size, productType, productId, service, isUsed }) {
-    return this.apiGet('/my/record/list', { page, size, productType, productId, service, isUsed });
+  listRecord({ page, size, productType, productId, service, isUsed, isExpire, filterChildren }) {
+    return this.apiGet('/order/record/list', { page, size, productType, productId, service, isUsed, isExpire, filterChildren });
   }
 
   /**
@@ -51,7 +59,7 @@ export default class OrderStore extends BaseStore {
    * @param {*} id
    */
   getRecord(id) {
-    return this.apiGet('/my/record/detail', { id });
+    return this.apiGet('/order/record/detail', { id });
   }
 
   /**
@@ -59,7 +67,7 @@ export default class OrderStore extends BaseStore {
    * @param {*} id
    */
   useRecord(id, isSubscribe) {
-    return this.apiPost('/my/record/use', { id, isSubscribe });
+    return this.apiPost('/order/record/use', { id, isSubscribe });
   }
 }
 

+ 3 - 3
front/src/containers/Page.js

@@ -4,20 +4,20 @@ import * as querystring from 'querystring';
 export default class extends Component {
   constructor(props) {
     super(props);
-    const { config, core, match } = this.props;
+    const { config, core, match, size } = this.props;
     this.inited = false;
     this.loaded = 0;
     this.params = match.params;
     const state = {
       page: {
         current: core.query.page ? Number(core.query.page) : 1,
-        pageSize: core.query.size ? Number(core.query.size) : 20,
+        pageSize: core.query.size ? Number(core.query.size) : size || 20,
         showQuickJumper: true,
         showSizeChanger: true,
         showTotal: total => `总数: ${total}`,
         total: 0,
       },
-      search: Object.assign({ page: 1, size: 20 }, core.query),
+      search: Object.assign({ page: 1, size: size || 20 }, core.query),
       list: [],
       data: {},
       selectedKeys: [],

+ 35 - 0
server/data/src/main/java/com/qxgmat/data/dao/entity/User.java

@@ -166,6 +166,12 @@ public class User implements Serializable {
     private Integer latestError;
 
     /**
+     * 上一次收藏组卷
+     */
+    @Column(name = "`latest_collect`")
+    private Integer latestCollect;
+
+    /**
      * 邀请用户
      */
     @Column(name = "`origin_id`")
@@ -733,6 +739,24 @@ public class User implements Serializable {
     }
 
     /**
+     * 获取上一次收藏组卷
+     *
+     * @return latest_collect - 上一次收藏组卷
+     */
+    public Integer getLatestCollect() {
+        return latestCollect;
+    }
+
+    /**
+     * 设置上一次收藏组卷
+     *
+     * @param latestCollect 上一次收藏组卷
+     */
+    public void setLatestCollect(Integer latestCollect) {
+        this.latestCollect = latestCollect;
+    }
+
+    /**
      * 获取邀请用户
      *
      * @return origin_id - 邀请用户
@@ -1031,6 +1055,7 @@ public class User implements Serializable {
         sb.append(", prepareScoreTime=").append(prepareScoreTime);
         sb.append(", latestExercise=").append(latestExercise);
         sb.append(", latestError=").append(latestError);
+        sb.append(", latestCollect=").append(latestCollect);
         sb.append(", originId=").append(originId);
         sb.append(", inviteCode=").append(inviteCode);
         sb.append(", totalMoney=").append(totalMoney);
@@ -1328,6 +1353,16 @@ public class User implements Serializable {
         }
 
         /**
+         * 设置上一次收藏组卷
+         *
+         * @param latestCollect 上一次收藏组卷
+         */
+        public Builder latestCollect(Integer latestCollect) {
+            obj.setLatestCollect(latestCollect);
+            return this;
+        }
+
+        /**
          * 设置邀请用户
          *
          * @param originId 邀请用户

+ 35 - 0
server/data/src/main/java/com/qxgmat/data/dao/entity/UserTextbookFeedback.java

@@ -24,6 +24,12 @@ public class UserTextbookFeedback implements Serializable {
     private Integer topicId;
 
     /**
+     * 学科
+     */
+    @Column(name = "`question_subject`")
+    private String questionSubject;
+
+    /**
      * 换库表
      */
     @Column(name = "`library_id`")
@@ -109,6 +115,24 @@ public class UserTextbookFeedback implements Serializable {
     }
 
     /**
+     * 获取学科
+     *
+     * @return question_subject - 学科
+     */
+    public String getQuestionSubject() {
+        return questionSubject;
+    }
+
+    /**
+     * 设置学科
+     *
+     * @param questionSubject 学科
+     */
+    public void setQuestionSubject(String questionSubject) {
+        this.questionSubject = questionSubject;
+    }
+
+    /**
      * 获取换库表
      *
      * @return library_id - 换库表
@@ -221,6 +245,7 @@ public class UserTextbookFeedback implements Serializable {
         sb.append(", id=").append(id);
         sb.append(", userId=").append(userId);
         sb.append(", topicId=").append(topicId);
+        sb.append(", questionSubject=").append(questionSubject);
         sb.append(", libraryId=").append(libraryId);
         sb.append(", target=").append(target);
         sb.append(", createTime=").append(createTime);
@@ -271,6 +296,16 @@ public class UserTextbookFeedback implements Serializable {
         }
 
         /**
+         * 设置学科
+         *
+         * @param questionSubject 学科
+         */
+        public Builder questionSubject(String questionSubject) {
+            obj.setQuestionSubject(questionSubject);
+            return this;
+        }
+
+        /**
          * 设置换库表
          *
          * @param libraryId 换库表

+ 3 - 2
server/data/src/main/java/com/qxgmat/data/dao/mapping/UserMapper.xml

@@ -32,6 +32,7 @@
     <result column="prepare_score_time" jdbcType="TIMESTAMP" property="prepareScoreTime" />
     <result column="latest_exercise" jdbcType="INTEGER" property="latestExercise" />
     <result column="latest_error" jdbcType="INTEGER" property="latestError" />
+    <result column="latest_collect" jdbcType="INTEGER" property="latestCollect" />
     <result column="origin_id" jdbcType="INTEGER" property="originId" />
     <result column="invite_code" jdbcType="VARCHAR" property="inviteCode" />
     <result column="total_money" jdbcType="DECIMAL" property="totalMoney" />
@@ -57,8 +58,8 @@
     `wechat_expire_time`, `real_time`, `real_name`, `real_address`, `real_identity`, 
     `real_photo_front`, `real_photo_back`, `real_status`, `prepare_time`, `prepare_status`, 
     `prepare_goal`, `prepare_examination_time`, `prepare_score_time`, `latest_exercise`, 
-    `latest_error`, `origin_id`, `invite_code`, `total_money`, `invite_number`, `textbook_half`, 
-    `qx_cat`, `register_ip`, `register_city`, `latest_login_ip`, `latest_login_time`, 
+    `latest_error`, `latest_collect`, `origin_id`, `invite_code`, `total_money`, `invite_number`, 
+    `textbook_half`, `qx_cat`, `register_ip`, `register_city`, `latest_login_ip`, `latest_login_time`, 
     `is_frozen`, `create_time`, `data_email_subscribe`, `textbook_email_subscribe`, `total_alert`
   </sql>
 </mapper>

+ 3 - 1
server/data/src/main/java/com/qxgmat/data/dao/mapping/UserTextbookFeedbackMapper.xml

@@ -8,6 +8,7 @@
     <id column="id" jdbcType="INTEGER" property="id" />
     <result column="user_id" jdbcType="INTEGER" property="userId" />
     <result column="topic_id" jdbcType="INTEGER" property="topicId" />
+    <result column="question_subject" jdbcType="VARCHAR" property="questionSubject" />
     <result column="library_id" jdbcType="INTEGER" property="libraryId" />
     <result column="target" jdbcType="VARCHAR" property="target" />
     <result column="create_time" jdbcType="TIMESTAMP" property="createTime" />
@@ -24,7 +25,8 @@
     <!--
       WARNING - @mbg.generated
     -->
-    `id`, `user_id`, `topic_id`, `library_id`, `target`, `create_time`, `status`, `handle_time`
+    `id`, `user_id`, `topic_id`, `question_subject`, `library_id`, `target`, `create_time`, 
+    `status`, `handle_time`
   </sql>
   <sql id="Blob_Column_List">
     <!--

+ 1 - 1
server/data/src/main/java/com/qxgmat/data/relation/UserOrderRecordRelationMapper.java

@@ -32,7 +32,7 @@ public interface UserOrderRecordRelationMapper {
 
     List<UserOrderRecord> listWithCourse(
             @Param("userId") Integer userId,
-            @Param("vsType") String courseModule,
+            @Param("courseModules") String[] modules,
             @Param("isUsed") Boolean isUsed,
             @Param("isEnd") Boolean isEnd,
             @Param("order") String order,

+ 7 - 7
server/data/src/main/java/com/qxgmat/data/relation/mapping/UserAskQuestionRelationMapper.xml

@@ -100,12 +100,12 @@
     </if>
     left join `textbook_question` tq on tq.`question_id` = q.`id`
     and (q.`question_module` = 'textbook')
-    <if test="libraryId != null">
-      and tq.`library_id` = #{libraryId,jdbcType=VARCHAR}
-    </if>
-    <if test="year != null">
-      and tq.`year` = #{year,jdbcType=VARCHAR}
-    </if>
+      <if test="libraryId != null and libraryId > 0">
+        and tq.`library_id` = #{libraryId,jdbcType=VARCHAR}
+      </if>
+      <if test="year != null">
+        and tq.`year` = #{year,jdbcType=VARCHAR}
+      </if>
     where
     q.`id` > 0 and uaq.`user_id` = #{userId,jdbcType=VARCHAR}
     <if test="keyword != null">
@@ -119,7 +119,7 @@
     <if test="structIds == null">
       and (qn.`id` > 0 or tq.`id` > 0)
     </if>
-    <if test="library != null">
+    <if test="libraryId != null">
       and (tq.`id` > 0)
     </if>
     <if test="year != null">

+ 2 - 2
server/data/src/main/java/com/qxgmat/data/relation/mapping/UserCollectQuestionRelationMapper.xml

@@ -101,7 +101,7 @@
     </if>
     left join `textbook_question` tq on tq.`question_id` = q.`id`
       and (q.`question_module` = 'textbook')
-      <if test="libraryId != null">
+      <if test="libraryId != null and libraryId > 0">
         and tq.`library_id` = #{libraryId,jdbcType=VARCHAR}
       </if>
       <if test="year != null">
@@ -120,7 +120,7 @@
     <if test="structIds == null">
       and (qn.`id` > 0 or tq.`id` > 0)
     </if>
-    <if test="library != null">
+    <if test="libraryId != null">
       and (tq.`id` > 0)
     </if>
     <if test="year != null">

+ 7 - 7
server/data/src/main/java/com/qxgmat/data/relation/mapping/UserNoteQuestionRelationMapper.xml

@@ -88,12 +88,12 @@
     </if>
     left join `textbook_question` tq on tq.`question_id` = q.`id`
     and (q.`question_module` = 'textbook')
-    <if test="libraryId != null">
-      and tq.`library_id` = #{libraryId,jdbcType=VARCHAR}
-    </if>
-    <if test="year != null">
-      and tq.`year` = #{year,jdbcType=VARCHAR}
-    </if>
+      <if test="libraryId != null and libraryId > 0">
+        and tq.`library_id` = #{libraryId,jdbcType=VARCHAR}
+      </if>
+      <if test="year != null">
+        and tq.`year` = #{year,jdbcType=VARCHAR}
+      </if>
     where
     q.`id` > 0 and unq.`user_id` = #{userId,jdbcType=VARCHAR}
     <if test="keyword != null">
@@ -107,7 +107,7 @@
     <if test="structIds == null">
       and (qn.`id` > 0 or tq.`id` > 0)
     </if>
-    <if test="library != null">
+    <if test="libraryId != null">
       and (tq.`id` > 0)
     </if>
     <if test="year != null">

+ 25 - 0
server/data/src/main/java/com/qxgmat/data/relation/mapping/UserOrderRecordRelationMapper.xml

@@ -85,4 +85,29 @@
     order by ${order} ${direction}
   </select>
 
+  <!--
+    获取用户课程
+  -->
+  <select id="listWithCourse" resultMap="IdMap">
+    select
+    <include refid="Id_Column_List" />
+    from `user_order_record` uor
+    left join `course` c on c.`id` = uor.`product_id` and uor.`product_type` = 'course'
+    <if test="courseModules != null">
+      and c.`course_module` IN
+      <foreach collection="courseModules" item="item" index="index" open="(" close=")" separator=",">
+        #{item, jdbcType=VARCHAR}
+      </foreach>
+    </if>
+    where
+    c.`id` &gt; 0
+    <if test="isUsed != null">
+      and uor.`isUsed` = #{userId,jdbcType=INTEGER}
+    </if>
+    <if test="useEndTime != null">
+      and uor.`useEndTime` &lt; #{useEndTime,jdbcType=INTEGER}
+    </if>
+    order by ${order} ${direction}
+  </select>
+
 </mapper>

+ 12 - 8
server/data/src/main/java/com/qxgmat/data/relation/mapping/UserPaperRelationMapper.xml

@@ -56,7 +56,11 @@
       <bind name="keywordLike" value="'%' + keyword + '%'" />
     </if>
     select
-    up.`id`, ur.`user_correct` / up.`question_number` as `correct`, up.`user_time` / up.`question_number` as `time`
+    up.`id`,
+    up.`title` as `title`,
+    ur.`user_correct` / up.`question_number` as `correct`,
+    up.`user_time` / up.`question_number` as `time`,
+    ur.`user_number` / ur.`question_number` as `progress`,
     from `user_paper` up
     left join `user_report` ur on ur.`id`= up.`latest_report_id`
     left join `exercise_paper` ep on up.`paper_origin` = 'exercise' and ep.`id` = up.`origin_id`
@@ -145,12 +149,12 @@
       )
     </if>
     left join `textbook_paper` tp on up.`paper_origin` = 'textbook' and tp.`id` = up.`origin_id`
-    <if test="libraryId != null">
-      and tp.`library_id` = #{libraryId,jdbcType=VARCHAR}
-    </if>
-    <if test="year != null">
-      and tp.`year` = #{year,jdbcType=VARCHAR}
-    </if>
+      <if test="libraryId != null and libraryId > 0">
+        and tp.`library_id` = #{libraryId,jdbcType=VARCHAR}
+      </if>
+      <if test="year != null">
+        and tp.`year` = #{year,jdbcType=VARCHAR}
+      </if>
     where up.`user_id` = #{userId,jdbcType=VARCHAR}
     <if test="keyword != null">
       and (ep.`title` like #{keywordLike,jdbcType=VARCHAR}
@@ -162,7 +166,7 @@
     <if test="structIds == null">
       and (ep.`id` > 0 or tp.`id` > 0)
     </if>
-    <if test="library != null">
+    <if test="libraryId != null">
       and (tp.`id` > 0)
     </if>
     <if test="year != null">

+ 2 - 2
server/data/src/main/java/com/qxgmat/data/relation/mapping/UserQuestionRelationMapper.xml

@@ -110,7 +110,7 @@
     </if>
     left join `textbook_question` tq on tq.`question_id` = q.`id`
       and (q.`question_module` = 'textbook')
-      <if test="libraryId != null">
+      <if test="libraryId != null and libraryId > 0">
         and tq.`library_id` = #{libraryId,jdbcType=VARCHAR}
       </if>
       <if test="year != null">
@@ -134,7 +134,7 @@
     <if test="structIds == null">
       and (qn.`id` > 0 or tq.`id` > 0)
     </if>
-    <if test="library != null">
+    <if test="libraryId != null">
       and (tq.`id` > 0)
     </if>
     <if test="year != null">

+ 1 - 0
server/data/src/main/resources/db/migration/V1__init_table.sql

@@ -918,6 +918,7 @@ CREATE TABLE user (
   prepare_score_time datetime DEFAULT NULL COMMENT '备考:出分时间',
   latest_exercise int(11) unsigned NOT NULL DEFAULT '0' COMMENT '上次做题',
   latest_error int(11) unsigned NOT NULL DEFAULT '0' COMMENT '上一次错题组卷',
+  latest_collect int(11) unsigned NOT NULL DEFAULT '0' COMMENT '上一次收藏组卷',
   origin_id int(11) unsigned NOT NULL DEFAULT '0' COMMENT '邀请用户',
   invite_code varchar(20) NOT NULL DEFAULT '' COMMENT '个人邀请码',
   total_money decimal(10,2) unsigned NOT NULL DEFAULT '0.00' COMMENT '消费金额',

+ 45 - 1
server/gateway-api/src/main/java/com/qxgmat/controller/api/MyController.java

@@ -421,6 +421,15 @@ public class MyController {
         return ResponseHelp.success(true);
     }
 
+    @RequestMapping(value = "/clear/collect/latest", method = RequestMethod.PUT)
+    @ApiOperation(value = "清除最后一次收藏组卷做题记录", notes = "清除最后一次收藏组卷做题记录", httpMethod = "PUT")
+    public Response<Boolean> clearLatestCollect()  {
+        User user = (User) shiroHelp.getLoginUser();
+        usersService.edit(User.builder().id(user.getId()).latestCollect(0).build());
+
+        return ResponseHelp.success(true);
+    }
+
     @RequestMapping(value = "/prepare", method = RequestMethod.PUT)
     @ApiOperation(value = "修改备考信息", notes = "修改用户备考信息", httpMethod = "PUT")
     public Response<Boolean> editPrepare(@RequestBody @Validated UserPrepareDto dto)  {
@@ -893,6 +902,21 @@ public class MyController {
         return ResponseHelp.success(userPaper);
     }
 
+    @RequestMapping(value = "/collect/question/remove", method = RequestMethod.POST)
+    @ApiOperation(value = "移除正确题", notes = "移除正确题", httpMethod = "POST")
+    public Response<Boolean> removeCollect(@RequestBody @Validated ErrorReportDto dto)  {
+        User user = (User) shiroHelp.getLoginUser();
+
+        UserReport report = userReportService.get(dto.getUserReportId());
+        if (report.getIsFinish() == 0){
+            throw new ParameterException("试卷未完成");
+        }
+        List<UserQuestion> questionList = userQuestionService.listByReport(user.getId(), dto.getUserReportId());
+        userPaperQuestionService.addRemoveError(questionList);
+
+        return ResponseHelp.success(true);
+    }
+
     @RequestMapping(value = "/collect/question/list", method = RequestMethod.GET)
     @ApiOperation(value = "获取收藏题目列表", notes = "获取收藏题目列表", httpMethod = "GET")
     public Response<PageMessage<UserCollectQuestionInfoDto>> listQuestionCollect(
@@ -920,6 +944,8 @@ public class MyController {
                     TextbookLibrary textbookLibrary = textbookLibraryService.getLatest();
                     libraryId = textbookLibrary.getId();
                     year = null;
+                }else{
+                    libraryId = 0;
                 }
             }
             p = userCollectQuestionService.listExamination(page, size, user.getId(), keyword, questionTypes, structIds, libraryId, year, startTime, endTime, order != null ? order.replace("|", " ") : null);
@@ -988,6 +1014,8 @@ public class MyController {
                     TextbookLibrary textbookLibrary = textbookLibraryService.getLatest();
                     libraryId = textbookLibrary.getId();
                     year = null;
+                }else{
+                    libraryId = 0;
                 }
             }
             p = userQuestionService.listExaminationError(page, size, user.getId(), keyword, questionTypes, structIds, libraryId, year, startTime, endTime, order != null ? order.replace("|", " ") : null);
@@ -1130,6 +1158,8 @@ public class MyController {
                     TextbookLibrary textbookLibrary = textbookLibraryService.getLatest();
                     libraryId = textbookLibrary.getId();
                     year = null;
+                }else{
+                    libraryId = 0;
                 }
             }
             p = userNoteQuestionService.listExamination(page, size, user.getId(), keyword, questionTypes, structIds, libraryId, year, startTime, endTime, order != null ? order.replace("|", " ") : null);
@@ -1205,6 +1235,8 @@ public class MyController {
                     TextbookLibrary textbookLibrary = textbookLibraryService.getLatest();
                     libraryId = textbookLibrary.getId();
                     year = null;
+                }else{
+                    libraryId = 0;
                 }
             }
             p = userPaperService.listExamination(page, size, user.getId(), keyword, structIds, libraryId, year, startTime, endTime, order != null ? order.replace("|", " ") : null);
@@ -1356,6 +1388,8 @@ public class MyController {
                     TextbookLibrary textbookLibrary = textbookLibraryService.getLatest();
                     libraryId = textbookLibrary.getId();
                     year = null;
+                }else{
+                    libraryId = 0;
                 }
             }
             p = userAskQuestionService.listExamination(page, size, user.getId(), keyword, questionTypes, structIds, libraryId, year, AskStatus.ValueOf(askStatus), startTime, endTime, order != null ? order.replace("|", " ") : null);
@@ -1557,7 +1591,17 @@ public class MyController {
             HttpSession session) {
         User user = (User) shiroHelp.getLoginUser();
 
-        Page<UserOrderRecord> p = userOrderRecordService.listWithCourse(page, size, user.getId(), CourseModule.ValueOf(courseModule), isUsed, isEnd, order, DirectionStatus.ValueOf(direction));
+        CourseModule module = CourseModule.ValueOf(courseModule);
+        Page<UserOrderRecord> p;
+        if (module == CourseModule.ONLINE){
+            // 在线课程包含:视频课程、小班课程
+            p = userOrderRecordService.listWithCourse(page, size, user.getId(), new String[]{CourseModule.VIDEO.key, CourseModule.ONLINE.key}, null,null, order, DirectionStatus.ValueOf(direction));
+        } else if (module == CourseModule.VS){
+            // 1v1课程:只有系统授课有作业
+            p = userOrderRecordService.listWithCourse(page, size, user.getId(), new String[]{CourseModule.VS.key}, null,null, order, DirectionStatus.ValueOf(direction));
+        }else{
+            throw new ParameterException("课程类型错误");
+        }
         List<UserCourseDetailDto> pr = Transform.convert(p, UserCourseDetailDto.class);
 
         Collection recordIds = Transform.getIds(p, UserOrderRecord.class,"id");

+ 59 - 1
server/gateway-api/src/main/java/com/qxgmat/controller/api/OrderController.java

@@ -188,6 +188,63 @@ public class OrderController {
         return ResponseHelp.success(order.getPayStatus() > 0);
     }
 
+    @RequestMapping(value = "/list", method = RequestMethod.GET)
+    @ApiOperation(value = "获取订单列表", notes = "获取订单列表", httpMethod = "GET")
+    public Response<PageMessage<UserOrderRecordListDto>> listOrder(
+            @RequestParam(required = false, defaultValue = "1") int page,
+            @RequestParam(required = false, defaultValue = "100") int size,
+            @RequestParam(required = false, defaultValue = "id") String order,
+            @RequestParam(required = false, defaultValue = "desc") String direction,
+            HttpSession session)  {
+        User user = (User) shiroHelp.getLoginUser();
+
+        Page<UserOrder> p = userOrderService.list(page, size, user.getId(), order, DirectionStatus.ValueOf(direction));
+
+        List<UserOrderRecordListDto> pr = Transform.convert(p, UserOrderRecordListDto.class);
+
+        // 绑定服务
+        Map<Object, JSONObject> serviceList = new HashMap<Object, JSONObject>(){{
+            Setting vipSetting = settingService.getByKey(SettingKey.SERVICE_VIP);
+            put(ServiceKey.VIP.key, vipSetting.getValue());
+            Setting textbookSetting = settingService.getByKey(SettingKey.SERVICE_TEXTBOOK);
+            put(ServiceKey.TEXTBOOK.key, textbookSetting.getValue());
+            Setting qxCatSetting = settingService.getByKey(SettingKey.SERVICE_QX_CAT);
+            put(ServiceKey.QX_CAT.key, qxCatSetting.getValue());
+        }};
+        List<UserOrderRecordListDto> prService = pr.stream().filter((row)-> row.getProductType().equals(ProductType.SERVICE.key)).collect(Collectors.toList());
+        Transform.combine(prService, serviceList, UserOrderRecordListDto.class, "service", "serviceInfo");
+
+        // 绑定课程
+        List<UserOrderRecordListDto> prCourse = pr.stream().filter((row)-> row.getProductType().equals(ProductType.COURSE.key)).collect(Collectors.toList());
+        Collection courseIds = Transform.getIds(prCourse, UserOrderRecordListDto.class, "productId");
+        List<Course> courseList = courseService.select(courseIds);
+        Transform.combine(prCourse, courseList, UserOrderRecordListDto.class, "productId", "course", Course.class, "id", CourseExtendDto.class);
+
+        // 绑定资料
+        List<UserOrderRecordListDto> prData = pr.stream().filter((row)-> row.getProductType().equals(ProductType.DATA.key)).collect(Collectors.toList());
+        Collection dataIds = Transform.getIds(prData, UserOrderRecordListDto.class, "productId");
+        List<CourseData> dataList = courseDataService.select(dataIds);
+        Transform.combine(prData, dataList, UserOrderRecordListDto.class, "productId", "data", CourseData.class, "id", CourseDataExtendDto.class);
+
+
+        return ResponseHelp.success(pr, page, size, p.getTotal());
+    }
+
+    @RequestMapping(value = "/detail", method = RequestMethod.GET)
+    @ApiOperation(value = "获取订单记录", notes = "获取订单记录", httpMethod = "GET")
+    public Response<UserOrderRecord> getOrder(
+            @RequestParam(required = true) Integer id
+    )  {
+        User user = (User) shiroHelp.getLoginUser();
+        UserOrderRecord record = userOrderRecordService.get(id);
+        if (!record.getUserId().equals(user.getId())){
+            throw new ParameterException("记录不存在");
+        }
+        // todo
+
+        return ResponseHelp.success(record);
+    }
+
     @RequestMapping(value = "/record/list", method = RequestMethod.GET)
     @ApiOperation(value = "获取订单记录列表", notes = "获取订单记录列表", httpMethod = "GET")
     public Response<PageMessage<UserOrderRecordListDto>> listRecord(
@@ -198,12 +255,13 @@ public class OrderController {
             @RequestParam(required = false) String service,
             @RequestParam(required = false) Boolean isUsed,
             @RequestParam(required = false) Boolean isExpire,
+            @RequestParam(required = false) Boolean filterChildren,
             @RequestParam(required = false, defaultValue = "id") String order,
             @RequestParam(required = false, defaultValue = "desc") String direction,
             HttpSession session)  {
         User user = (User) shiroHelp.getLoginUser();
 
-        Page<UserOrderRecord> p = userOrderRecordService.list(page, size, user.getId(), ProductType.ValueOf(productType), productId, ServiceKey.ValueOf(service), isUsed, isExpire, order, DirectionStatus.ValueOf(direction));
+        Page<UserOrderRecord> p = userOrderRecordService.list(page, size, user.getId(), ProductType.ValueOf(productType), productId, ServiceKey.ValueOf(service), isUsed, isExpire, filterChildren, order, DirectionStatus.ValueOf(direction));
 
         List<UserOrderRecordListDto> pr = Transform.convert(p, UserOrderRecordListDto.class);
 

+ 23 - 0
server/gateway-api/src/main/java/com/qxgmat/dto/response/UserOrderRecordListDto.java

@@ -5,8 +5,11 @@ import com.nuliji.tools.annotation.Dto;
 import com.qxgmat.data.dao.entity.UserOrderRecord;
 import com.qxgmat.dto.extend.CourseDataExtendDto;
 import com.qxgmat.dto.extend.CourseExtendDto;
+import com.qxgmat.dto.extend.CoursePackageExtendDto;
+import com.qxgmat.dto.extend.UserOrderRecordExtendDto;
 
 import java.util.Date;
+import java.util.List;
 
 @Dto(entity = UserOrderRecord.class)
 public class UserOrderRecordListDto {
@@ -24,6 +27,10 @@ public class UserOrderRecordListDto {
 
     private CourseDataExtendDto data;
 
+    private CoursePackageExtendDto coursePackage;
+
+    private List<UserOrderRecordExtendDto> records;
+
     private String service;
 
     private String param;
@@ -239,4 +246,20 @@ public class UserOrderRecordListDto {
     public void setServiceInfo(JSONObject serviceInfo) {
         this.serviceInfo = serviceInfo;
     }
+
+    public CoursePackageExtendDto getCoursePackage() {
+        return coursePackage;
+    }
+
+    public void setCoursePackage(CoursePackageExtendDto coursePackage) {
+        this.coursePackage = coursePackage;
+    }
+
+    public List<UserOrderRecordExtendDto> getRecords() {
+        return records;
+    }
+
+    public void setRecords(List<UserOrderRecordExtendDto> records) {
+        this.records = records;
+    }
 }

+ 8 - 8
server/gateway-api/src/main/java/com/qxgmat/service/inline/UserOrderRecordService.java

@@ -273,7 +273,7 @@ public class UserOrderRecordService extends AbstractService {
      * @param direction
      * @return
      */
-    public Page<UserOrderRecord> list(int page, int size, Integer userId, ProductType productType, Integer productId, ServiceKey service, Boolean isUsed, Boolean isExpire, String order, DirectionStatus direction){
+    public Page<UserOrderRecord> list(int page, int size, Integer userId, ProductType productType, Integer productId, ServiceKey service, Boolean isUsed, Boolean isExpire, Boolean filterChildren, String order, DirectionStatus direction){
         Example example = new Example(UserOrderRecord.class);
         example.and(
                 example.createCriteria()
@@ -298,6 +298,11 @@ public class UserOrderRecordService extends AbstractService {
                                     .andGreaterThan("endTime", now)
             );
         }
+        if (filterChildren != null && filterChildren)
+            example.and(
+                    example.createCriteria().
+                                    andEqualTo("parentId", 0)
+            );
         if(productType != null) {
             example.and(
                     example.createCriteria()
@@ -507,22 +512,17 @@ public class UserOrderRecordService extends AbstractService {
      * @param userId
      * @return
      */
-    public Page<UserOrderRecord> listWithCourse(int page, int size, Integer userId, CourseModule courseModule, Boolean isUsed, Boolean isEnd, String order, DirectionStatus direction){
+    public Page<UserOrderRecord> listWithCourse(int page, int size, Integer userId, String[] modules, Boolean isUsed, Boolean isEnd, String order, DirectionStatus direction){
         if(order == null || order.isEmpty()){
             order = "id";
         }
-        if(adminMap.containsKey(order)){
-            order = adminMap.get(order)+".`"+Tools.underscoreName(order)+"`";
-        }else{
-            order = adminMap.get("")+".`"+Tools.underscoreName(order)+"`";
-        }
         if (direction == null){
             direction = DirectionStatus.DESC;
         }
         String finalOrder = order;
         DirectionStatus finalDirection = direction;
         Page<UserOrderRecord> p = page(
-                ()-> userOrderRecordRelationMapper.listWithCourse(userId, courseModule.key, isUsed, isEnd, finalOrder, finalDirection.key)
+                ()-> userOrderRecordRelationMapper.listWithCourse(userId, modules, isUsed, isEnd, finalOrder, finalDirection.key)
                 , page, size);
 
         Collection ids = Transform.getIds(p, UserOrderRecord.class, "id");

+ 21 - 0
server/gateway-api/src/main/java/com/qxgmat/service/inline/UserOrderService.java

@@ -36,6 +36,27 @@ public class UserOrderService extends AbstractService {
         put("", "uo");
     }};
 
+    public Page<UserOrder> list(int page, int size, Integer userId, String order, DirectionStatus direction){
+        Example example = new Example(UserOrder.class);
+        if(userId != null){
+            example.and(
+                    example.createCriteria()
+                            .andEqualTo("userId", userId)
+            );
+        }
+
+        if(order == null || order.isEmpty()) order = "id";
+        switch(direction){
+            case ASC:
+                example.orderBy(order).asc();
+                break;
+            case DESC:
+            default:
+                example.orderBy(order).desc();
+        }
+        return select(userOrderMapper, example, page, size);
+    }
+
     public Page<UserOrder> listAdmin(int page, int size, Integer userId, String productType, String payMethod, Integer orderId, String order, DirectionStatus direction){
         if(order == null || order.isEmpty()){
             order = "id";