page.js 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499
  1. import React from 'react';
  2. import './index.less';
  3. import Page from '@src/containers/Page';
  4. // import LineChart from '@src/components/LineChart';
  5. import BarChart from '@src/components/BarChart';
  6. import PieChart from '@src/components/PieChart';
  7. // import { getMap } from '@src/services/Tools';
  8. import UserLayout from '../../../layouts/User';
  9. import UserTable from '../../../components/UserTable';
  10. import UserAction from '../../../components/UserAction';
  11. import Select from '../../../components/Select';
  12. import menu from '../index';
  13. import Tabs from '../../../components/Tabs';
  14. import { My } from '../../../stores/my';
  15. import { QuestionDifficult } from '../../../../Constant';
  16. import { getMap, formatPercent, formatSeconds } from '../../../../../src/services/Tools';
  17. const QuestionDifficultMap = getMap(QuestionDifficult, 'value', 'label');
  18. console.log(QuestionDifficultMap);
  19. const columns = [
  20. {
  21. key: 'title',
  22. title: '',
  23. render(text) {
  24. return <b>{text}</b>;
  25. },
  26. },
  27. {
  28. key: 'progress',
  29. title: '进度',
  30. render(text, record) {
  31. return (
  32. <div className="v">
  33. <div className="t">{formatPercent(record.userQuestion, record.questionNumber, false)}</div>
  34. <div className="d">已做{record.userQuestion}道</div>
  35. </div>
  36. );
  37. },
  38. },
  39. {
  40. key: 'ratio',
  41. title: '正确率',
  42. render(text, record) {
  43. return (
  44. <div className="v">
  45. <div className="t">{formatPercent(record.userCorrect, record.userNumber, false)}</div>
  46. <div className="d">{record.userCorrect}/{record.userNumber}</div>
  47. </div>
  48. );
  49. },
  50. },
  51. {
  52. key: 'time',
  53. title: '平均用时',
  54. help: '',
  55. render(text, record) {
  56. return formatSeconds(record.userTime / record.userNumber);
  57. },
  58. },
  59. ];
  60. // const QuestionDifficultMap = getMap(QuestionDifficult, 'value', 'label');
  61. function pieOption1(value, text, subtext) {
  62. return {
  63. title: {
  64. text,
  65. textAlign: 'center',
  66. textVerticalAlign: 'middle',
  67. subtext,
  68. top: '28%',
  69. left: '48%',
  70. },
  71. // value < 50 ? '#f19057' :
  72. color: ['#6966fb', '#f7f7f7'],
  73. series: [
  74. {
  75. type: 'pie',
  76. radius: ['90%', '100%'],
  77. label: {
  78. show: false,
  79. },
  80. data: [value, 100 - value],
  81. },
  82. ],
  83. };
  84. }
  85. function barOption1(avgTotal, avgCorrect, avgIncorrent) {
  86. return {
  87. xAxis: {
  88. type: 'category',
  89. axisTick: { show: false },
  90. axisLine: { lineStyle: { color: '#D1D6DF' } },
  91. splitLine: { show: false },
  92. data: [
  93. {
  94. value: 'Avg Time\nTotal',
  95. textStyle: { color: '#686872', fontWeight: '500', fontSize: 14, lineHeight: 20 },
  96. },
  97. {
  98. value: 'Avg Time\nCorrect',
  99. textStyle: { color: '#686872', fontWeight: '500', fontSize: 14, lineHeight: 20 },
  100. },
  101. {
  102. value: 'Avg Time\nIncorrect',
  103. textStyle: { color: '#686872', fontWeight: '500', fontSize: 14, lineHeight: 20 },
  104. },
  105. ],
  106. },
  107. yAxis: {
  108. show: false,
  109. min: 0,
  110. max: 100,
  111. axisTick: { show: false },
  112. axisLine: { show: false },
  113. splitLine: { show: false },
  114. },
  115. grid: { width: 300, left: '10%' },
  116. series: {
  117. type: 'bar',
  118. barWidth: 50,
  119. data: [
  120. {
  121. value: avgTotal,
  122. name: 'Avg Time\nTotal',
  123. itemStyle: { color: '#92AFD2' },
  124. label: {
  125. show: true,
  126. position: 'top',
  127. formatter: `{a|${formatSeconds(avgTotal)}}`,
  128. rich: { a: { fontSize: 16, fontWeight: 'bold', color: '#686872' } },
  129. },
  130. },
  131. {
  132. value: avgCorrect,
  133. name: 'Avg Time\nCorrect',
  134. itemStyle: { color: '#989FC1' },
  135. label: {
  136. show: true,
  137. position: 'top',
  138. formatter: `{a|${formatSeconds(avgCorrect)}}`,
  139. rich: { a: { fontSize: 16, fontWeight: 'bold', color: '#686872' } },
  140. },
  141. },
  142. {
  143. value: avgIncorrent,
  144. name: 'Avg Time\nIncorrect',
  145. itemStyle: { color: '#BFD4EE' },
  146. label: {
  147. show: true,
  148. position: 'top',
  149. formatter: `{a|${formatSeconds(avgIncorrent)}}`,
  150. rich: { a: { fontSize: 16, fontWeight: 'bold', color: '#686872' } },
  151. },
  152. },
  153. ],
  154. },
  155. };
  156. }
  157. function barOption2(title, subTitle, data) {
  158. return {
  159. title: {
  160. text: title,
  161. subtext: subTitle,
  162. textStyle: { fontSize: 16 },
  163. },
  164. tooltip: {
  165. trigger: 'axis',
  166. },
  167. color: '#989FC1',
  168. dataset: {
  169. source: [['type', 'self'], ...data],
  170. },
  171. grid: { left: 30, right: 30, height: 250 },
  172. xAxis: {
  173. type: 'category',
  174. axisLabel: { color: '#686872' },
  175. axisLine: { lineStyle: { color: '#D1D6DF' } },
  176. },
  177. yAxis: {
  178. type: 'value',
  179. min: 0,
  180. max: 100,
  181. axisLabel: { color: '#686872' },
  182. axisLine: { lineStyle: { color: '#D1D6DF' } },
  183. },
  184. series: {
  185. type: 'bar',
  186. barWidth: 40,
  187. },
  188. };
  189. }
  190. function barOption3(titles, source, data1, data2, color1, color2) {
  191. return {
  192. title: [
  193. {
  194. text: titles[0],
  195. textStyle: { fontSize: 16, fontWeight: 'bold', color: '#686872' },
  196. left: 30,
  197. top: 15,
  198. },
  199. {
  200. text: titles[1],
  201. textStyle: { fontSize: 16, fontWeight: 'bold', color: '#686872' },
  202. left: 100,
  203. top: 15,
  204. },
  205. {
  206. text: titles[2],
  207. textStyle: { fontSize: 16, fontWeight: 'bold', color: '#686872' },
  208. left: 430,
  209. top: 15,
  210. },
  211. ],
  212. grid: [{ width: 200, x: 100, bottom: 30 }, { width: 200, x: 430, bottom: 30 }],
  213. xAxis: [
  214. {
  215. gridIndex: 0,
  216. show: false,
  217. axisTick: { show: false },
  218. axisLine: { show: false },
  219. splitLine: { show: false },
  220. },
  221. {
  222. gridIndex: 1,
  223. show: false,
  224. axisTick: { show: false },
  225. axisLine: { show: false },
  226. splitLine: { show: false },
  227. },
  228. ],
  229. yAxis: [
  230. {
  231. gridIndex: 0,
  232. type: 'category',
  233. axisTick: { show: false },
  234. axisLine: { show: false },
  235. splitLine: { show: false },
  236. offset: 15,
  237. data: source,
  238. axisLabel: { color: '#686872', fontSize: 12 },
  239. },
  240. {
  241. gridIndex: 1,
  242. type: 'category',
  243. axisTick: { show: false },
  244. axisLine: { show: false },
  245. splitLine: { show: false },
  246. axisLabel: { show: false },
  247. },
  248. ],
  249. series: [
  250. {
  251. type: 'bar',
  252. xAxisIndex: 0,
  253. yAxisIndex: 0,
  254. barWidth: 24,
  255. data: data1.map((item, index) => ({
  256. value: item,
  257. itemStyle: { color: index % 2 ? color1[0] : color1[1] },
  258. label: {
  259. show: true,
  260. color: '#303036',
  261. align: 'right',
  262. position: [250, 5],
  263. fontSize: 12,
  264. formatter: item,
  265. },
  266. })),
  267. },
  268. {
  269. type: 'bar',
  270. xAxisIndex: 1,
  271. yAxisIndex: 1,
  272. barWidth: 24,
  273. data: data2.map((item, index) => ({
  274. value: item,
  275. itemStyle: { color: index % 2 ? color2[0] : color2[1] },
  276. label: {
  277. show: true,
  278. color: '#303036',
  279. align: 'right',
  280. fontSize: 12,
  281. position: [260, 5],
  282. formatter: item,
  283. },
  284. })),
  285. },
  286. ],
  287. };
  288. }
  289. export default class extends Page {
  290. initState() {
  291. return {
  292. filterMap: {},
  293. selectList: [],
  294. tab: 'exercise',
  295. questionType: '',
  296. info: 'base',
  297. };
  298. }
  299. initData() {
  300. const data = Object.assign(this.state, this.state.search);
  301. if (data.order) {
  302. data.sortMap = { [data.order]: data.direction };
  303. }
  304. data.filterMap = this.state.search;
  305. const startTime = null;
  306. const endTime = null;
  307. switch (data.timerange) {
  308. case 'all':
  309. break;
  310. case 'week':
  311. break;
  312. case 'month':
  313. break;
  314. case 'month3':
  315. break;
  316. case 'today':
  317. default:
  318. }
  319. My.getData(data.tab, data.subject, '', startTime, endTime).then(result => {
  320. console.log(result);
  321. this.data = result;
  322. this.setState({ list: Object.values(result) });
  323. this.onQuestionTypeChange();
  324. });
  325. }
  326. onTabChange(tab) {
  327. const data = { tab };
  328. this.refreshQuery(data);
  329. }
  330. onQuestionTypeChange(questionType) {
  331. const data = this.data[questionType];
  332. this.setState({ questionType, data });
  333. }
  334. onInfoChange(info) {
  335. this.setState({ info });
  336. }
  337. onFilter(value) {
  338. this.search(value);
  339. }
  340. onAction() { }
  341. onSelect(selectList) {
  342. this.setState({ selectList });
  343. }
  344. renderView() {
  345. const { config } = this.props;
  346. return <UserLayout active={config.key} menu={menu} center={this.renderTable()} />;
  347. }
  348. renderTable() {
  349. const { tab, questionType, info, questionTypeSelect, filterMap = {}, list = [] } = this.state;
  350. return (
  351. <div className="table-layout">
  352. <Tabs
  353. border
  354. type="division"
  355. theme="theme"
  356. size="small"
  357. space={2.5}
  358. width={100}
  359. active={tab}
  360. tabs={[{ key: 'exercise', title: '练习' }, { key: 'examination', title: '模考' }]}
  361. onChange={key => this.onTabChange(key)}
  362. />
  363. <UserAction
  364. selectList={[{
  365. children: [
  366. {
  367. key: 'one',
  368. select: [{ title: '123', key: '1' }, { title: '123', key: '2' }, { title: '123', key: '2' }],
  369. },
  370. {
  371. key: 'two',
  372. be: 'one',
  373. placeholder: '全部',
  374. selectMap: [{ title: '123', key: '1' }, { title: '123', key: '2' }, { title: '123', key: '2' }],
  375. },
  376. ],
  377. }, {
  378. right: true,
  379. key: 'timerange',
  380. select: [{ title: '今天', key: 'today' }, { title: '近一周', key: 'week' }, { title: '近一个月', key: 'month' }, { title: '近三个月', key: 'month3' }, { title: '全部', key: 'all' }],
  381. }]}
  382. filterMap={filterMap}
  383. onFilter={value => this.onFilter(value)}
  384. />
  385. <div className="title">整体情况</div>
  386. <UserTable size="small" columns={columns} data={list} />
  387. <div className="title">
  388. 单项分析
  389. <Select
  390. size="small"
  391. theme="default"
  392. value={questionType}
  393. list={questionTypeSelect}
  394. onChange={({ key }) => this.onQuestionTypeChange(key)}
  395. />
  396. </div>
  397. <Tabs
  398. border
  399. type="line"
  400. theme="theme"
  401. size="small"
  402. width={80}
  403. active={info}
  404. tabs={[{ key: 'base', title: '基本情况' }, { key: 'difficult', title: '难度分析' }, { key: 'place', title: '考点分析' }]}
  405. onChange={key => this.onInfoChange(key)}
  406. />
  407. {this[`renderTab${info}`]()}
  408. </div>
  409. );
  410. }
  411. renderTabbase() {
  412. const { data = {} } = this.state;
  413. return (
  414. <div className="tab-1-layout">
  415. <div className="block">
  416. <div className="chart">
  417. <PieChart height={110} width={110} option={pieOption1(formatPercent(data.userQuestion, data.questionNumber), formatPercent(data.userQuestion, data.questionNumber, false), `全站${formatPercent(data.totalCorrect, data.totalNumber, false)}`)} />
  418. </div>
  419. <div className="value">
  420. <div className="total">共{data.questionNumber}题</div>
  421. <div className="item">
  422. <div className="t">已做</div>
  423. <div className="v">
  424. <b>{data.userQuestion}</b>题
  425. </div>
  426. </div>
  427. <div className="item">
  428. <div className="t">剩余</div>
  429. <div className="v">
  430. <b>{data.questionNumber - data.userQuestion}</b>题
  431. </div>
  432. </div>
  433. <div className="item">
  434. <div className="t">正确率</div>
  435. <div className="v">
  436. <b>{formatPercent(data.userCorrect, data.userNumber, false)}</b>
  437. </div>
  438. </div>
  439. <div className="item">
  440. <div className="t">全站</div>
  441. <div className="v">
  442. <b>{formatPercent(data.totalCorrect, data.totalNumber, false)}</b>
  443. </div>
  444. </div>
  445. </div>
  446. </div>
  447. <div className="block">
  448. <BarChart height={300} option={barOption1(data.userNumber ? data.userTime / data.userNumber : 0, data.userCorrect ? data.correctTime / data.userCorrect : 0, data.userNumber - data.userCorrect ? data.incorrectTime / (data.userNumber - data.userCorrect) : 0)} />
  449. </div>
  450. </div>
  451. );
  452. }
  453. renderTabdifficult() {
  454. const { data = {} } = this.state;
  455. return (
  456. <div className="tab-2-layout">
  457. <BarChart
  458. height={350}
  459. option={barOption2(`平均正确率${formatPercent(data.userCorrect, data.userNumber, false)}`, '正确率', [['easy', 10], ['Medium', 30], ['Hard', 40]])}
  460. />
  461. </div>
  462. );
  463. }
  464. renderTabplace() {
  465. return (
  466. <div className="tab-3-layout">
  467. <BarChart
  468. height={350}
  469. option={barOption3(
  470. ['知识点', '正确率分析', '用时分析'],
  471. ['Idiom', 'Comparison', 'Verb', 'Pronoun', 'Modifier', 'Parallelism'],
  472. [10, 12, 12, 13, 14, 15],
  473. [10, 12, 12, 13, 14, 15],
  474. ['#92AFD2', '#BFD4EE'],
  475. ['#989FC1', '#CCCCDC'],
  476. )}
  477. />
  478. </div>
  479. );
  480. }
  481. }