Go преди 5 години
родител
ревизия
1147856438
променени са 100 файла, в които са добавени 985 реда и са изтрити 601 реда
  1. 8 0
      front/package-lock.json
  2. 1 0
      front/package.json
  3. 5 0
      front/project/Constant.js
  4. 1 0
      front/project/admin/routes/course/package/page.js
  5. 20 14
      front/project/admin/routes/course/previewDetail/page.js
  6. 4 0
      front/project/admin/routes/course/student/page.js
  7. 1 1
      front/project/admin/routes/interaction/askQuestion/page.js
  8. 7 1
      front/project/admin/routes/ready/article/index.less
  9. 33 11
      front/project/admin/routes/ready/article/page.js
  10. 16 1
      front/project/admin/routes/ready/data/page.js
  11. 7 0
      front/project/admin/routes/setting/index/index.less
  12. 1 1
      front/project/admin/routes/setting/time/page.js
  13. 9 4
      front/project/admin/routes/show/ad/page.js
  14. 1 1
      front/project/admin/routes/show/messageDetail/page.js
  15. 1 1
      front/project/admin/routes/student/askCourse/page.js
  16. 4 5
      front/project/admin/routes/student/askCourseDetail/page.js
  17. 1 1
      front/project/admin/routes/student/askQuestion/page.js
  18. 15 7
      front/project/admin/routes/student/studyDetail/page.js
  19. 1 1
      front/project/admin/routes/subject/examination/page.js
  20. 7 2
      front/project/admin/routes/subject/exercise/page.js
  21. 51 6
      front/project/admin/routes/subject/question/page.js
  22. 3 0
      front/project/admin/routes/subject/sentenceQuestion/page.js
  23. 0 3
      front/project/admin/routes/subject/textbook/page.js
  24. 1 0
      front/project/admin/routes/subject/textbookQuestion/page.js
  25. 2 1
      front/project/admin/routes/textbook/library/page.js
  26. 2 3
      front/project/admin/routes/textbook/topic/page.js
  27. 1 1
      front/project/admin/routes/textbook/topicDetail/page.js
  28. 1 1
      front/project/admin/routes/user/abnormal/page.js
  29. 22 15
      front/project/admin/routes/user/detail/page.js
  30. 1 1
      front/project/admin/routes/user/list/page.js
  31. 1 1
      front/project/admin/routes/user/orderDetail/page.js
  32. 1 1
      front/project/admin/stores/system.js
  33. 2 2
      front/project/h5/routes/page/identity/page.js
  34. BIN
      front/project/www/assets/good.png
  35. BIN
      front/project/www/assets/qrcode_vague.png
  36. 10 11
      front/project/www/components/Card/index.js
  37. 19 3
      front/project/www/components/Card/index.less
  38. 1 0
      front/project/www/components/Date/index.less
  39. 10 8
      front/project/www/components/Examination/index.js
  40. 2 2
      front/project/www/components/Header/index.js
  41. 2 2
      front/project/www/components/Input/index.js
  42. 7 6
      front/project/www/components/Item/index.js
  43. 7 6
      front/project/www/components/Item/index.less
  44. 9 7
      front/project/www/components/OtherModal/index.js
  45. 8 8
      front/project/www/components/PayModal/index.js
  46. 1 1
      front/project/www/components/QrCode/index.js
  47. 4 9
      front/project/www/components/UserAction/index.js
  48. 6 7
      front/project/www/components/UserAction/index.less
  49. 1 1
      front/project/www/components/UserTable/index.js
  50. 2 1
      front/project/www/components/Video/index.js
  51. 3 0
      front/project/www/components/Video/index.less
  52. 1 1
      front/project/www/components/VipRenew/index.js
  53. 2 2
      front/project/www/routes/course/dataDetail/page.js
  54. 1 1
      front/project/www/routes/course/detail/index.less
  55. 32 23
      front/project/www/routes/course/detail/page.js
  56. 3 3
      front/project/www/routes/course/main/page.js
  57. 1 1
      front/project/www/routes/course/note/page.js
  58. 3 3
      front/project/www/routes/course/online/index.less
  59. 17 14
      front/project/www/routes/course/online/page.js
  60. 5 4
      front/project/www/routes/course/packageDetail/page.js
  61. 22 16
      front/project/www/routes/course/vs/page.js
  62. 42 33
      front/project/www/routes/examination/list/page.js
  63. 1 0
      front/project/www/routes/examination/main/page.js
  64. 1 1
      front/project/www/routes/exercise/list/page.js
  65. 138 138
      front/project/www/routes/exercise/main/page.js
  66. 1 0
      front/project/www/routes/my/answer/index.js
  67. 1 0
      front/project/www/routes/my/collect/index.js
  68. 12 8
      front/project/www/routes/my/collect/page.js
  69. 1 0
      front/project/www/routes/my/course/index.js
  70. 3 3
      front/project/www/routes/my/course/index.less
  71. 122 58
      front/project/www/routes/my/course/page.js
  72. 1 0
      front/project/www/routes/my/data/index.js
  73. 1 0
      front/project/www/routes/my/error/index.js
  74. 4 4
      front/project/www/routes/my/error/page.js
  75. 1 1
      front/project/www/routes/my/index.js
  76. 1 0
      front/project/www/routes/my/main/index.js
  77. 5 2
      front/project/www/routes/my/main/index.less
  78. 1 0
      front/project/www/routes/my/message/index.js
  79. 37 29
      front/project/www/routes/my/message/page.js
  80. 1 0
      front/project/www/routes/my/note/index.js
  81. 2 2
      front/project/www/routes/my/note/page.js
  82. 1 0
      front/project/www/routes/my/order/index.js
  83. 1 0
      front/project/www/routes/my/report/index.js
  84. 9 4
      front/project/www/routes/my/report/page.js
  85. 1 0
      front/project/www/routes/my/tools/index.js
  86. 5 7
      front/project/www/routes/my/tools/page.js
  87. 1 0
      front/project/www/routes/page/cart/index.js
  88. 7 0
      front/project/www/routes/page/cart/index.less
  89. 2 1
      front/project/www/routes/page/cart/page.js
  90. 7 5
      front/project/www/routes/page/export/page.js
  91. 1 1
      front/project/www/routes/page/home/index.less
  92. 6 6
      front/project/www/routes/page/home/page.js
  93. 1 0
      front/project/www/routes/page/order/index.js
  94. 6 0
      front/project/www/routes/page/order/index.less
  95. 12 10
      front/project/www/routes/page/order/page.js
  96. 12 0
      front/project/www/routes/page/ready/index.less
  97. 37 20
      front/project/www/routes/page/ready/page.js
  98. 16 17
      front/project/www/routes/paper/process/base/index.js
  99. 74 25
      front/project/www/routes/paper/process/page.js
  100. 0 0
      front/project/www/routes/paper/question/detail/index.js

+ 8 - 0
front/package-lock.json

@@ -7176,6 +7176,14 @@
       "resolved": "http://registry.npm.taobao.org/moment/download/moment-2.22.2.tgz",
       "integrity": "sha1-PCV/mDn8DpP/UxSWMiOeuQeD/2Y="
     },
+    "moment-timezone": {
+      "version": "0.5.28",
+      "resolved": "https://registry.npm.taobao.org/moment-timezone/download/moment-timezone-0.5.28.tgz?cache=0&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fmoment-timezone%2Fdownload%2Fmoment-timezone-0.5.28.tgz",
+      "integrity": "sha1-8JPXidCR7XsFXYKqgagkZ/cuQzg=",
+      "requires": {
+        "moment": ">= 2.9.0"
+      }
+    },
     "mpd-parser": {
       "version": "0.8.1",
       "resolved": "https://registry.npm.taobao.org/mpd-parser/download/mpd-parser-0.8.1.tgz",

+ 1 - 0
front/package.json

@@ -79,6 +79,7 @@
     "js-sha1": "^0.6.0",
     "katex": "^0.11.1",
     "moment": "^2.22.2",
+    "moment-timezone": "^0.5.28",
     "parchment": "^1.1.4",
     "promise": "^7.1.1",
     "quill": "^1.3.7",

+ 5 - 0
front/project/Constant.js

@@ -1,3 +1,8 @@
+
+import moment from 'moment-timezone';
+
+moment.tz.setDefault('Asia/Shanghai');
+
 export const PcUrl = __PcUrl__;
 
 export const WechatPcAppId = __WechatPcAppId__;

+ 1 - 0
front/project/admin/routes/course/package/page.js

@@ -59,6 +59,7 @@ export default class extends Page {
       if (list) {
         item.select = list;
         item.type = 'select';
+        item.allowClear = true;
       }
       this.itemList.push(item);
     });

+ 20 - 14
front/project/admin/routes/course/previewDetail/page.js

@@ -76,6 +76,7 @@ export default class extends Page {
         this.setState({ module: result.courseModule, data: result, questionNoIds });
         this.refresh();
         if (result.courseId) this.refreshCourse(result.courseId);
+        if (result.questionType) this.onRefreshType(result.questionType);
       });
   }
 
@@ -96,14 +97,6 @@ export default class extends Page {
         }, null, null);
         break;
       case 'vs':
-        bindSearch(this.vsList, 'userId', this, (search) => {
-          return User.list(search);
-        }, (row) => {
-          return {
-            title: `${row.nickname}(${row.mobile})`,
-            value: row.id,
-          };
-        }, null, null);
         break;
       default:
     }
@@ -131,7 +124,7 @@ export default class extends Page {
         courseNo: result.map((row) => {
           return {
             title: `${row.title}(${row.no})`,
-            value: row.id,
+            value: row.no,
           };
         }),
       });
@@ -188,8 +181,10 @@ export default class extends Page {
     const { data } = this.state;
     asyncForm('添加', this.onlineList, {}, info => {
       if (info.time && info.time.length > 0) {
-        info.startTime = info.time[0].format('YYYY-MM-DD');
-        info.endTime = info.time[1].format('YYYY-MM-DD');
+        info.time[0].local();
+        info.time[1].local();
+        info.startTime = info.time[0].format('YYYY-MM-DDT00:00:00Z');
+        info.endTime = info.time[1].format('YYYY-MM-DDT00:00:00Z');
       }
       return Preview.addAssign(Object.assign({ paperModule: data.paperModule, paperId: data.id, courseModule: data.courseModule }, info)).then(() => {
         asyncSMessage('添加成功!');
@@ -204,8 +199,10 @@ export default class extends Page {
     const { data } = this.state;
     asyncForm('添加', this.vsList, {}, info => {
       if (info.time && info.time.length > 0) {
-        info.startTime = info.time[0].format('YYYY-MM-DD');
-        info.endTime = info.time[1].format('YYYY-MM-DD');
+        info.time[0].local();
+        info.time[1].local();
+        info.startTime = info.time[0].format('YYYY-MM-DDT00:00:00Z');
+        info.endTime = info.time[1].format('YYYY-MM-DDT00:00:00Z');
       }
       return User.listCourseAppointment({ courseId: data.courseId, userId: info.userId, startTime: info.startTime, endTime: info.endTime }).then(result => {
         if (result.total === 0) {
@@ -213,11 +210,20 @@ export default class extends Page {
         } if (result.total > 1) {
           return Promise.reject(new Error('时间段内存在多条预约记录'));
         }
-        return Preview.addAssign(Object.assign({ paperModule: data.paperModule, paperId: data.id, courseModule: data.courseModule }, info)).then(() => {
+        return Preview.addAssign(Object.assign({ paperModule: data.paperModule, paperId: data.id, courseModule: data.courseModule, courseAppointment: result.list[0].id }, info)).then(() => {
           asyncSMessage('添加成功!');
           this.refresh();
         });
       });
+    }).then((form) => {
+      bindSearch(this.vsList, 'userId', form, (search) => {
+        return User.list(search);
+      }, (row) => {
+        return {
+          title: `${row.nickname}(${row.mobile})`,
+          value: row.id,
+        };
+      }, null, null);
     }).catch(err => {
       console.log(err);
     });

+ 4 - 0
front/project/admin/routes/course/student/page.js

@@ -136,6 +136,10 @@ export default class extends Page {
       key: 'cctalkName',
       type: 'input',
       name: 'CCTalk名',
+    }, {
+      key: 'number',
+      type: 'hidden',
+      name: '课时数',
     }];
     this.vsColumns = [{
       title: '学员id',

+ 1 - 1
front/project/admin/routes/interaction/askQuestion/page.js

@@ -194,7 +194,7 @@ export default class extends Page {
         data={this.state.search}
         onChange={data => {
           if (data.time.length > 0) {
-            data.time = [data.time[0].format('YYYY-MM-DD HH:mm:ss'), data.time[1].format('YYYY-MM-DD HH:mm:ss')];
+            data.time = [data.time[0].format('YYYY-MM-DD 00:00:00'), data.time[1].format('YYYY-MM-DD 23:59:59')];
           }
           data.page = 1;
           this.search(data);

+ 7 - 1
front/project/admin/routes/ready/article/index.less

@@ -1,3 +1,9 @@
 @charset "utf-8";
 
-#ready-article {}
+#ready-article {}
+
+.article-category-modal {
+  .editable-cell {
+    display: inline-block;
+  }
+}

+ 33 - 11
front/project/admin/routes/ready/article/page.js

@@ -1,6 +1,6 @@
 import React from 'react';
 import { Link } from 'react-router-dom';
-import { Button, Modal, Checkbox, Row, Col, Switch } from 'antd';
+import { Button, Modal, Checkbox, Row, Col, Switch, Icon } from 'antd';
 import './index.less';
 import Page from '@src/containers/Page';
 import Block from '@src/components/Block';
@@ -58,6 +58,7 @@ export default class extends Page {
         }} checked={!!text} />;
       },
     }];
+    this.filterF = null;
     this.filterForm = [{
       key: 'parentCategoryId',
       type: 'select',
@@ -67,6 +68,7 @@ export default class extends Page {
       select: [],
       number: true,
       onChange: (value) => {
+        this.filterF.setFieldsValue({ categoryId: null });
         this.changeSearch(this.filterForm, this, value);
       },
     }, {
@@ -128,18 +130,29 @@ export default class extends Page {
   refreshCategory() {
     Ready.allCategory().then(result => {
       this.categoryTitleMap = getMap(result, 'id', 'title');
-      this.categoryTree = formatTreeData(result, 'id', 'title', 'parentId');
       this.categoryMap = getMap(result, 'id');
-      this.categoryList = result.map(row => {
+      result = result.filter(row => {
+        if (!row.parentId) return true;
+        const parent = this.categoryMap[row.parentId];
+        if (parent) return true;
+        return false;
+      });
+      const copy = result.map(row => Object.assign({}, row)).map((row) => {
         const parent = this.categoryMap[row.parentId];
         row.value = row.id;
         row.title = <Row style={{ width: 400 }}>
-          <Col span={11}><EditTableCell value={row.title} onChange={(value) => this.changeCategoryTitle(row.id, value)} /></Col>
+          <Col span={12}><Icon type="delete" onClick={() => this.deleteCategory(row.id)} /><EditTableCell value={row.title} onChange={(value) => this.changeCategoryTitle(row.id, value)} /></Col>
+          <Col span={2} />
           <Col span={5}>{row.parentId === 0 && <Switch checked={row.isData} checkedChildren='资料节点' unCheckedChildren='非资料' onChange={() => this.changeCategoryData(row.id, !row.isData)} />}{row.parentId === 0 && <Switch checked={row.isRoom} checkedChildren='考场节点' unCheckedChildren='非考场' onChange={() => this.changeCategoryRoom(row.id, !row.isRoom)} />}</Col>
-          <Col span={5}>{row.parentId > 0 && parent.isData > 0 && <Switch checked={row.isOfficial} checkedChildren='官方资料' unCheckedChildren='非官方资料' onChange={() => this.changeCategoryOfficial(row.id, !row.isOfficial)} />}</Col>
+          <Col span={5}>{row.parentId > 0 && parent && parent.isData > 0 && <Switch checked={row.isOfficial} checkedChildren='官方资料' unCheckedChildren='非官方资料' onChange={() => this.changeCategoryOfficial(row.id, !row.isOfficial)} />}</Col>
         </Row>;
         return row;
       });
+      this.categoryTree = formatTreeData(copy, 'id', 'title', 'parentId');
+      this.categoryList = result.map(row => {
+        row.value = row.id;
+        return row;
+      });
       this.filterForm[0].select = this.categoryList.filter(row => row.parentId === 0);
       this.changeSearch(this.filterForm, this, this.state.search.parentCategoryId);
       this.initData();
@@ -150,7 +163,7 @@ export default class extends Page {
   changeSearch(list, component, value) {
     if (value) {
       list[1].disabled = false;
-      list[1].select = this.categoryList.filter(row => row.parentId === value);
+      list[1].select = this.categoryList.filter(row => row.parentId === Number(value));
     } else {
       list[1].disabled = true;
       list[1].select = [];
@@ -202,16 +215,22 @@ export default class extends Page {
   }
 
   changeCategoryOfficial(id, checked) {
-    const category = this.categoryMap[id];
-    if (category.isData) {
-      if (checked) return;
-    } else if (!checked) return;
     Ready.editCategory({ id, isOfficial: checked ? 1 : 0 })
       .then(() => {
         this.refreshCategory();
       });
   }
 
+  deleteCategory(id) {
+    asyncDelConfirm('删除确认', '是否删除选中?', () => {
+      const handler = Ready.deleteCategory({ id });
+      return handler.then(() => {
+        asyncSMessage('删除成功!');
+        this.refreshCategory();
+      });
+    });
+  }
+
   changeCategoryOrder(from, to) {
     if (from.id === to.id) return;
     if (from.parentId !== to.parentId) {
@@ -251,6 +270,9 @@ export default class extends Page {
     return <Block flex>
       <FilterLayout
         show
+        ref={ref => {
+          if (ref) this.filterF = ref;
+        }}
         itemList={this.filterForm}
         data={this.state.search}
         onChange={data => {
@@ -275,7 +297,7 @@ export default class extends Page {
         this.close(false, 'detail');
       }} onOk={() => {
         this.close(true, 'detail');
-      }}>
+      }} className="article-category-modal">
 
         <TreeLayout
           autoExpandParent

+ 16 - 1
front/project/admin/routes/ready/data/page.js

@@ -7,7 +7,7 @@ import ShowImage from '@src/components/ShowImage';
 import ActionLayout from '@src/layouts/ActionLayout';
 import TableLayout from '@src/layouts/TableLayout';
 // import { formatDate } from '@src/services/Tools';
-import { asyncSMessage, asyncForm } from '@src/services/AsyncTools';
+import { asyncSMessage, asyncForm, asyncDelConfirm } from '@src/services/AsyncTools';
 import { Ready } from '../../../stores/ready';
 import { System } from '../../../stores/system';
 
@@ -55,6 +55,11 @@ export default class extends Page {
               this.editAction(record);
             }}>编辑</a>
           )}
+          {(
+            <a onClick={() => {
+              this.deleteAction(record);
+            }}>删除</a>
+          )}
         </div>;
       },
     }];
@@ -113,6 +118,16 @@ export default class extends Page {
     });
   }
 
+  deleteAction(row) {
+    asyncDelConfirm('删除确认', '是否删除选中?', () => {
+      const handler = Ready.delData({ id: row.id });
+      return handler.then(() => {
+        asyncSMessage('删除成功!');
+        this.refresh();
+      });
+    });
+  }
+
   renderOfficial() {
     return <Block flex>
       <ActionLayout

+ 7 - 0
front/project/admin/routes/setting/index/index.less

@@ -15,4 +15,11 @@
     right: 10px;
     top: 10px;
   }
+
+  .ant-upload {
+    img {
+      max-width: 100px;
+      max-height: 100px;
+    }
+  }
 }

+ 1 - 1
front/project/admin/routes/setting/time/page.js

@@ -355,7 +355,7 @@ export default class extends Page {
           <Form.Item labelCol={{ span: 10 }} wrapperCol={{ span: 14 }} label='下次错误率组卷'>
             {getFieldDecorator('exercisePaper.date', {
               rules: [
-                { required: true, message: '请输入下次练习错误率组卷时间' },
+                { required: true, message: '下次错误率组卷时间' },
               ],
             })(
               <DatePicker placeholder='请输入下次练习错误率组卷时间' onChange={(value) => {

+ 9 - 4
front/project/admin/routes/show/ad/page.js

@@ -143,8 +143,10 @@ export default class extends Page {
   addAction() {
     asyncForm('创建广告', this.itemList, {}, data => {
       if (data.time && data.time.length > 0) {
-        data.startTime = data.time[0].format('YYYY-MM-DD');
-        data.endTime = data.time[1].format('YYYY-MM-DD');
+        data.time[0].local();
+        data.time[1].local();
+        data.startTime = data.time[0].format('YYYY-MM-DDT00:00:00Z');
+        data.endTime = data.time[1].format('YYYY-MM-DDT00:00:00Z');
       }
       data.channel = data.channel.join('-');
       return System.addAd(data).then(() => {
@@ -160,10 +162,13 @@ export default class extends Page {
   editAction(row) {
     const info = Object.assign({}, row);
     info.channel = info.channel.split('-');
+    info.time = [info.startTime, info.endTime];
     asyncForm('编辑广告', this.itemList, info, data => {
       if (data.time && data.time.length > 0) {
-        data.startTime = data.time[0].format('YYYY-MM-DD');
-        data.endTime = data.time[1].format('YYYY-MM-DD');
+        data.time[0].local();
+        data.time[1].local();
+        data.startTime = data.time[0].format('YYYY-MM-DDT00:00:00Z');
+        data.endTime = data.time[1].format('YYYY-MM-DDT00:00:00Z');
       }
       data.channel = data.channel.join('-');
       return System.editAd(data).then(() => {

+ 1 - 1
front/project/admin/routes/show/messageDetail/page.js

@@ -23,7 +23,7 @@ export default class extends Page {
     if (id) {
       handler = System.getMessage({ id });
     } else {
-      handler = Promise.resolve({});
+      handler = Promise.resolve({ messageMethod: 'inside', messageCategory: 'custom' });
     }
 
     handler

+ 1 - 1
front/project/admin/routes/student/askCourse/page.js

@@ -208,7 +208,7 @@ export default class extends Page {
         data={this.state.search}
         onChange={data => {
           if (data.time.length > 0) {
-            data.time = [data.time[0].format('YYYY-MM-DD HH:mm:ss'), data.time[1].format('YYYY-MM-DD HH:mm:ss')];
+            data.time = [data.time[0].format('YYYY-MM-DD 00:00:00'), data.time[1].format('YYYY-MM-DD 23:59:59')];
           }
           data.page = 1;
           this.search(data);

+ 4 - 5
front/project/admin/routes/student/askCourseDetail/page.js

@@ -6,7 +6,7 @@ import Page from '@src/containers/Page';
 import Block from '@src/components/Block';
 import DragList from '@src/components/DragList';
 // import FileUpload from '@src/components/FileUpload';
-import { formatFormError, formatDate, formatTreeData, getMap } from '@src/services/Tools';
+import { formatFormError, formatDate, getMap } from '@src/services/Tools';
 import { asyncSMessage } from '@src/services/AsyncTools';
 import { PcUrl } from '../../../../Constant';
 import { Exercise } from '../../../stores/exercise';
@@ -14,9 +14,8 @@ import { Course } from '../../../stores/course';
 
 export default class extends Page {
   init() {
+    this.exerciseMap = {};
     Exercise.courseStruct().then((result) => {
-      const list = result.map(row => { row.title = `${row.titleZh}`; row.value = row.id; return row; });
-      this.filterForm[0].tree = formatTreeData(list, 'id', 'title', 'parentId');
       this.exerciseMap = getMap(result.map(row => {
         row.title = `${row.titleZh}`;
         row.value = row.id;
@@ -108,7 +107,7 @@ export default class extends Page {
           {this.exerciseMap[course.structId]}
         </Form.Item>
         <Form.Item labelCol={{ span: 5 }} wrapperCol={{ span: 16 }} label='课程'>
-          <a href='' target='_blank'>{course.title}</a>
+          <a href={`${PcUrl}/course/detail/${course.id}`} target='_blank'>{course.title}</a>
         </Form.Item>
       </Form>
     </Block>;
@@ -117,7 +116,7 @@ export default class extends Page {
   renderAsk() {
     const { getFieldDecorator, getFieldValue } = this.props.form;
     const { data, editContent } = this.state;
-    const { user = {}, createTime, courseNo, position, originContent, content } = data;
+    const { user = {}, createTime, courseNo = {}, position, originContent, content } = data;
     return <Block>
       <h1>提问信息</h1>
       <Form>

+ 1 - 1
front/project/admin/routes/student/askQuestion/page.js

@@ -207,7 +207,7 @@ export default class extends Page {
         data={this.state.search}
         onChange={data => {
           if (data.time.length > 0) {
-            data.time = [data.time[0].format('YYYY-MM-DD HH:mm:ss'), data.time[1].format('YYYY-MM-DD HH:mm:ss')];
+            data.time = [data.time[0].format('YYYY-MM-DD 00:00:00'), data.time[1].format('YYYY-MM-DD 23:59:59')];
           }
           data.page = 1;
           this.search(data);

+ 15 - 7
front/project/admin/routes/student/studyDetail/page.js

@@ -35,7 +35,8 @@ export default class extends Page {
       title: '预习作业进度',
       dataIndex: 'userPaper',
       render: (text, record) => {
-        return `${text.times}遍+${formatPercent(record.userReport.userNumber, record.userReport.questionNumber)}`;
+        const { userPaper, userReport } = record;
+        return `${userPaper ? `${userPaper.times}遍` : ''}${userReport ? formatPercent(userReport.userNumber, userReport.questionNumber, false) : '0%'}`;
       },
     }, {
       title: '操作',
@@ -101,11 +102,12 @@ export default class extends Page {
       },
     }, {
       title: '预习作业',
-      dataIndex: 'userPaper',
+      dataIndex: 'previewAssign',
       render: (text, record) => {
-        return text ? <a onClick={() => {
+        const { userPaper } = record;
+        return userPaper ? <a onClick={() => {
           User.locationUser(record.userId, `${PcUrl}/paper/report/${record.reportId}`);
-        }}>查看{text.times > 0 ? '(已完成)' : ''}</a> : '';
+        }} target="_blank">查看{userPaper.times > 0 ? '(已完成)' : ''}</a> : (text ? text.title : '');
       },
     }, {
       title: '笔记批阅',
@@ -242,7 +244,7 @@ export default class extends Page {
   }
 
   deleteComment(comment) {
-    User.delCourseAppointmentComment({ id: comment }).then(() => {
+    User.delCourseAppointmentComment({ id: comment.id }).then(() => {
       this.refreshComment(comment.appointmentId, comment.type);
     });
   }
@@ -311,7 +313,10 @@ export default class extends Page {
               <Button>上传文档{(this.state.comment || {}).file ? '(已上传)' : ''}</Button>
             </Upload></Col>
             <Col span={1} offset={8}><Button type='primary' onClick={() => {
-              if (!this.state.comment || !this.state.comment.content) return;
+              if (!this.state.comment || !this.state.comment.content) {
+                asyncSMessage('请输入内容', 'error');
+                return;
+              }
               this.state.comment.appointmentId = this.state.note.id;
               this.state.comment.type = 'note';
               this.state.comment.recordId = this.state.note.recordId;
@@ -355,7 +360,10 @@ export default class extends Page {
               <Button>上传文档{(this.state.comment || {}).file ? '(已上传)' : ''}</Button>
             </Upload></Col>
             <Col span={1} offset={8}><Button type='primary' onClick={() => {
-              if (!this.state.comment || !this.state.comment.content) return;
+              if (!this.state.comment || !this.state.comment.content) {
+                asyncSMessage('请输入内容', 'error');
+                return;
+              }
               this.state.comment.appointmentId = this.state.supply.id;
               this.state.comment.type = 'supply';
               this.state.comment.recordId = this.state.supply.recordId;

+ 1 - 1
front/project/admin/routes/subject/examination/page.js

@@ -29,7 +29,7 @@ const filterForm = [
     key: 'paperId',
     type: 'select',
     allowClear: true,
-    name: '练习册',
+    name: '试卷',
     select: [],
     placeholder: '请选择',
     number: true,

+ 7 - 2
front/project/admin/routes/subject/exercise/page.js

@@ -219,7 +219,12 @@ export default class extends Page {
       this.setState({ exercise: result });
     });
     System.getPlace().then(result => {
-      filterForm[3].select = [].concat(...Object.keys(result).map(key => result[key]));
+      filterForm[3].select = [].concat(...Object.keys(result).map(key => result[key].map(row => {
+        return {
+          label: row,
+          value: row,
+        };
+      })));
       this.setState({ place: result });
     });
     bindSearch(filterForm, 'paperId', this, (search) => {
@@ -281,7 +286,7 @@ export default class extends Page {
         data={this.state.search}
         onChange={data => {
           if (data.time.length > 0) {
-            data.time = [data.time[0].format('YYYY-MM-DD HH:mm:ss'), data.time[1].format('YYYY-MM-DD HH:mm:ss')];
+            data.time = [data.time[0].format('YYYY-MM-DD 00:00:00'), data.time[1].format('YYYY-MM-DD 23:59:59')];
           }
           data.page = 1;
           this.search(data);

+ 51 - 6
front/project/admin/routes/subject/question/page.js

@@ -286,6 +286,15 @@ export default class extends Page {
     form.setFieldsValue({ [`answer.questions[${index}].single`]: answer });
   }
 
+  autoChangeQuestion(index) {
+    const { form } = this.props;
+    const keys = form.getFieldValue(`content.questions[${index}].keys`) || [];
+    const answer = form.getFieldValue(`answer.questions[${index}].single`) || [];
+    if (answer.filter(row => row).length === 0) {
+      this.changeQuestion(index, keys[0], true);
+    }
+  }
+
   changeDouble(index, k, o, value) {
     const { form } = this.props;
     const direction = form.getFieldValue(`content.questions[${index}].direction`);
@@ -308,6 +317,34 @@ export default class extends Page {
     form.setFieldsValue({ [`answer.questions[${index}].double`]: answer });
   }
 
+  autoChangeDouble(index) {
+    const { form } = this.props;
+    const direction = form.getFieldValue(`content.questions[${index}].direction`);
+    const answer = form.getFieldValue(`answer.questions[${index}].double`) || [];
+    const { length } = answer;
+    switch (direction) {
+      case 'landscape':
+        answer.forEach((row, k) => {
+          if (row.filter(r => r).length === 0) {
+            this.changeDouble(index, k, 0, true);
+          }
+        });
+        break;
+      case 'portrait':
+        for (let i = 0; i < length; i += 1) {
+          let flag = 0;
+          answer.forEach(row => {
+            flag += row[i] ? 1 : 0;
+          });
+          if (!flag) {
+            this.changeDouble(index, i, 0, true);
+          }
+        }
+        break;
+      default:
+    }
+  }
+
   submit() {
     const { form } = this.props;
     const fields = ['id', 'questionType', 'place', 'difficult', 'difficultScore', 'content', 'answer', 'stem', 'keyword', 'questionNoIds', 'officialContent', 'qxContent', 'associationContent'];
@@ -574,7 +611,7 @@ export default class extends Page {
                   rules: [{
                     required: true, message: '输入编号',
                   }],
-                })(<InputNumber placeholder='题目id' onClick={(value) => {
+                })(<InputNumber placeholder='题目id' min={1} onClick={(value) => {
                   this.setState({ questionNo: value });
                 }} />)}
               </Form.Item>
@@ -745,8 +782,7 @@ export default class extends Page {
         <Form>
           {type !== 'inline' && (
             <Form.Item>
-              {getFieldDecorator(`content.questions[${index}].description`, {
-              })(
+              {getFieldDecorator(`content.questions[${index}].description`, {})(
                 <Editor placeholder='选项问题说明' />,
               )}
             </Form.Item>
@@ -800,7 +836,10 @@ export default class extends Page {
                     <Icon
                       type='minus-circle-o'
                       disabled={keys.length === 1}
-                      onClick={() => this.removeQuestion(index, k)}
+                      onClick={() => {
+                        this.removeQuestion(index, k);
+                        // this.autoChangeQuestion(index);
+                      }}
                     />
                   ) : null}
                 </Form.Item>
@@ -810,7 +849,10 @@ export default class extends Page {
         )}
       />,
       <Form.Item>
-        <Button type='dashed' onClick={() => this.addQuestion(index)} >
+        <Button type='dashed' onClick={() => {
+          this.addQuestion(index);
+          // this.autoChangeQuestion(index);
+        }} >
           <Icon type='plus' /> 新增
       </Button>
       </Form.Item>,
@@ -902,7 +944,10 @@ export default class extends Page {
         }}
       />,
       <Form.Item>
-        <Button type='dashed' onClick={() => this.addQuestion(index)} >
+        <Button type='dashed' onClick={() => {
+          this.addQuestion(index);
+          // this.autoChangeQuestion(index);
+        }}>
           <Icon type='plus' /> 新增
       </Button>
       </Form.Item>,

+ 3 - 0
front/project/admin/routes/subject/sentenceQuestion/page.js

@@ -130,6 +130,9 @@ export default class extends Page {
           }
         }).catch((e) => {
           if (e.result) form.setFields(formatFormError(data, e.result));
+          else {
+            asyncSMessage(e.message, 'error');
+          }
         });
       }
     });

+ 0 - 3
front/project/admin/routes/subject/textbook/page.js

@@ -64,9 +64,6 @@ export default class extends Page {
         return TextbookTypeMap[record.question.questionType] || text;
       },
     }, {
-      title: '练习册',
-      dataIndex: 'paper.title',
-    }, {
       title: '题目ID',
       dataIndex: 'title',
     }, {

+ 1 - 0
front/project/admin/routes/subject/textbookQuestion/page.js

@@ -183,6 +183,7 @@ export default class extends Page {
           }
         }).catch((e) => {
           if (e.result) form.setFields(formatFormError(data, e.result));
+          else asyncSMessage(e.message, 'error');
         });
       }
     });

+ 2 - 1
front/project/admin/routes/textbook/library/page.js

@@ -97,7 +97,8 @@ export default class extends Page {
         return;
       }
       const data = fieldsValue.add;
-      data.startDate = data.startDate.format('YYYY-MM-DD');
+      data.startDate.local();
+      data.startDate = data.startDate.format('YYYY-MM-DDT00:00:00Z');
       asyncDelConfirm('新增确认', '是否添加新的换库?', () => {
         return Textbook.addLibrary(data).then(() => {
           asyncSMessage('添加成功!');

+ 2 - 3
front/project/admin/routes/textbook/topic/page.js

@@ -47,7 +47,6 @@ export default class extends Page {
       type: 'select',
       name: '机经质量',
       select: TextbookQuality,
-      number: true,
       allowClear: true,
     }];
     this.columns = [{
@@ -115,8 +114,8 @@ export default class extends Page {
   }
 
   deleteAction(row) {
-    asyncDelConfirm('删除确认', '是否删除选中用户?', () => {
-      return Textbook.delTopic({ id: row.userId }).then(() => {
+    asyncDelConfirm('删除确认', '是否删除选中记录?', () => {
+      return Textbook.delTopic({ id: row.id }).then(() => {
         asyncSMessage('操作成功!');
         this.refresh();
       });

+ 1 - 1
front/project/admin/routes/textbook/topicDetail/page.js

@@ -97,7 +97,7 @@ export default class extends Page {
           const { libraryId, textbookSubject } = this.state.search;
           handler = Promise.all([data.topic.filter(row => row).map((row, index) => Textbook.addTopic(Object.assign({ libraryId, textbookSubject }, row, { isOld: row.isOld ? 1 : 0, no: this.state.no + index })))]);
         } else {
-          handler = Textbook.editTopic(Object.assign({ id: this.params.id }, data.topic[0], { isOld: data.topic[0].isOld }));
+          handler = Textbook.editTopic(Object.assign({ id: this.params.id }, data.topic[0], { isOld: data.topic[0].isOld ? 1 : 0 }));
         }
         handler.then(() => {
           asyncSMessage('保存成功');

+ 1 - 1
front/project/admin/routes/user/abnormal/page.js

@@ -163,7 +163,7 @@ export default class extends Page {
         data={this.state.search}
         onChange={data => {
           if (data.time.length > 0) {
-            data.time = [data.time[0].format('YYYY-MM-DD HH:mm:ss'), data.time[1].format('YYYY-MM-DD HH:mm:ss')];
+            data.time = [data.time[0].format('YYYY-MM-DD 00:00:00'), data.time[1].format('YYYY-MM-DD 23:59:59')];
           }
           data.page = 1;
           this.search(data);

+ 22 - 15
front/project/admin/routes/user/detail/page.js

@@ -73,13 +73,20 @@ export default class extends Page {
   }
 
   changeInfo(field, value) {
-    const { real = {} } = this.state;
+    const { real = {}, realEmpty = {} } = this.state;
     real[field] = value;
-    this.setState({ real });
+    if (value) realEmpty[field] = !value;
+    this.setState({ real, realEmpty });
   }
 
   submitReal() {
-    const { real = {} } = this.state;
+    const { real = {}, realEmpty = {} } = this.state;
+    if (!real.realIdentity || !real.realName) {
+      realEmpty.realIdentity = !real.realIdentity;
+      realEmpty.realName = !real.realName;
+      this.setState({ realEmpty });
+      return;
+    }
     User.real(real).then(() => {
       this.close(true, 'real');
     });
@@ -182,7 +189,6 @@ export default class extends Page {
           <h2>实名认证{<Button onClick={() => {
             this.realAction();
           }}>人工认证</Button>}</h2>
-
           {data.realStatus > 0 && <Row>
             <Col span={12}>
               <Form.Item labelCol={{ span: 5 }} wrapperCol={{ span: 16 }} label='身份证id'>
@@ -217,7 +223,7 @@ export default class extends Page {
             </Col>
             <Col span={12}>
               <Form.Item labelCol={{ span: 5 }} wrapperCol={{ span: 16 }} label='考试时间'>
-                {PrepareExaminationTimeMap[data.PrepareExaminationTime]}
+                {PrepareExaminationTimeMap[data.prepareExaminationTime]}
               </Form.Item>
             </Col>
             <Col span={12}>
@@ -284,49 +290,50 @@ export default class extends Page {
   }
 
   renderView() {
+    const { real, realEmpty = {} } = this.state;
     return <div flex>
       {this.renderBase()}
       {this.renderService()}
       {this.renderStudy()}
 
-      {this.state.real && <Modal visible closable title='实名认证' onCancel={() => {
+      {real && <Modal visible closable title='实名认证' onCancel={() => {
         this.close(false, 'real');
       }} onOk={() => {
         this.submitReal();
       }}>
         <Form>
-          <Form.Item labelCol={{ span: 5 }} wrapperCol={{ span: 16 }} label='身份id'>
-            <Input value={this.state.real.realIdentity || ''} placeholder='输入身份id' onChange={e => {
+          <Form.Item labelCol={{ span: 5 }} validateStatus={realEmpty.realIdentity ? 'error' : ''} wrapperCol={{ span: 16 }} label='身份id'>
+            <Input value={real.realIdentity || ''} placeholder='输入身份id' onChange={e => {
               this.changeInfo('realIdentity', e.target.value);
             }} />
           </Form.Item>
-          <Form.Item labelCol={{ span: 5 }} wrapperCol={{ span: 16 }} label='真实姓名'>
-            <Input value={this.state.real.realName || ''} placeholder='输入真实姓名' onChange={e => {
+          <Form.Item labelCol={{ span: 5 }} validateStatus={realEmpty.realName ? 'error' : ''} wrapperCol={{ span: 16 }} label='真实姓名'>
+            <Input value={real.realName || ''} placeholder='输入真实姓名' onChange={e => {
               this.changeInfo('realName', e.target.value);
             }} />
           </Form.Item>
           <Form.Item labelCol={{ span: 5 }} wrapperCol={{ span: 16 }} label='地址'>
-            <Input value={this.state.real.realAddress || ''} placeholder='输入地址' onChange={e => {
+            <Input value={real.realAddress || ''} placeholder='输入地址' onChange={e => {
               this.changeInfo('realAddress', e.target.value);
             }} />
           </Form.Item>
           <Form.Item labelCol={{ span: 5 }} wrapperCol={{ span: 16 }} label='正面照'><Upload
             showUploadList={false}
             beforeUpload={(file) => System.uploadImage(file).then((result) => {
-              this.changeInfo('realFront', result.url);
+              this.changeInfo('realPhotoFront', result.url);
               return Promise.reject();
             })}
           >
-            <Button>上传正面{(this.state.real || {}).realFront ? '(已上传)' : ''}</Button>
+            <Button>上传正面{(real || {}).realPhotoFront ? '(已上传)' : ''}</Button>
           </Upload></Form.Item>
           <Form.Item labelCol={{ span: 5 }} wrapperCol={{ span: 16 }} label='反面照'><Upload
             showUploadList={false}
             beforeUpload={(file) => System.uploadImage(file).then((result) => {
-              this.changeInfo('realBack', result.url);
+              this.changeInfo('realPhotoBack', result.url);
               return Promise.reject();
             })}
           >
-            <Button>上传反面{(this.state.real || {}).realBack ? '(已上传)' : ''}</Button>
+            <Button>上传反面{(real || {}).realPhotoBack ? '(已上传)' : ''}</Button>
           </Upload></Form.Item>
         </Form>
       </Modal>}

+ 1 - 1
front/project/admin/routes/user/list/page.js

@@ -180,7 +180,7 @@ export default class extends Page {
         data={this.state.search}
         onChange={data => {
           if (data.time.length > 0) {
-            data.time = [data.time[0].format('YYYY-MM-DD HH:mm:ss'), data.time[1].format('YYYY-MM-DD HH:mm:ss')];
+            data.time = [data.time[0].format('YYYY-MM-DD 00:00:00'), data.time[1].format('YYYY-MM-DD 23:59:59')];
           }
           data.page = 1;
           this.search(data);

+ 1 - 1
front/project/admin/routes/user/orderDetail/page.js

@@ -39,7 +39,7 @@ export default class extends Page {
       <Form>
         {(checkouts || []).map(row => {
           // 赠送过滤
-          if (row.source.indexOf('gift') >= 0) return null;
+          if ((row.source || '').indexOf('gift') >= 0) return null;
           let title = '';
           switch (row.productType) {
             case 'course':

+ 1 - 1
front/project/admin/stores/system.js

@@ -314,7 +314,7 @@ export default class SystemStore extends BaseStore {
   }
 
   uploadVideo(file) {
-    return this.apiForm('/common/upload/video', { file });
+    return this.apiForm('/common/upload/video', { file }, { time: 1 * 3600 * 1000 });
   }
 }
 

+ 2 - 2
front/project/h5/routes/page/identity/page.js

@@ -108,7 +108,7 @@ export default class extends Page {
     return (
       <div>
         <div className="text">
-          请扫描您的居民身份证原件,领取90天VIP权限。
+          请扫描您的居民身份证原件,领取30天VIP权限。
           <br />
           千行将重视和保护您的隐私。
         </div>
@@ -151,7 +151,7 @@ export default class extends Page {
           <Icon type="check-circle" />
         </div>
         <div className="title">认证完成!</div>
-        <div className="desc">90天VIP权限已赠送至您的账户</div>
+        <div className="desc">30天VIP权限已赠送至您的账户</div>
         <div className="desc">生效时间:{record.useStartTime && formatDate(record.useStartTime, 'YYYY-MM-DD')}</div>
       </div>
     );

BIN
front/project/www/assets/good.png


BIN
front/project/www/assets/qrcode_vague.png


+ 10 - 11
front/project/www/components/Card/index.js

@@ -120,6 +120,11 @@ export default class Card extends Component {
 }
 
 export class Card1 extends Component {
+  constructor(props) {
+    super(props);
+    this.state = {};
+  }
+
   getEndBody() {
     const { data, onPreview } = this.props;
     const { useStartTime, useEndTime } = data;
@@ -149,19 +154,12 @@ export class Card1 extends Component {
   getOpenBody() {
     const { checked } = this.state;
     const { data, onOpen, contract } = this.props;
-    const { teacher, endTime, courseModule } = data;
-    switch (courseModule) {
+    const { teacher, endTime, course = {} } = data;
+    switch (course.courseModule) {
       case 'video':
         return <div className="body">
-          <div className="text">
-            <span>
-              请于{endTime && formatDate(endTime, 'YYYY-MM-DD')}前开通课程
-            </span>
-          </div>
-          <div className="btn">
-            <Button size="lager" radius onClick={() => onOpen && onOpen()}>
-              开通作业
-            </Button>
+          <div className="text-end">
+            <div className="t-1">可至「个人中心-课程」开通</div>
           </div>
         </div>;
       case 'online':
@@ -201,6 +199,7 @@ export class Card1 extends Component {
         {list.length > 0 && <div className="title">近期待完成</div>}
         {list.length === 0 ? (
           <div className="text">
+            <Assets name="good" className="good-icon" />
             <div className="t-1">好棒!</div>
             <div className="t-2">近期的作业都完成啦</div>
           </div>

+ 19 - 3
front/project/www/components/Card/index.less

@@ -159,13 +159,19 @@
     font-size: 18px;
     height: 44px;
     line-height: 44px;
-    padding-left: 36px;
-    padding-right: 32px;
   }
 
   .body {
     padding: 24px;
   }
+
+  .good-icon {
+    position: absolute;
+    left: 50px;
+    width: 40px;
+    height: 40px;
+    top: 125px;
+  }
 }
 
 .module.card1.open {
@@ -218,6 +224,16 @@
         display: inline-block;
       }
     }
+
+    .text-end {
+      padding-top: 70px;
+      text-align: center;
+
+      .t-1 {
+        font-size: 18px;
+        color: #5E677B;
+      }
+    }
   }
 }
 
@@ -291,7 +307,7 @@
     text-align: center;
 
     .t-1 {
-      font-size: 20px;
+      font-size: 18px;
       color: #5E677B;
     }
   }

+ 1 - 0
front/project/www/components/Date/index.less

@@ -110,6 +110,7 @@
 }
 
 .g-date.hide-input {
+  position: unset !important;
 
   .ant-calendar-input-wrap {
     display: none;

+ 10 - 8
front/project/www/components/Examination/index.js

@@ -236,14 +236,16 @@ export default class extends Component {
     return (
       <div className="step-3-layout">
         {info && <a href={info.link} className="a-l" target='_blank'>{info.title}</a>}
-        <Date
-          show={step === 3}
-          theme="filled"
-          value={prepareScoreTime}
-          onChange={date => {
-            this.onChange('prepareScoreTime', date);
-          }}
-        />
+        <div style={{ position: 'relative' }}>
+          <Date
+            show={step === 3}
+            theme="filled"
+            value={prepareScoreTime}
+            onChange={date => {
+              this.onChange('prepareScoreTime', date);
+            }}
+          />
+        </div>
         <div className="action-layout">
           <div className="prev" onClick={() => this.onPrev()}>
             <Icon type="left-circle" theme="filled" />

+ 2 - 2
front/project/www/components/Header/index.js

@@ -46,12 +46,12 @@ function Header(props) {
                     </div>
                     {info.previewNumber > 0 && <div className="t-s-12 nowrap" onClick={(e) => {
                       e.stopPropagation();
-                      linkTo('exercise?tab=course');
+                      linkTo('/exercise?tab1=preview');
                     }}>
                       <span className="t-4">{info.previewNumber}</span> 份作业待完成
                     </div>}
                   </div>
-                  <div className={`item ${info.textbook ? 'more-message' : ''}`} onClick={() => linkTo('/textbook')}>
+                  <div className={`item ${info.textbook ? 'more-message' : ''}`} onClick={() => linkTo('/my/tools?tab=textbook')}>
                     <div className="t-1">
                       <Assets name={info.textbook ? 'jijing_more' : 'jijing'} svg />
                       机经

+ 2 - 2
front/project/www/components/Input/index.js

@@ -3,8 +3,8 @@ import './index.less';
 import { Icon } from 'antd';
 
 export function Input(props) {
-  const { className = '', value, onChange, placeholder, empty } = props;
-  return <input className={`${className} ${empty ? 'empty' : ''}`} value={value} placeholder={placeholder} onChange={data => onChange && onChange(data)} />;
+  const { className = '', value, onChange, placeholder, empty, style } = props;
+  return <input className={`${className} ${empty ? 'empty' : ''}`} style={style} value={value} placeholder={placeholder} onChange={data => onChange && onChange(data)} />;
 }
 
 export function Textarea(props) {

+ 7 - 6
front/project/www/components/Item/index.js

@@ -2,7 +2,7 @@ import React, { Component } from 'react';
 import { Link } from 'react-router-dom';
 import './index.less';
 import Assets from '@src/components/Assets';
-import { getMap, formatSeconds, formatDate } from '@src/services/Tools';
+import { getMap, formatSeconds, formatDate, formatMoney } from '@src/services/Tools';
 import Button from '../Button';
 import More from '../More';
 import { Order } from '../../stores/order';
@@ -136,6 +136,7 @@ export class PackageItem extends Component {
       teacherMap[row.teacher] = true;
     });
     const teachers = Object.keys(teacherMap);
+    const hasGift = data.gift && Object.keys(data.gift).length > 0;
     return (
       <div className="package-item">
         <div className="block m-b-1 c-p" onClick={() => linkTo(`/course/package/detail/${data.id}`)}>
@@ -162,8 +163,8 @@ export class PackageItem extends Component {
             <div className="t m-b-1 t-4 m-r-5 t-s-12 d-i-b p-5">预习作业</div>
             <div className="t m-b-1 t-4 m-r-5 t-s-12 d-i-b p-5">课后答题</div>
           </div>
-          <div className="t-1 t-s-12">赠送服务</div>
-          <div className="p-t-5">
+          {hasGift && <div className="t-1 t-s-12">赠送服务</div>}
+          {hasGift && <div className="p-t-5">
             {data.gift &&
               ServiceKey.map(row => {
                 if (!data.gift[row.value]) return null;
@@ -182,12 +183,12 @@ export class PackageItem extends Component {
                   </div>
                 );
               })}
-          </div>
+          </div>}
         </div>
         <div className="t-1 t-s-12">
-          原价: <span className="t-d-l-t">¥{originMoney}</span>
+          原价: <span className="t-d-l-t">¥{formatMoney(originMoney)}</span>
         </div>
-        <div className="t-7 t-s-18 f-w-b m-b-1">套餐价: ¥{data.price}</div>
+        <div className="t-7 t-s-18 f-w-b m-b-1">套餐价: ¥{formatMoney(data.price)}</div>
         <div className="action">
           <Button radius size="lager" onClick={() => this.buy()}>
             立即购买

+ 7 - 6
front/project/www/components/Item/index.less

@@ -1,6 +1,6 @@
 .single-item {
   display: inline-block;
-  width: 350px;
+  width: 343px;
   position: relative;
   border: 1px solid #E5E5E5FF;
   border-radius: 4px;
@@ -16,7 +16,7 @@
 
     .title {
       font-weight: 500;
-      color: rgba(255, 255, 255, 1)!important;
+      color: rgba(255, 255, 255, 1) !important;
       line-height: 24px;
       font-size: 16px;
       margin-left: 15px;
@@ -32,9 +32,10 @@
         text-align: center;
         margin-right: 5px;
       }
-       a{
-        color: rgba(255, 255, 255, 1)!important;
-       }
+
+      a {
+        color: rgba(255, 255, 255, 1) !important;
+      }
     }
 
     .left {
@@ -95,7 +96,7 @@
 
 .package-item {
   display: inline-block;
-  width: 350px;
+  width: 343px;
   position: relative;
 
   .block {

+ 9 - 7
front/project/www/components/OtherModal/index.js

@@ -780,7 +780,9 @@ export class DownloadModal extends Component {
 // 模考开通确认
 export class OpenConfirmModal extends Component {
   render() {
-    const { show, onConfirm, onCancel, data = {} } = this.props;
+    const { show, onConfirm, onCancel } = this.props;
+    let { data } = this.props;
+    if (!data) data = {};
     return (
       <Modal
         className="open-confirm-modal"
@@ -989,7 +991,7 @@ export class AskCourseModal extends Component {
       this.setState({ empty: { content: !data.content, originContent: !data.originContent } });
       return Promise.reject();
     }
-    return My.addCourseAsk(course.id, courseNo.id, data.position.join(','), data.originContent, data.content).then(
+    return My.addCourseAsk(course.id, courseNo.id, data.position, data.originContent, data.content).then(
       () => {
         this.setState({ data: { position: [], content: '', originContent: '' } });
         if (onConfirm) onConfirm();
@@ -1170,7 +1172,7 @@ export class TextbookFeedbackModal extends Component {
 
   componentWillReceiveProps(nextProps) {
     if (nextProps.show && nextProps.defaultData) {
-      this.setState({ data: Object.assign({}, nextProps.defaultData, this.state.data) });
+      this.setState({ data: Object.assign({}, nextProps.defaultData) });
     }
   }
 
@@ -1196,7 +1198,7 @@ export class TextbookFeedbackModal extends Component {
     }
     return My.addTextbookFeedback(data.textbookSubject, data.target, data.no, data.content)
       .then(() => {
-        this.setState({ data: { content: '' } });
+        this.setState({ data: { content: '', no: '' } });
         if (onConfirm) onConfirm();
       })
       .catch(e => {
@@ -1221,8 +1223,8 @@ export class TextbookFeedbackModal extends Component {
             value={data.textbookSubject}
             theme="white"
             list={textbookSubject}
-            onChange={value => {
-              data.textbookSubject = value;
+            onChange={({ key }) => {
+              data.textbookSubject = key;
               this.setState({ data });
             }}
           />
@@ -1245,7 +1247,7 @@ export class TextbookFeedbackModal extends Component {
               className="m-l-1 b-c-1 t-c"
               empty={empty.no}
               onChange={e => {
-                this.changeData('no', e.target.value);
+                this.changeData('no', !Number(e.target.value) ? '' : Number(e.target.value));
               }}
             />
           </span>

+ 8 - 8
front/project/www/components/PayModal/index.js

@@ -265,11 +265,11 @@ export class PayMModal extends Component {
               <div className="t">支付金额: ¥ {order.money}</div>
             </div>
             <div hidden={pay !== 'bank'} className="bank">
-              <div className="t">汇款银行:中国工商银行上海市浦东支行</div>
-              <div className="t">汇款账号:6100 0000 0000 000</div>
+              <div className="t">汇款银行:{checked ? '中国工商银行上海市浦东支行' : 'xxxxxxxxxxxxx'}</div>
+              <div className="t">汇款账号:{checked ? '6100 0000 0000 000' : 'xxxx xxxx xxxx xxxx'}</div>
             </div>
-            <div className="agree" hidden={!showChecked}>
-              <Checkbox className="m-r-1" checked={checked} onClick={() => {
+            <div className="agree">
+              <Checkbox className="m-r-1" disabled={!showChecked} checked={checked} onClick={() => {
                 this.setState({ showChecked: checked, checked: !checked });
               }} />
               我已阅读并同意<a href={`/contract/${contract.key}`} target="_blank">《{contract.title}》</a>
@@ -354,11 +354,11 @@ export class PayMutilModal extends Component {
               <div className="t">支付金额: ¥ {order.money}</div>
             </div>
             <div hidden={pay !== 'bank'} className="bank">
-              <div className="t">汇款银行:中国工商银行上海市浦东支行</div>
-              <div className="t">汇款账号:6100 0000 0000 000</div>
+              <div className="t">汇款银行:{checked ? '中国工商银行上海市浦东支行' : 'xxxxxxxxxxxxx'}</div>
+              <div className="t">汇款账号:{checked ? '6100 0000 0000 000' : 'xxxx xxxx xxxx xxxx'}</div>
             </div>
-            <div className="agree" hidden={!showChecked}>
-              <Checkbox className="m-r-1" checked={checked} onClick={() => {
+            <div className="agree">
+              <Checkbox className="m-r-1" disabled={!showChecked} checked={checked} onClick={() => {
                 this.setState({ showChecked: checked, checked: !checked });
               }} />
               我已阅读并同意<a href={`/contract/${contract.key}`} target="_blank">《{contract.title}》</a>

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

@@ -8,7 +8,7 @@ function QrCode(props) {
   return (
     <div className={`qr-code-item ${className}`}>
       {!vague && qrCode && <Assets width={width} height={height} src={qrCode} />}
-      {vague && <Assets width={width} height={height} name='' />}
+      {vague && <Assets width={width} height={height} name='qrcode_vague' />}
       {refresh && <div style={{ lineHeight: `${height}px` }} className='fixed-refresh' onClick={() => onRefresh && onRefresh()}><Icon type="sync" /></div>}
     </div>
   );

+ 4 - 9
front/project/www/components/UserAction/index.js

@@ -102,17 +102,13 @@ export default class UserAction extends Component {
                 {item.fixed ? (
                   sortMap[item.key] ? (
                     <GIcon active name="arrow-down" onClick={() => this.onSort(item.key, '')} />
-                  ) : (
-                    <GIcon name="arrow-up" onClick={() => this.onSort(item.key, 'desc')} />
-                  )
+                  ) : (<GIcon name="arrow-up" onClick={() => this.onSort(item.key, 'desc')} />)
                 ) : sortMap[item.key] ? (
                   <Assets
                     name={sortMap[item.key] === 'asc' ? 'seqencing2_up_select' : 'seqencing2_down_select'}
-                    onClick={() => this.onSort(item.key, sortMap[item.key] === 'asc' ? 'desc' : '')}
+                    onClick={() => this.onSort(item.key, sortMap[item.key] === 'asc' ? 'desc' : 'asc')}
                   />
-                ) : (
-                  <Assets name="seqencing2_normal" onClick={() => this.onSort(item.key, 'asc')} />
-                )}
+                ) : (<Assets name="seqencing2_normal" onClick={() => this.onSort(item.key, 'asc')} />)}
               </div>
             );
           })}
@@ -134,8 +130,7 @@ export default class UserAction extends Component {
         {right && (
           <div
             className={`right ${
-              btnList.length === 0 && selectList.length === 0 && sortList.length === 0 ? 'only' : ''
-            }`}
+              btnList.length === 0 && selectList.length === 0 && sortList.length === 0 ? 'only' : ''}`}
           >
             {right}
           </div>

+ 6 - 7
front/project/www/components/UserAction/index.less

@@ -47,7 +47,7 @@
 
   .right {
     text-align: right;
-    margin-left: 15px;
+    margin-left: 0;
   }
 
   .right.only {
@@ -64,7 +64,6 @@
 
   .item.right {
     float: right;
-    margin-right: 0;
   }
 
   .item.sort-item {
@@ -86,14 +85,14 @@
 
   .item {
     display: inline-block;
-    margin-right: 12px;
+    margin-right: 10px;
 
     .label {
-      margin-left: 12px;
+      margin-left: 10px;
     }
 
     .select {
-      margin-left: 12px;
+      margin-left: 10px;
       display: inline-block;
 
       .button {
@@ -103,14 +102,14 @@
   }
 
   .search {
-    margin-left: 15px;
+    margin-left: 10px;
 
     input {
       border-radius: 12px;
       height: 24px;
       padding: 2px 10px;
       line-height: 20px;
-      width: 180px;
+      width: 150px;
       border: 1px solid #e0e0e0;
       font-size: 12px;
     }

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

@@ -65,7 +65,7 @@ export default class UserTable extends Component {
                         (sortMap[item.key] ? (
                           <Assets
                             name={sortMap[item.key] === 'asc' ? 'seqencing2_up_select' : 'seqencing2_down_select'}
-                            onClick={() => this.onSort(item.key, sortMap[item.key] === 'asc' ? 'desc' : '')}
+                            onClick={() => this.onSort(item.key, sortMap[item.key] === 'asc' ? 'desc' : 'asc')}
                           />
                         ) : (<Assets name="seqencing2_normal" onClick={() => this.onSort(item.key, 'asc')} />))}
                     </th>

+ 2 - 1
front/project/www/components/Video/index.js

@@ -1,6 +1,7 @@
 import React, { Component } from 'react';
 import { Slider } from 'antd';
 import './index.less';
+import 'video.js/dist/video-js.css';
 import videojs from 'video.js';
 import Assets from '@src/components/Assets';
 import { generateUUID, formatSecondAuto } from '@src/services/Tools';
@@ -195,7 +196,7 @@ export default class Video extends Component {
             ref={node => {
               this.videoNode = node;
             }}
-          // vjs-fluid
+            className="video-js vjs-fluid"
           />
           {water}
           {!playing && <Assets className="play c-p" name="play" />}

+ 3 - 0
front/project/www/components/Video/index.less

@@ -58,6 +58,8 @@
 
   .ant-slider {
     margin: 0;
+    height: 6px;
+    padding: 0;
 
     .ant-slider-rail {
       border-radius: 0;
@@ -138,6 +140,7 @@
     width: 100%;
     height: 100%;
     position: relative;
+    overflow: hidden;
 
     .vjs_video_3-dimensions {
       width: 100% !important;

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

@@ -258,7 +258,7 @@ export default class extends Component {
             </Button>
           </div>
         </div>
-        {showInvite && <Invite data={data} />}
+        {showInvite && <Invite user={{ info: data }} />}
       </div>
     );
   }

+ 2 - 2
front/project/www/routes/course/dataDetail/page.js

@@ -3,7 +3,7 @@ import { Link } from 'react-router-dom';
 import './index.less';
 import Page from '@src/containers/Page';
 import Assets from '@src/components/Assets';
-import { getMap, formatDate } from '@src/services/Tools';
+import { getMap, formatDate, formatMoney } from '@src/services/Tools';
 import Footer from '../../../components/Footer';
 import { Contact, AnswerCarousel, Comment } from '../../../components/Other';
 import { FaqModal, CommentModal, FinishModal } from '../../../components/OtherModal';
@@ -140,7 +140,7 @@ export default class extends Page {
                 <div style={{ marginTop: 12 }} className="d-i-b t-1 t-s-16 v-a-t">
                   价格:
                 </div>
-                <div className="d-i-b t-7 t-s-28 f-w-b"> ¥ {data.price}</div>
+                <div className="d-i-b t-7 t-s-28 f-w-b"> ¥ {formatMoney(data.price)}</div>
               </div>
               <div className="action">
                 {!data.have && (

+ 1 - 1
front/project/www/routes/course/detail/index.less

@@ -85,7 +85,7 @@
           .video-fixed {
             display: none;
             width: 400px;
-            bottom: 60px;
+            bottom: 56px;
             right: 10px;
             position: absolute;
 

+ 32 - 23
front/project/www/routes/course/detail/page.js

@@ -40,59 +40,59 @@ export default class extends Page {
       {
         title: '预习作业',
         key: 'paper',
-        render: text => {
-          text = text || {};
-          const progress = text.report ? formatPercent(text.report.userNumber, text.report.questionNumber) : 0;
-          const times = text.paper ? text.paper.resetTimes : 0;
+        render: (text, record) => {
+          const { paper = {} } = record;
+          const progress = paper.report ? formatPercent(paper.report.userNumber, paper.report.questionNumber) : 0;
+          const times = paper.paper ? paper.paper.resetTimes : 0;
           return (
             <div>
               <div className="v-a-m d-i-b">
                 <ProgressText width={50} size="small" times={times} progress={progress} unit="次" />
               </div>
-              {!text.report && (
+              {!paper.report && (
                 <IconButton
                   className="m-l-2"
                   type="start"
                   tip="Start"
                   onClick={() => {
                     User.needLogin().then(() => {
-                      Question.startLink('preview', text);
+                      Question.startLink('preview', paper);
                     });
                   }}
                 />
               )}
-              {text.report && !text.report.isFinish && (
+              {paper.report && !paper.report.isFinish && (
                 <IconButton
                   className="m-l-2"
                   type="continue"
                   tip="Continue"
                   onClick={() => {
                     User.needLogin().then(() => {
-                      Question.continueLink('preview', text);
+                      Question.continueLink('preview', paper);
                     });
                   }}
                 />
               )}
-              {text.report && (
+              {paper.report && (
                 <IconButton
                   className="m-l-2"
                   type="restart"
                   tip="Restart"
                   onClick={() => {
                     User.needLogin().then(() => {
-                      Question.restart('preview', text);
+                      Question.restart('preview', paper.id);
                     });
                   }}
                 />
               )}
-              {text.report && !!text.report.isFinish && (
+              {paper.report && !!paper.report.isFinish && (
                 <IconButton
                   className="m-l-5"
                   type="report"
                   tip="Report"
                   onClick={() => {
                     User.needLogin().then(() => {
-                      Question.reportLink('preview', text);
+                      Question.reportLink('preview', paper);
                     });
                   }}
                 />
@@ -105,24 +105,26 @@ export default class extends Page {
         title: '进度',
         key: 'progress',
         render: (text, record) => {
-          const { paper = {} } = record;
-          return `${paper.paper && paper.paper.finishTimes > 0 ? `${paper.paper.finishTimes}次+` : ''}${
-            paper.report ? formatPercent(paper.report.userNumber, paper.report.questionNumber, false) : '0%'}`;
+          const { progress } = record;
+          return `${progress && progress.times > 0 ? `${progress.times}次+` : ''}${
+            progress ? `${progress.progress}%` : '0%'}`;
         },
       },
       {
         title: '最近学习',
         key: 'lastTime',
-        render: (text, record) => {
-          const { paper = {} } = record;
-          return paper.report && formatDate(paper.report.updateTime, 'YYYY-MM-DD HH:mm:ss');
+        render: (text, rr) => {
+          const { record } = rr;
+          return record && formatDate(record.createTime, 'YYYY-MM-DD HH:mm:ss');
         },
       },
       {
         title: '笔记',
         key: 'note',
         render: (text, record) => {
-          return <GIcon name="note" active={record.note} />;
+          return <GIcon name="note" active={record.note} onClick={() => {
+            this.setState({ showNote: true, note: this.noteMap ? this.noteMap[record.id] || { courseNoId: record.id } : { courseNoId: record.id } });
+          }} />;
         },
       },
       {
@@ -176,6 +178,12 @@ export default class extends Page {
         row.progressMap[progress.courseNoId] = progress;
       });
     }
+    row.recordMap = {};
+    if (row.records) {
+      row.records.forEach(record => {
+        row.recordMap[record.courseNoId] = record;
+      });
+    }
 
     row.courseNoMap = {};
     row.courseTime = 0;
@@ -183,8 +191,9 @@ export default class extends Page {
       row.courseNos.forEach(no => {
         row.courseNoMap[no.id] = no;
         row.courseTime += no.time;
-        no.paper = row.paperMap[no.id];
+        no.paper = row.paperMap[no.no];
         no.progress = row.progressMap[no.id];
+        no.record = row.recordMap[no.id];
       });
     }
     if (row.currentNo) {
@@ -204,7 +213,7 @@ export default class extends Page {
       this.setState({ data: result });
       // 选择课时
       if (this.state.search.no) {
-        this.onChangeItem(this.state.search.no);
+        this.onChangeItem(Number(this.state.search.no));
       } else {
         this.onChangeItem(1);
       }
@@ -259,7 +268,7 @@ export default class extends Page {
     const timelineSelect = [];
     const current = data.courseNos[index];
     if (current) {
-      const max = current.time;
+      const max = current.time / 60;
       let start = 0;
       let end = start + 5;
       while (start < max) {
@@ -592,7 +601,7 @@ export default class extends Page {
           return (
             <div className={`item ${item.no === no ? 'active' : ''}`} onClick={() => this.onChangeItem(item.no)}>
               <span className="t-1">课时{item.no}</span>
-              <span className="t-2">{item.title}</span>
+              <span className="t-2 p-l-1">{item.title}</span>
             </div>
           );
         })}

+ 3 - 3
front/project/www/routes/course/main/page.js

@@ -2,7 +2,7 @@ import React from 'react';
 import './index.less';
 import Assets from '@src/components/Assets';
 import Page from '@src/containers/Page';
-import { getMap } from '@src/services/Tools';
+import { getMap, formatMoney } from '@src/services/Tools';
 import Footer from '../../../components/Footer';
 import { FaqModal, FinishModal } from '../../../components/OtherModal';
 import { CommentFalls, AnswerCarousel, Consultation, Contact } from '../../../components/Other';
@@ -102,9 +102,9 @@ export default class extends Page {
                     })}
                 </div>
                 <div className="t-8">
-                  总价值: <span className="t-d-l-t">¥{originMoney}</span>
+                  总价值: <span className="t-d-l-t">¥{formatMoney(originMoney)}</span>
                 </div>
-                <div className="t-1 t-s-18 f-w-b m-b-1">套餐价: ¥{data.price}</div>
+                <div className="t-1 t-s-18 f-w-b m-b-1">套餐价: ¥{formatMoney(data.price)}</div>
                 <div className="m-b-5">
                   <Button size="lager" onClick={() => User.needLogin().then(() => Order.addCheckout({ productType: 'course_package', productId: data.id }).then(() => linkTo('/cart')))}>立即购买</Button>
                 </div>

+ 1 - 1
front/project/www/routes/course/note/page.js

@@ -268,7 +268,7 @@ export default class extends Page {
         </div>
       </Modal>,
       <Modal show={showExportWait} title="导出" confirmText="好的,知道了" btnAlign="center" onConfirm={() => this.setState({ showExportWait: false })}>
-        <div className="t-2 t-s-18">正在下载中,请不要关闭当前页面!</div>
+        <div className="t-2 t-s-18">正在导出中,请不要关闭当前页面!</div>
       </Modal>,
       <Examination
         show={showExamination}

+ 3 - 3
front/project/www/routes/course/online/index.less

@@ -6,7 +6,7 @@
   }
 
   .top {
-    padding: 8px 0;
+    padding: 8px 20px;
     height: 60px;
 
     .tabs.text {
@@ -31,7 +31,7 @@
     .tab-1-list {
       padding-top: 30px;
       padding-bottom: 40px;
-      margin: 0 -20px;
+      margin: 0 -10px;
 
       .single-item {
         margin-bottom: 20px;
@@ -43,7 +43,7 @@
     .tab-2-list {
       padding-top: 30px;
       padding-bottom: 40px;
-      margin: 0 -20px;
+      margin: 0 -10px;
 
       .package-item {
         margin-bottom: 20px;

+ 17 - 14
front/project/www/routes/course/online/page.js

@@ -174,23 +174,26 @@ export default class extends Page {
         key: row.key,
       };
     });
-    child.unshift({
-      title: '全部',
-      key: '',
-    });
+    const filterList = [
+      {
+        key: 'parentStructId',
+        children: courseStructSelect,
+      }];
+    if (child.length > 0) {
+      child.unshift({
+        title: '全部',
+        key: '',
+      });
+      filterList.push({
+        key: 'structId',
+        children: child,
+      });
+    }
+
     return [
       <Filter
         filter={filterMap}
-        list={[
-          {
-            key: 'parentStructId',
-            children: courseStructSelect,
-          },
-          {
-            key: 'structId',
-            children: child,
-          },
-        ]}
+        list={filterList}
         onFilter={(key, value) => this.onFilter(key, value)}
       />,
       <div className="tab-1-list">

+ 5 - 4
front/project/www/routes/course/packageDetail/page.js

@@ -3,7 +3,7 @@ import { Link } from 'react-router-dom';
 import './index.less';
 import Assets from '@src/components/Assets';
 import Page from '@src/containers/Page';
-import { getMap } from '@src/services/Tools';
+import { getMap, formatMoney } from '@src/services/Tools';
 import Button from '../../../components/Button';
 import { SingleItem } from '../../../components/Item';
 import Footer from '../../../components/Footer';
@@ -63,6 +63,7 @@ export default class extends Page {
 
   renderView() {
     const { data = {}, base = {}, add } = this.state;
+    const hasGift = data.gift && Object.keys(data.gift).length > 0;
     return (
       <div>
         <div className="top content t-8">
@@ -87,7 +88,7 @@ export default class extends Page {
               </Button>
             </div>
           </div>
-          <div className="t-7 t-s-18 f-w-b m-b-1">套餐价: ¥ {data.price}</div>
+          <div className="t-7 t-s-18 f-w-b m-b-1">套餐价: ¥ {formatMoney(data.price)}</div>
           <div className="t-1 t-s-16 desc">{data.description}</div>
           <div className="main-title">包含课程</div>
           <div className="list">
@@ -104,8 +105,8 @@ export default class extends Page {
               <Assets name="" />
             </div>
           </div>
-          {data.gift && <div className="main-title">赠送服务</div>}
-          {data.gift && <div className="list">
+          {hasGift && <div className="main-title">赠送服务</div>}
+          {hasGift && <div className="list">
             {ServiceKey.map(row => {
               if (!data.gift[row.value]) return null;
               const list = ServiceParamMap[row.value];

+ 22 - 16
front/project/www/routes/course/vs/page.js

@@ -3,6 +3,7 @@ import './index.less';
 import { Icon } from 'antd';
 import Page from '@src/containers/Page';
 import Assets from '@src/components/Assets';
+import { formatMoney } from '@src/services/Tools';
 import Footer from '../../../components/Footer';
 import { FaqModal, CommentModal, FinishModal } from '../../../components/OtherModal';
 import { Contact, Comment, Consultation, AnswerCarousel } from '../../../components/Other';
@@ -79,6 +80,7 @@ export default class extends Page {
   changeNumber(number) {
     const { data } = this.state;
     let price = data.price * number;
+    const days = data.expirePreDays / 10 * number;
     let max = 0;
     let maxIndex = -1;
     this.promote.forEach((row, i) => {
@@ -92,22 +94,26 @@ export default class extends Page {
     if (maxIndex >= 0) {
       price *= this.promote[maxIndex].percent / 100;
     }
-    this.setState({ number, price });
+    this.setState({ number, price, days });
   }
 
   buy() {
-    const { data } = this.state;
-    Order.speedPay({ productType: 'course', productId: data.id, number: 1 }).then(result => {
-      User.needPay(result).then(() => {
-        linkTo('/my/course');
+    const { data, number } = this.state;
+    User.needLogin().then(() => {
+      Order.speedPay({ productType: 'course', productId: data.id, number }).then(result => {
+        User.needPay(result).then(() => {
+          linkTo('/my/course');
+        });
       });
     });
   }
 
   add() {
-    const { data } = this.state;
-    Order.addCheckout({ productType: 'course', productId: data.id, number: 1 }).then(() => {
-      this.setState({ add: true });
+    const { data, number } = this.state;
+    User.needLogin().then(() => {
+      Order.addCheckout({ productType: 'course', productId: data.id, number }).then(() => {
+        this.setState({ add: true });
+      });
     });
   }
 
@@ -151,7 +157,7 @@ export default class extends Page {
   }
 
   renderDetail() {
-    const { info, key, data, number, price } = this.state;
+    const { info, key, data, number, price, days } = this.state;
     return [
       <div className="center">
         <div className="content">
@@ -161,7 +167,7 @@ export default class extends Page {
               const course = this.courseVsMap[t.value] || {};
               return (
                 <div className={`item ${key === t.value ? 'active' : ''}`} onClick={() => this.onChangeItem(t.value)}>
-                  <Assets name={t.cover} />
+                  <Assets name={t.icon} />
                   <div className="t-1 t-s-20 f-w-b">{course.title}</div>
                   <div className="t-2">{course.comment}</div>
                 </div>
@@ -170,7 +176,7 @@ export default class extends Page {
           </div>
           <div className="item-detail">
             <div className="left">
-              <Assets src={data.cover} />
+              <div className="img c-p" style={{ backgroundImage: `url(${data.cover})`, backgroundSize: '100% 100%', width: '100%', height: '100%' }} />
             </div>
             <div className="right">
               <div className="t-1 t-s-16 m-b-2" dangerouslySetInnerHTML={{ __html: data.serviceContent }} />
@@ -196,7 +202,7 @@ export default class extends Page {
                 <div style={{ width: 120 }} className="d-i-b t-2">
                   课程有效期
                 </div>
-                <div className="d-i-b t-1">{data.expirePreDays}天/10课时</div>
+                <div className="d-i-b t-1">{days}天</div>
               </div>
               <div className="m-b-5 input">
                 <div style={{ width: 120 }} className="d-i-b t-2">
@@ -207,12 +213,12 @@ export default class extends Page {
                   <Icon
                     className="up"
                     type="caret-up"
-                    onClick={() => number < data.maxNumber && this.setState({ number: number + 1 })}
+                    onClick={() => number < data.maxNumber && this.changeNumber(number + 1)}
                   />
                   <Icon
                     className="down"
                     type="caret-down"
-                    onClick={() => number !== 1 && number > data.minNumber && this.setState({ number: number - 1 })}
+                    onClick={() => number !== 1 && number > data.minNumber && this.changeNumber(number - 1)}
                   />
                 </div>
               </div>
@@ -220,7 +226,7 @@ export default class extends Page {
                 <div style={{ width: 120 }} className="d-i-b t-2">
                   课程总价
                 </div>
-                <div className="d-i-b t-7 t-s-20 f-w-b"> ¥ {price}</div>
+                <div className="d-i-b t-7 t-s-20 f-w-b"> ¥ {formatMoney(price)}</div>
               </div>
               <div className="action">
                 {data.have && <Button className="m-r-1" radius size="lager" onClick={() => linkTo('/my/course')}>
@@ -236,7 +242,7 @@ export default class extends Page {
             </div>
           </div>
         </div>
-      </div>,
+      </div >,
       <div className="bottom">
         <div className="content">
           <Tabs

+ 42 - 33
front/project/www/routes/examination/list/page.js

@@ -17,7 +17,7 @@ export default class extends Page {
     this.qxCatColumns = [
       {
         title: '模考',
-        width: 210,
+        width: 250,
         align: 'left',
         render: record => {
           let progress = 0;
@@ -49,13 +49,13 @@ export default class extends Page {
       },
       {
         title: 'Total',
-        width: 110,
+        width: 100,
         align: 'left',
         render: record => {
           return [
             <div className="table-row">
               <div className="night f-s-16 f-w-b">
-                {record.report ? `${record.report.score.totalScore}分${record.report.score.totalRank}th` : '-分-th'}
+                {record.report && record.report.score ? `${record.report.score.totalScore}分${record.report.score.totalRank}th` : '-分-th'}
               </div>
               <div className="f-s-12">全站: {record.totalTimes > 0 ? Math.round(record.totalScore / record.totalTimes) : '-'}分</div>
             </div>,
@@ -72,13 +72,13 @@ export default class extends Page {
       },
       {
         title: 'Verbal',
-        width: 110,
+        width: 100,
         align: 'left',
         render: record => {
           return [
             <div className="table-row">
               <div className="night f-s-16 f-w-b">
-                {record.report ? `${record.report.score.verbalScore}分${record.report.score.verbalRank}th` : '-分-th'}
+                {record.report && record.report.score ? `${record.report.score.verbalScore}分${record.report.score.verbalRank}th` : '-分-th'}
               </div>
               <div className="f-s-12">全站: {record.totalTimes > 0 ? Math.round(record.verbalScore / record.totalTimes) : '-'}分</div>
             </div>,
@@ -95,12 +95,12 @@ export default class extends Page {
       },
       {
         title: 'Quant',
-        width: 110,
+        width: 100,
         align: 'left',
         render: record => {
           return [<div className="table-row">
             <div className="night f-s-16 f-w-b">
-              {record.report ? `${record.report.score.quantScore}分${record.report.score.quantRank}th` : '-分-th'}
+              {record.report && record.report.score ? `${record.report.score.quantScore}分${record.report.score.quantRank}th` : '-分-th'}
             </div>
             <div className="f-s-12">全站: {record.totalTimes > 0 ? Math.round(record.quantScore / record.totalTimes) : '-'}分</div>
           </div>,
@@ -117,12 +117,12 @@ export default class extends Page {
       },
       {
         title: 'IR',
-        width: 110,
+        width: 100,
         align: 'left',
         render: record => {
           return [<div className="table-row">
             <div className="night f-s-16 f-w-b">
-              {record.report ? `${record.report.score.irScore}分${record.report.score.irRank}th` : '-分-th'}
+              {record.report && record.report.score ? `${record.report.score.irScore}分${record.report.score.irRank}th` : '-分-th'}
             </div>
             <div className="f-s-12">全站: {record.totalTimes > 0 ? Math.round(record.irScore / record.totalTimes) : '-'}分</div>
           </div>,
@@ -224,7 +224,7 @@ export default class extends Page {
     this.catColumns = [
       {
         title: '模考',
-        width: 210,
+        width: 250,
         align: 'left',
         render: record => {
           let progress = 0;
@@ -246,60 +246,60 @@ export default class extends Page {
       },
       {
         title: 'Total',
-        width: 110,
+        width: 100,
         align: 'left',
         render: record => {
           return (
             <div className="table-row">
               <div className="night f-s-16 f-w-b">
-                {record.report ? `${record.report.score.totalScore}分${record.report.score.totalRank}th` : '-分-th'}
+                {record.report && record.report.score ? `${record.report.score.totalScore}分${record.report.score.totalRank}th` : '-分-th'}
               </div>
-              <div className="f-s-12">全站: {Math.round(record.totalScore / record.totalTimes)}分</div>
+              <div className="f-s-12">全站: {record.totalTimes ? Math.round(record.totalScore / record.totalTimes) : '-'}分</div>
             </div>
           );
         },
       },
       {
         title: 'Verbal',
-        width: 110,
+        width: 100,
         align: 'left',
         render: record => {
           return (
             <div className="table-row">
               <div className="night f-s-16 f-w-b">
-                {record.report ? `${record.report.score.verbalScore}分${record.report.score.verbalRank}th` : '-分-th'}
+                {record.report && record.report.score ? `${record.report.score.verbalScore}分${record.report.score.verbalRank}th` : '-分-th'}
               </div>
-              <div className="f-s-12">全站: {Math.round(record.verbalScore / record.totalTimes)}分</div>
+              <div className="f-s-12">全站: {record.totalTimes ? Math.round(record.verbalScore / record.totalTimes) : '-'}分</div>
             </div>
           );
         },
       },
       {
         title: 'Quant',
-        width: 110,
+        width: 100,
         align: 'left',
         render: record => {
           return (
             <div className="table-row">
               <div className="night f-s-16 f-w-b">
-                {record.report ? `${record.report.score.quantScore}分${record.report.score.quantRank}th` : '-分-th'}
+                {record.report && record.report.score ? `${record.report.score.quantScore}分${record.report.score.quantRank}th` : '-分-th'}
               </div>
-              <div className="f-s-12">全站: {Math.round(record.quantScore / record.totalTimes)}分</div>
+              <div className="f-s-12">全站: {record.totalTimes ? Math.round(record.quantScore / record.totalTimes) : '-'}分</div>
             </div>
           );
         },
       },
       {
         title: 'IR',
-        width: 110,
+        width: 100,
         align: 'left',
         render: record => {
           return (
             <div className="table-row">
               <div className="night f-s-16 f-w-b">
-                {record.report ? `${record.report.score.irScore}分${record.report.score.irRank}th` : '-分-th'}
+                {record.report && record.report.score ? `${record.report.score.irScore}分${record.report.score.irRank}th` : '-分-th'}
               </div>
-              <div className="f-s-12">全站: {Math.round(record.irScore / record.totalTimes)}分</div>
+              <div className="f-s-12">全站: {record.totalTimes ? Math.round(record.irScore / record.totalTimes) : '-'}分</div>
             </div>
           );
         },
@@ -391,7 +391,7 @@ export default class extends Page {
     this.columns = [
       {
         title: '模考',
-        width: 210,
+        width: 250,
         align: 'left',
         render: record => {
           let progress = 0;
@@ -410,7 +410,7 @@ export default class extends Page {
       },
       {
         title: 'Total',
-        width: 110,
+        width: 100,
         align: 'left',
         render: () => {
           return (
@@ -422,14 +422,14 @@ export default class extends Page {
       },
       {
         title: 'Verbal',
-        width: 110,
+        width: 100,
         align: 'left',
         render: record => {
           return (
             <div className="table-row">
               <div className="f-s-12">
-                {record.report
-                  ? formatPercent(record.report.setting.number.verbal, this.nums.verbal.number, false)
+                {record.report && record.report.setting
+                  ? formatPercent(record.report.setting.real ? record.report.setting.real.verbal : record.report.setting.number.verbal, this.nums.verbal.number, false)
                   : '0%'}
               </div>
             </div>
@@ -438,14 +438,14 @@ export default class extends Page {
       },
       {
         title: 'Quant',
-        width: 110,
+        width: 100,
         align: 'left',
         render: record => {
           return (
             <div className="table-row">
               <div className="f-s-12">
-                {record.report
-                  ? formatPercent(record.report.setting.number.quant, this.nums.quant.number, false)
+                {record.report && record.report.setting
+                  ? formatPercent(record.report.setting.real ? record.report.setting.real.quant : record.report.setting.number.quant, this.nums.quant.number, false)
                   : '0%'}
               </div>
             </div>
@@ -454,13 +454,13 @@ export default class extends Page {
       },
       {
         title: 'IR',
-        width: 110,
+        width: 100,
         align: 'left',
         render: record => {
           return (
             <div className="table-row">
               <div className="f-s-12">
-                {record.report ? formatPercent(record.report.setting.number.ir, this.nums.ir.number, false) : '0%'}
+                {record.report && record.report.setting ? formatPercent(record.report.setting.real ? record.report.setting.real.ir : record.report.setting.number.ir, this.nums.ir.number, false) : '0%'}
               </div>
             </div>
           );
@@ -544,8 +544,17 @@ export default class extends Page {
   init() {
     const { id } = this.params;
     Main.getExaminationParent(id).then(result => {
+      let tab1 = '';
+      let tab2 = '';
       const navs = result.map(row => {
         row.title = `${row.titleZh}${row.titleEn}`;
+        if (!tab1) {
+          tab1 = row.extend;
+        } else if (!tab2) {
+          tab2 = row.extend;
+        }
+        row.tab1 = tab1;
+        row.tab2 = tab2;
         return row;
       });
       this.cat = navs.filter(row => row.isAdapt > 0).length > 0;
@@ -608,7 +617,7 @@ export default class extends Page {
           <Breadcrumb separator=">">
             <Breadcrumb.Item href="/examination">模考</Breadcrumb.Item>
             {(navs || []).map(row => {
-              return <Breadcrumb.Item>{row.title}</Breadcrumb.Item>;
+              return <Breadcrumb.Item href={`/examination?tab1=${row.tab1}&tab2=${row.tab2}`}>{row.title}</Breadcrumb.Item>;
             })}
           </Breadcrumb>
           <ListTable

+ 1 - 0
front/project/www/routes/examination/main/page.js

@@ -273,6 +273,7 @@ export default class extends Page {
                   col="3"
                   data={struct}
                   onOpen={() => {
+                    struct.unUseRecord.title = '数学机经';
                     this.setState({ record: struct.unUseRecord });
                   }}
                 />;

+ 1 - 1
front/project/www/routes/exercise/list/page.js

@@ -55,7 +55,7 @@ export default class extends Page {
         );
       },
     }, {
-      title: '全站用时',
+      title: '平均用时',
       width: 150,
       align: 'left',
       render: (record) => {

+ 138 - 138
front/project/www/routes/exercise/main/page.js

@@ -3,7 +3,7 @@ import './index.less';
 import { Modal, Tooltip } from 'antd';
 // import { Link } from 'react-router-dom';
 import Page from '@src/containers/Page';
-import { asyncConfirm } from '@src/services/AsyncTools';
+import { asyncConfirm, asyncSMessage } from '@src/services/AsyncTools';
 import { formatTreeData, formatSeconds, formatDate, formatPercent, getMap } from '@src/services/Tools';
 import { AnswerCarousel, Comment } from '../../../components/Other';
 import Continue from '../../../components/Continue';
@@ -38,102 +38,101 @@ const PREVIEW_LIST = 'PREVIEW_LIST';
 const CourseModuleMap = getMap(CourseModule, 'value', 'label');
 const CourseSorted = getMap([{ value: 'sc', sort: 1 }, { value: 'rc', sort: 2 }, { value: 'cr', sort: 3 }], 'value', 'sort');
 
-const exerciseColumns = [{
-  title: '练习册',
-  width: 250,
-  align: 'left',
-  render: record => {
-    let progress = 0;
-    if (record.report) {
-      progress = formatPercent(record.report.userNumber, record.report.questionNumber);
-    }
-    return (
-      <div className="table-row">
-        <div className="night f-s-16">{record.title}</div>
-        <div>
-          <ProgressText progress={progress} times={record.paper ? record.paper.resetTimes : 0} size="small" />
-        </div>
-      </div>
-    );
-  },
-}, {
-  title: '正确率',
-  width: 150,
-  align: 'left',
-  render: item => {
-    return (
-      <div className="table-row">
-        <div className="night f-s-16 f-w-b">--</div>
-        <div className="f-s-12">{formatPercent(item.stat.totalCorrect, item.stat.totalNumber, false)}</div>
-      </div>
-    );
-  },
-}, {
-  title: '全站用时',
-  width: 150,
-  align: 'left',
-  render: record => {
-    let time = '--';
-    if (record.report) {
-      time = formatSeconds(record.report.userTime / record.report.userNumber);
-    }
-    return (
-      <div className="table-row">
-        <div className="night f-s-16 f-w-b">{time}</div>
-        <div className="f-s-12">全站{formatSeconds(record.stat.totalTime / record.stat.totalNumber)}</div>
-      </div>
-    );
-  },
-}, {
-  title: '最近做题',
-  width: 150,
-  align: 'left',
-  render: (record) => {
-    const time = record.report ? record.report.updateTime : record.paper ? record.paper.latestTime : null;
-    return (
-      <div className="table-row">
-        <div>{time && formatDate(time, 'YYYY-MM-DD')}</div>
-        <div>{time && formatDate(time, 'HH:mm')}</div>
-      </div>
-    );
-  },
-}, {
-  title: '操作',
-  width: 180,
-  align: 'left',
-  render: record => {
-    return (
-      <div className="table-row p-t-1">
-        {!record.report && <IconButton type="start" tip="Start" onClick={() => {
-          Question.startLink('exercise', record);
-        }} />}
-        {(record.report && !record.report.isFinish) && <IconButton className="m-r-2" type="continue" tip="Continue" onClick={() => {
-          Question.continueLink('exercise', record);
-        }} />}
-        {(record.report) && <IconButton type="restart" tip="Restart" onClick={() => {
-          this.restart(record);
-        }} />}
-      </div>
-    );
-  },
-}, {
-  title: '报告',
-  width: 30,
-  align: 'right',
-  render: record => {
-    return (
-      <div className="table-row p-t-1">
-        {record.report && !!record.report.isFinish && <IconButton type="report" tip="Report" onClick={() => {
-          Question.reportLink(record);
-        }} />}
-      </div>
-    );
-  },
-}];
-
 export default class extends Page {
   constructor(props) {
     super(props);
+    this.previewColumns = [{
+      title: '练习册',
+      width: 250,
+      align: 'left',
+      render: record => {
+        let progress = 0;
+        if (record.report) {
+          progress = formatPercent(record.report.userNumber, record.report.questionNumber);
+        }
+        return (
+          <div className="table-row">
+            <div className="night f-s-16">{record.title}</div>
+            <div>
+              <ProgressText progress={progress} times={record.paper ? record.paper.resetTimes : 0} size="small" />
+            </div>
+          </div>
+        );
+      },
+    }, {
+      title: '正确率',
+      width: 150,
+      align: 'left',
+      render: item => {
+        return (
+          <div className="table-row">
+            <div className="night f-s-16 f-w-b">--</div>
+            <div className="f-s-12">{formatPercent(item.stat.totalCorrect, item.stat.totalNumber, false)}</div>
+          </div>
+        );
+      },
+    }, {
+      title: '全站用时',
+      width: 150,
+      align: 'left',
+      render: record => {
+        let time = '--';
+        if (record.report) {
+          time = formatSeconds(record.report.userTime / record.report.userNumber);
+        }
+        return (
+          <div className="table-row">
+            <div className="night f-s-16 f-w-b">{time}</div>
+            <div className="f-s-12">全站{formatSeconds(record.stat.totalTime / record.stat.totalNumber)}</div>
+          </div>
+        );
+      },
+    }, {
+      title: '最近做题',
+      width: 150,
+      align: 'left',
+      render: (record) => {
+        const time = record.report ? record.report.updateTime : record.paper ? record.paper.latestTime : null;
+        return (
+          <div className="table-row">
+            <div>{time && formatDate(time, 'YYYY-MM-DD')}</div>
+            <div>{time && formatDate(time, 'HH:mm')}</div>
+          </div>
+        );
+      },
+    }, {
+      title: '操作',
+      width: 180,
+      align: 'left',
+      render: record => {
+        return (
+          <div className="table-row p-t-1">
+            {!record.report && <IconButton type="start" tip="Start" onClick={() => {
+              Question.startLink('preview', record);
+            }} />}
+            {(record.report && !record.report.isFinish) && <IconButton className="m-r-2" type="continue" tip="Continue" onClick={() => {
+              Question.continueLink('preview', record);
+            }} />}
+            {(record.report) && <IconButton type="restart" tip="Restart" onClick={() => {
+              this.restart(record);
+            }} />}
+          </div>
+        );
+      },
+    }, {
+      title: '报告',
+      width: 30,
+      align: 'right',
+      render: record => {
+        return (
+          <div className="table-row p-t-1">
+            {record.report && !!record.report.isFinish && <IconButton type="report" tip="Report" onClick={() => {
+              Question.reportLink(record);
+            }} />}
+          </div>
+        );
+      },
+    }];
     this.sentenceColums = [{
       title: '练习册',
       width: 250,
@@ -267,7 +266,6 @@ export default class extends Page {
 
   initState() {
     this.code = null;
-    this.columns = exerciseColumns;
     this.exerciseProgress = {};
     this.courseProgress = {};
     this.inited = false;
@@ -475,7 +473,7 @@ export default class extends Page {
       });
       const now = new Date().getTime();
       courseMap.open = result.filter(row => !row.isUsed && (!row.useEndTime || new Date(row.useEndTime).getTime() > now));
-      courseMap.end = result.filter(row => (!row.isSuspend || (row.isSuspend && row.restoreTime)) && new Date(row.useEndTime).getTime() < now);
+      courseMap.end = result.filter(row => row.isUsed && (!row.isSuspend || (row.isSuspend && row.restoreTime)) && new Date(row.useEndTime).getTime() < now);
       courseMap.process = result.filter(row => row.isUsed && ((!row.isSuspend && new Date(row.useEndTime).getTime() >= now) || (row.isSuspend && !row.restoreTime)));
       this.setState({ courseMap });
     });
@@ -488,7 +486,7 @@ export default class extends Page {
   refreshListPreview() {
     const { recordId, endTime, finish } = this.state;
     Course.listPreview({ recordId, endTime, finish }).then(result => {
-      this.setState({ previews: result.list });
+      this.setState({ previews: result });
     });
     Course.record(recordId).then(result => {
       this.setState({ record: result });
@@ -610,6 +608,8 @@ export default class extends Page {
   open(recordId) {
     Order.useRecord(recordId).then(() => {
       this.refresh();
+    }).catch((err) => {
+      asyncSMessage(err.message, 'error');
     });
   }
 
@@ -825,55 +825,55 @@ export default class extends Page {
 
   renderPreviewList() {
     const { previews = [], record = {}, search = {} } = this.state;
-    const { finish, endTime } = search;
-    let finishTime = '';
-    if (endTime) {
-      const endTimeD = new Date(endTime);
-      const now = new Date();
-      if ((now.getTime() + 86400000) > endTimeD.getTime()) {
-        finishTime = 'today';
-      } else {
-        finishTime = 'tomorrow';
-      }
-    }
+    const { finish } = search;
+    // let finishTime = '';
+    // if (endTime) {
+    //   const endTimeD = new Date(endTime);
+    //   const now = new Date();
+    //   if ((now.getTime() + 86400000) > endTimeD.getTime()) {
+    //     finishTime = 'today';
+    //   } else {
+    //     finishTime = 'tomorrow';
+    //   }
+    // }
     return (
       <div className="work-body">
         <div className="work-nav">
           <div className="left">{`${(record.course || {}).title || ''}${record.vsNo > 0 ? `V${record.vsNo}` : ''}${record.number > 0 ? `(${record.number}课时)` : ''}`}全部作业</div>
-          <div className="right theme c-p" onClick={() => this.onPreviewCourse()}>
+          <div className="right theme c-p" onClick={() => linkTo('/my/course')}>
             我的课程 >
           </div>
         </div>
         <ListTable
           filters={[
-            {
-              type: 'radio',
-              checked: finishTime,
-              list: [{ key: 'today', title: '今日需完成' }, { key: 'tomorrow', title: '明日需完成' }],
-              onChange: (item) => {
-                if (item.key === finishTime) {
-                  this.search({ endTime: null });
-                } else if (item.key === 'today') {
-                  const a = new Date();
-                  a.setDate(a.getDate() + 1);
-                  a.setHours(0);
-                  a.setMinutes(0);
-                  a.setMilliseconds(0);
-                  a.setSeconds(0);
-                  this.search({ endTime: formatDate(a, 'YYYY-MM-DD') });
-                } else if (item.key === 'tomorrow') {
-                  const a = new Date();
-                  a.setDate(a.getDate() + 2);
-                  a.setHours(0);
-                  a.setMinutes(0);
-                  a.setMilliseconds(0);
-                  a.setSeconds(0);
-                  this.search({ endTime: formatDate(a, 'YYYY-MM-DD') });
-                } else {
-                  this.search({ endTime: null });
-                }
-              },
-            },
+            // {
+            //   type: 'radio',
+            //   checked: finishTime,
+            //   list: [{ key: 'today', title: '今日需完成' }, { key: 'tomorrow', title: '明日需完成' }],
+            //   onChange: (item) => {
+            //     if (item.key === finishTime) {
+            //       this.search({ endTime: null });
+            //     } else if (item.key === 'today') {
+            //       const a = new Date();
+            //       a.setDate(a.getDate() + 1);
+            //       a.setHours(0);
+            //       a.setMinutes(0);
+            //       a.setMilliseconds(0);
+            //       a.setSeconds(0);
+            //       this.search({ endTime: formatDate(a, 'YYYY-MM-DD') });
+            //     } else if (item.key === 'tomorrow') {
+            //       const a = new Date();
+            //       a.setDate(a.getDate() + 2);
+            //       a.setHours(0);
+            //       a.setMinutes(0);
+            //       a.setMilliseconds(0);
+            //       a.setSeconds(0);
+            //       this.search({ endTime: formatDate(a, 'YYYY-MM-DD') });
+            //     } else {
+            //       this.search({ endTime: null });
+            //     }
+            //   },
+            // },
             {
               type: 'radio',
               checked: finish,
@@ -892,7 +892,7 @@ export default class extends Page {
             },
           ]}
           data={previews}
-          columns={this.columns}
+          columns={this.previewColumns}
         />
       </div>
     );

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

@@ -3,6 +3,7 @@ export default {
   key: 'my-answer',
   title: '个人中心-问答',
   short: '问答',
+  repeat: true,
   needLogin: true,
   component() {
     return import('./page');

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

@@ -3,6 +3,7 @@ export default {
   key: 'my-collect',
   title: '个人中心-收藏',
   short: '收藏',
+  repeat: true,
   needLogin: true,
   component() {
     return import('./page');

+ 12 - 8
front/project/www/routes/my/collect/page.js

@@ -136,7 +136,7 @@ export default class extends Page {
     if (data.order) {
       data.sortMap = { [data.order]: data.direction };
     } else if (data.order !== null && data.order !== '') {
-      data.sortMap = this.state.defaultSortMap;
+      data.sortMap = Object.assign({}, this.state.defaultSortMap);
     }
     if (data.timerange) {
       data.filterMap.timerange = data.timerange;
@@ -155,10 +155,14 @@ export default class extends Page {
     const [startTime, endTime] = timeRange(data.timerange);
     refreshQuestionType(this, data.subject, data.questionType, {
       all: true,
-      needSentence: true,
+      needSentence: data.module !== 'examination',
       allSubject: true,
-    }).then(({ questionTypes }) => {
-      this.setState({ moduleSelect: [{ title: '练习', key: 'exercise' }, { title: '模考', key: 'examination' }] });
+    }).then(({ questionTypes, selectSentence }) => {
+      const moduleSelect = [{ title: '练习', key: 'exercise' }];
+      if (!selectSentence) {
+        moduleSelect.push({ title: '模考', key: 'examination' });
+      }
+      this.setState({ moduleSelect });
       My.listQuestionCollect(
         Object.assign(
           { module: data.module, questionTypes, startTime, endTime },
@@ -166,7 +170,7 @@ export default class extends Page {
           {
             order: Object.keys(data.sortMap)
               .map(key => {
-                if (key === 'title') return 'pid desc, no desc';
+                if (key === 'title') return 'pid asc, no asc';
                 return `${key} ${data.sortMap[key]}`;
               })
               .join(','),
@@ -213,7 +217,7 @@ export default class extends Page {
   }
 
   note(index) {
-    const { list } = this.props;
+    const { list } = this.state;
     const userQuestion = list[index];
     const { questionNo } = userQuestion;
     My.getQuestionNote(questionNo.id)
@@ -245,7 +249,7 @@ export default class extends Page {
     let direction = keys.length ? value[keys[index]] : null;
     if (order == null) {
       const [prevOrder] = Object.keys(sortMap);
-      console.log(defaultSortMap[prevOrder]);
+      console.log(prevOrder, defaultSortMap[prevOrder]);
       if (!defaultSortMap[prevOrder]) {
         [order] = Object.keys(defaultSortMap);
         direction = defaultSortMap[order];
@@ -597,7 +601,7 @@ export default class extends Page {
         </div>
       </Modal>,
       <Modal show={showExportWait} title="导出" confirmText="好的,知道了" btnAlign="center" onConfirm={() => this.setState({ showExportWait: false })}>
-        <div className="t-2 t-s-18">正在下载中,请不要关闭当前页面!</div>
+        <div className="t-2 t-s-18">正在导出中,请不要关闭当前页面!</div>
       </Modal>,
       <Modal show={showExportConfirm} title="导出" confirmText="导出" onConfirm={() => this.export()} onCancel={() => this.setState({ showExportConfirm: false })}>
         <div className="t-2 t-s-18 m-b-5">

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

@@ -3,6 +3,7 @@ export default {
   key: 'my-course',
   title: '个人中心-课程',
   short: '课程',
+  repeat: true,
   needLogin: true,
   component() {
     return import('./page');

+ 3 - 3
front/project/www/routes/my/course/index.less

@@ -109,7 +109,7 @@
         margin-bottom: 5px;
 
         .left {
-          min-width: 280px;
+          min-width: 305px;
           border-right: 1px solid #D8D8D8;
 
           .assets {
@@ -138,12 +138,12 @@
         .right {
           flex: 1;
           padding-top: 30px;
-          padding-left: 20px;
+          padding-left: 0px;
 
           .item {
             text-align: center;
             display: inline-block;
-            width: 100px;
+            width: 95px;
 
             .assets {}
 

+ 122 - 58
front/project/www/routes/my/course/page.js

@@ -6,7 +6,7 @@ import FileUpload from '@src/components/FileUpload';
 import Page from '@src/containers/Page';
 import Assets from '@src/components/Assets';
 import { asyncSMessage } from '@src/services/AsyncTools';
-import { formatDate, formatSeconds, formatPercent } from '@src/services/Tools';
+import { formatDate, formatSeconds, formatPercent, getMap } from '@src/services/Tools';
 import UserLayout from '../../../layouts/User';
 import Button from '../../../components/Button';
 import ProgressText from '../../../components/ProgressText';
@@ -19,7 +19,7 @@ import More from '../../../components/More';
 import Modal from '../../../components/Modal';
 import DatePlane from '../../../components/Date';
 import Note from '../../../components/Note';
-import { FinishModal, CommentModal } from '../../../components/OtherModal';
+import { FinishModal, CommentModal, CourseNoteModal } from '../../../components/OtherModal';
 import { My } from '../../../stores/my';
 import { User } from '../../../stores/user';
 import { Question } from '../../../stores/question';
@@ -86,11 +86,12 @@ export default class extends Page {
       // 是否是最后一课时,是否过预约时间
       const last = row.appointments.length - 1;
       const appointment = row.appointments[last];
-
-      if (new Date(appointment.endTime).getTime() < new Date().getTime()) {
-        // row.status = 'end';
-        if (row.number !== row.appointments.length) {
-          row.appointments.push([{}]);
+      if (appointment) {
+        if (new Date(appointment.endTime).getTime() < new Date().getTime()) {
+          // row.status = 'end';
+          if (row.number !== row.appointments.length) {
+            row.appointments.push([{}]);
+          }
         }
       }
     }
@@ -180,6 +181,22 @@ export default class extends Page {
     });
   }
 
+  refreshNote(recordId) {
+    let { list } = this.state;
+    const [record] = list.filter(row => row.id === recordId);
+    if (!record) return Promise.reject(new Error('记录错误'));
+    return My.listCourseNote({ courseId: record.productId, page: 1, size: record.courseNos.length })
+      .then((result) => {
+        list = list.map(row => {
+          if (row.id === recordId) {
+            row.noteMap = getMap(result.list, 'courseNoId');
+          }
+          return row;
+        });
+        this.setState({ list });
+      });
+  }
+
   suspend() {
     const { suspend } = this.state;
     My.suspendCourse(suspend.id)
@@ -240,6 +257,8 @@ export default class extends Page {
   open(recordId) {
     Order.useRecord(recordId).then(() => {
       this.refreshDetail(recordId);
+    }).catch((err) => {
+      asyncSMessage(err.message, 'error');
     });
   }
 
@@ -282,6 +301,7 @@ export default class extends Page {
       showUploadSupply,
       showSupply,
       showNote,
+      showCourseNote,
       showComment,
       showFinish,
       comment = {},
@@ -357,31 +377,32 @@ export default class extends Page {
               停课
             </div>
           </div>
-          <DatePlane
-            hideInput
-            show={showTime}
-            onChange={() => { }}
-            disabledDate={current => {
-              const date = current.format('YYYY-MM-DD');
-              return data.stopTimeMap[date];
-            }}
-            dateRender={current => {
-              const date = current.format('YYYY-MM-DD');
-              return (
-                <div className="ant-calendar-date">
-                  {current.get('date')}
-                  {data.courseTimeMap[date] && <i className="s1" style={{ background: '#6865FD' }} />}
-                  {data.previewTimeMap[date] && <i className="s2" style={{ background: '#4299FF' }} />}
-                </div>
-              );
-            }}
-            checkRefresh={(date, refresh) => {
-              setTimeout(() => {
-                refresh();
-              }, 2000);
-              return true;
-            }}
-          />
+          <div style={{ position: 'relative' }}>
+            <DatePlane
+              hideInput
+              show={showTime}
+              onChange={() => { }}
+              disabledDate={current => {
+                const date = current.format('YYYY-MM-DD');
+                return data.stopTimeMap[date];
+              }}
+              dateRender={current => {
+                const date = current.format('YYYY-MM-DD');
+                return (
+                  <div className="ant-calendar-date">
+                    {current.get('date')}
+                    {data.courseTimeMap[date] && <i className="s1" style={{ background: '#6865FD' }} />}
+                    {data.previewTimeMap[date] && <i className="s2" style={{ background: '#4299FF' }} />}
+                  </div>
+                );
+              }}
+              checkRefresh={(date, refresh) => {
+                setTimeout(() => {
+                  refresh();
+                }, 2000);
+                return true;
+              }}
+            /></div>
           <div className="t-2 t-s-12">*听课频率≤2天/课时,作业完成度≥90%,课程有效期可延长7-10天。</div>
         </Modal>
         <Modal
@@ -530,6 +551,10 @@ export default class extends Page {
           show={showFinish}
           onConfirm={() => this.setState({ showFinish: false })}
         />
+        <CourseNoteModal getContainer={() => document.body} show={showCourseNote} defaultData={note} course={data.course} courseNos={data.courseNos} noteMap={data.noteMap} onConfirm={() => {
+          this.setState({ showCourseNote: false });
+          this.refreshNote();
+        }} onCancel={() => this.setState({ showCourseNote: false })} />
       </div>
     );
   }
@@ -556,6 +581,15 @@ export default class extends Page {
           onComment={() => {
             this.setState({ showComment: true, comment: { channel: 'course-video', position: item.course.id } });
           }}
+          onNote={(courseNo) => {
+            let handler = Promise.resolve();
+            if (!item.noteMap) {
+              handler = this.refreshNote(item.id);
+            }
+            handler.then(() => {
+              this.setState({ showCourseNote: true, note: item.noteMap[courseNo.id], data: item });
+            });
+          }}
           closeCommentTips={() => this.closeCommentTips(item.id)}
         />
       );
@@ -615,65 +649,65 @@ class CourseOnline extends Component {
         title: '学习内容',
         key: 'title',
         render: (text, record) => {
-          return `课时 ${record.no}: ${text}`;
+          return <a href={`/course/detail/${record.courseId}?no=${record.no}`} target="_blank">课时 {record.no}: {text}</a>;
         },
       },
       {
         title: '预习作业',
         key: 'paper',
-        render: text => {
-          text = text || {};
-          const progress = text.report ? formatPercent(text.report.userNumber, text.report.questionNumber) : 0;
-          const times = text.paper ? text.paper.resetTimes : 0;
+        render: (text, record) => {
+          const { paper = {} } = record;
+          const progress = paper.report ? formatPercent(paper.report.userNumber, paper.report.questionNumber) : 0;
+          const times = paper.paper ? paper.paper.resetTimes : 0;
           return (
             <div>
               <div className="v-a-m d-i-b">
                 <ProgressText width={50} size="small" times={times} progress={progress} unit="次" />
               </div>
-              {!text.report && (
+              {!paper.report && (
                 <IconButton
                   className="m-l-2"
                   type="start"
                   tip="Start"
                   onClick={() => {
                     User.needLogin().then(() => {
-                      Question.startLink('preview', text);
+                      Question.startLink('preview', paper);
                     });
                   }}
                 />
               )}
-              {text.report && !text.report.isFinish && (
+              {paper.report && !paper.report.isFinish && (
                 <IconButton
                   className="m-l-2"
                   type="continue"
                   tip="Continue"
                   onClick={() => {
                     User.needLogin().then(() => {
-                      Question.continueLink('preview', text);
+                      Question.continueLink('preview', paper);
                     });
                   }}
                 />
               )}
-              {text.report && (
+              {paper.report && (
                 <IconButton
                   className="m-l-2"
                   type="restart"
                   tip="Restart"
                   onClick={() => {
                     User.needLogin().then(() => {
-                      Question.restart('preview', text);
+                      Question.restart('preview', paper);
                     });
                   }}
                 />
               )}
-              {text.report && !!text.report.isFinish && (
+              {paper.report && !!paper.report.isFinish && (
                 <IconButton
                   className="m-l-5"
                   type="report"
                   tip="Report"
                   onClick={() => {
                     User.needLogin().then(() => {
-                      Question.reportLink('preview', text);
+                      Question.reportLink('preview', paper);
                     });
                   }}
                 />
@@ -685,30 +719,35 @@ class CourseOnline extends Component {
       {
         title: '进度',
         key: 'progress',
+        width: 70,
         render: (text, record) => {
-          const { paper = {} } = record;
-          return `${paper.paper && paper.paper.finishTimes > 0 ? `${paper.paper.finishTimes}次+` : ''}${
-            paper.report ? formatPercent(paper.report.userNumber, paper.report.questionNumber, false) : '0%'}`;
+          const { progress } = record;
+          return progress ? `${progress.progress}%` : '0%';
         },
       },
       {
         title: '最近学习',
         key: 'lastTime',
-        render: (text, record) => {
-          const { paper = {} } = record;
-          return paper.report && formatDate(paper.report.updateTime, 'YYYY-MM-DD HH:mm:ss');
+        width: 105,
+        render: (text, rr) => {
+          const { record } = rr;
+          return record && formatDate(record.createTime, 'YYYY-MM-DD HH:mm:ss');
         },
       },
       {
         title: '笔记',
         key: 'note',
+        width: 70,
         render: (text, record) => {
-          return <GIcon name="note" active={record.note} />;
+          return <GIcon name="note" active={record.note} onClick={() => {
+            this.onNote(record);
+          }} />;
         },
       },
       {
         title: '问答',
         key: 'ask',
+        width: 70,
         render: (text, record) => {
           return (
             <Link to={`/course/answer/${record.courseId}?tab=my&courseNoId=${record.id}`}>{`${record.answerNumber ||
@@ -720,6 +759,19 @@ class CourseOnline extends Component {
     this.state = { open: props.data.status === 'ing' };
   }
 
+  open(recordId) {
+    Order.useRecord(recordId).then(() => {
+      this.props.refreshDetail(recordId);
+    }).catch((err) => {
+      asyncSMessage(err.message, 'error');
+    });
+  }
+
+  onNote(record) {
+    const { onNote } = this.props;
+    onNote(record);
+  }
+
   render() {
     const { data = {} } = this.props;
     switch (data.status) {
@@ -780,13 +832,17 @@ class CourseOnline extends Component {
             </div>
           </div>
           <div className="right">
-            <div className="item">
+            <div className="item" onClick={() => {
+              openLink(`/course/detail/${data.course.id}?no=${data.currentNo}`);
+            }}>
               <GIcon name="speed-block" active noHover />
               <div className="text">
-                <span>{data.currentNo}</span>/{data.courseNos.length}
+                <span>{data.currentNo}</span>/{(data.courseNos || []).length}
               </div>
             </div>
-            <div className="item">
+            <div className="item" onClick={() => {
+              openLink(`/course/answer/${data.course.id}`);
+            }}>
               <GIcon name="question-block" active noHover />
               <div className="text">
                 <span>{data.answerNumber}</span>/{data.askNumber}
@@ -798,7 +854,9 @@ class CourseOnline extends Component {
                 <span>{formatSeconds(data.totalTime)}</span> {data.previewProgress || 0}%
               </div>
             </div>
-            <div className="item">
+            <div className="item" onClick={() => {
+              openLink(`/course/note/${data.course.id}`);
+            }}>
               <GIcon name="note-block" active noHover />
               <div className="text">{data.noteNumber}</div>
             </div>
@@ -967,7 +1025,7 @@ class CourseOnline extends Component {
             <div className="item">
               <GIcon name="speed-block" noHover />
               <div className="text">
-                <span>{data.currentNo}</span>/{data.courseNos.length}
+                <span>{data.currentNo}</span>/{(data.courseNos || []).length}
               </div>
             </div>
             <div className="item">
@@ -999,7 +1057,7 @@ class CourseOnline extends Component {
   renderTable() {
     const { data = {} } = this.props;
     const { courseNos = [] } = data;
-    return <UserTable size="small" columns={this.columns} data={courseNos} />;
+    return <UserTable size="small" columns={this.columns} data={courseNos || []} />;
   }
 }
 
@@ -1192,6 +1250,12 @@ class CourseVs extends Component {
       system: [1, 7, 3, 4],
       answer: [1, 2, 3],
     };
+    if (!props.data.appointments) {
+      props.data.appointments = [];
+    }
+    if (!props.data.teacher) {
+      props.data.teacher = {};
+    }
     const index = props.data.appointments.length - 1;
     this.state = {
       open: props.data.status === 'ing',

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

@@ -3,6 +3,7 @@ export default {
   key: 'my-data',
   title: '个人中心-数据',
   short: '数据',
+  repeat: true,
   needLogin: true,
   component() {
     return import('./page');

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

@@ -3,6 +3,7 @@ export default {
   key: 'my-error',
   title: '个人中心-错题',
   short: '错题',
+  repeat: true,
   needLogin: true,
   component() {
     return import('./page');

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

@@ -141,13 +141,13 @@ export default class extends Page {
     if (data.order) {
       data.sortMap = { [data.order]: data.direction };
     } else if (data.order !== null && data.order !== '') {
-      data.sortMap = this.state.defaultSortMap;
+      data.sortMap = Object.assign({}, this.state.defaultSortMap);
     }
     if (data.timerange) {
       data.filterMap.timerange = data.timerange;
     }
     const { info = {} } = this.props.user;
-    if (info.latestExercise) {
+    if (info.latestError) {
       // 获取最后一次错题记录
       Question.baseReport(info.latestError).then(result => {
         this.setState({ latest: result });
@@ -259,7 +259,7 @@ export default class extends Page {
     let direction = keys.length ? value[keys[index]] : null;
     if (order == null) {
       const [prevOrder] = Object.keys(sortMap);
-      console.log(defaultSortMap[prevOrder]);
+      console.log(prevOrder, defaultSortMap[prevOrder]);
       if (!defaultSortMap[prevOrder]) {
         [order] = Object.keys(defaultSortMap);
         direction = defaultSortMap[order];
@@ -584,7 +584,7 @@ export default class extends Page {
         </div>
       </Modal>,
       <Modal show={showExportWait} title="导出" confirmText="好的,知道了" btnAlign="center" onConfirm={() => this.setState({ showExportWait: false })}>
-        <div className="t-2 t-s-18">正在下载中,请不要关闭当前页面!</div>
+        <div className="t-2 t-s-18">正在导出中,请不要关闭当前页面!</div>
       </Modal>,
       <Modal show={showExportConfirm} title="导出" confirmText="导出" onConfirm={() => this.export()} onCancel={() => this.setState({ showExportConfirm: false })}>
         <div className="t-2 t-s-18 m-b-5">

+ 1 - 1
front/project/www/routes/my/index.js

@@ -186,7 +186,7 @@ export function refreshStruct(compontent, questionTypes, module, one, two, { all
             key: '',
           }];
           oneSelectPlaceholder = null;
-          twoSelectPlaceholder = null;
+          twoSelectPlaceholder = '千行长难句';
         }
         compontent.setState({ oneSelect, twoSelectMap, oneSelectPlaceholder, twoSelectPlaceholder });
 

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

@@ -3,6 +3,7 @@ export default {
   key: 'my-main',
   title: '个人中心-我的',
   short: '我的',
+  repeat: true,
   needLogin: true,
   component() {
     return import('./page');

+ 5 - 2
front/project/www/routes/my/main/index.less

@@ -94,6 +94,7 @@
         }
 
         .g-date {
+          position: absolute !important;
           right: 0;
           left: unset !important;
           top: 24px !important;
@@ -240,6 +241,7 @@
           margin-right: 15px;
           position: relative;
           overflow: hidden;
+
           .avatar-hover {
             display: none;
             position: absolute;
@@ -247,15 +249,16 @@
             left: 0;
             right: 0;
             bottom: 0;
-            background:rgba(0,0,0,0.2);
+            background: rgba(0, 0, 0, 0.2);
             color: #fff;
             line-height: 50px;
             text-align: center;
             font-size: 12px;
           }
         }
+
         .avatar:hover {
-          .avatar-hover{
+          .avatar-hover {
             display: block;
           }
         }

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

@@ -3,6 +3,7 @@ export default {
   key: 'my-message',
   title: '个人中心-消息',
   short: '消息',
+  repeat: true,
   needLogin: true,
   component() {
     return import('./page');

+ 37 - 29
front/project/www/routes/my/message/page.js

@@ -14,39 +14,41 @@ import { User } from '../../../stores/user';
 
 const MessageTypeMap = getMap(MessageType, 'value', 'label');
 
-const columns = [
-  {
-    title: '消息',
-    key: 'title',
-
-    render: (text, row) => {
-      return <div>
-        {!row.isRead ? <span className='dot'>{text}</span> : text}
-        {row.content && <div className='ws-pl'>{row.content}{row.link && <a className='m-l-5' href={row.link} target="_blank">{row.linkTitle || '查看详情'}</a>}{row.linkSecond && <a className='m-l-5' href={row.linkSecond} target="_blank">{row.linkSecondTitle || '查看详情'}</a>}</div>}
-      </div>;
-    },
-  },
-  {
-    title: '类型',
-    key: 'type',
-    width: 120,
-  },
-  {
-    title: '发送时间',
-    key: 'date',
-    width: 180,
-    render: (text) => {
-      return <div className="sub">
-        <div className="t-2 t-s-12">{text.split(' ')[0]}</div>
-        <div className="t-6 t-s-12">{text.split(' ')[1]}</div>
-      </div>;
-    },
-  }];
 
 export default class extends Page {
   constructor(props) {
     props.size = 15;
     super(props);
+    this.columns = [
+      {
+        title: '消息',
+        key: 'title',
+
+        render: (text, row) => {
+          return <div onClick={() => {
+            this.realMessage(row);
+          }}>
+            {!row.isRead ? <span className='dot'>{text}</span> : text}
+            {row.content && <div className='ws-pl'>{row.content}{row.link && <a className='m-l-5' href={row.link} target="_blank">{row.linkTitle || '查看详情'}</a>}{row.linkSecond && <a className='m-l-5' href={row.linkSecond} target="_blank">{row.linkSecondTitle || '查看详情'}</a>}</div>}
+          </div>;
+        },
+      },
+      {
+        title: '类型',
+        key: 'type',
+        width: 120,
+      },
+      {
+        title: '发送时间',
+        key: 'date',
+        width: 180,
+        render: (text) => {
+          return <div className="sub">
+            <div className="t-2 t-s-12">{text.split(' ')[0]}</div>
+            <div className="t-6 t-s-12">{text.split(' ')[1]}</div>
+          </div>;
+        },
+      }];
   }
 
   initState() {
@@ -105,6 +107,12 @@ export default class extends Page {
     });
   }
 
+  realMessage(record) {
+    My.readMessage(record.id).then(() => {
+      this.refresh();
+    });
+  }
+
   renderView() {
     const { config } = this.props;
     return <UserLayout active={config.key} menu={menu} center={this.renderTable()} />;
@@ -142,7 +150,7 @@ export default class extends Page {
         />
         <UserTable
           size="small"
-          columns={columns}
+          columns={this.columns}
           data={list}
           current={page}
           total={total}

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

@@ -3,6 +3,7 @@ export default {
   key: 'my-note',
   title: '个人中心-笔记',
   short: '笔记',
+  repeat: true,
   needLogin: true,
   component() {
     return import('./page');

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

@@ -113,7 +113,7 @@ export default class extends Page {
     if (data.order) {
       data.sortMap = { [data.order]: data.direction };
     } else if (data.order !== null && data.order !== '') {
-      data.sortMap = this.state.defaultSortMap;
+      data.sortMap = Object.assign({}, this.state.defaultSortMap);
     }
     if (data.timerange) {
       data.filterMap.timerange = data.timerange;
@@ -622,7 +622,7 @@ export default class extends Page {
         </div>
       </Modal>,
       <Modal show={showExportWait} title="导出" confirmText="好的,知道了" btnAlign="center" onConfirm={() => this.setState({ showExportWait: false })}>
-        <div className="t-2 t-s-18">正在下载中,请不要关闭当前页面!</div>
+        <div className="t-2 t-s-18">正在导出中,请不要关闭当前页面!</div>
       </Modal>,
       <Examination
         show={showExamination}

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

@@ -3,6 +3,7 @@ export default {
   key: 'my-order',
   title: '个人中心-订单',
   short: '订单',
+  repeat: true,
   needLogin: true,
   component() {
     return import('./page');

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

@@ -3,6 +3,7 @@ export default {
   key: 'my-report',
   title: '个人中心-报告',
   short: '报告',
+  repeat: true,
   needLogin: true,
   component() {
     return import('./page');

+ 9 - 4
front/project/www/routes/my/report/page.js

@@ -286,6 +286,7 @@ export default class extends Page {
               </div>
             );
           }
+          if (!report.score) return null;
           return (
             <div className="sub">
               <div className="t-2 t-s-12">{report.score.totalScore}</div>
@@ -308,10 +309,11 @@ export default class extends Page {
           if (!report.qxCat) {
             return (
               <div className="f-s-12">
-                {formatPercent(report.setting.number.verbal, this.nums.verbal.number, false)}
+                {formatPercent(report.setting.real ? report.setting.real.verbal : report.setting.number.verbal, this.nums.verbal.number, false)}
               </div>
             );
           }
+          if (!report.score) return null;
           return (
             <div className="sub">
               <div className="t-2 t-s-12">{report.score.verbalScore}</div>
@@ -333,9 +335,10 @@ export default class extends Page {
           if (!report) return null;
           if (!report.qxCat) {
             return (
-              <div className="f-s-12">{formatPercent(report.setting.number.quant, this.nums.quant.number, false)}</div>
+              <div className="f-s-12">{formatPercent(report.setting.real ? report.setting.real.quant : report.setting.number.quant, false)}</div>
             );
           }
+          if (!report.score) return null;
           return (
             <div className="sub">
               <div className="t-2 t-s-12">{report.score.quantScore}</div>
@@ -356,8 +359,9 @@ export default class extends Page {
           const report = childIndex === -1 ? reports[0] : record;
           if (!report) return null;
           if (!report.qxCat) {
-            return <div className="f-s-12">{formatPercent(report.setting.number.ir, this.nums.ir.number, false)}</div>;
+            return <div className="f-s-12">{formatPercent(report.setting.real ? report.setting.real.ir : report.setting.number.ir, this.nums.ir.number, false)}</div>;
           }
+          if (!report.score) return null;
           return (
             <div className="sub">
               <div className="t-2 t-s-12">{report.score.irScore}</div>
@@ -503,7 +507,7 @@ export default class extends Page {
     if (data.order) {
       data.sortMap = { [data.order]: data.direction };
     } else if (data.order !== null && data.order !== '') {
-      data.sortMap = this.state.defaultSortMap;
+      data.sortMap = Object.assign({}, this.state.defaultSortMap);
     }
     if (data.timerange) {
       data.filterMap.timerange = data.timerange;
@@ -552,6 +556,7 @@ export default class extends Page {
                 {
                   order: Object.keys(data.sortMap)
                     .map(key => {
+                      if (key === 'title') return 'struct_three asc, struct_four asc, no asc';
                       return `${key} ${data.sortMap[key]}`;
                     })
                     .join(','),

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

@@ -3,6 +3,7 @@ export default {
   key: 'my-tools',
   title: '个人中心-工具',
   short: '工具',
+  repeat: true,
   needLogin: true,
   component() {
     return import('./page');

+ 5 - 7
front/project/www/routes/my/tools/page.js

@@ -566,9 +566,8 @@ export default class extends Page {
             </div>
           }
           right={
-            !data.hasService &&
             data.unUseRecord && (
-              <div className="email" >
+              <div className="email c-p" >
                 <span onClick={() => {
                   this.recordList({ page: 1, size: 10, service: 'textbook', isUse: false, isExpire: false });
                 }} >待开通</span>
@@ -583,12 +582,12 @@ export default class extends Page {
                 <div className="data-item">
                   <Assets name="sun_blue" onClick={() => linkTo(`/textbook/topic/list/${item.subject}`)} />
                   <div className="title">
-                    已更新至<b>{item.num}</b>题
+                    已更新至<b>{item.number || 0}</b>题
                   </div>
                   <div className="date">{item.date}</div>
                   <More
                     menu={[
-                      { label: '更新', key: 'update' },
+                      { label: '更新日志', key: 'update' },
                       { label: '反馈', key: 'feedback' },
                       { label: '评价', key: 'comment' },
                     ]}
@@ -599,7 +598,7 @@ export default class extends Page {
                       } else if (key === 'update') {
                         this.textbookHistory({ page: 1, size: 100, subject: item.subject });
                       } else if (key === 'feedback') {
-                        this.setState({ showFeedback: true, feedback: { questionSubject: item.subject, target: TextbookFeedbackTarget[0].value } });
+                        this.setState({ showFeedback: true, feedback: { textbookSubject: item.subject, target: TextbookFeedbackTarget[0].value, no: '' } });
                       }
                     }}
                   />
@@ -650,10 +649,9 @@ export default class extends Page {
       <div className="tab-3-layout">
         <UserAction
           right={
-            !data.hasService &&
             data.unUseRecord && (
               <div
-                className="email"
+                className="email c-p"
                 onClick={() => {
                   this.recordList({ page: 1, size: 10, service: 'qx_cat', isUse: false, isExpire: false });
                 }}

+ 1 - 0
front/project/www/routes/page/cart/index.js

@@ -2,6 +2,7 @@ export default {
   path: '/cart',
   key: 'cart',
   title: '购物车',
+  repeat: true,
   needLogin: true,
   component() {
     return import('./page');

+ 7 - 0
front/project/www/routes/page/cart/index.less

@@ -133,4 +133,11 @@
       padding: 20px 0;
     }
   }
+}
+
+.order-modal {
+  .close {
+    top: -17px !important;
+    right: -17px !important;
+  }
 }

+ 2 - 1
front/project/www/routes/page/cart/page.js

@@ -270,10 +270,11 @@ class OrderItem extends Component {
         <Modal
           show={showList}
           maskClosable
-          close={false}
+          close
           body={false}
           width={630}
           onClose={() => this.setState({ showList: false })}
+          className='order-modal'
         >
           <UserTable
             size="small"

+ 7 - 5
front/project/www/routes/page/export/page.js

@@ -225,15 +225,17 @@ class BaseDetail extends Component {
 
   renderAnswer() {
     const { data = {} } = this.props;
-    const { question = {}, note = {}, userAnswer = {} } = data;
+    const { question = {}, note = {}, userAnswer } = data;
     const { answer } = question;
+    let a = true;
+    if (!answer.questions[0].single) a = false;
     return (
       <div className="detail-item-block">
-        <div className="title-item">答案</div>
-        <div className="t-1 t-s-16 m-b-2">
-          {userAnswer && <span className="m-r-2">我的答案 {AnswerIndex[userAnswer.questions[0].single.indexOf(true)]}</span>}
+        {a && <div className="title-item">答案</div>}
+        {a && <div className="t-1 t-s-16 m-b-2">
+          <span className="m-r-2">我的答案 {userAnswer && AnswerIndex[userAnswer.questions[0].single.indexOf(true)]}</span>
           <span>正确答案 {AnswerIndex[answer.questions[0].single.indexOf(true)]}</span>
-        </div>
+        </div>}
         {this.renderNote('题目', note.questionContent)}
       </div>
     );

+ 1 - 1
front/project/www/routes/page/home/index.less

@@ -272,7 +272,7 @@
     }
 
     .step-c-4 {
-      margin-bottom: 130px;
+      margin-bottom: 110px;
 
       .m-title {
         margin-bottom: 20px;

+ 6 - 6
front/project/www/routes/page/home/page.js

@@ -27,7 +27,7 @@ export default class extends Page {
   }
 
   location(url) {
-    window.location.href = url;
+    openLink(url);
   }
 
   test() {
@@ -146,7 +146,7 @@ export default class extends Page {
                   <div className="item m-l-1-5">
                     <div className="title">
                       <Assets name="userfriendly" />
-                      ⼈⼈会⽤<span className="sub">User-frriendly</span>
+                      ⼈⼈会⽤<span className="sub">User-friendly</span>
                     </div>
                     <div className="desc">
                       除提供有用的数据外,网站多处设有信息引导,提供必要的解释和原理说明,从源头规避误区,协助考生做出更加明智的决策,提高备考效率。
@@ -216,16 +216,16 @@ export default class extends Page {
               <div className="step-c step-c-5">
                 <div className="m-title">独家服务</div>
                 <div className="list">
-                  <div className="item m-r-1-5 item1">
+                  <div className="item m-r-1-5 item1 c-p">
                     <Assets className="sun" name="sun_blue" />
-                    <div className="title" style={{ color: '#4292F0' }} onClick={() => linkTo('/examination')}>
+                    <div className="title" style={{ color: '#4292F0' }} onClick={() => openLink('/examination')}>
                       千⾏CAT模考<span className="sub">></span>
                     </div>
                     <div className="desc">采⽤CAT出题机制、排名制算分⽅法</div>
                     <div className="desc">独家题源,排除重题⼲扰</div>
                     <div className="desc">模考报告提供具体考点分析,明确提升 ⽅向</div>
                   </div>
-                  <div className="item m-r-1-5 m-l-1-5 item2" onClick={() => linkTo('/textbook')}>
+                  <div className="item m-r-1-5 m-l-1-5 item2 c-p" onClick={() => openLink('/textbook')}>
                     <Assets className="sun" name="sun_red" />
                     <div className="title" style={{ color: '#F36565' }}>
                       机经服务<span className="sub">></span>
@@ -234,7 +234,7 @@ export default class extends Page {
                     <div className="desc">轻松获取:⾃动更新⾄邮箱代替⼿动 领取</div>
                     <div className="desc">随时查阅:⼿机查看、在线浏览、在 线做题</div>
                   </div>
-                  <div className="item m-l-1-5 item3" onClick={() => User.needLogin().then(() => linkTo('/my'))}>
+                  <div className="item m-l-1-5 item3 c-p" onClick={() => User.needLogin().then(() => openLink('/my'))}>
                     <Assets className="sun" name="sun_yello" />
                     <div className="title" style={{ color: '#FFB676' }}>
                       VIP服务<span className="sub">></span>

+ 1 - 0
front/project/www/routes/page/order/index.js

@@ -2,6 +2,7 @@ export default {
   path: '/order/:id',
   key: 'order',
   title: '订单详情',
+  repeat: true,
   needLogin: true,
   component() {
     return import('./page');

+ 6 - 0
front/project/www/routes/page/order/index.less

@@ -65,4 +65,10 @@
       }
     }
   }
+}
+.order-modal {
+  .close {
+    top: -17px !important;
+    right: -17px !important;
+  }
 }

+ 12 - 10
front/project/www/routes/page/order/page.js

@@ -8,7 +8,7 @@ import UserTable from '../../../components/UserTable';
 import IconButton from '../../../components/IconButton';
 import { Order } from '../../../stores/order';
 import { User } from '../../../stores/user';
-import { formatDate } from '../../../../../src/services/Tools';
+import { formatDate, formatMoney } from '../../../../../src/services/Tools';
 // import { ServiceKey, ServiceParamMap, OrderInfoMap } from '../../../../Constant';
 
 export default class extends Page {
@@ -33,6 +33,7 @@ export default class extends Page {
         </div>
         <div className="list content">
           {(list || []).map(item => {
+            if (item.source === 'gift_course') return null;
             return <OrderItem data={item} />;
           })}
         </div>
@@ -45,15 +46,15 @@ export default class extends Page {
           <div className="t-1 t-s-18 f-w-b title">订单金额</div>
           <div className="t-2 t-s-12 item">
             <span>应付金额</span>
-            <span>¥ {order.originMoney}</span>
+            <span>¥ {formatMoney(order.originMoney)}</span>
           </div>
           {order.originMoney - order.money > 0 && <div className="t-2 t-s-12 item">
             <span>优惠金额</span>
-            <span>¥ {order.originMoney - order.money}</span>
+            <span>¥ {formatMoney(order.originMoney - order.money)}</span>
           </div>}
           <div className="t-2 t-s-12 item">
             <span>实付金额</span>
-            <span>¥ {order.money}</span>
+            <span>¥ {formatMoney(order.money)}</span>
           </div>
         </div>
         <div className="content block">
@@ -61,13 +62,13 @@ export default class extends Page {
           <div className="t-2 t-s-12 item">
             <span>支付方式</span>
             <span>
-              {order.payMethod && <Assets name={order.payMethod === 'wechat' ? 'wechatpay' : order.payMethod} />}
+              {order.payMethod ? <Assets name={order.payMethod === 'wechat' ? 'wechatpay' : order.payMethod} /> : '待确定'}
               {/* alipay wechatpay bank */}
             </span>
           </div>
           <div className="t-2 t-s-12 item">
             <span>付款时间</span>
-            <span>{order.payTime && formatDate(order.payTime, 'YYYY-MM-DD HH:mm:ss')}</span>
+            <span>{order.payTime ? formatDate(order.payTime, 'YYYY-MM-DD HH:mm:ss') : '待确定'}</span>
           </div>
         </div>
       </div>
@@ -131,10 +132,11 @@ class OrderItem extends Component {
         <Modal
           show={showList}
           maskClosable
-          close={false}
+          close
           body={false}
           width={630}
           onClose={() => this.setState({ showList: false })}
+          className="order-modal"
         >
           <UserTable
             size="small"
@@ -156,8 +158,8 @@ class OrderItem extends Component {
           {data.title}
         </div>
         <div style={{ width: 530 }} hidden={checkout.productType === 'data'} className="d-i-b t-8 t-s-12">
-          <span className="m-r-2">开通有效期: {checkout.expireDays ? `${checkout.expireDays}天` : '付款后立即生效'}</span>
-          <span className="m-l-2">使用有效期: {checkout.useExpireDays ? `${checkout.useExpireDays}天` : '永久'}</span>
+          {checkout.productType !== 'course_package' && <span className="m-r-2">开通有效期: {checkout.expireDays ? `${checkout.expireDays}天` : '付款后立即生效'}</span>}
+          {checkout.productType !== 'course_package' && <span className="m-l-2">使用有效期: {checkout.useExpireDays ? `${checkout.useExpireDays}天` : '永久'}</span>}
         </div>
         <div style={{ width: 530 }} hidden={checkout.productType !== 'data'} className="d-i-b t-8 t-s-12">
           <IconButton
@@ -203,7 +205,7 @@ class OrderItem extends Component {
               {(children || []).map(checkout => checkout.title).join(' + ')}
             </span>
           </div>
-          <div className="d-i-b t-1 t-s-12 t-d-l-t"> ¥ {data.originMoney}</div>
+          <div className="d-i-b t-1 t-s-12 t-d-l-t"> ¥ {formatMoney(data.originMoney)}</div>
         </div>
         <div hidden={!open} className="contain-list m-b-5 l-h-16">
           {(children || []).map(checkout => {

+ 12 - 0
front/project/www/routes/page/ready/index.less

@@ -78,6 +78,18 @@
         margin: 0px;
       }
 
+      img {
+        width: 840px;
+      }
+
+      ul {
+        margin-bottom: 0;
+      }
+
+      .ql-indent-1 {
+        padding-left: 40px;
+      }
+
       .query-layout {
 
         .search-wrapper {

+ 37 - 20
front/project/www/routes/page/ready/page.js

@@ -23,6 +23,7 @@ export default class extends Page {
     this.dataList = null;
     return {
       readTab: '1',
+      bottom: 600,
       // load: false,
       // list: [
       //   {
@@ -124,6 +125,7 @@ export default class extends Page {
   }
 
   init() {
+    this.bottom = 600;
     Main.readyInfo()
       .then(result => {
         // read, category, area
@@ -145,6 +147,12 @@ export default class extends Page {
           isFaq: true,
         });
         this.categoryMap = getMap(result.category, 'id');
+        result.category = result.category.filter(row => {
+          if (!row.parentId) return true;
+          const parent = this.categoryMap[row.parentId];
+          if (row.parentId > 0 && parent) return true;
+          return false;
+        });
         const list = formatTreeData(result.category, 'id', 'title', 'parentId').map((row) => {
           if (row.isRoom) row.children = [];
           return row;
@@ -232,23 +240,29 @@ export default class extends Page {
   }
 
   readyScroll(list) {
-    if (this.scrollTimeKey) clearTimeout(this.scrollTimeKey);
+    if (this.scrollIntervalKey) clearInterval(this.scrollIntervalKey);
     this.initScroll(list);
   }
 
   initScroll(list) {
     this.load = false;
-    try {
-      list.forEach(item => {
-        item.top = document.getElementById(item.id).offsetTop - 100;
-        return item;
-      });
-      this.load = true;
-    } catch (e) {
-      this.scrollTimeKey = setTimeout(() => {
-        this.initScroll(list);
-      }, 500);
-    }
+    this.scrollIntervalKey = setInterval(() => {
+      try {
+        list.forEach(item => {
+          item.top = document.getElementById(item.id).offsetTop - 35;
+          return item;
+        });
+        const bottom = document.body.clientHeight - list[list.length - 1] - 35 - 44;
+        if (bottom === this.bottom) {
+          if (this.scrollIntervalKey) clearInterval(this.scrollIntervalKey);
+        }
+        this.load = true;
+        this.setState({ bottom });
+        this.bottom = bottom;
+      } catch (e) {
+        this.load = false;
+      }
+    }, 1000);
   }
 
   scrollListener(e) {
@@ -321,7 +335,7 @@ export default class extends Page {
   }
 
   renderView() {
-    const { list, current, scrollCurrent, open } = this.state;
+    const { list, current, scrollCurrent, open, bottom } = this.state;
     let detail = {};
     return (
       <div>
@@ -367,7 +381,7 @@ export default class extends Page {
               })}
             </Anchor>
           </div>
-          <div className="center">
+          <div className="center" style={{ paddingBottom: `${bottom}px` }}>
             <div className="t-1 t-s-24 m-b-2">{detail.title}</div>
             {!detail.isData && !detail.isRoom && !detail.isRead && !detail.isFaq && this.renderList()}
             {!!detail.isRoom && this.renderRoom()}
@@ -386,7 +400,11 @@ export default class extends Page {
       <div className="query-layout">
         <div className="search-wrapper">
           <Select theme="white" value={isOverseas} list={[{ title: '中国', key: false }, { title: '海外', key: true }]} onChange={({ key }) => this.setState({ isOverseas: key })} />
-          <input value={keyword} placeholder='请输入城市名称' onChange={(e) => this.setState({ keyword: e.target.value })} />
+          <input value={keyword} placeholder='请输入城市名称' onChange={(e) => this.setState({ keyword: e.target.value })} onKeyUp={(e) => {
+            if (e.keyCode === 13) {
+              this.refreshRoom();
+            }
+          }} />
           <Button width={150} onClick={() => this.refreshRoom()}>
             <Icon className="m-r-5" type="search" />
             搜索考场
@@ -417,10 +435,9 @@ export default class extends Page {
           <div className="t-1 t-s-18 m-b-1">{item.title}</div>
           <div className="data-list">
             {item.children.map(child => {
-              if (child.id === dataId) {
-                return <div style={{ width: 240, height: 310, overflowY: 'auto', display: 'inline-block' }} className="ws-p data-item" onClick={() => this.setState({ dataId: null })}>{child.content}</div>;
-              }
-              return <Assets width={240} height={310} src={child.cover} className="data-item" onClick={() => this.setState({ dataId: child.id })} />;
+              return <div style={{ width: 240, height: 310, overflowY: 'auto', display: 'inline-block', border: '1px solid #ccc', padding: '5px' }} className="ws-pl data-item" onClick={() => this.setState({ dataId: child.id === dataId ? null : child.id })}>
+                {child.id === dataId ? child.content : <Assets width={240} height={310} src={child.cover} />}
+              </div>;
             })}
           </div>
         </div>
@@ -432,7 +449,7 @@ export default class extends Page {
     const { articles = [], scrollCurrent } = this.state;
     return articles.map(item => {
       return (
-        <div className="p-t-1 m-b-2" id={item.categoryId}>
+        <div className="p-t-1 m-b-2" style={{ whiteSpace: 'pre-wrap' }} id={item.categoryId}>
           <div className="t-1 t-s-18 m-b-1">{scrollCurrent === item.categoryId ? <span className="t-4">{item.title}</span> : item.title}</div>
           <div className="t-2" dangerouslySetInnerHTML={{ __html: item.content }} />
         </div>

+ 16 - 17
front/project/www/routes/paper/process/base/index.js

@@ -20,8 +20,6 @@ export default class extends Component {
   constructor(props) {
     super(props);
     this.state = {
-      showTime: true,
-      showNo: true,
       showCalculator: false,
       disorder: false,
       order: [],
@@ -75,8 +73,10 @@ export default class extends Component {
     let title = null;
     let desc = null;
     let width = null;
-    if (question.questionType === 'awa' && !answer.awa) {
-      desc = 'Please answer the question first.';
+    if (question.questionType === 'awa') {
+      if (!answer.awa) {
+        desc = 'Please answer the question first.';
+      }
     } else {
       let flag = false;
       if (!answer || !answer.questions) {
@@ -166,7 +166,7 @@ export default class extends Component {
     if (this.checkAnswer()) {
       this.showConfirm('Answer Confirmation', 'Click Yes to confirm your answer and continue to the next \n question. ', 560, () => {
         flow.submit(answer).then(() => {
-          flow.next();
+          return flow.next();
         });
       });
     }
@@ -174,7 +174,7 @@ export default class extends Component {
 
   stage() {
     const { flow } = this.props;
-    flow.next();
+    flow.relaxToNextStage();
   }
 
   render() {
@@ -204,7 +204,7 @@ export default class extends Component {
   renderContent() {
     const { question = { content: {} } } = this.props;
     const { step } = this.state;
-    const { steps = [] } = question.content;
+    const { steps = [], typeset = 'one', type } = question.content;
     return (
       <div className="block block-content">
         {steps.length > 0 && (
@@ -217,6 +217,7 @@ export default class extends Component {
         )}
         <div
           className="text"
+          style={{ paddingBottom: typeset === 'one' && type !== 'inline' ? '' : '100px' }}
           dangerouslySetInnerHTML={{ __html: this.formatStrem(steps.length > 0 ? steps[step].stem : question.stem) }}
         />
       </div>
@@ -250,9 +251,9 @@ export default class extends Component {
   }
 
   renderDetail() {
-    const { paper, userQuestion, question = { content: {} }, totalTime, stageTime, totalNumber, flow } = this.props;
+    const { paper, userQuestion, question = { content: {} }, totalTime, stageTime, totalNumber, flow, showTime, showNo } = this.props;
     if (!userQuestion.id) return null;
-    const { showCalculator, showTime, showNo } = this.state;
+    const { showCalculator } = this.state;
     const { typeset = 'one' } = question.content;
     return (
       <div className="layout">
@@ -279,7 +280,7 @@ export default class extends Component {
             <div
               className="block"
               onClick={() => {
-                this.setState({ showTime: !showTime });
+                flow.switchTime();
               }}
             >
               <Assets name="timeleft_icon" />
@@ -289,7 +290,7 @@ export default class extends Component {
             <div
               className="block"
               onClick={() => {
-                this.setState({ showNo: !showNo });
+                flow.switchNo();
               }}
             >
               <Assets name="subjectnumber_icon" />
@@ -322,8 +323,7 @@ export default class extends Component {
 
   renderExaminationStart() {
     // const { paper, userQuestion, singleTime, stageTime, flow } = this.props;
-    const { showTime } = this.state;
-    const { paper, flow, startTime, setting } = this.props;
+    const { paper, flow, startTime, setting, showTime } = this.props;
     const { disorder = true, order } = setting;
     return (
       <div className="layout">
@@ -334,7 +334,7 @@ export default class extends Component {
             <div
               className="block"
               onClick={() => {
-                this.setState({ showTime: !showTime });
+                flow.switchTime();
               }}
             >
               <Assets name="timeleft_icon" />
@@ -507,8 +507,7 @@ export default class extends Component {
   }
 
   renderRelax() {
-    const { paper, stageTime, flow } = this.props;
-    const { showTime } = this.state;
+    const { paper, stageTime, flow, showTime } = this.props;
     return (
       <div className="layout">
         <div className="layout-header">
@@ -517,7 +516,7 @@ export default class extends Component {
             <div
               className="block"
               onClick={() => {
-                this.setState({ showTime: !showTime });
+                flow.switchTime();
               }}
             >
               <Assets name="timeleft_icon" />

+ 74 - 25
front/project/www/routes/paper/process/page.js

@@ -22,7 +22,6 @@ export default class extends Page {
     this.stage = '';
     this.stageInterval = null;
     this.stageTime = 0;
-    this.stageNumber = 0;
     this.stageProcess = { number: 0, time: 0 };
     this.relaxProcess = { time: 0 };
 
@@ -31,10 +30,29 @@ export default class extends Page {
 
     this.baseRef = null;
     this.sentenceRef = null;
+
+    this.accTime = 0;
+  }
+
+  outPage() {
+    if (this.stageInterval) {
+      clearInterval(this.stageInterval);
+      this.stageInterval = null;
+    }
+    if (this.totalInterval) {
+      clearInterval(this.totalInterval);
+      this.totalInterval = null;
+    }
+    if (this.singleInterval) {
+      clearInterval(this.singleInterval);
+      this.singleInterval = null;
+    }
   }
 
   initState() {
     return {
+      showTime: true,
+      showNo: true,
       setting: {},
       report: {},
       question: {},
@@ -44,6 +62,14 @@ export default class extends Page {
     };
   }
 
+  switchTime() {
+    this.setState({ showTime: !this.state.showTime });
+  }
+
+  switchNo() {
+    this.setState({ showNo: !this.state.showNo });
+  }
+
   initData() {
     const { type, id } = this.params;
     // type 是获取基础paper的表信息
@@ -134,23 +160,8 @@ export default class extends Page {
   }
 
   next() {
-    const { report, scene } = this.state;
+    const { report } = this.state;
     const { setting = {} } = report;
-    if (scene === 'relax') {
-      // 进入下一阶段
-      this.initNextStage();
-    }
-    // 更新模考做题进度
-    if (report.paperModule === 'examination') {
-      if (this.stageNumber >= this.stageProcess.number) {
-        const { order } = report.setting;
-        // 进入休息
-        if (order.indexOf(this.stage) < order.length - 1) {
-          this.relaxStage();
-          return Promise.resolve();
-        }
-      }
-    }
     return Question.next(report.id)
       .then(userQuestion => {
         const questionSetting = {};
@@ -202,8 +213,18 @@ export default class extends Page {
     return Question.submit(userQuestion.id, answer, singleTime, questionSetting).then(() => {
       this.singleQuestionTime(true);
       if (report.paperModule === 'examination') {
-        this.stageNumber += 1;
+        // 更新模考做题进度
+        this.stageNumber = userQuestion.stageNo;
+        if (this.stageNumber >= this.stageProcess.number) {
+          const { order } = report.setting;
+          // 进入休息
+          if (order.indexOf(this.stage) < order.length - 1) {
+            this.stageToRelax();
+            return Promise.reject();
+          }
+        }
       }
+      return Promise.resolve();
     });
   }
 
@@ -213,11 +234,34 @@ export default class extends Page {
     return Question.stage(report.id)
       .then(() => {
         this.baseRef.timeOut(() => {
-          this.relaxStage();
+          this.stageToRelax();
         });
+      })
+      .catch(err => {
+        if (err.message === 'finish') {
+          // 考试结束
+          return this.finish();
+        }
+        return false;
       });
   }
 
+  relaxToNextStage() {
+    this.stageQuestionTime(0, true);
+    // 进入下一阶段
+    this.initNextStage();
+    this.next();
+  }
+
+  stageToRelax() {
+    if (this.accTime > 55 * 60) {
+      this.relaxStage();
+      this.accTime = 0;
+    } else {
+      this.next();
+    }
+  }
+
   finish() {
     const { report } = this.state;
     return Question.finish(report.id)
@@ -271,10 +315,11 @@ export default class extends Page {
       this.stageInterval = setInterval(() => {
         this.stageTime += 1;
         if (this.stageTime >= this.stageProcess.time) {
+          clearInterval(this.stageInterval);
           const { scene } = this.state;
           if (scene === 'relax') {
             // 进入下一阶段,获取下一题
-            this.next();
+            this.relaxToNextStage();
           } else {
             // 提交当前阶段
             this.nextStage();
@@ -304,7 +349,10 @@ export default class extends Page {
       } else if (this.startTime >= 60) {
         clearInterval(this.startInterval);
         this.startInterval = null;
-        this.start(Object.assign({ order: ExaminationOrder[0].value }, { disorder: paper.finishTimes > 0 ? disorder : false, order: order.filter(row => row) }));
+        this.start({
+          disorder: paper.finishTimes > 0 ? disorder : false,
+          order: order.length > 0 ? order.filter(row => row) : ExaminationOrder[0].value,
+        });
       }
       this.setState({ startTime: 60 - this.startTime });
     }, 1000);
@@ -316,14 +364,14 @@ export default class extends Page {
     const { order } = report.setting;
     this.stage = order[order.indexOf(this.stage) + 1];
     this.stageProcess = this.stages[this.stage];
-    this.stageNumber = 0;
     this.stageQuestionTime(0);
     this.setState({ totalNumber: this.stageProcess.number });
+
+    this.accTime += this.stageProcess.time;
   }
 
   relaxStage() {
     this.stageProcess = this.relaxProcess;
-    this.stageNumber = 0;
     this.stageQuestionTime(0);
     this.setState({
       scene: 'relax',
@@ -331,13 +379,14 @@ export default class extends Page {
     return true;
   }
 
-  initStage(stage, time, number) {
+  initStage(stage, time) {
     this.stage = stage;
     this.stageProcess = this.stages[stage];
     this.stageTime = time;
-    this.stageNumber = number;
     this.stageQuestionTime(time);
     this.setState({ totalNumber: this.stageProcess.number });
+
+    this.accTime += this.stageProcess.time;
   }
 
   toggleFullscreen() {

+ 0 - 0
front/project/www/routes/paper/question/detail/index.js


Някои файлове не бяха показани, защото твърде много файлове са промени