Browse Source

fix(all): 修复阶段性bug

Go 4 years ago
parent
commit
8dc3b50009
100 changed files with 1131 additions and 555 deletions
  1. 10 0
      front/build/webpack.config.js
  2. 16 1
      front/package-lock.json
  3. 1 0
      front/package.json
  4. 1 1
      front/project/Constant.js
  5. 1 0
      front/project/admin/components/Association/index.js
  6. 2 2
      front/project/admin/components/QuestionNoList/index.js
  7. 22 3
      front/project/admin/local.json
  8. 9 9
      front/project/admin/routes/course/detail/page.js
  9. 23 0
      front/project/admin/routes/course/package/page.js
  10. 9 3
      front/project/admin/routes/course/previewDetail/page.js
  11. 1 1
      front/project/admin/routes/interaction/comment/page.js
  12. 1 1
      front/project/admin/routes/show/ad/page.js
  13. 23 1
      front/project/admin/routes/show/comment/page.js
  14. 10 3
      front/project/admin/routes/subject/exercise/page.js
  15. 7 5
      front/project/admin/routes/subject/question/page.js
  16. 1 1
      front/project/h5/components/Input/index.js
  17. 4 0
      front/project/www/app.less
  18. BIN
      front/project/www/assets/qrcode.png
  19. BIN
      front/project/www/assets/qrcode_test.png
  20. 2 2
      front/project/www/components/Calculator/index.js
  21. 25 19
      front/project/www/components/Examination/index.js
  22. 1 1
      front/project/www/components/Input/index.js
  23. 1 1
      front/project/www/components/Item/index.js
  24. 2 2
      front/project/www/components/Other/index.js
  25. 8 7
      front/project/www/components/OtherModal/index.js
  26. 2 1
      front/project/www/components/Panel/index.js
  27. 1 1
      front/project/www/components/Panel/index.less
  28. 0 2
      front/project/www/components/TotalSort/index.js
  29. 3 7
      front/project/www/components/UserTable/index.js
  30. 2 2
      front/project/www/components/VipRenew/index.js
  31. 15 3
      front/project/www/local.json
  32. 1 1
      front/project/www/routes/course/detail/page.js
  33. 2 2
      front/project/www/routes/course/main/page.js
  34. 1 1
      front/project/www/routes/examination/list/page.js
  35. 1 1
      front/project/www/routes/examination/main/page.js
  36. 1 1
      front/project/www/routes/exercise/list/page.js
  37. 3 3
      front/project/www/routes/exercise/main/page.js
  38. 11 11
      front/project/www/routes/my/answer/page.js
  39. 61 37
      front/project/www/routes/my/collect/page.js
  40. 3 3
      front/project/www/routes/my/course/page.js
  41. 53 31
      front/project/www/routes/my/error/page.js
  42. 19 6
      front/project/www/routes/my/index.js
  43. 3 3
      front/project/www/routes/my/main/page.js
  44. 178 97
      front/project/www/routes/my/note/page.js
  45. 36 35
      front/project/www/routes/my/report/page.js
  46. 5 3
      front/project/www/routes/my/tools/page.js
  47. 7 0
      front/project/www/routes/page/export/index.less
  48. 7 7
      front/project/www/routes/paper/process/base/index.js
  49. 1 0
      front/project/www/routes/paper/process/page.js
  50. 37 14
      front/project/www/routes/paper/question/detail/index.js
  51. 4 0
      front/project/www/routes/paper/question/detail/index.less
  52. 1 0
      front/project/www/routes/paper/report/index.less
  53. 6 6
      front/project/www/routes/paper/report/page.js
  54. 1 1
      front/project/www/routes/textbook/list/page.js
  55. 3 3
      front/project/www/stores/my.js
  56. 5 1
      front/src/components/Editor/index.js
  57. 117 111
      front/src/index.html
  58. 10 2
      front/src/services/Tools.js
  59. 18 1
      server/build.gradle
  60. 0 6
      server/data/build.gradle
  61. 2 2
      server/data/src/main/java/com/qxgmat/data/constants/enums/ServiceKey.java
  62. 26 0
      server/data/src/main/java/com/qxgmat/data/dao/entity/Comment.java
  63. 35 0
      server/data/src/main/java/com/qxgmat/data/dao/entity/CoursePackage.java
  64. 35 0
      server/data/src/main/java/com/qxgmat/data/dao/entity/UserPaper.java
  65. 2 1
      server/data/src/main/java/com/qxgmat/data/dao/mapping/CommentMapper.xml
  66. 3 2
      server/data/src/main/java/com/qxgmat/data/dao/mapping/CoursePackageMapper.xml
  67. 3 2
      server/data/src/main/java/com/qxgmat/data/dao/mapping/UserPaperMapper.xml
  68. 1 1
      server/data/src/main/java/com/qxgmat/data/relation/ExaminationPaperRelationMapper.java
  69. 8 1
      server/data/src/main/java/com/qxgmat/data/relation/QuestionNoRelationMapper.java
  70. 1 1
      server/data/src/main/java/com/qxgmat/data/relation/mapping/CourseDataRelationMapper.xml
  71. 1 1
      server/data/src/main/java/com/qxgmat/data/relation/mapping/ExaminationPaperRelationMapper.xml
  72. 29 3
      server/data/src/main/java/com/qxgmat/data/relation/mapping/QuestionNoRelationMapper.xml
  73. 2 2
      server/data/src/main/java/com/qxgmat/data/relation/mapping/UserAskQuestionRelationMapper.xml
  74. 8 4
      server/data/src/main/java/com/qxgmat/data/relation/mapping/UserCollectQuestionRelationMapper.xml
  75. 2 2
      server/data/src/main/java/com/qxgmat/data/relation/mapping/UserNoteQuestionRelationMapper.xml
  76. 15 15
      server/data/src/main/java/com/qxgmat/data/relation/mapping/UserPaperRelationMapper.xml
  77. 2 2
      server/data/src/main/java/com/qxgmat/data/relation/mapping/UserQuestionRelationMapper.xml
  78. 1 1
      server/data/src/main/java/com/qxgmat/data/relation/mapping/UserRelationMapper.xml
  79. 3 0
      server/data/src/main/resources/db/migration/V10__update_user_paper.sql
  80. 5 0
      server/data/src/main/resources/db/migration/V9__update_package.sql
  81. 1 1
      server/data/src/main/resources/jdbc.properties.d
  82. 0 7
      server/gateway-api/build.gradle
  83. 8 0
      server/gateway-api/src/main/java/com/qxgmat/Application.java
  84. 2 2
      server/gateway-api/src/main/java/com/qxgmat/controller/admin/QuestionController.java
  85. 14 14
      server/gateway-api/src/main/java/com/qxgmat/controller/api/MyController.java
  86. 8 1
      server/gateway-api/src/main/java/com/qxgmat/controller/api/QuestionController.java
  87. 10 0
      server/gateway-api/src/main/java/com/qxgmat/dto/admin/extend/QuestionNoExtendDto.java
  88. 10 0
      server/gateway-api/src/main/java/com/qxgmat/dto/admin/request/CommentDto.java
  89. 10 0
      server/gateway-api/src/main/java/com/qxgmat/dto/admin/request/CoursePackageDto.java
  90. 10 0
      server/gateway-api/src/main/java/com/qxgmat/dto/admin/request/QuestionNoSearchDto.java
  91. 12 0
      server/gateway-api/src/main/java/com/qxgmat/dto/extend/QuestionExtendDto.java
  92. 10 0
      server/gateway-api/src/main/java/com/qxgmat/dto/extend/UserPaperBaseExtendDto.java
  93. 10 0
      server/gateway-api/src/main/java/com/qxgmat/dto/extend/UserPaperDetailExtendDto.java
  94. 10 0
      server/gateway-api/src/main/java/com/qxgmat/dto/response/CommentDto.java
  95. 3 3
      server/gateway-api/src/main/java/com/qxgmat/dto/response/UserExaminationPaperDto.java
  96. 10 10
      server/gateway-api/src/main/java/com/qxgmat/dto/response/UserNoteQuestionDetailDto.java
  97. 10 10
      server/gateway-api/src/main/java/com/qxgmat/dto/response/UserNoteQuestionInfoDto.java
  98. 10 0
      server/gateway-api/src/main/java/com/qxgmat/dto/response/UserOrderDetailDto.java
  99. 1 1
      server/gateway-api/src/main/java/com/qxgmat/service/UserPaperService.java
  100. 0 0
      server/gateway-api/src/main/java/com/qxgmat/service/UsersService.java

+ 10 - 0
front/build/webpack.config.js

@@ -78,6 +78,15 @@ function getLibPath(path) {
   return pach;
 }
 
+if (!config.css) config.css = [];
+for (let i = 0; i < config.css.length; i += 1) {
+  const script = config.css[i];
+  if (script.indexOf('http') !== 0) {
+    config.css[i] = getPublicPath(script);
+  }
+}
+
+if (!config.scripts) config.scripts = [];
 for (let i = 0; i < config.scripts.length; i += 1) {
   const script = config.scripts[i];
   if (script.indexOf('http') !== 0) {
@@ -111,6 +120,7 @@ webpackConfig.plugins = [
       keyword: config.keyword,
       description: config.description,
       scripts: config.scripts,
+      css: config.css,
     }),
   }),
   new webpack.LoaderOptionsPlugin({

+ 16 - 1
front/package-lock.json

@@ -270,7 +270,7 @@
     },
     "@types/quill": {
       "version": "1.3.10",
-      "resolved": "https://registry.npm.taobao.org/@types/quill/download/@types/quill-1.3.10.tgz",
+      "resolved": "http://registry.npm.taobao.org/@types/quill/download/@types/quill-1.3.10.tgz",
       "integrity": "sha1-3B97ZYf37pS99SkbySKJ9vBJdhM=",
       "requires": {
         "parchment": "^1.1.2"
@@ -6500,6 +6500,21 @@
       "integrity": "sha1-OGchPo3Xm/Ho8jAMDPwe+xgsDfE=",
       "dev": true
     },
+    "katex": {
+      "version": "0.11.1",
+      "resolved": "https://registry.npm.taobao.org/katex/download/katex-0.11.1.tgz",
+      "integrity": "sha1-3zDKQMVlyd8BpGagDVPgeehP+qI=",
+      "requires": {
+        "commander": "^2.19.0"
+      },
+      "dependencies": {
+        "commander": {
+          "version": "2.20.3",
+          "resolved": "https://registry.npm.taobao.org/commander/download/commander-2.20.3.tgz",
+          "integrity": "sha1-/UhehMA+tIgcIHIrpIA16FMa6zM="
+        }
+      }
+    },
     "keycode": {
       "version": "2.2.0",
       "resolved": "https://registry.npm.taobao.org/keycode/download/keycode-2.2.0.tgz",

+ 1 - 0
front/package.json

@@ -77,6 +77,7 @@
     "fastclick": "^1.0.6",
     "history": "^4.7.2",
     "js-sha1": "^0.6.0",
+    "katex": "^0.11.1",
     "moment": "^2.22.2",
     "parchment": "^1.1.4",
     "promise": "^7.1.1",

+ 1 - 1
front/project/Constant.js

@@ -90,7 +90,7 @@ export const ExperienceDay = [{ label: '<=1个月', value: 'lt1month' }, { label
 
 export const ExperiencePercent = [{ label: '50+', value: '_50' }, { label: '100+', value: '_100' }, { label: '150+', value: '_150' }, { label: '200+', value: '_200' }];
 
-export const CourseModule = [{ label: '视频课程', value: 'video' }, { label: '小班课程', value: 'online' }, { label: '1v1课程', value: 'vs' }];
+export const CourseModule = [{ label: '视频课程', value: 'video', hasPreview: true }, { label: '小班课程', value: 'online', hasPreview: false }, { label: '1v1课程', value: 'vs', hasPreview: true }];
 
 export const CourseModuleShow = [{ label: '在线课程', value: 'online', courseModules: ['video', 'online'] }, { label: '1V1私教', value: 'vs', courseModules: ['vs'] }];
 

+ 1 - 0
front/project/admin/components/Association/index.js

@@ -82,6 +82,7 @@ class Association extends Component {
   searchQuestion(params) {
     let handler;
     params.module = this.props.module;
+    params.questionType = this.props.questionType;
     switch (this.props.module) {
       case 'sentence':
       case 'exercise':

+ 2 - 2
front/project/admin/components/QuestionNoList/index.js

@@ -50,13 +50,13 @@ export default class QuestionNoList extends Component {
             this.deleteQuestion(index);
           });
         }
-        return <List.Item actions={[<Icon type='delete' onClick={() => {
+        return <List.Item actions={[<a href={`/subject/question/${item.question.id}`} target='_blank'><Icon type='small-dash' /></a>, <Icon type='delete' onClick={() => {
           this.deleteQuestion(index);
         }} />, <Icon type='bars' className='icon' />]}>
           <List.Item.Meta
             avatar={<Avatar alt={index + 1} size='small' >{index + 1}</Avatar>}
             title={item.title}
-            description={<Typography.Text ellipsis disabled>{item.description}</Typography.Text>}
+            description={<Typography.Paragraph ellipsis={{ rows: 2, expandable: true }} disabled >{item.question ? item.question.description : ''}</Typography.Paragraph>}
           />
         </List.Item>;
       }} />;

+ 22 - 3
front/project/admin/local.json

@@ -1,7 +1,12 @@
 {
   "development": {
+    "css": [
+      "https://cdn.jsdelivr.net/npm/katex@0.11.1/dist/katex.min.css"
+    ],
+    "scripts": [
+      "https://cdn.jsdelivr.net/npm/katex@0.11.1/dist/katex.min.js"
+    ],
     "serverPort": 2999,
-    "scripts": [],
     "proxy": [
       {
         "target": "http://127.0.0.1:8888",
@@ -10,6 +15,20 @@
       }
     ]
   },
-  "test": {},
-  "production": {}
+  "test": {
+    "css": [
+      "https://cdn.jsdelivr.net/npm/katex@0.11.1/dist/katex.min.css"
+    ],
+    "scripts": [
+      "https://cdn.jsdelivr.net/npm/katex@0.11.1/dist/katex.min.js"
+    ]
+  },
+  "production": {
+    "css": [
+      "https://cdn.jsdelivr.net/npm/katex@0.11.1/dist/katex.min.css"
+    ],
+    "scripts": [
+      "https://cdn.jsdelivr.net/npm/katex@0.11.1/dist/katex.min.js"
+    ]
+  }
 }

+ 9 - 9
front/project/admin/routes/course/detail/page.js

@@ -51,7 +51,7 @@ export default class extends Page {
       dataIndex: 'title',
       render: (text, record) => {
         return <EditTableCell value={text} onChange={(v) => {
-          this.changeNo('title', record.id, v);
+          this.changeNo('title', record, v);
         }} />;
       },
     }, {
@@ -72,18 +72,18 @@ export default class extends Page {
       render: (text, record) => {
         return <div>
           <Checkbox checked={!!text} onChange={(v) => {
-            this.changeNo('isTrail', record.id, v.target.checked ? 1 : 0);
+            this.changeNo('isTrail', record, v.target.checked ? 1 : 0);
           }} />
           {text > 0 && <InputNumber value={record.startTrail} onChange={(v) => {
-            this.changeNo('startTrail', record.id, v);
+            this.changeNo('startTrail', record, v);
           }} />}
           {text > 0 && <InputNumber value={record.endTrail} onChange={(v) => {
-            this.changeNo('endTrail', record.id, v);
+            this.changeNo('endTrail', record, v);
           }} />}
           {text > 0 && <Upload
             showUploadList={false}
             beforeUpload={(file) => System.uploadVideo(file).then((result) => {
-              return this.changeNo('trailResource', record.id, result.url);
+              return this.changeNo('trailResource', record, result.url);
             })}
           >
             <Button>上传视频{record.trailResource ? '(已上传)' : ''}</Button>
@@ -98,12 +98,12 @@ export default class extends Page {
         return <div className="table-button">
           {record.no > 1 && (
             <a onClick={() => {
-              this.changeNo('no', record.id, record.no - 1);
+              this.changeNo('no', record, record.no - 1);
             }}>上移</a>
           )}
           {record.no <= total && (
             <a onClick={() => {
-              this.changeNo('no', record.id, record.no + 1);
+              this.changeNo('no', record, record.no + 1);
             }}>下移</a>
           )}
           {(
@@ -198,8 +198,8 @@ export default class extends Page {
     });
   }
 
-  changeNo(field, id, value) {
-    Course.editNo({ id, [field]: value }).then(() => {
+  changeNo(field, record, value) {
+    Course.editNo({ id: record.id, [field]: value, courseId: record.courseId }).then(() => {
       this.refreshNo();
     });
   }

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

@@ -84,6 +84,12 @@ export default class extends Page {
         return SwitchSelectMap[text] || text;
       },
     }, {
+      title: '上架',
+      dataIndex: 'isShow',
+      render: (text) => {
+        return SwitchSelectMap[text] || text;
+      },
+    }, {
       title: '操作',
       dataIndex: 'handler',
       render: (text, record) => {
@@ -103,6 +109,16 @@ export default class extends Page {
               this.special(record, 1);
             }}>首页推荐</a>
           )}
+          {!record.isShow && (
+            <a onClick={() => {
+              this.show(record, 1);
+            }}>上架</a>
+          )}
+          {!!record.isShow && (
+            <a onClick={() => {
+              this.show(record, 0);
+            }}>下架</a>
+          )}
         </div>;
       },
     }];
@@ -163,6 +179,13 @@ export default class extends Page {
     });
   }
 
+  show(row, isShow) {
+    Course.editPackage({ id: row.id, isShow }).then(() => {
+      asyncSMessage('编辑成功!');
+      this.refresh();
+    });
+  }
+
   renderView() {
     return <Block flex>
       {/* <FilterLayout

+ 9 - 3
front/project/admin/routes/course/previewDetail/page.js

@@ -145,6 +145,10 @@ export default class extends Page {
     }
   }
 
+  onRefreshType(questionType) {
+    this.setState({ currentQuestionType: questionType });
+  }
+
   submit() {
     const { form } = this.props;
     form.validateFields((err) => {
@@ -218,7 +222,7 @@ export default class extends Page {
 
 
   renderBase() {
-    const { data = {}, questionNoIds, questionType } = this.state;
+    const { data = {}, questionNoIds, questionType, currentQuestionType } = this.state;
     const { getFieldDecorator } = this.props.form;
     return <Block>
       <Form>
@@ -249,7 +253,9 @@ export default class extends Page {
               { required: true, message: '请选择题型' },
             ],
           })(
-            <Select select={questionType} placeholder='请选择题型' />,
+            <Select select={questionType} placeholder='请选择题型' onChange={(v) => {
+              this.onRefreshType(v);
+            }} />,
           )}
         </Form.Item>
         <Form.Item labelCol={{ span: 5 }} wrapperCol={{ span: 16 }} label='作业标题'>
@@ -262,7 +268,7 @@ export default class extends Page {
           )}
         </Form.Item>
       </Form>
-      {data.paperModule && <Association title='选择题目' ids={questionNoIds} field={'questionNoIds'} module={data.paperModule} modal={false} onConfirm={(info) => {
+      {data.paperModule && <Association title='选择题目' ids={questionNoIds} field={'questionNoIds'} questionType={currentQuestionType} module={data.paperModule} modal={false} onConfirm={(info) => {
         this.setState({ questionNoIds: info.questionNoIds });
         return Promise.resolve();
       }} />}

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

@@ -93,7 +93,7 @@ export default class extends Page {
     }, {
       title: '评价时间',
       sorter: true,
-      dataIndex: 'createTime',
+      dataIndex: 'updateTime',
       render: (text) => {
         return formatDate(text, 'YYYY-MM-DD HH:mm:ss');
       },

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

@@ -25,7 +25,7 @@ const AdChannelMap = getMap(AdChannelFlatten, 'value', 'label');
 const AdPlaceTree = formatTreeData([].concat(AdChannelFlatten.map(row => Object.assign({}, row)), AdPlace), 'value', 'label', 'parent');
 const AdPlaceChannelMap = getMap(AdPlaceTree, 'value', 'children');
 const AdPlaceMap = getMap(AdPlaceTree, 'value', 'label', 'children');
-console.log(AdPlaceTree, AdPlaceMap);
+
 export default class extends Page {
   init() {
     this.formF = null;

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

@@ -8,7 +8,7 @@ import ActionLayout from '@src/layouts/ActionLayout';
 import TableLayout from '@src/layouts/TableLayout';
 import DragList from '@src/components/DragList';
 import { getMap, formatDate, bindSearch, formatTreeData, flattenTree } from '@src/services/Tools';
-import { asyncSMessage, asyncForm } from '@src/services/AsyncTools';
+import { asyncSMessage, asyncForm, asyncDelConfirm } from '@src/services/AsyncTools';
 import { CommentChannel, SystemSelect } from '../../../../Constant';
 import { System } from '../../../stores/system';
 import { Course } from '../../../stores/course';
@@ -132,6 +132,13 @@ export default class extends Page {
         return formatDate(text, 'YYYY-MM-DD HH:mm:ss');
       },
     }, {
+      title: '更新时间',
+      sorter: true,
+      dataIndex: 'updateTime',
+      render: (text) => {
+        return formatDate(text, 'YYYY-MM-DD HH:mm:ss');
+      },
+    }, {
       title: '来源',
       dataIndex: 'isSystem',
       render: (text) => {
@@ -147,6 +154,11 @@ export default class extends Page {
               this.editAction(record);
             }}>编辑</a>
           )}
+          {(
+            <a onClick={() => {
+              this.deleteAction(record);
+            }}>删除</a>
+          )}
         </div>;
       },
     }];
@@ -242,6 +254,16 @@ export default class extends Page {
     });
   }
 
+  deleteAction(row) {
+    asyncDelConfirm('删除确认', '是否删除选中?', () => {
+      const handler = System.delComment({ id: row.id });
+      return handler.then(() => {
+        asyncSMessage('删除成功!');
+        this.refresh();
+      });
+    });
+  }
+
   special(row, isShow) {
     System.editComment({ id: row.id, isShow }).then(() => {
       asyncSMessage('操作成功!');

+ 10 - 3
front/project/admin/routes/subject/exercise/page.js

@@ -108,7 +108,11 @@ export default class extends Page {
       },
     }, {
       title: '题目ID',
-      dataIndex: 'title',
+      sorter: true,
+      dataIndex: 'no',
+      render: (text, record) => {
+        return record.title;
+      },
     }, {
       title: '练习册',
       dataIndex: 'paper',
@@ -123,18 +127,21 @@ export default class extends Page {
       },
     }, {
       title: '难度',
-      dataIndex: 'difficlut',
+      sorter: true,
+      dataIndex: 'difficult',
       render: (text, record) => {
         return record.question.difficult;
       },
     }, {
       title: '易错度',
+      sorter: true,
       dataIndex: 'correct',
       render: (text, record) => {
         return formatPercent(record.totalNumber - record.totalCorrect, record.totalNumber, false);
       },
     }, {
       title: '平均时间',
+      sorter: true,
       dataIndex: 'time',
       render: (text, record) => {
         return formatSeconds(record.totalTime / record.totalNumber);
@@ -142,7 +149,7 @@ export default class extends Page {
     }, {
       title: '序号',
       sorter: this.state.search.paperId,
-      dataIndex: 'no',
+      dataIndex: 'number',
       render: (text, record) => {
         const { search } = this.state;
         if (search.paperId) {

+ 7 - 5
front/project/admin/routes/subject/question/page.js

@@ -133,7 +133,7 @@ export default class extends Page {
         this.refreshDifficultScore(result.questionType, result.difficult);
         form.getFieldDecorator('difficultScore');
         form.setFieldsValue({ difficultScore: result.difficultScore });
-        this.setState({ associationContentQuestionNos: [], realtionQuestionNos: [] });
+        this.setState({ associationContentQuestionNos: [], relationQuestionNos: [] });
         if (result.questionNoIds && result.questionNoIds.length > 0) {
           Question.listNo({ ids: result.questionNoIds }).then(list => {
             this.setState({ questionNos: list });
@@ -316,7 +316,7 @@ export default class extends Page {
         const data = form.getFieldsValue(fields);
         let handler;
         data.associationContent = this.state.associationContentQuestionNos.map(row => row.id);
-        data.stem = data.stem || '';
+        data.stem = data.stem || ''; // .replace(/<span\s*class=\"ql-formula\"<\/span>/, '')
         data.description = (data.stem || '').replace(/<[^>]+>/g, '').replace(/#select#/, '').replace(/#table#/, '').replace(/&nbsp;/, '');
         data.qxContent = data.qxContent || '';
         data.officialContent = data.officialContent || '';
@@ -333,7 +333,7 @@ export default class extends Page {
           row.select = (row.select || []).filter(r => r);
           return row;
         });
-        data.relationQuestion = this.state.realtionQuestionNos.map(row => row.id);
+        data.relationQuestion = this.state.relationQuestionNos.map(row => row.id);
         if (!data.id) {
           handler = Question.add(data);
         } else {
@@ -356,7 +356,8 @@ export default class extends Page {
   searchStem() {
     const { form } = this.props;
     const content = (form.getFieldValue('stem') || '').replace(/<[^>]+>/g, '');
-    Question.searchStem({ content })
+    const questionType = (form.getFieldValue('questionType') || '');
+    Question.searchStem({ content, questionType })
       .then(result => {
         if (result.list.length > 0) {
           asyncGet(() => import('../../../components/SimilarQuestionNo'),
@@ -393,7 +394,7 @@ export default class extends Page {
         value: row.id,
       };
     }, relationQuestion, null);
-    if (relationQuestion) this.listQuestion(relationQuestion, 'ids', 'realtionQuestionNos');
+    if (relationQuestion) this.listQuestion(relationQuestion, 'ids', 'relationQuestionNos');
   }
 
   listQuestion(values, field, targetField) {
@@ -429,6 +430,7 @@ export default class extends Page {
                 ['bold', 'underline', 'blockquote'],
                 [{ color: [] }, { background: [] }],
                 [{ list: 'ordered' }, { list: 'bullet' }, { indent: '-1' }, { indent: '+1' }],
+                ['formula'],
                 ['clean'],
               ],
               handlers: {

+ 1 - 1
front/project/h5/components/Input/index.js

@@ -39,7 +39,7 @@ export class SelectInput extends Component {
         left={
           <span className="g-input-left-select" onClick={() => this.setState({ showSelect: !showSelect })}>
             {selectValue}
-            <Icon type={showSelect ? 'up' : 'down'} />
+            <Icon type={showSelect ? 'down' : 'up'} />
             {showSelect && <ul className="select-list">{select.map((row) => {
               return <li onClick={() => {
                 this.setState({ showSelect: false });

+ 4 - 0
front/project/www/app.less

@@ -318,6 +318,10 @@
   color: #6E6E78FF !important;
 }
 
+.t-16 {
+  color: #1890ff !important;
+}
+
 .b-c-1 {
   background: #F7F7F7;
 }

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


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


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

@@ -45,9 +45,9 @@ export default class Calculator extends Component {
     let { value } = this.state;
     for (let i = 0; i < this.typeList.length; i += 1) {
       if (i === 0) {
-        value = this.f(this.valueList[0], this.valueList[1], this.typeList[0]);
+        value = this.f(Number(this.valueList[0]), Number(this.valueList[1]), this.typeList[0]);
       } else if (this.valueList[i + 1] !== undefined) {
-        value = this.f(value, this.valueList[i + 1], this.typeList[i]);
+        value = this.f(Number(value), Number(this.valueList[i + 1]), this.typeList[i]);
       }
     }
     return value;

+ 25 - 19
front/project/www/components/Examination/index.js

@@ -16,6 +16,7 @@ const PrepareStatusMap = getMap(PrepareStatus, 'value', 'short');
 const PrepareExaminationTimeMap = getMap(PrepareExaminationTime, 'value', 'label');
 const PrepareScoreTimeMap = getMap(PrepareScoreTime, 'value', 'label');
 
+console.log(PrepareStatusMap, PrepareExaminationTimeMap);
 export default class extends Component {
   constructor(props) {
     super(props);
@@ -48,7 +49,7 @@ export default class extends Component {
         cancelText: '修改',
       },
     };
-    this.state = { step: 0, data: { prepareGoal: 650 } };
+    this.state = { step: 0, data: { prepareGoal: 700 } };
   }
 
   componentWillReceiveProps(nextProps) {
@@ -75,31 +76,35 @@ export default class extends Component {
           const scoreTimeTotal = result.stat.scoreTime.reduce((p, n) => { return p + n.value; }, 0);
           const stat = {
             status: result.stat.status.map((row, index) => {
-              row.value = formatPercent(row.value, statusTotal);
-              row.label = `${PrepareStatusMap[row.key]}; ${row.value}%`;
-              row.color = this.statusColors[index];
-              return row;
+              const a = {};
+              a.value = formatPercent(row.value, statusTotal);
+              a.label = `${PrepareStatusMap[row.key]}; ${a.value}%`;
+              a.color = this.statusColors[index];
+              return a;
             }),
             goal: result.stat.goal.map((row, index) => {
-              row.value = formatPercent(row.value, goalTotal);
-              row.label = `${row.key}+; ${row.value}%`;
-              row.color = this.goalColors[index];
-              return row;
+              const a = {};
+              a.value = formatPercent(row.value, goalTotal);
+              a.label = `${row.key}+; ${a.value}%`;
+              a.color = this.goalColors[index];
+              return a;
             }),
-            examinationTime: result.stat.status.map((row, index) => {
-              row.value = formatPercent(row.value, examinationTimeTotal);
-              row.label = `${PrepareExaminationTimeMap[row.key]}; ${row.value}%`;
-              row.color = this.examinationTimeColors[index];
-              return row;
+            examinationTime: result.stat.examinationTime.map((row, index) => {
+              const a = {};
+              a.value = formatPercent(row.value, examinationTimeTotal);
+              a.label = `${PrepareExaminationTimeMap[row.key]}; ${a.value}%`;
+              a.color = this.examinationTimeColors[index];
+              return a;
             }),
             scoreTime: result.stat.scoreTime.map((row, index) => {
-              row.value = formatPercent(row.value, scoreTimeTotal);
-              row.label = `${PrepareScoreTimeMap[row.key]}; ${row.value}%`;
-              row.color = this.scoreTimeColors[index];
-              return row;
+              const a = {};
+              a.value = formatPercent(row.value, scoreTimeTotal);
+              a.label = `${PrepareScoreTimeMap[row.key]}; ${a.value}%`;
+              a.color = this.scoreTimeColors[index];
+              return a;
             }),
           };
-          result.prepareGoal = result.prepareGoal || 650;
+          result.prepareGoal = result.prepareGoal || 700;
           result.prepareScoreTime = result.prepareScoreTime ? moment(result.prepareScoreTime) : null;
           this.setState({ data: result, stat, first: !result.prepareStatus, step: !result.prepareStatus ? 0 : 4, info: result.info });
         });
@@ -260,6 +265,7 @@ export default class extends Component {
 
   renderStep4() {
     const { first, data, stat = {} } = this.state;
+    console.log(data, stat);
     return (
       <div className="step-4-layout">
         {first && (

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

@@ -51,7 +51,7 @@ export class SelectInput extends Component {
         left={
           <span className="g-input-left-select" onClick={() => this.setState({ showSelect: !showSelect })}>
             {selectValue}
-            <Icon type={showSelect ? 'up' : 'down'} />
+            <Icon type={showSelect ? 'down' : 'up'} />
             {showSelect && (
               <ul className="select-list">
                 {select.map(row => {

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

@@ -43,7 +43,7 @@ export class SingleItem extends Component {
     const { add } = this.state;
     return (
       <div className="single-item">
-        <div className="img c-p" style={{ backgroundImage: `url(${data.cover})` }} onClick={() => linkTo(`/course/detail/${data.id}`)}>
+        <div className="img c-p" style={{ backgroundImage: `url(${data.cover})`, backgroundSize: '100% 100%' }} onClick={() => linkTo(`/course/detail/${data.id}`)}>
           <div className="title">
             <div className="tag">{CrowdMap[data.crowd]}</div>
             <Link className='f-w-b' to={`/course/detail/${data.id}`} target="_blank">{data.title}</Link>

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

@@ -44,7 +44,7 @@ export class CommentFalls extends Component {
                 <div className="item-header">
                   <Assets className="avatar" src={row.user ? row.user.avatar : row.avatar} />
                   <div className="name">{row.user ? row.user.nickname : row.nickname}</div>
-                  <div className="date">{formatDate(row.createTime, 'YYYY年MM月DD日')}</div>
+                  <div className="date">{formatDate(row.updateTime, 'YYYY年MM月DD日')}</div>
                 </div>
                 <div className="item-body">{row.content}</div>
               </div>
@@ -216,7 +216,7 @@ export class Comment extends Component {
         <Assets className="avatar m-r-1" src={data.user ? data.user.avatar : data.avatar} />
         <div className="d-i-b">
           <div className="t-1 t-s-18">{data.user ? data.user.nickname : data.nickname}</div>
-          <div className="t-3">{formatDate(data.createTime, 'YYYY-MM-DD')}</div>
+          <div className="t-3">{formatDate(data.updateTime, 'YYYY-MM-DD')}</div>
         </div>
         <div className="t-1 t-s-18 m-t-1">{data.content}</div>
       </div>

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

@@ -517,7 +517,7 @@ export class RealAuth extends Component {
         <div className="real-auth-modal-wrapper">
           <div className="real-auth-text">
             <div className="t1">完成实名认证即可领取:</div>
-            <div className="t2">6个月VIP权限 和 5折机经优惠劵。</div>
+            <div className="t2">30天VIP权限。</div>
             <div className="t3">扫码关注公众号,点击“我的-实名认证”</div>
           </div>
           <div className="real-auth-qrcode">
@@ -1470,6 +1470,7 @@ export class QuestionNoteModal extends Component {
   constructor(props) {
     super(props);
     this.state = { data: {}, noteField: AskTarget[0].value };
+    this.target = [{ label: '题目', value: 'question', title: '题目', key: 'question' }];
   }
 
   componentWillReceiveProps(nextProps) {
@@ -1492,8 +1493,8 @@ export class QuestionNoteModal extends Component {
     const { data } = this.state;
     return My.updateQuestionNote(questionNo.id, data).then(() => {
       if (close) {
-        this.setState({ data: {}, noteField: AskTarget[0].value });
-        if (onConfirm) onConfirm();
+        this.setState({ noteField: AskTarget[0].value });
+        if (onConfirm) onConfirm(data);
       }
     });
   }
@@ -1511,10 +1512,10 @@ export class QuestionNoteModal extends Component {
       <div hidden={!show} className="question-modal question-note-modal">
         <div className="mask" />
         <div className="modal-body">
-          <div className="modal-title">提问</div>
+          <div className="modal-title">笔记</div>
           <div className="modal-content">
             <div className="tabs">
-              {AskTarget.map(item => {
+              {this.target.map(item => {
                 return (
                   <div
                     className={`tab ${noteField === item.key ? 'active' : ''}`}
@@ -1549,14 +1550,14 @@ export class QuestionNoteModal extends Component {
                 >
                   取消
                 </AnswerButton>
-                <AnswerButton
+                {/* <AnswerButton
                   size="lager"
                   onClick={() => {
                     this.onConfirm();
                   }}
                 >
                   编辑
-                </AnswerButton>
+                </AnswerButton> */}
                 <AnswerButton
                   size="lager"
                   onClick={() => {

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

@@ -207,7 +207,8 @@ export function SmallPanel(props) {
 
 export function SmallWaitPanel(props) {
   const { style, title, lock, data = {}, onOpen } = props;
-  const { endTime } = data;
+  const { unUseRecord } = data;
+  const { endTime } = unUseRecord;
   return (
     <Module style={style} className="panel small-wait-panel">
       <div className="header">

+ 1 - 1
front/project/www/components/Panel/index.less

@@ -32,7 +32,7 @@
       margin-bottom: 24px;
 
       .pie-chart {
-        width: 110px;
+        width: 100px;
         height: 110px;
         margin-right: 22px;
         display: inline-block;

+ 0 - 2
front/project/www/components/TotalSort/index.js

@@ -18,8 +18,6 @@ export default class extends Component {
   }
 
   compute(value, quant) {
-    if (value === this.props.value && quant === this.state.quant) return;
-    console.log(quant);
     this.time += 1;
     const number = this.time;
     this.setState({ quant });

+ 3 - 7
front/project/www/components/UserTable/index.js

@@ -59,18 +59,14 @@ export default class UserTable extends Component {
                       {item.fixSort &&
                         (sortMap[item.key] ? (
                           <Icon active name="arrow-down" onClick={() => this.onSort(item.key, '')} />
-                        ) : (
-                          <Icon name="arrow-up" onClick={() => this.onSort(item.key, 'desc')} />
-                        ))}
+                        ) : (<Icon name="arrow-up" onClick={() => this.onSort(item.key, 'desc')} />))}
                       {item.sort &&
                         (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' : '')}
                           />
-                        ) : (
-                          <Assets name="seqencing2_normal" onClick={() => this.onSort(item.key, 'asc')} />
-                        ))}
+                        ) : (<Assets name="seqencing2_normal" onClick={() => this.onSort(item.key, 'asc')} />))}
                     </th>
                   );
                 })}
@@ -94,7 +90,7 @@ export default class UserTable extends Component {
         </table>
         {data.length === 0 && <div className="empty">暂无数据</div>}
         {total > 0 && data.length > 0 && (
-          <UserPagination jump={jump} total={total} pageSize={pageSize} current={current} onChange={onChange} />
+          <UserPagination jump={jump} total={Number(total)} pageSize={Number(pageSize)} current={Number(current)} onChange={onChange} />
         )}
       </div>
     );

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

@@ -224,7 +224,7 @@ export default class extends Component {
             <Assets className="icon" name={`realname2${data.bindReal ? '' : '_gray'}`} />
             <div className="t">
               <Assets name={data.bindReal ? 'gift_active' : 'gift'} />
-              6个月
+              30天
             </div>
             <Button size="small" radius disabled={data.bindReal} onClick={() => {
               onReal();
@@ -249,7 +249,7 @@ export default class extends Component {
             <Assets className="icon" name={`information2${data.bindPrepare ? '' : '_gray'}`} />
             <div className="t">
               <Assets name={data.bindPrepare ? 'gift_active' : 'gift'} />
-              1个月
+              7天
             </div>
             <Button size="small" radius disabled={data.bindPrepare} onClick={() => {
               onPrepare();

+ 15 - 3
front/project/www/local.json

@@ -1,10 +1,14 @@
 {
   "development": {
+    "css": [
+      "https://cdn.jsdelivr.net/npm/katex@0.11.1/dist/katex.min.css"
+    ],
     "scripts": [
       "masonry.pkgd.min.js",
       "/qrious.js",
       "/ckeditor/ckeditor.js",
-      "http://res.wx.qq.com/connect/zh_CN/htmledition/js/wxLogin.js"
+      "http://res.wx.qq.com/connect/zh_CN/htmledition/js/wxLogin.js",
+      "https://cdn.jsdelivr.net/npm/katex@0.11.1/dist/katex.min.js"
     ],
     "proxy": [
       {
@@ -15,19 +19,27 @@
     ]
   },
   "test": {
+    "css": [
+      "https://cdn.jsdelivr.net/npm/katex@0.11.1/dist/katex.min.css"
+    ],
     "scripts": [
       "masonry.pkgd.min.js",
       "/qrious.js",
       "/ckeditor/ckeditor.js",
-      "http://res.wx.qq.com/connect/zh_CN/htmledition/js/wxLogin.js"
+      "http://res.wx.qq.com/connect/zh_CN/htmledition/js/wxLogin.js",
+      "https://cdn.jsdelivr.net/npm/katex@0.11.1/dist/katex.min.js"
     ]
   },
   "production": {
+    "css": [
+      "https://cdn.jsdelivr.net/npm/katex@0.11.1/dist/katex.min.css"
+    ],
     "scripts": [
       "masonry.pkgd.min.js",
       "/qrious.js",
       "/ckeditor/ckeditor.js",
-      "http://res.wx.qq.com/connect/zh_CN/htmledition/js/wxLogin.js"
+      "http://res.wx.qq.com/connect/zh_CN/htmledition/js/wxLogin.js",
+      "https://cdn.jsdelivr.net/npm/katex@0.11.1/dist/katex.min.js"
     ]
   }
 }

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

@@ -42,7 +42,7 @@ export default class extends Page {
         render: text => {
           text = text || {};
           const progress = text.report ? formatPercent(text.report.userNumber, text.report.questionNumber) : 0;
-          const times = text.paper ? text.paper.finishTimes : 0;
+          const times = text.paper ? text.paper.resetTimes : 0;
           return (
             <div>
               <div className="v-a-m d-i-b">

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

@@ -61,11 +61,11 @@ export default class extends Page {
           <div className="main-title">找到你的Style</div>
           <div className="video-list">
             <div className="video-div">
-              <Video src={courseIndex.onlineVideo || '/1.mp4'} width={580} height={360} hideAction hideProgress />
+              {courseIndex.onlineVideo ? <Video src={courseIndex.onlineVideo} width={580} height={360} hideAction hideProgress /> : null}
               <div className="name" onClick={() => linkTo('/course/online')}>在线课程 ></div>
             </div>
             <div className="video-div">
-              <Video src={courseIndex.vsVideo || '/1.mp4'} width={580} height={360} hideAction hideProgress />
+              {courseIndex.vsVideo ? <Video src={courseIndex.vsVideo} width={580} height={360} hideAction hideProgress /> : null}
               <div className="name" onClick={() => linkTo('/course/vs')}>1v1私教 ></div>
             </div>
           </div>

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

@@ -402,7 +402,7 @@ export default class extends Page {
             <div className="table-row">
               <div className="night f-s-16">{record.title}</div>
               <div>
-                <ProgressText progress={progress} times={record.paper ? record.paper.finishTimes : 0} size="small" />
+                <ProgressText progress={progress} times={record.paper ? record.paper.resetTimes : 0} size="small" />
               </div>
             </div>
           );

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

@@ -255,7 +255,7 @@ export default class extends Page {
           {this.state.faqs && <QAList data={this.state.faqs} active={'faq'} tabs={[{ key: 'faq', name: 'FAQs' }]} />}
         </div>
 
-        <OpenConfirmModal data={record} onCancel={() => this.setState({ record: null })} onConfirm={() => this.open(record.id)} />
+        <OpenConfirmModal show={record} data={record} onCancel={() => this.setState({ record: null })} onConfirm={() => this.open(record.id)} />
       </div>
     );
   }

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

@@ -33,7 +33,7 @@ export default class extends Page {
           <div className="table-row">
             <div className="night f-s-16">{record.title}</div>
             <div>
-              <ProgressText progress={progress} times={record.paper ? record.paper.finishTimes : 0} size="small" />
+              <ProgressText progress={progress} times={record.paper ? record.paper.resetTimes : 0} size="small" />
             </div>
           </div>
         );

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

@@ -51,7 +51,7 @@ const exerciseColumns = [{
       <div className="table-row">
         <div className="night f-s-16">{record.title}</div>
         <div>
-          <ProgressText progress={progress} times={record.paper ? record.paper.finishTimes : 0} size="small" />
+          <ProgressText progress={progress} times={record.paper ? record.paper.resetTimes : 0} size="small" />
         </div>
       </div>
     );
@@ -147,7 +147,7 @@ export default class extends Page {
           <div className="table-row">
             <div className="night f-s-16">{record.title}</div>
             <div>
-              <ProgressText progress={progress} times={record.paper ? record.paper.finishTimes : 0} size="small" />
+              <ProgressText progress={progress} times={record.paper ? record.paper.resetTimes : 0} size="small" />
             </div>
           </div>
         );
@@ -363,7 +363,7 @@ export default class extends Page {
           }
           return result;
         })
-        .then(({ code, trailPages, chapters }) => {
+        .then(({ code, trailPages, chapters = [] }) => {
           return Sentence.listArticle().then(result => {
             const chapterSteps = [];
             const chapterMap = {};

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

@@ -1,5 +1,5 @@
 import React from 'react';
-import { Link } from 'react-router-dom';
+// import { Link } from 'react-router-dom';
 import './index.less';
 import Page from '@src/containers/Page';
 import { timeRange, getMap, formatDate } from '@src/services/Tools';
@@ -25,11 +25,11 @@ const columns = [
     render(text, row) {
       return (
         <div className="group">
-          <Link
-            to={row.userQuestionId ? `/paper/question/${row.userQuestionId}` : `/question/detail/${row.questionNoId}`}
+          <a
+            href={row.userQuestionId ? `/paper/question/${row.userQuestionId}` : `/question/detail/${row.questionNoId}`} target="_blank"
           >
             {QuestionTypeMap[text]}
-          </Link>
+          </a>
         </div>
       );
     },
@@ -40,11 +40,11 @@ const columns = [
     render(text, row) {
       return (
         <div className="group">
-          <Link
-            to={row.userQuestionId ? `/paper/question/${row.userQuestionId}` : `/question/detail/${row.questionNoId}`}
+          <a
+            href={row.userQuestionId ? `/paper/question/${row.userQuestionId}` : `/question/detail/${row.questionNoId}`} target="_blank"
           >
             {text}
-          </Link>
+          </a>
         </div>
       );
     },
@@ -54,12 +54,12 @@ const columns = [
     width: 540,
     render(text, row) {
       return (
-        <div className="group text-hidden">
-          <Link
-            to={row.userQuestionId ? `/paper/question/${row.userQuestionId}` : `/question/detail/${row.questionNoId}`}
+        <div className="group text-hidden" style={{ width: '540px' }}>
+          <a
+            href={row.userQuestionId ? `/paper/question/${row.userQuestionId}` : `/question/detail/${row.questionNoId}`} target="_blank"
           >
             {text}
-          </Link>
+          </a>
         </div>
       );
     },

+ 61 - 37
front/project/www/routes/my/collect/page.js

@@ -1,10 +1,10 @@
 import React, { Component } from 'react';
-import { Link } from 'react-router-dom';
+// import { Link } from 'react-router-dom';
 import './index.less';
 import { Icon, Checkbox } from 'antd';
 import Page from '@src/containers/Page';
 import Assets from '@src/components/Assets';
-import { timeRange, getMap, formatPercent, formatMonth, formatDate, formatSeconds } from '@src/services/Tools';
+import { timeRange, getMap, formatMonth, formatDate, formatSeconds } from '@src/services/Tools';
 import UserLayout from '../../../layouts/User';
 import UserTable from '../../../components/UserTable';
 import UserAction from '../../../components/UserAction';
@@ -28,9 +28,9 @@ const exportType = [
   { key: 'question', title: '题目' },
   { key: 'official', title: '官方解析' },
   { key: 'note', title: '我的笔记' },
-  { key: 'qx', title: '千行解析', auth: true },
-  { key: 'association', title: '题源联想', auth: true },
-  { key: 'qa', title: '相关问答', auth: true },
+  // { key: 'qx', title: '千行解析', auth: true },
+  // { key: 'association', title: '题源联想', auth: true },
+  // { key: 'qa', title: '相关问答', auth: true },
 ];
 
 export default class extends Page {
@@ -70,13 +70,13 @@ export default class extends Page {
         width: 100,
         fixSort: true,
         render: (text, record) => {
-          return <Link to={`/question/detail/${record.questionNoId}`}>{text}</Link>;
+          return <a href={`/question/detail/${record.questionNoId}`} target="_blank">{text}</a>;
         },
       },
       {
         key: 'description',
         title: '内容',
-        width: 220,
+        width: 180,
         render: (text) => {
           return <div style={{ maxHeight: '80px', overflow: 'hidden' }}>{text}</div>;
         },
@@ -84,33 +84,34 @@ export default class extends Page {
       {
         key: 'time',
         title: '耗时',
-        width: 80,
+        width: 100,
         sort: true,
         render: (text, record) => {
           const user = record.stat ? record.stat.userTime / record.stat.userNumber : 0;
           const all = record.questionNo.totalNumber ? record.questionNo.totalTime / record.questionNo.totalNumber : 0;
           return <div className="sub">
-            <div className="t-2 t-s-12">{user > 0 ? formatSeconds(user) : '-'}{user > 0 && <Assets height={10} width={10} name={user > all ? 'up' : 'down'} />}</div>
+            <div className="t-2 t-s-12">{user > 0 ? formatSeconds(user) : '-'}{user > 0 && <Assets height={10} width={10} name={user > all ? 'down' : 'up'} />}</div>
             <div className="t-6 t-s-12">全站{all > 0 ? formatSeconds(all) : '-'}</div>
           </div>;
         },
       },
       {
-        key: 'correct',
-        title: '错误率',
-        width: 95,
+        key: 'update_time',
+        title: '更新时间',
+        width: 105,
         sort: true,
-        render: (text, record) => {
+        render: (text) => {
           return <div className="sub">
-            <div className="t-2 t-s-12">{record.stat ? formatPercent(record.stat.userNumber - record.stat.userCorrect, record.stat.userNumber, false) : '-'}</div>
-            <div className="t-6 t-s-12">{record.stat ? `${record.stat.userNumber - record.stat.userCorrect}/${record.stat.userNumber}` : '-'}</div>
+            <div className="t-2 t-s-12">{text.split(' ')[0]}</div>
+            <div className="t-6 t-s-12">{text.split(' ')[1]}</div>
           </div>;
         },
       },
       {
-        key: 'latest_time',
-        title: '最近做题',
-        width: 95,
+        key: 'collect_time',
+        title: '收藏时间',
+        width: 105,
+        sort: true,
         render: (text) => {
           return <div className="sub">
             <div className="t-2 t-s-12">{text.split(' ')[0]}</div>
@@ -133,6 +134,8 @@ export default class extends Page {
     data.filterMap = this.state.search;
     if (data.order) {
       data.sortMap = { [data.order]: data.direction };
+    } else {
+      data.sortMap = { collect_time: 'desc' };
     }
     if (data.timerange) {
       data.filterMap.timerange = data.timerange;
@@ -176,13 +179,31 @@ export default class extends Page {
           row.title = row.questionNo.title;
           row.description = row.question.description;
           row.latest_time = row.latestTime ? formatDate(row.latestTime, 'YYYY-MM-DD HH:mm:ss') : '';
+          row.collect_time = row.createTime ? formatDate(row.createTime, 'YYYY-MM-DD HH:mm:ss') : '';
+          row.update_time = row.question.updateTime ? formatDate(row.question.updateTime, 'YYYY-MM-DD HH:mm:ss') : '';
           return row;
         });
-        this.setState({ list: result.list, total: result.total });
+        const { selectList, allChecked, selectPage } = this.updateSelectInfo(data.page, result.list);
+        this.setState({ list: result.list, total: result.total, selectList, allChecked, selectPage });
       });
     });
   }
 
+  updateSelectInfo(page, list, selectList = null) {
+    const { selectPage = {} } = this.state;
+    let allChecked = false;
+    if (selectList == null) {
+      selectList = [];
+      if (selectPage[Number(page)]) {
+        selectList = selectPage[Number(page)];
+      }
+    } else {
+      selectPage[Number(page)] = selectList;
+    }
+    allChecked = list.filter(row => selectList.indexOf(row.key) >= 0).length === list.length && list.length > 0;
+    return { selectList, allChecked, selectPage };
+  }
+
   refreshArticle(data) {
     const [startTime, endTime] = timeRange(data.timerange);
     My.listExperienceCollect(Object.assign({ startTime, endTime }, this.state.search)).then(result => {
@@ -230,30 +251,33 @@ export default class extends Page {
   }
 
   onAll(checked) {
-    const { selectList } = this.state;
+    const { selectPage = {}, search = {} } = this.state;
+    let { selectList } = this.state;
+    const { page } = search;
     const { list = [] } = this.state;
     if (checked) {
       list.forEach(item => {
         if (selectList.indexOf(item.key) >= 0) return;
         selectList.push(item.key);
       });
+      selectPage[Number(page)] = selectList;
     } else {
-      list.forEach(item => {
-        const index = selectList.indexOf(item.key);
-        if (index < 0) return;
-        selectList.splice(index, 1);
-      });
+      selectList = [];
+      delete selectPage[Number(page)];
     }
-    this.setState({ selectList, allChecked: checked });
+    this.setState({ selectList, selectPage, allChecked: checked });
   }
 
-  onSelect(selectList) {
-    this.setState({ selectList, allCheckbox: false });
+  onSelect(slist) {
+    const { search = {}, list } = this.state;
+    const { selectList, allChecked, selectPage } = this.updateSelectInfo(search.page, list, slist);
+    this.setState({ selectList, allChecked, selectPage });
   }
 
   onAction(key) {
     const { info } = this.props.user;
-    const { selectList } = this.state;
+    const { selectPage = {} } = this.state;
+    const selectList = [].concat(...Object.values(selectPage).concat());
     switch (key) {
       case 'help':
         this.setState({ showTips: true });
@@ -297,23 +321,23 @@ export default class extends Page {
           this.setState({ showWarn: true, warn: { title: '导出', content: '不可多余100题,请重新选择' } });
           return;
         }
-        if (info.bindReal) {
-          this.setState({ showExportConfirm: true, exportInfo: { include: exportType.map(row => row.key), questionNoIds: selectList } });
-        } else {
-          this.setState({ showExportAuthConfirm: true, exportInfo: { include: exportType.filter(row => !row.auth).map(row => row.key), questionNoIds: selectList } });
-        }
+        // if (info.bindReal) {
+        this.setState({ showExportConfirm: true, exportInfo: { include: exportType.map(row => row.key), questionNoIds: selectList } });
+        // } else {
+        //   this.setState({ showExportAuthConfirm: true, exportInfo: { include: exportType.filter(row => !row.auth).map(row => row.key), questionNoIds: selectList } });
+        // }
         break;
       default:
     }
   }
 
   clearCollectQuestion() {
-    const { exportInfo } = this.state;
-    My.clearQuestionCollect(exportInfo.questionNoIds)
+    const { clearInfo } = this.state;
+    My.clearQuestionCollect(clearInfo.questionNoIds)
       .then(() => {
         this.refresh();
       })
-      .then(e => {
+      .catch(e => {
         this.setState({ showWarn: true, warn: { title: '移除', content: e.message }, showClearConfirm: false });
       });
   }

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

@@ -624,7 +624,7 @@ class CourseOnline extends Component {
         render: text => {
           text = text || {};
           const progress = text.report ? formatPercent(text.report.userNumber, text.report.questionNumber) : 0;
-          const times = text.paper ? text.paper.finishTimes : 0;
+          const times = text.paper ? text.paper.resetTimes : 0;
           return (
             <div>
               <div className="v-a-m d-i-b">
@@ -804,7 +804,7 @@ class CourseOnline extends Component {
             </div>
           </div>
           <div className="open">
-            <GIcon name={open ? 'up' : 'down'} onClick={() => this.setState({ open: !open })} />
+            <GIcon name={open ? 'down' : 'up'} onClick={() => this.setState({ open: !open })} />
           </div>
         </div>
         {open && this.renderTable()}
@@ -1065,7 +1065,7 @@ class CourseVs extends Component {
           render: text => {
             text = text || {};
             const progress = text.report ? formatPercent(text.report.userNumber, text.report.questionNumber) : 0;
-            const times = text.paper ? text.paper.finishTimes : 0;
+            const times = text.paper ? text.paper.resetTimes : 0;
             return (
               <div>
                 <div className="v-a-m d-i-b">

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

@@ -1,5 +1,5 @@
 import React from 'react';
-import { Link } from 'react-router-dom';
+// import { Link } from 'react-router-dom';
 import './index.less';
 import { Icon, Checkbox } from 'antd';
 import Page from '@src/containers/Page';
@@ -27,9 +27,9 @@ const exportType = [
   { key: 'question', title: '题目' },
   { key: 'official', title: '官方解析' },
   { key: 'note', title: '我的笔记' },
-  { key: 'qx', title: '千行解析', auth: true },
-  { key: 'association', title: '题源联想', auth: true },
-  { key: 'qa', title: '相关问答', auth: true },
+  // { key: 'qx', title: '千行解析', auth: true },
+  // { key: 'association', title: '题源联想', auth: true },
+  // { key: 'qa', title: '相关问答', auth: true },
 ];
 
 export default class extends Page {
@@ -67,13 +67,13 @@ export default class extends Page {
         width: 100,
         fixSort: true,
         render: (text, record) => {
-          return <Link to={`/paper/question/${record.id}`}>{text}</Link>;
+          return <a href={`/paper/question/${record.id}`} target="_blank">{text}</a>;
         },
       },
       {
         key: 'description',
         title: '内容',
-        width: 220,
+        width: 185,
         render: (text) => {
           return <div style={{ maxHeight: '80px', overflow: 'hidden' }}>{text}</div>;
         },
@@ -81,13 +81,13 @@ export default class extends Page {
       {
         key: 'time',
         title: '耗时',
-        width: 80,
+        width: 100,
         sort: true,
         render: (text, record) => {
           const user = record.stat ? record.stat.userTime / record.stat.userNumber : 0;
           const all = record.questionNo.totalNumber ? record.questionNo.totalTime / record.questionNo.totalNumber : 0;
           return <div className="sub">
-            <div className="t-2 t-s-12">{user > 0 ? formatSeconds(user) : '-'}{user > 0 && <Assets height={10} width={10} name={user > all ? 'up' : 'down'} />}</div>
+            <div className="t-2 t-s-12">{user > 0 ? formatSeconds(user) : '-'}{user > 0 && <Assets height={10} width={10} name={user > all ? 'down' : 'up'} />}</div>
             <div className="t-6 t-s-12">全站{all > 0 ? formatSeconds(all) : '-'}</div>
           </div>;
         },
@@ -107,7 +107,8 @@ export default class extends Page {
       {
         key: 'latest_time',
         title: '最近做题',
-        width: 95,
+        width: 105,
+        sort: true,
         render: (text) => {
           return <div className="sub">
             <div className="t-2 t-s-12">{text.split(' ')[0]}</div>
@@ -127,6 +128,7 @@ export default class extends Page {
 
   initData() {
     const data = Object.assign(this.state, this.state.search);
+    console.log(data);
     data.filterMap = this.state.search;
     if (data.order) {
       data.sortMap = { [data.order]: data.direction };
@@ -175,14 +177,30 @@ export default class extends Page {
             row.latest_time = row.latestTime ? formatDate(row.latestTime, 'YYYY-MM-DD HH:mm:ss') : '';
             return row;
           });
-          this.setState({ list: result.list, total: result.total });
+          const { selectList, allChecked, selectPage } = this.updateSelectInfo(data.page, result.list);
+          this.setState({ list: result.list, total: result.total, selectList, allChecked, selectPage });
         });
       });
     });
   }
 
+  updateSelectInfo(page, list, selectList = null) {
+    const { selectPage = {} } = this.state;
+    let allChecked = false;
+    if (selectList == null) {
+      selectList = [];
+      if (selectPage[Number(page)]) {
+        selectList = selectPage[Number(page)];
+      }
+    } else {
+      selectPage[Number(page)] = selectList;
+    }
+    allChecked = list.filter(row => selectList.indexOf(row.key) >= 0).length === list.length && list.length > 0;
+    return { selectList, allChecked, selectPage };
+  }
+
   toggleCollect(index) {
-    const { list } = this.props;
+    const { list } = this.state;
     const userQuestion = list[index];
     if (!userQuestion.collect) {
       My.addQuestionCollect(userQuestion.questionNo.id).then(() => {
@@ -198,7 +216,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)
@@ -237,30 +255,33 @@ export default class extends Page {
   }
 
   onAll(checked) {
-    const { selectList } = this.state;
+    const { selectPage = {}, search = {} } = this.state;
+    let { selectList } = this.state;
+    const { page } = search;
     const { list = [] } = this.state;
     if (checked) {
       list.forEach(item => {
         if (selectList.indexOf(item.key) >= 0) return;
         selectList.push(item.key);
       });
+      selectPage[Number(page)] = selectList;
     } else {
-      list.forEach(item => {
-        const index = selectList.indexOf(item.key);
-        if (index < 0) return;
-        selectList.splice(index, 1);
-      });
+      selectList = [];
+      delete selectPage[Number(page)];
     }
-    this.setState({ selectList, allChecked: checked });
+    this.setState({ selectList, selectPage, allChecked: checked });
   }
 
-  onSelect(selectList) {
-    this.setState({ selectList, allCheckbox: false });
+  onSelect(slist) {
+    const { search = {}, list } = this.state;
+    const { selectList, allChecked, selectPage } = this.updateSelectInfo(search.page, list, slist);
+    this.setState({ selectList, allChecked, selectPage });
   }
 
   onAction(key) {
     const { info } = this.props.user;
-    const { selectList } = this.state;
+    const { selectPage = {} } = this.state;
+    const selectList = [].concat(...Object.values(selectPage).concat());
     switch (key) {
       case 'help':
         this.setState({ showTips: true });
@@ -304,11 +325,11 @@ export default class extends Page {
           this.setState({ showWarn: true, warn: { title: '导出', content: '不可多余100题,请重新选择' } });
           return;
         }
-        if (info.bindReal) {
-          this.setState({ showExportConfirm: true, exportInfo: { include: exportType.map(row => row.key), questionNoIds: selectList, answer: true } });
-        } else {
-          this.setState({ showExportAuthConfirm: true, exportInfo: { include: exportType.filter(row => !row.auth).map(row => row.key), questionNoIds: selectList, answer: true } });
-        }
+        // if (info.bindReal) {
+        this.setState({ showExportConfirm: true, exportInfo: { include: exportType.map(row => row.key), questionNoIds: selectList, answer: true } });
+        // } else {
+        //   this.setState({ showExportAuthConfirm: true, exportInfo: { include: exportType.filter(row => !row.auth).map(row => row.key), questionNoIds: selectList, answer: true } });
+        // }
         break;
       default:
     }
@@ -318,6 +339,7 @@ export default class extends Page {
     const { clearInfo } = this.state;
     My.clearError(clearInfo.questionNoIds)
       .then(() => {
+        this.setState({ showClearConfirm: false, selectList: [], selectPage: {}, allChecked: false });
         this.refresh();
       })
       .catch(e => {
@@ -327,10 +349,10 @@ export default class extends Page {
 
   group() {
     const { groupInfo } = this.state;
-    My.groupQuestionCollect(groupInfo)
+    My.groupError(groupInfo)
       .then((result) => {
         Question.startLink('error', result);
-        this.setState({ showGroupConfirm: false });
+        this.setState({ showGroupConfirm: false, selectList: [], selectPage: {}, allChecked: false });
       })
       .catch(e => {
         this.setState({ showWarn: true, warn: { title: '组卷练习', content: e.message }, showGroupConfirm: false });
@@ -343,7 +365,7 @@ export default class extends Page {
     My.exportQuestionError(exportInfo)
       .then((result) => {
         openLink(`/export/${result}`);
-        this.setState({ showExportWait: false });
+        this.setState({ showExportWait: false, selectList: [], selectPage: {}, allChecked: false });
       })
       .catch(e => {
         this.setState({ showWarn: true, warn: { title: '导出', content: e.message }, showExportWait: false });
@@ -472,7 +494,7 @@ export default class extends Page {
           sortMap={sortMap}
           data={list}
           current={page}
-          total={total}
+          total={Number(total)}
           pageSize={this.state.search.size}
           selectList={selectList}
           onSelect={l => this.onSelect(l)}

+ 19 - 6
front/project/www/routes/my/index.js

@@ -17,9 +17,9 @@ import { Main } from '../../stores/main';
 export default [main, course, tools, error, note, answer, report, data, collect, order, message];
 
 
-export function refreshQuestionType(compontent, subject, questionType, { all, needSentence, allSubject }) {
+export function refreshQuestionType(compontent, subject, questionType, { all, needSentence, allSubject, onlyPreview }) {
   return Main.getExercise().then(result => {
-    const list = result.filter(row => (needSentence ? true : row.isExamination)).map(row => {
+    const list = result.filter(row => (needSentence ? true : row.isExamination)).filter(row => (onlyPreview ? row.isData || ['ds', 'ps'].indexOf(row.extend) >= 0 : true)).map(row => {
       row.title = `${row.titleZh}${row.titleEn}`;
       row.key = row.extend;
       return row;
@@ -29,10 +29,17 @@ export function refreshQuestionType(compontent, subject, questionType, { all, ne
     compontent.questionSubjectSelect = tree.filter(row => row.level === 1 && (allSubject ? true : row.children.length > 1));
     if (all) {
       compontent.questionSubjectSelect.forEach(row => {
-        row.children.unshift({
-          title: '全部',
-          key: '',
-        });
+        if (row.children.length > 0) {
+          row.children.unshift({
+            title: '全部',
+            key: '',
+          });
+        } else {
+          row.children.unshift({
+            title: row.title,
+            key: row.key,
+          });
+        }
       });
       compontent.questionSubjectSelect.unshift({
         title: '全部',
@@ -79,6 +86,11 @@ export function refreshStruct(compontent, questionTypes, module, one, two, { all
         let list = Object.values(map);
         if (questionTypes && questionTypes.length > 0) {
           list = list.filter(row => row.questionTypes.filter((v) => questionTypes.indexOf(v) >= 0).length);
+          // 预习作业过滤
+          if (needPreview) {
+            const previewList = result.filter(row => row.level === 2).filter(row => questionTypes.indexOf(row.extend) >= 0).filter(row => row.isData || ['ds', 'ps'].indexOf(row.extend) >= 0);
+            if (previewList.length === 0) needPreview = false;
+          }
         }
         if (needPreview) {
           list.push({
@@ -87,6 +99,7 @@ export function refreshStruct(compontent, questionTypes, module, one, two, { all
             id: 'preview',
           });
           CourseModule.forEach(row => {
+            if (!row.hasPreview) return;
             list.push({
               title: row.label,
               key: row.value,

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

@@ -425,10 +425,10 @@ export default class extends Page {
               }}
             />
             <span>
-              同比上周<b>{diffLastPercent}</b>% <Assets name={diffLast > 0 ? 'up' : 'down'} />
+              同比上周<b>{diffLastPercent}</b>% <Assets name={diffLast > 0 ? 'down' : 'up'} />
             </span>
             <span>
-              同比全站<b>{diffPercent}</b>% <Assets name={diff > 0 ? 'up' : 'down'} />
+              同比全站<b>{diffPercent}</b>% <Assets name={diff > 0 ? 'down' : 'up'} />
             </span>
           </div>
         </div>
@@ -598,7 +598,7 @@ export default class extends Page {
               }}
             />
           </div>
-          {!info.bindReal && <div className="t-3 t-s-12 m-t-1">完成实名认证送6个月VIP <a onClick={() => this.setState({ showReal: true })}>去完成</a></div>}
+          {!info.bindReal && <div className="t-3 t-s-12 m-t-1">完成实名认证送30天VIP <a onClick={() => this.setState({ showReal: true })}>去完成</a></div>}
         </div>
         {info.vip &&
           <div className="footer">

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

@@ -1,6 +1,6 @@
 import React from 'react';
-import { Link } from 'react-router-dom';
-import { Icon } from 'antd';
+// import { Link } from 'react-router-dom';
+import { Icon, Checkbox } from 'antd';
 import './index.less';
 import Page from '@src/containers/Page';
 import { timeRange, getMap, formatDate } from '@src/services/Tools';
@@ -21,13 +21,22 @@ import { OpenText } from '../../../components/Open';
 const QuestionTypeMap = getMap(QuestionType, 'value', 'label');
 const AskTargetMap = getMap(AskTarget, 'value', 'label');
 
+const exportType = [
+  { key: 'question', title: '题目' },
+  { key: 'official', title: '官方解析' },
+  { key: 'note', title: '我的笔记' },
+  // { key: 'qx', title: '千行解析', auth: true },
+  // { key: 'association', title: '题源联想', auth: true },
+  // { key: 'qa', title: '相关问答', auth: true },
+];
+
 const questionColumns = [
   {
     key: 'questionType',
     width: 140,
     render(text, row) {
       return <div className="group">
-        <Link to={row.userQuestionId ? `/paper/question/${row.userQuestionId}` : `/question/detail/${row.questionNoId}`}>{QuestionTypeMap[text]}</Link>
+        <a href={row.userQuestionId ? `/paper/question/${row.userQuestionId}` : `/question/detail/${row.questionNoId}`} target="_blank">{QuestionTypeMap[text]}</a>
       </div>;
     },
   },
@@ -36,15 +45,15 @@ const questionColumns = [
     width: 100,
     render(text, row) {
       return <div className="group">
-        <Link to={row.userQuestionId ? `/paper/question/${row.userQuestionId}` : `/question/detail/${row.questionNoId}`}>{text}</Link>
+        <a href={row.userQuestionId ? `/paper/question/${row.userQuestionId}` : `/question/detail/${row.questionNoId}`} target="_blank">{text}</a>
       </div>;
     },
   },
   {
     key: 'content',
     width: 540,
-    render(text, row) {
-      return <div className="group text-hidden"><Link to={row.userQuestionId ? `/paper/question/${row.userQuestionId}` : `/question/detail/${row.questionNoId}`}>{text}</Link></div>;
+    render(text) {
+      return <div className="group text-hidden t-16" style={{ width: '540px' }}>{text}</div>;
     },
   },
 ];
@@ -53,7 +62,7 @@ const contentColumns = [
   {
     key: 'title',
     title: '笔记对象',
-    width: 140,
+    width: 120,
     render(text) {
       return <div className="sub">{AskTargetMap[text]}</div>;
     },
@@ -61,7 +70,7 @@ const contentColumns = [
   {
     key: 'updateTime',
     title: '更新时间',
-    width: 100,
+    width: 105,
     render(text) {
       return <div className="sub">
         <div className="t-2 t-s-12">{text.split(' ')[0]}</div>
@@ -138,7 +147,7 @@ export default class extends Page {
             row.title = row.questionNo.title;
             row.content = row.question.description;
             row.list = [];
-            AskTarget.forEach((r) => {
+            [{ label: '题目', value: 'question', title: '题目', key: 'question' }].forEach((r) => {
               if (!row[`${r.value}Content`]) return;
               row.list.push({
                 title: r.value,
@@ -149,12 +158,28 @@ export default class extends Page {
             });
             return row;
           });
-          this.setState({ list: result.list, total: result.total, page: data.page });
+          const { selectList, allChecked, selectPage } = this.updateSelectInfo(data.page, result.list);
+          this.setState({ list: result.list, total: result.total, selectList, allChecked, selectPage });
         });
       });
     });
   }
 
+  updateSelectInfo(page, list, selectList = null) {
+    const { selectPage = {} } = this.state;
+    let allChecked = false;
+    if (selectList == null) {
+      selectList = [];
+      if (selectPage[Number(page)]) {
+        selectList = selectPage[Number(page)];
+      }
+    } else {
+      selectPage[Number(page)] = selectList;
+    }
+    allChecked = list.filter(row => selectList.indexOf(row.key) >= 0).length === list.length && list.length > 0;
+    return { selectList, allChecked, selectPage };
+  }
+
   onTabChange(tab) {
     const data = { tab };
     this.refreshQuery(data);
@@ -176,6 +201,7 @@ export default class extends Page {
     const { sortMap } = this.state;
     const index = keys.length > 1 && sortMap[keys[0]] ? 1 : 0;
     this.search({ order: keys.length && value[keys[index]] ? keys[index] : null, direction: keys.length ? value[keys[index]] : null }, false);
+    this.initData();
   }
 
   onChangePage(page) {
@@ -184,67 +210,85 @@ export default class extends Page {
   }
 
   onAll(checked) {
-    const { selectList, contentSelectList } = this.state;
+    const { selectPage = {}, search = {} } = this.state;
+    let { selectList } = this.state;
+    const { page } = search;
     const { list = [] } = this.state;
     if (checked) {
       list.forEach(item => {
-        if (selectList.indexOf(item.key) < 0) {
-          selectList.push(item.key);
-        }
-
-        AskTarget.forEach((r) => {
-          if (!item[`${r.value}Content`]) return;
-          if (contentSelectList.indexOf(`${item.key}|${r.value}`) < 0) {
-            contentSelectList.push(`${item.key}|${r.value}`);
-          }
-        });
+        if (selectList.indexOf(item.key) >= 0) return;
+        selectList.push(item.key);
       });
+      selectPage[Number(page)] = selectList;
     } else {
-      list.forEach(item => {
-        const index = selectList.indexOf(item.key);
-        if (index >= 0) {
-          selectList.splice(index, 1);
-        }
-
-        AskTarget.forEach((r) => {
-          if (!item[`${r.value}Content`]) return;
-          const i = contentSelectList.indexOf(`${item.key}|${r.value}`);
-          if (i >= 0) {
-            contentSelectList.splice(i, 1);
-          }
-        });
-      });
+      selectList = [];
+      delete selectPage[Number(page)];
     }
-    this.setState({ selectList, contentSelectList, allChecked: checked });
+    this.setState({ selectList, selectPage, allChecked: checked });
+    // const { selectList, contentSelectList } = this.state;
+    // const { list = [] } = this.state;
+    // if (checked) {
+    //   list.forEach(item => {
+    //     if (selectList.indexOf(item.key) < 0) {
+    //       selectList.push(item.key);
+    //     }
+
+    //     AskTarget.forEach((r) => {
+    //       if (!item[`${r.value}Content`]) return;
+    //       if (contentSelectList.indexOf(`${item.key}|${r.value}`) < 0) {
+    //         contentSelectList.push(`${item.key}|${r.value}`);
+    //       }
+    //     });
+    //   });
+    // } else {
+    //   list.forEach(item => {
+    //     const index = selectList.indexOf(item.key);
+    //     if (index >= 0) {
+    //       selectList.splice(index, 1);
+    //     }
+
+    //     AskTarget.forEach((r) => {
+    //       if (!item[`${r.value}Content`]) return;
+    //       const i = contentSelectList.indexOf(`${item.key}|${r.value}`);
+    //       if (i >= 0) {
+    //         contentSelectList.splice(i, 1);
+    //       }
+    //     });
+    //   });
+    // }
+    // this.setState({ selectList, contentSelectList, allChecked: checked });
   }
 
-  onSelect(selectList, key, checked) {
-    const { contentSelectList, list = [] } = this.state;
-    if (checked) {
-      const [item] = list.filter(row => row.key === key);
-      if (item) {
-        // 选中下面所有
-        AskTarget.forEach((r) => {
-          if (!item[`${r.value}Content`]) return;
-          if (contentSelectList.indexOf(`${item.key}|${r.value}`) < 0) {
-            contentSelectList.push(`${item.key}|${r.value}`);
-          }
-        });
-      }
-    } else {
-      const [item] = list.filter(row => row.key === key);
-      if (item) {
-        // 取消下面所有
-        AskTarget.forEach((r) => {
-          if (!item[`${r.value}Content`]) return;
-          const index = contentSelectList.indexOf(`${item.key}|${r.value}`);
-          if (index >= 0) {
-            contentSelectList.splice(index, 1);
-          }
-        });
-      }
-    }
-    this.setState({ selectList, contentSelectList, allCheckbox: false });
+  onSelect(slist) {
+    const { search = {}, list } = this.state;
+    const { selectList, allChecked, selectPage } = this.updateSelectInfo(search.page, list, slist);
+    this.setState({ selectList, allChecked, selectPage });
+    // const { contentSelectList, list = [] } = this.state;
+    // if (checked) {
+    //   const [item] = list.filter(row => row.key === key);
+    //   if (item) {
+    //     // 选中下面所有
+    //     AskTarget.forEach((r) => {
+    //       if (!item[`${r.value}Content`]) return;
+    //       if (contentSelectList.indexOf(`${item.key}|${r.value}`) < 0) {
+    //         contentSelectList.push(`${item.key}|${r.value}`);
+    //       }
+    //     });
+    //   }
+    // } else {
+    //   const [item] = list.filter(row => row.key === key);
+    //   if (item) {
+    //     // 取消下面所有
+    //     AskTarget.forEach((r) => {
+    //       if (!item[`${r.value}Content`]) return;
+    //       const index = contentSelectList.indexOf(`${item.key}|${r.value}`);
+    //       if (index >= 0) {
+    //         contentSelectList.splice(index, 1);
+    //       }
+    //     });
+    //   }
+    // }
+    // this.setState({ selectList, contentSelectList, allCheckbox: false });
   }
 
   onSelectContent(contentSelectList, key, checked) {
@@ -267,7 +311,7 @@ export default class extends Page {
     const { info } = this.props.user;
     const { selectList, contentSelectList } = this.state;
     const questionNoMap = {};
-    let questionNoIds;
+    // let questionNoIds;
     switch (key) {
       case 'help':
         this.setState({ showTips: true });
@@ -286,20 +330,7 @@ export default class extends Page {
           questionNoMap[questionNoId][target] = '';
           questionNoMap[questionNoId].fields += 1;
         });
-        questionNoIds = Object.keys(questionNoMap).filter(row => questionNoMap[row].fields < AskTarget.length);
-        if (questionNoIds.length > 0) {
-          Promise.all(questionNoIds.map(row => {
-            return My.updateQuestionNote(row, questionNoMap);
-          }))
-            .then(() => {
-              const clearList = Object.keys(questionNoMap).filter(row => questionNoMap[row].fields === AskTarget.length);
-              if (clearList.length > 0) {
-                this.clearNote(clearList);
-              } else {
-                this.refresh();
-              }
-            });
-        }
+        this.clearNote(Object.keys(questionNoMap));
         // this.setState({ showClearConfirm: true, clearInfo: { questionNoIds: selectList } });
         break;
       case 'export':
@@ -315,17 +346,23 @@ export default class extends Page {
           this.setState({ showWarn: true, warn: { title: '导出', content: '不可多余100题,请重新选择' } });
           return;
         }
-        contentSelectList.forEach(row => {
-          const [questionNoIdStr, target] = row.split('|');
-          const questionNoId = Number(questionNoIdStr);
-          if (!questionNoMap[questionNoId]) {
-            questionNoMap[questionNoId] = { fields: 0 };
-          }
-          questionNoMap[questionNoId][target] = '';
-          questionNoMap[questionNoId].fields += 1;
-        });
-        questionNoIds = Object.keys(questionNoMap);
-        this.setState({ showExportConfirm: true, exportInfo: { questionNoIds, questionNoMap } });
+        // if (info.bindReal) {
+        this.setState({ showExportConfirm: true, exportInfo: { include: exportType.map(row => row.key), questionNoIds: selectList, answer: true } });
+        // } else {
+        //   this.setState({ showExportAuthConfirm: true, exportInfo: { include: exportType.filter(row => !row.auth).map(row => row.key), questionNoIds: selectList, answer: true } });
+        // }
+
+        // contentSelectList.forEach(row => {
+        //   const [questionNoIdStr, target] = row.split('|');
+        //   const questionNoId = Number(questionNoIdStr);
+        //   if (!questionNoMap[questionNoId]) {
+        //     questionNoMap[questionNoId] = { fields: 0 };
+        //   }
+        //   questionNoMap[questionNoId][target] = '';
+        //   questionNoMap[questionNoId].fields += 1;
+        // });
+        // questionNoIds = Object.keys(questionNoMap);
+        // this.setState({ showExportConfirm: true, exportInfo: { questionNoIds, questionNoMap } });
         break;
       default:
     }
@@ -461,7 +498,7 @@ export default class extends Page {
               <UserTable
                 border={false}
                 size="small"
-                select
+                // select
                 even="default"
                 selectList={contentSelectList}
                 columns={contentColumns}
@@ -481,17 +518,12 @@ export default class extends Page {
   }
 
   renderModal() {
-    const { showTips, showWarn, warn = {}, showClearConfirm, clearInfo = {}, showVip, showExamination, showReal, showExportWait, showExportConfirm, exportInfo = {} } = this.state;
+    const { showTips, showWarn, warn = {}, showClearConfirm, clearInfo = {}, showVip, showExamination, showReal, showExportWait, showExportConfirm, showExportAuthConfirm, exportInfo = {} } = this.state;
     const { info } = this.props.user;
     return [
       <Modal show={showTips} title="操作提示" confirmText="好的,知道了" btnAlign="center" onConfirm={() => this.setState({ showTips: false })}>
         <div className="flex-layout m-b-2">
           <div className="flex-block">
-            <div className="t-1 t-s-18">组卷功能</div>
-            <div className="t-2">操作数量:10-50;</div>
-            <div className="t-2">注意事项:可跨题型、不可跨学科。</div>
-          </div>
-          <div className="flex-block">
             <div className="t-1 t-s-18">导出功能</div>
             <div className="t-2">操作数量:1-100;</div>
             <div className="t-2">注意事项:“综合推理IR”暂时无法导出。</div>
@@ -514,7 +546,56 @@ export default class extends Page {
       </Modal>,
       <Modal show={showExportConfirm} title="导出" confirmText="导出" onConfirm={() => this.export()} onCancel={() => this.setState({ showExportConfirm: false })}>
         <div className="t-2 t-s-18 m-b-5">
-          当前共选中 <span className="t-4">{exportInfo.questionNoIds ? exportInfo.questionNoIds.length : 0}</span> 道题笔记,是否开始导出:
+          当前共选中 <span className="t-4">{exportInfo.questionNoIds ? exportInfo.questionNoIds.length : 0}</span> 道题,请确认需要导出的内容:
+      </div>
+        <div className="t-2 t-s-16">
+          {exportType.map(item => {
+            return (
+              <div className="d-i-b m-b-5" style={{ width: 135 }}>
+                <Checkbox checked={exportInfo.include ? exportInfo.include.indexOf(item.key) >= 0 : false} className="m-r-5" onChange={() => {
+                  exportInfo.include.push(item.key);
+                  this.setState({ exportInfo });
+                }} />
+                {item.title}
+              </div>
+            );
+          })}
+        </div>
+      </Modal>,
+      <Modal show={showExportAuthConfirm} title="导出" confirmText="导出" onConfirm={() => this.export()} onCancel={() => this.setState({ showExportAuthConfirm: false })}>
+        <div className="t-2 t-s-18 m-b-5">
+          当前共选中 <span className="t-4">{exportInfo.questionNoIds ? exportInfo.questionNoIds.length : 0}</span> 道题,请确认需要导出的内容:
+      </div>
+        <div className="t-2 t-s-16 m-b-2">
+          {exportType
+            .filter(item => !item.auth)
+            .map(item => {
+              return (
+                <div className="d-i-b m-b-2" style={{ width: 135 }}>
+                  <Checkbox checked={exportInfo.include ? exportInfo.include.indexOf(item.key) >= 0 : false} className="m-r-5" onChange={() => {
+                    exportInfo.include.push(item.key);
+                    this.setState({ exportInfo });
+                  }} />
+                  {item.title}
+                </div>
+              );
+            })}
+        </div>
+        <div className="b-b m-b-2 m-t-2" />
+        <div className="t-3 m-b-1">
+          以下内容需实名认证后才可导出: <a className="f-r" onClick={() => this.setState({ showExportAuthConfirm: false, showReal: true })}>去认证 ></a>
+        </div>
+        <div className="t-2 t-s-16 m-b-2">
+          {exportType
+            .filter(item => item.auth)
+            .map(item => {
+              return (
+                <div className="d-i-b" style={{ width: 135 }}>
+                  <Checkbox disabled className="m-r-5" />
+                  {item.title}
+                </div>
+              );
+            })}
         </div>
       </Modal>,
       <Modal show={showExportWait} title="导出" confirmText="好的,知道了" btnAlign="center" onConfirm={() => this.setState({ showExportWait: false })}>

+ 36 - 35
front/project/www/routes/my/report/page.js

@@ -105,17 +105,17 @@ export default class extends Page {
         key: 'title',
         title: '练习册名称',
         fixSort: true,
-        // render: (text, record) => {
-        //   const { reports } = record;
-        //   return reports.map((report, index) => {
-        //     return <div className="sub">{index === 0 && text}</div>;
-        //   });
-        // },
+        render: (text, record) => {
+          if (text) {
+            return `${QuestionnTypeMap[record.questionTypes[0]]}-${text}`;
+          }
+          return '';
+        },
       },
       {
         key: 'latest_time',
         title: '做题时间',
-        fixSort: true,
+        sort: true,
         render: (text, record, index, childIndex) => {
           const { reports, show } = record;
           const report = childIndex === -1 ? reports[0] : record;
@@ -167,7 +167,7 @@ export default class extends Page {
           const user = formatPercent(report.userCorrect, report.userNumber);
           const all = formatPercent(record.stat.totalCorrect, record.stat.totalNumber);
           return <div className="">
-            <div className="t-2 t-s-12">{user}%<Assets height={10} width={10} name={user > all ? 'up' : 'down'} /></div>
+            <div className="t-2 t-s-12">{user}%<Assets height={10} width={10} name={user > all ? 'down' : 'up'} /></div>
             <div className="t-6 t-s-12">全站{all}%</div>
           </div>;
         },
@@ -182,7 +182,7 @@ export default class extends Page {
           const user = report.userTime / report.userNumber;
           const all = record.stat.totalTime / record.stat.totalNumber;
           return <div className="">
-            <div className="t-2 t-s-12">{formatSeconds(user)}<Assets height={10} width={10} name={user > all ? 'up' : 'down'} /></div>
+            <div className="t-2 t-s-12">{formatSeconds(user)}<Assets height={10} width={10} name={user > all ? 'down' : 'up'} /></div>
             <div className="t-6 t-s-12">全站{formatSeconds(all)}</div>
           </div>;
         },
@@ -212,7 +212,7 @@ export default class extends Page {
                 type="report"
                 tip="report"
                 onClick={() => {
-                  Question.reportLink({ report });
+                  Question.reportLink({ report }, true);
                 }}
               />
             </div>
@@ -226,7 +226,7 @@ export default class extends Page {
       {
         key: 'latest_time',
         title: '做题时间',
-        fixSort: true,
+        sort: true,
         render: (text, record) => {
           const { reports } = record;
           if (!reports) return null;
@@ -347,7 +347,7 @@ export default class extends Page {
               type="report"
               tip="report"
               onClick={() => {
-                Question.reportLink({ report });
+                Question.reportLink({ report }, true);
               }}
             />
           );
@@ -365,9 +365,16 @@ export default class extends Page {
         },
       },
       {
+        key: 'questionType',
+        title: '题型',
+        render: (text, record) => {
+          return record.questionTypes.map(row => <p>{QuestionnTypeMap[row]}</p>);
+        },
+      },
+      {
         key: 'latest_time',
         title: '做题时间',
-        fixSort: true,
+        sort: true,
         render: (text, record) => {
           const { reports } = record;
           if (!reports) return null;
@@ -383,15 +390,9 @@ export default class extends Page {
         },
       },
       {
-        key: 'questionType',
-        title: '题型',
-        render: (text, record) => {
-          return record.questionTypes.map(row => <p>{QuestionnTypeMap[row]}</p>);
-        },
-      },
-      {
-        key: 'progress',
-        title: '完成度',
+        key: 'correct',
+        title: '正确率',
+        sort: true,
         render: (text, record) => {
           const { reports } = record;
           if (!reports) return null;
@@ -399,17 +400,14 @@ export default class extends Page {
           if (!report) return null;
           return (
             <div className="sub">
-              <div className="t-2 t-s-12">{formatPercent(report.userNumber, report.questionNumber, false)}</div>
-              <div className="t-6 t-s-12">
-                {report.userNumber}/{report.questionNumber}
-              </div>
+              <div className="t-2 t-s-12">{formatPercent(report.userCorrect, report.userNumber, false)}</div>
             </div>
           );
         },
       },
       {
-        key: 'correct',
-        title: '正确率',
+        key: 'time',
+        title: '平均耗时',
         sort: true,
         render: (text, record) => {
           const { reports } = record;
@@ -418,15 +416,14 @@ export default class extends Page {
           if (!report) return null;
           return (
             <div className="sub">
-              <div className="t-2 t-s-12">{formatPercent(report.userCorrect, report.userNumber, false)}</div>
+              <div className="t-2 t-s-12">{formatSeconds(report.userTime / report.userNumber)}</div>
             </div>
           );
         },
       },
       {
-        key: 'time',
-        title: '平均耗时',
-        sort: true,
+        key: 'progress',
+        title: '完成度',
         render: (text, record) => {
           const { reports } = record;
           if (!reports) return null;
@@ -434,7 +431,10 @@ export default class extends Page {
           if (!report) return null;
           return (
             <div className="sub">
-              <div className="t-2 t-s-12">{formatSeconds(report.userTime / report.userNumber)}</div>
+              <div className="t-2 t-s-12">{formatPercent(report.userNumber, report.questionNumber, false)}</div>
+              <div className="t-6 t-s-12">
+                {report.userNumber}/{report.questionNumber}
+              </div>
             </div>
           );
         },
@@ -453,7 +453,7 @@ export default class extends Page {
                 type="report"
                 tip="report"
                 onClick={() => {
-                  Question.reportLink({ report });
+                  Question.reportLink({ report }, true);
                 }}
               />
             </div>
@@ -503,6 +503,7 @@ export default class extends Page {
           all: true,
           needSentence: true,
           allSubject: true,
+          onlyPreview: data.one === 'preview',
         }).then(({ questionTypes }) => {
           return refreshStruct(this, questionTypes, data.tab, data.one, data.two, {
             all: true,
@@ -666,7 +667,7 @@ export default class extends Page {
           tabs={[
             { key: 'exercise', title: '练习' },
             { key: 'examination', title: '模考' },
-            { key: 'error', title: '错组卷' },
+            { key: 'error', title: '错组卷' },
             { key: 'collect', title: '收藏组卷' },
           ]}
           onChange={key => this.onTabChange(key)}

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

@@ -146,7 +146,7 @@ export default class extends Page {
     Main.getService('qx_cat').then(result => {
       this.setState({ service: result });
     });
-    Question.getExaminationInfo(result => {
+    Question.getExaminationInfo().then(result => {
       result.expireDay =
         result.expireTime && parseInt((new Date().getTime() - new Date(result.expireTime).getTime()) / 86400000, 10);
       this.setState({ data: result });
@@ -325,6 +325,7 @@ export default class extends Page {
 
   renderView() {
     const { config } = this.props;
+    console.log('view');
     return <UserLayout active={config.key} menu={menu} center={this.renderDetail()} />;
   }
 
@@ -348,6 +349,7 @@ export default class extends Page {
       showVip,
     } = this.state;
     const { info } = this.props.user;
+    console.log('detail');
     return (
       <div className="table-layout">
         <Tabs
@@ -666,7 +668,7 @@ export default class extends Page {
             <div className="t1">未购买</div>
             <div className="desc">¥ {service && service.package && service.package[0].price}</div>
             <Button radius size="lager" width={150} onClick={() => {
-              this.buyExamination();
+              this.buyQxCat();
             }}>
               立即购买
             </Button>
@@ -758,7 +760,7 @@ export default class extends Page {
     return (
       <div className="tab-5-layout">
         <TotalSort
-          value={data.value || 650}
+          value={data.value || 700}
           onChange={value => {
             data.value = value;
             this.setState({ data });

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

@@ -8,6 +8,7 @@
     padding-bottom: 20px;
   }
 
+
   .head-layout {
     padding: 30px 0;
     background: #F7F8FCFF;
@@ -116,4 +117,10 @@
       padding: 15px 20px;
     }
   }
+}
+
+@media print {
+  #export .content {
+    width: 600px !important;
+  }
 }

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

@@ -360,7 +360,7 @@ export default class extends Component {
 
   renderExerciseStart() {
     const { paper, flow, setting } = this.props;
-    const { disorder } = setting;
+    const { disorder = false } = setting;
     return (
       <div className="start">
         <div className="bg" />
@@ -383,11 +383,11 @@ export default class extends Component {
             </div>
           </div>
           {paper.finishTimes > 0 && <div className="tip">
-            <Checkbox className="m-r-1" checked={!disorder} onChange={() => flow.setSetting({ disorder: !!disorder })} />
+            <Checkbox className="m-r-1" checked={disorder} onChange={() => flow.setSetting({ disorder: !disorder })} />
             题目选项乱序显示
           </div>}
           <div className="submit">
-            <Button size="lager" radius onClick={() => flow.start({ disorder: paper.finishTimes > 0 ? !disorder : false })}>
+            <Button size="lager" radius onClick={() => flow.start({ disorder: paper.finishTimes > 0 ? disorder : false })}>
               开始练习
             </Button>
           </div>
@@ -448,14 +448,14 @@ export default class extends Component {
 
   renderExaminationStartDefault() {
     const { paper, flow, setting } = this.props;
-    const { disorder, order, orderIndex } = setting;
+    const { disorder = false, order = [], orderIndex } = setting;
     return (
       <div className="exercise-start cat">
         <div className="title">Section Ordering</div>
         <div className="block-list">
           {ExaminationOrder.map((row, index) => {
             return <div className="block-item" onClick={() => {
-              this.setState({ order: row.value, orderIndex: index });
+              flow.setSetting({ order: row.value, orderIndex: index });
             }}>
               <div className={orderIndex === index ? 'block-item-body active' : 'block-item-body'}>
                 {orderIndex === index && <AntDIcon type="check" style={{ color: '#fff' }} />}
@@ -479,11 +479,11 @@ export default class extends Component {
         </div>
         <div className="bottom">
           {paper.finishTimes > 0 && <div className="text">
-            <Checkbox checked={!disorder} onChange={() => flow.setSetting({ disorder: !!disorder })} /> 题目选项乱序显示
+            <Checkbox checked={disorder} onChange={() => flow.setSetting({ disorder: !disorder })} /> 题目选项乱序显示
           </div>}
           <div className="text">
             Click{' '}
-            <div className="next" onClick={() => flow.start({ disorder: paper.finishTimes > 0 ? !disorder : false, order: order.filter(row => row) })}>
+            <div className="next" onClick={() => flow.start({ disorder: paper.finishTimes > 0 ? disorder : false, order: order.filter(row => row) })}>
               Next
               <Assets name="next_icon" />
             </div>{' '}

+ 1 - 0
front/project/www/routes/paper/process/page.js

@@ -215,6 +215,7 @@ export default class extends Page {
       });
   }
 
+  // todo initTime
   singleQuestionTime(stop) {
     if (this.singleInterval) {
       clearInterval(this.singleInterval);

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

@@ -38,6 +38,20 @@ export default class extends Component {
     };
   }
 
+  note() {
+    const { userQuestion } = this.props;
+    const { questionNo } = userQuestion;
+    const { note } = this.state;
+    if (note) {
+      this.setState({ noteModal: true });
+      return;
+    }
+    My.getQuestionNote(questionNo.id)
+      .then(result => {
+        this.setState({ note: result || {}, noteModal: true });
+      });
+  }
+
   prevQuestion() {
     const { userQuestion, report } = this.props;
     if (userQuestion.no === 1) return;
@@ -67,25 +81,29 @@ export default class extends Component {
 
   submitAsk() {
     const { userQuestion, questionNo = {} } = this.props;
-    const { ask = {} } = this.state;
+    const { data = {} } = this.state;
+    const { ask = {} } = data;
     if (!ask.originContent || !ask.content || !ask.target) {
       this.setState({ empty: { ask: { originContent: !ask.originContent, content: !ask.content, target: !ask.target } } });
       return Promise.reject();
     }
+    data.ask = {};
     return My.addQuestionAsk(userQuestion.id, ask.target, questionNo.id, ask.originContent, ask.content).then(() => {
-      this.setState({ askModal: false, askOkModal: true, ask: {} });
+      this.setState({ askModal: false, askOkModal: true, data });
     }).catch(err => {
-      this.setState({ askError: err.message, ask: {} });
+      this.setState({ askError: err.message, data });
     });
   }
 
   submitFeedbackError() {
     const { questionNo = {} } = this.props;
-    const { feedback = {} } = this.state;
+    const { data = {} } = this.state;
+    const { feedback = {} } = data;
     if (!feedback.originContent || !feedback.content || !feedback.target) {
       this.setState({ empty: { feedback: { originContent: !feedback.originContent, content: !feedback.content, target: !feedback.target } } });
       return Promise.reject();
     }
+    data.feedback = {};
     return My.addFeedbackErrorQuestion(
       questionNo.id,
       questionNo.title,
@@ -94,10 +112,10 @@ export default class extends Component {
       feedback.content,
     )
       .then(() => {
-        this.setState({ feedbackModal: false, feedbackOkModal: true, feedback: {} });
+        this.setState({ feedbackModal: false, feedbackOkModal: true, data });
       })
       .catch(err => {
-        this.setState({ feedbackError: err.message, feedback: {} });
+        this.setState({ feedbackError: err.message, data });
       });
   }
 
@@ -358,7 +376,7 @@ export default class extends Component {
             </Tooltip>
           </div>
           <div className="center">
-            <AnswerButton className="item" onClick={() => User.needLogin().then(() => this.setState({ noteModal: true }))}>
+            <AnswerButton className="item" onClick={() => User.needLogin().then(() => this.note())}>
               笔记
             </AnswerButton>
             {questionStatus >= 0 && (
@@ -396,7 +414,7 @@ export default class extends Component {
         {this.state.feedbackModal && this.renderFeedbackError()}
         {this.state.feedbackOkModal && this.renderFeedbackErrorOk()}
         {/* {this.state.noteModal && this.renderNote()} */}
-        <QuestionNoteModal show={this.state.noteModal} defaultData={this.state.note} questionNo={questionNo} onConfirm={() => this.setState({ noteModal: false })} onCancel={() => this.setState({ noteModal: false })} />
+        <QuestionNoteModal show={this.state.noteModal} defaultData={this.state.note} questionNo={questionNo} onConfirm={(data) => this.setState({ noteModal: false, note: data })} onCancel={() => this.setState({ noteModal: false })} />
       </div>
     );
   }
@@ -647,7 +665,8 @@ export default class extends Component {
   }
 
   renderAsk() {
-    const { ask = {}, empty = {} } = this.state;
+    const { data = {}, empty = {} } = this.state;
+    const { ask = {} } = data;
     const emptyAsk = empty.ask || {};
     return (
       <div className="question-modal ask">
@@ -775,8 +794,12 @@ export default class extends Component {
   }
 
   renderFeedbackError() {
-    const { feedback = {}, empty = {} } = this.state;
+    const { data = {}, empty = {} } = this.state;
+    const { feedback = {} } = data;
     const emptyFeedback = empty.feedback || {};
+    if (!feedback.target) {
+      feedback.target = AskTarget[0].value;
+    }
     return (
       <div className="question-modal error">
         <div className="mask" />
@@ -795,7 +818,7 @@ export default class extends Component {
                   this.changeData('feedback', 'target', item.value);
                 }}
               />
-              进行提问
+              进行纠错
             </div>
             <div className="label">错误内容是:</div>
             <Textarea
@@ -922,18 +945,18 @@ export default class extends Component {
                 >
                   取消
                 </AnswerButton>
-                <AnswerButton
+                {/* <AnswerButton
                   size="lager"
                   onClick={() => {
                     this.submitNote();
                   }}
                 >
                   编辑
-                </AnswerButton>
+                </AnswerButton> */}
                 <AnswerButton
                   size="lager"
                   onClick={() => {
-                    this.submitNote(true);
+                    this.submitNote();
                   }}
                 >
                   保存

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

@@ -274,6 +274,10 @@
           display: flex;
           flex-direction: column;
 
+          .tabs {
+            overflow: visible;
+          }
+
           .block-answer {
             padding: 38px 50px;
           }

+ 1 - 0
front/project/www/routes/paper/report/index.less

@@ -218,6 +218,7 @@
   }
 }
 
+.exercise.question,
 .sentence.question {
   .header {
     text-align: center;

+ 6 - 6
front/project/www/routes/paper/report/page.js

@@ -1,5 +1,5 @@
 import React from 'react';
-import { Link } from 'react-router-dom';
+// import { Link } from 'react-router-dom';
 import './index.less';
 import LineChart from '@src/components/LineChart';
 import BarChart from '@src/components/BarChart';
@@ -7,7 +7,7 @@ import PieChart from '@src/components/PieChart';
 import Assets from '@src/components/Assets';
 import Page from '@src/containers/Page';
 import { formatDate, formatPercent, formatSeconds, formatMinute, formatSecond, formatMinuteSecond, getMap } from '@src/services/Tools';
-import { Icon, Tooltip } from 'antd';
+import { Icon, Tooltip, Typography } from 'antd';
 import { Question } from '../../../stores/question';
 import { Button } from '../../../components/Button';
 import Tabs from '../../../components/Tabs';
@@ -838,7 +838,7 @@ export default class extends Page {
                 return <tr>
                   <td>{row.no}</td>
                   <td>
-                    <div className='n'><Link to={`/paper/question/${row.id}`}>{(row.questionNo || {}).title}</Link></div>
+                    <div className='n'><a href={`/paper/question/${row.id}`} target="_blank">{(row.questionNo || {}).title}</a></div>
                     <div className='desc'>{row.question.description}</div>
                   </td>
                   <td><GIcon name={row.detail.subject && row.detail.predicate && row.detail.object ? 'right' : 'error'} noHover /></td>
@@ -920,7 +920,7 @@ export default class extends Page {
 
   renderExerciseQuestion() {
     const { report, list, order, showNote, note = {}, questionNo = {} } = this.state;
-    return <div className='sentence question'>
+    return <div className='exercise question'>
       <div className='header'>
         <div className='content'>
           <div className='title'>题目回顾</div>
@@ -962,8 +962,8 @@ export default class extends Page {
                 return <tr>
                   <td>{row.no}</td>
                   <td>
-                    <div className='n'><Link to={`/paper/question/${row.id}`}>{(row.questionNo || {}).title}</Link></div>
-                    <div className='desc'>{row.question.description}</div>
+                    <div className='n'><a href={`/paper/question/${row.id}`} target="_blank">{(row.questionNo || {}).title}</a></div>
+                    <div className='desc'><Typography.Paragraph ellipsis={{ rows: 3, expandable: false }}>{row.question.description}</Typography.Paragraph></div>
                   </td>
                   <td><GIcon name={row.isCorrect ? 'right' : 'error'} noHover /></td>
                   <td>{row.question.difficult}</td>

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

@@ -29,7 +29,7 @@ export default class extends Page {
           <div className="table-row">
             <div className="night f-s-16">{record.title}</div>
             <div>
-              <ProgressText progress={progress} times={record.paper ? record.paper.finishTimes : 0} size="small" />
+              <ProgressText progress={progress} times={record.paper ? record.paper.resetTimes : 0} size="small" />
             </div>
           </div>
         );

+ 3 - 3
front/project/www/stores/my.js

@@ -169,7 +169,7 @@ export default class MyStore extends BaseStore {
    * @param {*} ids: questionId
    */
   clearQuestionCollect(questionNoIds) {
-    return this.apiPost('/my/collect/question/clear', { questionNoIds });
+    return this.apiDel('/my/collect/question/clear', { questionNoIds });
   }
 
   /**
@@ -256,8 +256,8 @@ export default class MyStore extends BaseStore {
    * @param {*} associationContent
    * @param {*} qaContent
    */
-  updateQuestionNote(questionNoId, { content, qxContent, officialContent, associationContent, qaContent }) {
-    return this.apiPut('/my/note/question', { questionNoId, content, qxContent, officialContent, associationContent, qaContent });
+  updateQuestionNote(questionNoId, { content, questionContent, qxContent, officialContent, associationContent, qaContent }) {
+    return this.apiPut('/my/note/question', { questionNoId, content, questionContent, qxContent, officialContent, associationContent, qaContent });
   }
 
   clearQuestionNote(questionNoIds) {

+ 5 - 1
front/src/components/Editor/index.js

@@ -15,6 +15,7 @@ const defaultContainer = [
   [{ list: 'ordered' }, { list: 'bullet' }, { indent: '-1' }, { indent: '+1' }],
   // ['image'],
   // ['link', 'video'],
+  ['formula'],
   ['clean'],
 ];
 
@@ -33,6 +34,7 @@ const formats = [
   'indent',
   'link',
   'image',
+  'formula',
   // 'video',
 ];
 class ListAttributor extends Parchment.Attributor.Style {
@@ -67,6 +69,7 @@ class Editor extends React.Component {
     this.state = {};
     this.loading = 0;
     this.modules = {
+      formula: true,
       toolbar: {
         container: [
         ],
@@ -154,7 +157,7 @@ class Editor extends React.Component {
   render() {
     // console.log(this.props.value);
     return (
-      <div>
+      <div id="editor">
         <ReactQuill
           ref={el => {
             this.quillRef = el;
@@ -164,6 +167,7 @@ class Editor extends React.Component {
           onChange={(content, delta, source, editor) => {
             if (this.props.onChange) this.props.onChange(content, delta, source, editor);
           }}
+          bounds={'#editor'}
           defaultValue={this.props.defaultValue || ''}
           value={this.props.value || ''}
           modules={this.modules}

+ 117 - 111
front/src/index.html

@@ -1,117 +1,123 @@
 <!DOCTYPE html>
 <html lang="zh">
-  <head>
-    <title>
-      <%= htmlWebpackPlugin.options.title %>
-    </title>
-    <meta name="description" content="<%= htmlWebpackPlugin.options.globals.description %>" />
-    <meta name="keywords" content="<%= htmlWebpackPlugin.options.globals.keyword %>" />
-    <link rel="shortcut icon" href="/favicon.ico" />
-    <script src="/pace.min.js"></script>
-    <link href="/pace-theme-flash.css" rel="stylesheet" />
-    <meta charset="utf-8" />
-  </head>
-  <script>
-    !(function(e) {
-      function t(a) {
-        if (i[a]) return i[a].exports;
-        var n = (i[a] = {
-          exports: {},
-          id: a,
-          loaded: !1,
-        });
-        return e[a].call(n.exports, n, n.exports, t), (n.loaded = !0), n.exports;
-      }
-      var i = {};
-      return (t.m = e), (t.c = i), (t.p = ''), t(0);
-    })([
-      function(e, t) {
-        'use strict';
-        Object.defineProperty(t, '__esModule', {
-          value: !0,
-        });
-        var i = window;
-        (t['default'] = i.flex = function(e, t) {
-          var a = e || 100,
-            n = t || 1,
-            r = i.document,
-            o = navigator.userAgent,
-            d = o.match(/Android[\S\s]+AppleWebkit\/(\d{3})/i),
-            l = o.match(/U3\/((\d+|\.){5,})/i),
-            c = l && parseInt(l[1].split('.').join(''), 10) >= 80,
-            p = navigator.appVersion.match(/(iphone|ipad|ipod)/gi),
-            s = 1; //i.devicePixelRatio || 1;
-          p || (d && d[1] > 534) || c || (s = 1);
-          var u = 1 / s,
-            m = r.querySelector('meta[name="viewport"]');
-          m || ((m = r.createElement('meta')), m.setAttribute('name', 'viewport'), r.head.appendChild(m)),
-            m.setAttribute(
-              'content',
-              'width=device-width,user-scalable=no,initial-scale=1,maximum-scale=' + u + ',minimum-scale=' + u,
-            ),
-            (r.documentElement.style.fontSize = (a / 2) * s * n + 'px');
-        }),
-          (e.exports = t['default']);
-      },
-    ]);
+
+<head>
+  <title>
+    <%= htmlWebpackPlugin.options.title %>
+  </title>
+  <meta name="description" content="<%= htmlWebpackPlugin.options.globals.description %>" />
+  <meta name="keywords" content="<%= htmlWebpackPlugin.options.globals.keyword %>" />
+  <link rel="shortcut icon" href="/favicon.ico" />
+  <script src="/pace.min.js"></script>
+  <link href="/pace-theme-flash.css" rel="stylesheet" />
+
+  <% for (var i = 0; i < htmlWebpackPlugin.options.globals.css.length; i++) { %>
+  <link rel="stylesheet" href="<%= htmlWebpackPlugin.options.globals.css[i] %>" />
+  <% } %>
+  <meta charset="utf-8" />
+</head>
+<script>
+  !(function (e) {
+    function t(a) {
+      if (i[a]) return i[a].exports;
+      var n = (i[a] = {
+        exports: {},
+        id: a,
+        loaded: !1,
+      });
+      return e[a].call(n.exports, n, n.exports, t), (n.loaded = !0), n.exports;
+    }
+    var i = {};
+    return (t.m = e), (t.c = i), (t.p = ''), t(0);
+  })([
+    function (e, t) {
+      'use strict';
+      Object.defineProperty(t, '__esModule', {
+        value: !0,
+      });
+      var i = window;
+      (t['default'] = i.flex = function (e, t) {
+        var a = e || 100,
+          n = t || 1,
+          r = i.document,
+          o = navigator.userAgent,
+          d = o.match(/Android[\S\s]+AppleWebkit\/(\d{3})/i),
+          l = o.match(/U3\/((\d+|\.){5,})/i),
+          c = l && parseInt(l[1].split('.').join(''), 10) >= 80,
+          p = navigator.appVersion.match(/(iphone|ipad|ipod)/gi),
+          s = 1; //i.devicePixelRatio || 1;
+        p || (d && d[1] > 534) || c || (s = 1);
+        var u = 1 / s,
+          m = r.querySelector('meta[name="viewport"]');
+        m || ((m = r.createElement('meta')), m.setAttribute('name', 'viewport'), r.head.appendChild(m)),
+          m.setAttribute(
+            'content',
+            'width=device-width,user-scalable=no,initial-scale=1,maximum-scale=' + u + ',minimum-scale=' + u,
+          ),
+          (r.documentElement.style.fontSize = (a / 2) * s * n + 'px');
+      }),
+        (e.exports = t['default']);
+    },
+  ]);
+  flex(100, 1);
+  window.onresize = function () {
     flex(100, 1);
-    window.onresize = function() {
-      flex(100, 1);
-    };
-  </script>
+  };
+</script>
 
-  <body>
-    <div id="root"></div>
-    <script>
-      if (!Array.prototype.findIndex) {
-        Object.defineProperty(Array.prototype, 'findIndex', {
-          value: function(predicate) {
-            if (this === null) {
-              throw new TypeError('Array.prototype.find called on null or undefined');
-            }
-            if (typeof predicate !== 'function') {
-              throw new TypeError('predicate must be a function');
-            }
-            var list = Object(this);
-            var length = list.length >>> 0;
-            var thisArg = arguments[1];
-            var value;
+<body>
+  <div id="root"></div>
+  <script>
+    if (!Array.prototype.findIndex) {
+      Object.defineProperty(Array.prototype, 'findIndex', {
+        value: function (predicate) {
+          if (this === null) {
+            throw new TypeError('Array.prototype.find called on null or undefined');
+          }
+          if (typeof predicate !== 'function') {
+            throw new TypeError('predicate must be a function');
+          }
+          var list = Object(this);
+          var length = list.length >>> 0;
+          var thisArg = arguments[1];
+          var value;
 
-            for (var i = 0; i < length; i++) {
-              value = list[i];
-              if (predicate.call(thisArg, value, i, list)) {
-                return value;
-              }
-            }
-            return undefined;
-          },
-        });
-      }
-      if (!Array.prototype.fill) {
-        Object.defineProperty(Array.prototype, 'fill', {
-          value: function(value) {
-            if (this == null) {
-              throw new TypeError('this is null or not defined');
-            }
-            var O = Object(this);
-            var len = O.length >>> 0;
-            var start = arguments[1];
-            var relativeStart = start >> 0;
-            var k = relativeStart < 0 ? Math.max(len + relativeStart, 0) : Math.min(relativeStart, len);
-            var end = arguments[2];
-            var relativeEnd = end === undefined ? len : end >> 0;
-            var final = relativeEnd < 0 ? Math.max(len + relativeEnd, 0) : Math.min(relativeEnd, len);
-            while (k < final) {
-              O[k] = value;
-              k++;
+          for (var i = 0; i < length; i++) {
+            value = list[i];
+            if (predicate.call(thisArg, value, i, list)) {
+              return value;
             }
-            return O;
-          },
-        });
-      }
-    </script>
-    <% for (var i = 0; i < htmlWebpackPlugin.options.globals.scripts.length; i++) { %>
-    <script type="text/javascript" src="<%= htmlWebpackPlugin.options.globals.scripts[i] %>"></script>
-    <% } %>
-  </body>
-</html>
+          }
+          return undefined;
+        },
+      });
+    }
+    if (!Array.prototype.fill) {
+      Object.defineProperty(Array.prototype, 'fill', {
+        value: function (value) {
+          if (this == null) {
+            throw new TypeError('this is null or not defined');
+          }
+          var O = Object(this);
+          var len = O.length >>> 0;
+          var start = arguments[1];
+          var relativeStart = start >> 0;
+          var k = relativeStart < 0 ? Math.max(len + relativeStart, 0) : Math.min(relativeStart, len);
+          var end = arguments[2];
+          var relativeEnd = end === undefined ? len : end >> 0;
+          var final = relativeEnd < 0 ? Math.max(len + relativeEnd, 0) : Math.min(relativeEnd, len);
+          while (k < final) {
+            O[k] = value;
+            k++;
+          }
+          return O;
+        },
+      });
+    }
+  </script>
+  <% for (var i = 0; i < htmlWebpackPlugin.options.globals.scripts.length; i++) { %>
+  <script type="text/javascript" src="<%= htmlWebpackPlugin.options.globals.scripts[i] %>"></script>
+  <% } %>
+</body>
+
+</html>

+ 10 - 2
front/src/services/Tools.js

@@ -592,19 +592,27 @@ export function timeRange(timerange) {
   let endTime = null;
   switch (timerange) {
     case 'all':
-      break;
+      return [null, null];
     case 'week':
       endTime = new Date();
+      endTime.setHours(0, 0, 0, 0);
+      endTime.setDate(endTime.getDate() + 1);
       startTime = new Date(endTime.getTime() - 86400000 * 7);
       break;
     case 'month':
       endTime = new Date();
+      endTime.setHours(0, 0, 0, 0);
+      endTime.setDate(endTime.getDate() + 1);
       startTime = new Date();
+      startTime.setHours(0, 0, 0, 0);
       startTime.setMonth(startTime.getMonth() - 1);
       break;
     case 'month3':
       endTime = new Date();
+      endTime.setHours(0, 0, 0, 0);
+      endTime.setDate(endTime.getDate() + 1);
       startTime = new Date();
+      startTime.setHours(0, 0, 0, 0);
       startTime.setMonth(startTime.getMonth() - 3);
       break;
     case 'today':
@@ -613,5 +621,5 @@ export function timeRange(timerange) {
       startTime.setHours(0, 0, 0, 0);
       endTime = new Date(startTime.getTime() + 86400000);
   }
-  return [startTime, endTime];
+  return [formatDate(startTime, 'YYYY-MM-DD'), formatDate(endTime, 'YYYY-MM-DD')];
 }

+ 18 - 1
server/build.gradle

@@ -8,8 +8,10 @@ buildscript {
 //        wrapperVersion = '1.0.21.RELEASE'
     }
     repositories {
+        maven { url 'https://maven.aliyun.com/repository/public/' }
+        maven { url 'https://maven.aliyun.com/repository/google/'}
+        maven { url 'https://maven.aliyun.com/repository/jcenter/'}
         mavenLocal()
-//		maven { url = "http://maven.aliyun.com/nexus/content/groups/public" }
         mavenCentral()
         maven {
             url "https://plugins.gradle.org/m2/"
@@ -21,6 +23,21 @@ buildscript {
     }
 }
 
+allprojects {
+    repositories {
+        //google()
+        //jcenter()
+        maven { url 'https://maven.aliyun.com/repository/public/' }
+        maven { url 'https://maven.aliyun.com/repository/google/'}
+        maven { url 'https://maven.aliyun.com/repository/jcenter/'}
+        mavenLocal()
+        mavenCentral()
+        maven {
+            url "https://plugins.gradle.org/m2/"
+        }
+    }
+}
+
 subprojects {
     apply plugin: 'java'
     apply plugin: "idea"

+ 0 - 6
server/data/build.gradle

@@ -2,12 +2,6 @@ apply plugin: 'java'
 
 def libraries = rootProject.ext.libraries
 
-repositories {
-    mavenLocal()
-    maven { url = "http://maven.aliyun.com/nexus/content/groups/public" }
-    mavenCentral()
-}
-
 dependencies {
     //mybatis
 //    compileClasspath(libraries."mybatis", libraries."mybatis-mapper", libraries."mybatis-page")

+ 2 - 2
server/data/src/main/java/com/qxgmat/data/constants/enums/ServiceKey.java

@@ -2,8 +2,8 @@ package com.qxgmat.data.constants.enums;
 
 public enum ServiceKey {
     VIP("vip", "VIP", 0, 0), // 收藏和错题处的组卷、导出;笔记导出功能;部分解析只有VIP可以看;下载模考报告; 解锁完整版模考报告;“提问开放”期间有提问权限
-    TEXTBOOK("textbook", "千行-CAT模考(6套)", 180, 30),
-    QX_CAT("qx_cat", "千行机经", 180, 180), // 可以考2次
+    TEXTBOOK("textbook", "千行机经", 180, 30),
+    QX_CAT("qx_cat", "千行-CAT模考(6套)", 180, 180), // 可以考2次
 
     ;
     public String key;

+ 26 - 0
server/data/src/main/java/com/qxgmat/data/dao/entity/Comment.java

@@ -68,6 +68,9 @@ public class Comment implements Serializable {
     @Column(name = "`create_time`")
     private Date createTime;
 
+    @Column(name = "`update_time`")
+    private Date updateTime;
+
     @Column(name = "`content`")
     private String content;
 
@@ -264,6 +267,20 @@ public class Comment implements Serializable {
     }
 
     /**
+     * @return update_time
+     */
+    public Date getUpdateTime() {
+        return updateTime;
+    }
+
+    /**
+     * @param updateTime
+     */
+    public void setUpdateTime(Date updateTime) {
+        this.updateTime = updateTime;
+    }
+
+    /**
      * @return content
      */
     public String getContent() {
@@ -294,6 +311,7 @@ public class Comment implements Serializable {
         sb.append(", isSystem=").append(isSystem);
         sb.append(", sort=").append(sort);
         sb.append(", createTime=").append(createTime);
+        sb.append(", updateTime=").append(updateTime);
         sb.append(", content=").append(content);
         sb.append("]");
         return sb.toString();
@@ -417,6 +435,14 @@ public class Comment implements Serializable {
         }
 
         /**
+         * @param updateTime
+         */
+        public Builder updateTime(Date updateTime) {
+            obj.setUpdateTime(updateTime);
+            return this;
+        }
+
+        /**
          * @param content
          */
         public Builder content(String content) {

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

@@ -44,6 +44,12 @@ public class CoursePackage implements Serializable {
     private Integer isSpecial;
 
     /**
+     * 是否显示
+     */
+    @Column(name = "`is_show`")
+    private Integer isShow;
+
+    /**
      * 赠品:json
      */
     @Column(name = "`gift`")
@@ -174,6 +180,24 @@ public class CoursePackage implements Serializable {
     }
 
     /**
+     * 获取是否显示
+     *
+     * @return is_show - 是否显示
+     */
+    public Integer getIsShow() {
+        return isShow;
+    }
+
+    /**
+     * 设置是否显示
+     *
+     * @param isShow 是否显示
+     */
+    public void setIsShow(Integer isShow) {
+        this.isShow = isShow;
+    }
+
+    /**
      * 获取赠品:json
      *
      * @return gift - 赠品:json
@@ -267,6 +291,7 @@ public class CoursePackage implements Serializable {
         sb.append(", price=").append(price);
         sb.append(", courseIds=").append(courseIds);
         sb.append(", isSpecial=").append(isSpecial);
+        sb.append(", isShow=").append(isShow);
         sb.append(", gift=").append(gift);
         sb.append(", saleNumber=").append(saleNumber);
         sb.append(", createTime=").append(createTime);
@@ -346,6 +371,16 @@ public class CoursePackage implements Serializable {
         }
 
         /**
+         * 设置是否显示
+         *
+         * @param isShow 是否显示
+         */
+        public Builder isShow(Integer isShow) {
+            obj.setIsShow(isShow);
+            return this;
+        }
+
+        /**
          * 设置赠品:json
          *
          * @param gift 赠品:json

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

@@ -90,6 +90,12 @@ public class UserPaper implements Serializable {
     private Integer finishTimes;
 
     /**
+     * 重置次数
+     */
+    @Column(name = "`reset_times`")
+    private Integer resetTimes;
+
+    /**
      * 单次时间:系统时间
      */
     @Column(name = "`time`")
@@ -388,6 +394,24 @@ public class UserPaper implements Serializable {
     }
 
     /**
+     * 获取重置次数
+     *
+     * @return reset_times - 重置次数
+     */
+    public Integer getResetTimes() {
+        return resetTimes;
+    }
+
+    /**
+     * 设置重置次数
+     *
+     * @param resetTimes 重置次数
+     */
+    public void setResetTimes(Integer resetTimes) {
+        this.resetTimes = resetTimes;
+    }
+
+    /**
      * 获取单次时间:系统时间
      *
      * @return time - 单次时间:系统时间
@@ -551,6 +575,7 @@ public class UserPaper implements Serializable {
         sb.append(", questionNumber=").append(questionNumber);
         sb.append(", times=").append(times);
         sb.append(", finishTimes=").append(finishTimes);
+        sb.append(", resetTimes=").append(resetTimes);
         sb.append(", time=").append(time);
         sb.append(", latestTime=").append(latestTime);
         sb.append(", latestReportId=").append(latestReportId);
@@ -723,6 +748,16 @@ public class UserPaper implements Serializable {
         }
 
         /**
+         * 设置重置次数
+         *
+         * @param resetTimes 重置次数
+         */
+        public Builder resetTimes(Integer resetTimes) {
+            obj.setResetTimes(resetTimes);
+            return this;
+        }
+
+        /**
          * 设置最近一次做题时间
          *
          * @param latestTime 最近一次做题时间

+ 2 - 1
server/data/src/main/java/com/qxgmat/data/dao/mapping/CommentMapper.xml

@@ -16,6 +16,7 @@
     <result column="is_system" jdbcType="INTEGER" property="isSystem" />
     <result column="sort" jdbcType="INTEGER" property="sort" />
     <result column="create_time" jdbcType="TIMESTAMP" property="createTime" />
+    <result column="update_time" jdbcType="TIMESTAMP" property="updateTime" />
   </resultMap>
   <resultMap extends="BaseResultMap" id="ResultMapWithBLOBs" type="com.qxgmat.data.dao.entity.Comment">
     <!--
@@ -28,7 +29,7 @@
       WARNING - @mbg.generated
     -->
     `id`, `user_id`, `nickname`, `avatar`, `channel`, `position`, `is_show`, `is_special`, 
-    `is_system`, `sort`, `create_time`
+    `is_system`, `sort`, `create_time`, `update_time`
   </sql>
   <sql id="Blob_Column_List">
     <!--

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

@@ -11,6 +11,7 @@
     <result column="price" jdbcType="DECIMAL" property="price" />
     <result column="course_ids" jdbcType="VARCHAR" property="courseIds" typeHandler="com.nuliji.tools.mybatis.handler.IntegerArrayWithJsonHandler" />
     <result column="is_special" jdbcType="INTEGER" property="isSpecial" />
+    <result column="is_show" jdbcType="INTEGER" property="isShow" />
     <result column="gift" jdbcType="VARCHAR" property="gift" typeHandler="com.nuliji.tools.mybatis.handler.JsonObjectHandler" />
     <result column="sale_number" jdbcType="INTEGER" property="saleNumber" />
     <result column="create_time" jdbcType="TIMESTAMP" property="createTime" />
@@ -26,8 +27,8 @@
     <!--
       WARNING - @mbg.generated
     -->
-    `id`, `struct_id`, `title`, `price`, `course_ids`, `is_special`, `gift`, `sale_number`, 
-    `create_time`, `update_time`
+    `id`, `struct_id`, `title`, `price`, `course_ids`, `is_special`, `is_show`, `gift`, 
+    `sale_number`, `create_time`, `update_time`
   </sql>
   <sql id="Blob_Column_List">
     <!--

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

@@ -19,6 +19,7 @@
     <result column="question_number" jdbcType="INTEGER" property="questionNumber" />
     <result column="times" jdbcType="INTEGER" property="times" />
     <result column="finish_times" jdbcType="INTEGER" property="finishTimes" />
+    <result column="reset_times" jdbcType="INTEGER" property="resetTimes" />
     <result column="time" jdbcType="INTEGER" property="time" />
     <result column="latest_time" jdbcType="TIMESTAMP" property="latestTime" />
     <result column="latest_report_id" jdbcType="INTEGER" property="latestReportId" />
@@ -34,7 +35,7 @@
     -->
     `id`, `user_id`, `title`, `paper_module`, `paper_origin`, `is_adapt`, `origin_id`, 
     `record_id`, `paper_no`, `qx_cat`, `question_no_ids`, `question_number`, `times`, 
-    `finish_times`, `time`, `latest_time`, `latest_report_id`, `total_time`, `total_number`, 
-    `total_correct`, `delete_time`, `is_reset`
+    `finish_times`, `reset_times`, `time`, `latest_time`, `latest_report_id`, `total_time`, 
+    `total_number`, `total_correct`, `delete_time`, `is_reset`
   </sql>
 </mapper>

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

@@ -22,7 +22,7 @@ public interface ExaminationPaperRelationMapper {
     List<ExercisePaper> listWithUser(
             @Param("structId") Number structId,
             @Param("userId") Number userId,
-            @Param("qxCatNo") Integer qxCatNo,
+            @Param("qxCat") Integer qxCat,
             @Param("times") Integer times
     );
 }

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

@@ -28,7 +28,14 @@ public interface QuestionNoRelationMapper {
     );
 
     List<QuestionNo> searchStem(
-            @Param("stem") String stem
+            @Param("stem") String stem,
+            @Param("questionType") String questionType
+    );
+
+    List<QuestionNo> searchNo(
+            @Param("keyword") String keyword,
+            @Param("module") String module,
+            @Param("questionType") String questionType
     );
 
     List<QuestionNoRelation> searchStemFulltext(

+ 1 - 1
server/data/src/main/java/com/qxgmat/data/relation/mapping/CourseDataRelationMapper.xml

@@ -32,7 +32,7 @@
     select
     <include refid="Id_Column_List" />
     from `course_data` cd
-    left join `user_course_data_subscribe` ucds on `ucds`.`data_id`= cd.`id`
+    left join `user_data_subscribe` ucds on `ucds`.`data_id`= cd.`id`
     <if test="userId != null">
       and `ucds`.user_id = #{userId,jdbcType=VARCHAR}
     </if>

+ 1 - 1
server/data/src/main/java/com/qxgmat/data/relation/mapping/ExaminationPaperRelationMapper.xml

@@ -48,7 +48,7 @@
       and up.`paper_origin` = 'examination'
       and up.`user_id` = #{userId,jdbcType=VARCHAR}
       <if test="qxCatNo != null">
-        and up.`qx_cat_no` = #{qxCatNo,jdbcType=VARCHAR}
+        and up.`qx_cat` = #{qxCat,jdbcType=VARCHAR}
       </if>
       <if test="times != null">
         <if test="times == 0">

+ 29 - 3
server/data/src/main/java/com/qxgmat/data/relation/mapping/QuestionNoRelationMapper.xml

@@ -53,6 +53,30 @@
       and MATCH (q.`description`) AGAINST (#{stem, jdbcType=VARCHAR} IN NATURAL LANGUAGE MODE) > 0.8
     where
       q.`id` &gt; 0 and qn.`delete_time` is null
+    <if test="questionType != null">
+      and q.`question_type` = #{questionType,jdbcType=VARCHAR}
+    </if>
+  </select>
+
+  <!--
+    按题干搜索相似度80%以上的
+  -->
+  <select id="searchNo" resultMap="IdMap">
+    select
+    <include refid="Id_Column_List" />
+    from `question_no` qn
+    left join `question` q on q.`id` = qn.`question_id`
+    where
+    q.`id` &gt; 0 and qn.`delete_time` is null
+    <if test="keyword">
+      and qn.`title` = #{keyword, jdbcType=VARCHAR}
+    </if>
+    <if test="module != null">
+      and qn.`module` = #{module,jdbcType=VARCHAR}
+    </if>
+    <if test="questionType != null">
+      and q.`question_type` = #{questionType,jdbcType=VARCHAR}
+    </if>
   </select>
 
   <!--
@@ -65,8 +89,8 @@
     if(qn.`title` = #{keyword, jdbcType=VARCHAR}, 1, MATCH (q.`description`) AGAINST (#{keyword, jdbcType=VARCHAR} IN NATURAL LANGUAGE MODE)) as `relation_score`,
     </if>
     qn.`collect_number` as `collect_number`,
-    qn.`total_correct` / qn.`total_number` as `correct`,
-    qn.`total_time` / qn.`total_number` as `time`
+    if(qn.`total_number` > 0, qn.`total_correct` / qn.`total_number`,0) as `correct`,
+    if(qn.`total_number`>0, qn.`total_time` / qn.`total_number`,0) as `time`
     from `question_no` qn
     left join `question` q on q.`id` = qn.`question_id`
       and (q.`question_module` = 'base')
@@ -183,8 +207,10 @@
     select
     <include refid="Id_Column_List" />
     <if test="paperId != null">
-      , find_in_set(qn.`id`, trim(TRAILING ']' from trim(LEADING '[' from ep.`question_no_ids`))) as `no`
+      , find_in_set(qn.`id`, trim(TRAILING ']' from trim(LEADING '[' from ep.`question_no_ids`))) as `number`
     </if>
+    ,if(qn.`total_number` > 0, qn.`total_correct`- qn.`total_number` / qn.`total_number`,0) as `correct`
+    ,if(qn.`total_number`>0, qn.`total_time` / qn.`total_number`,0) as `time`
     from `question_no` qn
     left join `question` q on q.`id` = qn.`question_id`
     <if test="paperId != null">

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

@@ -40,7 +40,7 @@
         q.`question_type` = #{item}
       </foreach>
     </if>
-    left join `question_no` qn on qn.`question_id` = q.`id` and qn.`module` = 'exercise'
+    left join `question_no` qn on qn.`id` = uaq.`question_no_id` and qn.`module` = 'exercise'
       and (q.`question_module` = 'base')
     <if test="structIds != null">
       and
@@ -94,7 +94,7 @@
         q.`question_type` = #{item}
       </foreach>
     </if>
-    left join `question_no` qn on qn.`question_id` = q.`id` and qn.`module` = 'examination'
+    left join `question_no` qn on qn.`id` = uaq.`question_no_id` and qn.`module` = 'examination'
       and (q.`question_module` = 'base')
     <if test="structIds != null">
       and

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

@@ -20,6 +20,8 @@
     </if>
     select max(ucq.`id`) as `id`,
     max(uq.`create_time`) as `latest_time`,
+    max(ucq.`create_time`) as `collect_time`,
+    max(q.`update_time`) as `update_time`,
     sum(uq.`is_correct`) / count(uq.id) as `correct`,
     sum(uq.`user_time`) / count(uq.id) as `time`,
     if(q.`question_type`='sc', 1, if(q.`question_type`='rc', 2, if(q.`question_type`='cr', 3,if(q.`question_type`='ds', 4, if(q.`question_type`='ps', 5,if(q.`question_type`='ir', 6, 7)))))) as `question_type`,
@@ -36,7 +38,7 @@
         q.`question_type` = #{item}
       </foreach>
     </if>
-    left join `question_no` qn on qn.`question_id` = q.`id` and qn.`module` = 'exercise'
+    left join `question_no` qn on qn.`id` = ucq.`question_no_id` and qn.`module` = 'exercise'
     and (q.`question_module` = 'base')
     <if test="structIds != null">
       and
@@ -47,7 +49,7 @@
     left join `sentence_question` sq on sq.`question_id` = q.`id`
       and (q.`question_module` = 'sentence')
     where
-    q.`id` > 0
+    q.`id` > 0 and ucq.`user_id` = #{userId,jdbcType=VARCHAR}
     <if test="keyword != null">
       and (q.`stem` like #{keywordLike,jdbcType=VARCHAR}
       or qn.`title` like #{keywordLike,jdbcType=VARCHAR}
@@ -77,6 +79,8 @@
     </if>
     select max(ucq.`id`) as `id`,
     max(uq.`create_time`) as `latest_time`,
+    ucq.`create_time` as `collect_time`,
+    q.`update_time` as `update_time`,
     sum(uq.`is_correct`) / count(uq.id) as `correct`,
     sum(uq.`user_time`) / count(uq.id) as `time`,
     if(q.`question_type`='sc', 1, if(q.`question_type`='rc', 2, if(q.`question_type`='cr', 3,if(q.`question_type`='ds', 4, if(q.`question_type`='ps', 5,if(q.`question_type`='ir', 6, 7)))))) as `question_type`,
@@ -93,7 +97,7 @@
         q.`question_type` = #{item}
       </foreach>
     </if>
-    left join `question_no` qn on qn.`question_id` = q.`id` and qn.`module` = 'examination'
+    left join `question_no` qn on qn.`id` = ucq.`question_no_id` and qn.`module` = 'examination'
     and (q.`question_module` = 'base')
     <if test="structIds != null">
       and
@@ -110,7 +114,7 @@
         and tq.`year` = #{year,jdbcType=VARCHAR}
       </if>
     where
-    q.`id` > 0
+    q.`id` > 0 and ucq.`user_id` = #{userId,jdbcType=VARCHAR}
     <if test="keyword != null">
       and (q.`stem` like #{keywordLike,jdbcType=VARCHAR}
       or qn.`title` like #{keywordLike,jdbcType=VARCHAR}

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

@@ -30,7 +30,7 @@
         q.`question_type` = #{item}
       </foreach>
     </if>
-    left join `question_no` qn on qn.`question_id` = q.`id` and qn.`module` = 'exercise'
+    left join `question_no` qn on qn.`id` = unq.`question_no_id` and qn.`module` = 'exercise'
     and (q.`question_module` = 'base')
     <if test="structIds != null">
       and
@@ -80,7 +80,7 @@
         q.`question_type` = #{item}
       </foreach>
     </if>
-    left join `question_no` qn on qn.`question_id` = q.`id` and qn.`module` = 'examination'
+    left join `question_no` qn on qn.`id` = unq.`question_no_id` and qn.`module` = 'examination'
     and (q.`question_module` = 'base')
     <if test="structIds != null">
       and

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

@@ -37,9 +37,9 @@
     DISTINCT(up.`id`),
     up.`title` as `title`,
     up.`latest_time` as `latest_time`,
-    ur.`user_correct` / up.`question_number` as `correct`,
-    ur.`user_time` / up.`question_number` as `time`,
-    ur.`user_number` / up.`question_number` as `progress`
+    if(up.`question_number`>0,ur.`user_correct` / up.`question_number`,0) as `correct`,
+    if(up.`question_number`>0,ur.`user_time` / up.`question_number`,0) as `time`,
+    if(up.`question_number`>0,ur.`user_number` / up.`question_number`,0) as `progress`
     from `user_paper` up
     left join `user_report` ur on ur.`id`= up.`latest_report_id`
     where up.`user_id` = #{userId,jdbcType=VARCHAR} and ur.`id` is not null
@@ -65,9 +65,9 @@
     DISTINCT(up.`id`),
     up.`title` as `title`,
     up.`latest_time` as `latest_time`,
-    ur.`user_correct` / up.`question_number` as `correct`,
-    ur.`user_time` / up.`question_number` as `time`,
-    ur.`user_number` / up.`question_number` as `progress`
+    if(up.`question_number`>0,ur.`user_correct` / up.`question_number`,0) as `correct`,
+    if(up.`question_number`>0,ur.`user_time` / up.`question_number`,0) as `time`,
+    if(up.`question_number`>0,ur.`user_number` / up.`question_number`,0) as `progress`
     from `user_paper` up
     left join `user_report` ur on ur.`id`= up.`latest_report_id`
     left join `exercise_paper` ep on up.`paper_origin` = 'exercise' and ep.`id` = up.`origin_id`
@@ -109,11 +109,11 @@
         pp.`question_type` = #{item}
       </foreach>
     </if>
-    where up.`user_id` = #{userId,jdbcType=VARCHAR} and ur.`id` is not null
+    where up.`user_id` = #{userId,jdbcType=VARCHAR} and ur.`id` is not null and ur.user_number > 0
     <if test="keyword != null">
       and (ep.`title` like #{keywordLike,jdbcType=VARCHAR}
-      or pa.`title like #{keywordLike,jdbcType=VARCHAR}`
-      or pp.`title like #{keywordLike,jdbcType=VARCHAR}`)
+      or pa.`title` like #{keywordLike,jdbcType=VARCHAR}
+      or pp.`title` like #{keywordLike,jdbcType=VARCHAR})
     </if>
     <if test="structIds != null">
       and ep.`id` > 0
@@ -125,10 +125,10 @@
       and (pa.`id` > 0)
     </if>
     <if test="startTime != null">
-      and uq.`create_time` &gt; #{startTime,jdbcType=TIMESTAMP}
+      and ur.`create_time` &gt; #{startTime,jdbcType=TIMESTAMP}
     </if>
     <if test="endTime != null">
-      and uq.`create_time` &lt; #{endTime,jdbcType=TIMESTAMP}
+      and ur.`create_time` &lt; #{endTime,jdbcType=TIMESTAMP}
     </if>
     <if test="order != null">
       order by ${order}
@@ -167,10 +167,10 @@
       <if test="year != null">
         and tp.`year` = #{year,jdbcType=VARCHAR}
       </if>
-    where up.`user_id` = #{userId,jdbcType=VARCHAR} and ur.`id` is not null
+    where up.`user_id` = #{userId,jdbcType=VARCHAR} and ur.`id` is not null and ur.user_number > 0
     <if test="keyword != null">
       and (ep.`title` like #{keywordLike,jdbcType=VARCHAR}
-      or tp.`title like #{keywordLike,jdbcType=VARCHAR}`)
+      or tp.`title` like #{keywordLike,jdbcType=VARCHAR})
     </if>
     <if test="structIds != null">
       and ep.`id` > 0
@@ -188,10 +188,10 @@
       and (tp.`id` > 0)
     </if>
     <if test="startTime != null">
-      and uq.`create_time` &gt; #{startTime,jdbcType=TIMESTAMP}
+      and ur.`create_time` &gt; #{startTime,jdbcType=TIMESTAMP}
     </if>
     <if test="endTime != null">
-      and uq.`create_time` &lt; #{endTime,jdbcType=TIMESTAMP}
+      and ur.`create_time` &lt; #{endTime,jdbcType=TIMESTAMP}
     </if>
     <if test="order != null">
       order by ${order}

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

@@ -41,7 +41,7 @@
         q.`question_type` = #{item}
       </foreach>
     </if>
-    left join `question_no` qn on qn.`question_id` = q.`id` and qn.`module` = 'exercise'
+    left join `question_no` qn on qn.`id` = uq.`question_no_id` and qn.`module` = 'exercise'
     and (q.`question_module` = 'base')
     <if test="structIds != null">
       and
@@ -101,7 +101,7 @@
         q.`question_type` = #{item}
       </foreach>
     </if>
-    left join `question_no` qn on qn.`question_id` = q.`id` and qn.`module` = 'examination'
+    left join `question_no` qn on qn.`id` = uq.`question_no_id` and qn.`module` = 'examination'
     and (q.`question_module` = 'base')
     <if test="structIds != null">
       and

+ 1 - 1
server/data/src/main/java/com/qxgmat/data/relation/mapping/UserRelationMapper.xml

@@ -40,7 +40,7 @@
     from `user`
     where
       `prepare_goal` > 0
-    and `${field}` &gt; 0 and `${field}` != ""
+    and `${field}` is not null and `${field}` != ""
     group by `${field}`
   </select>
 

+ 3 - 0
server/data/src/main/resources/db/migration/V10__update_user_paper.sql

@@ -0,0 +1,3 @@
+ALTER TABLE user_paper add column reset_times int(11) unsigned NOT NULL DEFAULT '0' COMMENT '重置次数' AFTER finish_times;
+
+UPDATE user_paper set reset_times = finish_times;

+ 5 - 0
server/data/src/main/resources/db/migration/V9__update_package.sql

@@ -0,0 +1,5 @@
+ALTER TABLE course_package add column is_show tinyint(1) unsigned NOT NULL DEFAULT '0' COMMENT '是否显示' AFTER is_special;
+
+ALTER TABLE comment add column update_time datetime DEFAULT NULL AFTER create_time;
+
+UPDATE comment set update_time = create_time;

+ 1 - 1
server/data/src/main/resources/jdbc.properties.d

@@ -1,7 +1,7 @@
 # JDBC 驱动类名
 jdbc.driverClassName=com.mysql.cj.jdbc.Driver
 # JDBC URL: jdbc:mysql:// + 数据库主机地址 + 端口号 + 数据库名
-jdbc.url=jdbc:mysql://127.0.0.1:3306/qianxing?useUnicode=true&amp;characterEncoding=utf-8&amp;allowMultiQueries=true
+jdbc.url=jdbc:mysql://127.0.0.1:3306/qianxing?useUnicode=true&amp;characterEncoding=utf-8&amp;allowMultiQueries=true&serverTimezone=GMT%2b8
 # JDBC 用户名及密码
 jdbc.username=qianxing
 jdbc.password=qianxing

+ 0 - 7
server/gateway-api/build.gradle

@@ -2,13 +2,6 @@ apply plugin: 'java'
 
 mainClassName = 'com.qxgmat.Application'
 
-repositories {
-    mavenLocal()
-    maven { url = "http://maven.aliyun.com/nexus/content/groups/public" }
-    mavenCentral()
-}
-
-
 dependencies {
     // shiro
 //    compile(libraries."shiro-core", libraries."shiro-spring", libraries."shiro-cache")

+ 8 - 0
server/gateway-api/src/main/java/com/qxgmat/Application.java

@@ -9,6 +9,9 @@ import org.springframework.scheduling.annotation.EnableAsync;
 import org.springframework.scheduling.annotation.EnableScheduling;
 import org.springframework.transaction.annotation.EnableTransactionManagement;
 
+import javax.annotation.PostConstruct;
+import java.util.TimeZone;
+
 
 @EnableScheduling
 @EnableCaching
@@ -24,6 +27,11 @@ public class Application {
     public static void main(String[] args) {
         SpringApplication.run(Application.class, args);
     }
+
+    @PostConstruct
+    void setDefaultTimezone(){
+        TimeZone.setDefault(TimeZone.getTimeZone("Asia/Shanghai"));
+    }
 }
 
 // 服务:开通,服务权限验证,后台支付

+ 2 - 2
server/gateway-api/src/main/java/com/qxgmat/controller/admin/QuestionController.java

@@ -161,7 +161,7 @@ public class QuestionController {
     @RequestMapping(value = "/search/stem", method = RequestMethod.POST)
     @ApiOperation(value = "搜索题目编号列表:题干搜索", httpMethod = "POST")
     public Response<PageMessage<QuestionNoExtendDto>> searchStem(@RequestBody @Validated QuestionNoSearchDto dto, HttpServletRequest request) {
-        PageResult<QuestionNoRelation> p = questionNoService.searchStem(dto.getPage(), dto.getSize(), dto.getContent());
+        PageResult<QuestionNoRelation> p = questionNoService.searchStem(dto.getPage(), dto.getSize(), dto.getContent(), dto.getQuestionType());
         List<QuestionNoExtendDto> pr = Transform.convert(p, QuestionNoExtendDto.class);
 
         return ResponseHelp.success(pr, dto.getPage(), dto.getSize(), p.getTotal());
@@ -170,7 +170,7 @@ public class QuestionController {
     @RequestMapping(value = "/search/no", method = RequestMethod.POST)
     @ApiOperation(value = "搜索题目编号列表:题目编号搜索", httpMethod = "POST")
     public Response<PageMessage<QuestionNoExtendDto>> searchNo(@RequestBody @Validated QuestionNoSearchDto dto, HttpServletRequest request) {
-        PageResult<QuestionNoRelation> p = questionNoService.searchNo(dto.getPage(), dto.getSize(), dto.getKeyword(), dto.getModule(), dto.getIds() !=null ? dto.getIds().length == 0? null:dto.getIds() : dto.getIds());
+        PageResult<QuestionNoRelation> p = questionNoService.searchNo(dto.getPage(), dto.getSize(), dto.getKeyword(), dto.getModule(), dto.getQuestionType(), dto.getIds() !=null ? dto.getIds().length == 0? null:dto.getIds() : dto.getIds());
         List<QuestionNoExtendDto> pr = Transform.convert(p, QuestionNoExtendDto.class);
 
         return ResponseHelp.success(pr, dto.getPage(), dto.getSize(), p.getTotal());

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

@@ -788,7 +788,7 @@ public class MyController {
             dto.setTotalTime(stat.getTotalTime());
 
             Collection questionNoIds = Transform.getIds(list, QuestionNo.class, "id");
-            List<UserQuestion> userQuestionList = userQuestionService.listByQuestionWithTime(user.getId(), QuestionModule.BASE, questionNoIds, Tools.baseTime(startTime), Tools.baseTime(endTime));
+            List<UserQuestion> userQuestionList = userQuestionService.listByQuestionWithTime(user.getId(), QuestionModule.BASE, questionNoIds, Tools.baseDate(startTime), Tools.baseDate(endTime));
             Map userQuestionMap = Transform.getMap(userQuestionList, UserQuestion.class, "questionNoId");
             dto.setUserQuestion(userQuestionMap.size());
 
@@ -886,7 +886,7 @@ public class MyController {
             @RequestParam(required = false, defaultValue = "desc") String direction,
             HttpSession session)  {
         User user = (User) shiroHelp.getLoginUser();
-        Page<CourseExperience> p = courseExperienceService.listWithUser(page, size, user.getId(), Tools.baseTime(startTime), Tools.baseTime(endTime), order, DirectionStatus.ValueOf(direction));
+        Page<CourseExperience> p = courseExperienceService.listWithUser(page, size, user.getId(), Tools.baseDate(startTime), Tools.baseDate(endTime), order, DirectionStatus.ValueOf(direction));
 
         return ResponseHelp.success(p, page, size, p.getTotal());
     }
@@ -980,13 +980,13 @@ public class MyController {
             @RequestParam(required = false) String endTime,
             @RequestParam(required = false) Boolean latest,
             @RequestParam(required = false) String year,
-            @RequestParam(required = false) String order, // (pid asc, no asc), time, correct, question_type, latest_time
+            @RequestParam(required = false) String order, // (pid asc, no asc), time, correct, question_type, update_time, collect_time
             HttpSession session)  {
         User user = (User) shiroHelp.getLoginUser();
         QuestionNoModule questionNoModule = QuestionNoModule.ValueOf(module);
         Page<UserCollectQuestion> p = null;
         if(questionNoModule == QuestionNoModule.EXERCISE){
-            p = userCollectQuestionService.listExercise(page, size, user.getId(), keyword, questionTypes, structIds, Tools.baseTime(startTime), Tools.baseTime(endTime), order != null ? order.replace("|", " ") : null);
+            p = userCollectQuestionService.listExercise(page, size, user.getId(), keyword, questionTypes, structIds, Tools.baseDate(startTime), Tools.baseDate(endTime), order != null ? order.replace("|", " ") : null);
         }else if (questionNoModule == QuestionNoModule.EXAMINATION){
             Integer libraryId = null;
             if (latest != null){
@@ -998,7 +998,7 @@ public class MyController {
                     libraryId = 0;
                 }
             }
-            p = userCollectQuestionService.listExamination(page, size, user.getId(), keyword, questionTypes, structIds, libraryId, year, Tools.baseTime(startTime), Tools.baseTime(endTime), order != null ? order.replace("|", " ") : null);
+            p = userCollectQuestionService.listExamination(page, size, user.getId(), keyword, questionTypes, structIds, libraryId, year, Tools.baseDate(startTime), Tools.baseDate(endTime), order != null ? order.replace("|", " ") : null);
         }else{
             throw new ParameterException("参数逻辑错误");
         }
@@ -1056,7 +1056,7 @@ public class MyController {
         QuestionNoModule questionNoModule = QuestionNoModule.ValueOf(module);
         Page<UserQuestion> p = null;
         if(questionNoModule == QuestionNoModule.EXERCISE){
-            p = userQuestionService.listExerciseError(page, size, user.getId(), keyword, questionTypes, structIds, Tools.baseTime(startTime), Tools.baseTime(endTime), order != null ? order.replace("|", " ") : null);
+            p = userQuestionService.listExerciseError(page, size, user.getId(), keyword, questionTypes, structIds, Tools.baseDate(startTime), Tools.baseDate(endTime), order != null ? order.replace("|", " ") : null);
         }else if (questionNoModule == QuestionNoModule.EXAMINATION){
             Integer libraryId = null;
             if (latest != null){
@@ -1068,7 +1068,7 @@ public class MyController {
                     libraryId = 0;
                 }
             }
-            p = userQuestionService.listExaminationError(page, size, user.getId(), keyword, questionTypes, structIds, libraryId, year, Tools.baseTime(startTime), Tools.baseTime(endTime), order != null ? order.replace("|", " ") : null);
+            p = userQuestionService.listExaminationError(page, size, user.getId(), keyword, questionTypes, structIds, libraryId, year, Tools.baseDate(startTime), Tools.baseDate(endTime), order != null ? order.replace("|", " ") : null);
         }else{
             throw new ParameterException("参数逻辑错误");
         }
@@ -1217,7 +1217,7 @@ public class MyController {
         QuestionNoModule questionNoModule = QuestionNoModule.ValueOf(module);
         Page<UserNoteQuestion> p = null;
         if(questionNoModule == QuestionNoModule.EXERCISE){
-            p = userNoteQuestionService.listExercise(page, size, user.getId(), keyword, questionTypes, structIds, Tools.baseTime(startTime), Tools.baseTime(endTime), order != null ? order.replace("|", " ") : null);
+            p = userNoteQuestionService.listExercise(page, size, user.getId(), keyword, questionTypes, structIds, Tools.baseDate(startTime), Tools.baseDate(endTime), order != null ? order.replace("|", " ") : null);
         }else if (questionNoModule == QuestionNoModule.EXAMINATION){
             Integer libraryId = null;
             if (latest != null){
@@ -1229,7 +1229,7 @@ public class MyController {
                     libraryId = 0;
                 }
             }
-            p = userNoteQuestionService.listExamination(page, size, user.getId(), keyword, questionTypes, structIds, libraryId, year, Tools.baseTime(startTime), Tools.baseTime(endTime), order != null ? order.replace("|", " ") : null);
+            p = userNoteQuestionService.listExamination(page, size, user.getId(), keyword, questionTypes, structIds, libraryId, year, Tools.baseDate(startTime), Tools.baseDate(endTime), order != null ? order.replace("|", " ") : null);
         }else{
             throw new ParameterException("参数逻辑错误");
         }
@@ -1307,9 +1307,9 @@ public class MyController {
         QuestionNoModule questionNoModule = QuestionNoModule.ValueOf(module);
         Page<UserPaper> p = null;
         if (paperOrigin == PaperOrigin.COLLECT || paperOrigin == PaperOrigin.ERROR){
-            p = userPaperService.list(page, size, user.getId(), keyword, paperOrigin, Tools.baseTime(startTime), Tools.baseTime(endTime), order != null ? order.replace("|", " ") : null);
+            p = userPaperService.list(page, size, user.getId(), keyword, paperOrigin, Tools.baseDate(startTime), Tools.baseDate(endTime), order != null ? order.replace("|", " ") : null);
         }else if(questionNoModule == QuestionNoModule.EXERCISE){
-            p = userPaperService.listExercise(page, size, user.getId(), keyword, questionTypes, structIds, courseModules, Tools.baseTime(startTime), Tools.baseTime(endTime), order != null ? order.replace("|", " ") : null);
+            p = userPaperService.listExercise(page, size, user.getId(), keyword, questionTypes, structIds, courseModules, Tools.baseDate(startTime), Tools.baseDate(endTime), order != null ? order.replace("|", " ") : null);
         }else if (questionNoModule == QuestionNoModule.EXAMINATION){
             Integer libraryId = null;
             if (latest != null){
@@ -1322,7 +1322,7 @@ public class MyController {
                     libraryId = 0;
                 }
             }
-            p = userPaperService.listExamination(page, size, user.getId(), keyword, structIds, libraryId, year, Tools.baseTime(startTime), Tools.baseTime(endTime), order != null ? order.replace("|", " ") : null);
+            p = userPaperService.listExamination(page, size, user.getId(), keyword, structIds, libraryId, year, Tools.baseDate(startTime), Tools.baseDate(endTime), order != null ? order.replace("|", " ") : null);
         }else{
             throw new ParameterException("参数逻辑错误");
         }
@@ -1441,7 +1441,7 @@ public class MyController {
         QuestionNoModule questionNoModule = QuestionNoModule.ValueOf(module);
         Page<UserAskQuestion> p = null;
         if(questionNoModule == QuestionNoModule.EXERCISE){
-            p = userAskQuestionService.listExercise(page, size, user.getId(), keyword, questionTypes, structIds, AnswerStatus.ValueOf(answerStatus), Tools.baseTime(startTime), Tools.baseTime(endTime), order != null ? order.replace("|", " ") : null);
+            p = userAskQuestionService.listExercise(page, size, user.getId(), keyword, questionTypes, structIds, AnswerStatus.ValueOf(answerStatus), Tools.baseDate(startTime), Tools.baseDate(endTime), order != null ? order.replace("|", " ") : null);
         }else if (questionNoModule == QuestionNoModule.EXAMINATION){
             Integer libraryId = null;
             if (latest != null){
@@ -1453,7 +1453,7 @@ public class MyController {
                     libraryId = 0;
                 }
             }
-            p = userAskQuestionService.listExamination(page, size, user.getId(), keyword, questionTypes, structIds, libraryId, year, AnswerStatus.ValueOf(answerStatus), Tools.baseTime(startTime), Tools.baseTime(endTime), order != null ? order.replace("|", " ") : null);
+            p = userAskQuestionService.listExamination(page, size, user.getId(), keyword, questionTypes, structIds, libraryId, year, AnswerStatus.ValueOf(answerStatus), Tools.baseDate(startTime), Tools.baseDate(endTime), order != null ? order.replace("|", " ") : null);
         }else{
             throw new ParameterException("参数逻辑错误");
         }

+ 8 - 1
server/gateway-api/src/main/java/com/qxgmat/controller/api/QuestionController.java

@@ -287,10 +287,17 @@ public class QuestionController {
                 List<ExercisePaper> paperList = exercisePaperService.listByLogic(struct.getId(), 0, ExerciseLogic.NO, null);
                 List<UserExerciseGroupExtendDto> childrenDtos = new ArrayList<>(paperList.size());
 
+                Integer start = 0;
+
                 for(ExercisePaper child : paperList){
                     UserExerciseGroupExtendDto extendDto = new UserExerciseGroupExtendDto();
                     extendDto.setId(child.getId());
-                    extendDto.setTitle(child.getNo().toString());
+                    if (child.getQuestionNumber() > 1){
+                        extendDto.setTitle((start+1) + "-"+(start+child.getQuestionNumber()));
+                        start += child.getQuestionNumber();
+                    }else{
+                        extendDto.setTitle(child.getNo().toString());
+                    }
                     extendDto.setQuestionNumber(child.getQuestionNumber());
                     if(user != null){
                         int minTimes = 0;

+ 10 - 0
server/gateway-api/src/main/java/com/qxgmat/dto/admin/extend/QuestionNoExtendDto.java

@@ -12,6 +12,8 @@ public class QuestionNoExtendDto {
 
     private QuestionExtendDto question;
 
+    private int[] relationQuestion;
+
     public String getNo() {
         return no;
     }
@@ -51,4 +53,12 @@ public class QuestionNoExtendDto {
     public void setTitle(String title) {
         this.title = title;
     }
+
+    public int[] getRelationQuestion() {
+        return relationQuestion;
+    }
+
+    public void setRelationQuestion(int[] relationQuestion) {
+        this.relationQuestion = relationQuestion;
+    }
 }

+ 10 - 0
server/gateway-api/src/main/java/com/qxgmat/dto/admin/request/CommentDto.java

@@ -32,6 +32,8 @@ public class CommentDto {
 
     private Date createTime;
 
+    private Date updateTime;
+
     public Integer getId() {
         return id;
     }
@@ -127,4 +129,12 @@ public class CommentDto {
     public void setUser(UserExtendDto user) {
         this.user = user;
     }
+
+    public Date getUpdateTime() {
+        return updateTime;
+    }
+
+    public void setUpdateTime(Date updateTime) {
+        this.updateTime = updateTime;
+    }
 }

+ 10 - 0
server/gateway-api/src/main/java/com/qxgmat/dto/admin/request/CoursePackageDto.java

@@ -19,6 +19,8 @@ public class CoursePackageDto {
 
     private Integer isSpecial;
 
+    private Integer isShow;
+
     private Object gift;
 
     private String description;
@@ -86,4 +88,12 @@ public class CoursePackageDto {
     public void setStructId(Integer structId) {
         this.structId = structId;
     }
+
+    public Integer getIsShow() {
+        return isShow;
+    }
+
+    public void setIsShow(Integer isShow) {
+        this.isShow = isShow;
+    }
 }

+ 10 - 0
server/gateway-api/src/main/java/com/qxgmat/dto/admin/request/QuestionNoSearchDto.java

@@ -15,6 +15,8 @@ public class QuestionNoSearchDto {
 
     private String content;
 
+    private String questionType;
+
     public String getModule() {
         return module;
     }
@@ -70,4 +72,12 @@ public class QuestionNoSearchDto {
     public void setKeyword(String keyword) {
         this.keyword = keyword;
     }
+
+    public String getQuestionType() {
+        return questionType;
+    }
+
+    public void setQuestionType(String questionType) {
+        this.questionType = questionType;
+    }
 }

+ 12 - 0
server/gateway-api/src/main/java/com/qxgmat/dto/extend/QuestionExtendDto.java

@@ -3,6 +3,8 @@ package com.qxgmat.dto.extend;
 import com.nuliji.tools.annotation.Dto;
 import com.qxgmat.data.dao.entity.Question;
 
+import java.util.Date;
+
 @Dto(entity = Question.class)
 public class QuestionExtendDto {
 
@@ -18,6 +20,8 @@ public class QuestionExtendDto {
 
     private Integer collectionNumber;
 
+    private Date updateTime;
+
     public Integer getId() {
         return id;
     }
@@ -65,4 +69,12 @@ public class QuestionExtendDto {
     public void setQuestionType(String questionType) {
         this.questionType = questionType;
     }
+
+    public Date getUpdateTime() {
+        return updateTime;
+    }
+
+    public void setUpdateTime(Date updateTime) {
+        this.updateTime = updateTime;
+    }
 }

+ 10 - 0
server/gateway-api/src/main/java/com/qxgmat/dto/extend/UserPaperBaseExtendDto.java

@@ -15,6 +15,8 @@ public class UserPaperBaseExtendDto {
 
     private Integer finishTimes;
 
+    private Integer resetTimes;
+
     private String title;
 
     private Integer paperNo;
@@ -108,4 +110,12 @@ public class UserPaperBaseExtendDto {
     public void setFinishTimes(Integer finishTimes) {
         this.finishTimes = finishTimes;
     }
+
+    public Integer getResetTimes() {
+        return resetTimes;
+    }
+
+    public void setResetTimes(Integer resetTimes) {
+        this.resetTimes = resetTimes;
+    }
 }

+ 10 - 0
server/gateway-api/src/main/java/com/qxgmat/dto/extend/UserPaperDetailExtendDto.java

@@ -14,6 +14,8 @@ public class UserPaperDetailExtendDto {
 
     private Integer finishTimes;
 
+    private Integer resetTimes;
+
     private String title;
 
     private String paperModule;
@@ -87,4 +89,12 @@ public class UserPaperDetailExtendDto {
     public void setFinishTimes(Integer finishTimes) {
         this.finishTimes = finishTimes;
     }
+
+    public Integer getResetTimes() {
+        return resetTimes;
+    }
+
+    public void setResetTimes(Integer resetTimes) {
+        this.resetTimes = resetTimes;
+    }
 }

+ 10 - 0
server/gateway-api/src/main/java/com/qxgmat/dto/response/CommentDto.java

@@ -20,6 +20,8 @@ public class CommentDto {
 
     private Date createTime;
 
+    private Date updateTime;
+
     public Integer getId() {
         return id;
     }
@@ -67,4 +69,12 @@ public class CommentDto {
     public void setAvatar(String avatar) {
         this.avatar = avatar;
     }
+
+    public Date getUpdateTime() {
+        return updateTime;
+    }
+
+    public void setUpdateTime(Date updateTime) {
+        this.updateTime = updateTime;
+    }
 }

+ 3 - 3
server/gateway-api/src/main/java/com/qxgmat/dto/response/UserExaminationPaperDto.java

@@ -10,7 +10,7 @@ import com.qxgmat.dto.extend.UserReportExtendDto;
 public class UserExaminationPaperDto {
     private Integer id;
 
-    private Integer title;
+    private String title;
 
     private Integer totalScore;
 
@@ -76,11 +76,11 @@ public class UserExaminationPaperDto {
         this.id = id;
     }
 
-    public Integer getTitle() {
+    public String getTitle() {
         return title;
     }
 
-    public void setTitle(Integer title) {
+    public void setTitle(String title) {
         this.title = title;
     }
 

+ 10 - 10
server/gateway-api/src/main/java/com/qxgmat/dto/response/UserNoteQuestionDetailDto.java

@@ -18,7 +18,7 @@ public class UserNoteQuestionDetailDto {
 
     private QuestionNoExtendDto questionNo;
 
-    private String content;
+    private String questionContent;
 
     private String officialContent;
 
@@ -28,7 +28,7 @@ public class UserNoteQuestionDetailDto {
 
     private String qaContent;
 
-    private Date contentTime;
+    private Date questionTime;
 
     private Date officialTime;
 
@@ -78,12 +78,12 @@ public class UserNoteQuestionDetailDto {
         this.questionNo = questionNo;
     }
 
-    public String getContent() {
-        return content;
+    public String getQuestionContent() {
+        return questionContent;
     }
 
-    public void setContent(String content) {
-        this.content = content;
+    public void setQuestionContent(String questionContent) {
+        this.questionContent = questionContent;
     }
 
     public String getOfficialContent() {
@@ -110,12 +110,12 @@ public class UserNoteQuestionDetailDto {
         this.associationContent = associationContent;
     }
 
-    public Date getContentTime() {
-        return contentTime;
+    public Date getQuestionTime() {
+        return questionTime;
     }
 
-    public void setContentTime(Date contentTime) {
-        this.contentTime = contentTime;
+    public void setQuestionTime(Date questionTime) {
+        this.questionTime = questionTime;
     }
 
     public Date getOfficialTime() {

+ 10 - 10
server/gateway-api/src/main/java/com/qxgmat/dto/response/UserNoteQuestionInfoDto.java

@@ -21,7 +21,7 @@ public class UserNoteQuestionInfoDto {
 
     private QuestionExtendDto question;
 
-    private String content;
+    private String questionContent;
 
     private String officialContent;
 
@@ -31,7 +31,7 @@ public class UserNoteQuestionInfoDto {
 
     private String qaContent;
 
-    private Date contentTime;
+    private Date questionTime;
 
     private Date officialTime;
 
@@ -81,12 +81,12 @@ public class UserNoteQuestionInfoDto {
         this.questionNo = questionNo;
     }
 
-    public String getContent() {
-        return content;
+    public String getQuestionContent() {
+        return questionContent;
     }
 
-    public void setContent(String content) {
-        this.content = content;
+    public void setQuestionContent(String questionContent) {
+        this.questionContent = questionContent;
     }
 
     public String getOfficialContent() {
@@ -113,12 +113,12 @@ public class UserNoteQuestionInfoDto {
         this.associationContent = associationContent;
     }
 
-    public Date getContentTime() {
-        return contentTime;
+    public Date getQuestionTime() {
+        return questionTime;
     }
 
-    public void setContentTime(Date contentTime) {
-        this.contentTime = contentTime;
+    public void setQuestionTime(Date questionTime) {
+        this.questionTime = questionTime;
     }
 
     public Date getOfficialTime() {

+ 10 - 0
server/gateway-api/src/main/java/com/qxgmat/dto/response/UserOrderDetailDto.java

@@ -31,6 +31,8 @@ public class UserOrderDetailDto {
 
     private Date createTime;
 
+    private Date payTime;
+
     private BigDecimal invoiceMoney;
 
     private Boolean hasInvoice;
@@ -142,4 +144,12 @@ public class UserOrderDetailDto {
     public void setProductTypes(String[] productTypes) {
         this.productTypes = productTypes;
     }
+
+    public Date getPayTime() {
+        return payTime;
+    }
+
+    public void setPayTime(Date payTime) {
+        this.payTime = payTime;
+    }
 }

+ 1 - 1
server/gateway-api/src/main/java/com/qxgmat/service/UserPaperService.java

@@ -194,7 +194,7 @@ public class UserPaperService extends AbstractService {
                         .andEqualTo("userId", userId)
                         .andEqualTo("paperOrigin", PaperOrigin.EXAMINATION.key)
                         .andIn("originId", ids)
-                        .andEqualTo("qxCatNo", no)
+                        .andEqualTo("qxCat", no)
         );
         return select(userPaperMapper, example);
     }

+ 0 - 0
server/gateway-api/src/main/java/com/qxgmat/service/UsersService.java


Some files were not shown because too many files changed in this diff