page.js 53 KB

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