page.js 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413
  1. import React from 'react';
  2. import './index.less';
  3. import { Link } from 'react-router-dom';
  4. import Page from '@src/containers/Page';
  5. import { asyncConfirm } from '@src/services/AsyncTools';
  6. import { formatTreeData, getMap } from '@src/services/Tools';
  7. import Tabs from '../../../components/Tabs';
  8. import Module from '../../../components/Module';
  9. import Input from '../../../components/Input';
  10. import Button from '../../../components/Button';
  11. import Division from '../../../components/Division';
  12. import Card from '../../../components/Card';
  13. import ListTable from '../../../components/ListTable';
  14. import ProgressText from '../../../components/ProgressText';
  15. import IconButton from '../../../components/IconButton';
  16. import { Main } from '../../../stores/main';
  17. import { Sentence } from '../../../stores/sentence';
  18. import { Question } from '../../../stores/question';
  19. // import { Course } from '../../../stores/course';
  20. const SENTENCE = 'sentence';
  21. const PREVIEW = 'preview';
  22. const PREVIEW_CLASS = 'PREVIEW_CLASS';
  23. const PREVIEW_LIST = 'PREVIEW_LIST';
  24. const columns = [
  25. {
  26. title: '练习册',
  27. width: 250,
  28. align: 'left',
  29. render: item => {
  30. return (
  31. <div className="table-row">
  32. <div className="night f-s-16">{item.title}</div>
  33. <div>
  34. <ProgressText
  35. progress={item.report.id ? item.repport.userNumber / item.report.questionNumber : 0}
  36. size="small"
  37. />
  38. </div>
  39. </div>
  40. );
  41. },
  42. },
  43. {
  44. title: '正确率',
  45. width: 150,
  46. align: 'left',
  47. render: item => {
  48. return (
  49. <div className="table-row">
  50. <div className="night f-s-16 f-w-b">--</div>
  51. <div className="f-s-12">{item.stat.totalCorrect / item.stat.totalNumber}</div>
  52. </div>
  53. );
  54. },
  55. },
  56. {
  57. title: '全站用时',
  58. width: 150,
  59. align: 'left',
  60. render: item => {
  61. return (
  62. <div className="table-row">
  63. <div className="night f-s-16 f-w-b">--</div>
  64. <div className="f-s-12">全站{item.stat.totalTime / item.stat.totalNumber}s</div>
  65. </div>
  66. );
  67. },
  68. },
  69. {
  70. title: '最近做题',
  71. width: 150,
  72. align: 'left',
  73. render: () => {
  74. return (
  75. <div className="table-row">
  76. <div>2019-04-28</div>
  77. <div>07:30</div>
  78. </div>
  79. );
  80. },
  81. },
  82. {
  83. title: '操作',
  84. width: 180,
  85. align: 'left',
  86. render: item => {
  87. return (
  88. <div className="table-row p-t-1">
  89. {!item.repport.id && (
  90. <IconButton type="start" tip="Start" onClick={() => this.previewAction('start', item)} />
  91. )}
  92. {item.repport.id && (
  93. <IconButton
  94. className="m-r-2"
  95. type="continue"
  96. tip="Continue"
  97. onClick={() => this.previewAction('continue', item)}
  98. />
  99. )}
  100. {item.repport.id && (
  101. <IconButton type="restart" tip="Restart" onClick={() => this.previewAction('restart', item)} />
  102. )}
  103. </div>
  104. );
  105. },
  106. },
  107. {
  108. title: '报告',
  109. width: 30,
  110. align: 'right',
  111. render: item => {
  112. return (
  113. <div className="table-row p-t-1">
  114. {item.report.userNumber === item.report.questionNumber && <IconButton type="report" tip="Report" />}
  115. </div>
  116. );
  117. },
  118. },
  119. ];
  120. export default class extends Page {
  121. initState() {
  122. this.code = null;
  123. this.columns = columns;
  124. this.exerciseProcess = {};
  125. return {
  126. tab1: PREVIEW,
  127. tab2: '',
  128. previewType: PREVIEW_CLASS,
  129. tabs: [],
  130. allClass: [],
  131. classProcess: {},
  132. };
  133. }
  134. initData() {
  135. Main.getExercise().then(result => {
  136. const list = result.map((row) => {
  137. row.title = `${row.titleZh}${row.titleEn}`;
  138. row.key = row.extend;
  139. return row;
  140. });
  141. const tabs = formatTreeData(list);
  142. tabs.push({ key: PREVIEW, name: '预习作业' });
  143. const map = getMap(tabs, 'key');
  144. this.setState({ tabs, map });
  145. });
  146. this.refresh();
  147. }
  148. refresh() {
  149. const { tab1 } = this.state;
  150. switch (tab1) {
  151. case SENTENCE:
  152. this.refreshSentence();
  153. break;
  154. case PREVIEW:
  155. this.refreshPreview();
  156. break;
  157. default:
  158. this.refreshExercise();
  159. }
  160. }
  161. refreshSentence() {
  162. Sentence.getInfo().then(result => {
  163. this.setState({ sentence: result });
  164. });
  165. Sentence.listArticle().then(result => {
  166. const articleMap = {};
  167. result.forEach((article) => {
  168. if (!articleMap[article.chapter]) {
  169. articleMap[article.chapter] = [];
  170. }
  171. articleMap[article.chapter].push(article);
  172. });
  173. this.setState({ articleMap });
  174. });
  175. }
  176. refreshPreview() {
  177. const { previewType } = this.state;
  178. switch (previewType) {
  179. case PREVIEW_LIST:
  180. this.refreshListPreview();
  181. break;
  182. case PREVIEW_CLASS:
  183. default:
  184. this.refreshClassProcess();
  185. break;
  186. }
  187. }
  188. refreshClassProcess() {
  189. Question.getClassProcess().then(result => {
  190. const classProcess = {};
  191. for (let i = 0; i < result.length; i += 1) {
  192. const item = result[i];
  193. classProcess[item.category].push(item);
  194. }
  195. this.setState({ classProcess });
  196. });
  197. }
  198. refreshListPreview() {
  199. Question.listPreview().then(result => {
  200. this.setState({ previews: result });
  201. });
  202. }
  203. refreshExercise() {
  204. const { map, tab1 } = this.state;
  205. let { tab2 } = this.state;
  206. const subject = map[tab1];
  207. if (tab2 === '') {
  208. tab2 = subject.children[0].key;
  209. this.onChangeTab(2, tab2);
  210. return;
  211. }
  212. const type = map[tab2];
  213. Main.getExerciseChildren(type.id, true).then(result => {
  214. const exerciseChild = result;
  215. this.setState({ exerciseChild });
  216. });
  217. Question.getExerciseProcess(type.id).then((r => {
  218. const exerciseProcess = getMap(r, 'id');
  219. this.setState({ exerciseProcess });
  220. }));
  221. }
  222. onChangePreviewType(type) {
  223. this.setState({ previewType: type });
  224. this.refreshPreview();
  225. }
  226. onChangeTab(level, tab) {
  227. const state = {};
  228. state[`tab${level}`] = tab;
  229. this.setState(state);
  230. this.refresh();
  231. }
  232. previewAction(type, item) {
  233. switch (type) {
  234. case 'start':
  235. this.start('preview', item);
  236. break;
  237. case 'restart':
  238. this.restart(item);
  239. break;
  240. case 'continue':
  241. this.continue('preview', item);
  242. break;
  243. default:
  244. break;
  245. }
  246. }
  247. restart(item) {
  248. asyncConfirm('提示', '是否重置', () => {
  249. Question.restart(item.report.id).then(() => {
  250. this.refresh();
  251. });
  252. });
  253. }
  254. start(type, item) {
  255. linkTo(`/paper/process/${type}/${item.id}`);
  256. }
  257. continue(type, item) {
  258. linkTo(`/paper/process/${type}/${item.id}?r=${item.report.id}`);
  259. }
  260. activeSentence() {
  261. Sentence.active(this.code)
  262. .then(() => {
  263. this.refresh();
  264. });
  265. }
  266. renderView() {
  267. const { tab1 = {}, tab2 = {}, tabs, map = {} } = this.state;
  268. const children = (map[tab1] || {}).children || [];
  269. return (
  270. <div>
  271. <div className="content">
  272. <Module className="m-t-2">
  273. <Tabs type="card" active={tab1} tabs={tabs} onChange={key => {
  274. this.onChangeTab(1, key);
  275. }} />
  276. {children.length > 1 && <Tabs active={tab2} tabs={children} onChange={key => this.onChangeTab(2, key)} />}
  277. </Module>
  278. {tab1 !== SENTENCE && tab1 !== PREVIEW && this.renderExercise()}
  279. {tab1 === SENTENCE && this.renderSentence()}
  280. {tab1 === PREVIEW && this.renderPreview()}
  281. </div>
  282. </div>
  283. );
  284. }
  285. renderPreview() {
  286. const { previewType } = this.state;
  287. switch (previewType) {
  288. case PREVIEW_CLASS:
  289. return this.renderPreviewClass();
  290. case PREVIEW_LIST:
  291. return this.renderPreviewList();
  292. default:
  293. return <div />;
  294. }
  295. }
  296. renderPreviewClass() {
  297. const { allClass, classProcess } = this.state;
  298. return (
  299. <div className="work-body">
  300. <div className="work-nav">
  301. <div className="left">完成情况</div>
  302. <div className="right theme c-p" onClick={() => this.onChangePreviewType(PREVIEW_LIST)}>
  303. 全部作业 >
  304. </div>
  305. </div>
  306. <Division col="3">
  307. {allClass.map(item => {
  308. return <Card data={item} process={classProcess[item.id]} previewAction={this.previewAction} />;
  309. })}
  310. </Division>
  311. </div>
  312. );
  313. }
  314. renderPreviewList() {
  315. const { previews } = this.state;
  316. return (
  317. <div className="work-body">
  318. <div className="work-nav">
  319. <div className="left">全部作业</div>
  320. <div className="right theme c-p" onClick={() => this.onChangePreviewType(PREVIEW_CLASS)}>
  321. 我的课程 >
  322. </div>
  323. </div>
  324. <ListTable
  325. filters={[
  326. {
  327. type: 'radio',
  328. checked: 'today',
  329. list: [{ key: 'today', title: '今日需完成' }, { key: 'tomorrow', title: '明日需完成' }],
  330. },
  331. {
  332. type: 'radio',
  333. checked: 'unfinish',
  334. list: [{ key: 'unfinish', title: '未完成' }, { key: 'finish', title: '已完成' }],
  335. },
  336. { type: 'select', checked: 'all', list: [{ key: 'all', title: '全部' }] },
  337. ]}
  338. data={previews}
  339. columns={this.columns}
  340. />
  341. </div>
  342. );
  343. }
  344. renderSentence() {
  345. const { sentence = {}, trail = false } = this.state;
  346. if (sentence.code || trail) {
  347. return this.renderSentenceArticle();
  348. }
  349. return this.renderInputCode();
  350. }
  351. renderSentenceArticle() {
  352. // const { sentence = {}, trail } = this.state;
  353. return <div />;
  354. }
  355. renderInputCode() {
  356. return (
  357. <Module className="code-module">
  358. <div className="title">输入《千行GMAT长难句》专属 Code,解锁在线练习功能。</div>
  359. <div className="input-block">
  360. <Input size="lager" placeholder="请输入CODE" onChange={(value) => {
  361. this.code = value;
  362. }} />
  363. <Button size="lager" onClick={() => {
  364. this.activeSentence();
  365. }}>解锁</Button>
  366. </div>
  367. <div className="tip">
  368. <Link to="/" className="left link">
  369. 什么是CODE?
  370. </Link>
  371. <span>没有 CODE?</span>
  372. <Link to="/" className="link">
  373. 去获取 >>
  374. </Link>
  375. <a onClick={() => {
  376. this.setState({ trail: true });
  377. }} className="right link">
  378. 试用 >>
  379. </a>
  380. </div>
  381. </Module>
  382. );
  383. }
  384. renderExercise() {
  385. return <div />;
  386. }
  387. }