page.js 50 KB

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