Parcourir la source

feat(h5): 机经页面

Go il y a 4 ans
Parent
commit
d4146662f1
100 fichiers modifiés avec 5587 ajouts et 3083 suppressions
  1. 26 4
      front/.eslintrc
  2. 2350 2323
      front/package-lock.json
  3. 1 0
      front/package.json
  4. 13 1
      front/project/Constant.js
  5. 2 2
      front/project/admin/local.json
  6. 2 2
      front/project/admin/routes/course/invoice/page.js
  7. 28 2
      front/project/admin/routes/setting/comment/page.js
  8. 0 3
      front/project/admin/routes/setting/faq/index.less
  9. 15 0
      front/project/admin/routes/setting/faqConsult/index.js
  10. 3 0
      front/project/admin/routes/setting/faqConsult/index.less
  11. 64 94
      front/project/admin/routes/setting/faq/page.js
  12. 3 3
      front/project/admin/routes/setting/faq/index.js
  13. 3 0
      front/project/admin/routes/setting/faqSystem/index.less
  14. 183 0
      front/project/admin/routes/setting/faqSystem/page.js
  15. 3 2
      front/project/admin/routes/setting/index.js
  16. 19 14
      front/project/admin/routes/user/service/page.js
  17. 1 0
      front/project/h5/app.js
  18. 23 7
      front/project/h5/components/Input/index.js
  19. 15 0
      front/project/h5/components/Input/index.less
  20. 2 2
      front/project/h5/components/Radio/index.js
  21. 6 3
      front/project/h5/local.json
  22. 101 27
      front/project/h5/routes/page/bind/page.js
  23. 0 68
      front/project/h5/routes/page/changeHistory/index.less
  24. 0 67
      front/project/h5/routes/page/changeHistory/page.js
  25. 0 48
      front/project/h5/routes/page/data/index.less
  26. 0 46
      front/project/h5/routes/page/data/page.js
  27. 3 3
      front/project/h5/routes/page/data/index.js
  28. 5 0
      front/project/h5/routes/page/demo/index.less
  29. 44 0
      front/project/h5/routes/page/demo/page.js
  30. 14 4
      front/project/h5/routes/page/home/page.js
  31. 4 3
      front/project/h5/routes/page/machine/index.js
  32. 5 0
      front/project/h5/routes/page/id/index.less
  33. 16 0
      front/project/h5/routes/page/id/page.js
  34. 119 4
      front/project/h5/routes/page/identity/page.js
  35. 3 6
      front/project/h5/routes/page/index.js
  36. 82 9
      front/project/h5/routes/page/invitation/page.js
  37. 1 1
      front/project/h5/routes/page/login/index.js
  38. 10 5
      front/project/h5/routes/page/login/page.js
  39. 0 55
      front/project/h5/routes/page/machine/page.js
  40. 1 0
      front/project/h5/routes/page/open/index.js
  41. 16 2
      front/project/h5/routes/page/open/page.js
  42. 46 1
      front/project/h5/routes/page/study/index.less
  43. 48 2
      front/project/h5/routes/page/study/page.js
  44. 0 9
      front/project/h5/routes/page/update/index.js
  45. 0 63
      front/project/h5/routes/page/update/index.less
  46. 0 60
      front/project/h5/routes/page/update/page.js
  47. 61 1
      front/project/h5/routes/product/dataHistory/index.less
  48. 52 4
      front/project/h5/routes/product/dataHistory/page.js
  49. 6 2
      front/project/h5/routes/product/index.js
  50. 0 0
      front/project/h5/routes/product/main/index.js
  51. 0 0
      front/project/h5/routes/product/main/index.less
  52. 5 5
      front/project/h5/routes/page/product/page.js
  53. 1 1
      front/project/h5/routes/product/serviceDetail/index.js
  54. 1 1
      front/project/h5/routes/textbook/detail/index.js
  55. 18 0
      front/project/h5/routes/textbook/detail/index.less
  56. 132 23
      front/project/h5/routes/textbook/detail/page.js
  57. 3 1
      front/project/h5/routes/textbook/index.js
  58. 0 3
      front/project/h5/routes/textbook/index/index.less
  59. 66 1
      front/project/h5/routes/textbook/library/index.less
  60. 56 2
      front/project/h5/routes/textbook/library/page.js
  61. 0 0
      front/project/h5/routes/textbook/main/index.js
  62. 2 1
      front/project/h5/routes/page/machine/index.less
  63. 110 0
      front/project/h5/routes/textbook/main/page.js
  64. 61 0
      front/project/h5/stores/common.js
  65. 5 1
      front/project/h5/stores/index.js
  66. 95 0
      front/project/h5/stores/main.js
  67. 299 0
      front/project/h5/stores/my.js
  68. 24 0
      front/project/h5/stores/textbook.js
  69. 30 8
      front/project/h5/stores/user.js
  70. 4 1
      front/project/www/app.js
  71. 1 1
      front/project/www/routes/page/home/page.js
  72. 4 3
      front/project/h5/routes/page/changeHistory/index.js
  73. 5 0
      front/project/www/routes/page/id/index.less
  74. 4 0
      front/project/h5/routes/textbook/index/page.js
  75. 3 2
      front/project/www/stores/common.js
  76. 8 0
      front/project/www/stores/main.js
  77. 0 8
      front/project/www/stores/my.js
  78. 12 0
      front/project/www/stores/textbook.js
  79. 16 0
      front/project/www/stores/user.js
  80. 3 3
      front/src/containers/Page.js
  81. 1 1
      front/src/routes/index.js
  82. 12 0
      front/src/services/Tools.js
  83. 9 0
      front/src/static/sha1.js
  84. 24 0
      server/data/src/main/java/com/qxgmat/data/constants/enums/TopicQuality.java
  85. 18 0
      server/data/src/main/java/com/qxgmat/data/constants/enums/module/FaqModule.java
  86. 7 0
      server/data/src/main/java/com/qxgmat/data/dao/TextbookTopicMapper.java
  87. 7 0
      server/data/src/main/java/com/qxgmat/data/dao/UserTextbookEnrollMapper.java
  88. 7 0
      server/data/src/main/java/com/qxgmat/data/dao/UserTextbookFeedbackMapper.java
  89. 52 17
      server/data/src/main/java/com/qxgmat/data/dao/entity/Faq.java
  90. 8 8
      server/data/src/main/java/com/qxgmat/data/dao/entity/TextbookLibrary.java
  91. 396 0
      server/data/src/main/java/com/qxgmat/data/dao/entity/TextbookTopic.java
  92. 70 0
      server/data/src/main/java/com/qxgmat/data/dao/entity/User.java
  93. 35 0
      server/data/src/main/java/com/qxgmat/data/dao/entity/UserCourseAppointment.java
  94. 35 35
      server/data/src/main/java/com/qxgmat/data/dao/entity/UserService.java
  95. 160 0
      server/data/src/main/java/com/qxgmat/data/dao/entity/UserTextbookEnroll.java
  96. 335 0
      server/data/src/main/java/com/qxgmat/data/dao/entity/UserTextbookFeedback.java
  97. 4 3
      server/data/src/main/java/com/qxgmat/data/dao/mapping/FaqMapper.xml
  98. 2 2
      server/data/src/main/java/com/qxgmat/data/dao/mapping/TextbookLibraryMapper.xml
  99. 38 0
      server/data/src/main/java/com/qxgmat/data/dao/mapping/TextbookTopicMapper.xml
  100. 0 0
      server/data/src/main/java/com/qxgmat/data/dao/mapping/UserCourseAppointmentMapper.xml

+ 26 - 4
front/.eslintrc

@@ -1,11 +1,30 @@
 {
   "parser": "babel-eslint",
-  "extends": ["standard-react", "airbnb-base"],
-  "plugins": ["react", "import"],
+  "extends": [
+    "standard-react",
+    "airbnb-base"
+  ],
+  "plugins": [
+    "react",
+    "import"
+  ],
   "settings": {
     "import/resolver": {
       "alias": {
-        "map": [["@src", "./src"], ["@project", "./project/admin"], ["@components", "./components"]]
+        "map": [
+          [
+            "@src",
+            "./src"
+          ],
+          [
+            "@project",
+            "./project/admin"
+          ],
+          [
+            "@components",
+            "./components"
+          ]
+        ]
       }
     }
   },
@@ -30,6 +49,9 @@
     "toLink": false,
     "openLink": false,
     "WxLogin": false,
+    "WeixinJSBridge": false,
+    "sha1": false,
+    "wx": false,
     "CKEDITOR": false,
     "Masonry": false
   },
@@ -56,4 +78,4 @@
     "global-require": "off",
     "import/no-extraneous-dependencies": "off"
   }
-}
+}

Fichier diff supprimé car celui-ci est trop grand
+ 2350 - 2323
front/package-lock.json


+ 1 - 0
front/package.json

@@ -76,6 +76,7 @@
     "echarts-for-react": "^2.0.15-beta.0",
     "fastclick": "^1.0.6",
     "history": "^4.7.2",
+    "js-sha1": "^0.6.0",
     "moment": "^2.22.2",
     "promise": "^7.1.1",
     "react": "^16.6.3",

+ 13 - 1
front/project/Constant.js

@@ -1,4 +1,8 @@
-export const UserUrl = 'http://www.baidu.com/';
+export const UserUrl = 'http://www.baidu.com';
+
+export const H5Url = 'http://127.0.0.1:3000';
+
+export const WechatAppId = 'wxbee75af2ece94ed7';
 
 export const QuestionDifficult = [{ label: 'easy', value: 'easy' }, { label: 'medium', value: 'medium' }, { label: 'hard', value: 'hard' }];
 
@@ -67,6 +71,14 @@ export const CrowdList = [{ label: '新手', value: 'novice' }, { label: '非新
 export const CourseStatus = [{ label: '未开通', value: 0 }, { label: '学习中', value: 1 }, { label: '已到期', value: 2 }];
 
 export const InvoiceType = [{ label: '个人', value: 'personal' }, { label: '企业', value: 'enterprise' }];
+
+export const TextbookQuality = [{ label: '残缺', value: 'incomplete' }, { label: '较完整', value: 'morecomplete' }, { label: '完整', value: 'complete' }];
+
+export const TextbookSubject = [{ label: '数学', value: 'quant' }, { label: '语文', value: 'verbal' }, { label: '逻辑', value: 'rc' }];
+
+export const MobileArea = ['+86', '+1'].map(row => {
+  return { label: row, value: row };
+});
 // const content = {
 //   steps: [{
 //     title: '',

+ 2 - 2
front/project/admin/local.json

@@ -4,7 +4,7 @@
     "scripts": [],
     "proxy": [
       {
-        "target": "http://qianxing-admin.nuliji.com",
+        "target": "http://127.0.0.1:8888",
         "from": "/api",
         "to": "/admin"
       }
@@ -12,4 +12,4 @@
   },
   "test": {},
   "production": {}
-}
+}

+ 2 - 2
front/project/admin/routes/course/invoice/page.js

@@ -108,7 +108,7 @@ export default class extends Page {
   downloadAction() {
     const { selectedKeys } = this.state;
     asyncDelConfirm('下载确认', '是否下载选中记录?', () => {
-      return Promise.all(selectedKeys.map(row => User.editInvoice({ id: row, isFinish: 1 }))).then(() => {
+      return User.downloadInvoice({ ids: selectedKeys }).then(() => {
         asyncSMessage('操作成功!');
         this.refresh();
       });
@@ -118,7 +118,7 @@ export default class extends Page {
   finishAction() {
     const { selectedKeys } = this.state;
     asyncDelConfirm('开票确认', '是否开票选中记录?', () => {
-      return User.editInvoice({ ids: selectedKeys }).then(() => {
+      return User.finishInvoice({ ids: selectedKeys }).then(() => {
         asyncSMessage('操作成功!');
         this.refresh();
       });

+ 28 - 2
front/project/admin/routes/setting/comment/page.js

@@ -53,6 +53,28 @@ export default class extends Page {
       type: 'textarea',
       name: '评价内容',
     }];
+    this.userItemList = [{
+      key: 'id',
+      type: 'hidden',
+    }, {
+      key: 'channel',
+      type: 'select',
+      allowClear: true,
+      name: '频道',
+      select: ChannelModule,
+      placeholder: '请选择',
+    }, {
+      key: 'position',
+      type: 'select',
+      allowClear: true,
+      name: '位置',
+      select: ChannelModule,
+      placeholder: '请选择',
+    }, {
+      key: 'content',
+      type: 'textarea',
+      name: '评价内容',
+    }];
     this.filterForm = [{
       key: 'channel',
       type: 'select',
@@ -170,7 +192,11 @@ export default class extends Page {
   }
 
   editAction(row) {
-    asyncForm('编辑', this.itemList, row, data => {
+    let item = this.itemList;
+    if (row.userId) {
+      item = this.userItemList;
+    }
+    asyncForm('编辑', item, row, data => {
       return System.editComment(data).then(() => {
         asyncSMessage('编辑成功!');
         this.refresh();
@@ -180,7 +206,7 @@ export default class extends Page {
 
   special(row, isSpecial) {
     System.editComment({ id: row.id, isSpecial }).then(() => {
-      asyncSMessage('编辑成功!');
+      asyncSMessage('操作成功!');
       this.refresh();
     });
   }

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

@@ -1,3 +0,0 @@
-@charset "utf-8";
-
-#setting-faq {}

+ 15 - 0
front/project/admin/routes/setting/faqConsult/index.js

@@ -0,0 +1,15 @@
+import module from '../../module';
+import group from '../group';
+
+export default {
+  path: '/setting/faq/consult',
+  key: 'setting-faq-consult',
+  title: '咨询管理',
+  needLogin: true,
+  module,
+  group,
+  index: true,
+  component() {
+    return import('./page');
+  },
+};

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

@@ -0,0 +1,3 @@
+@charset "utf-8";
+
+#setting-faq-consult {}

+ 64 - 94
front/project/admin/routes/setting/faq/page.js

@@ -5,22 +5,16 @@ import Block from '@src/components/Block';
 import FilterLayout from '@src/layouts/FilterLayout';
 import ActionLayout from '@src/layouts/ActionLayout';
 import TableLayout from '@src/layouts/TableLayout';
-import { getMap, bindSearch, formatDate } from '@src/services/Tools';
+import { getMap, formatDate } from '@src/services/Tools';
 import { asyncSMessage, asyncForm } from '@src/services/AsyncTools';
 import { ChannelModule, SwitchSelect, AskStatus } from '../../../../Constant';
 import { System } from '../../../stores/system';
-import { User } from '../../../stores/user';
 
 const SwitchSelectMap = getMap(SwitchSelect, 'value', 'label');
 const ChannelModuleMap = getMap(ChannelModule, 'value', 'label');
 const AskStatusMap = getMap(AskStatus, 'value', 'label');
 export default class extends Page {
   init() {
-    this.actionList = [{
-      key: 'add',
-      type: 'primary',
-      name: '创建',
-    }];
     this.formF = null;
     this.itemList = [{
       key: 'id',
@@ -76,15 +70,7 @@ export default class extends Page {
       select: ChannelModule,
       placeholder: '请选择',
     }, {
-      key: 'userId',
-      type: 'select',
-      allowClear: true,
-      name: '用户',
-      select: [],
-      number: true,
-      placeholder: '请输入',
-    }, {
-      key: 'status',
+      key: 'answerStatus',
       type: 'select',
       allowClear: true,
       number: true,
@@ -98,91 +84,76 @@ export default class extends Page {
       number: true,
       select: SwitchSelect,
     }];
-    this.columns = [
-      {
-        title: '频道',
-        dataIndex: 'channel',
-        render: (text, record) => {
-          return ChannelModuleMap[record.channel];
-        },
+    this.columns = [{
+      title: '频道',
+      dataIndex: 'channel',
+      render: (text, record) => {
+        return ChannelModuleMap[record.channel];
+      },
+    }, {
+      title: '位置',
+      dataIndex: 'position',
+    }, {
+      title: '提问时间',
+      dataIndex: 'createTime',
+      render: (text) => {
+        return formatDate(text);
       },
-      {
-        title: '位置',
-        dataIndex: 'position',
+    }, {
+      title: '提问者',
+      dataIndex: 'user',
+      render: (text, record) => {
+        if (record.isSystem) return '系统创建';
+        if (!record.userId) return '未注册';
+        return text ? text.nickname : '';
       },
-      {
-        title: '提问时间',
-        dataIndex: 'createTime',
-        render: (text) => {
-          return formatDate(text);
-        },
+    }, {
+      title: '问题摘要',
+      dataIndex: 'content',
+    }, {
+      title: '回答状态',
+      dataIndex: 'answerStatus',
+      render: (text) => {
+        return AskStatusMap[text] || '';
       },
-      {
-        title: '提问者',
-        dataIndex: 'user',
-        render: (text, record) => {
-          let extend = '';
-          if (record.isSystem) extend = '系统创建';
-          else if (!record.userId) extend = '未注册';
-          return `${text.nickname || record.nickname}${extend ? `(${extend})` : ''}`;
-        },
+    }, {
+      title: '精选',
+      dataIndex: 'isSpecial',
+      render: (text, record) => {
+        return record.status > 0 ? SwitchSelectMap[text] || text : '-';
       },
-      {
-        title: '问题摘要',
-        dataIndex: 'content',
-      }, {
-        title: '回答状态',
-        dataIndex: 'status',
-        render: (text) => {
-          return AskStatusMap[text] || '';
-        },
-      }, {
-        title: '精选',
-        dataIndex: 'isSpecial',
-        render: (text, record) => {
-          return record.status > 0 ? SwitchSelectMap[text] || text : '-';
-        },
-      }, {
-        title: '操作',
-        dataIndex: 'handler',
-        render: (text, record) => {
-          return <div className="table-button">
-            {(
-              <a onClick={() => {
-                this.editAction(record);
-              }}>编辑</a>
-            )}
-            {!record.isSystem && record.status === 0 && (
-              <a onClick={() => {
-                this.answerAction(record);
-              }}>回复</a>
-            )}
-            {!!record.isSpecial && (
-              <a onClick={() => {
-                this.special(record, 0);
-              }}>取消精选</a>
-            )}
-            {!record.isSpecial && (
-              <a onClick={() => {
-                this.special(record, 1);
-              }}>精选</a>
-            )}
-          </div>;
-        },
+    }, {
+      title: '操作',
+      dataIndex: 'handler',
+      render: (text, record) => {
+        return <div className="table-button">
+          {(
+            <a onClick={() => {
+              this.editAction(record);
+            }}>编辑</a>
+          )}
+          {!record.isSystem && record.status === 0 && (
+            <a onClick={() => {
+              this.answerAction(record);
+            }}>回复</a>
+          )}
+          {!!record.isSpecial && (
+            <a onClick={() => {
+              this.special(record, 0);
+            }}>取消精选</a>
+          )}
+          {!record.isSpecial && (
+            <a onClick={() => {
+              this.special(record, 1);
+            }}>精选</a>
+          )}
+        </div>;
       },
-    ];
-    bindSearch(this.filterForm, 'userId', this, (search) => {
-      return User.list(search);
-    }, (row) => {
-      return {
-        title: `${row.nickname}(${row.mobile})`,
-        value: row.id,
-      };
-    }, this.state.search.userId ? Number(this.state.search.userId) : [], null);
+    }];
   }
 
   initData() {
-    System.listFAQ(this.state.search).then(result => {
+    System.listFAQ(Object.assign({ faqModule: 'consult' }, this.state.search)).then(result => {
       this.setTableData(result.list, result.total);
     });
   }
@@ -243,7 +214,6 @@ export default class extends Page {
         onAction={key => this.onAction(key)}
       />
       <TableLayout
-        select
         columns={this.columns}
         list={this.state.list}
         pagination={this.state.page}

+ 3 - 3
front/project/admin/routes/setting/faq/index.js

@@ -2,9 +2,9 @@ import module from '../../module';
 import group from '../group';
 
 export default {
-  path: '/setting/faq',
-  key: 'setting-faq',
-  title: 'FAQ设置',
+  path: '/setting/faq/system',
+  key: 'setting-faq-system',
+  title: 'FAQ管理',
   needLogin: true,
   module,
   group,

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

@@ -0,0 +1,3 @@
+@charset "utf-8";
+
+#setting-faq-system {}

+ 183 - 0
front/project/admin/routes/setting/faqSystem/page.js

@@ -0,0 +1,183 @@
+import React from 'react';
+import './index.less';
+import Page from '@src/containers/Page';
+import Block from '@src/components/Block';
+import FilterLayout from '@src/layouts/FilterLayout';
+import ActionLayout from '@src/layouts/ActionLayout';
+import TableLayout from '@src/layouts/TableLayout';
+import { getMap } from '@src/services/Tools';
+import { asyncSMessage, asyncForm } from '@src/services/AsyncTools';
+import { ChannelModule, SwitchSelect, AskStatus } from '../../../../Constant';
+import { System } from '../../../stores/system';
+
+const SwitchSelectMap = getMap(SwitchSelect, 'value', 'label');
+const ChannelModuleMap = getMap(ChannelModule, 'value', 'label');
+export default class extends Page {
+  init() {
+    this.actionList = [{
+      key: 'add',
+      type: 'primary',
+      name: '创建',
+    }];
+    this.formF = null;
+    this.itemList = [{
+      key: 'id',
+      type: 'hidden',
+    }, {
+      key: 'channel',
+      type: 'select',
+      allowClear: true,
+      name: '频道',
+      select: ChannelModule,
+      placeholder: '请选择',
+    }, {
+      key: 'position',
+      type: 'select',
+      allowClear: true,
+      name: '位置',
+      select: ChannelModule,
+      placeholder: '请选择',
+    }, {
+      key: 'content',
+      type: 'textarea',
+      name: '用户留言',
+    }, {
+      key: 'answer',
+      type: 'textarea',
+      name: '编辑回复',
+    }];
+    this.filterForm = [{
+      key: 'channel',
+      type: 'select',
+      allowClear: true,
+      name: '频道',
+      select: ChannelModule,
+      placeholder: '请选择',
+    }, {
+      key: 'position',
+      type: 'select',
+      allowClear: true,
+      name: '位置',
+      select: ChannelModule,
+      placeholder: '请选择',
+    }, {
+      key: 'answerStatus',
+      type: 'select',
+      allowClear: true,
+      number: true,
+      name: '状态',
+      select: AskStatus,
+    }, {
+      key: 'isSpecial',
+      type: 'select',
+      allowClear: true,
+      name: '精选',
+      number: true,
+      select: SwitchSelect,
+    }];
+    this.columns = [{
+      title: '频道',
+      dataIndex: 'channel',
+      render: (text, record) => {
+        return ChannelModuleMap[record.channel];
+      },
+    }, {
+      title: '位置',
+      dataIndex: 'position',
+    }, {
+      title: '问题摘要',
+      dataIndex: 'content',
+    }, {
+      title: '精选',
+      dataIndex: 'isSpecial',
+      render: (text, record) => {
+        return record.status > 0 ? SwitchSelectMap[text] || text : '-';
+      },
+    }, {
+      title: '操作',
+      dataIndex: 'handler',
+      render: (text, record) => {
+        return <div className="table-button">
+          {(
+            <a onClick={() => {
+              this.editAction(record);
+            }}>编辑</a>
+          )}
+          {!!record.isSpecial && (
+            <a onClick={() => {
+              this.special(record, 0);
+            }}>取消精选</a>
+          )}
+          {!record.isSpecial && (
+            <a onClick={() => {
+              this.special(record, 1);
+            }}>精选</a>
+          )}
+        </div>;
+      },
+    }];
+  }
+
+  initData() {
+    System.listFAQ(Object.assign({ faqModule: 'system' }, this.state.search)).then(result => {
+      this.setTableData(result.list, result.total);
+    });
+  }
+
+  addAction() {
+    asyncForm('创建', this.itemList, {}, data => {
+      data.faqModule = 'system';
+      return System.addFAQ(data).then(() => {
+        asyncSMessage('添加成功!');
+        this.refresh();
+      });
+    }).then(component => {
+      this.formF = component;
+    });
+  }
+
+  editAction(row) {
+    asyncForm('编辑', this.itemList, row, data => {
+      data.faqModule = 'system';
+      return System.editFAQ(data).then(() => {
+        asyncSMessage('编辑成功!');
+        this.refresh();
+      });
+    }).then(component => {
+      this.formF = component;
+    });
+  }
+
+  special(row, isSpecial) {
+    System.editFAQ({ id: row.id, isSpecial }).then(() => {
+      asyncSMessage('编辑成功!');
+      this.refresh();
+    });
+  }
+
+  renderView() {
+    return <Block flex>
+      <FilterLayout
+        show
+        itemList={this.filterForm}
+        data={this.state.search}
+        onChange={data => {
+          this.search(data);
+        }} />
+      <ActionLayout
+        itemList={this.actionList}
+        selectedKeys={this.state.selectedKeys}
+        onAction={key => this.onAction(key)}
+      />
+      <TableLayout
+        columns={this.columns}
+        list={this.state.list}
+        pagination={this.state.page}
+        loading={this.props.core.loading}
+        onChange={(pagination, filters, sorter) => this.tableChange(pagination, filters, sorter)}
+        onSelect={(keys, rows) => this.tableSelect(keys, rows)}
+        selectedKeys={this.state.selectedKeys}
+      />
+    </Block>;
+  }
+}

+ 3 - 2
front/project/admin/routes/setting/index.js

@@ -5,8 +5,9 @@ import index from './index/';
 import place from './place';
 import time from './time';
 import comment from './comment';
-import faq from './faq';
+import faqConsult from './faqConsult';
+import faqSystem from './faqSystem';
 import message from './message';
 import promote from './promote';
 
-export default [struct, tips, service, index, place, time, comment, faq, message, promote];
+export default [struct, tips, service, index, place, time, comment, faqConsult, faqSystem, message, promote];

+ 19 - 14
front/project/admin/routes/user/service/page.js

@@ -7,7 +7,7 @@ import ActionLayout from '@src/layouts/ActionLayout';
 import TableLayout from '@src/layouts/TableLayout';
 import { getMap, formatDate, formatMoney, bindSearch } from '@src/services/Tools';
 import { asyncSMessage, asyncForm } from '@src/services/AsyncTools';
-import { ServiceParamMap, ServiceKey, ServiceSource } from '../../../../Constant';
+import { ServiceParamMap, ServiceKey, ServiceSource, MobileArea } from '../../../../Constant';
 import { User } from '../../../stores/user';
 
 const ServiceKeyMap = getMap(ServiceKey, 'value', 'label');
@@ -33,6 +33,11 @@ export default class extends Page {
       key: 'id',
       type: 'hidden',
     }, {
+      key: 'area',
+      type: 'select',
+      name: '国际码',
+      select: MobileArea,
+    }, {
       key: 'mobile',
       type: 'input',
       name: '手机号',
@@ -45,9 +50,9 @@ export default class extends Page {
             this.timeout = null;
           }
           this.timeout = setTimeout(() => {
-            User.validMobile({ mobile: value }).then(result => {
+            User.validMobile({ area: this.formF.getFieldValue(), mobile: value }).then(result => {
               this.mobile = value;
-              this.itemList[1].suffix = result ? '已注册' : '未注册';
+              this.itemList[1].suffix = !result ? '已注册' : '未注册';
               this.formF.setFieldsValue({ load: true });
             });
           }, 1500);
@@ -176,17 +181,17 @@ export default class extends Page {
     });
   }
 
-  editAction(row) {
-    this.itemList[1].disabled = true;
-    asyncForm('编辑', this.itemList, row, data => {
-      return User.editService(data).then(() => {
-        asyncSMessage('编辑成功!');
-        this.refresh();
-      });
-    }).then(component => {
-      this.formF = component;
-    });
-  }
+  // editAction(row) {
+  //   this.itemList[1].disabled = true;
+  //   asyncForm('编辑', this.itemList, row, data => {
+  //     return User.editService(data).then(() => {
+  //       asyncSMessage('编辑成功!');
+  //       this.refresh();
+  //     });
+  //   }).then(component => {
+  //     this.formF = component;
+  //   });
+  // }
 
   stopAction(id) {
     return User.editService({ id, isStop: 1 }).then(() => {

+ 1 - 0
front/project/h5/app.js

@@ -7,6 +7,7 @@ export default class extends Component {
     super(props);
     const state = {};
     this.state = state;
+    // 判断链接参数是否有邀请,如果有,保存
   }
 
   render() {

+ 23 - 7
front/project/h5/components/Input/index.js

@@ -4,12 +4,12 @@ import Icon from '../Icon';
 
 export default class Input extends Component {
   render() {
-    const { className = '', onChange, placeholder, error, left, right } = this.props;
+    const { className = '', onChange, placeholder, value, error, left, right } = this.props;
     return (
       <div className={`g-input-container ${className}`}>
         <div className={`g-input-wrapper ${error ? 'error' : ''}`}>
           {left && <div className="g-input-left">{left}</div>}
-          <input className="g-input" placeholder={placeholder} onChange={data => onChange && onChange(data)} />
+          <input className="g-input" placeholder={placeholder} value={value} onChange={data => onChange && onChange(data)} />
           {right && <div className="g-input-right">{right}</div>}
         </div>
         <div hidden={!error} className="g-input-error">
@@ -21,20 +21,33 @@ export default class Input extends Component {
 }
 
 export class SelectInput extends Component {
+  constructor(props) {
+    super(props);
+    this.state = { showSelect: false };
+  }
+
   render() {
-    const { className = '', onChange, placeholder, value, selectValue, onSelect } = this.props;
+    const { showSelect } = this.state;
+    const { className = '', onChange, placeholder, value, error, selectValue, select, onSelect } = this.props;
     return (
       <Input
         className={className}
         left={
-          <span className="g-input-left-select" onClick={() => onSelect && onSelect()}>
+          <span className="g-input-left-select" onClick={() => this.setState({ showSelect: !showSelect })}>
             {selectValue}
-            <Icon type="down" />
+            <Icon type={showSelect ? 'up' : 'down'} />
+            {showSelect && <ul className="select-list">{select.map((row) => {
+              return <li onClick={() => {
+                this.setState({ showSelect: false });
+                if (onSelect) onSelect(row.value);
+              }}>{row.label}</li>;
+            })}</ul>}
           </span>
         }
         value={value}
         placeholder={placeholder}
         onChange={data => onChange && onChange(data)}
+        error={error}
       />
     );
   }
@@ -54,9 +67,12 @@ export class VerificationInput extends Component {
   onSend() {
     const { onSend, time = 60 } = this.props;
     if (onSend) {
-      onSend();
+      onSend()
+        .then(() => {
+          console.log(time);
+          this.setTime(time);
+        });
     }
-    this.setTime(time);
   }
 
   setTime(time) {

+ 15 - 0
front/project/h5/components/Input/index.less

@@ -19,6 +19,8 @@
     }
 
     .g-input-left {
+      position: relative;
+
       .g-input-left-select {
         height: 28px;
         border-right: 1px solid #eee;
@@ -55,4 +57,17 @@
     height: 20px;
     line-height: 20px;
   }
+
+  .select-list {
+    top: 36px;
+    width: 61px;
+    border: 1px solid #EAEDF2;
+    max-height: 200px;
+    overflow-y: auto;
+    list-style: none;
+    margin: 0;
+    padding: 5px 10px;
+    position: absolute;
+    background: #fff;
+  }
 }

+ 2 - 2
front/project/h5/components/Radio/index.js

@@ -21,12 +21,12 @@ export class SpecialRadioGroup extends Component {
   }
 
   render() {
-    const { list = [], value } = this.props;
+    const { list = [], value, values = [] } = this.props;
     return (
       <div className="g-special-radio-group">
         {list.map(item => {
           return (
-            <SpecialRadio checked={value === item.key} onClick={() => this.onClickItem(item.key)}>
+            <SpecialRadio checked={value === item.key || values.indexOf(item.key) >= 0} onClick={() => this.onClickItem(item.key)}>
               {item.label}
             </SpecialRadio>
           );

+ 6 - 3
front/project/h5/local.json

@@ -1,9 +1,12 @@
 {
   "development": {
-    "scripts": [],
+    "scripts": [
+      "http://res.wx.qq.com/open/js/jweixin-1.2.0.js",
+      "/sha1.js"
+    ],
     "proxy": [
       {
-        "target": "http://qianxing.nuliji.com",
+        "target": "http://127.0.0.1:8888",
         "from": "/api",
         "to": "/api"
       }
@@ -15,4 +18,4 @@
   "production": {
     "scripts": []
   }
-}
+}

+ 101 - 27
front/project/h5/routes/page/bind/page.js

@@ -1,43 +1,117 @@
 import React from 'react';
 import './index.less';
 import Page from '@src/containers/Page';
+import { asyncSMessage } from '@src/services/AsyncTools';
+import { MobileArea } from '../../../../Constant';
 import Input, { SelectInput, VerificationInput } from '../../../components/Input';
 import Button from '../../../components/Button';
-import { SpecialRadioGroup, RadioGroup } from '../../../components/Radio';
-import Tag from '../../../components/Tag';
-import Checkbox from '../../../components/CheckBox';
-import Money from '../../../components/Money';
-import { Block, TopBlock, TagBlock, LinkBlock } from '../../../components/Block';
+import { User } from '../../../stores/user';
+import { My } from '../../../stores/my';
+import { Common } from '../../../stores/common';
 
 export default class extends Page {
-  init() {}
+  initState() {
+    return {
+      data: { mobile: '', area: MobileArea[0].value, email: '' },
+    };
+  }
+
+  init() { }
+
+  changeData(field, value) {
+    let { data } = this.state;
+    data = data || {};
+    data[field] = value;
+    this.setState({ data });
+  }
+
+  validMobile() {
+    const { data } = this.state;
+    const { area, mobile } = data;
+    if (area === '' || mobile === '') return;
+    User.validWechat(area, mobile)
+      .then(result => {
+        if (result) {
+          this.setState({ mobileError: '' });
+          return User.validMobile(area, mobile)
+            .then(r => {
+              this.setState({ needEmail: r });
+            });
+        }
+        this.setState({ needEmail: false });
+        return Promise.reject(new Error('该手机已绑定其他账号,请更换手机号码'));
+      })
+      .catch(err => {
+        this.setState({ mobileError: err.message });
+      });
+  }
+
+  sendValid() {
+    const { data, mobileError } = this.state;
+    const { area, mobile } = data;
+    if (area === '' || mobile === '' || mobileError !== '') return Promise.reject();
+    return Common.sendSms(area, mobile)
+      .then((result) => {
+        if (result) {
+          asyncSMessage('发送成功');
+          this.setState({ mobileError: '', validError: '' });
+        } else {
+          throw new Error('发送失败');
+        }
+      })
+      .catch(err => {
+        this.setState({ mobileError: err.message });
+        throw err;
+      });
+  }
+
+  submit() {
+    const { data, needEmail, mobileError, validError } = this.state;
+    const { area, mobile, mobileVerifyCode, email } = data;
+    if (mobileError !== '' || validError !== '') return;
+    if (area === '' || mobile === '' || mobileVerifyCode === '') return;
+    if (needEmail && email === '') return;
+    User.bind(area, mobile, mobileVerifyCode).then(() => {
+      let handler = null;
+      if (needEmail) {
+        handler = My.bindEmail(email);
+      } else {
+        handler = Promise.resolve();
+      }
+      handler.then(() => {
+        linkTo('/identity');
+      });
+    })
+      .catch(err => {
+        if (err.message.indexOf('验证码') >= 0) {
+          this.setState({ validError: err.message });
+        } else {
+          this.setState({ mobileError: err.message });
+        }
+      });
+  }
 
   renderView() {
+    const { needEmail } = this.state;
     return (
       <div>
-        <Input placeholder="请输入手机号" error="该手机号已绑定其他账号,请更换手机号码。" onChange={() => {}} />
-        <SelectInput placeholder="请输入手机号" selectValue="+86" onSelect={() => {}} />
-        <VerificationInput placeholder="请输入验证码" onSend={() => {}} />
-        <Button margin={25} radius block>
+        <SelectInput placeholder="请输入手机号" selectValue={this.state.data.area} select={MobileArea} value={this.state.data.mobile} error={this.state.mobileError} onSelect={(value) => {
+          this.changeData('area', value);
+        }} onChange={(e) => {
+          this.changeData('mobile', e.target.value);
+          this.validMobile(e.target.value);
+        }} />
+        <VerificationInput placeholder="请输入验证码" value={this.state.data.mobileVerifyCode} error={this.state.validError} onSend={() => {
+          return this.sendValid();
+        }} />
+        {needEmail && <Input placeholder="请输入邮箱" value={this.state.data.email} onChange={(e) => {
+          this.changeData('email', e.target.value);
+        }} />}
+        <Button margin={25} radius block onClick={() => {
+          this.submit();
+        }}>
           绑定
         </Button>
-        <SpecialRadioGroup list={[{ key: '1', label: 1 }, { key: '2', label: 2 }]} value="1" onChange={() => {}} />
-        <RadioGroup list={[{ key: '1', label: 1 }, { key: '2', label: 2 }]} value="1" onChange={() => {}} />
-        <Tag size="small">适合新手</Tag>
-        <Tag theme="border">OG20阅读刷题(7课时)</Tag>
-        <Tag size="small" theme="border" radius>
-          OG20阅读刷题(7课时)
-        </Tag>
-        <Checkbox checked />
-        <Checkbox />
-        <Money value="100" />
-        <Money value="100" size="lager" />
-        <Money value="100" size="lager" theme="sell" />
-        <Block />
-        <TopBlock />
-        <TagBlock tag="Yuwen" />
-        <LinkBlock title="新手辅导" sub="GMAT 全面了解,定制学习计划" />
-        <LinkBlock title="诊断辅导" sub="复习效果不理想,制定突破计划" theme="not" />
       </div>
     );
   }

+ 0 - 68
front/project/h5/routes/page/changeHistory/index.less

@@ -1,68 +0,0 @@
-@charset "utf-8";
-
-#change-history {
-  padding: 15px;
-
-  .title {
-    font-size: 16px;
-    color: #303036;
-    margin-bottom: 15px;
-
-    .date {
-      color: #686872;
-      float: right;
-
-      .assets {
-        width: 15px;
-        height: 15px;
-        margin-left: 5px;
-      }
-    }
-  }
-
-  .main {
-    text-align: center;
-    font-size: 24px;
-    font-weight: 600;
-    padding-top: 15px;
-    padding-bottom: 40px;
-    border-bottom: 1px solid #eee;
-    margin-bottom: 25px;
-  }
-
-  .list {
-    .item:nth-child(odd) {
-      background: #FBFBFB;
-    }
-
-    .item {
-      border-bottom: 1px solid #eee;
-
-      .d {
-        color: #686872;
-        display: inline-block;
-        width: 60px;
-        height: 60px;
-        line-height: 60px;
-        text-align: center;
-      }
-
-      .v {
-        display: inline-block;
-        padding: 15px 20px;
-
-        span {
-          display: inline-block;
-          width: 30px;
-          height: 30px;
-          line-height: 30px;
-          color: #fff;
-          margin: 0 5px;
-          background: #7660A4;
-          border-radius: 50%;
-          text-align: center;
-        }
-      }
-    }
-  }
-}

+ 0 - 67
front/project/h5/routes/page/changeHistory/page.js

@@ -1,67 +0,0 @@
-import React from 'react';
-import './index.less';
-import { DatePicker } from 'antd-mobile';
-import Page from '@src/containers/Page';
-import Assets from '@src/components/Assets';
-
-export default class extends Page {
-  init() {}
-
-  renderView() {
-    return (
-      <div>
-        <div className="title">最新换库</div>
-        <div className="main">2019年05月22日</div>
-        <div className="title">
-          历史记录
-          <div className="date">
-            <DatePicker mode="year" onChange={date => this.setState({ date })}>
-              <div>
-                2019年
-                <Assets name="down_down3" />
-              </div>
-            </DatePicker>
-          </div>
-        </div>
-        <div className="list">
-          <div className="item">
-            <div className="d">4月</div>
-            <div className="v">
-              <span>12</span>
-              <span>12</span>
-              <span>12</span>
-              <span>12</span>
-            </div>
-          </div>
-          <div className="item">
-            <div className="d">4月</div>
-            <div className="v">
-              <span>12</span>
-              <span>12</span>
-              <span>12</span>
-              <span>12</span>
-            </div>
-          </div>
-          <div className="item">
-            <div className="d">4月</div>
-            <div className="v">
-              <span>12</span>
-              <span>12</span>
-              <span>12</span>
-              <span>12</span>
-            </div>
-          </div>
-          <div className="item">
-            <div className="d">4月</div>
-            <div className="v">
-              <span>12</span>
-              <span>12</span>
-              <span>12</span>
-              <span>12</span>
-            </div>
-          </div>
-        </div>
-      </div>
-    );
-  }
-}

+ 0 - 48
front/project/h5/routes/page/data/index.less

@@ -1,48 +0,0 @@
-@charset "utf-8";
-
-#data {
-  padding: 15px;
-
-  .block {
-    box-shadow: 0px 10px 20px 0px rgba(0, 0, 0, 0.02);
-    border-radius: 8px;
-    border: 1px solid rgba(227, 227, 227, 1);
-    text-align: center;
-    padding: 30px 0;
-    margin-bottom: 35px;
-
-    span {
-      font-size: 24px;
-      color: #7660A4;
-      margin: 0 5px;
-      font-weight: 600;
-    }
-  }
-
-  .title {
-    color: #686872;
-    font-size: 16px;
-    margin-bottom: 25px;
-  }
-
-  .item {
-    width: 90px;
-    display: inline-block;
-
-    .text {
-      color: #303036;
-      margin-bottom: 10px;
-    }
-
-    .value {
-      color: #686872;
-
-      span {
-        color: #303036;
-        font-size: 32px;
-        font-weight: 600;
-        margin: 0 2px;
-      }
-    }
-  }
-}

+ 0 - 46
front/project/h5/routes/page/data/page.js

@@ -1,46 +0,0 @@
-import React from 'react';
-import './index.less';
-import Page from '@src/containers/Page';
-import Icon from '../../../components/Icon';
-
-export default class extends Page {
-  init() {}
-
-  renderView() {
-    return (
-      <div>
-        <div className="block">
-          <div className="text">
-            自2019-05-26,您已在千行学习<span>88</span>天
-          </div>
-          <div className="text">
-            累积<span>88</span>H<span>30</span>Min
-          </div>
-        </div>
-        <div className="title">本周数据</div>
-        <div className="t-c">
-          <div className="item">
-            <div className="text">学习时间</div>
-            <div className="value">
-              <span>23</span>Hour
-            </div>
-          </div>
-          <div className="item">
-            <div className="text">同比上周</div>
-            <div className="value">
-              <Icon type="caret-up" theme="filled" color="#6EC64B" />
-              <span>15</span>%
-            </div>
-          </div>
-          <div className="item">
-            <div className="text">同比全站</div>
-            <div className="value">
-              <Icon type="caret-down" theme="filled" color="#F36565" />
-              <span>15</span>%
-            </div>
-          </div>
-        </div>
-      </div>
-    );
-  }
-}

+ 3 - 3
front/project/h5/routes/page/data/index.js

@@ -1,7 +1,7 @@
 export default {
-  path: '/data',
-  key: 'data',
-  title: '数据',
+  path: '/bind',
+  key: 'bind',
+  title: '绑定手机', // 绑定手机
   needLogin: false,
   component() {
     return import('./page');

+ 5 - 0
front/project/h5/routes/page/demo/index.less

@@ -0,0 +1,5 @@
+@charset "utf-8";
+
+#bind {
+  padding: 15px;
+}

+ 44 - 0
front/project/h5/routes/page/demo/page.js

@@ -0,0 +1,44 @@
+import React from 'react';
+import './index.less';
+import Page from '@src/containers/Page';
+import Input, { SelectInput, VerificationInput } from '../../../components/Input';
+import Button from '../../../components/Button';
+import { SpecialRadioGroup, RadioGroup } from '../../../components/Radio';
+import Tag from '../../../components/Tag';
+import Checkbox from '../../../components/CheckBox';
+import Money from '../../../components/Money';
+import { Block, TopBlock, TagBlock, LinkBlock } from '../../../components/Block';
+
+export default class extends Page {
+  init() {}
+
+  renderView() {
+    return (
+      <div>
+        <Input placeholder="请输入手机号" error="该手机号已绑定其他账号,请更换手机号码。" onChange={() => {}} />
+        <SelectInput placeholder="请输入手机号" selectValue="+86" onSelect={() => {}} />
+        <VerificationInput placeholder="请输入验证码" onSend={() => {}} />
+        <Button margin={25} radius block>
+          绑定
+        </Button>
+        <SpecialRadioGroup list={[{ key: '1', label: 1 }, { key: '2', label: 2 }]} value="1" onChange={() => {}} />
+        <RadioGroup list={[{ key: '1', label: 1 }, { key: '2', label: 2 }]} value="1" onChange={() => {}} />
+        <Tag size="small">适合新手</Tag>
+        <Tag theme="border">OG20阅读刷题(7课时)</Tag>
+        <Tag size="small" theme="border" radius>
+          OG20阅读刷题(7课时)
+        </Tag>
+        <Checkbox checked />
+        <Checkbox />
+        <Money value="100" />
+        <Money value="100" size="lager" />
+        <Money value="100" size="lager" theme="sell" />
+        <Block />
+        <TopBlock />
+        <TagBlock tag="Yuwen" />
+        <LinkBlock title="新手辅导" sub="GMAT 全面了解,定制学习计划" />
+        <LinkBlock title="诊断辅导" sub="复习效果不理想,制定突破计划" theme="not" />
+      </div>
+    );
+  }
+}

+ 14 - 4
front/project/h5/routes/page/home/page.js

@@ -2,9 +2,19 @@ import React from 'react';
 import './index.less';
 import Page from '@src/containers/Page';
 import Assets from '@src/components/Assets';
+import { UserUrl } from '../../../../Constant';
+import { Main } from '../../../stores/main';
 
 export default class extends Page {
+  initData() {
+    Main.getIndex().then(result => {
+      this.setState(result);
+    });
+  }
+
   renderView() {
+    const { user = {} } = this.state;
+    const { info = {} } = this.props.user;
     return (
       <div>
         <div className="block-1">
@@ -14,7 +24,7 @@ export default class extends Page {
           <div className="copy-title">复制下方地址至浏览器打开</div>
           <div className="input">
             <div className="prefix">http://</div>
-            <div className="value">qianhanggmat.com/id123134341431</div>
+            <div className="value">{UserUrl.replace('http://', '')}/id/{info.inviteCode}</div>
             <Assets name="copy" />
           </div>
         </div>
@@ -64,15 +74,15 @@ export default class extends Page {
             </div>
             <div className="block-2-2">
               <div className="block-2-2-i">
-                <div className="block-2-2-i-t blue">123342</div>
+                <div className="block-2-2-i-t blue">{user.numberOffline}</div>
                 <div className="block-2-2-i-d">注册用户</div>
               </div>
               <div className="block-2-2-i">
-                <div className="block-2-2-i-t yellow">2234</div>
+                <div className="block-2-2-i-t yellow">{user.number700}</div>
                 <div className="block-2-2-i-d">700+分学员</div>
               </div>
               <div className="block-2-2-i">
-                <div className="block-2-2-i-t red">680</div>
+                <div className="block-2-2-i-t red">{user.numberScore}</div>
                 <div className="block-2-2-i-d">学员均分</div>
               </div>
             </div>

+ 4 - 3
front/project/h5/routes/page/machine/index.js

@@ -1,7 +1,8 @@
 export default {
-  path: '/machine',
-  key: 'machine',
-  title: '机经',
+  path: '/id',
+  matchPath: '/id/:id',
+  key: 'id',
+  title: '邀请', // 绑定手机
   needLogin: false,
   component() {
     return import('./page');

+ 5 - 0
front/project/h5/routes/page/id/index.less

@@ -0,0 +1,5 @@
+@charset "utf-8";
+
+#id {
+  padding: 15px;
+}

+ 16 - 0
front/project/h5/routes/page/id/page.js

@@ -0,0 +1,16 @@
+import React from 'react';
+import './index.less';
+import Page from '@src/containers/Page';
+import { User } from '../../../stores/user';
+
+export default class extends Page {
+  init() {
+    const { id } = this.params;
+    User.originInviteCode(id);
+    replaceLink('/');
+  }
+
+  renderView() {
+    return <div />;
+  }
+}

+ 119 - 4
front/project/h5/routes/page/identity/page.js

@@ -2,11 +2,108 @@ import React from 'react';
 import './index.less';
 import Page from '@src/containers/Page';
 import Assets from '@src/components/Assets';
+import { asyncSMessage } from '@src/services/AsyncTools';
+import { dataURLtoBlob } from '@src/services/Tools';
+import { Common } from '../../../stores/common';
+import { My } from '../../../stores/my';
 
 export default class extends Page {
-  init() {}
+  initState() {
+    return { state: 'wait' };
+  }
 
-  renderView() {
+  init() {
+    this.wx = null;
+    Common.readyWechat(window.location.href, ['chooseImage', 'getLocalImgData'])
+      .then((wx) => {
+        this.wx = wx;
+      });
+  }
+
+  submitFront() {
+    if (this.wx) {
+      this.wx.chooseImage({
+        count: 1, // 默认9
+        sizeType: ['original', 'compressed'], // 可以指定是原图还是压缩图,默认二者都有
+        sourceType: ['camera'], // 可以指定来源是相册还是相机,默认二者都有
+        success(response) {
+          wx.getLocalImgData({
+            localId: response.localIds[0].toString(),
+            success: (res) => {
+              const { localData } = res;
+              let imageBase64 = '';
+              if (localData.indexOf('data:image') === 0) {
+                // 苹果的直接赋值,默认生成'data:image/jpeg;base64,'的头部拼接
+                imageBase64 = localData;
+              } else {
+                // 此处是安卓中的唯一得坑!在拼接前需要对localData进行换行符的全局替换
+                // 此时一个正常的base64图片路径就完美生成赋值到img的src中了
+                imageBase64 = `data:image/jpeg;base64,${localData.replace(/\n/g, '')}`;
+              }
+              this.setState({ front: imageBase64 });
+              My.realFront(new File(dataURLtoBlob(imageBase64), 'front.jpg', { type: 'image/jpeg' }))
+                .then(() => {
+                  this.back = true;
+                  this.submit();
+                })
+                .catch(err => {
+                  asyncSMessage(err.message, 'error');
+                });
+            },
+          });
+        },
+      });
+    }
+  }
+
+  submitBack() {
+    if (this.wx) {
+      this.wx.chooseImage({
+        count: 1, // 默认9
+        sizeType: ['original', 'compressed'], // 可以指定是原图还是压缩图,默认二者都有
+        sourceType: ['camera'], // 可以指定来源是相册还是相机,默认二者都有
+        success(response) {
+          wx.getLocalImgData({
+            localId: response.localIds[0].toString(),
+            success: (res) => {
+              const { localData } = res;
+              let imageBase64 = '';
+              if (localData.indexOf('data:image') === 0) {
+                // 苹果的直接赋值,默认生成'data:image/jpeg;base64,'的头部拼接
+                imageBase64 = localData;
+              } else {
+                // 此处是安卓中的唯一得坑!在拼接前需要对localData进行换行符的全局替换
+                // 此时一个正常的base64图片路径就完美生成赋值到img的src中了
+                imageBase64 = `data:image/jpeg;base64,${localData.replace(/\n/g, '')}`;
+              }
+              this.setState({ back: imageBase64 });
+              dataURLtoBlob(imageBase64);
+
+              My.realBack(new File(dataURLtoBlob(imageBase64), 'back.jpg', { type: 'image/jpeg' }))
+                .then(() => {
+                  this.back = true;
+                  this.submit();
+                })
+                .catch(err => {
+                  asyncSMessage(err.message, 'error');
+                });
+            },
+          });
+        },
+      });
+    }
+  }
+
+  submit() {
+    if (!this.front || !this.back) {
+      return;
+    }
+    My.realFinish().then(() => {
+      this.setState({ state: 'finish' });
+    });
+  }
+
+  renderWait() {
     return (
       <div>
         <div className="text">
@@ -16,13 +113,31 @@ export default class extends Page {
         </div>
         <div className="id-card-1">
           <Assets name="IDcard" />
-          <Assets className="scan" name="scan1" />
+          <Assets className="scan" src={this.state.front} name="scan1" onClick={() => {
+            this.submitFront();
+          }} />
         </div>
         <div className="id-card-2">
           <Assets name="IDcard2" />
-          <Assets className="scan" name="scan2" />
+          <Assets className="scan" src={this.state.back} name="scan2" onClick={() => {
+            this.submitBack();
+          }} />
         </div>
       </div>
     );
   }
+
+  renderFinish() {
+    return <div />;
+  }
+
+  renderView() {
+    const { state } = this.state;
+    switch (state) {
+      case 'finish':
+        return this.renderFinish();
+      default:
+        return this.renderWait();
+    }
+  }
 }

+ 3 - 6
front/project/h5/routes/page/index.js

@@ -1,14 +1,11 @@
 import home from './home';
 import login from './login';
 import bind from './bind';
+import id from './id';
 import identity from './identity';
 import invitation from './invitation';
-import data from './data';
-import product from './product';
+import study from './study';
 import message from './message';
-import update from './update';
-import changeHistory from './changeHistory';
 import open from './open';
-import machine from './machine';
 
-export default [home, login, bind, identity, invitation, data, product, message, update, changeHistory, open, machine];
+export default [home, login, bind, identity, invitation, study, id, message, open];

+ 82 - 9
front/project/h5/routes/page/invitation/page.js

@@ -2,12 +2,70 @@ import React from 'react';
 import './index.less';
 import Page from '@src/containers/Page';
 import Assets from '@src/components/Assets';
+import { asyncSMessage } from '@src/services/AsyncTools';
+import { H5Url } from '../../../../Constant';
 import Button from '../../../components/Button';
 import Input from '../../../components/Input';
 import Tag from '../../../components/Tag';
+import { My } from '../../../stores/my';
+import { Common } from '../../../stores/common';
 
 export default class extends Page {
-  init() {}
+  initState() {
+    return {
+      emails: [],
+      select: -1,
+    };
+  }
+
+  init() {
+    Common.readyWechat(window.location.href, ['onMenuShareAppMessage'])
+      .then((wx) => {
+        const { info } = this.props.user;
+        wx.onMenuShareAppMessage({
+          title: '', // Share title
+          desc: '', // Share description
+          link: `${H5Url}/id/${info.inviteCode}`, // Share Link,this link domain name and path must be the same as the current page which corresponding to JS secured domain name as Official account
+          imgUrl: '', // Share Icon
+          type: 'link', // Share type, music, video link, not filled default link
+          dataUrl: '', // If type is music or video, provide data links, the default is empty
+          success() {
+            // The user confirms the callback function that was executed after sharing
+          },
+          cancel() {
+            // The user cancels the callback function that was executed after sharing
+          },
+        });
+      });
+  }
+
+  addEmail(value) {
+    const { emails } = this.state;
+    emails.push(value.replace(';', '').replace(';', ''));
+    this.setState({
+      email: '',
+      emails,
+    });
+  }
+
+  removeEmail(index) {
+    const { emails } = this.state;
+    emails.splice(index, 1);
+    this.setState({
+      emails,
+    });
+  }
+
+  submit() {
+    const { emails } = this.state;
+    if (emails.length === 0) return;
+    My.inviteEmail(emails).then(() => {
+      asyncSMessage('发送成功');
+      this.setState({ email: '', emails: [] });
+    }).catch(() => {
+      asyncSMessage('发送失败', 'warn');
+    });
+  }
 
   renderView() {
     return (
@@ -33,16 +91,31 @@ export default class extends Page {
           去邀请
         </Button>
         <div className="title">邮件邀请</div>
-        <Input placeholder="添加多个邮箱,请用「;」间隔" />
+        <Input placeholder="添加多个邮箱,请用「;」间隔" value={this.state.email || ''} onChange={(e) => {
+          const { value } = e.target;
+          if (value.length > 0) {
+            const last = value[value.length - 1];
+            if (last === ';' || last === ';') {
+              // 添加邮箱
+              this.addEmail(value);
+            } else {
+              this.setState({ email: value });
+            }
+          } else {
+            this.setState({ email: value });
+          }
+        }} />
         <div className="tag-list">
-          <Tag theme="disabled" radius onClose={() => {}}>
-            adfagfafha@163.com
-          </Tag>
-          <Tag theme="white" radius onClose={() => {}}>
-            adfagfafha@163.com
-          </Tag>
+          {this.state.emails.map((email, index) => <Tag theme={this.state.select !== index ? 'white' : 'disabled'} radius onClick={() => {
+            if (this.state.select === index) this.setState({ select: -1 });
+            else this.setState({ select: index });
+          }} onClose={this.state.select === index ? () => this.removeEmail(index) : null}>
+            {email}
+          </Tag>)}
         </div>
-        <Button block width={110} radius>
+        <Button block width={110} radius onClick={() => {
+          this.submit();
+        }}>
           发送邀请
         </Button>
       </div>

+ 1 - 1
front/project/h5/routes/page/login/index.js

@@ -1,7 +1,7 @@
 export default {
   path: '/login',
   key: 'login',
-  title: '登录', // 绑定手机
+  title: '登录',
   needLogin: false,
   component() {
     return import('./page');

+ 10 - 5
front/project/h5/routes/page/login/page.js

@@ -1,19 +1,24 @@
 import React from 'react';
 import './index.less';
 import Page from '@src/containers/Page';
+import { WechatAppId, H5Url } from '../../../../Constant';
 import { User } from '../../../stores/user';
 
 export default class extends Page {
   init() {
     const { code } = this.props.core.query;
     if (code) {
-      User.loginWechat(code).then(() => {
-        replaceLink('/');
+      User.loginWechat(code).then((info) => {
+        if (info.mobile) {
+          replaceLink('/');
+        } else {
+          replaceLink('/bind');
+        }
       });
     } else {
-      const url = `https://open.weixin.qq.com/connect/oauth2/authorize?appid=1&redirect_uri=${encodeURIComponent(
-        '/login',
-      )}&response_type=code&scope=snsapi_userinfo&state=STATE#wechat_redirect`;
+      const url = `https://open.weixin.qq.com/connect/oauth2/authorize?appid=${WechatAppId}&redirect_uri=${encodeURIComponent(
+        `${H5Url}/login`,
+      )}&response_type=code&scope=snsapi_base&state=STATE#wechat_redirect`;
       window.location.href = url;
     }
   }

+ 0 - 55
front/project/h5/routes/page/machine/page.js

@@ -1,55 +0,0 @@
-import React from 'react';
-import './index.less';
-import { Tabs } from 'antd-mobile';
-import Page from '@src/containers/Page';
-import Button from '../../../components/Button';
-
-export default class extends Page {
-  renderView() {
-    const { list = [] } = this.state;
-    return (
-      <div>
-        <div className="tip">最近换库时间:2019-07-30,已换库n天。</div>
-        <Tabs tabs={[{ title: '数学', key: 's' }, { title: '阅读RC', key: '1' }, { title: '逻辑CR', key: 'l' }]} />
-        <div>{list.length > 0 ? this.renderList() : this.renderEmpty()}</div>
-        <div className="fixed">
-          <Button block disabled={list.length === 0} size="lager">
-            查阅机经
-          </Button>
-        </div>
-      </div>
-    );
-  }
-
-  renderList() {
-    return (
-      <div className="list">
-        <div className="log-title">更新日志</div>
-        <div className="item">
-          <div className="title">
-            版本7<span className="date">2019-07-21 09:50:26</span>
-          </div>
-          <div className="desc">
-            新增 7 道数学机经;
-            <br /> 补充第 23 题条件;
-            <br /> 更新第 54题解析和答案
-          </div>
-        </div>
-      </div>
-    );
-  }
-
-  renderEmpty() {
-    return (
-      <div className="empty">
-        <div className="text">
-          还未购买本月机经。 <br />
-          您可至千行网站「我的-工具」查看往期机经。
-        </div>
-        <Button block width={120} radius>
-          去购买
-        </Button>
-      </div>
-    );
-  }
-}

+ 1 - 0
front/project/h5/routes/page/open/index.js

@@ -1,5 +1,6 @@
 export default {
   path: '/open',
+  matchPath: '/open/:id',
   key: 'open',
   title: '开通',
   needLogin: false,

+ 16 - 2
front/project/h5/routes/page/open/page.js

@@ -4,8 +4,18 @@ import Page from '@src/containers/Page';
 import Assets from '@src/components/Assets';
 import Checkbox from '../../../components/CheckBox';
 import Button from '../../../components/Button';
+import { My } from '../../../stores/my';
 
 export default class extends Page {
+  initData() {
+    const { id } = this.params;
+    My.getOrderRecord(id);
+  }
+
+  submit() {
+
+  }
+
   renderView() {
     return (
       <div>
@@ -18,10 +28,14 @@ export default class extends Page {
           <Checkbox />
           <span>邮箱订阅机经</span>
         </div>
-        <Button block radius>
+        <Button block radius onClick={() => {
+          this.submit();
+        }}>
           立即开通
         </Button>
-        <div className="no">暂不开通</div>
+        <div className="no" onClick={() => {
+          goBack();
+        }}>暂不开通</div>
       </div>
     );
   }

+ 46 - 1
front/project/h5/routes/page/study/index.less

@@ -1,3 +1,48 @@
 @charset "utf-8";
 
-#study {}
+#study {
+  padding: 15px;
+
+  .block {
+    box-shadow: 0px 10px 20px 0px rgba(0, 0, 0, 0.02);
+    border-radius: 8px;
+    border: 1px solid rgba(227, 227, 227, 1);
+    text-align: center;
+    padding: 30px 0;
+    margin-bottom: 35px;
+
+    span.s {
+      font-size: 24px;
+      color: #7660A4;
+      margin: 0 5px;
+      font-weight: 600;
+    }
+  }
+
+  .title {
+    color: #686872;
+    font-size: 16px;
+    margin-bottom: 25px;
+  }
+
+  .item {
+    width: 90px;
+    display: inline-block;
+
+    .text {
+      color: #303036;
+      margin-bottom: 10px;
+    }
+
+    .value {
+      color: #686872;
+
+      span.s {
+        color: #303036;
+        font-size: 32px;
+        font-weight: 600;
+        margin: 0 2px;
+      }
+    }
+  }
+}

+ 48 - 2
front/project/h5/routes/page/study/page.js

@@ -1,12 +1,58 @@
 import React from 'react';
 import './index.less';
 import Page from '@src/containers/Page';
+import { formatDate, formatSeconds } from '@src/services/Tools';
+import Icon from '../../../components/Icon';
+import { My } from '../../../stores/my';
 
 export default class extends Page {
-  init() {
+  init() { }
+
+  initData() {
+    My.getStudyTotal().then(total => {
+      this.setState({ total });
+    });
+    My.getStudyWeek().then(week => {
+      this.setState({ week });
+    });
   }
 
   renderView() {
-    return <div />;
+    const { total } = this.state;
+    return (
+      <div>
+        <div className="block">
+          <div className="text">
+            自{formatDate(total.createTime, 'YYYY-MM-DD')},您已在千行学习<span>{total.days}</span>天
+          </div>
+          <div className="text">
+            累积<span dangerouslySetInnerHTML={{ __html: formatSeconds(total.time).replace(/([0-9]+)([msh])/g, '<span class="s">$1</span>$2') }} />
+          </div>
+        </div>
+        <div className="title">本周数据</div>
+        <div className="t-c">
+          <div className="item">
+            <div className="text">学习时间</div>
+            <div className="value">
+              <span>23</span>Hour
+            </div>
+          </div>
+          <div className="item">
+            <div className="text">同比上周</div>
+            <div className="value">
+              <Icon type="caret-up" theme="filled" color="#6EC64B" />
+              <span>15</span>%
+            </div>
+          </div>
+          <div className="item">
+            <div className="text">同比全站</div>
+            <div className="value">
+              <Icon type="caret-down" theme="filled" color="#F36565" />
+              <span>15</span>%
+            </div>
+          </div>
+        </div>
+      </div>
+    );
   }
 }

+ 0 - 9
front/project/h5/routes/page/update/index.js

@@ -1,9 +0,0 @@
-export default {
-  path: '/update',
-  key: 'update',
-  title: '资料更新',
-  needLogin: false,
-  component() {
-    return import('./page');
-  },
-};

+ 0 - 63
front/project/h5/routes/page/update/index.less

@@ -1,63 +0,0 @@
-@charset "utf-8";
-
-#update {
-  padding: 0 15px;
-
-  .list {
-    .item {
-      border-bottom: 1px solid #eee;
-      position: relative;
-      padding-top: 20px;
-      padding-bottom: 15px;
-
-      .title {
-        font-size: 16px;
-        color: #303036;
-        margin-bottom: 10px;
-
-        .date {
-          float: right;
-          color: #686872;
-          font-size: 14px;
-        }
-      }
-
-      .tip {
-        color: #686872;
-        font-size: 12px;
-
-        .new {
-          margin-right: 5px;
-
-          .g-tag {
-            background: #FFC800;
-            border: none;
-          }
-        }
-      }
-
-      .desc {
-        font-size: 12px;
-        color: #686872;
-        margin-top: 10px;
-        margin-bottom: 15px;
-      }
-
-      .drop {
-        width: 22px;
-        height: 22px;
-        position: absolute;
-        bottom: 15px;
-        right: 0;
-      }
-    }
-  }
-
-  .empty {
-    position: fixed;
-    top: 50%;
-    left: 50%;
-    transform: translate(-50%, -50%);
-    color: #999999;
-  }
-}

+ 0 - 60
front/project/h5/routes/page/update/page.js

@@ -1,60 +0,0 @@
-import React, { Component } from 'react';
-import './index.less';
-import Page from '@src/containers/Page';
-import Assets from '@src/components/Assets';
-import Tag from '../../../components/Tag';
-
-class Item extends Component {
-  constructor(props) {
-    super(props);
-    this.state = { show: false };
-  }
-
-  render() {
-    const { show } = this.state;
-    return (
-      <div className="item">
-        <div className="title">
-          OG19 语法千行<span className="date">2019-08-11</span>
-        </div>
-        <div className="tip">
-          <Tag className="new" size="small">
-            NEW
-          </Tag>
-          变更点:版本2
-        </div>
-        <div hidden={!show} className="desc">
-          删除“the number of wolf population 意思重复”删除“the number of wolf population 意思重复”删除“the number of
-          wolf population 意思重复”
-        </div>
-        <Assets
-          className="drop"
-          name={!show ? 'drop_down1' : 'drop_up1'}
-          onClick={() => this.setState({ show: !show })}
-        />
-      </div>
-    );
-  }
-}
-
-export default class extends Page {
-  init() {}
-
-  renderView() {
-    const { list = [] } = this.state;
-    return <div>{list.length > 0 ? this.renderList() : this.renderEmpty()}</div>;
-  }
-
-  renderList() {
-    return (
-      <div className="list">
-        <Item />
-        <Item />
-      </div>
-    );
-  }
-
-  renderEmpty() {
-    return <div className="empty">还未购买或订阅资料</div>;
-  }
-}

+ 61 - 1
front/project/h5/routes/product/dataHistory/index.less

@@ -1,3 +1,63 @@
 @charset "utf-8";
 
-#product-data-history {}
+#product-data-history {
+  padding: 0 15px;
+
+  .list {
+    .item {
+      border-bottom: 1px solid #eee;
+      position: relative;
+      padding-top: 20px;
+      padding-bottom: 15px;
+
+      .title {
+        font-size: 16px;
+        color: #303036;
+        margin-bottom: 10px;
+
+        .date {
+          float: right;
+          color: #686872;
+          font-size: 14px;
+        }
+      }
+
+      .tip {
+        color: #686872;
+        font-size: 12px;
+
+        .new {
+          margin-right: 5px;
+
+          .g-tag {
+            background: #FFC800;
+            border: none;
+          }
+        }
+      }
+
+      .desc {
+        font-size: 12px;
+        color: #686872;
+        margin-top: 10px;
+        margin-bottom: 15px;
+      }
+
+      .drop {
+        width: 22px;
+        height: 22px;
+        position: absolute;
+        bottom: 15px;
+        right: 0;
+      }
+    }
+  }
+
+  .empty {
+    position: fixed;
+    top: 50%;
+    left: 50%;
+    transform: translate(-50%, -50%);
+    color: #999999;
+  }
+}

+ 52 - 4
front/project/h5/routes/product/dataHistory/page.js

@@ -1,12 +1,60 @@
-import React from 'react';
+import React, { Component } from 'react';
 import './index.less';
 import Page from '@src/containers/Page';
+import Assets from '@src/components/Assets';
+import Tag from '../../../components/Tag';
 
-export default class extends Page {
-  init() {
+class Item extends Component {
+  constructor(props) {
+    super(props);
+    this.state = { show: false };
+  }
+
+  render() {
+    const { show } = this.state;
+    return (
+      <div className="item">
+        <div className="title">
+          OG19 语法千行<span className="date">2019-08-11</span>
+        </div>
+        <div className="tip">
+          <Tag className="new" size="small">
+            NEW
+          </Tag>
+          变更点:版本2
+        </div>
+        <div hidden={!show} className="desc">
+          删除“the number of wolf population 意思重复”删除“the number of wolf population 意思重复”删除“the number of
+          wolf population 意思重复”
+        </div>
+        <Assets
+          className="drop"
+          name={!show ? 'drop_down1' : 'drop_up1'}
+          onClick={() => this.setState({ show: !show })}
+        />
+      </div>
+    );
   }
+}
+
+export default class extends Page {
+  init() { }
 
   renderView() {
-    return <div />;
+    const { list = [] } = this.state;
+    return <div>{list.length > 0 ? this.renderList() : this.renderEmpty()}</div>;
+  }
+
+  renderList() {
+    return (
+      <div className="list">
+        <Item />
+        <Item />
+      </div>
+    );
+  }
+
+  renderEmpty() {
+    return <div className="empty">还未购买或订阅资料</div>;
   }
 }

+ 6 - 2
front/project/h5/routes/product/index.js

@@ -1,9 +1,13 @@
-import courseDetail from './courseDetail';
 import serviceDetail from './serviceDetail';
+import data from './data';
 import dataDetail from './dataDetail';
+import dataHistory from './dataHistory';
 import list from './list';
 import course from './course';
+import courseDetail from './courseDetail';
 import coursePackage from './coursePackage';
+import courseSingle from './courseSingle';
 import bought from './bought';
+import main from './main';
 
-export default [list, course, courseDetail, serviceDetail, dataDetail, coursePackage, bought];
+export default [list, course, coursePackage, courseSingle, courseDetail, serviceDetail, data, dataDetail, dataHistory, bought, main];

front/project/h5/routes/page/product/index.js → front/project/h5/routes/product/main/index.js


front/project/h5/routes/page/product/index.less → front/project/h5/routes/product/main/index.less


+ 5 - 5
front/project/h5/routes/page/product/page.js

@@ -7,17 +7,17 @@ import Money from '../../../components/Money';
 import { LinkBlock, CourseBlock, DataBlock } from '../../../components/Block';
 
 export default class extends Page {
-  init() {}
+  init() { }
 
   renderView() {
     return (
       <div>
         <Tabs
           tabs={[
-            { title: '服务', key: 's' },
-            { title: '1V1私教', key: '1' },
-            { title: '在线课程', key: 'l' },
-            { title: '资料', key: 'd' },
+            { title: '服务', key: 'service' },
+            { title: '1V1私教', key: 'vs' },
+            { title: '在线课程', key: 'video' },
+            { title: '资料', key: 'data' },
           ]}
         />
         <div className="title">服务</div>

+ 1 - 1
front/project/h5/routes/product/serviceDetail/index.js

@@ -1,5 +1,5 @@
 export default {
-  path: '/product/service/detail/:id',
+  path: '/product/service/:id',
   key: 'product-service-detail',
   title: '服务详情',
   needLogin: false,

+ 1 - 1
front/project/h5/routes/textbook/detail/index.js

@@ -1,5 +1,5 @@
 export default {
-  path: '/textbook/detail/:id',
+  path: '/textbook/detail',
   key: 'textbook-detail',
   title: '机经详情',
   needLogin: false,

+ 18 - 0
front/project/h5/routes/textbook/detail/index.less

@@ -58,6 +58,8 @@
     padding: 0 20px;
 
     .prev {
+      font-size: 12px;
+      color: #A2AAB5;
       display: inline-block;
       margin-right: 25px;
 
@@ -67,6 +69,8 @@
     }
 
     .next {
+      font-size: 12px;
+      color: #A2AAB5;
       display: inline-block;
 
       .anticon {
@@ -88,6 +92,11 @@
         margin-left: 5px;
         width: 15px;
         height: 15px;
+        margin-top: 14px;
+      }
+
+      .ant-list-item {
+        padding: 0;
       }
     }
   }
@@ -147,10 +156,19 @@
             .arrow {
               width: 15px;
               height: 15px;
+              margin-top: 8px;
             }
           }
         }
 
+        .ant-list-item {
+          padding: 0px;
+
+          .ant-list-item-extra {
+            display: none;
+          }
+        }
+
         .footer {
           position: absolute;
           bottom: 20px;

+ 132 - 23
front/project/h5/routes/textbook/detail/page.js

@@ -1,12 +1,15 @@
 import React, { Component } from 'react';
 import './index.less';
-import { Drawer } from 'antd-mobile';
+import { Drawer, Picker } from 'antd-mobile';
 import Page from '@src/containers/Page';
 import Assets from '@src/components/Assets';
+import { List } from 'antd';
 import Switch from '../../../components/Switch';
 import Icon from '../../../components/Icon';
 import { SpecialRadioGroup } from '../../../components/Radio';
 import Button from '../../../components/Button';
+import { TextbookQuality } from '../../../../Constant';
+import { Textbook } from '../../../stores/textbook';
 
 class Detail extends Component {
   constructor(props) {
@@ -15,34 +18,112 @@ class Detail extends Component {
   }
 
   render() {
-    const { show } = this.state;
+    const { show, data = {} } = this.state;
     return (
       <div className="detail">
-        <div className="detail-title">1.幼儿吞药</div>
-        <div className="detail-desc">
-          幼兒容易將藥丸吞食,故許多藥局將藥丸放在一個幼兒不容易打開的盒子裡,但之後幼兒誤
-          吞藥丸的比例變高了,since_____ ==>大人將盒子放在幼兒容易拿到的地方(感覺跟一題打火機放在幼兒容易拿到的地方相似)
-        </div>
+        <div className="detail-title">{data.no || 0}.{data.keyword}</div>
+        <div className="detail-desc" dangerouslySetInnerHTML={{ __html: data.detail }} />
         <div className="detail-switch">
           显示答案
           <Switch size="small" checked={show} onClick={() => this.setState({ show: !show })} />
         </div>
-        <div hidden={!show} className="detail-result">
-          :吞藥丸的比例變高了,since_____
-          ==>大人將盒子放在幼兒容易拿到的地方(感覺跟一題打火機放在幼兒容易拿到的地方相似)
-          ==>大人將盒子放在幼兒容易拿到的地方(感覺跟一題打火機放在幼兒容易拿到的地方相似)
-        </div>
+        <div hidden={!show} className="detail-result" dangerouslySetInnerHTML={{ __html: data.content }} />
       </div>
     );
   }
 }
 
 export default class extends Page {
-  init() {}
+  initState() {
+    return { pageData: [{ label: 1, value: 1 }] };
+  }
+
+  init() {
+    const { search } = this.state;
+    search.isOld = false;
+    search.qualitys = [];
+    search.order = '';
+    this.setState({ search });
+  }
+
+  initData() {
+    this.setState({ filter: false });
+    Textbook.listTopic(Object.assign({ latest: true }, this.state.search))
+      .then(result => {
+        this.setTableData(result.list, result.total);
+        const pageData = [];
+        let i = 0;
+        let page = 1;
+        while (i < result.total) {
+          pageData.push({ label: page, value: page });
+          i += this.state.search.size;
+          page += 1;
+        }
+        this.setState({ pageData });
+      });
+  }
+
+  prev() {
+    const { search } = this.state;
+    if (search.page > 1) {
+      search.page -= 1;
+    } else {
+      return;
+    }
+    this.setState({ search });
+    this.refresh();
+  }
+
+  next() {
+    const { search, page } = this.state;
+
+    if (search.page < Math.ceil(page.total / page.pageSize)) {
+      search.page += 1;
+    } else {
+      return;
+    }
+    this.setState({ search });
+    this.refresh();
+  }
+
+  changeQuality(value) {
+    const { search = {} } = this.state;
+    const { qualitys = [] } = search;
+    const index = qualitys.indexOf(value);
+
+    if (index >= 0) {
+      qualitys.splice(index, 1);
+    } else {
+      qualitys.push(value);
+    }
+    search.qualitys = qualitys;
+    this.setState({ search });
+  }
+
+  changeOld() {
+    const { search = {} } = this.state;
+    search.isOld = !search.isOld;
+
+    this.setState({ search });
+  }
+
+  changeDirection(direction) {
+    const { search = {} } = this.state;
+    search.direction = direction;
+
+    this.setState({ search });
+  }
+
+  changePage(page) {
+    const { search = {} } = this.state;
+    if (search.page === page) return;
+    search.page = page;
+
+    this.refresh();
+  }
 
   renderView() {
-    const { filter } = this.state;
-    console.log(filter);
+    const { filter, search, pageData } = this.state;
     return (
       <Drawer
         style={{ minHeight: document.documentElement.clientHeight }}
@@ -57,17 +138,28 @@ export default class extends Page {
           <Detail />
         </div>
         <div className="fixed">
-          <div className="prev">
+          <div className="prev" onClick={() => {
+            this.prev();
+          }}>
             <Icon type="left" />
             Previous
           </div>
-          <div className="next">
+          <div className="next" onClick={() => {
+            this.next();
+          }}>
             Next
             <Icon type="right" />
           </div>
           <div className="page">
-            <span>跳转至</span>第<span>15</span>页
-            <Assets name="down_down3" />
+
+            <Picker title="选择页数"
+              cols={1}
+              value={[this.state.search.page]}
+              data={pageData}
+              onChange={(i) => {
+                this.changePage(i[0]);
+              }}><List.Item><span>跳转至</span>第{search.page}页<Assets name="down_down3" /></List.Item>
+            </Picker>
           </div>
         </div>
         <div hidden={filter} className="filter-switch">
@@ -78,6 +170,7 @@ export default class extends Page {
   }
 
   renderFilter() {
+    const { search } = this.state;
     return (
       <div className="filter">
         <div className="body">
@@ -85,25 +178,41 @@ export default class extends Page {
             <div className="label">机经质量</div>
             <div className="value">
               <SpecialRadioGroup
-                list={[{ key: '1', label: '完整' }, { key: '2', label: '较完整' }, { key: '3', label: '残缺' }]}
+                list={TextbookQuality}
+                values={search.qualitys}
+                onChange={(value) => {
+                  this.changeQuality(value);
+                }}
               />
             </div>
           </div>
           <div className="item">
             <div className="label left">更新时间</div>
             <div className="value right">
-              由远到近 <Assets className="arrow" name="down_down3" />
+              <Picker title="更新时间"
+                cols={1}
+                value={[this.state.search.direction]}
+                data={[{ label: '由远到近', value: 'asc' }, { label: '由近到远', value: 'desc' }]}
+                onChange={(i) => {
+                  this.changeDirection(i[0]);
+                }}><List.Item extra={false}>{search.direction === 'asc' ? '由远到近' : '由近到远'} <Assets className="arrow" name="down_down3" /></List.Item>
+              </Picker>
+
             </div>
           </div>
           <div className="item">
             <div className="label left">看考古</div>
             <div className="value right">
-              <Switch />
+              <Switch checked={search.isOld} onClick={() => {
+                this.changeOld();
+              }} />
             </div>
           </div>
         </div>
         <div className="footer">
-          <Button radius width={90}>
+          <Button radius width={90} onClick={() => {
+            this.refresh();
+          }}>
             确定
           </Button>
         </div>

+ 3 - 1
front/project/h5/routes/textbook/index.js

@@ -1,3 +1,5 @@
 import detail from './detail';
+import main from './main';
+import library from './library';
 
-export default [detail];
+export default [detail, main, library];

+ 0 - 3
front/project/h5/routes/textbook/index/index.less

@@ -1,3 +0,0 @@
-@charset "utf-8";
-
-#textbook {}

+ 66 - 1
front/project/h5/routes/textbook/library/index.less

@@ -1,3 +1,68 @@
 @charset "utf-8";
 
-#textbook-library {}
+#textbook-library {
+  padding: 15px;
+
+  .title {
+    font-size: 16px;
+    color: #303036;
+    margin-bottom: 15px;
+
+    .date {
+      color: #686872;
+      float: right;
+
+      .assets {
+        width: 15px;
+        height: 15px;
+        margin-left: 5px;
+      }
+    }
+  }
+
+  .main {
+    text-align: center;
+    font-size: 24px;
+    font-weight: 600;
+    padding-top: 15px;
+    padding-bottom: 40px;
+    border-bottom: 1px solid #eee;
+    margin-bottom: 25px;
+  }
+
+  .list {
+    .item:nth-child(odd) {
+      background: #FBFBFB;
+    }
+
+    .item {
+      border-bottom: 1px solid #eee;
+
+      .d {
+        color: #686872;
+        display: inline-block;
+        width: 60px;
+        height: 60px;
+        line-height: 60px;
+        text-align: center;
+      }
+
+      .v {
+        display: inline-block;
+        padding: 15px 20px;
+
+        span {
+          display: inline-block;
+          width: 30px;
+          height: 30px;
+          line-height: 30px;
+          color: #fff;
+          margin: 0 5px;
+          background: #7660A4;
+          border-radius: 50%;
+          text-align: center;
+        }
+      }
+    }
+  }
+}

+ 56 - 2
front/project/h5/routes/textbook/library/page.js

@@ -1,12 +1,66 @@
 import React from 'react';
 import './index.less';
+import { DatePicker } from 'antd-mobile';
 import Page from '@src/containers/Page';
+import Assets from '@src/components/Assets';
+import { formatDate } from '@src/services/Tools';
+import { Textbook } from '../../../stores/textbook';
 
 export default class extends Page {
-  init() {
+  initState() {
+    return {
+      date: new Date(),
+    };
+  }
+
+  initData() {
+    Textbook.getInfo()
+      .then(result => {
+        this.setState(result);
+      });
+    this.refreshYear(this.state.date);
+  }
+
+  refreshYear(date) {
+    this.setState({ date });
+    Textbook.listYear(date.getFullYear())
+      .then(() => {
+        this.setState({ months: [] });
+      });
   }
 
   renderView() {
-    return <div />;
+    const { library = {}, date, months = [] } = this.state;
+    return (
+      <div>
+        <div className="title">最新换库</div>
+        <div className="main">{library.startDate ? formatDate(library.startDate, 'YYYY年MM月DD日') : ''}</div>
+        <div className="title">
+          历史记录
+          <div className="date">
+            <DatePicker value={date} mode="year" onChange={value => {
+              this.refreshYear(value);
+            }}>
+              <div>
+                {date.getFullYear()}年
+                <Assets name="down_down3" />
+              </div>
+            </DatePicker>
+          </div>
+        </div>
+        <div className="list">
+          {(months || []).map(month => {
+            return <div className="item">
+              <div className="d">{month.value}月</div>
+              <div className="v">
+                {month.list.map(row => {
+                  return <span>{row}</span>;
+                })}
+              </div>
+            </div>;
+          })}
+        </div>
+      </div>
+    );
   }
 }

front/project/h5/routes/textbook/index/index.js → front/project/h5/routes/textbook/main/index.js


+ 2 - 1
front/project/h5/routes/page/machine/index.less

@@ -1,11 +1,12 @@
 @charset "utf-8";
 
-#machine {
+#textbook {
   .tip {
     background: #7660A4;
     color: #fff;
     line-height: 36px;
     font-size: 12px;
+    padding: 0 10px;
   }
 
   .list {

+ 110 - 0
front/project/h5/routes/textbook/main/page.js

@@ -0,0 +1,110 @@
+import React from 'react';
+import './index.less';
+import { Tabs } from 'antd-mobile';
+import Page from '@src/containers/Page';
+import { formatDate } from '@src/services/Tools';
+import Button from '../../../components/Button';
+import { Textbook } from '../../../stores/textbook';
+import { TextbookSubject } from '../../../../Constant';
+
+const TextbookSubjectTabs = TextbookSubject.map(row => {
+  return {
+    title: row.label,
+    key: row.value,
+  };
+});
+export default class extends Page {
+  initState() {
+    return {
+      tab: TextbookSubjectTabs[0],
+      day: 0,
+    };
+  }
+
+  initData() {
+    Textbook.getInfo()
+      .then(result => {
+        result.day = parseInt((new Date().getTime() - new Date(result.library.startDate).getTime()) / 86400000, 10);
+        result.hasService = true;
+        this.setState(result);
+      });
+    this.refreshTab(this.state.tab);
+  }
+
+  refreshTab(tab) {
+    this.setState({ tab });
+    Textbook.listHistory(tab.key).then(result => {
+      this.setState({ list: result });
+    });
+  }
+
+  renderView() {
+    const { library = {}, hasService, tab, day } = this.state;
+    return (
+      <div>
+        <div className="tip">最近换库时间:{library.startDate ? formatDate(library.startDate, 'YYYY-MM-DD') : ''},已换库{day}天。</div>
+        <Tabs page={tab.key} tabs={TextbookSubjectTabs} onChange={(v) => {
+          linkTo('/textbook/detail');
+          this.refreshTab(v);
+        }} />
+        <div>{hasService ? this.renderList() : this.renderEmpty()}</div>
+        <div className="fixed">
+          <Button block disabled={!hasService} size="lager" onClick={() => {
+            console.log(1);
+            linkTo('/textbook/detail');
+          }}>
+            查阅机经
+          </Button>
+        </div>
+      </div>
+    );
+  }
+
+  renderList() {
+    const { list = [] } = this.state;
+    return (
+      <div className="list">
+        <div className="log-title">更新日志</div>
+        {list.map(row => {
+          return <div className="item">
+            <div className="title">
+              版本{row.version}<span className="date">{formatDate(row.createTime)}</span>
+            </div>
+            <div className="desc" dangerouslySetInnerHTML={{ __html: row.content }} />
+          </div>;
+        })}
+
+      </div>
+    );
+  }
+
+  renderEmpty() {
+    const { unUseRecord } = this.state;
+    if (unUseRecord > 0) {
+      return <div className="empty">
+        <div className="text">
+          还未开通本月机经。 <br />
+          您可至千行网站「我的-工具」查看往期机经。
+      </div>
+        <Button block width={120} radius onClick={() => {
+          linkTo(`/open/${unUseRecord}`);
+        }}>
+          去开通
+      </Button>
+      </div>;
+    }
+    return (
+      <div className="empty">
+        <div className="text">
+          还未购买本月机经。 <br />
+          您可至千行网站「我的-工具」查看往期机经。
+        </div>
+        <Button block width={120} radius onClick={() => {
+          linkTo('/product/service/textbook');
+        }}>
+          去购买
+        </Button>
+      </div>
+    );
+  }
+}

+ 61 - 0
front/project/h5/stores/common.js

@@ -0,0 +1,61 @@
+import BaseStore from '@src/stores/base';
+import { generateUUID } from '@src/services/Tools';
+import { WechatAppId } from '../../Constant';
+
+export default class CommonStore extends BaseStore {
+  /**
+   * 发送短信验证码
+   * @param {*} area 区域码
+   * @param {*} mobile 手机号
+   */
+  sendSms(area, mobile) {
+    return this.apiPost('/common/sms/valid', { area, mobile });
+  }
+
+  /**
+   * 上传至本地服务器
+   * @param {*} file 图片文件
+   */
+  upload(file) {
+    return this.apiForm('/common/upload/image', { file });
+  }
+
+  readyWechatBridge(callback) {
+    if (typeof WeixinJSBridge === 'object' && typeof WeixinJSBridge.invoke === 'function') {
+      callback();
+    } else if (document.addEventListener) {
+      document.addEventListener('WeixinJSBridgeReady', callback, false);
+    } else if (document.attachEvent) {
+      document.attachEvent('WeixinJSBridgeReady', callback);
+      document.attachEvent('onWeixinJSBridgeReady', callback);
+    }
+  }
+
+  readyWechat(url, list) {
+    return this.apiGet('/common/wechat/ticket').then(ticket => {
+      const time = new Date().getTime() / 1000;
+      const nonce = generateUUID(6, 10);
+      const p = `jsapi_ticket=${ticket}&noncestr=${nonce}&timestamp=${time}&url=${url.split('#')[0]}`;
+      const a = list.map(row => row);
+      a.push('checkJsApi');
+      wx.config({
+        debug: false, // 是否打开调试模式,调用的api会被alert出来,在pc端也能看到log信息
+        appid: WechatAppId, // 必填,微信公众号的唯一标识
+        timestamp: time, // 必填,生成签名的时间戳
+        nonceStr: nonce, // 必填,生成签名的随机串
+        signature: sha1(p), // 必填,用于验证的签名
+        jsApiList: a, // 必填,需要使用到的JS接口列表
+      });
+      return new Promise((resolve, reject) => {
+        wx.ready(() => {
+          resolve(wx);
+        });
+        wx.error((err) => {
+          reject(err);
+        });
+      });
+    });
+  }
+}
+
+export const Common = new CommonStore({ key: 'common' });

+ 5 - 1
front/project/h5/stores/index.js

@@ -1,3 +1,7 @@
 import { User } from './user';
+import { Common } from './common';
+import { Main } from './main';
+import { Textbook } from './textbook';
+import { My } from './my';
 
-export default [User];
+export default [User, Common, Main, Textbook, My];

+ 95 - 0
front/project/h5/stores/main.js

@@ -0,0 +1,95 @@
+import BaseStore from '@src/stores/base';
+
+export default class MainStore extends BaseStore {
+  /**
+   * 获取首页配置
+   */
+  getIndex() {
+    return this.apiGet('/base/index');
+  }
+
+  /**
+   * 获取广告列表
+   */
+  getAd() {
+    return this.apiGet('/base/ad');
+  }
+
+  /**
+   * 获取对应位置的提示tips
+   * @param {*} position
+   */
+  getTips(position) {
+    return this.apiGet('/base/tips', { position });
+  }
+
+  /**
+   * 获取考分排行信息
+   */
+  getScore(total, quant) {
+    return this.apiGet('/base/score', { total, quant });
+  }
+
+  /**
+   * 所有练习头2层
+   */
+  getExercise() {
+    return this.getApiCache('API:main:getExercise', () => {
+      return this.apiGet('/base/exercise/main');
+    });
+  }
+
+  /**
+   * 获取对应节点下的子节点
+   * @param {*} id
+   * @param {*} children
+   */
+  getExerciseChildren(id, children) {
+    return this.apiGet('/base/exercise/children', { id, children });
+  }
+
+  /**
+   * 获取对应节点的所有父级节点
+   * @param {*} id
+   */
+  getExerciseParent(id) {
+    return this.apiGet('/base/exercise/parent', { id });
+  }
+
+  /**
+   * 所有模考层级
+   */
+  getExamination() {
+    return this.getApiCache('API:main:getExamination', () => {
+      return this.apiGet('/base/examination/main');
+    });
+  }
+
+  /**
+   * 获取对应节点下的子节点
+   * @param {*} id
+   * @param {*} children
+   */
+  getExaminationChildren(id, children) {
+    return this.apiGet('/base/examination/children', { id, children });
+  }
+
+  /**
+   * 获取对应节点的所有父级节点
+   * @param {*} id
+   */
+  getExaminationParent(id) {
+    return this.apiGet('/base/examination/parent', { id });
+  }
+
+  /**
+   * 获取模考题目数
+   */
+  getExaminationNumber() {
+    return this.getApiCache('API:main:getExaminationNumber', () => {
+      return this.apiGet('/base/examination/number');
+    });
+  }
+}
+
+export const Main = new MainStore({ key: 'main' });

+ 299 - 0
front/project/h5/stores/my.js

@@ -0,0 +1,299 @@
+import BaseStore from '@src/stores/base';
+
+export default class MyStore extends BaseStore {
+  /**
+   * 绑定邮箱
+   * @param {*} email 邮箱
+   */
+  bindEmail(email) {
+    return this.apiPost('/my/email', { email });
+  }
+
+  /**
+   * 实名认证: 正面
+   * @param {*} file
+   */
+  realFront(file) {
+    return this.apiForm('/my/real/front', { file });
+  }
+
+  /**
+   * 实名认证: 反面
+   * @param {*} file
+   */
+  realBack(file) {
+    return this.apiForm('/my/real/back', { file });
+  }
+
+  /**
+   * 实名认证:
+   * @param {*} file
+   */
+  realFinish() {
+    return this.apiPost('/my/real/finish', {});
+  }
+
+  /**
+   * 发送邀请码到邮箱
+   * @param {*} emails
+   */
+  inviteEmail(emails) {
+    return this.apiPost('/my/invite/email', { emails });
+  }
+
+  /**
+   * 用户站内信
+   * @param {*} page
+   * @param {*} size
+   * @param {*} type
+   * @param {*} read
+   */
+  message(page, size, type, read) {
+    return this.apiGet('/my/message', { page, size, type, read });
+  }
+
+  /**
+   * 读取用户消息/全部
+   */
+  readAllMessage() {
+    return this.apiPut('/my/message/read', { all: true });
+  }
+
+  /**
+   * 读取用户消息
+   */
+  readMessage(id) {
+    return this.apiPut('/my/message/read', { all: false, id });
+  }
+
+  /**
+   * 清除最后一次练习记录
+   */
+  clearLatestExercise() {
+    return this.apiPut('/my/clear/exercise/latest').then(() => {
+      this.setState({ info: { latestExercise: 0 } });
+    });
+  }
+
+  /**
+   * 清除最后一次错误组卷记录
+   */
+  clearLatestError() {
+    return this.apiPut('/my/clear/error/latest').then(() => {
+      this.setState({ info: { latestError: 0 } });
+    });
+  }
+
+  /**
+   * 修改备考信息
+   * @param {*} info prepareStatus: 身份  prepareGoal: 目标分数 prepareExaminationTime: 考试时间 prepareScoreTime: 出分时间
+   */
+  editPrepare(info) {
+    return this.apiPut('/my/prepare', { ...info });
+  }
+
+  /**
+   * 获取备考信息
+   */
+  getPrepare() {
+    return this.apiGet('/my/prepare');
+  }
+
+  /**
+   * 获取学习记录
+   * @param {*} date 时间
+   */
+  getStudy(date) {
+    return this.apiGet('/my/study', { date });
+  }
+
+  /**
+   * 获取本周学习记录
+   */
+  getStudyWeek() {
+    return this.apiGet('/my/study/week', {});
+  }
+
+  /**
+   * 获取总学习记录
+   */
+  getStudyTotal() {
+    return this.apiGet('/my/study/total');
+  }
+
+  /**
+   * 添加收藏
+   * @param {*} questionModule
+   * @param {*} questionNoId
+   */
+  addQuestionCollect(questionModule, questionNoId) {
+    return this.apiPut('/my/collect/question/add', { questionModule, questionNoId });
+  }
+
+  /**
+   * 删除收藏
+   * @param {*} questionModule
+   * @param {*} questionNoId
+   */
+  delQuestionCollect(questionModule, questionNoId) {
+    return this.apiDel('/my/collect/question/delete', { questionModule, questionNoId });
+  }
+
+  /**
+   * 收藏卷组
+   * @param {*} questionModule
+   * @param {*} questionNoIds
+   * @param {*} filterTimes
+   */
+  bindQuestionCollect(questionModule, questionNoIds, filterTimes) {
+    return this.apiPost('/my/collect/question/bind', { questionModule, questionNoIds, filterTimes });
+  }
+
+  /**
+   * 获取收藏题目列表
+   * @param {*} questionModule
+   * @param {*} questionType
+   * @param {*} page
+   * @param {*} size
+   * @param {*} startTime
+   * @param {*} endTime
+   * @param {*} order
+   * @param {*} direction
+   */
+  listQuestionCollect(questionModule, questionType, page, size, startTime, endTime, order, direction) {
+    return this.apiGet('/my/collect/question/list', { questionModule, questionType, page, size, startTime, endTime, order, direction });
+  }
+
+  /**
+   * 获取错题列表
+   * @param {*} questionModule
+   * @param {*} page
+   * @param {*} size
+   */
+  listError(questionModule, page, size) {
+    return this.apiGet('/my/error/list', { questionModule, page, size });
+  }
+
+  /**
+   * 错题组卷
+   * @param {*} questionModule
+   * @param {*} questionNoIds
+   * @param {*} filterTimes
+   */
+  bindError(questionModule, questionNoIds, filterTimes) {
+    return this.apiPost('/my/error/bind', { questionModule, questionNoIds, filterTimes });
+  }
+
+  /**
+   * 错题移除
+   * @param {*} ids
+   */
+  clearError(ids) {
+    return this.apiPost('/my/error/clear', { questionNoIds: ids });
+  }
+
+  /**
+   * 移除正确题
+   * @param {*} userReportId
+   */
+  removeError(userReportId) {
+    return this.apiPost('/my/error/remove', { userReportId });
+  }
+
+  /**
+   * 更新笔记
+   * @param {*} questionModule
+   * @param {*} questionNoId
+   * @param {*} content
+   * @param {*} qxContent
+   * @param {*} officialContent
+   * @param {*} associationContent
+   * @param {*} qaContent
+   */
+  updateQuestionNote(questionModule, questionNoId, { content, qxContent, officialContent, associationContent, qaContent }) {
+    return this.apiPut('/my/note/question', { questionModule, questionNoId, content, qxContent, officialContent, associationContent, qaContent });
+  }
+
+  /**
+   * 获取笔记列表
+   * @param {*} questionModule
+   * @param {*} questionType
+   * @param {*} page
+   * @param {*} size
+   * @param {*} startTime
+   * @param {*} endTime
+   * @param {*} order
+   * @param {*} direction
+   */
+  questionNoteList(questionModule, questionType, page, size, startTime, endTime, order, direction) {
+    return this.apiGet('/my/note/question/list', { questionModule, questionType, page, size, startTime, endTime, order, direction });
+  }
+
+  /**
+   * 获取报告列表
+   * @param {*} origin
+   * @param {*} structId
+   * @param {*} page
+   * @param {*} size
+   * @param {*} startTime
+   * @param {*} endTime
+   * @param {*} order
+   * @param {*} direction
+   */
+  reportList(origin, structId, page, size, startTime, endTime, order, direction) {
+    return this.apiGet('/my/report/list', { origin, structId, page, size, startTime, endTime, order, direction });
+  }
+
+  /**
+   * 添加提问
+   * @param {*} target
+   * @param {*} questionModule
+   * @param {*} questionNoId
+   * @param {*} content
+   */
+  addQuestionAsk(userQuestionId, target, questionModule, questionNoId, originContent, content) {
+    return this.apiPost('/my/ask/question', { userQuestionId, target, questionModule, questionNoId, originContent, content });
+  }
+
+  /**
+   * 添加题目勘误
+   * @param {*} moduleId
+   * @param {*} title
+   * @param {*} position
+   * @param {*} originContent
+   * @param {*} content
+   */
+  addFeedbackErrorQuestion(moduleId, title, position, originContent, content) {
+    return this.apiPost('/my/feedback/error/question', { moduleId, title, position, originContent, content });
+  }
+
+  /**
+   * 添加数据勘误
+   * @param {*} moduleId
+   * @param {*} title
+   * @param {*} position
+   * @param {*} originContent
+   * @param {*} content
+   */
+  addErrorData(moduleId, title, position, originContent, content) {
+    return this.apiPost('/my/feedback/error/question', { moduleId, title, position, originContent, content });
+  }
+
+  /**
+   * 获取订单记录
+   * @param {*} id
+   */
+  getOrderRecord(id) {
+    return this.apiGet('/my/order/record', { id });
+  }
+
+  /**
+   * 开通服务、课程等
+   * @param {*} id
+   */
+  useRecord(id) {
+    return this.apiGet('/my/record/use', { id });
+  }
+}
+
+export const My = new MyStore({ key: 'my' });

+ 24 - 0
front/project/h5/stores/textbook.js

@@ -0,0 +1,24 @@
+import BaseStore from '@src/stores/base';
+
+export default class TextbookStore extends BaseStore {
+  /**
+   * 所有机经信息
+   */
+  getInfo() {
+    return this.apiGet('/textbook/info');
+  }
+
+  listYear(year) {
+    return this.apiGet('/textbook/year', { year });
+  }
+
+  listHistory(subject) {
+    return this.apiGet('/textbook/history/list', { subject });
+  }
+
+  listTopic({ page, size, latest, qualitys, isOld, order, direction }) {
+    return this.apiGet('/textbook/topic/list', { page, size, latest, qualitys, isOld, order, direction });
+  }
+}
+
+export const Textbook = new TextbookStore({ key: 'textbook' });

+ 30 - 8
front/project/h5/stores/user.js

@@ -10,6 +10,12 @@ export default class UserStore extends BaseStore {
     return { login: false };
   }
 
+  originInviteCode(inviteCode) {
+    this.setState({
+      inviteCode,
+    });
+  }
+
   /**
    * 验证token
    */
@@ -19,17 +25,22 @@ export default class UserStore extends BaseStore {
 
   /**
    * 登陆
+   * @param {*} area 区域码
    * @param {*} mobile 手机号
    * @param {*} mobileVerifyCode 手机验证码
    * @param {*} inviteCode 邀请人手机/邀请码
    */
-  login(mobile, mobileVerifyCode, inviteCode) {
-    return this.apiPost('/auth/login', { mobile, mobileVerifyCode, inviteCode });
+  login(area, mobile, mobileVerifyCode, inviteCode) {
+    if (!inviteCode) {
+      ({ inviteCode } = this.state);
+    }
+    return this.apiPost('/auth/login', { area, mobile, mobileVerifyCode, inviteCode });
   }
 
   loginWechat(code) {
-    return this.apiGet('/auth/wechat', { code }).then(() => {
-      this.setState({ login: true });
+    return this.apiGet('/auth/wechat', { code }).then((info) => {
+      this.setState({ login: true, info });
+      return info;
     });
   }
 
@@ -42,12 +53,16 @@ export default class UserStore extends BaseStore {
 
   /**
    * 绑定手机
+   * @param {*} area 区域码
    * @param {*} mobile 手机号
    * @param {*} mobileVerifyCode 手机验证码
    * @param {*} inviteCode 邀请人手机/邀请码
    */
-  bind(mobile, mobileVerifyCode, inviteCode) {
-    return this.apiPost('/auth/bind', { mobile, mobileVerifyCode, inviteCode });
+  bind(area, mobile, mobileVerifyCode, inviteCode) {
+    if (!inviteCode) {
+      ({ inviteCode } = this.state);
+    }
+    return this.apiPost('/auth/bind', { area, mobile, mobileVerifyCode, inviteCode });
   }
 
   /**
@@ -61,8 +76,15 @@ export default class UserStore extends BaseStore {
   /**
    * 查询手机对应账号
    */
-  validMobile(mobile) {
-    return this.apiGet('/auth/valid/mobile', { mobile });
+  validMobile(area, mobile) {
+    return this.apiGet('/auth/valid/mobile', { area, mobile });
+  }
+
+  /**
+   * 查询手机是否绑定微信
+   */
+  validWechat(area, mobile) {
+    return this.apiGet('/auth/valid/wechat', { area, mobile });
   }
 }
 

+ 4 - 1
front/project/www/app.js

@@ -13,7 +13,10 @@ export default class extends Component {
     // 初始化登录
     User.token().then(() => {
       this.setState({ show: true });
-    });
+    })
+      .catch(() => {
+        this.setState({ show: true });
+      });
   }
 
   render() {

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

@@ -18,7 +18,7 @@ export default class extends Page {
 
   initData() {
     Main.getIndex().then(result => {
-      this.setState({ result });
+      this.setState(result);
     });
     setTimeout(() => {
       this.createLayout();

+ 4 - 3
front/project/h5/routes/page/changeHistory/index.js

@@ -1,7 +1,8 @@
 export default {
-  path: '/change/history',
-  key: 'change-history',
-  title: '资料更新',
+  path: '/id',
+  matchPath: '/id/:id',
+  key: 'id',
+  title: '邀请', // 绑定手机
   needLogin: false,
   component() {
     return import('./page');

+ 5 - 0
front/project/www/routes/page/id/index.less

@@ -0,0 +1,5 @@
+@charset "utf-8";
+
+#id {
+  padding: 15px;
+}

+ 4 - 0
front/project/h5/routes/textbook/index/page.js

@@ -1,9 +1,13 @@
 import React from 'react';
 import './index.less';
 import Page from '@src/containers/Page';
+import { User } from '../../../stores/user';
 
 export default class extends Page {
   init() {
+    const { id } = this.params;
+    User.originInviteCode(id);
+    replaceLink('/');
   }
 
   renderView() {

+ 3 - 2
front/project/www/stores/common.js

@@ -3,10 +3,11 @@ import BaseStore from '@src/stores/base';
 export default class CommonStore extends BaseStore {
   /**
    * 发送短信验证码
+   * @param {*} area 区域码
    * @param {*} mobile 手机号
    */
-  sendSms(mobile) {
-    return this.apiPost('/common/sms/valid', { mobile });
+  sendSms(area, mobile) {
+    return this.apiPost('/common/sms/valid', { area, mobile });
   }
 
   /**

+ 8 - 0
front/project/www/stores/main.js

@@ -90,6 +90,14 @@ export default class MainStore extends BaseStore {
       return this.apiGet('/base/examination/number');
     });
   }
+
+  listFaq(page, size, channel, position) {
+    return this.apiGet('/base/faq/list', { page, size, channel, position });
+  }
+
+  listComment(page, size, channel, position) {
+    return this.apiGet('/base/comment/list', { page, size, channel, position });
+  }
 }
 
 export const Main = new MainStore({ key: 'main' });

+ 0 - 8
front/project/www/stores/my.js

@@ -18,14 +18,6 @@ export default class MyStore extends BaseStore {
   }
 
   /**
-   * 实名认证
-   * @param {*} file
-   */
-  real(file) {
-    return this.apiForm('/my/real', { file });
-  }
-
-  /**
    * 用户站内信
    * @param {*} page
    * @param {*} size

+ 12 - 0
front/project/www/stores/textbook.js

@@ -7,6 +7,18 @@ export default class TextbookStore extends BaseStore {
   getInfo() {
     return this.apiGet('/textbook/info');
   }
+
+  listYear(year) {
+    return this.apiGet('/textbook/year', { year });
+  }
+
+  listHistory(subject) {
+    return this.apiGet('/textbook/history/list', { subject });
+  }
+
+  listTopic(page, size, latest, qualitys, isOld, order, direction) {
+    return this.apiGet('/textbook/topic/list', { page, size, latest, qualitys, isOld, order, direction });
+  }
 }
 
 export const Textbook = new TextbookStore({ key: 'textbook' });

+ 16 - 0
front/project/www/stores/user.js

@@ -13,6 +13,12 @@ export default class UserStore extends BaseStore {
     return { login: !!token, sentenceTrail };
   }
 
+  originInviteCode(inviteCode) {
+    this.setState({
+      inviteCode,
+    });
+  }
+
   /**
    * 设置长难句试用
    */
@@ -43,6 +49,9 @@ export default class UserStore extends BaseStore {
    * @param {*} inviteCode 邀请人手机/邀请码
    */
   login(mobile, mobileVerifyCode, inviteCode) {
+    if (!inviteCode) {
+      ({ inviteCode } = this.state);
+    }
     return this.apiPost('/auth/login', { mobile, mobileVerifyCode, inviteCode });
   }
 
@@ -83,6 +92,13 @@ export default class UserStore extends BaseStore {
   validMobile(mobile) {
     return this.apiGet('/auth/valid/mobile', { mobile });
   }
+
+  /**
+   * 查询手机是否绑定微信
+   */
+  validWechat(area, mobile) {
+    return this.apiGet('/auth/valid/wechat', { area, mobile });
+  }
 }
 
 export const User = new UserStore({ key: 'user', local: true });

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

@@ -11,13 +11,13 @@ export default class extends Component {
     const state = {
       page: {
         current: core.query.page ? Number(core.query.page) : 1,
-        pageSize: core.query.number ? Number(core.query.number) : 20,
+        pageSize: core.query.size ? Number(core.query.size) : 20,
         showQuickJumper: true,
         showSizeChanger: true,
         showTotal: total => `总数: ${total}`,
         total: 0,
       },
-      search: Object.assign({ page: 1, number: 20 }, core.query),
+      search: Object.assign({ page: 1, size: 20 }, core.query),
       list: [],
       data: {},
       selectedKeys: [],
@@ -65,7 +65,7 @@ export default class extends Component {
     this.outPage();
   }
 
-  init() {}
+  init() { }
 
   initState() {
     return {};

+ 1 - 1
front/src/routes/index.js

@@ -4,4 +4,4 @@ import Empty from './empty';
 import Power from './power';
 import Login from './login';
 
-export default [Empty, Power, Login, ...project];
+export default [...project, Empty, Power, Login];

+ 12 - 0
front/src/services/Tools.js

@@ -192,6 +192,18 @@ export function search(data = [], key, value) {
   return index;
 }
 
+export function dataURLtoBlob(dataurl) {
+  const arr = dataurl.split(',');
+  const mime = arr[0].match(/:(.*?);/)[1];
+  const bstr = atob(arr[1]);
+  const n = bstr.length;
+  const u8arr = new Uint8Array(n);
+  for (let i = 0; i < n; i += 1) {
+    u8arr[i] = bstr.charCodeAt(i);
+  }
+  return new Blob([u8arr], { type: mime });
+}
+
 export function formatSecond(value) {
   let secondTime = parseInt(value || 0, 10); // 秒
   let minuteTime = 0;

Fichier diff supprimé car celui-ci est trop grand
+ 9 - 0
front/src/static/sha1.js


+ 24 - 0
server/data/src/main/java/com/qxgmat/data/constants/enums/TopicQuality.java

@@ -0,0 +1,24 @@
+package com.qxgmat.data.constants.enums;
+
+/**
+ * Created by gaojie on 2017/11/19.
+ */
+public enum TopicQuality {
+    INCOMPLETE("incomplete", "残缺"),
+    MORECOMPLETE("morecomplete", "较完整"),
+    COMPLETE("complete","完整"),
+    ;
+    final static public String message = "机经题目质量";
+
+    public String key;
+    public String title;
+    private TopicQuality(String key, String title){
+        this.key = key;
+        this.title = title;
+    }
+
+    public static TopicQuality ValueOf(String name){
+        if (name == null) return null;
+        return TopicQuality.valueOf(name.toUpperCase());
+    }
+}

+ 18 - 0
server/data/src/main/java/com/qxgmat/data/constants/enums/module/FaqModule.java

@@ -0,0 +1,18 @@
+package com.qxgmat.data.constants.enums.module;
+
+// Faq模块
+public enum FaqModule {
+    CONSULT("consult"),
+    SYSTEM("system"),
+
+    ;
+    public String key;
+    private FaqModule(String key){
+        this.key = key;
+    }
+
+    public static FaqModule ValueOf(String name){
+        if (name == null) return null;
+        return FaqModule.valueOf(name.toUpperCase());
+    }
+}

+ 7 - 0
server/data/src/main/java/com/qxgmat/data/dao/TextbookTopicMapper.java

@@ -0,0 +1,7 @@
+package com.qxgmat.data.dao;
+
+import com.nuliji.tools.mybatis.Mapper;
+import com.qxgmat.data.dao.entity.TextbookTopic;
+
+public interface TextbookTopicMapper extends Mapper<TextbookTopic> {
+}

+ 7 - 0
server/data/src/main/java/com/qxgmat/data/dao/UserTextbookEnrollMapper.java

@@ -0,0 +1,7 @@
+package com.qxgmat.data.dao;
+
+import com.nuliji.tools.mybatis.Mapper;
+import com.qxgmat.data.dao.entity.UserTextbookEnroll;
+
+public interface UserTextbookEnrollMapper extends Mapper<UserTextbookEnroll> {
+}

+ 7 - 0
server/data/src/main/java/com/qxgmat/data/dao/UserTextbookFeedbackMapper.java

@@ -0,0 +1,7 @@
+package com.qxgmat.data.dao;
+
+import com.nuliji.tools.mybatis.Mapper;
+import com.qxgmat.data.dao.entity.UserTextbookFeedback;
+
+public interface UserTextbookFeedbackMapper extends Mapper<UserTextbookFeedback> {
+}

+ 52 - 17
server/data/src/main/java/com/qxgmat/data/dao/entity/Faq.java

@@ -12,6 +12,12 @@ public class Faq implements Serializable {
     private Integer id;
 
     /**
+     * faq模块
+     */
+    @Column(name = "`faq_module`")
+    private String faqModule;
+
+    /**
      * 用户id
      */
     @Column(name = "`user_id`")
@@ -62,8 +68,8 @@ public class Faq implements Serializable {
     /**
      * 问答状态:0未回答,1回答,忽略
      */
-    @Column(name = "`status`")
-    private Integer status;
+    @Column(name = "`answer_status`")
+    private Integer answerStatus;
 
     /**
      * 回复时间
@@ -109,6 +115,24 @@ public class Faq implements Serializable {
     }
 
     /**
+     * 获取faq模块
+     *
+     * @return faq_module - faq模块
+     */
+    public String getFaqModule() {
+        return faqModule;
+    }
+
+    /**
+     * 设置faq模块
+     *
+     * @param faqModule faq模块
+     */
+    public void setFaqModule(String faqModule) {
+        this.faqModule = faqModule;
+    }
+
+    /**
      * 获取用户id
      *
      * @return user_id - 用户id
@@ -255,19 +279,19 @@ public class Faq implements Serializable {
     /**
      * 获取问答状态:0未回答,1回答,忽略
      *
-     * @return status - 问答状态:0未回答,1回答,忽略
+     * @return answer_status - 问答状态:0未回答,1回答,忽略
      */
-    public Integer getStatus() {
-        return status;
+    public Integer getAnswerStatus() {
+        return answerStatus;
     }
 
     /**
      * 设置问答状态:0未回答,1回答,忽略
      *
-     * @param status 问答状态:0未回答,1回答,忽略
+     * @param answerStatus 问答状态:0未回答,1回答,忽略
      */
-    public void setStatus(Integer status) {
-        this.status = status;
+    public void setAnswerStatus(Integer answerStatus) {
+        this.answerStatus = answerStatus;
     }
 
     /**
@@ -363,6 +387,7 @@ public class Faq implements Serializable {
         sb.append(" [");
         sb.append("Hash = ").append(hashCode());
         sb.append(", id=").append(id);
+        sb.append(", faqModule=").append(faqModule);
         sb.append(", userId=").append(userId);
         sb.append(", email=").append(email);
         sb.append(", phone=").append(phone);
@@ -371,7 +396,7 @@ public class Faq implements Serializable {
         sb.append(", position=").append(position);
         sb.append(", managerId=").append(managerId);
         sb.append(", isSpecial=").append(isSpecial);
-        sb.append(", status=").append(status);
+        sb.append(", answerStatus=").append(answerStatus);
         sb.append(", answerTime=").append(answerTime);
         sb.append(", isSystem=").append(isSystem);
         sb.append(", createTime=").append(createTime);
@@ -401,6 +426,16 @@ public class Faq implements Serializable {
         }
 
         /**
+         * 设置faq模块
+         *
+         * @param faqModule faq模块
+         */
+        public Builder faqModule(String faqModule) {
+            obj.setFaqModule(faqModule);
+            return this;
+        }
+
+        /**
          * 设置用户id
          *
          * @param userId 用户id
@@ -481,22 +516,22 @@ public class Faq implements Serializable {
         }
 
         /**
-         * 设置问答状态:0未回答,1回答,忽略
+         * 设置回复内容
          *
-         * @param status 问答状态:0未回答,1回答,忽略
+         * @param answer 回复内容
          */
-        public Builder status(Integer status) {
-            obj.setStatus(status);
+        public Builder answer(String answer) {
+            obj.setAnswer(answer);
             return this;
         }
 
         /**
-         * 设置回复内容
+         * 设置问答状态:0未回答,1回答,忽略
          *
-         * @param answer 回复内容
+         * @param answerStatus 问答状态:0未回答,1回答,忽略
          */
-        public Builder answer(String answer) {
-            obj.setAnswer(answer);
+        public Builder answerStatus(Integer answerStatus) {
+            obj.setAnswerStatus(answerStatus);
             return this;
         }
 

+ 8 - 8
server/data/src/main/java/com/qxgmat/data/dao/entity/TextbookLibrary.java

@@ -15,13 +15,13 @@ public class TextbookLibrary implements Serializable {
      * 库头
      */
     @Column(name = "`start_date`")
-    private String startDate;
+    private Date startDate;
 
     /**
      * 库尾
      */
     @Column(name = "`end_date`")
-    private String endDate;
+    private Date endDate;
 
     /**
      * 数学
@@ -110,7 +110,7 @@ public class TextbookLibrary implements Serializable {
      *
      * @return start_date - 库头
      */
-    public String getStartDate() {
+    public Date getStartDate() {
         return startDate;
     }
 
@@ -119,7 +119,7 @@ public class TextbookLibrary implements Serializable {
      *
      * @param startDate 库头
      */
-    public void setStartDate(String startDate) {
+    public void setStartDate(Date startDate) {
         this.startDate = startDate;
     }
 
@@ -128,7 +128,7 @@ public class TextbookLibrary implements Serializable {
      *
      * @return end_date - 库尾
      */
-    public String getEndDate() {
+    public Date getEndDate() {
         return endDate;
     }
 
@@ -137,7 +137,7 @@ public class TextbookLibrary implements Serializable {
      *
      * @param endDate 库尾
      */
-    public void setEndDate(String endDate) {
+    public void setEndDate(Date endDate) {
         this.endDate = endDate;
     }
 
@@ -398,7 +398,7 @@ public class TextbookLibrary implements Serializable {
          *
          * @param startDate 库头
          */
-        public Builder startDate(String startDate) {
+        public Builder startDate(Date startDate) {
             obj.setStartDate(startDate);
             return this;
         }
@@ -408,7 +408,7 @@ public class TextbookLibrary implements Serializable {
          *
          * @param endDate 库尾
          */
-        public Builder endDate(String endDate) {
+        public Builder endDate(Date endDate) {
             obj.setEndDate(endDate);
             return this;
         }

+ 396 - 0
server/data/src/main/java/com/qxgmat/data/dao/entity/TextbookTopic.java

@@ -0,0 +1,396 @@
+package com.qxgmat.data.dao.entity;
+
+import java.io.Serializable;
+import java.util.Date;
+import javax.persistence.*;
+
+@Table(name = "textbook_topic")
+public class TextbookTopic implements Serializable {
+    @Id
+    @Column(name = "`id`")
+    @GeneratedValue(strategy = GenerationType.IDENTITY)
+    private Integer id;
+
+    /**
+     * 换库表
+     */
+    @Column(name = "`library_id`")
+    private Integer libraryId;
+
+    /**
+     * 单项-题型
+     */
+    @Column(name = "`question_type`")
+    private String questionType;
+
+    /**
+     * 题目序号
+     */
+    @Column(name = "`no`")
+    private Integer no;
+
+    /**
+     * 关键词
+     */
+    @Column(name = "`keyword`")
+    private String keyword;
+
+    /**
+     * 质量
+     */
+    @Column(name = "`quality`")
+    private String quality;
+
+    /**
+     * 是否考古题:1是,0否
+     */
+    @Column(name = "`is_old`")
+    private Integer isOld;
+
+    @Column(name = "`create_time`")
+    private Date createTime;
+
+    @Column(name = "`update_time`")
+    private Date updateTime;
+
+    /**
+     * 题目内容
+     */
+    @Column(name = "`detail`")
+    private String detail;
+
+    /**
+     * 题目解析
+     */
+    @Column(name = "`content`")
+    private String content;
+
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * @return id
+     */
+    public Integer getId() {
+        return id;
+    }
+
+    /**
+     * @param id
+     */
+    public void setId(Integer id) {
+        this.id = id;
+    }
+
+    /**
+     * 获取换库表
+     *
+     * @return library_id - 换库表
+     */
+    public Integer getLibraryId() {
+        return libraryId;
+    }
+
+    /**
+     * 设置换库表
+     *
+     * @param libraryId 换库表
+     */
+    public void setLibraryId(Integer libraryId) {
+        this.libraryId = libraryId;
+    }
+
+    /**
+     * 获取单项-题型
+     *
+     * @return question_type - 单项-题型
+     */
+    public String getQuestionType() {
+        return questionType;
+    }
+
+    /**
+     * 设置单项-题型
+     *
+     * @param questionType 单项-题型
+     */
+    public void setQuestionType(String questionType) {
+        this.questionType = questionType;
+    }
+
+    /**
+     * 获取题目序号
+     *
+     * @return no - 题目序号
+     */
+    public Integer getNo() {
+        return no;
+    }
+
+    /**
+     * 设置题目序号
+     *
+     * @param no 题目序号
+     */
+    public void setNo(Integer no) {
+        this.no = no;
+    }
+
+    /**
+     * 获取关键词
+     *
+     * @return keyword - 关键词
+     */
+    public String getKeyword() {
+        return keyword;
+    }
+
+    /**
+     * 设置关键词
+     *
+     * @param keyword 关键词
+     */
+    public void setKeyword(String keyword) {
+        this.keyword = keyword;
+    }
+
+    /**
+     * 获取质量
+     *
+     * @return quality - 质量
+     */
+    public String getQuality() {
+        return quality;
+    }
+
+    /**
+     * 设置质量
+     *
+     * @param quality 质量
+     */
+    public void setQuality(String quality) {
+        this.quality = quality;
+    }
+
+    /**
+     * 获取是否考古题:1是,0否
+     *
+     * @return is_old - 是否考古题:1是,0否
+     */
+    public Integer getIsOld() {
+        return isOld;
+    }
+
+    /**
+     * 设置是否考古题:1是,0否
+     *
+     * @param isOld 是否考古题:1是,0否
+     */
+    public void setIsOld(Integer isOld) {
+        this.isOld = isOld;
+    }
+
+    /**
+     * @return create_time
+     */
+    public Date getCreateTime() {
+        return createTime;
+    }
+
+    /**
+     * @param createTime
+     */
+    public void setCreateTime(Date createTime) {
+        this.createTime = createTime;
+    }
+
+    /**
+     * @return update_time
+     */
+    public Date getUpdateTime() {
+        return updateTime;
+    }
+
+    /**
+     * @param updateTime
+     */
+    public void setUpdateTime(Date updateTime) {
+        this.updateTime = updateTime;
+    }
+
+    /**
+     * 获取题目内容
+     *
+     * @return detail - 题目内容
+     */
+    public String getDetail() {
+        return detail;
+    }
+
+    /**
+     * 设置题目内容
+     *
+     * @param detail 题目内容
+     */
+    public void setDetail(String detail) {
+        this.detail = detail;
+    }
+
+    /**
+     * 获取题目解析
+     *
+     * @return content - 题目解析
+     */
+    public String getContent() {
+        return content;
+    }
+
+    /**
+     * 设置题目解析
+     *
+     * @param content 题目解析
+     */
+    public void setContent(String content) {
+        this.content = content;
+    }
+
+    @Override
+    public String toString() {
+        StringBuilder sb = new StringBuilder();
+        sb.append(getClass().getSimpleName());
+        sb.append(" [");
+        sb.append("Hash = ").append(hashCode());
+        sb.append(", id=").append(id);
+        sb.append(", libraryId=").append(libraryId);
+        sb.append(", questionType=").append(questionType);
+        sb.append(", no=").append(no);
+        sb.append(", keyword=").append(keyword);
+        sb.append(", quality=").append(quality);
+        sb.append(", isOld=").append(isOld);
+        sb.append(", createTime=").append(createTime);
+        sb.append(", updateTime=").append(updateTime);
+        sb.append(", detail=").append(detail);
+        sb.append(", content=").append(content);
+        sb.append("]");
+        return sb.toString();
+    }
+
+    public static TextbookTopic.Builder builder() {
+        return new TextbookTopic.Builder();
+    }
+
+    public static class Builder {
+        private TextbookTopic obj;
+
+        public Builder() {
+            this.obj = new TextbookTopic();
+        }
+
+        /**
+         * @param id
+         */
+        public Builder id(Integer id) {
+            obj.setId(id);
+            return this;
+        }
+
+        /**
+         * 设置换库表
+         *
+         * @param libraryId 换库表
+         */
+        public Builder libraryId(Integer libraryId) {
+            obj.setLibraryId(libraryId);
+            return this;
+        }
+
+        /**
+         * 设置单项-题型
+         *
+         * @param questionType 单项-题型
+         */
+        public Builder questionType(String questionType) {
+            obj.setQuestionType(questionType);
+            return this;
+        }
+
+        /**
+         * 设置题目序号
+         *
+         * @param no 题目序号
+         */
+        public Builder no(Integer no) {
+            obj.setNo(no);
+            return this;
+        }
+
+        /**
+         * 设置关键词
+         *
+         * @param keyword 关键词
+         */
+        public Builder keyword(String keyword) {
+            obj.setKeyword(keyword);
+            return this;
+        }
+
+        /**
+         * 设置质量
+         *
+         * @param quality 质量
+         */
+        public Builder quality(String quality) {
+            obj.setQuality(quality);
+            return this;
+        }
+
+        /**
+         * 设置是否考古题:1是,0否
+         *
+         * @param isOld 是否考古题:1是,0否
+         */
+        public Builder isOld(Integer isOld) {
+            obj.setIsOld(isOld);
+            return this;
+        }
+
+        /**
+         * @param createTime
+         */
+        public Builder createTime(Date createTime) {
+            obj.setCreateTime(createTime);
+            return this;
+        }
+
+        /**
+         * @param updateTime
+         */
+        public Builder updateTime(Date updateTime) {
+            obj.setUpdateTime(updateTime);
+            return this;
+        }
+
+        /**
+         * 设置题目内容
+         *
+         * @param detail 题目内容
+         */
+        public Builder detail(String detail) {
+            obj.setDetail(detail);
+            return this;
+        }
+
+        /**
+         * 设置题目解析
+         *
+         * @param content 题目解析
+         */
+        public Builder content(String content) {
+            obj.setContent(content);
+            return this;
+        }
+
+        public TextbookTopic build() {
+            return this.obj;
+        }
+    }
+}

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

@@ -82,6 +82,12 @@ public class User implements Serializable {
     private Date wechatExpireTime;
 
     /**
+     * 实名通过时间
+     */
+    @Column(name = "`real_time`")
+    private Date realTime;
+
+    /**
      * 实名:姓名
      */
     @Column(name = "`real_name`")
@@ -118,6 +124,12 @@ public class User implements Serializable {
     private Integer realStatus;
 
     /**
+     * 备考设置时间
+     */
+    @Column(name = "`prepare_time`")
+    private Date prepareTime;
+
+    /**
      * 备考:身份
      */
     @Column(name = "`prepare_status`")
@@ -415,6 +427,24 @@ public class User implements Serializable {
     }
 
     /**
+     * 获取实名通过时间
+     *
+     * @return real_time - 实名通过时间
+     */
+    public Date getRealTime() {
+        return realTime;
+    }
+
+    /**
+     * 设置实名通过时间
+     *
+     * @param realTime 实名通过时间
+     */
+    public void setRealTime(Date realTime) {
+        this.realTime = realTime;
+    }
+
+    /**
      * 获取实名:姓名
      *
      * @return real_name - 实名:姓名
@@ -523,6 +553,24 @@ public class User implements Serializable {
     }
 
     /**
+     * 获取备考设置时间
+     *
+     * @return prepare_time - 备考设置时间
+     */
+    public Date getPrepareTime() {
+        return prepareTime;
+    }
+
+    /**
+     * 设置备考设置时间
+     *
+     * @param prepareTime 备考设置时间
+     */
+    public void setPrepareTime(Date prepareTime) {
+        this.prepareTime = prepareTime;
+    }
+
+    /**
      * 获取备考:身份
      *
      * @return prepare_status - 备考:身份
@@ -753,12 +801,14 @@ public class User implements Serializable {
         sb.append(", wechatAccessToken=").append(wechatAccessToken);
         sb.append(", wechatRefreshToken=").append(wechatRefreshToken);
         sb.append(", wechatExpireTime=").append(wechatExpireTime);
+        sb.append(", realTime=").append(realTime);
         sb.append(", realName=").append(realName);
         sb.append(", realAddress=").append(realAddress);
         sb.append(", realIdentity=").append(realIdentity);
         sb.append(", realPhotoFront=").append(realPhotoFront);
         sb.append(", realPhotoBack=").append(realPhotoBack);
         sb.append(", realStatus=").append(realStatus);
+        sb.append(", prepareTime=").append(prepareTime);
         sb.append(", prepareStatus=").append(prepareStatus);
         sb.append(", prepareGoal=").append(prepareGoal);
         sb.append(", prepareExaminationTime=").append(prepareExaminationTime);
@@ -913,6 +963,16 @@ public class User implements Serializable {
         }
 
         /**
+         * 设置实名通过时间
+         *
+         * @param realTime 实名通过时间
+         */
+        public Builder realTime(Date realTime) {
+            obj.setRealTime(realTime);
+            return this;
+        }
+
+        /**
          * 设置实名:姓名
          *
          * @param realName 实名:姓名
@@ -973,6 +1033,16 @@ public class User implements Serializable {
         }
 
         /**
+         * 设置备考设置时间
+         *
+         * @param prepareTime 备考设置时间
+         */
+        public Builder prepareTime(Date prepareTime) {
+            obj.setPrepareTime(prepareTime);
+            return this;
+        }
+
+        /**
          * 设置备考:身份
          *
          * @param prepareStatus 备考:身份

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

@@ -49,6 +49,12 @@ public class UserCourseAppointment implements Serializable {
     private String channel;
 
     /**
+     * 对应用户名
+     */
+    @Column(name = "`channel_username`")
+    private String channelUsername;
+
+    /**
      * 预约开始时间
      */
     @Column(name = "`start_time`")
@@ -212,6 +218,24 @@ public class UserCourseAppointment implements Serializable {
     }
 
     /**
+     * 获取对应用户名
+     *
+     * @return channel_username - 对应用户名
+     */
+    public String getChannelUsername() {
+        return channelUsername;
+    }
+
+    /**
+     * 设置对应用户名
+     *
+     * @param channelUsername 对应用户名
+     */
+    public void setChannelUsername(String channelUsername) {
+        this.channelUsername = channelUsername;
+    }
+
+    /**
      * 获取预约开始时间
      *
      * @return start_time - 预约开始时间
@@ -346,6 +370,7 @@ public class UserCourseAppointment implements Serializable {
         sb.append(", recordId=").append(recordId);
         sb.append(", courseId=").append(courseId);
         sb.append(", channel=").append(channel);
+        sb.append(", channelUsername=").append(channelUsername);
         sb.append(", startTime=").append(startTime);
         sb.append(", endTime=").append(endTime);
         sb.append(", paperId=").append(paperId);
@@ -447,6 +472,16 @@ public class UserCourseAppointment implements Serializable {
         }
 
         /**
+         * 设置对应用户名
+         *
+         * @param channelUsername 对应用户名
+         */
+        public Builder channelUsername(String channelUsername) {
+            obj.setChannelUsername(channelUsername);
+            return this;
+        }
+
+        /**
          * 设置预约开始时间
          *
          * @param startTime 预约开始时间

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

@@ -23,18 +23,18 @@ public class UserService implements Serializable {
     @Column(name = "`service`")
     private String service;
 
+    /**
+     * 机经是否邮箱订阅
+     */
+    @Column(name = "`is_subscribe`")
+    private Integer isSubscribe;
+
     @Column(name = "`start_time`")
     private Date startTime;
 
     @Column(name = "`expire_time`")
     private Date expireTime;
 
-    /**
-     * 服务当前扩展信息:json
-     */
-    @Column(name = "`extend`")
-    private String extend;
-
     private static final long serialVersionUID = 1L;
 
     /**
@@ -88,6 +88,24 @@ public class UserService implements Serializable {
     }
 
     /**
+     * 获取机经是否邮箱订阅
+     *
+     * @return is_subscribe - 机经是否邮箱订阅
+     */
+    public Integer getIsSubscribe() {
+        return isSubscribe;
+    }
+
+    /**
+     * 设置机经是否邮箱订阅
+     *
+     * @param isSubscribe 机经是否邮箱订阅
+     */
+    public void setIsSubscribe(Integer isSubscribe) {
+        this.isSubscribe = isSubscribe;
+    }
+
+    /**
      * @return start_time
      */
     public Date getStartTime() {
@@ -115,24 +133,6 @@ public class UserService implements Serializable {
         this.expireTime = expireTime;
     }
 
-    /**
-     * 获取服务当前扩展信息:json
-     *
-     * @return extend - 服务当前扩展信息:json
-     */
-    public String getExtend() {
-        return extend;
-    }
-
-    /**
-     * 设置服务当前扩展信息:json
-     *
-     * @param extend 服务当前扩展信息:json
-     */
-    public void setExtend(String extend) {
-        this.extend = extend;
-    }
-
     @Override
     public String toString() {
         StringBuilder sb = new StringBuilder();
@@ -142,9 +142,9 @@ public class UserService implements Serializable {
         sb.append(", id=").append(id);
         sb.append(", userId=").append(userId);
         sb.append(", service=").append(service);
+        sb.append(", isSubscribe=").append(isSubscribe);
         sb.append(", startTime=").append(startTime);
         sb.append(", expireTime=").append(expireTime);
-        sb.append(", extend=").append(extend);
         sb.append("]");
         return sb.toString();
     }
@@ -189,6 +189,16 @@ public class UserService implements Serializable {
         }
 
         /**
+         * 设置机经是否邮箱订阅
+         *
+         * @param isSubscribe 机经是否邮箱订阅
+         */
+        public Builder isSubscribe(Integer isSubscribe) {
+            obj.setIsSubscribe(isSubscribe);
+            return this;
+        }
+
+        /**
          * @param startTime
          */
         public Builder startTime(Date startTime) {
@@ -204,16 +214,6 @@ public class UserService implements Serializable {
             return this;
         }
 
-        /**
-         * 设置服务当前扩展信息:json
-         *
-         * @param extend 服务当前扩展信息:json
-         */
-        public Builder extend(String extend) {
-            obj.setExtend(extend);
-            return this;
-        }
-
         public UserService build() {
             return this.obj;
         }

+ 160 - 0
server/data/src/main/java/com/qxgmat/data/dao/entity/UserTextbookEnroll.java

@@ -0,0 +1,160 @@
+package com.qxgmat.data.dao.entity;
+
+import java.io.Serializable;
+import java.util.Date;
+import javax.persistence.*;
+
+@Table(name = "user_textbook_enroll")
+public class UserTextbookEnroll implements Serializable {
+    @Id
+    @Column(name = "`id`")
+    @GeneratedValue(strategy = GenerationType.IDENTITY)
+    private Integer id;
+
+    /**
+     * 用户ID
+     */
+    @Column(name = "`user_id`")
+    private Integer userId;
+
+    /**
+     * 报名月份
+     */
+    @Column(name = "`month`")
+    private Date month;
+
+    @Column(name = "`create_time`")
+    private Date createTime;
+
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * @return id
+     */
+    public Integer getId() {
+        return id;
+    }
+
+    /**
+     * @param id
+     */
+    public void setId(Integer id) {
+        this.id = id;
+    }
+
+    /**
+     * 获取用户ID
+     *
+     * @return user_id - 用户ID
+     */
+    public Integer getUserId() {
+        return userId;
+    }
+
+    /**
+     * 设置用户ID
+     *
+     * @param userId 用户ID
+     */
+    public void setUserId(Integer userId) {
+        this.userId = userId;
+    }
+
+    /**
+     * 获取报名月份
+     *
+     * @return month - 报名月份
+     */
+    public Date getMonth() {
+        return month;
+    }
+
+    /**
+     * 设置报名月份
+     *
+     * @param month 报名月份
+     */
+    public void setMonth(Date month) {
+        this.month = month;
+    }
+
+    /**
+     * @return create_time
+     */
+    public Date getCreateTime() {
+        return createTime;
+    }
+
+    /**
+     * @param createTime
+     */
+    public void setCreateTime(Date createTime) {
+        this.createTime = createTime;
+    }
+
+    @Override
+    public String toString() {
+        StringBuilder sb = new StringBuilder();
+        sb.append(getClass().getSimpleName());
+        sb.append(" [");
+        sb.append("Hash = ").append(hashCode());
+        sb.append(", id=").append(id);
+        sb.append(", userId=").append(userId);
+        sb.append(", month=").append(month);
+        sb.append(", createTime=").append(createTime);
+        sb.append("]");
+        return sb.toString();
+    }
+
+    public static UserTextbookEnroll.Builder builder() {
+        return new UserTextbookEnroll.Builder();
+    }
+
+    public static class Builder {
+        private UserTextbookEnroll obj;
+
+        public Builder() {
+            this.obj = new UserTextbookEnroll();
+        }
+
+        /**
+         * @param id
+         */
+        public Builder id(Integer id) {
+            obj.setId(id);
+            return this;
+        }
+
+        /**
+         * 设置用户ID
+         *
+         * @param userId 用户ID
+         */
+        public Builder userId(Integer userId) {
+            obj.setUserId(userId);
+            return this;
+        }
+
+        /**
+         * 设置报名月份
+         *
+         * @param month 报名月份
+         */
+        public Builder month(Date month) {
+            obj.setMonth(month);
+            return this;
+        }
+
+        /**
+         * @param createTime
+         */
+        public Builder createTime(Date createTime) {
+            obj.setCreateTime(createTime);
+            return this;
+        }
+
+        public UserTextbookEnroll build() {
+            return this.obj;
+        }
+    }
+}

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

@@ -0,0 +1,335 @@
+package com.qxgmat.data.dao.entity;
+
+import java.io.Serializable;
+import java.util.Date;
+import javax.persistence.*;
+
+@Table(name = "user_textbook_feedback")
+public class UserTextbookFeedback implements Serializable {
+    @Id
+    @Column(name = "`id`")
+    @GeneratedValue(strategy = GenerationType.IDENTITY)
+    private Integer id;
+
+    /**
+     * 用户id
+     */
+    @Column(name = "`user_id`")
+    private Integer userId;
+
+    /**
+     * 机经问题
+     */
+    @Column(name = "`topic_id`")
+    private Integer topicId;
+
+    /**
+     * 换库表
+     */
+    @Column(name = "`library_id`")
+    private Integer libraryId;
+
+    /**
+     * 反馈类型
+     */
+    @Column(name = "`target`")
+    private String target;
+
+    @Column(name = "`create_time`")
+    private Date createTime;
+
+    /**
+     * 处理状态
+     */
+    @Column(name = "`status`")
+    private Integer status;
+
+    /**
+     * 处理时间
+     */
+    @Column(name = "`handle_time`")
+    private Date handleTime;
+
+    /**
+     * 正确内容
+     */
+    @Column(name = "`content`")
+    private String content;
+
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * @return id
+     */
+    public Integer getId() {
+        return id;
+    }
+
+    /**
+     * @param id
+     */
+    public void setId(Integer id) {
+        this.id = id;
+    }
+
+    /**
+     * 获取用户id
+     *
+     * @return user_id - 用户id
+     */
+    public Integer getUserId() {
+        return userId;
+    }
+
+    /**
+     * 设置用户id
+     *
+     * @param userId 用户id
+     */
+    public void setUserId(Integer userId) {
+        this.userId = userId;
+    }
+
+    /**
+     * 获取机经问题
+     *
+     * @return topic_id - 机经问题
+     */
+    public Integer getTopicId() {
+        return topicId;
+    }
+
+    /**
+     * 设置机经问题
+     *
+     * @param topicId 机经问题
+     */
+    public void setTopicId(Integer topicId) {
+        this.topicId = topicId;
+    }
+
+    /**
+     * 获取换库表
+     *
+     * @return library_id - 换库表
+     */
+    public Integer getLibraryId() {
+        return libraryId;
+    }
+
+    /**
+     * 设置换库表
+     *
+     * @param libraryId 换库表
+     */
+    public void setLibraryId(Integer libraryId) {
+        this.libraryId = libraryId;
+    }
+
+    /**
+     * 获取反馈类型
+     *
+     * @return target - 反馈类型
+     */
+    public String getTarget() {
+        return target;
+    }
+
+    /**
+     * 设置反馈类型
+     *
+     * @param target 反馈类型
+     */
+    public void setTarget(String target) {
+        this.target = target;
+    }
+
+    /**
+     * @return create_time
+     */
+    public Date getCreateTime() {
+        return createTime;
+    }
+
+    /**
+     * @param createTime
+     */
+    public void setCreateTime(Date createTime) {
+        this.createTime = createTime;
+    }
+
+    /**
+     * 获取处理状态
+     *
+     * @return status - 处理状态
+     */
+    public Integer getStatus() {
+        return status;
+    }
+
+    /**
+     * 设置处理状态
+     *
+     * @param status 处理状态
+     */
+    public void setStatus(Integer status) {
+        this.status = status;
+    }
+
+    /**
+     * 获取处理时间
+     *
+     * @return handle_time - 处理时间
+     */
+    public Date getHandleTime() {
+        return handleTime;
+    }
+
+    /**
+     * 设置处理时间
+     *
+     * @param handleTime 处理时间
+     */
+    public void setHandleTime(Date handleTime) {
+        this.handleTime = handleTime;
+    }
+
+    /**
+     * 获取正确内容
+     *
+     * @return content - 正确内容
+     */
+    public String getContent() {
+        return content;
+    }
+
+    /**
+     * 设置正确内容
+     *
+     * @param content 正确内容
+     */
+    public void setContent(String content) {
+        this.content = content;
+    }
+
+    @Override
+    public String toString() {
+        StringBuilder sb = new StringBuilder();
+        sb.append(getClass().getSimpleName());
+        sb.append(" [");
+        sb.append("Hash = ").append(hashCode());
+        sb.append(", id=").append(id);
+        sb.append(", userId=").append(userId);
+        sb.append(", topicId=").append(topicId);
+        sb.append(", libraryId=").append(libraryId);
+        sb.append(", target=").append(target);
+        sb.append(", createTime=").append(createTime);
+        sb.append(", status=").append(status);
+        sb.append(", handleTime=").append(handleTime);
+        sb.append(", content=").append(content);
+        sb.append("]");
+        return sb.toString();
+    }
+
+    public static UserTextbookFeedback.Builder builder() {
+        return new UserTextbookFeedback.Builder();
+    }
+
+    public static class Builder {
+        private UserTextbookFeedback obj;
+
+        public Builder() {
+            this.obj = new UserTextbookFeedback();
+        }
+
+        /**
+         * @param id
+         */
+        public Builder id(Integer id) {
+            obj.setId(id);
+            return this;
+        }
+
+        /**
+         * 设置用户id
+         *
+         * @param userId 用户id
+         */
+        public Builder userId(Integer userId) {
+            obj.setUserId(userId);
+            return this;
+        }
+
+        /**
+         * 设置机经问题
+         *
+         * @param topicId 机经问题
+         */
+        public Builder topicId(Integer topicId) {
+            obj.setTopicId(topicId);
+            return this;
+        }
+
+        /**
+         * 设置换库表
+         *
+         * @param libraryId 换库表
+         */
+        public Builder libraryId(Integer libraryId) {
+            obj.setLibraryId(libraryId);
+            return this;
+        }
+
+        /**
+         * 设置反馈类型
+         *
+         * @param target 反馈类型
+         */
+        public Builder target(String target) {
+            obj.setTarget(target);
+            return this;
+        }
+
+        /**
+         * @param createTime
+         */
+        public Builder createTime(Date createTime) {
+            obj.setCreateTime(createTime);
+            return this;
+        }
+
+        /**
+         * 设置处理状态
+         *
+         * @param status 处理状态
+         */
+        public Builder status(Integer status) {
+            obj.setStatus(status);
+            return this;
+        }
+
+        /**
+         * 设置处理时间
+         *
+         * @param handleTime 处理时间
+         */
+        public Builder handleTime(Date handleTime) {
+            obj.setHandleTime(handleTime);
+            return this;
+        }
+
+        /**
+         * 设置正确内容
+         *
+         * @param content 正确内容
+         */
+        public Builder content(String content) {
+            obj.setContent(content);
+            return this;
+        }
+
+        public UserTextbookFeedback build() {
+            return this.obj;
+        }
+    }
+}

+ 4 - 3
server/data/src/main/java/com/qxgmat/data/dao/mapping/FaqMapper.xml

@@ -6,6 +6,7 @@
       WARNING - @mbg.generated
     -->
     <id column="id" jdbcType="INTEGER" property="id" />
+    <result column="faq_module" jdbcType="VARCHAR" property="faqModule" />
     <result column="user_id" jdbcType="INTEGER" property="userId" />
     <result column="email" jdbcType="VARCHAR" property="email" />
     <result column="phone" jdbcType="VARCHAR" property="phone" />
@@ -14,7 +15,7 @@
     <result column="position" jdbcType="VARCHAR" property="position" />
     <result column="manager_id" jdbcType="INTEGER" property="managerId" />
     <result column="is_special" jdbcType="INTEGER" property="isSpecial" />
-    <result column="status" jdbcType="INTEGER" property="status" />
+    <result column="answer_status" jdbcType="INTEGER" property="answerStatus" />
     <result column="answer_time" jdbcType="TIMESTAMP" property="answerTime" />
     <result column="is_system" jdbcType="INTEGER" property="isSystem" />
     <result column="create_time" jdbcType="TIMESTAMP" property="createTime" />
@@ -30,8 +31,8 @@
     <!--
       WARNING - @mbg.generated
     -->
-    `id`, `user_id`, `email`, `phone`, `message`, `channel`, `position`, `manager_id`, 
-    `is_special`, `status`, `answer_time`, `is_system`, `create_time`
+    `id`, `faq_module`, `user_id`, `email`, `phone`, `message`, `channel`, `position`, 
+    `manager_id`, `is_special`, `answer_status`, `answer_time`, `is_system`, `create_time`
   </sql>
   <sql id="Blob_Column_List">
     <!--

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

@@ -6,8 +6,8 @@
       WARNING - @mbg.generated
     -->
     <id column="id" jdbcType="INTEGER" property="id" />
-    <result column="start_date" jdbcType="VARCHAR" property="startDate" />
-    <result column="end_date" jdbcType="VARCHAR" property="endDate" />
+    <result column="start_date" jdbcType="DATE" property="startDate" />
+    <result column="end_date" jdbcType="DATE" property="endDate" />
     <result column="quant" jdbcType="VARCHAR" property="quant" />
     <result column="quant_version" jdbcType="INTEGER" property="quantVersion" />
     <result column="quant_time" jdbcType="TIMESTAMP" property="quantTime" />

+ 38 - 0
server/data/src/main/java/com/qxgmat/data/dao/mapping/TextbookTopicMapper.xml

@@ -0,0 +1,38 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="com.qxgmat.data.dao.TextbookTopicMapper">
+  <resultMap id="BaseResultMap" type="com.qxgmat.data.dao.entity.TextbookTopic">
+    <!--
+      WARNING - @mbg.generated
+    -->
+    <id column="id" jdbcType="INTEGER" property="id" />
+    <result column="library_id" jdbcType="INTEGER" property="libraryId" />
+    <result column="question_type" jdbcType="VARCHAR" property="questionType" />
+    <result column="no" jdbcType="INTEGER" property="no" />
+    <result column="keyword" jdbcType="VARCHAR" property="keyword" />
+    <result column="quality" jdbcType="VARCHAR" property="quality" />
+    <result column="is_old" jdbcType="INTEGER" property="isOld" />
+    <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.TextbookTopic">
+    <!--
+      WARNING - @mbg.generated
+    -->
+    <result column="detail" jdbcType="LONGVARCHAR" property="detail" />
+    <result column="content" jdbcType="LONGVARCHAR" property="content" />
+  </resultMap>
+  <sql id="Base_Column_List">
+    <!--
+      WARNING - @mbg.generated
+    -->
+    `id`, `library_id`, `question_type`, `no`, `keyword`, `quality`, `is_old`, `create_time`, 
+    `update_time`
+  </sql>
+  <sql id="Blob_Column_List">
+    <!--
+      WARNING - @mbg.generated
+    -->
+    `detail`, `content`
+  </sql>
+</mapper>

+ 0 - 0
server/data/src/main/java/com/qxgmat/data/dao/mapping/UserCourseAppointmentMapper.xml


Certains fichiers n'ont pas été affichés car il y a eu trop de fichiers modifiés dans ce diff