page.js 40 KB

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