1
0
Quellcode durchsuchen

feat(front): 个人中心-数据

Go vor 4 Jahren
Ursprung
Commit
ed83b6e3e2

+ 4 - 0
front/project/Constant.js

@@ -191,6 +191,10 @@ export const AdPlace = [
   { label: '顶部', value: 'top', parent: 'course-index' },
 ];
 
+export const TimeRange = [{ title: '今天', key: 'today' }, { title: '近一周', key: 'week' }, { title: '近一个月', key: 'month' }, { title: '近三个月', key: 'month3' }, { title: '全部', key: 'all' }];
+
+export const TextbookMinYear = 2018;
+
 export const MobileArea = ['+86', '+1'].map(row => {
   return { label: row, value: row };
 });

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

@@ -8,9 +8,9 @@ export default class Select extends Component {
     this.state = { selecting: false };
   }
 
-  componentWillMount() {}
+  componentWillMount() { }
 
-  componentWillUnmount() {}
+  componentWillUnmount() { }
 
   open() {
     this.setState({ selecting: true });
@@ -23,14 +23,14 @@ export default class Select extends Component {
   render() {
     const { selecting } = this.state;
     const { placeholder, value, list = [], size = 'basic', theme = 'theme', excludeSelf, onChange } = this.props;
-    let index = 0;
+    let index = -1;
     for (let i = 0; i < list.length; i += 1) {
       if (list[i].key === value) {
         index = i;
         break;
       }
     }
-    const title = list.length > 0 ? list[index].title : placeholder;
+    const title = list.length > 0 && index >= 0 ? list[index].title : placeholder;
     return (
       <div className={`select ${theme || ''} ${size}`}>
         <div hidden={!selecting} className="mask" onClick={() => this.close()} />

+ 220 - 46
front/project/www/routes/my/data/page.js

@@ -12,11 +12,13 @@ import Select from '../../../components/Select';
 import menu from '../index';
 import Tabs from '../../../components/Tabs';
 import { My } from '../../../stores/my';
-import { QuestionDifficult } from '../../../../Constant';
-import { getMap, formatPercent, formatSeconds } from '../../../../../src/services/Tools';
+import { QuestionDifficult, QuestionType, TimeRange, TextbookMinYear, CourseModule } from '../../../../Constant';
+import { getMap, formatPercent, formatSeconds, timeRange, formatTreeData } from '../../../../../src/services/Tools';
+import { Main } from '../../../stores/main';
 
 const QuestionDifficultMap = getMap(QuestionDifficult, 'value', 'label');
-console.log(QuestionDifficultMap);
+const QuestionTypeMap = getMap(QuestionType, 'value', 'label');
+
 const columns = [
   {
     key: 'title',
@@ -298,39 +300,195 @@ export default class extends Page {
       filterMap: {},
       selectList: [],
       tab: 'exercise',
+      subject: 'verbal',
       questionType: '',
+      timerange: 'today',
       info: 'base',
     };
   }
 
   initData() {
     const data = Object.assign(this.state, this.state.search);
+    data.filterMap = this.state;
     if (data.order) {
       data.sortMap = { [data.order]: data.direction };
     }
-    data.filterMap = this.state.search;
-    const startTime = null;
-    const endTime = null;
-    switch (data.timerange) {
-      case 'all':
-        break;
-      case 'week':
-        break;
-      case 'month':
-        break;
-      case 'month3':
-        break;
-      case 'today':
+    const [startTime, endTime] = timeRange(data.timerange);
+    this.refreshQuestionType(data.subject, { needSentence: false, allSubject: false })
+      .then(() => {
+        return this.refreshStruct(data.tab, data.one, data.two, { needPreview: false, needTextbook: false });
+      })
+      .then(({ structIds }) => {
+        My.getData(data.tab, data.subject, structIds, startTime, endTime).then(result => {
+          this.data = result;
+          this.setState({
+            list: Object.values(result).map(row => {
+              row.title = QuestionTypeMap[row.questionType];
+              return row;
+            }),
+          });
+          this.onQuestionTypeChange(Object.keys(result)[0]);
+        });
+      });
+  }
+
+  refreshQuestionType(subject, { needSentence, allSubject }) {
+    return Main.getExercise().then(result => {
+      const list = result.filter(row => (needSentence ? true : row.isExamination)).map(row => {
+        row.title = `${row.titleZh}${row.titleEn}`;
+        row.key = row.extend;
+        return row;
+      });
+      const tree = formatTreeData(list, 'id', 'title', 'parentId');
+      this.questionSubjectMap = getMap(tree, 'key', 'children');
+      this.questionSubjectSelect = tree.filter(row => row.level === 1 && (allSubject ? true : row.children.length > 1));
+      this.setState({
+        questionSubjectSelect: this.questionSubjectSelect,
+        questionSubjectMap: this.questionSubjectMap,
+      });
+      return {
+        questionTypes: subject ? this.questionSubjectMap[subject].map(row => row.key) : null,
+      };
+    });
+  }
+
+  refreshStruct(module, one, two, { needTextbook, needPreview }) {
+    switch (module) {
+      case 'exercise':
+        return Main.getExerciseAll().then(result => {
+          const tmp = result.filter(row => row.level > 2).map(row => {
+            row.title = `${row.titleZh}`;
+            row.key = row.titleEn;
+            return row;
+          });
+          const idsMap = getMap(tmp, 'id', 'key');
+          const map = {};
+          tmp.forEach(row => {
+            if (!map[row.key]) {
+              map[row.key] = {
+                title: row.title,
+                key: row.key,
+                structIds: [],
+                parentId: row.level > 3 ? idsMap[row.parentId] : null,
+                subject: [],
+                questionType: [],
+              };
+            }
+            const item = map[row.key];
+            item.structIds.push(row.id);
+            if (item.subject.indexOf(row.subject) < 0) {
+              item.subject.push(row.subject);
+            }
+            if (item.questionType.indexOf(row.questionType) < 0) {
+              item.questionType.push(row.questionType);
+            }
+          });
+          const list = Object.values(map);
+          if (needPreview) {
+            list.push({
+              title: '预习作业',
+              key: 'preview',
+              id: 'preview',
+            });
+            CourseModule.forEach(row => {
+              list.push({
+                title: row.label,
+                key: row.value,
+                parentId: 'preview',
+              });
+            });
+          }
+          let courseModules = null;
+          let structIds = null;
+          if (one === 'preview') {
+            if (!two) {
+              courseModules = CourseModule.map(row => row.value);
+            } else {
+              courseModules = [two];
+            }
+          } else if (one) {
+            const resultMap = getMap(list, 'key', 'structIds');
+            if (!two) {
+              structIds = resultMap[one];
+            } else {
+              structIds = resultMap[two];
+            }
+          }
+
+          const tree = formatTreeData(list, 'key', 'title', 'parentId');
+          const oneSelect = tree;
+          const twoSelectMap = getMap(tree, 'key', 'children');
+          this.setState({ oneSelect, twoSelectMap });
+
+          return {
+            structIds,
+            courseModules,
+          };
+        });
+      case 'examination':
+        return Main.getExamination().then(result => {
+          const list = result.map(row => {
+            row.title = `${row.titleZh}${row.titleEn}`;
+            row.key = `${row.id}`;
+            return row;
+          });
+          if (needTextbook) {
+            list.push({
+              title: '数学机经',
+              key: 'textbook',
+              id: 'textbook',
+            });
+            list.push({
+              title: '最新',
+              key: 'latest',
+              parentId: 'textbook',
+            });
+
+            const nowYear = new Date().getFullYear();
+            for (let i = TextbookMinYear; i <= nowYear; i += 1) {
+              list.push({
+                title: i.toString(),
+                key: i.toString(),
+                parentId: 'textbook',
+              });
+            }
+          }
+          let latest = null;
+          let year = null;
+          let structIds = null;
+          if (one === 'textbook') {
+            if (!two) {
+              latest = true;
+            } else if (two === 'latest') {
+              latest = true;
+            } else {
+              year = two;
+            }
+          } else if (one) {
+            if (!two) {
+              structIds = [Number(one)];
+            } else {
+              structIds = [Number(two)];
+            }
+          }
+
+          const tree = formatTreeData(list, 'key', 'title', 'parentId');
+          const oneSelect = tree;
+          const twoSelectMap = getMap(tree, 'key', 'children');
+
+          this.setState({ oneSelect, twoSelectMap });
+          return {
+            structIds,
+            latest,
+            year,
+          };
+        });
       default:
+        return Promise.resolve({});
     }
-    My.getData(data.tab, data.subject, '', startTime, endTime).then(result => {
-      console.log(result);
-      this.data = result;
-      this.setState({ list: Object.values(result) });
-      this.onQuestionTypeChange();
-    });
   }
 
+
   onTabChange(tab) {
     const data = { tab };
     this.refreshQuery(data);
@@ -361,7 +519,7 @@ export default class extends Page {
   }
 
   renderTable() {
-    const { tab, questionType, info, questionTypeSelect, filterMap = {}, list = [] } = this.state;
+    const { tab, subject, questionType, info, questionSubjectSelect, questionSubjectMap = {}, oneSelect, twoSelectMap = {}, filterMap = {}, list = [] } = this.state;
     return (
       <div className="table-layout">
         <Tabs
@@ -376,24 +534,31 @@ export default class extends Page {
           onChange={key => this.onTabChange(key)}
         />
         <UserAction
-          selectList={[{
-            children: [
-              {
-                key: 'one',
-                select: [{ title: '123', key: '1' }, { title: '123', key: '2' }, { title: '123', key: '2' }],
-              },
-              {
-                key: 'two',
-                be: 'one',
-                placeholder: '全部',
-                selectMap: [{ title: '123', key: '1' }, { title: '123', key: '2' }, { title: '123', key: '2' }],
-              },
-            ],
-          }, {
-            right: true,
-            key: 'timerange',
-            select: [{ title: '今天', key: 'today' }, { title: '近一周', key: 'week' }, { title: '近一个月', key: 'month' }, { title: '近三个月', key: 'month3' }, { title: '全部', key: 'all' }],
-          }]}
+          selectList={[
+            {
+              key: 'subject',
+              select: questionSubjectSelect,
+            },
+            {
+              label: '范围',
+              children: [
+                {
+                  key: 'one',
+                  placeholder: '全部',
+                  select: oneSelect,
+                },
+                {
+                  key: 'two',
+                  be: 'one',
+                  placeholder: '全部',
+                  selectMap: twoSelectMap,
+                },
+              ],
+            }, {
+              right: true,
+              key: 'timerange',
+              select: TimeRange,
+            }]}
           filterMap={filterMap}
           onFilter={value => this.onFilter(value)}
         />
@@ -405,7 +570,7 @@ export default class extends Page {
             size="small"
             theme="default"
             value={questionType}
-            list={questionTypeSelect}
+            list={questionSubjectMap[subject]}
             onChange={({ key }) => this.onQuestionTypeChange(key)}
           />
         </div>
@@ -473,22 +638,31 @@ export default class extends Page {
       <div className="tab-2-layout">
         <BarChart
           height={350}
-          option={barOption2(`平均正确率${formatPercent(data.userCorrect, data.userNumber, false)}`, '正确率', [['easy', 10], ['Medium', 30], ['Hard', 40]])}
+          option={barOption2(`平均正确率${formatPercent(data.userCorrect, data.userNumber, false)}`, '正确率', data.difficult.map(row => {
+            return [QuestionDifficultMap[row.key], formatPercent(row.userCorrect, row.userNumber)];
+          }))}
         />
       </div>
     );
   }
 
   renderTabplace() {
+    const { data = {} } = this.state;
     return (
       <div className="tab-3-layout">
         <BarChart
           height={350}
           option={barOption3(
             ['知识点', '正确率分析', '用时分析'],
-            ['Idiom', 'Comparison', 'Verb', 'Pronoun', 'Modifier', 'Parallelism'],
-            [10, 12, 12, 13, 14, 15],
-            [10, 12, 12, 13, 14, 15],
+            data.place.map(row => {
+              return row.key;
+            }),
+            data.place.map(row => {
+              return formatPercent(row.userCorrect, row.userNumber);
+            }),
+            data.place.map(row => {
+              return row.userTime / row.userNumber;
+            }),
             ['#92AFD2', '#BFD4EE'],
             ['#989FC1', '#CCCCDC'],
           )}

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

@@ -11,8 +11,7 @@ import Button from '../../../components/Button';
 import { Question } from '../../../stores/question';
 import { Textbook } from '../../../stores/textbook';
 import Select from '../../../components/Select';
-
-const TextbookMinYear = 2019;
+import { TextbookMinYear } from '../../../../Constant';
 
 export default class extends Page {
   initState() {

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

@@ -97,6 +97,10 @@ export default class MainStore extends BaseStore {
     return this.apiGet('/base/exercise/parent', { id });
   }
 
+  getExerciseAll() {
+    return this.apiGet('/base/exercise/all');
+  }
+
   /**
    * 所有模考层级
    */

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

@@ -539,3 +539,32 @@ export function toHump(name) {
 export function toLine(name) {
   return name.replace(/([A-Z])/g, '_$1').toLowerCase();
 }
+
+export function timeRange(timerange) {
+  let startTime = null;
+  let endTime = null;
+  switch (timerange) {
+    case 'all':
+      break;
+    case 'week':
+      endTime = new Date();
+      startTime = new Date(endTime.getTime() - 86400000 * 7);
+      break;
+    case 'month':
+      endTime = new Date();
+      startTime = new Date();
+      startTime.setMonth(startTime.getMonth() - 1);
+      break;
+    case 'month3':
+      endTime = new Date();
+      startTime = new Date();
+      startTime.setMonth(startTime.getMonth() - 3);
+      break;
+    case 'today':
+    default:
+      startTime = new Date();
+      startTime.setHours(0, 0, 0, 0);
+      endTime = new Date(startTime.getTime() + 86400000);
+  }
+  return [startTime, endTime];
+}

+ 17 - 1
server/data/src/main/java/com/qxgmat/data/constants/enums/QuestionType.java

@@ -22,7 +22,8 @@ public enum QuestionType {
 
     final static public String message = "题目类型";
 
-    static private List<String> verbal = Arrays.asList(SC.key, CR.key); // 不包含rc,rc题都是直接查询
+    static private List<String> verbal = Arrays.asList(SC.key, CR.key, RC.key);
+    static private List<String> verbalSpecial = Arrays.asList(SC.key, CR.key); // 不包含rc,rc题都是直接查询
     static private List<String> quant = Arrays.asList(PS.key, DS.key);
     static private List<String> ir = Collections.singletonList(IR.key);
     static private List<String> awa = Collections.singletonList(AWA.key);
@@ -53,4 +54,19 @@ public enum QuestionType {
                 return null;
         }
     }
+
+    public static List<String> SpecialFromSubject(QuestionSubject subject){
+        switch (subject){
+            case VERBAL:
+                return verbalSpecial;
+            case QUANT:
+                return quant;
+            case IR:
+                return ir;
+            case AWA:
+                return awa;
+            default:
+                return null;
+        }
+    }
 }

+ 2 - 4
server/gateway-api/src/main/java/com/qxgmat/controller/api/BaseController.java

@@ -168,11 +168,9 @@ public class BaseController {
         return ResponseHelp.success(p);
     }
 
-    @RequestMapping(value = "/exercise/range", method = RequestMethod.GET)
+    @RequestMapping(value = "/exercise/all", method = RequestMethod.GET)
     @ApiOperation(value = "练习层级范围", httpMethod = "GET")
-    public Response<List<ExerciseStruct>> exerciseRange(
-            @RequestParam(required = true) Integer id,
-            HttpSession session) {
+    public Response<List<ExerciseStruct>> exerciseRange(HttpSession session) {
         List<ExerciseStruct> p = exerciseStructService.all();
 
         return ResponseHelp.success(p);

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

@@ -683,8 +683,8 @@ public class MyController {
     @RequestMapping(value = "/data", method = RequestMethod.GET)
     @ApiOperation(value = "获取做题数据", notes = "获取做题数据", httpMethod = "GET")
     public Response<Map<String, UserDataDto>> questionData(
-            @RequestParam(required = false) String module,
-            @RequestParam(required = false) String subject,
+            @RequestParam(required = true) String module,
+            @RequestParam(required = true) String subject,
             @RequestParam(required = false) Integer[] structIds,
             @RequestParam(required = false) String startTime,
             @RequestParam(required = false) String endTime
@@ -703,6 +703,7 @@ public class MyController {
         Map<String, UserDataDto> dtoMap = new HashMap<>();
         for(String questionType : questionTypes){
             UserDataDto dto = new UserDataDto();
+            dto.setQuestionType(questionType);
             JSONObject placeMap = new JSONObject();
             JSONObject difficultMap = new JSONObject();
 

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

@@ -3,6 +3,8 @@ package com.qxgmat.dto.response;
 import com.alibaba.fastjson.JSONArray;
 
 public class UserDataDto {
+    private String questionType;
+
     private Integer questionNumber;
 
     private Integer totalNumber;
@@ -122,4 +124,12 @@ public class UserDataDto {
     public void setUserQuestion(Integer userQuestion) {
         this.userQuestion = userQuestion;
     }
+
+    public String getQuestionType() {
+        return questionType;
+    }
+
+    public void setQuestionType(String questionType) {
+        this.questionType = questionType;
+    }
 }

+ 4 - 5
server/gateway-api/src/main/java/com/qxgmat/service/extend/ExaminationService.java

@@ -416,7 +416,7 @@ public class ExaminationService extends AbstractService {
         List<QuestionNoRelation> rcRelationList = questionNoService.listWithRelationByIds(rcQ);
         Integer rcLevel = computeNoLevel(rcRelationList);
         Integer targetLevel = 0;
-        Collection types = QuestionType.FromSubject(QuestionSubject.VERBAL);
+        Collection types = QuestionType.SpecialFromSubject(QuestionSubject.VERBAL);
         List<QuestionDifficultRelation> difficultList = questionNoService.allExaminationByType(structId, types);
         List<QuestionDifficultRelation> selectedList = null;
         do{
@@ -448,7 +448,7 @@ public class ExaminationService extends AbstractService {
         Integer[] rcQ = questionNoService.randomExaminationRc(structId, 3, ids);
         List<QuestionNoRelation> rcRelationList = questionNoService.listWithRelationByIds(rcQ);
         Integer rcLevel = computeNoLevel(rcRelationList);
-        Collection types = QuestionType.FromSubject(QuestionSubject.VERBAL);
+        Collection types = QuestionType.SpecialFromSubject(QuestionSubject.VERBAL);
         List<QuestionDifficultRelation> difficultList = questionNoService.allExaminationByType(structId, types);
         List<QuestionDifficultRelation> selectedList = new ArrayList<>(verbalPre - 3);
         Integer[] levels = generateLevel(level - rcLevel,verbalPre - 3);
@@ -483,7 +483,6 @@ public class ExaminationService extends AbstractService {
 
     /**
      * 初始化第一阶段题目
-     *  一篇4题的阅读压轴
      *   { "ids": [], "level": 0 }
      * @param structId
      * @return
@@ -491,7 +490,7 @@ public class ExaminationService extends AbstractService {
     public JSONObject initQuant(Integer structId){
         JSONObject info = new JSONObject();
         Integer targetLevel = 0;
-        Collection types = QuestionType.FromSubject(QuestionSubject.QUANT);
+        Collection types = QuestionType.SpecialFromSubject(QuestionSubject.QUANT);
         Integer number = quantBaseLevel[0];
         List<QuestionDifficultRelation> difficultList = questionNoService.allExaminationByType(structId, types);
         List<QuestionDifficultRelation> selectedList = null;
@@ -520,7 +519,7 @@ public class ExaminationService extends AbstractService {
      */
     public JSONObject generateQuant(Integer structId, Integer level, Map<String, Integer> typeNumbers, Collection ids, Integer step){
         JSONObject info = new JSONObject();
-        Collection types = QuestionType.FromSubject(QuestionSubject.QUANT);
+        Collection types = QuestionType.SpecialFromSubject(QuestionSubject.QUANT);
         Integer number = quantBaseLevel[step];
         List<QuestionDifficultRelation> difficultList = questionNoService.allExaminationByType(structId, types);
         List<QuestionDifficultRelation> selectedList = null;

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

@@ -793,7 +793,7 @@ public class QuestionFlowService {
         Integer subnumber =  number.getIntValue(stage);
         Integer surplus = totalNumber - subnumber;
         QuestionSubject subject = QuestionSubject.ValueOf(stage);
-        List<String> questionTypes = QuestionType.FromSubject(subject);
+        List<String> questionTypes = QuestionType.SpecialFromSubject(subject);
         List<UserQuestion> userQuestionList = userQuestionService.listByReportAndType(report.getUserId(), report.getId(), questionTypes);
         Collection ids = Transform.getIds(userQuestionList, UserQuestion.class, "questionNoId");
         // 根据设置出题

+ 6 - 4
server/gateway-api/src/main/java/com/qxgmat/service/inline/QuestionNoService.java

@@ -164,11 +164,13 @@ public class QuestionNoService extends AbstractService {
                 example.createCriteria()
                         .andEqualTo("module", module.key)
         );
-        Example.Criteria criteria = example.createCriteria();
-        for(Integer structId : structIds){
-            criteria.orCondition(String.format(formatSet, structId, "module_struct"));
+        if (structIds != null){
+            Example.Criteria criteria = example.createCriteria();
+            for(Integer structId : structIds){
+                criteria.orCondition(String.format(formatSet, structId, "module_struct"));
+            }
+            example.and(criteria);
         }
-        example.and(criteria);
         example.orderBy("no").asc();
         return select(questionNoMapper, example);
     }