page.js 50 KB

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