Browse Source

feat(server): 调整后台结构

Go 5 years ago
parent
commit
6873404093
100 changed files with 1742 additions and 167 deletions
  1. 14 2
      front/project/Constant.js
  2. 0 3
      front/project/admin/routes/course/askDetail/index.less
  3. 1 1
      front/project/admin/routes/course/experience/page.js
  4. 6 7
      front/project/admin/routes/course/index.js
  5. 23 23
      front/project/admin/routes/course/invoice/page.js
  6. 2 2
      front/project/admin/routes/user/preview/index.js
  7. 3 0
      front/project/admin/routes/course/preview/index.less
  8. 1 1
      front/project/admin/routes/subject/preview/page.js
  9. 3 3
      front/project/admin/routes/subject/preview/index.js
  10. 3 0
      front/project/admin/routes/course/previewDetail/index.less
  11. 2 17
      front/project/admin/routes/subject/previewDetail/page.js
  12. 1 1
      front/project/admin/routes/course/student/page.js
  13. 7 2
      front/project/admin/routes/index.js
  14. 15 0
      front/project/admin/routes/interaction/askQuestion/index.js
  15. 3 0
      front/project/admin/routes/interaction/askQuestion/index.less
  16. 196 0
      front/project/admin/routes/interaction/askQuestion/page.js
  17. 15 0
      front/project/admin/routes/interaction/askQuestionDetail/index.js
  18. 3 0
      front/project/admin/routes/interaction/askQuestionDetail/index.less
  19. 0 0
      front/project/admin/routes/interaction/askQuestionDetail/page.js
  20. 3 3
      front/project/admin/routes/setting/faqSystem/index.js
  21. 18 0
      front/project/admin/routes/interaction/comment/index.less
  22. 1 1
      front/project/admin/routes/setting/comment/page.js
  23. 3 3
      front/project/admin/routes/setting/comment/index.js
  24. 3 0
      front/project/admin/routes/interaction/faq/index.less
  25. 0 0
      front/project/admin/routes/interaction/faq/page.js
  26. 15 0
      front/project/admin/routes/interaction/feedback/index.js
  27. 3 0
      front/project/admin/routes/interaction/feedback/index.less
  28. 0 0
      front/project/admin/routes/interaction/feedback/page.js
  29. 5 0
      front/project/admin/routes/interaction/group.js
  30. 8 0
      front/project/admin/routes/interaction/index.js
  31. 5 0
      front/project/admin/routes/ready/group.js
  32. 3 0
      front/project/admin/routes/ready/index.js
  33. 0 3
      front/project/admin/routes/setting/faqConsult/index.less
  34. 0 3
      front/project/admin/routes/setting/faqSystem/index.less
  35. 6 9
      front/project/admin/routes/setting/index.js
  36. 1 1
      front/project/admin/routes/setting/index/index.js
  37. 1 1
      front/project/admin/routes/setting/place/index.js
  38. 1 1
      front/project/admin/routes/setting/rank/index.js
  39. 1 1
      front/project/admin/routes/setting/service/index.js
  40. 1 1
      front/project/admin/routes/setting/time/index.js
  41. 3 3
      front/project/admin/routes/user/ask/index.js
  42. 1 1
      front/project/admin/routes/course/ask/index.less
  43. 102 0
      front/project/admin/routes/show/ad/page.js
  44. 3 3
      front/project/admin/routes/course/study/index.js
  45. 1 1
      front/project/admin/routes/course/study/index.less
  46. 102 0
      front/project/admin/routes/show/article/page.js
  47. 16 0
      front/project/admin/routes/show/articleDetail/index.js
  48. 3 0
      front/project/admin/routes/show/articleDetail/index.less
  49. 106 0
      front/project/admin/routes/show/articleDetail/page.js
  50. 3 3
      front/project/admin/routes/setting/message/index.js
  51. 1 1
      front/project/admin/routes/setting/comment/index.less
  52. 240 0
      front/project/admin/routes/show/comment/page.js
  53. 3 3
      front/project/admin/routes/setting/tips/index.js
  54. 1 1
      front/project/admin/routes/user/ask/index.less
  55. 0 0
      front/project/admin/routes/show/faq/page.js
  56. 5 0
      front/project/admin/routes/show/group.js
  57. 10 0
      front/project/admin/routes/show/index.js
  58. 15 0
      front/project/admin/routes/show/message/index.js
  59. 0 0
      front/project/admin/routes/show/message/index.less
  60. 0 0
      front/project/admin/routes/show/message/page.js
  61. 16 0
      front/project/admin/routes/show/readDetail/index.js
  62. 3 0
      front/project/admin/routes/show/readDetail/index.less
  63. 169 0
      front/project/admin/routes/show/readDetail/page.js
  64. 3 3
      front/project/admin/routes/course/ask/index.js
  65. 0 0
      front/project/admin/routes/show/tips/index.less
  66. 0 0
      front/project/admin/routes/show/tips/page.js
  67. 15 0
      front/project/admin/routes/student/askCourse/index.js
  68. 3 0
      front/project/admin/routes/student/askCourse/index.less
  69. 2 2
      front/project/admin/routes/course/ask/page.js
  70. 3 3
      front/project/admin/routes/course/askDetail/index.js
  71. 3 0
      front/project/admin/routes/student/askCourseDetail/index.less
  72. 0 0
      front/project/admin/routes/student/askCourseDetail/page.js
  73. 3 3
      front/project/admin/routes/setting/faqConsult/index.js
  74. 3 0
      front/project/admin/routes/student/askQuestion/index.less
  75. 3 3
      front/project/admin/routes/user/ask/page.js
  76. 15 0
      front/project/admin/routes/student/askQuestionDetail/index.js
  77. 3 0
      front/project/admin/routes/student/askQuestionDetail/index.less
  78. 213 0
      front/project/admin/routes/student/askQuestionDetail/page.js
  79. 5 0
      front/project/admin/routes/student/group.js
  80. 9 0
      front/project/admin/routes/student/index.js
  81. 15 0
      front/project/admin/routes/student/study/index.js
  82. 3 0
      front/project/admin/routes/student/study/index.less
  83. 0 0
      front/project/admin/routes/student/study/page.js
  84. 0 0
      front/project/admin/routes/student/studyDetail/index.js
  85. 0 0
      front/project/admin/routes/student/studyDetail/index.less
  86. 0 0
      front/project/admin/routes/student/studyDetail/page.js
  87. 1 1
      front/project/admin/routes/subject/examination/index.js
  88. 3 6
      front/project/admin/routes/subject/index.js
  89. 0 3
      front/project/admin/routes/subject/preview/index.less
  90. 0 15
      front/project/admin/routes/subject/previewDetail/index.js
  91. 0 3
      front/project/admin/routes/subject/previewDetail/index.less
  92. 1 1
      front/project/admin/routes/subject/sentence/index.js
  93. 1 1
      front/project/admin/routes/subject/textbook/index.js
  94. 0 15
      front/project/admin/routes/subject/textbookLibrary/index.js
  95. 0 3
      front/project/admin/routes/subject/textbookLibrary/index.less
  96. 15 0
      front/project/admin/routes/textbook/feedback/index.js
  97. 3 0
      front/project/admin/routes/textbook/feedback/index.less
  98. 233 0
      front/project/admin/routes/textbook/feedback/page.js
  99. 5 0
      front/project/admin/routes/textbook/group.js
  100. 0 0
      front/project/admin/routes/textbook/index.js

+ 14 - 2
front/project/Constant.js

@@ -22,7 +22,9 @@ export const ServiceParamMap = {
   vip: [{ label: '1个月', value: 'month1' }, { label: '3个月', value: 'month3' }, { label: '6个月', value: 'month6' }],
 };
 
-export const ServiceSource = [{ label: '银行转账', value: 'bank' }, { label: '微信', value: 'wechat' }, { label: '支付宝', value: 'alipay' }, { label: '实名认证', value: 'real' }, { label: '邀请好友', value: 'invite' }, { label: '备考设置', value: 'prepare' }, { label: '课程赠送', value: 'gift_course' }, { label: '活动赠送', value: 'gift_activity' }];
+export const RecordSource = [{ label: '银行转账', value: 'bank' }, { label: '微信', value: 'wechat' }, { label: '支付宝', value: 'alipay' }, { label: '实名认证', value: 'real' }, { label: '邀请好友', value: 'invite' }, { label: '备考设置', value: 'prepare' }, { label: '课程赠送', value: 'gift_course' }, { label: '活动赠送', value: 'gift_activity' }];
+
+export const RecordBuySource = [{ label: '银行转账', value: 'bank' }, { label: '微信', value: 'wechat' }, { label: '支付宝', value: 'alipay' }];
 
 export const CourseSource = [{ label: '网上报名', value: 'bank' }, { label: '网上报名', value: 'wechat' }, { label: '网上报名', value: 'alipay' }, { label: '线下报名', value: 'offline' }];
 
@@ -30,6 +32,8 @@ export const SwitchSelect = [{ value: 0, label: '否' }, { value: 1, label: '是
 
 export const PassSelect = [{ value: 0, label: '未认证' }, { value: 1, label: '通过' }];
 
+export const InputSelect = [{ value: 0, label: '未填' }, { value: 1, label: '已填' }];
+
 export const AskStatus = [{ value: 0, label: '新增' }, { value: 1, label: '已回答' }, { value: 2, label: '忽略' }];
 
 export const FeedbackStatus = [{ value: 0, label: '新增' }, { value: 1, label: '已处理' }, { value: 2, label: '忽略' }, { value: 3, label: '无需修改' }];
@@ -42,6 +46,8 @@ export const PrepareExaminationTime = [{ label: '近1个月', value: 'one_month'
 
 export const ProductType = [{ label: '服务', value: 'service' }, { label: '课程', value: 'course' }, { label: '资料', value: 'data' }, { label: '课程-套餐', value: 'course-package' }];
 
+export const ProductTypeMain = [{ label: '服务', value: 'service' }, { label: '课程', value: 'course' }, { label: '资料', value: 'data' }];
+
 export const QuestionStyleType = [{ label: '单选', value: 'single' }, { label: '横纵向单选', value: 'double' }, { label: '题目内选择', value: 'inline' }];
 
 export const QuestionRadioDirection = [{ label: '横向', value: 'landscape' }, { label: '纵向', value: 'portrait' }];
@@ -76,7 +82,13 @@ export const InvoiceType = [{ label: '个人', value: 'personal' }, { label: '
 
 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 TextbookSubject = [{ label: '数学', value: 'quant' }, { label: '阅读', value: 'rc' }, { label: '逻辑', value: 'lr' }];
+
+export const TextbookFeedbackStatus = [{ value: 0, label: '新增' }, { value: 1, label: '采纳' }, { value: 2, label: '忽略' }, { value: 3, label: '无需修改' }];
+
+export const TextbookFeedbackTarget = [{ label: '纠正', value: 'correct' }, { label: '完善', value: 'perfect' }, { label: '反馈全新', value: 'new' }];
+
+export const ArticleChannel = [{}];
 
 export const MobileArea = ['+86', '+1'].map(row => {
   return { label: row, value: row };

+ 0 - 3
front/project/admin/routes/course/askDetail/index.less

@@ -1,3 +0,0 @@
-@charset "utf-8";
-
-#course-ask-detail {}

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

@@ -134,7 +134,7 @@ export default class extends Page {
         title: `${row.nickname}(${row.mobile})`,
         value: row.id,
       };
-    }, this.state.search.userId ? Number(this.state.search.userId) : [], null);
+    }, this.state.search.userId ? Number(this.state.search.userId) : null, null);
   }
 
   initData() {

+ 6 - 7
front/project/admin/routes/course/index.js

@@ -1,16 +1,15 @@
 import list from './list';
-import ask from './ask';
-import askDetail from './askDetail';
-import invoice from './invoice';
 import detail from './detail';
 import vsDetail from './vsDetail';
+import preview from './preview';
+import previewDetail from './previewDetail';
+import Package from './package';
 import data from './data';
 import dataDetail from './dataDetail';
-import study from './study';
-import studyDetail from './studyDetail';
 import experience from './experience';
 import experienceDetail from './experienceDetail';
+import invoice from './invoice';
+
 import student from './student';
-import Package from './package';
 
-export default [list, ask, askDetail, invoice, detail, vsDetail, data, dataDetail, study, studyDetail, experience, experienceDetail, student, Package];
+export default [list, detail, vsDetail, preview, previewDetail, Package, data, dataDetail, experience, experienceDetail, invoice, student];

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

@@ -16,28 +16,28 @@ const InvoiceTypeMap = getMap(InvoiceType, 'value', 'label');
 export default class extends Page {
   init() {
     this.exerciseMap = {};
-    this.filterForm = [
-      {
-        key: 'userId',
-        type: 'tree',
-        allowClear: true,
-        tree: [],
-        name: '用户',
-        placeholder: '用户',
-      }, {
-        key: 'isDownload',
-        type: 'select',
-        allowClear: true,
-        name: '下载状态',
-        select: SwitchSelect,
-      }, {
-        key: 'isFinish',
-        type: 'select',
-        allowClear: true,
-        name: '开票状态',
-        select: SwitchSelect,
-      },
-    ];
+    this.filterForm = [{
+      key: 'userId',
+      type: 'tree',
+      allowClear: true,
+      tree: [],
+      name: '用户',
+      placeholder: '用户',
+    }, {
+      key: 'isDownload',
+      type: 'select',
+      allowClear: true,
+      name: '下载状态',
+      select: SwitchSelect,
+      number: true,
+    }, {
+      key: 'isFinish',
+      type: 'select',
+      allowClear: true,
+      name: '开票状态',
+      select: SwitchSelect,
+      number: true,
+    }];
     this.actionList = [{
       key: 'download',
       name: '下载',
@@ -96,7 +96,7 @@ export default class extends Page {
         title: `${row.nickname}(${row.mobile})`,
         value: row.id,
       };
-    }, this.state.search.userId ? Number(this.state.search.userId) : [], null);
+    }, this.state.search.userId ? Number(this.state.search.userId) : null, null);
   }
 
   initData() {

+ 2 - 2
front/project/admin/routes/user/preview/index.js

@@ -2,8 +2,8 @@ import module from '../../module';
 import group from '../group';
 
 export default {
-  path: '/user/preview',
-  key: 'user-preview',
+  path: '/course/preview',
+  key: 'course-preview',
   title: '预习作业',
   needLogin: true,
   module,

+ 3 - 0
front/project/admin/routes/course/preview/index.less

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

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

@@ -77,7 +77,7 @@ export default class extends Page {
       title: '单项',
       dataIndex: 'course.structId',
       render: (text) => {
-        return this.exerciseMap[text] || text;
+        return this.exerciseMap[text] || '';
       },
     }, {
       title: '作业标题',

+ 3 - 3
front/project/admin/routes/subject/preview/index.js

@@ -2,13 +2,13 @@ import module from '../../module';
 import group from '../group';
 
 export default {
-  path: '/subject/preview',
-  key: 'subject-preview',
+  path: '/course/preview/detail/:id?',
+  key: 'course-preview-detail',
   title: '预习作业',
   needLogin: true,
   module,
   group,
-  index: true,
+  showKey: 'course-preview',
   component() {
     return import('./page');
   },

+ 3 - 0
front/project/admin/routes/course/previewDetail/index.less

@@ -0,0 +1,3 @@
+@charset "utf-8";
+
+#course-preview-detail {}

+ 2 - 17
front/project/admin/routes/subject/previewDetail/page.js

@@ -62,6 +62,7 @@ export default class extends Page {
             value: row.id,
           };
         }, result.courseId, null);
+        form.getFieldDecorator('courseNo');
         form.setFieldsValue(result);
         this.setState({ module: result.courseModule, data: result, questionNoIds });
         this.refresh();
@@ -92,7 +93,7 @@ export default class extends Page {
             title: `${row.nickname}(${row.mobile})`,
             value: row.id,
           };
-        }, [], null);
+        }, null, null);
         break;
       default:
     }
@@ -154,22 +155,6 @@ export default class extends Page {
     });
   }
 
-  deleteQuestion(index) {
-    const { questionNos } = this.state;
-    questionNos.splice(index, 1);
-    this.setState({ questionNos });
-    this.props.form.setFieldsValue({ questionNos: questionNos.map(row => row.no) });
-  }
-
-  orderQuestion(oldIndex, newIndex) {
-    const { questionNos } = this.state;
-    const tmp = questionNos[oldIndex];
-    questionNos[oldIndex] = questionNos[newIndex];
-    questionNos[newIndex] = tmp;
-    this.setState({ questionNos });
-    this.props.form.setFieldsValue({ questionNos: questionNos.map(row => row.no) });
-  }
-
   addOnlineAction() {
     const { data } = this.state;
     asyncForm('添加', this.onlineList, {}, info => {

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

@@ -247,7 +247,7 @@ export default class extends Page {
             title: `${row.nickname}(${row.mobile})`,
             value: row.id,
           };
-        }, [], null);
+        }, null, null);
         this.refreshOnline();
         break;
       case 'vs':

+ 7 - 2
front/project/admin/routes/index.js

@@ -1,10 +1,15 @@
 // We only need to import the modules necessary for initial render
 
 import Page from './page';
-import subject from './subject';
 import user from './user';
+import subject from './subject';
 import course from './course';
+import student from './student';
+import interaction from './interaction';
+import textbook from './textbook';
+import ready from './ready';
 import setting from './setting';
+import show from './show';
 import System from './system';
 
-export default [...Page, ...subject, ...user, ...course, ...setting, ...System];
+export default [...Page, ...user, ...subject, ...course, ...student, ...interaction, ...textbook, ...ready, ...setting, ...show, ...System];

+ 15 - 0
front/project/admin/routes/interaction/askQuestion/index.js

@@ -0,0 +1,15 @@
+import module from '../../module';
+import group from '../group';
+
+export default {
+  path: '/innteraction/ask/question',
+  key: 'innteraction-ask-question',
+  title: '非学员提问',
+  needLogin: true,
+  module,
+  group,
+  index: true,
+  component() {
+    return import('./page');
+  },
+};

+ 3 - 0
front/project/admin/routes/interaction/askQuestion/index.less

@@ -0,0 +1,3 @@
+@charset "utf-8";
+
+#interaction-ask-question {}

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

@@ -0,0 +1,196 @@
+import React from 'react';
+import { Link } from 'react-router-dom';
+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, bindSearch, formatDate } from '@src/services/Tools';
+import { asyncSMessage, asyncDelConfirm } from '@src/services/AsyncTools';
+import { QuestionType, AskStatus, MoneyRange, SwitchSelect, AskTarget } from '../../../../Constant';
+import { User } from '../../../stores/user';
+import { Question } from '../../../stores/question';
+
+const QuestionTypeMap = getMap(QuestionType, 'value', 'label');
+const AskStatusMap = getMap(AskStatus, 'value', 'label');
+const SwitchSelectMap = getMap(SwitchSelect, 'value', 'label');
+export default class extends Page {
+  init() {
+    this.actionList = [{
+      key: 'ignore',
+      type: 'danger',
+      name: '批量忽略',
+      needSelect: 1,
+    }];
+    this.filterForm = [{
+      key: 'questionType',
+      type: 'select',
+      allowClear: true,
+      name: '题型',
+      select: QuestionType,
+      placeholder: '请选择',
+    }, {
+      key: 'answerStatus',
+      type: 'select',
+      allowClear: true,
+      name: '状态',
+      select: AskStatus,
+      number: true,
+    }, {
+      key: 'money',
+      type: 'select',
+      allowClear: true,
+      name: '消费金额',
+      select: MoneyRange,
+      number: true,
+    }, {
+      key: 'showStatus',
+      type: 'select',
+      allowClear: true,
+      name: '展示状态',
+      select: SwitchSelect,
+      number: true,
+    }, {
+      key: 'target',
+      type: 'select',
+      allowClear: true,
+      name: '提问内容',
+      select: AskTarget,
+    }, {
+      key: 'questionNoId',
+      type: 'select',
+      allowClear: true,
+      name: '题目ID',
+      select: [],
+      number: true,
+      placeholder: '请输入',
+    }, {
+      key: 'userId',
+      type: 'select',
+      name: '用户',
+      allowClear: true,
+      select: [],
+      number: true,
+      placeholder: '请输入',
+    }];
+    this.columns = [{
+      title: '题型',
+      dataIndex: 'type',
+      render: (text, record) => {
+        return QuestionTypeMap[record.question.type];
+      },
+    },
+    {
+      title: '题目id',
+      dataIndex: 'questionNo.no',
+    },
+    {
+      title: '提问时间',
+      dataIndex: 'createTime',
+      render: (text) => {
+        return formatDate(text);
+      },
+    },
+    {
+      title: '提问摘要',
+      dataIndex: 'content',
+    }, {
+      title: '提问者',
+      dataIndex: 'user.nickname',
+    }, {
+      title: '回答状态',
+      dataIndex: 'answerStatus',
+      render: (text) => {
+        return AskStatusMap[text] || text;
+      },
+    }, {
+      title: '回答者',
+      dataIndex: 'manager.username',
+    }, {
+      title: '回答时间',
+      dataIndex: 'answerTime',
+      render: (text) => {
+        return text ? formatDate(text) : '';
+      },
+    }, {
+      title: '展示状态',
+      dataIndex: 'showStatus',
+      render: (text) => {
+        return SwitchSelectMap[text] || text;
+      },
+    }, {
+      title: '操作',
+      dataIndex: 'handler',
+      render: (text, record) => {
+        return <div className="table-button">
+          {(
+            <Link to={`/interaction/ask/question/detail/${record.id}`}>编辑</Link>
+          )}
+        </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, null);
+    bindSearch(this.filterForm, 'questionNoId', this, (search) => {
+      return Question.searchNo(search);
+    }, (row) => {
+      return {
+        title: row.title,
+        value: row.id,
+      };
+    }, this.state.search.questionNoId ? Number(this.state.search.questionNoId) : null, null);
+  }
+
+  initData() {
+    Question.listAsk(Object.assign({ hasRecord: false }, this.state.search)).then(result => {
+      this.setTableData(result.list, result.total);
+    });
+  }
+
+  ignoreAction() {
+    const { selectedKeys } = this.state;
+    asyncDelConfirm('忽略确认', '是否忽略选中提问?', () => {
+      return Promise.all(selectedKeys.map(row => Question.editAsk({ id: row, ignoreStatus: 1 }))).then(() => {
+        asyncSMessage('操作成功!');
+        this.refresh();
+      });
+    });
+  }
+
+  renderView() {
+    return <Block flex>
+      <FilterLayout
+        show
+        itemList={this.filterForm}
+        data={this.state.search}
+        onChange={data => {
+          if (data.time.length > 0) {
+            data.time = [data.time[0].format('YYYY-MM-DD HH:mm:ss'), data.time[1].format('YYYY-MM-DD HH:mm:ss')];
+          }
+          this.search(data);
+        }} />
+      <ActionLayout
+        itemList={this.actionList}
+        selectedKeys={this.state.selectedKeys}
+        onAction={key => this.onAction(key)}
+      />
+      <TableLayout
+        select
+        columns={this.tableSort(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>;
+  }
+}

+ 15 - 0
front/project/admin/routes/interaction/askQuestionDetail/index.js

@@ -0,0 +1,15 @@
+import module from '../../module';
+import group from '../group';
+
+export default {
+  path: '/innteraction/ask/question/detail/:id?',
+  key: 'innteraction-ask-question-detail',
+  title: '提问详情',
+  needLogin: true,
+  module,
+  group,
+  showKey: 'innteraction-ask-question',
+  component() {
+    return import('./page');
+  },
+};

+ 3 - 0
front/project/admin/routes/interaction/askQuestionDetail/index.less

@@ -0,0 +1,3 @@
+@charset "utf-8";
+
+#interaction-ask-question-detail {}

front/project/admin/routes/user/askDetail/page.js → front/project/admin/routes/interaction/askQuestionDetail/page.js


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

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

+ 18 - 0
front/project/admin/routes/interaction/comment/index.less

@@ -0,0 +1,18 @@
+@charset "utf-8";
+
+#interaction-comment {
+  .ant-card.plus {
+    text-align: center;
+    cursor: pointer;
+  }
+
+  .ant-card {
+    margin-bottom: 20px;
+  }
+
+  .delete-button {
+    position: absolute;
+    right: 10px;
+    top: 10px;
+  }
+}

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

@@ -173,7 +173,7 @@ export default class extends Page {
         title: `${row.nickname}(${row.mobile})`,
         value: row.id,
       };
-    }, this.state.search.userId ? Number(this.state.search.userId) : [], null);
+    }, this.state.search.userId ? Number(this.state.search.userId) : null, null);
   }
 
   initData() {

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

@@ -2,9 +2,9 @@ import module from '../../module';
 import group from '../group';
 
 export default {
-  path: '/setting/comment',
-  key: 'setting-comment',
-  title: '评价设置',
+  path: '/interaction/faq',
+  key: 'interaction-faq',
+  title: 'FAQs (咨询类)',
   needLogin: true,
   module,
   group,

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

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

front/project/admin/routes/setting/faqConsult/page.js → front/project/admin/routes/interaction/faq/page.js


+ 15 - 0
front/project/admin/routes/interaction/feedback/index.js

@@ -0,0 +1,15 @@
+import module from '../../module';
+import group from '../group';
+
+export default {
+  path: '/interaction/feedback',
+  key: 'interaction-feedback',
+  title: '纠错',
+  needLogin: true,
+  module,
+  group,
+  index: true,
+  component() {
+    return import('./page');
+  },
+};

+ 3 - 0
front/project/admin/routes/interaction/feedback/index.less

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

front/project/admin/routes/user/feedback/page.js → front/project/admin/routes/interaction/feedback/page.js


+ 5 - 0
front/project/admin/routes/interaction/group.js

@@ -0,0 +1,5 @@
+export default {
+  key: 'interaction',
+  name: '互动管理',
+  icon: 'appstore',
+};

+ 8 - 0
front/project/admin/routes/interaction/index.js

@@ -0,0 +1,8 @@
+
+import askQuestion from './askQuestion';
+import askQuestionDetail from './askQuestionDetail';
+import faqConsult from './faqConsult';
+import comment from './comment';
+import feedback from './feedback';
+
+export default [askQuestion, askQuestionDetail, faqConsult, comment, feedback];

+ 5 - 0
front/project/admin/routes/ready/group.js

@@ -0,0 +1,5 @@
+export default {
+  key: 'ready',
+  name: 'GetReady板块',
+  icon: 'appstore',
+};

+ 3 - 0
front/project/admin/routes/ready/index.js

@@ -0,0 +1,3 @@
+
+
+export default [];

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

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

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

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

+ 6 - 9
front/project/admin/routes/setting/index.js

@@ -1,13 +1,10 @@
-import struct from './struct';
-import tips from './tips';
+
 import service from './service';
+import promote from './promote';
 import index from './index/';
-import place from './place';
+import struct from './struct';
 import time from './time';
-import comment from './comment';
-import faqConsult from './faqConsult';
-import faqSystem from './faqSystem';
-import message from './message';
-import promote from './promote';
+import place from './place';
+import rank from './rank';
 
-export default [struct, tips, service, index, place, time, comment, faqConsult, faqSystem, message, promote];
+export default [service, promote, index, struct, time, place, rank];

+ 1 - 1
front/project/admin/routes/setting/index/index.js

@@ -4,7 +4,7 @@ import group from '../group';
 export default {
   path: '/setting/index',
   key: 'setting-index',
-  title: '首页设置',
+  title: '首页板块设置',
   needLogin: true,
   module,
   group,

+ 1 - 1
front/project/admin/routes/setting/place/index.js

@@ -4,7 +4,7 @@ import group from '../group';
 export default {
   path: '/setting/place',
   key: 'setting-place',
-  title: '考点置',
+  title: '考点置',
   needLogin: true,
   module,
   group,

+ 1 - 1
front/project/admin/routes/setting/rank/index.js

@@ -4,7 +4,7 @@ import group from '../group';
 export default {
   path: '/setting/rank',
   key: 'setting-rank',
-  title: '排名置',
+  title: '排名置',
   needLogin: true,
   module,
   group,

+ 1 - 1
front/project/admin/routes/setting/service/index.js

@@ -4,7 +4,7 @@ import group from '../group';
 export default {
   path: '/setting/service',
   key: 'setting-service',
-  title: '服务设置',
+  title: '服务管理',
   needLogin: true,
   module,
   group,

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

@@ -4,7 +4,7 @@ import group from '../group';
 export default {
   path: '/setting/time',
   key: 'setting-time',
-  title: '时间设置',
+  title: '数据设置',
   needLogin: true,
   module,
   group,

+ 3 - 3
front/project/admin/routes/user/ask/index.js

@@ -2,9 +2,9 @@ import module from '../../module';
 import group from '../group';
 
 export default {
-  path: '/user/ask',
-  key: 'user-ask',
-  title: '学生提问',
+  path: '/show/ad',
+  key: 'show-ad',
+  title: '广告设置',
   needLogin: true,
   module,
   group,

+ 1 - 1
front/project/admin/routes/course/ask/index.less

@@ -1,3 +1,3 @@
 @charset "utf-8";
 
-#course-ask {}
+#show-ad {}

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

@@ -0,0 +1,102 @@
+import React from 'react';
+import { Link } from 'react-router-dom';
+import { Button } from 'antd';
+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 { formatDate, getMap } from '@src/services/Tools';
+import { ArticleChannel } from '../../../../Constant';
+import { System } from '../../../stores/system';
+
+const ArticleChannelMap = getMap(ArticleChannel, 'value', 'label');
+
+export default class extends Page {
+  init() {
+    this.filterForm = [{
+      key: 'channel',
+      type: 'select',
+      allowClear: true,
+      name: '频道',
+      select: ArticleChannel,
+      placeholder: '请选择',
+    }, {
+      key: 'position',
+      type: 'select',
+      allowClear: true,
+      name: '位置',
+      select: [],
+      placeholder: '请选择',
+    }];
+    this.actionList = [{
+      key: 'add',
+      type: 'primary',
+      name: '创建',
+      render: (item) => {
+        return <Link to='/show/article/detail'><Button>{item.name}</Button></Link>;
+      },
+    }];
+    this.columns = [{
+      title: '频道',
+      dataIndex: 'channel',
+      render: (text) => {
+        return ArticleChannelMap[text] || '';
+      },
+    }, {
+      title: '位置',
+      dataIndex: 'position',
+    }, {
+      title: '文章标题',
+      dataIndex: 'title',
+    }, {
+      title: '更新时间',
+      dataIndex: 'updateTime',
+      render: (text) => {
+        return formatDate(text);
+      },
+    }, {
+      title: '操作',
+      dataIndex: 'handler',
+      render: (text, record) => {
+        return <div className="table-button">
+          {<Link to={`/show/article/detail/${record.id}`}>编辑</Link>}
+        </div>;
+      },
+    }];
+  }
+
+  initData() {
+    System.listAd(this.state.search).then(result => {
+      this.setTableData(result.list, result.total);
+    });
+  }
+
+  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
+        select
+        columns={this.tableSort(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 - 3
front/project/admin/routes/course/study/index.js

@@ -2,9 +2,9 @@ import module from '../../module';
 import group from '../group';
 
 export default {
-  path: '/course/study',
-  key: 'course-study',
-  title: '学习记录',
+  path: '/show/article',
+  key: 'show-article',
+  title: '推荐阅读',
   needLogin: true,
   module,
   group,

+ 1 - 1
front/project/admin/routes/course/study/index.less

@@ -1,3 +1,3 @@
 @charset "utf-8";
 
-#course-study {}
+#show-article {}

+ 102 - 0
front/project/admin/routes/show/article/page.js

@@ -0,0 +1,102 @@
+import React from 'react';
+import { Link } from 'react-router-dom';
+import { Button } from 'antd';
+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 { formatDate, getMap } from '@src/services/Tools';
+import { ArticleChannel } from '../../../../Constant';
+import { System } from '../../../stores/system';
+
+const ArticleChannelMap = getMap(ArticleChannel, 'value', 'label');
+
+export default class extends Page {
+  init() {
+    this.filterForm = [{
+      key: 'channel',
+      type: 'select',
+      allowClear: true,
+      name: '频道',
+      select: ArticleChannel,
+      placeholder: '请选择',
+    }, {
+      key: 'position',
+      type: 'select',
+      allowClear: true,
+      name: '位置',
+      select: [],
+      placeholder: '请选择',
+    }];
+    this.actionList = [{
+      key: 'add',
+      type: 'primary',
+      name: '创建',
+      render: (item) => {
+        return <Link to='/show/article/detail'><Button>{item.name}</Button></Link>;
+      },
+    }];
+    this.columns = [{
+      title: '频道',
+      dataIndex: 'channel',
+      render: (text) => {
+        return ArticleChannelMap[text] || '';
+      },
+    }, {
+      title: '位置',
+      dataIndex: 'position',
+    }, {
+      title: '文章标题',
+      dataIndex: 'title',
+    }, {
+      title: '更新时间',
+      dataIndex: 'updateTime',
+      render: (text) => {
+        return formatDate(text);
+      },
+    }, {
+      title: '操作',
+      dataIndex: 'handler',
+      render: (text, record) => {
+        return <div className="table-button">
+          {<Link to={`/show/article/detail/${record.id}`}>编辑</Link>}
+        </div>;
+      },
+    }];
+  }
+
+  initData() {
+    System.listArticle(this.state.search).then(result => {
+      this.setTableData(result.list, result.total);
+    });
+  }
+
+  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
+        select
+        columns={this.tableSort(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>;
+  }
+}

+ 16 - 0
front/project/admin/routes/show/articleDetail/index.js

@@ -0,0 +1,16 @@
+import module from '../../module';
+import group from '../group';
+
+export default {
+  path: '/show/article/detail',
+  matchPath: '/show/article/detail/:id?',
+  key: 'show-article-detail',
+  title: '推荐阅读',
+  needLogin: true,
+  module,
+  group,
+  showKey: 'show-article',
+  component() {
+    return import('./page');
+  },
+};

+ 3 - 0
front/project/admin/routes/show/articleDetail/index.less

@@ -0,0 +1,3 @@
+@charset "utf-8";
+
+#show-article-detail {}

+ 106 - 0
front/project/admin/routes/show/articleDetail/page.js

@@ -0,0 +1,106 @@
+import React from 'react';
+import { Form, Input, Button, Row, Col } from 'antd';
+import './index.less';
+import Editor from '@src/components/Editor';
+import Page from '@src/containers/Page';
+import Block from '@src/components/Block';
+import Select from '@src/components/Select';
+// import FileUpload from '@src/components/FileUpload';
+import { formatFormError } from '@src/services/Tools';
+import { asyncSMessage } from '@src/services/AsyncTools';
+import { ArticleChannel } from '../../../../Constant';
+import { System } from '../../../stores/system';
+
+export default class extends Page {
+  initData() {
+    const { id } = this.params;
+    const { form } = this.props;
+    let handler;
+    if (id) {
+      handler = System.getArticle({ id });
+    } else {
+      handler = Promise.resolve({});
+    }
+
+    handler
+      .then(result => {
+        form.setFieldsValue(result);
+      });
+  }
+
+  submit() {
+    const { form } = this.props;
+    form.validateFields((err) => {
+      if (!err) {
+        const data = form.getFieldsValue();
+        let handler;
+        if (data.id) {
+          handler = System.editArticle(data);
+        } else {
+          handler = System.addArticle(data);
+        }
+        handler.then(() => {
+          asyncSMessage('保存成功');
+          goBack();
+        }).catch((e) => {
+          if (e.result) form.setFields(formatFormError(data, e.result));
+        });
+      }
+    });
+  }
+
+  renderBase() {
+    const { getFieldDecorator } = this.props.form;
+    return <Block>
+      <Form>
+        {getFieldDecorator('id')(<input hidden />)}
+        <Form.Item labelCol={{ span: 5 }} wrapperCol={{ span: 16 }} label='频道'>
+          {getFieldDecorator('channel', {
+            rules: [
+              { required: true, message: '请选择' },
+            ],
+          })(
+            <Select select={ArticleChannel} placeholder='请选择' />,
+          )}
+        </Form.Item>
+        <Form.Item labelCol={{ span: 5 }} wrapperCol={{ span: 16 }} label='位置'>
+          {getFieldDecorator('userId', {
+            rules: [
+              { required: true, message: '请选择' },
+            ],
+          })(
+            <Select {...this.state.userId} placeholder='请选择' />,
+          )}
+        </Form.Item>
+        <Form.Item labelCol={{ span: 5 }} wrapperCol={{ span: 16 }} label='文章标题'>
+          {getFieldDecorator('title', {
+            rules: [
+              { required: true, message: '请输入标题' },
+            ],
+          })(
+            <Input placeholder='请输入标题' />,
+          )}
+        </Form.Item>
+        <Form.Item label='正文'>
+          {getFieldDecorator('content', {
+          })(
+            <Editor placeholder='输入内容' />,
+          )}
+        </Form.Item>
+      </Form>
+    </Block>;
+  }
+
+  renderView() {
+    return <div flex>
+      {this.renderBase()}
+      <Row type="flex" justify="center">
+        <Col>
+          <Button type="primary" onClick={() => {
+            this.submit();
+          }}>保存</Button>
+        </Col>
+      </Row>
+    </div>;
+  }
+}

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

@@ -2,9 +2,9 @@ import module from '../../module';
 import group from '../group';
 
 export default {
-  path: '/setting/message',
-  key: 'setting-message',
-  title: '消息',
+  path: '/show/comment',
+  key: 'show-comment',
+  title: '评价',
   needLogin: true,
   module,
   group,

+ 1 - 1
front/project/admin/routes/setting/comment/index.less

@@ -1,6 +1,6 @@
 @charset "utf-8";
 
-#setting-index {
+#show-comment {
   .ant-card.plus {
     text-align: center;
     cursor: pointer;

+ 240 - 0
front/project/admin/routes/show/comment/page.js

@@ -0,0 +1,240 @@
+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, bindSearch, formatDate } from '@src/services/Tools';
+import { asyncSMessage, asyncForm } from '@src/services/AsyncTools';
+import { ChannelModule, SwitchSelect } from '../../../../Constant';
+import { System } from '../../../stores/system';
+import { User } from '../../../stores/user';
+
+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.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: 'nickname',
+      type: 'input',
+      name: '学员昵称',
+    }, {
+      key: 'avatar',
+      type: 'image',
+      name: '学员头像',
+      onUpload: ({ file }) => {
+        return System.uploadImage(file).then(result => { return result; });
+      },
+    }, {
+      key: 'content',
+      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',
+      allowClear: true,
+      name: '频道',
+      select: ChannelModule,
+      placeholder: '请选择',
+    }, {
+      key: 'position',
+      type: 'select',
+      allowClear: true,
+      name: '位置',
+      select: ChannelModule,
+      placeholder: '请选择',
+    }, {
+      key: 'userId',
+      type: 'select',
+      allowClear: true,
+      name: '用户',
+      select: [],
+      number: true,
+      placeholder: '请输入',
+    }, {
+      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: 'user',
+        render: (text, record) => {
+          let extend = '';
+          if (record.isSystem) extend = '系统创建';
+          else if (!record.userId) extend = '未注册';
+          return `${text.nickname || record.nickname}${extend ? `(${extend})` : ''}`;
+        },
+      }, {
+        title: '时间',
+        dataIndex: 'createTime',
+        render: (text) => {
+          return formatDate(text);
+        },
+      }, {
+        title: '精选',
+        dataIndex: 'isSpecial',
+        render: (text) => {
+          return 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>;
+        },
+      },
+    ];
+    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, null);
+  }
+
+  initData() {
+    System.listComment(this.state.search).then(result => {
+      this.setTableData(result.list, result.total);
+    });
+  }
+
+  addAction() {
+    asyncForm('创建评价', this.itemList, {}, data => {
+      return System.addComment(data).then(() => {
+        asyncSMessage('添加成功!');
+        this.refresh();
+      });
+    });
+  }
+
+  editAction(row) {
+    let item = this.itemList;
+    if (row.userId) {
+      item = this.userItemList;
+    }
+    asyncForm('编辑', item, row, data => {
+      return System.editComment(data).then(() => {
+        asyncSMessage('编辑成功!');
+        this.refresh();
+      });
+    });
+  }
+
+  special(row, isSpecial) {
+    System.editComment({ 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
+        select
+        columns={this.tableSort(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 - 3
front/project/admin/routes/setting/tips/index.js

@@ -2,9 +2,9 @@ import module from '../../module';
 import group from '../group';
 
 export default {
-  path: '/setting/tips',
-  key: 'setting-tips',
-  title: '结构说明',
+  path: '/show/faq',
+  key: 'show-faq',
+  title: 'FAQs',
   needLogin: true,
   module,
   group,

+ 1 - 1
front/project/admin/routes/user/ask/index.less

@@ -1,3 +1,3 @@
 @charset "utf-8";
 
-#user-ask {}
+#show-faq {}

front/project/admin/routes/setting/faqSystem/page.js → front/project/admin/routes/show/faq/page.js


+ 5 - 0
front/project/admin/routes/show/group.js

@@ -0,0 +1,5 @@
+export default {
+  key: 'show',
+  name: '展示文字',
+  icon: 'appstore',
+};

+ 10 - 0
front/project/admin/routes/show/index.js

@@ -0,0 +1,10 @@
+
+import tips from './tips';
+import faq from './faq';
+import article from './article';
+import articleDetail from './articleDetail';
+import comment from './comment';
+import ad from './ad';
+import message from './message';
+
+export default [tips, faq, article, articleDetail, comment, ad, message];

+ 15 - 0
front/project/admin/routes/show/message/index.js

@@ -0,0 +1,15 @@
+import module from '../../module';
+import group from '../group';
+
+export default {
+  path: '/show/message',
+  key: 'show-message',
+  title: '消息设置',
+  needLogin: true,
+  module,
+  group,
+  index: true,
+  component() {
+    return import('./page');
+  },
+};

front/project/admin/routes/setting/message/index.less → front/project/admin/routes/show/message/index.less


front/project/admin/routes/setting/message/page.js → front/project/admin/routes/show/message/page.js


+ 16 - 0
front/project/admin/routes/show/readDetail/index.js

@@ -0,0 +1,16 @@
+import module from '../../module';
+import group from '../group';
+
+export default {
+  path: '/show/read/detail',
+  matchPath: '/show/read/detail/:id?',
+  key: 'show-read-detail',
+  title: '推荐阅读',
+  needLogin: true,
+  module,
+  group,
+  showKey: 'show-read',
+  component() {
+    return import('./page');
+  },
+};

+ 3 - 0
front/project/admin/routes/show/readDetail/index.less

@@ -0,0 +1,3 @@
+@charset "utf-8";
+
+#course-experience-detail {}

+ 169 - 0
front/project/admin/routes/show/readDetail/page.js

@@ -0,0 +1,169 @@
+import React from 'react';
+import { Form, Input, Button, Row, Col, InputNumber } from 'antd';
+import './index.less';
+import Editor from '@src/components/Editor';
+import Page from '@src/containers/Page';
+import Block from '@src/components/Block';
+import Select from '@src/components/Select';
+// import FileUpload from '@src/components/FileUpload';
+import { formatFormError, generateSearch } from '@src/services/Tools';
+import { asyncSMessage } from '@src/services/AsyncTools';
+import { PrepareStatus, ExperienceScoreRange, ExperiencePercent } from '../../../../Constant';
+import { Course } from '../../../stores/course';
+import { User } from '../../../stores/user';
+
+const [minScore, maxScore, step] = ExperienceScoreRange;
+let tmp = minScore;
+const ExperienceScore = [];
+while (tmp <= maxScore) {
+  ExperienceScore.push({ label: `${tmp}`, value: tmp });
+  tmp += step;
+}
+
+export default class extends Page {
+  initData() {
+    const { id } = this.params;
+    const { form } = this.props;
+    let handler;
+    if (id) {
+      handler = Course.getExperience({ id });
+    } else {
+      handler = Promise.resolve({});
+    }
+
+    handler
+      .then(result => {
+        generateSearch('userId', {}, this, (search) => {
+          return User.list(search);
+        }, (row) => {
+          return {
+            title: `${row.nickname}(${row.mobile})`,
+            value: row.id,
+          };
+        }, result.userId, null);
+        form.setFieldsValue(result);
+      });
+  }
+
+  submit() {
+    const { form } = this.props;
+    form.validateFields((err) => {
+      if (!err) {
+        const data = form.getFieldsValue();
+        let handler;
+        if (data.id) {
+          handler = Course.editExperience(data);
+        } else {
+          handler = Course.addExperience(data);
+        }
+        handler.then(() => {
+          asyncSMessage('保存成功');
+          goBack();
+        }).catch((e) => {
+          if (e.result) form.setFields(formatFormError(data, e.result));
+        });
+      }
+    });
+  }
+
+  renderBase() {
+    const { getFieldDecorator } = this.props.form;
+    return <Block>
+      <Form>
+        {getFieldDecorator('id')(<input hidden />)}
+        <Form.Item labelCol={{ span: 5 }} wrapperCol={{ span: 16 }} label='题目'>
+          {getFieldDecorator('title', {
+            rules: [
+              { required: true, message: '请输入名称' },
+            ],
+          })(
+            <Input placeholder='请输入名称' />,
+          )}
+        </Form.Item>
+        <Form.Item labelCol={{ span: 5 }} wrapperCol={{ span: 16 }} label='作者'>
+          {getFieldDecorator('userId', {
+            rules: [
+              { required: true, message: '请选择作者' },
+            ],
+          })(
+            <Select {...this.state.userId} placeholder='请选择作者' />,
+          )}
+        </Form.Item>
+        <Form.Item labelCol={{ span: 5 }} wrapperCol={{ span: 16 }} label='昵称'>
+          {getFieldDecorator('nickname', {
+            rules: [
+              { required: true, message: '请输入昵称' },
+            ],
+          })(
+            <Input placeholder='输入昵称' />,
+          )}
+        </Form.Item>
+        <Form.Item labelCol={{ span: 5 }} wrapperCol={{ span: 16 }} label='作者信息'>
+          <Row>
+            <Col span={6}>
+              {getFieldDecorator('prepareStatus', {
+                rules: [{
+                  required: true, message: '请选择',
+                }],
+              })(
+                <Select select={PrepareStatus} placeholder='身份' />,
+              )}
+            </Col>
+            <Col span={6}>
+              {getFieldDecorator('experienceDay', {
+                rules: [{
+                  required: true, message: '请输入备考天数',
+                }],
+              })(
+                <InputNumber placeholder='备考周期: 天' />,
+              )}
+            </Col>
+            <Col span={6}>
+              {getFieldDecorator('experienceScore', {
+                rules: [{
+                  required: true, message: '请选择',
+                }],
+              })(
+                <Select select={ExperienceScore} placeholder='分手成绩' />,
+              )}
+            </Col>
+            <Col span={6}>
+              {getFieldDecorator('experiencePercent', {
+                rules: [{
+                  required: true, message: '请选择',
+                }],
+              })(
+                <Select select={ExperiencePercent} placeholder='提分范围' />,
+              )}
+            </Col>
+          </Row>
+        </Form.Item>
+        <Form.Item labelCol={{ span: 5 }} wrapperCol={{ span: 16 }} label='问卷链接'>
+          {getFieldDecorator('link', {
+          })(
+            <Input placeholder='输入内容' />,
+          )}
+        </Form.Item>
+        <Form.Item label='正文'>
+          {getFieldDecorator('content', {
+          })(
+            <Editor placeholder='输入内容' />,
+          )}
+        </Form.Item>
+      </Form>
+    </Block>;
+  }
+
+  renderView() {
+    return <div flex>
+      {this.renderBase()}
+      <Row type="flex" justify="center">
+        <Col>
+          <Button type="primary" onClick={() => {
+            this.submit();
+          }}>保存</Button>
+        </Col>
+      </Row>
+    </div>;
+  }
+}

+ 3 - 3
front/project/admin/routes/course/ask/index.js

@@ -2,9 +2,9 @@ import module from '../../module';
 import group from '../group';
 
 export default {
-  path: '/course/ask',
-  key: 'course-ask',
-  title: '学生提问',
+  path: '/show/tips',
+  key: 'show-tips',
+  title: '说明文案',
   needLogin: true,
   module,
   group,

front/project/admin/routes/setting/tips/index.less → front/project/admin/routes/show/tips/index.less


front/project/admin/routes/setting/tips/page.js → front/project/admin/routes/show/tips/page.js


+ 15 - 0
front/project/admin/routes/student/askCourse/index.js

@@ -0,0 +1,15 @@
+import module from '../../module';
+import group from '../group';
+
+export default {
+  path: '/student/ask/course',
+  key: 'studennt-ask-course',
+  title: '课程提问',
+  needLogin: true,
+  module,
+  group,
+  index: true,
+  component() {
+    return import('./page');
+  },
+};

+ 3 - 0
front/project/admin/routes/student/askCourse/index.less

@@ -0,0 +1,3 @@
+@charset "utf-8";
+
+#student-ask-course {}

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

@@ -132,7 +132,7 @@ export default class extends Page {
       render: (text, record) => {
         return <div className="table-button">
           {(
-            <Link to={`/course/ask/detail/${record.id}`}>编辑</Link>
+            <Link to={`/student/ask/course/detail/${record.id}`}>编辑</Link>
           )}
         </div>;
       },
@@ -144,7 +144,7 @@ export default class extends Page {
         title: `${row.nickname}(${row.mobile})`,
         value: row.id,
       };
-    }, this.state.search.userId ? Number(this.state.search.userId) : [], null);
+    }, this.state.search.userId ? Number(this.state.search.userId) : null, null);
     Exercise.courseStruct().then((result) => {
       const list = result.map(row => { row.title = `${row.titleZh}`; row.value = row.id; return row; });
       this.filterForm[0].tree = formatTreeData(list, 'id', 'title', 'parentId');

+ 3 - 3
front/project/admin/routes/course/askDetail/index.js

@@ -2,9 +2,9 @@ import module from '../../module';
 import group from '../group';
 
 export default {
-  path: '/course/ask/detail/:id?',
-  key: 'course-ask-detail',
-  title: '问详情',
+  path: '/student/ask/course/detail/:id?',
+  key: 'student-ask-course-detail',
+  title: '问详情',
   needLogin: true,
   module,
   group,

+ 3 - 0
front/project/admin/routes/student/askCourseDetail/index.less

@@ -0,0 +1,3 @@
+@charset "utf-8";
+
+#student-ask-course-detail {}

front/project/admin/routes/course/askDetail/page.js → front/project/admin/routes/student/askCourseDetail/page.js


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

@@ -2,9 +2,9 @@ import module from '../../module';
 import group from '../group';
 
 export default {
-  path: '/setting/faq/consult',
-  key: 'setting-faq-consult',
-  title: '咨询管理',
+  path: '/student/ask/question',
+  key: 'student-ask-question',
+  title: '题目提问',
   needLogin: true,
   module,
   group,

+ 3 - 0
front/project/admin/routes/student/askQuestion/index.less

@@ -0,0 +1,3 @@
+@charset "utf-8";
+
+#student-ask-question {}

+ 3 - 3
front/project/admin/routes/user/ask/page.js

@@ -125,7 +125,7 @@ export default class extends Page {
       render: (text, record) => {
         return <div className="table-button">
           {(
-            <Link to={`/user/ask/detail/${record.id}`}>编辑</Link>
+            <Link to={`/student/ask/question/detail/${record.id}`}>编辑</Link>
           )}
         </div>;
       },
@@ -137,7 +137,7 @@ export default class extends Page {
         title: `${row.nickname}(${row.mobile})`,
         value: row.id,
       };
-    }, this.state.search.userId ? Number(this.state.search.userId) : [], null);
+    }, this.state.search.userId ? Number(this.state.search.userId) : null, null);
     bindSearch(this.filterForm, 'questionNoId', this, (search) => {
       return Question.searchNo(search);
     }, (row) => {
@@ -149,7 +149,7 @@ export default class extends Page {
   }
 
   initData() {
-    Question.listAsk(this.state.search).then(result => {
+    Question.listAsk(Object.assign({ hasRecord: true }, this.state.search)).then(result => {
       this.setTableData(result.list, result.total);
     });
   }

+ 15 - 0
front/project/admin/routes/student/askQuestionDetail/index.js

@@ -0,0 +1,15 @@
+import module from '../../module';
+import group from '../group';
+
+export default {
+  path: '/student/ask/question/detail/:id?',
+  key: 'student-ask-question-detail',
+  title: '提问详情',
+  needLogin: true,
+  module,
+  group,
+  showKey: 'student-ask-question',
+  component() {
+    return import('./page');
+  },
+};

+ 3 - 0
front/project/admin/routes/student/askQuestionDetail/index.less

@@ -0,0 +1,3 @@
+@charset "utf-8";
+
+#student-ask-question-detail {}

+ 213 - 0
front/project/admin/routes/student/askQuestionDetail/page.js

@@ -0,0 +1,213 @@
+import React from 'react';
+import { Form, Button, Row, Col, List, Icon, Switch, Typography, Input } from 'antd';
+import './index.less';
+import Editor from '@src/components/Editor';
+import Page from '@src/containers/Page';
+import Block from '@src/components/Block';
+import DragList from '@src/components/DragList';
+// import FileUpload from '@src/components/FileUpload';
+import { formatFormError, formatDate, getMap } from '@src/services/Tools';
+import { asyncSMessage } from '@src/services/AsyncTools';
+import { UserUrl, AskTarget, QuestionType } from '../../../../Constant';
+// import { User } from '../../../stores/user';
+import { Question } from '../../../stores/question';
+
+const QuestionTypeMap = getMap(QuestionType, 'value', 'label');
+const AskTargetMap = getMap(AskTarget, 'value', 'label');
+export default class extends Page {
+  init() {
+    // Exercise.allStruct().then(result => {
+    //   result = result.filter(row => row.level === 2).map(row => { row.title = `${row.titleZh}/${row.titleEn}`; row.value = row.id; return row; });
+    //   this.setState({ exercise: result });
+    // });
+  }
+
+  initData() {
+    const { id } = this.params;
+    let handler;
+    if (id) {
+      handler = Question.getAsk({ id });
+    } else {
+      handler = Promise.resolve({ others: [] });
+    }
+    handler
+      .then(result => {
+        result.ignoreStatus = result.answerStatus === 2;
+        const { getFieldDecorator, setFieldsValue } = this.props.form;
+        getFieldDecorator('id');
+        getFieldDecorator('answer');
+        getFieldDecorator('showStatus');
+        setFieldsValue({ id: result.id, answer: result.answer, showStatus: result.showStatus });
+        this.setState({ data: result });
+      });
+  }
+
+  orderQuestion(oldIndex, newIndex) {
+    const { data } = this.state;
+    const { others = [] } = data;
+    const tmp = others[oldIndex];
+    others[oldIndex] = others[newIndex];
+    others[newIndex] = tmp;
+    this.setState({ others });
+  }
+
+  addOrder() {
+    const { data } = this.state;
+    const { others } = data;
+    others.push(data);
+    this.setState({ data });
+  }
+
+  removeOrder() {
+    const { data } = this.state;
+    const { others } = data;
+    data.others = others.filter(row => row.id !== data.id);
+    this.setState({ data });
+  }
+
+  submit() {
+    const { form } = this.props;
+    form.validateFields((err) => {
+      if (!err) {
+        const data = form.getFieldsValue();
+        data.showStatus = data.showStatus ? 1 : 0;
+        data.ignoreStatus = data.ignoreStatus ? 1 : 0;
+        data.other = this.state.data.others.map(row => row.id);
+        Question.editAsk(data).then(() => {
+          asyncSMessage('保存成功');
+        }).catch((e) => {
+          if (e.result) form.setFields(formatFormError(data, e.result));
+        });
+      }
+    });
+  }
+
+  renderBase() {
+    const { data } = this.state;
+    const { question = {}, questionNo = {} } = data;
+    return <Block>
+      <h1>题目信息</h1>
+      <Form>
+        <Form.Item labelCol={{ span: 5 }} wrapperCol={{ span: 16 }} label='题型'>
+          {QuestionTypeMap[question.type]}
+        </Form.Item>
+        <Form.Item labelCol={{ span: 5 }} wrapperCol={{ span: 16 }} label='题目id'>
+          <a href='' target='_blank'>{questionNo.no}</a>
+        </Form.Item>
+      </Form>
+    </Block>;
+  }
+
+  renderAsk() {
+    const { getFieldDecorator, getFieldValue } = this.props.form;
+    const { data, editContent } = this.state;
+    const { user = {}, createTime, target, originContent, content } = data;
+    return <Block>
+      <h1>提问信息</h1>
+      <Form>
+        <Form.Item labelCol={{ span: 5 }} wrapperCol={{ span: 16 }} label='用户姓名'>
+          {user.realName}
+        </Form.Item>
+        <Form.Item labelCol={{ span: 5 }} wrapperCol={{ span: 16 }} label='提问时间'>
+          {formatDate(createTime)}
+        </Form.Item>
+        <Form.Item labelCol={{ span: 5 }} wrapperCol={{ span: 16 }} label='提问模块'>
+          {AskTargetMap[target]}
+        </Form.Item>
+        <Form.Item labelCol={{ span: 5 }} wrapperCol={{ span: 16 }} label='提问内容'>
+          {originContent}
+        </Form.Item>
+        <Form.Item labelCol={{ span: 5 }} wrapperCol={{ span: 16 }} label='提问详情'>
+          {!editContent && content}
+          {getFieldDecorator('content', {
+          })(
+            editContent ? <Input.TextArea placeholder='输入内容' /> : <input hidden />,
+          )}
+          <Switch checked={editContent} checkedChildren='编辑模式' unCheckedChildren='关闭' onChange={(value) => {
+            data.content = getFieldValue('content');
+            this.setState({ editContent: value, data });
+          }} />
+        </Form.Item>
+      </Form>
+    </Block>;
+  }
+
+  renderQuestionList() {
+    return <Block>
+      <h1>该题目的展示中问题</h1>
+      <DragList
+        loading={this.props.core.loading}
+        dataSource={this.state.data.others || []}
+        handle={'.icon'}
+        onMove={(oldIndex, newIndex) => {
+          this.orderQuestion(oldIndex, newIndex);
+        }}
+        renderItem={(item) => (
+          <List.Item actions={[<Icon type='bars' className='icon' />, <Typography.Text copyable={{ text: `${UserUrl}/paper/question/${this.state.data.userQuestionId}?askId=${item.id}` }} />]}>
+            <Row style={{ width: '100%' }}>
+              <Col span={11}>问题:<span dangerouslySetInnerHTML={{ __html: item.content }} /></Col>
+              <Col span={11} offset={1}>答复:<span dangerouslySetInnerHTML={{ __html: item.answer }} /></Col>
+            </Row>
+          </List.Item>
+        )}
+      /></Block>;
+  }
+
+  renderAnswer() {
+    const { getFieldDecorator } = this.props.form;
+    return <Block>
+      <Form>
+        {getFieldDecorator('id')(<input hidden />)}
+        <Form.Item label='教师回复'>
+          {getFieldDecorator('answer', {
+          })(
+            <Editor placeholder='输入内容' />,
+          )}
+        </Form.Item>
+        <Row type="flex" justify="center">
+          <Col span={12}>
+            <Form.Item labelCol={{ span: 12 }} wrapperCol={{ span: 10 }} label='显示状态'>
+              {getFieldDecorator('showStatus', {
+                valuePropName: 'checked',
+              })(
+                <Switch onChange={(value) => {
+                  if (value) {
+                    this.addOrder();
+                  } else {
+                    this.removeOrder();
+                  }
+                }} checkedChildren='on' unCheckedChildren='off' />,
+              )}
+            </Form.Item>
+          </Col>
+          <Col span={12}>
+            <Form.Item labelCol={{ span: 12 }} wrapperCol={{ span: 10 }} label='是否忽略'>
+              {getFieldDecorator('ignoreStatus', {
+                valuePropName: 'checked',
+              })(
+                <Switch checkedChildren='on' unCheckedChildren='off' />,
+              )}
+            </Form.Item>
+          </Col>
+        </Row>
+      </Form>
+    </Block>;
+  }
+
+  renderView() {
+    return <div flex>
+      {this.renderBase()}
+      {this.renderAsk()}
+      {this.renderQuestionList()}
+      {this.renderAnswer()}
+
+      <Row type="flex" justify="center">
+        <Col>
+          <Button type="primary" onClick={() => {
+            this.submit();
+          }}>保存</Button>
+        </Col>
+      </Row>
+    </div>;
+  }
+}

+ 5 - 0
front/project/admin/routes/student/group.js

@@ -0,0 +1,5 @@
+export default {
+  key: 'student',
+  name: '学员管理',
+  icon: 'appstore',
+};

+ 9 - 0
front/project/admin/routes/student/index.js

@@ -0,0 +1,9 @@
+
+import study from './study';
+import studyDetail from './studyDetail';
+import askQuestion from './askQuestion';
+import askQuestionDetail from './askQuestionDetail';
+import askCourse from './askCourse';
+import askCourseDetail from './askCourseDetail';
+
+export default [study, studyDetail, askQuestion, askQuestionDetail, askCourse, askCourseDetail];

+ 15 - 0
front/project/admin/routes/student/study/index.js

@@ -0,0 +1,15 @@
+import module from '../../module';
+import group from '../group';
+
+export default {
+  path: '/student/study',
+  key: 'student-study',
+  title: '学生列表',
+  needLogin: true,
+  module,
+  group,
+  index: true,
+  component() {
+    return import('./page');
+  },
+};

+ 3 - 0
front/project/admin/routes/student/study/index.less

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

front/project/admin/routes/course/study/page.js → front/project/admin/routes/student/study/page.js


front/project/admin/routes/course/studyDetail/index.js → front/project/admin/routes/student/studyDetail/index.js


front/project/admin/routes/course/studyDetail/index.less → front/project/admin/routes/student/studyDetail/index.less


front/project/admin/routes/course/studyDetail/page.js → front/project/admin/routes/student/studyDetail/page.js


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

@@ -4,7 +4,7 @@ import group from '../group';
 export default {
   path: '/subject/examination',
   key: 'subject-examination',
-  title: '模考列表',
+  title: '模考列表',
   needLogin: true,
   module,
   group,

+ 3 - 6
front/project/admin/routes/subject/index.js

@@ -1,13 +1,10 @@
 import question from './question';
 import exercise from './exercise';
 import examination from './examination';
-import textbook from './textbook';
-import textbookLibrary from './textbookLibrary';
-import textbookQuestion from './textbookQuestion';
 import sentence from './sentence';
-import preview from './preview';
-import previewDetail from './previewDetail';
 import sentenceArticle from './sentenceArticle';
 import sentenceQuestion from './sentenceQuestion';
+import textbook from './textbook';
+import textbookQuestion from './textbookQuestion';
 
-export default [question, exercise, examination, textbook, textbookLibrary, textbookQuestion, sentence, preview, previewDetail, sentenceArticle, sentenceQuestion];
+export default [question, exercise, examination, sentence, sentenceArticle, sentenceQuestion, textbook, textbookQuestion];

+ 0 - 3
front/project/admin/routes/subject/preview/index.less

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

+ 0 - 15
front/project/admin/routes/subject/previewDetail/index.js

@@ -1,15 +0,0 @@
-import module from '../../module';
-import group from '../group';
-
-export default {
-  path: '/subject/preview/detail/:id?',
-  key: 'subject-preview-detail',
-  title: '预习作业处理',
-  needLogin: true,
-  module,
-  group,
-  showKey: 'subject-preview',
-  component() {
-    return import('./page');
-  },
-};

+ 0 - 3
front/project/admin/routes/subject/previewDetail/index.less

@@ -1,3 +0,0 @@
-@charset "utf-8";
-
-#subject-preview-detail {}

+ 1 - 1
front/project/admin/routes/subject/sentence/index.js

@@ -4,7 +4,7 @@ import group from '../group';
 export default {
   path: '/subject/sentence',
   key: 'subject-sentence',
-  title: '长难句',
+  title: '长难句列表',
   needLogin: true,
   module,
   group,

+ 1 - 1
front/project/admin/routes/subject/textbook/index.js

@@ -4,7 +4,7 @@ import group from '../group';
 export default {
   path: '/subject/textbook',
   key: 'subject-textbook',
-  title: '机经列表',
+  title: '数学机经列表',
   needLogin: true,
   module,
   group,

+ 0 - 15
front/project/admin/routes/subject/textbookLibrary/index.js

@@ -1,15 +0,0 @@
-import module from '../../module';
-import group from '../group';
-
-export default {
-  path: '/subject/textbook/library',
-  key: 'subject-textbook/library',
-  title: '换库列表',
-  needLogin: true,
-  module,
-  group,
-  index: true,
-  component() {
-    return import('./page');
-  },
-};

+ 0 - 3
front/project/admin/routes/subject/textbookLibrary/index.less

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

+ 15 - 0
front/project/admin/routes/textbook/feedback/index.js

@@ -0,0 +1,15 @@
+import module from '../../module';
+import group from '../group';
+
+export default {
+  path: '/textbook/feedback',
+  key: 'textbook-feedback',
+  title: '机经反馈',
+  needLogin: true,
+  module,
+  group,
+  index: true,
+  component() {
+    return import('./page');
+  },
+};

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

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

+ 233 - 0
front/project/admin/routes/textbook/feedback/page.js

@@ -0,0 +1,233 @@
+import React from 'react';
+import { Modal, Button } from 'antd';
+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, formatDate } from '@src/services/Tools';
+import { asyncSMessage, asyncDelConfirm } from '@src/services/AsyncTools';
+import { TextbookFeedbackStatus, TextbookFeedbackTarget, TextbookSubject } from '../../../../Constant';
+import { User } from '../../../stores/user';
+
+const TextbookSubjectMap = getMap(TextbookSubject, 'value', 'label');
+const TextbookFeedbackStatusMap = getMap(TextbookFeedbackStatus, 'value', 'label');
+const TextbookFeedbackTargetMap = getMap(TextbookFeedbackTarget, 'value', 'label');
+export default class extends Page {
+  init() {
+    this.actionList = [{
+      key: 'handle',
+      type: 'danger',
+      name: '批量采纳',
+      needSelect: 1,
+    }, {
+      key: 'nohandle',
+      type: 'danger',
+      name: '批量不修改',
+      needSelect: 1,
+    }, {
+      key: 'ignore',
+      type: 'danger',
+      name: '批量忽略',
+      needSelect: 1,
+    }];
+    this.filterForm = [{
+      key: 'questionSubject',
+      type: 'select',
+      allowClear: true,
+      name: '单项',
+      select: TextbookSubject,
+      placeholder: '请选择',
+    }, {
+      key: 'target',
+      type: 'select',
+      allowClear: true,
+      name: '反馈类型',
+      select: TextbookFeedbackTarget,
+      placeholder: '请选择',
+    }, {
+      key: 'status',
+      type: 'select',
+      name: '处理状态',
+      allowClear: true,
+      select: TextbookFeedbackStatus,
+      number: true,
+      placeholder: '请选择',
+    }, {
+      key: 'no',
+      type: 'number',
+      name: '序号',
+      allowClear: true,
+      number: true,
+      placeholder: '请输入',
+    }];
+    this.columns = [{
+      title: '单项',
+      dataIndex: 'topic.questionSubject',
+      render: (text) => {
+        return TextbookSubjectMap[text] || '';
+      },
+    }, {
+      title: '反馈类型',
+      dataIndex: 'target',
+      render: (text) => {
+        return TextbookFeedbackTargetMap[text] || '';
+      },
+    }, {
+      title: '换库表',
+      dataIndex: 'library',
+      render: (text) => {
+        return `${formatDate(text.startDate, 'YYYY-MM-DD')}-${text.endDate ? `${formatDate(text.endDate, 'YYYY-MM-DD')}` : '至今'}`;
+      },
+    }, {
+      title: '题目序号',
+      dataIndex: 'topic.no',
+    }, {
+      title: '关键词',
+      dataIndex: 'topic.keyword',
+    }, {
+      title: '提交时间',
+      dataIndex: 'createTime',
+      render: (text) => {
+        return formatDate(text);
+      },
+    }, {
+      title: '提交人',
+      dataIndex: 'user.nickname',
+    }, {
+      title: '处理状态',
+      dataIndex: 'status',
+      render: (text) => {
+        return TextbookFeedbackStatusMap[text] || text;
+      },
+    }, {
+      title: '操作',
+      dataIndex: 'handler',
+      render: (text, record) => {
+        return <div className="table-button">
+          {(
+            <a onClick={() => {
+              this.detailAction(record);
+            }}>查看</a>
+          )}
+        </div>;
+      },
+    }];
+  }
+
+  initData() {
+    User.listTextbookFeedback(this.state.search).then(result => {
+      this.setTableData(result.list, result.total);
+    });
+  }
+
+  detailAction(row) {
+    this.setState({ detail: row });
+  }
+
+  handleDetail() {
+    const { detail } = this.state;
+    asyncDelConfirm('采纳确认', '是否采纳选中记录?', () => {
+      return User.editTextbookFeedback({ id: detail.id, status: 1 }).then(() => {
+        asyncSMessage('操作成功!');
+        this.setState({ detail: null });
+        this.refresh();
+      });
+    });
+  }
+
+  nohandleDetail() {
+    const { detail } = this.state;
+    asyncDelConfirm('不处理确认', '是否不处理选中记录?', () => {
+      return User.editTextbookFeedback({ id: detail.id, status: 3 }).then(() => {
+        asyncSMessage('操作成功!');
+        this.setState({ detail: null });
+        this.refresh();
+      });
+    });
+  }
+
+  ignoreDetail() {
+    const { detail } = this.state;
+    asyncDelConfirm('忽略确认', '是否忽略选中记录?', () => {
+      return User.editTextbookFeedback({ id: detail.id, status: 2 }).then(() => {
+        asyncSMessage('操作成功!');
+        this.setState({ detail: null });
+        this.refresh();
+      });
+    });
+  }
+
+  handleAction() {
+    const { selectedKeys } = this.state;
+    asyncDelConfirm('采纳确认', '是否采纳选中记录?', () => {
+      return Promise.all(selectedKeys.map(row => User.editTextbookFeedback({ id: row, status: 1 }))).then(() => {
+        asyncSMessage('操作成功!');
+        this.refresh();
+      });
+    });
+  }
+
+  nohandleAction() {
+    const { selectedKeys } = this.state;
+    asyncDelConfirm('不修改确认', '是否不修改选中记录?', () => {
+      return Promise.all(selectedKeys.map(row => User.editTextbookFeedback({ id: row, status: 3 }))).then(() => {
+        asyncSMessage('操作成功!');
+        this.refresh();
+      });
+    });
+  }
+
+  ignoreAction() {
+    const { selectedKeys } = this.state;
+    asyncDelConfirm('忽略确认', '是否忽略选中记录?', () => {
+      return Promise.all(selectedKeys.map(row => User.editTextbookFeedback({ id: row, status: 2 }))).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
+        select
+        columns={this.tableSort(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}
+      />
+      {this.state.detail && <Modal visible title='勘误详情' footer={null} closable onCancel={() => {
+        this.setState({ detail: null });
+      }}>
+        <p>单项:{TextbookSubjectMap[this.state.detail.topic.questionSubject]}</p>
+        <p>换库表:{formatDate(this.state.detail.library.startDate, 'YYYY-MM-DD')}-{this.state.detail.library.endDate ? `${formatDate(this.state.detail.library.endDate, 'YYYY-MM-DD')}` : '至今'}</p>
+        <p>反馈题目序号:{this.state.detail.topic.no}</p>
+        <p>反馈内容{this.state.detail.content}</p>
+        {!this.state.detail.status && <p><Button type="primary" onClick={() => {
+          this.handleDetail();
+        }}>采纳</Button><Button type="primary" onClick={() => {
+          this.nohandleDetail();
+        }}>无需修改</Button><Button type="ghost" onClick={() => {
+          this.ignoreDetail();
+        }}>忽略</Button></p>}
+      </Modal>}
+    </Block>;
+  }
+}

+ 5 - 0
front/project/admin/routes/textbook/group.js

@@ -0,0 +1,5 @@
+export default {
+  key: 'textbook',
+  name: '换库机经板块',
+  icon: 'appstore',
+};

+ 0 - 0
front/project/admin/routes/textbook/index.js


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