page.js 49 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476
  1. import React from 'react';
  2. import { Link } from 'react-router-dom';
  3. import './index.less';
  4. import LineChart from '@src/components/LineChart';
  5. import BarChart from '@src/components/BarChart';
  6. import PieChart from '@src/components/PieChart';
  7. import Assets from '@src/components/Assets';
  8. import Page from '@src/containers/Page';
  9. import { formatDate, formatPercent, formatSeconds, formatMinute, formatSecond, formatMinuteSecond, getMap } from '@src/services/Tools';
  10. import { Icon, Tooltip } from 'antd';
  11. import { Question } from '../../../stores/question';
  12. import { Button } from '../../../components/Button';
  13. import Tabs from '../../../components/Tabs';
  14. import { Icon as GIcon } from '../../../components/Icon';
  15. import { QuestionNoteModal } from '../../../components/OtherModal';
  16. import { My } from '../../../stores/my';
  17. import {
  18. QuestionDifficult,
  19. ExaminationQuestionType,
  20. ExaminationSubject,
  21. } from '../../../../Constant';
  22. const QuestionDifficultMap = getMap(QuestionDifficult, 'value', 'label');
  23. const QuestionDifficultSort = getMap(QuestionDifficult, 'value', 'sort');
  24. function BarOption3(titles, source, data1, data2, color1, color2) {
  25. return {
  26. title: [
  27. {
  28. text: titles[0],
  29. textStyle: { fontSize: 24, fontWeight: 'bold', color: '#686872' },
  30. left: 100,
  31. top: 15,
  32. },
  33. {
  34. text: titles[1],
  35. textStyle: { fontSize: 24, fontWeight: 'bold', color: '#686872' },
  36. left: 195,
  37. top: 15,
  38. },
  39. {
  40. text: titles[2],
  41. textStyle: { fontSize: 24, fontWeight: 'bold', color: '#686872' },
  42. left: 695,
  43. top: 15,
  44. },
  45. ],
  46. grid: [{ width: 250, x: 200, bottom: 30 }, { width: 250, x: 700, bottom: 30 }],
  47. xAxis: [
  48. {
  49. gridIndex: 0,
  50. show: false,
  51. axisTick: { show: false },
  52. axisLine: { show: false },
  53. splitLine: { show: false },
  54. },
  55. {
  56. gridIndex: 1,
  57. show: false,
  58. axisTick: { show: false },
  59. axisLine: { show: false },
  60. splitLine: { show: false },
  61. },
  62. ],
  63. yAxis: [
  64. {
  65. gridIndex: 0,
  66. type: 'category',
  67. axisTick: { show: false },
  68. axisLine: { show: false },
  69. splitLine: { show: false },
  70. offset: 15,
  71. data: source,
  72. axisLabel: { color: '#686872', fontSize: 16 },
  73. },
  74. {
  75. gridIndex: 1,
  76. type: 'category',
  77. axisTick: { show: false },
  78. axisLine: { show: false },
  79. splitLine: { show: false },
  80. axisLabel: { show: false },
  81. },
  82. ],
  83. series: [
  84. {
  85. type: 'bar',
  86. xAxisIndex: 0,
  87. yAxisIndex: 0,
  88. barWidth: 30,
  89. data: data1.map((item, index) => ({
  90. value: item,
  91. itemStyle: { color: index % 2 ? color1[0] : color1[1] },
  92. label: {
  93. show: true,
  94. color: '#303036',
  95. align: 'right',
  96. position: [360, 5],
  97. fontSize: 16,
  98. formatter: item,
  99. },
  100. })),
  101. },
  102. {
  103. type: 'bar',
  104. xAxisIndex: 1,
  105. yAxisIndex: 1,
  106. barWidth: 30,
  107. data: data2.map((item, index) => ({
  108. value: item,
  109. itemStyle: { color: index % 2 ? color2[0] : color2[1] },
  110. label: {
  111. show: true,
  112. color: '#303036',
  113. align: 'right',
  114. fontSize: 16,
  115. position: [360, 5],
  116. formatter: item,
  117. },
  118. })),
  119. },
  120. ],
  121. };
  122. }
  123. function BarOption2(title, data, legend, color) {
  124. return {
  125. title: {
  126. text: title,
  127. textStyle: { fontSize: 24, fontWeight: 'bold', color: '#686872' },
  128. },
  129. tooltip: {
  130. trigger: 'axis',
  131. },
  132. legend: {
  133. show: legend.length > 1,
  134. data: legend,
  135. right: 20,
  136. },
  137. color,
  138. dataset: {
  139. source: [['type', ...legend], ...data],
  140. },
  141. grid: { left: 30, right: 30, height: 300 },
  142. xAxis: {
  143. type: 'category',
  144. axisLabel: { color: '#686872' },
  145. axisLine: { lineStyle: { color: '#D1D6DF' } },
  146. },
  147. yAxis: {
  148. type: 'value',
  149. min: 0,
  150. max: 100,
  151. axisLabel: { color: '#686872' },
  152. axisLine: { lineStyle: { color: '#D1D6DF' } },
  153. },
  154. series: legend.map(() => ({
  155. type: 'bar',
  156. barWidth: 40,
  157. })),
  158. };
  159. }
  160. function lineOption1(title, data, legend, color) {
  161. return {
  162. title: {
  163. text: title,
  164. textStyle: { fontSize: 24, fontWeight: 'bold', color: '#686872' },
  165. left: '0',
  166. },
  167. tooltip: {
  168. trigger: 'axis',
  169. },
  170. legend: {
  171. show: legend.length > 1,
  172. data: legend,
  173. right: 20,
  174. },
  175. color,
  176. grid: { left: 30, right: 30, height: 300 },
  177. xAxis: {
  178. type: 'category',
  179. axisLabel: { color: '#686872' },
  180. axisLine: { lineStyle: { color: '#D1D6DF' } },
  181. },
  182. yAxis: {
  183. type: 'value',
  184. min: 0,
  185. max: 100,
  186. axisLabel: { color: '#686872' },
  187. axisLine: { lineStyle: { color: '#D1D6DF' } },
  188. },
  189. dataset: {
  190. source: [['type', ...legend], ...data],
  191. },
  192. series: legend.map(() => ({
  193. type: 'line',
  194. smooth: true,
  195. symbol: 'circle',
  196. })),
  197. };
  198. }
  199. function barOption1(title, value, allValue, avgCorrect, avgIncorrent) {
  200. const xAxis1 = [
  201. {
  202. gridIndex: 0,
  203. type: 'category',
  204. axisTick: { show: false },
  205. axisLine: { lineStyle: { color: '#D1D6DF' } },
  206. splitLine: { show: false },
  207. },
  208. {
  209. gridIndex: 1,
  210. type: 'category',
  211. axisTick: { show: false },
  212. axisLine: { lineStyle: { color: '#D1D6DF' } },
  213. splitLine: { show: false },
  214. data: [
  215. {
  216. value: 'Avg Time\nCorrect',
  217. textStyle: { color: '#686872', fontWeight: '500', fontSize: 14, lineHeight: 20 },
  218. },
  219. {
  220. value: 'Avg Time\nIncorrect',
  221. textStyle: { color: '#686872', fontWeight: '500', fontSize: 14, lineHeight: 20 },
  222. },
  223. ],
  224. },
  225. ];
  226. const xAxis2 = {
  227. type: 'category',
  228. axisTick: { show: false },
  229. axisLine: { lineStyle: { color: '#D1D6DF' } },
  230. splitLine: { show: false },
  231. };
  232. const yAxis1 = [
  233. {
  234. gridIndex: 0,
  235. show: false,
  236. min: 0,
  237. max: 100,
  238. axisTick: { show: false },
  239. axisLine: { show: false },
  240. splitLine: { show: false },
  241. },
  242. {
  243. gridIndex: 1,
  244. show: false,
  245. min: 0,
  246. max: 100,
  247. axisTick: { show: false },
  248. axisLine: { show: false },
  249. splitLine: { show: false },
  250. },
  251. ];
  252. const yAxis2 = {
  253. show: false,
  254. min: 0,
  255. max: 100,
  256. axisTick: { show: false },
  257. axisLine: { show: false },
  258. splitLine: { show: false },
  259. };
  260. const data1 = {
  261. type: 'bar',
  262. barWidth: 50,
  263. xAxisIndex: 0,
  264. yAxisIndex: 0,
  265. data: [
  266. {
  267. value,
  268. itemStyle: { color: '#7AA7DC' },
  269. label: {
  270. show: true,
  271. position: 'top',
  272. formatter: `{a|${value}}`,
  273. rich: { a: { fontSize: 16, fontWeight: 'bold', color: '#686872' } },
  274. },
  275. },
  276. {
  277. value: allValue,
  278. itemStyle: { color: '#598FCF' },
  279. label: {
  280. show: true,
  281. position: 'top',
  282. formatter: `{a|${allValue}}`,
  283. rich: { a: { fontSize: 12, color: '#686872' } },
  284. },
  285. },
  286. ],
  287. };
  288. const data2 = {
  289. type: 'bar',
  290. barWidth: 50,
  291. xAxisIndex: 1,
  292. yAxisIndex: 1,
  293. data: [
  294. {
  295. value: avgCorrect,
  296. name: 'Avg Time\nCorrect',
  297. itemStyle: { color: '#7775CA' },
  298. label: {
  299. show: true,
  300. position: 'top',
  301. formatter: `{a|${avgCorrect}}`,
  302. rich: { a: { fontSize: 16, fontWeight: 'bold', color: '#686872' } },
  303. },
  304. },
  305. {
  306. value: avgIncorrent,
  307. name: 'Avg Time\nIncorrect',
  308. itemStyle: { color: '#9396C9' },
  309. label: {
  310. show: true,
  311. position: 'top',
  312. formatter: `{a|${avgIncorrent}}`,
  313. rich: { a: { fontSize: 16, fontWeight: 'bold', color: '#686872' } },
  314. },
  315. },
  316. ],
  317. };
  318. return {
  319. title: [
  320. {
  321. text: title,
  322. textStyle: { fontSize: 24, fontWeight: 'bold', color: '#686872' },
  323. left: '0',
  324. },
  325. {
  326. text: 'Avg Time\nTotal',
  327. textAlign: 'center',
  328. textVerticalAlign: 'middle',
  329. textStyle: { color: '#686872', fontWeight: '500', fontSize: 14, lineHeight: 20 },
  330. bottom: '2%',
  331. left: '26%',
  332. },
  333. ],
  334. xAxis: avgCorrect ? xAxis1 : xAxis2,
  335. yAxis: avgCorrect ? yAxis1 : yAxis2,
  336. grid: avgCorrect ? [{ width: 200, x: 72 }, { width: 350, x: 272 }] : { width: 200, left: '30%' },
  337. series: avgCorrect ? [data1, data2] : [data1],
  338. };
  339. }
  340. function pieOption1(title, userCorrect, userNumber, totalCorrect, totalNumber) {
  341. const value = formatPercent(userCorrect, userNumber);
  342. const allValue = formatPercent(totalCorrect, totalNumber);
  343. return {
  344. title: [
  345. {
  346. text: title,
  347. textStyle: { fontSize: 24, fontWeight: 'bold', color: '#686872' },
  348. left: '0',
  349. },
  350. {
  351. text: `${value}%`,
  352. textAlign: 'center',
  353. textVerticalAlign: 'middle',
  354. textStyle: { color: value < 50 ? '#f19057' : '#7AA7DC', fontSize: 45 },
  355. subtext: `${userCorrect}/${userNumber}`,
  356. subtextStyle: { color: '#686872', fontSize: 16 },
  357. top: '35%',
  358. left: '49%',
  359. },
  360. ],
  361. series: [
  362. {
  363. type: 'pie',
  364. radius: ['64%', '70%'],
  365. label: {
  366. show: false,
  367. },
  368. hoverAnimation: false,
  369. animation: false,
  370. data: [
  371. {
  372. value: allValue,
  373. itemStyle: { color: '#7775CA' },
  374. label: {
  375. show: true,
  376. position: 'outside',
  377. formatter: `{a|全站用户}:{b|${allValue}%}`,
  378. rich: {
  379. a: {
  380. color: '#686872',
  381. fontSize: 16,
  382. },
  383. b: {
  384. color: '#6865FD',
  385. fontSize: 16,
  386. },
  387. },
  388. },
  389. },
  390. {
  391. value: 100 - allValue,
  392. itemStyle: { color: '#e1e1e1' },
  393. emphasis: { itemStyle: { color: '#e1e1e1' } },
  394. },
  395. ],
  396. },
  397. {
  398. type: 'pie',
  399. radius: ['45%', '61%'],
  400. label: {
  401. show: false,
  402. },
  403. hoverAnimation: false,
  404. animation: false,
  405. data: [
  406. { value, itemStyle: { color: value < 50 ? '#f19057' : '#7AA7DC' } },
  407. { value: 100 - value, itemStyle: { color: '#e1e1e1' }, emphasis: { itemStyle: { color: '#e1e1e1' } } },
  408. ],
  409. },
  410. ],
  411. };
  412. }
  413. function pieOption2(title, value1, value2, value3, total) {
  414. return {
  415. title: [
  416. {
  417. text: title,
  418. textStyle: { fontSize: 24, fontWeight: 'bold', color: '#686872' },
  419. left: '0',
  420. },
  421. {
  422. text: `${total}`,
  423. textAlign: 'center',
  424. textVerticalAlign: 'middle',
  425. textStyle: { color: '#686872', fontSize: 60 },
  426. subtext: '综合实力',
  427. subtextStyle: { color: '#686872', fontSize: 14 },
  428. top: '35%',
  429. left: '49.5%',
  430. },
  431. ],
  432. series: [
  433. {
  434. type: 'pie',
  435. radius: ['50%', '80%'],
  436. startAngle: 30,
  437. hoverAnimation: false,
  438. animation: false,
  439. data: [
  440. {
  441. value: value1,
  442. itemStyle: { color: '#6865FD' },
  443. label: {
  444. position: 'outside',
  445. formatter: `{a|逻辑关系} {b|${value1}}`,
  446. rich: {
  447. a: {
  448. color: '#686872',
  449. fontSize: 18,
  450. },
  451. b: {
  452. color: '#6865FD',
  453. fontSize: 18,
  454. },
  455. },
  456. },
  457. },
  458. {
  459. value: value2,
  460. itemStyle: { color: '#6EC28D' },
  461. label: {
  462. position: 'outside',
  463. formatter: `{a|句子结构} {b|${value2}}`,
  464. rich: {
  465. a: {
  466. color: '#686872',
  467. fontSize: 18,
  468. },
  469. b: {
  470. color: '#6EC28D',
  471. fontSize: 18,
  472. },
  473. },
  474. },
  475. },
  476. {
  477. value: value3,
  478. itemStyle: { color: '#598FCF' },
  479. label: {
  480. position: 'outside',
  481. formatter: `{a|阅读速度} {b|${value3}}`,
  482. rich: {
  483. a: {
  484. color: '#686872',
  485. fontSize: 18,
  486. },
  487. b: {
  488. color: '#598FCF',
  489. fontSize: 18,
  490. },
  491. },
  492. },
  493. },
  494. ],
  495. },
  496. ],
  497. };
  498. }
  499. export default class extends Page {
  500. initState() {
  501. return { tab: 'main', report: { paperModule: '' } };
  502. }
  503. initData() {
  504. const { id } = this.params;
  505. const { info = '' } = this.state.search;
  506. Question.detailReport(id).then(result => {
  507. switch (result.paperModule) {
  508. case 'sentence':
  509. this.refreshSentence(result);
  510. break;
  511. case 'textbook':
  512. this.refreshTextbook(result);
  513. break;
  514. case 'exercise':
  515. this.refreshExercise(result);
  516. break;
  517. case 'examination':
  518. this.refreshExamination(result);
  519. break;
  520. default:
  521. }
  522. this.setState({ report: result, paper: result.paper });
  523. });
  524. switch (info) {
  525. case 'question':
  526. // 题目回顾列表
  527. Question.questionReport(id).then(result => {
  528. switch (result.paperModule) {
  529. case 'sentence':
  530. result = result.map((row) => {
  531. row.struct = row.detail.subject && row.detail.predicate && row.detail.object ? 0 : 1;
  532. row.logic = row.detail.options ? 0 : 1;
  533. row.note = row.note ? 1 : 0;
  534. row.collect = row.collect ? 1 : 0;
  535. return row;
  536. });
  537. break;
  538. case 'textbook':
  539. case 'exercise':
  540. result = result.map((row) => {
  541. row.correct = row.isCorrect ? 0 : 1;
  542. row.diff = QuestionDifficultSort[row.question.difficult];
  543. row.note = row.note ? 1 : 0;
  544. row.collect = row.collect ? 1 : 0;
  545. return row;
  546. });
  547. this.refreshExercise(result);
  548. break;
  549. default:
  550. }
  551. this.setState({ list: result });
  552. });
  553. break;
  554. default:
  555. break;
  556. }
  557. }
  558. refreshSentence() {
  559. const { info = '' } = this.state.search;
  560. switch (info) {
  561. case 'question':
  562. break;
  563. default:
  564. }
  565. }
  566. refreshTextbook() {
  567. this.refreshExercise();
  568. }
  569. refreshExamination() {
  570. const { info = '' } = this.state.search;
  571. switch (info) {
  572. case 'score':
  573. break;
  574. default:
  575. }
  576. }
  577. refreshExercise() {
  578. const { info = '' } = this.state.search;
  579. switch (info) {
  580. case 'question':
  581. break;
  582. default:
  583. }
  584. }
  585. questionSort(field) {
  586. let { order } = this.state;
  587. const { list = [] } = this.state;
  588. if (order === field) {
  589. order = 'no';
  590. // direction = 'asc';
  591. } else {
  592. order = field;
  593. // direction = 'desc';
  594. }
  595. list.sort((a, b) => {
  596. const aValue = a[order];
  597. const bValue = a[order];
  598. if (aValue === bValue) {
  599. return a.no < b.no ? -1 : a.no > b.no ? 1 : 0;
  600. }
  601. return aValue > bValue ? -1 : 1;
  602. });
  603. // if (direction === 'desc') {
  604. // list.reverse();
  605. // }
  606. this.setState({ order, list });
  607. }
  608. toggleCollect(index) {
  609. const { list } = this.state;
  610. const userQuestion = list[index];
  611. if (!userQuestion.collect) {
  612. My.addQuestionCollect(userQuestion.questionNo.id).then(() => {
  613. userQuestion.collect = true;
  614. this.setState({ list });
  615. });
  616. } else {
  617. My.delQuestionCollect(userQuestion.questionNo.id).then(() => {
  618. userQuestion.collect = false;
  619. this.setState({ list });
  620. });
  621. }
  622. }
  623. note(index) {
  624. const { list } = this.state;
  625. const userQuestion = list[index];
  626. const { questionNo } = userQuestion;
  627. My.getQuestionNote(questionNo.id)
  628. .then(result => {
  629. this.setState({ questionNo, note: result || {}, showNote: true, index });
  630. });
  631. }
  632. renderView() {
  633. const { report = {}, search = {} } = this.state;
  634. const { info } = search;
  635. switch (report.paperModule) {
  636. case 'sentence':
  637. if (info === 'question') {
  638. return this.renderSentenceQuestion();
  639. }
  640. return this.renderSentence();
  641. case 'textbook':
  642. if (info === 'question') {
  643. return this.renderExerciseQuestion();
  644. }
  645. return this.renderTextbook();
  646. case 'exercise':
  647. if (info === 'question') {
  648. return this.renderExerciseQuestion();
  649. }
  650. return this.renderExercise();
  651. case 'examination':
  652. return this.renderExamination();
  653. default:
  654. return <div />;
  655. }
  656. }
  657. renderSentence() {
  658. const { paper = {}, report = {}, search = {} } = this.state;
  659. const { info } = search;
  660. const { user } = this.props;
  661. return (
  662. <div className="sentence">
  663. <div className="header">
  664. <div className="content">
  665. <div className="title">Report for 「{report.paperModule === 'examination' ? '模考' : '练习'}」{paper.title}</div>
  666. <div className="btns">
  667. <Button size="small" radius onClick={() => {
  668. linkTo('/');
  669. }}>返回首页</Button>
  670. {!info && <Button size="small" radius onClick={() => {
  671. linkTo(`/paper/report/${report.id}?info=question`);
  672. }}>题目回顾</Button>}
  673. {info === 'question' && <Button size="small" radius onClick={() => {
  674. linkTo(`/paper/report/${report.id}`);
  675. }}>详细报告</Button>}
  676. </div>
  677. <div className="right">
  678. <div className="text">{user.info.nickname}</div>
  679. <div className="desc">练习次数{paper.times + 1}</div>
  680. <div className="desc">{formatDate(report.updateTime, 'YYYY-MM-DD HH:mm:ss')}</div>
  681. </div>
  682. </div>
  683. </div>
  684. {info === 'question' && this.renderSentenceQuestion()}
  685. {!info && this.renderSentenceDetail()}
  686. </div>
  687. );
  688. }
  689. renderTextbook() {
  690. return this.renderExercise();
  691. }
  692. renderExercise() {
  693. const { paper = {}, report = {}, search = {} } = this.state;
  694. const { info } = search;
  695. const { user } = this.props;
  696. return (
  697. <div className="exercise">
  698. <div className="header">
  699. <div className="content">
  700. <div className="title">Report for「练习」{paper.title}</div>
  701. <div className="btns">
  702. <Button size="small" radius onClick={() => {
  703. linkTo('/');
  704. }}>返回首页</Button>
  705. {!info && <Button size="small" radius onClick={() => {
  706. linkTo(`/paper/report/${report.id}?info=question`);
  707. }}>题目回顾</Button>}
  708. {info === 'question' && <Button size="small" radius onClick={() => {
  709. linkTo(`/paper/report/${report.id}`);
  710. }}>详细报告</Button>}
  711. </div>
  712. <div className="right">
  713. <div className="text">{user.info.nickname}</div>
  714. <div className="desc">练习次数{paper.times + 1}</div>
  715. <div className="desc">{formatDate(report.updateTime, 'YYYY-MM-DD HH:mm:ss')}</div>
  716. </div>
  717. </div>
  718. </div>
  719. {info === 'question' && this.renderExerciseQuestion()}
  720. {!info && this.renderExerciseDetail()}
  721. </div>
  722. );
  723. }
  724. renderExamination() {
  725. const { paper = {}, report = {}, search = {} } = this.state;
  726. const { info } = search;
  727. const { user } = this.props;
  728. return (
  729. <div className="examination">
  730. <div className="header">
  731. <div className="content">
  732. <div className="title">Report for 「{report.paperModule === 'examination' ? '模考' : '练习'}」{paper.title}</div>
  733. <div className="btns">
  734. <Button size="small" radius onClick={() => {
  735. linkTo('/');
  736. }}>返回首页</Button>
  737. {!info && <Button size="small" radius onClick={() => {
  738. linkTo(`/paper/report/${report.id}?info=score`);
  739. }}>成绩单</Button>}
  740. {info === 'score' && <Button size="small" radius onClick={() => {
  741. linkTo(`/paper/report/${report.id}`);
  742. }}>详细报告</Button>}
  743. </div>
  744. <div className="right">
  745. <div className="text">{user.info.nickname}</div>
  746. <div className="desc">练习次数{paper.times + 1}</div>
  747. <div className="desc">{formatDate(report.updateTime, 'YYYY-MM-DD HH:mm:ss')}</div>
  748. </div>
  749. </div>
  750. </div>
  751. {info === 'score' && this.renderExaminationScore()}
  752. {!info && this.renderExaminationDetail()}
  753. </div>
  754. );
  755. }
  756. renderSentenceQuestion() {
  757. const { report, list, order, showNote, note = {}, questionNo = {} } = this.state;
  758. return <div className='sentence question'>
  759. <div className='header'>
  760. <div className='content'>
  761. <div className='title'>题目回顾</div>
  762. <Button className='back' radius onClick={() => {
  763. linkTo(`/paper/report/${report.id}`);
  764. }}>返回长难句报告</Button>
  765. </div>
  766. </div>
  767. <div className='body'>
  768. <div className='content'>
  769. <div className='tip'><Assets name='notice' />点击题目查看详情</div>
  770. <table>
  771. <thead>
  772. <tr>
  773. <th>序号</th>
  774. <th width='420'>题目</th>
  775. <th className="point" onClick={() => {
  776. this.questionSort('struct');
  777. }}>句子结构<GIcon name={order === 'struct' ? 'arrow-down' : 'arrow-up'} active={order === 'struct'} /></th>
  778. <th className="point" onClick={() => {
  779. this.questionSort('logic');
  780. }}>逻辑关系<GIcon name={order === 'logic' ? 'arrow-down' : 'arrow-up'} active={order === 'logic'} /></th>
  781. <th className="point" onClick={() => {
  782. this.questionSort('userTime');
  783. }}>用时<GIcon name={order === 'userTime' ? 'arrow-down' : 'arrow-up'} active={order === 'userTime'} /></th>
  784. <th className="point" onClick={() => {
  785. this.questionSort('collect');
  786. }}>收藏<GIcon name={order === 'collect' ? 'arrow-down' : 'arrow-up'} active={order === 'collect'} /></th>
  787. <th className="point" onClick={() => {
  788. this.questionSort('note');
  789. }}>笔记<GIcon name={order === 'note' ? 'arrow-down' : 'arrow-up'} active={order === 'note'} /></th>
  790. </tr>
  791. </thead>
  792. <tbody>
  793. {(list || []).map((row, index) => {
  794. return <tr>
  795. <td>{row.no}</td>
  796. <td>
  797. <div className='n'><Link to={`/paper/question/${row.id}`}>{row.questionNo.title}</Link></div>
  798. <div className='desc'>{row.question.description}</div>
  799. </td>
  800. <td><GIcon name={row.detail.subject && row.detail.predicate && row.detail.object ? 'right' : 'error'} noHover /></td>
  801. <td><GIcon name={row.detail.options ? 'right' : 'error'} noHover /></td>
  802. <td>{formatMinuteSecond(row.userTime)}</td>
  803. <td><GIcon name='star' active={row.collect} onClick={() => this.toggleCollect(index)} /></td>
  804. <td><GIcon name='note' active={row.note} onClick={() => this.note(index)} /></td>
  805. </tr>;
  806. })}
  807. </tbody>
  808. </table>
  809. </div>
  810. </div>
  811. <QuestionNoteModal show={showNote} defaultData={note} questionNo={questionNo} onConfirm={() => {
  812. list[this.state.index].note = true;
  813. this.setState({ showNote: false, list });
  814. }} onCancel={() => this.setState({ showNote: false })} />
  815. </div>;
  816. }
  817. renderSentenceDetail() {
  818. const { report = {} } = this.state;
  819. const { detail = {} } = report;
  820. return <div>
  821. <div className="body">
  822. <div className="content">
  823. <div className="title">完成情况</div>
  824. <div className="detail">
  825. <div className="block">
  826. <div className="t1">总耗时</div>
  827. <div className="t2">{formatMinute(detail.info.userTime, true)}</div>
  828. <div className="t3">min</div>
  829. </div>
  830. {detail.info.userTime > detail.info.time && <div className="block">
  831. <div className="t1">超出建议用时</div>
  832. <div className="t2">{formatMinute(detail.info.userTime - detail.info.time, true)}</div>
  833. <div className="t3">min</div>
  834. </div>}
  835. <div className="line" />
  836. <div className="block">
  837. <div className="t1">完成题目</div>
  838. <div className="t2">{detail.info.userNumber}</div>
  839. <div className="t3">题</div>
  840. </div>
  841. {detail.info.userNumber !== detail.info.questionNumber && <div className="block">
  842. <div className="t1">剩余未做</div>
  843. <div className="t2">{detail.info.questionNumber || 0 - detail.info.userNumber}</div>
  844. <div className="t3">题</div>
  845. </div>}
  846. </div>
  847. </div>
  848. </div>
  849. <div className="body gray">
  850. <div className="content">
  851. <div className="title">基本情况</div>
  852. <div className="block-wrapper">
  853. <div className="block">
  854. <PieChart option={pieOption1('正确率', detail.info.userCorrect, detail.info.userNumber, detail.info.totalCorrect, detail.info.totalNumber)} />
  855. </div>
  856. <div className="block">
  857. <BarChart option={barOption1('用时', detail.info.userTime / detail.info.userNumber, detail.info.totalTime / detail.info.totalNumber, detail.info.correctTime, detail.info.incorrectTime)} />
  858. </div>
  859. </div>
  860. </div>
  861. </div>
  862. <div className="body">
  863. <div className="content">
  864. <div className="title">能力评估</div>
  865. <PieChart option={pieOption2('综合得分', detail.ability.logic, detail.ability.struct, detail.ability.speed, detail.ability.score)} />
  866. </div>
  867. </div>
  868. <div className="body gray">
  869. <div className="content t-c">
  870. <Button size="lager" width={200} radius onClick={() => linkTo('/exercise?tab=sentence')}>
  871. 继续做题
  872. </Button>
  873. </div>
  874. </div>
  875. </div>;
  876. }
  877. renderExerciseQuestion() {
  878. const { report, list, order, showNote, note = {}, questionNo = {} } = this.state;
  879. return <div className='sentence question'>
  880. <div className='header'>
  881. <div className='content'>
  882. <div className='title'>题目回顾</div>
  883. <Button className='back' radius onClick={() => {
  884. linkTo(`/paper/report/${report.id}`);
  885. }}>返回练习报告</Button>
  886. </div>
  887. </div>
  888. <div className='body'>
  889. <div className='content'>
  890. <div className='tip'><Assets name='notice' />点击题目查看详情</div>
  891. <table>
  892. <thead>
  893. <tr>
  894. <th>序号</th>
  895. <th width='340'>题目</th>
  896. <th className="point" onClick={() => {
  897. this.questionSort('correct');
  898. }}>正误<GIcon name={order === 'correct' ? 'arrow-down' : 'arrow-up'} active={order === 'correct'} /></th>
  899. <th className="point" onClick={() => {
  900. this.questionSort('diff');
  901. }}>难度<GIcon name={order === 'diff' ? 'arrow-down' : 'arrow-up'} active={order === 'diff'} /></th>
  902. <th className="point" onClick={() => {
  903. this.questionSort('userTime');
  904. }}>用时<GIcon name={order === 'userTime' ? 'arrow-down' : 'arrow-up'} active={order === 'userTime'} /></th>
  905. <th className="point" onClick={() => {
  906. this.questionSort('place');
  907. }}>主要考点<GIcon name={order === 'place' ? 'arrow-down' : 'arrow-up'} active={order === 'place'} /></th>
  908. <th className="point" onClick={() => {
  909. this.questionSort('collect');
  910. }}>收藏<GIcon name={order === 'collect' ? 'arrow-down' : 'arrow-up'} active={order === 'collect'} /></th>
  911. <th className="point" onClick={() => {
  912. this.questionSort('note');
  913. }}>笔记<GIcon name={order === 'note' ? 'arrow-down' : 'arrow-up'} active={order === 'note'} /></th>
  914. </tr>
  915. </thead>
  916. <tbody>
  917. {(list || []).map((row, index) => {
  918. return <tr>
  919. <td>{row.no}</td>
  920. <td>
  921. <div className='n'><Link to={`/paper/question/${row.id}`}>{row.questionNo.title}</Link></div>
  922. <div className='desc'>{row.question.description}</div>
  923. </td>
  924. <td><GIcon name={row.isCorrect ? 'right' : 'error'} noHover /></td>
  925. <td>{row.question.difficult}</td>
  926. <td>{formatMinuteSecond(row.userTime)}</td>
  927. <td>{row.question.place}</td>
  928. <td><GIcon name='star' active={row.collect} onClick={() => this.toggleCollect(index)} /></td>
  929. <td><GIcon name='note' active={row.note} onClick={() => this.note(index)} /></td>
  930. </tr>;
  931. })}
  932. </tbody>
  933. </table>
  934. </div>
  935. </div>
  936. <QuestionNoteModal show={showNote} defaultData={note} questionNo={questionNo} onConfirm={() => {
  937. list[this.state.index].note = true;
  938. this.setState({ showNote: false, list });
  939. }} onCancel={() => this.setState({ showNote: false })} />
  940. </div>;
  941. }
  942. renderExerciseDetail() {
  943. const { report = {} } = this.state;
  944. let { detail = {} } = report;
  945. detail = detail || {};
  946. const { pace = [], info = {}, difficult = [], place = [], limit = {} } = detail;
  947. return <div>
  948. <div className="body">
  949. <div className="content">
  950. <div className="title">完成情况</div>
  951. <div className="detail">
  952. <div className="block">
  953. <div className="t1">总耗时</div>
  954. <div className="t2">{formatMinute(info.userTime, true)}</div>
  955. <div className="t3">min</div>
  956. </div>
  957. {info.userTime > info.time && <div className="block">
  958. <div className="t1">超出建议用时</div>
  959. <div className="t2">{formatMinute(info.userTime - info.time, true)}</div>
  960. <div className="t3">min</div>
  961. </div>}
  962. <div className="line" />
  963. <div className="block">
  964. <div className="t1">完成题目</div>
  965. <div className="t2">{info.userNumber}</div>
  966. <div className="t3">题</div>
  967. </div>
  968. {info.userNumber !== info.questionNumber && <div className="block">
  969. <div className="t1">剩余未做</div>
  970. <div className="t2">{info.questionNumber - info.userNumber}</div>
  971. <div className="t3">题</div>
  972. </div>}
  973. </div>
  974. </div>
  975. </div>
  976. <div className="body gray">
  977. <div className="content">
  978. <div className="title">基本情况</div>
  979. <div className="block-wrapper">
  980. <div className="block">
  981. <PieChart option={pieOption1('正确率', info.userCorrect, info.userNumber, info.totalCorrect, info.totalNumber)} />
  982. </div>
  983. <div className="block">
  984. <BarChart option={barOption1('用时', info.userTime / info.userNumber, info.totalTime / info.totalNumber, info.correctTime, info.incorrectTime)} />
  985. </div>
  986. </div>
  987. </div>
  988. </div>
  989. <div className="body">
  990. <div className="content">
  991. <div className="title">PACE</div>
  992. <div className="detail-1">
  993. <div className="block">
  994. <div className="t1">平均用时</div>
  995. <div className="t2" dangerouslySetInnerHTML={{ __html: formatSeconds(info.userTime / info.userNumber).replace(/([0-9]+)(m|min|h|hour|s)/g, '$1<div class="t3">$2</div>') }} />
  996. </div>
  997. <div className="block all">
  998. <div className="t1">全站用户</div>
  999. <div className="t2" dangerouslySetInnerHTML={{ __html: formatSeconds(info.totalTime / info.totalNumber).replace(/([0-9]+)(m|min|h|hour|s)/g, '$1<div class="t3">$2</div>') }} />
  1000. </div>
  1001. </div>
  1002. <LineChart
  1003. height={400}
  1004. option={lineOption1(
  1005. '每题用时情况',
  1006. pace.map(row => {
  1007. return [`${row.no}`, row.userTime, row.time];
  1008. }),
  1009. ['我的', '全站'],
  1010. ['#7AA7DC', '#8684df'],
  1011. )}
  1012. />
  1013. </div>
  1014. </div>
  1015. {report.paperModule !== 'textbook' && <div className="body gray">
  1016. <div className="content">
  1017. <div className="title">难度分析</div>
  1018. <div className="detail-1">
  1019. <div className="block">
  1020. <div className="t1">正确率</div>
  1021. <div className="t2">{formatPercent(info.userCorrect, info.userNumber, false)}</div>
  1022. </div>
  1023. <div className="block all">
  1024. <div className="t1">全站用户</div>
  1025. <div className="t2">{formatPercent(info.totalCorrect, info.totalNumber, false)}</div>
  1026. </div>
  1027. </div>
  1028. <BarChart
  1029. height={400}
  1030. option={BarOption2(
  1031. '正确率',
  1032. difficult.map(row => {
  1033. return [QuestionDifficultMap[row.key], formatPercent(row.userCorrect, row.userNumber), formatPercent(row.totalCorrect, row.totalNumber)];
  1034. }),
  1035. ['我的', '全站'],
  1036. ['#8684df', '#5195e5'],
  1037. )}
  1038. />
  1039. </div>
  1040. </div>}
  1041. <div className="body">
  1042. <div className="content">
  1043. <div className="title">知识体系分析</div>
  1044. <div className="detail-1">
  1045. <div className="block">
  1046. <div className="t1">平均用时</div>
  1047. <div className="t2" dangerouslySetInnerHTML={{ __html: formatSeconds(info.userTime / info.userNumber).replace(/([0-9]+)(m|min|h|hour|s)/g, '$1<div class="t3">$2</div>') }} />
  1048. </div>
  1049. <div className="block all">
  1050. <div className="t1">正确率</div>
  1051. <div className="t2">{formatPercent(info.userCorrect, info.userNumber, false)}</div>
  1052. </div>
  1053. </div>
  1054. <BarChart
  1055. height={400}
  1056. option={BarOption3(
  1057. ['知识点', '正确率分析', '用时分析'],
  1058. place.map(row => {
  1059. return row.key;
  1060. }),
  1061. place.map(row => {
  1062. return formatPercent(row.userCorrect, row.userNumber);
  1063. }),
  1064. place.map(row => {
  1065. return row.userTime / row.userNumber;
  1066. }),
  1067. ['#7AA7DC', '#BFD4EE'],
  1068. ['#8684df', '#C3C3E5'],
  1069. )}
  1070. />
  1071. </div>
  1072. </div>
  1073. <div className="body gray">
  1074. <div className="content">
  1075. <div className="title">实战提醒</div>
  1076. {info.userTime > info.time && <div className="tip">
  1077. <div className="t1">在实战限时情况下,本套题正确率为 </div>
  1078. <div className="t2">{formatPercent(limit.userCorrect, limit.userNumber)}</div>
  1079. <Tooltip title="仅统计在建议时间内完成题目正确率情况,更接近实战表现"><Icon type="question-circle" theme="filled" /></Tooltip>
  1080. </div>}
  1081. {info.userTime <= info.time && <div className="tip">
  1082. <div className="t1">目前的做题速度已达到实战标准!很棒!请继续保持!</div>
  1083. </div>}
  1084. </div>
  1085. </div>
  1086. <div className="body">
  1087. <div className="content t-c">
  1088. <Button size="lager" width={200} radius onClick={() => linkTo('/exercise')}>
  1089. 继续做题
  1090. </Button>
  1091. </div>
  1092. </div>
  1093. </div>;
  1094. }
  1095. renderTextbookQuestion() {
  1096. return this.renderExerciseQuestion();
  1097. }
  1098. renderExaminationDetail() {
  1099. const { report = {}, tab } = this.state;
  1100. const { detail = {} } = report;
  1101. const { subject = {} } = detail;
  1102. const subjectDetail = tab === 'main' ? null : (subject || {})[tab] || { info: {}, defficlt: [], place: [], pace: [] };
  1103. return <div>
  1104. <div className="body">
  1105. <div className="content">
  1106. <Tabs
  1107. type="division"
  1108. theme="gray"
  1109. active={tab}
  1110. space={7}
  1111. tabs={[
  1112. { key: 'main', name: '总览 Overview' },
  1113. { key: 'verbal', name: '语文 Verbal' },
  1114. { key: 'quant', name: '数学 Quant' },
  1115. { key: 'ir', name: '综合推理 IR' },
  1116. ]}
  1117. onChange={(value) => {
  1118. this.setState({ tab: value });
  1119. }}
  1120. />
  1121. {!subjectDetail && <div className="list">
  1122. <div className="title">完成情况</div>
  1123. <div className="detail">
  1124. <div className="block">
  1125. <div className="t1" />
  1126. <div className="t4">Verbal</div>
  1127. <div className="t4">Quant</div>
  1128. <div className="t4">IR</div>
  1129. </div>
  1130. <div className="block">
  1131. <div className="t1">总耗时</div>
  1132. <div className="t1">
  1133. <div className="t2">{formatMinute((subject.verbal || {}).userTime, true)}</div>
  1134. <div className="t3">min</div>
  1135. </div>
  1136. <div className="t1">
  1137. <div className="t2">{formatMinute((subject.quant || {}).userTime, true)}</div>
  1138. <div className="t3">min</div>
  1139. </div>
  1140. <div className="t1">
  1141. <div className="t2">{formatMinute((subject.ir || {}).userTime, true)}</div>
  1142. <div className="t3">min</div>
  1143. </div>
  1144. </div>
  1145. <div className="block">
  1146. <div className="t1">超出建议用时</div>
  1147. <div className="t1">
  1148. <div className="t2">{formatMinute((subject.verbal || {}).userTime - (subject.verbal || {}).time, true)}</div>
  1149. <div className="t3">min</div>
  1150. </div>
  1151. <div className="t1">
  1152. <div className="t2">{formatMinute((subject.quant || {}).userTime - (subject.quant || {}).time, true)}</div>
  1153. <div className="t3">min</div>
  1154. </div>
  1155. <div className="t1">
  1156. <div className="t2">{formatMinute((subject.ir || {}).userTime - (subject.ir || {}).time, true)}</div>
  1157. <div className="t3">min</div>
  1158. </div>
  1159. </div>
  1160. <div className="block">
  1161. <div className="t1" />
  1162. <div className="t1">
  1163. <div className="line" />
  1164. </div>
  1165. <div className="t1">
  1166. <div className="line" />
  1167. </div>
  1168. <div className="t1">
  1169. <div className="line" />
  1170. </div>
  1171. </div>
  1172. <div className="block">
  1173. <div className="t1">完成题目</div>
  1174. <div className="t1">
  1175. <div className="t2">{(subject.verbal || {}).userNumber}</div>
  1176. <div className="t3">题</div>
  1177. </div>
  1178. <div className="t1">
  1179. <div className="t2">{(subject.quant || {}).userNumber}</div>
  1180. <div className="t3">题</div>
  1181. </div>
  1182. <div className="t1">
  1183. <div className="t2">{(subject.ir || {}).userNumber}</div>
  1184. <div className="t3">题</div>
  1185. </div>
  1186. </div>
  1187. <div className="block">
  1188. <div className="t1">剩余未做</div>
  1189. <div className="t1">
  1190. <div className="t2">{(subject.verbal || {}).questionNumber - (subject.verbal || {}).userNumber}</div>
  1191. <div className="t3">题</div>
  1192. </div>
  1193. <div className="t1">
  1194. <div className="t2">{(subject.quant || {}).questionNumber - (subject.quant || {}).userNumber}</div>
  1195. <div className="t3">题</div>
  1196. </div>
  1197. <div className="t1">
  1198. <div className="t2">{(subject.ir || {}).questionNumber - (subject.ir || {}).userNumber}</div>
  1199. <div className="t3">题</div>
  1200. </div>
  1201. </div>
  1202. </div></div>}
  1203. {subjectDetail && <div>
  1204. <div className="title">PACE</div>
  1205. <div className="detail-1">
  1206. <div className="block">
  1207. <div className="t1">平均用时</div>
  1208. <div className="t2" dangerouslySetInnerHTML={{ __html: formatSeconds(subjectDetail.info.userTime / subjectDetail.info.userNumber).replace(/([0-9]+)(m|min|h|hour|s)/g, '$1<div class="t3">$2</div>') }} />
  1209. </div>
  1210. </div>
  1211. <LineChart
  1212. height={400}
  1213. option={lineOption1(
  1214. '每题用时情况',
  1215. subjectDetail.pace.map(row => {
  1216. return [`${row.no}`, row.userTime];
  1217. }),
  1218. ['我的'],
  1219. ['#A3A8BF'],
  1220. )}
  1221. />
  1222. <div className="m-b-4" />
  1223. <LineChart
  1224. height={400}
  1225. option={lineOption1(
  1226. '累计用时情况',
  1227. (function (sd) {
  1228. let userTime = 0;
  1229. let time = 0;
  1230. sd.pace.map(row => {
  1231. userTime += row.userTime;
  1232. time += row.time;
  1233. return [`${row.no}`, userTime, time];
  1234. });
  1235. }(subjectDetail)),
  1236. ['我的', '建议'],
  1237. ['#A3A8BF', '#7AA7DC'],
  1238. )}
  1239. />
  1240. </div>}
  1241. </div>
  1242. </div>
  1243. {!subjectDetail && <div className="body gray">
  1244. <div className="content">
  1245. <div className="title">成绩单</div>
  1246. <table>
  1247. <thead>
  1248. <tr>
  1249. <th>Question format</th>
  1250. <th>Total</th>
  1251. <th>Correct</th>
  1252. <th>%Correct</th>
  1253. <th>
  1254. Avg Time
  1255. </th>
  1256. <th>
  1257. Avg Time
  1258. <br />
  1259. Correct
  1260. </th>
  1261. <th>
  1262. Avg Time
  1263. <br />
  1264. Incorrect
  1265. </th>
  1266. <th>
  1267. Avg Diff
  1268. <br />
  1269. Correct
  1270. </th>
  1271. <th>
  1272. Avg Diff
  1273. <br />
  1274. Incorrect
  1275. </th>
  1276. </tr>
  1277. </thead>
  1278. <tbody>
  1279. {ExaminationQuestionType.map(row => {
  1280. const typeDetail = detail.type[row.value];
  1281. return <tr>
  1282. <td>{row.long}</td>
  1283. <td>{typeDetail.info.questionNumber}</td>
  1284. <td>{typeDetail.info.userCorrect}</td>
  1285. <td>
  1286. {formatPercent(typeDetail.info.userCorrect, typeDetail.info.userNumber)}
  1287. </td>
  1288. <td>
  1289. {formatSecond(typeDetail.info.userTime / typeDetail.info.userNumber)}
  1290. </td>
  1291. <td>
  1292. {formatSecond(typeDetail.info.correctTime / typeDetail.info.userCorrect)}
  1293. </td>
  1294. <td>
  1295. {formatSecond(typeDetail.info.incorrectTime / typeDetail.info.userNumber - typeDetail.info.userCorrect)}
  1296. </td>
  1297. <td>{typeDetail.info.avgDiffCorrect}</td>
  1298. <td>{typeDetail.info.avgDiffIncorrect}</td>
  1299. </tr>;
  1300. })}
  1301. </tbody>
  1302. </table>
  1303. </div>
  1304. </div>}
  1305. {subjectDetail && <div className="body gray">
  1306. <div className="content">
  1307. <div className="title">基本情况</div>
  1308. <table>
  1309. <thead>
  1310. <tr>
  1311. <th>%Correct</th>
  1312. <th>Avg Time</th>
  1313. <th>
  1314. Avg Time
  1315. <br />
  1316. Correct
  1317. </th>
  1318. <th>
  1319. Avg Time
  1320. <br />
  1321. Incorrect
  1322. </th>
  1323. <th>
  1324. Avg Diff
  1325. <br />
  1326. Correct
  1327. </th>
  1328. <th>
  1329. Avg Diff
  1330. <br />
  1331. Incorrect
  1332. </th>
  1333. </tr>
  1334. </thead>
  1335. <tbody>
  1336. <tr>
  1337. <td>
  1338. {formatPercent(subjectDetail.info.userCorrect, subjectDetail.info.userNumber)}
  1339. <br />
  1340. <span>{subjectDetail.info.userCorrect}题/{subjectDetail.info.userNumber}题</span>
  1341. </td>
  1342. <td>
  1343. {formatSecond(subjectDetail.info.userTime / subjectDetail.info.userNumber)}
  1344. <br />
  1345. <span>{formatMinute(subjectDetail.info.userTime)}min/{subjectDetail.info.userNumber}题</span>
  1346. </td>
  1347. <td>
  1348. {formatSecond(subjectDetail.info.correctTime / subjectDetail.info.userCorrect)}
  1349. <br />
  1350. <span>{formatMinute(subjectDetail.info.correctTime)}min/{subjectDetail.info.userCorrect}题</span>
  1351. </td>
  1352. <td>
  1353. {formatSecond(subjectDetail.info.incorrectTime / subjectDetail.info.userNumber - subjectDetail.info.userCorrect)}
  1354. <br />
  1355. <span>{formatMinute(subjectDetail.info.incorrectTime)}min/{subjectDetail.info.userNumber}题</span>
  1356. </td>
  1357. <td>{subjectDetail.info.avgDiffCorrect}</td>
  1358. <td>{subjectDetail.info.avgDiffIncorrect}</td>
  1359. </tr>
  1360. </tbody>
  1361. </table>
  1362. </div>
  1363. </div>}
  1364. {subjectDetail && <div className="body">
  1365. <div className="content">
  1366. <div className="title">难度分析</div>
  1367. <BarChart
  1368. height={400}
  1369. option={BarOption2(
  1370. '正确率',
  1371. subjectDetail.difficult.map(row => {
  1372. return [QuestionDifficultMap[row.key], formatPercent(row.userCorrect, row.userNumber)];
  1373. }),
  1374. ['我的'],
  1375. ['#989FC1'],
  1376. )}
  1377. />
  1378. </div>
  1379. </div>}
  1380. {subjectDetail && <div className="body gray">
  1381. <div className="content">
  1382. <div className="title">知识体系分析</div>
  1383. <BarChart
  1384. height={400}
  1385. option={BarOption3(
  1386. ['知识点', '正确率分析', '用时分析'],
  1387. subjectDetail.place.map(row => {
  1388. return row.key;
  1389. }),
  1390. subjectDetail.place.map(row => {
  1391. return formatPercent(row.userCorrect, row.userNumber);
  1392. }),
  1393. subjectDetail.place.map(row => {
  1394. return row.userTime / row.userNumber;
  1395. }),
  1396. ['#92AFD2', '#BFD4EE'],
  1397. ['#989FC1', '#CCCCDC'],
  1398. )}
  1399. />
  1400. </div>
  1401. </div>}
  1402. </div >;
  1403. }
  1404. renderExaminationScore() {
  1405. const { report = {} } = this.state;
  1406. const { score } = report;
  1407. return <div className="body">
  1408. <div className="content">
  1409. <div className="title">成绩单</div>
  1410. <table>
  1411. <thead>
  1412. <tr>
  1413. <th>学科</th>
  1414. <th>分数</th>
  1415. <th>排名</th>
  1416. <th>题目</th>
  1417. </tr>
  1418. </thead>
  1419. <tbody>
  1420. {ExaminationSubject.map(row => {
  1421. return <tr>
  1422. <td>{row.long}</td>
  1423. <td>{row.ignore ? '--' : score[`${row.value}Score`]}</td>
  1424. <td>{row.ignore ? '--' : score[`${row.value}Rank`]}</td>
  1425. <td><Button size="small" radius onClick={() => {
  1426. Question.getDetailByNo(report.id, 1, row.value).then((r) => {
  1427. linkTo(`/paper/question/${r.id}`);
  1428. });
  1429. }}>回顾</Button></td></tr>;
  1430. })}
  1431. <tr>
  1432. <td>Total</td>
  1433. <td>{score.totalScore}</td>
  1434. <td>{score.totalRank}</td>
  1435. <td><Button size="small" radius onClick={() => {
  1436. Question.getDetailByNo(report.id, 1).then((r) => {
  1437. linkTo(`/paper/question/${r.id}`);
  1438. });
  1439. }}>回顾</Button></td></tr>;
  1440. </tbody>
  1441. </table>
  1442. </div>
  1443. </div>;
  1444. }
  1445. }