page.js 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938
  1. import React from 'react';
  2. import './index.less';
  3. import { Modal, Tooltip } from 'antd';
  4. import { Link } from 'react-router-dom';
  5. import Page from '@src/containers/Page';
  6. import { asyncConfirm } from '@src/services/AsyncTools';
  7. import { formatTreeData, formatSeconds, formatDate, formatPercent } from '@src/services/Tools';
  8. import Continue from '../../../components/Continue';
  9. import Step from '../../../components/Step';
  10. import Panel from '../../../components/Panel';
  11. import List from '../../../components/List';
  12. import Tabs from '../../../components/Tabs';
  13. import Module from '../../../components/Module';
  14. import Input from '../../../components/Input';
  15. import Button from '../../../components/Button';
  16. import AnswerButton from '../../../components/AnswerButton';
  17. import Division from '../../../components/Division';
  18. import Card from '../../../components/Card';
  19. import ListTable from '../../../components/ListTable';
  20. import ProgressText from '../../../components/ProgressText';
  21. import IconButton from '../../../components/IconButton';
  22. import { Main } from '../../../stores/main';
  23. import { My } from '../../../stores/my';
  24. import { Sentence } from '../../../stores/sentence';
  25. import { Question } from '../../../stores/question';
  26. import { Course } from '../../../stores/course';
  27. import { User } from '../../../stores/user';
  28. const SENTENCE = 'sentence';
  29. const PREVIEW = 'preview';
  30. const PREVIEW_CLASS = 'PREVIEW_CLASS';
  31. const PREVIEW_LIST = 'PREVIEW_LIST';
  32. const exerciseColumns = [
  33. {
  34. title: '练习册',
  35. width: 250,
  36. align: 'left',
  37. render: item => {
  38. return (
  39. <div className="table-row">
  40. <div className="night f-s-16">{item.title}</div>
  41. <div>
  42. <ProgressText
  43. progress={item.report.id ? formatPercent(item.repport.userNumber, item.report.questionNumber) : 0}
  44. size="small"
  45. />
  46. </div>
  47. </div>
  48. );
  49. },
  50. },
  51. {
  52. title: '正确率',
  53. width: 150,
  54. align: 'left',
  55. render: item => {
  56. return (
  57. <div className="table-row">
  58. <div className="night f-s-16 f-w-b">--</div>
  59. <div className="f-s-12">{formatPercent(item.stat.totalCorrect, item.stat.totalNumber, false)}</div>
  60. </div>
  61. );
  62. },
  63. },
  64. {
  65. title: '全站用时',
  66. width: 150,
  67. align: 'left',
  68. render: item => {
  69. return (
  70. <div className="table-row">
  71. <div className="night f-s-16 f-w-b">--</div>
  72. <div className="f-s-12">全站{formatSeconds(item.stat.totalTime / item.stat.totalNumber)}</div>
  73. </div>
  74. );
  75. },
  76. },
  77. {
  78. title: '最近做题',
  79. width: 150,
  80. align: 'left',
  81. render: () => {
  82. return (
  83. <div className="table-row">
  84. <div>2019-04-28</div>
  85. <div>07:30</div>
  86. </div>
  87. );
  88. },
  89. },
  90. {
  91. title: '操作',
  92. width: 180,
  93. align: 'left',
  94. render: item => {
  95. return (
  96. <div className="table-row p-t-1">
  97. {!item.report && <IconButton type="start" tip="Start" onClick={() => Question.startLink('preview', item)} />}
  98. {item.report.id && !item.report.isFinish && (
  99. <IconButton
  100. className="m-r-2"
  101. type="continue"
  102. tip="Continue"
  103. onClick={() => Question.continueLink('preview', item)}
  104. />
  105. )}
  106. {item.report.id && <IconButton type="restart" tip="Restart" onClick={() => this.restart('preview', item)} />}
  107. </div>
  108. );
  109. },
  110. },
  111. {
  112. title: '报告',
  113. width: 30,
  114. align: 'right',
  115. render: item => {
  116. return (
  117. <div className="table-row p-t-1">
  118. {item.report.isFinish && <IconButton type="report" tip="Report" onClick={() => Question.reportLink(item)} />}
  119. </div>
  120. );
  121. },
  122. },
  123. ];
  124. export default class extends Page {
  125. constructor(props) {
  126. super(props);
  127. this.sentenceColums = [
  128. {
  129. title: '练习册',
  130. width: 250,
  131. align: 'left',
  132. render: record => {
  133. let progress = 0;
  134. if (record.report) {
  135. progress = formatPercent(record.report.userNumber, record.report.questionNumber);
  136. }
  137. return (
  138. <div className="table-row">
  139. <div className="night f-s-16">{record.title}</div>
  140. <div>
  141. <ProgressText progress={progress} size="small" />
  142. </div>
  143. </div>
  144. );
  145. },
  146. },
  147. {
  148. title: '正确率',
  149. width: 150,
  150. align: 'left',
  151. render: record => {
  152. let correct = '--';
  153. if (record.report) {
  154. correct = formatPercent(record.report.userCorrect, record.report.userNumber, false);
  155. }
  156. return (
  157. <div className="table-row">
  158. <div className="night f-s-16 f-w-b">{correct}</div>
  159. <div className="f-s-12">
  160. 全站{formatPercent(record.stat.totalCorrect, record.stat.totalNumber, false)}
  161. </div>
  162. </div>
  163. );
  164. },
  165. },
  166. {
  167. title: '全站用时',
  168. width: 150,
  169. align: 'left',
  170. render: record => {
  171. let time = '--';
  172. if (record.report) {
  173. time = formatSeconds(record.report.userTime / record.report.userNumber);
  174. }
  175. return (
  176. <div className="table-row">
  177. <div className="night f-s-16 f-w-b">{time}</div>
  178. <div className="f-s-12">全站{formatSeconds(record.stat.totalTime / record.stat.totalNumber)}</div>
  179. </div>
  180. );
  181. },
  182. },
  183. {
  184. title: '最近做题',
  185. width: 150,
  186. align: 'left',
  187. render: record => {
  188. const time = record.report ? record.report.updateTime : record.paper ? record.paper.latestTime : null;
  189. if (!time) return null;
  190. return (
  191. <div className="table-row">
  192. <div>{formatDate(time, 'YYYY-MM-DD')}</div>
  193. <div>{formatDate(time, 'HH:mm')}</div>
  194. </div>
  195. );
  196. },
  197. },
  198. {
  199. title: '操作',
  200. width: 180,
  201. align: 'left',
  202. render: record => {
  203. return (
  204. <div className="table-row p-t-1">
  205. {!record.report && (
  206. <IconButton
  207. type="start"
  208. tip="Start"
  209. onClick={() => {
  210. Question.startLink('sentence', record);
  211. }}
  212. />
  213. )}
  214. {record.report && !record.report.isFinish && (
  215. <IconButton
  216. className="m-r-2"
  217. type="continue"
  218. tip="Continue"
  219. onClick={() => {
  220. Question.continueLink('sentence', record);
  221. }}
  222. />
  223. )}
  224. {record.report && !!record.report.isFinish && (
  225. <IconButton
  226. type="restart"
  227. tip="Restart"
  228. onClick={() => {
  229. this.restart(record);
  230. }}
  231. />
  232. )}
  233. </div>
  234. );
  235. },
  236. },
  237. {
  238. title: '报告',
  239. width: 30,
  240. align: 'right',
  241. render: record => {
  242. if (!record.report || !record.report.isFinish) return null;
  243. return (
  244. <div className="table-row p-t-1">
  245. <IconButton
  246. type="report"
  247. tip="Report"
  248. onClick={() => {
  249. Question.reportLink(record);
  250. }}
  251. />
  252. </div>
  253. );
  254. },
  255. },
  256. ];
  257. }
  258. initState() {
  259. this.code = null;
  260. this.columns = exerciseColumns;
  261. this.exerciseProgress = {};
  262. this.inited = false;
  263. return {
  264. tab1: SENTENCE,
  265. tab2: '',
  266. pt: PREVIEW_CLASS,
  267. tabs: [],
  268. allClass: [],
  269. classProcess: {},
  270. };
  271. }
  272. init() {
  273. Main.getExercise().then(result => {
  274. const list = result.map(row => {
  275. row.title = `${row.titleZh}${row.titleEn}`;
  276. row.key = row.extend;
  277. return row;
  278. });
  279. const tabs = formatTreeData(list, 'id', 'title', 'parentId');
  280. tabs.push({ key: PREVIEW, name: '预习作业' });
  281. this.setState({ tabs });
  282. this.inited = true;
  283. this.refreshData();
  284. });
  285. }
  286. initData() {
  287. const { info = {} } = this.props.user;
  288. if (info.latestExercise) {
  289. // 获取最后一次做题记录
  290. Question.baseReport(info.latestExercise).then(result => {
  291. this.setState({ latest: result });
  292. });
  293. }
  294. const data = Object.assign(this.state, this.state.search);
  295. this.setState(data);
  296. if (this.inited) this.refreshData();
  297. }
  298. refreshData(tab) {
  299. const { tab1 } = this.state;
  300. switch (tab || tab1) {
  301. case SENTENCE:
  302. this.refreshSentence();
  303. break;
  304. case PREVIEW:
  305. this.refreshPreview();
  306. break;
  307. default:
  308. this.refreshExercise(tab || tab1);
  309. }
  310. }
  311. refreshSentence() {
  312. const { sentence } = this.state;
  313. if (!sentence) {
  314. User.clearSentenceTrail();
  315. Sentence.getInfo()
  316. .then(result => {
  317. // result.code = '123123';
  318. // result.trailPages = 20;
  319. this.setState({ sentence: result });
  320. return result;
  321. })
  322. .then(({ code, trailPages, chapters }) => {
  323. return Sentence.listArticle().then(result => {
  324. const chapterSteps = [];
  325. const chapterMap = {};
  326. const map = {};
  327. const trailArticles = [];
  328. let totalPage = 0;
  329. let introduction = null;
  330. let exerciseChapter = null;
  331. let index = 0;
  332. let lastChapter = -1;
  333. chapters.forEach(row => {
  334. chapterMap[row.value] = row;
  335. if (row.exercise) exerciseChapter = row;
  336. });
  337. result.forEach(article => {
  338. if (article.chapter === 0) introduction = article;
  339. if (!map[article.chapter]) {
  340. map[article.chapter] = [];
  341. }
  342. article.startPage = totalPage + 1;
  343. article.endPage = totalPage + article.pages;
  344. if (article.chapter) {
  345. article.position = `Part ${article.part}`;
  346. } else {
  347. // 设置list中的样式
  348. article.style = 'introduction';
  349. }
  350. totalPage += article.pages;
  351. if (article.startPage < trailPages) {
  352. if (lastChapter !== article.chapter) {
  353. lastChapter = article.chapter;
  354. trailArticles.push(Object.assign({ articles: [] }, chapterMap[article.chapter] || {}));
  355. }
  356. trailArticles[trailArticles.length - 1].articles.push(article);
  357. }
  358. map[article.chapter].push(article);
  359. });
  360. if (!code) {
  361. chapterSteps.push('试用');
  362. }
  363. // 添加前言
  364. if (introduction) {
  365. chapterSteps.push(`${introduction.title}`);
  366. chapterMap[0] = {
  367. title: introduction.title,
  368. value: 0,
  369. };
  370. }
  371. index += 1;
  372. chapters.forEach(row => {
  373. chapterSteps.push(`「${index}」${row.short}`);
  374. index += 1;
  375. });
  376. this.setState({ articleMap: map, trailArticles, chapterSteps, introduction, chapterMap, exerciseChapter });
  377. });
  378. })
  379. .then(() => {
  380. return Sentence.listPaper().then(result => {
  381. this.setState({ paperList: result, paperFilterList: result });
  382. });
  383. });
  384. }
  385. }
  386. refreshPreview() {
  387. const { pt } = this.state;
  388. switch (pt) {
  389. case PREVIEW_LIST:
  390. this.refreshListPreview();
  391. break;
  392. case PREVIEW_CLASS:
  393. default:
  394. this.refreshClassProcess();
  395. break;
  396. }
  397. }
  398. refreshClassProcess() {
  399. Course.classProcess().then(result => {
  400. const classProcess = {};
  401. for (let i = 0; i < result.length; i += 1) {
  402. const item = result[i];
  403. classProcess[item.category].push(item);
  404. }
  405. this.setState({ classProcess });
  406. });
  407. }
  408. refreshListPreview() {
  409. Question.listPreview().then(result => {
  410. this.setState({ previews: result });
  411. });
  412. }
  413. refreshExercise(tab) {
  414. const { tabs, tab1 } = this.state;
  415. let { tab2 } = this.state;
  416. if (!tabs) {
  417. // 等待数据加载
  418. return;
  419. }
  420. const [subject] = tabs.filter(row => row.key === tab);
  421. // 切换tab1的情况
  422. if (tab2 === '' || tab1 !== tab) {
  423. tab2 = subject.children[0].key;
  424. this.setState({ tab2 });
  425. }
  426. const [type] = subject.children.filter(row => row.key === tab2);
  427. Question.getExerciseProgress(type.id).then(result => {
  428. // const exerciseProgress = getMap(r, 'id');
  429. result = result.map(row => {
  430. row.info = [
  431. {
  432. title: '已做',
  433. number: row.userNumber || 0,
  434. unit: '题',
  435. },
  436. {
  437. title: '剩余',
  438. number: row.questionNumber - row.userNumber || 0,
  439. unit: '题',
  440. },
  441. {
  442. title: '总计',
  443. number: row.questionNumber || 0,
  444. unit: '题',
  445. },
  446. ];
  447. if (row.userStat) {
  448. row.correct = formatPercent(row.userStat.userCorrect, row.userStat.userNumber, false);
  449. } else {
  450. row.correct = '--';
  451. }
  452. row.progress = formatPercent(row.questionNumber - row.userNumber || 0, row.questionNumber);
  453. row.totalCorrect = formatPercent(row.stat.totalCorrect, row.stat.totalNumber, false);
  454. row.children = row.children.map(r => {
  455. r.title = r.title || r.titleZh;
  456. r.progress = formatPercent(r.userNumber, r.questionNumber);
  457. return r;
  458. });
  459. return row;
  460. });
  461. this.setState({ exerciseProgress: result });
  462. });
  463. }
  464. onChangePreviewType(type) {
  465. this.setState({ pt: type });
  466. this.refreshPreview();
  467. }
  468. onChangeTab(level, tab) {
  469. const { tab1 } = this.state;
  470. const data = {};
  471. if (level > 1) {
  472. data.tab1 = tab1;
  473. data.tab2 = tab;
  474. } else {
  475. data.tab1 = tab;
  476. }
  477. // this.refreshData(tab);
  478. this.refreshQuery(data);
  479. }
  480. previewAction(type, item) {
  481. switch (type) {
  482. case 'start':
  483. Question.startLink('preview', item);
  484. break;
  485. case 'restart':
  486. this.restart(item);
  487. break;
  488. case 'continue':
  489. Question.continueLink('preview', item);
  490. break;
  491. default:
  492. break;
  493. }
  494. }
  495. restart(item) {
  496. asyncConfirm('提示', '是否重置', () => {
  497. Question.restart(item.paper.id).then(() => {
  498. this.refresh();
  499. });
  500. });
  501. }
  502. exerciseList(item) {
  503. linkTo(`/exercise/list/${item.id}`);
  504. }
  505. activeSentence() {
  506. Sentence.active(this.code)
  507. .then(() => {
  508. // 重新获取长难句信息
  509. User.clearSentenceTrail();
  510. this.setState({ sentence: null, articleMap: null, paperList: null });
  511. this.refresh();
  512. })
  513. .catch(err => {
  514. this.setState({ sentenceError: err.message });
  515. });
  516. }
  517. trailSentence() {
  518. User.sentenceTrail();
  519. this.setState({ sentenceError: null });
  520. }
  521. sentenceRead(article) {
  522. if (article) {
  523. linkTo(`/sentence/read?chapter=${article.chapter}&part=${article.part}`);
  524. } else {
  525. linkTo('/sentence/read');
  526. }
  527. }
  528. sentenceFilter(value) {
  529. const { paperList } = this.state;
  530. const list = paperList.filter(row => {
  531. const finish = row.paper ? row.paper.times > 0 : false;
  532. if (value === 0) {
  533. return !finish;
  534. }
  535. return finish;
  536. });
  537. this.setState({ paperFilterList: list, paperChecked: value });
  538. }
  539. clearExercise() {
  540. My.clearLatestExercise();
  541. this.setState({ latest: null });
  542. }
  543. renderView() {
  544. const { tab1, tab2, tabs, latest, sentenceModel } = this.state;
  545. const [subject] = tabs.filter(row => row.key === tab1);
  546. const children = subject ? subject.children : [];
  547. return (
  548. <div>
  549. {latest && (
  550. <Continue
  551. data={latest}
  552. onClose={() => {
  553. this.clearExercise();
  554. }}
  555. onContinue={() => {
  556. Question.continueLink('exercise', latest);
  557. }}
  558. onRestart={() => {
  559. this.restart(latest);
  560. }}
  561. onNext={() => {
  562. Question.continueLink('exercise', latest);
  563. }}
  564. />
  565. )}
  566. <div className="content">
  567. <Module className="m-t-2">
  568. <Tabs
  569. type="card"
  570. active={tab1}
  571. tabs={tabs}
  572. onChange={key => {
  573. this.onChangeTab(1, key);
  574. }}
  575. />
  576. {children && children.length > 1 && (
  577. <Tabs active={tab2} tabs={children} onChange={key => this.onChangeTab(2, key)} />
  578. )}
  579. </Module>
  580. {tab1 !== SENTENCE && tab1 !== PREVIEW && this.renderExercise()}
  581. {tab1 === SENTENCE && this.renderSentence()}
  582. {tab1 === PREVIEW && this.renderPreview()}
  583. </div>
  584. {sentenceModel && this.renderInputCodeModel()}
  585. </div>
  586. );
  587. }
  588. renderPreview() {
  589. const { previewType } = this.state;
  590. switch (previewType) {
  591. case PREVIEW_CLASS:
  592. return this.renderPreviewClass();
  593. case PREVIEW_LIST:
  594. return this.renderPreviewList();
  595. default:
  596. return <div />;
  597. }
  598. }
  599. renderPreviewClass() {
  600. const { allClass, classProcess } = this.state;
  601. return (
  602. <div className="work-body">
  603. <div className="work-nav">
  604. <div className="left">完成情况</div>
  605. <div className="right theme c-p" onClick={() => this.onChangePreviewType(PREVIEW_LIST)}>
  606. 全部作业 >
  607. </div>
  608. </div>
  609. <Division col="3">
  610. {allClass.map(item => {
  611. return <Card data={item} process={classProcess[item.id]} previewAction={this.previewAction} />;
  612. })}
  613. </Division>
  614. </div>
  615. );
  616. }
  617. renderPreviewList() {
  618. const { previews } = this.state;
  619. return (
  620. <div className="work-body">
  621. <div className="work-nav">
  622. <div className="left">全部作业</div>
  623. <div className="right theme c-p" onClick={() => this.onChangePreviewType(PREVIEW_CLASS)}>
  624. 我的课程 >
  625. </div>
  626. </div>
  627. <ListTable
  628. filters={[
  629. {
  630. type: 'radio',
  631. checked: 'today',
  632. list: [{ key: 'today', title: '今日需完成' }, { key: 'tomorrow', title: '明日需完成' }],
  633. },
  634. {
  635. type: 'radio',
  636. checked: 'unfinish',
  637. list: [{ key: 'unfinish', title: '未完成' }, { key: 'finish', title: '已完成' }],
  638. },
  639. { type: 'select', checked: 'all', list: [{ key: 'all', title: '全部' }] },
  640. ]}
  641. rightAction={
  642. <div>
  643. 有效期至:2019-11-13{' '}
  644. <Tooltip overlayClassName="gray" placement="top" title="全部模考做完才可重置">
  645. <a>
  646. <Button size="small" disabled radius>
  647. Reset
  648. </Button>
  649. </a>
  650. </Tooltip>
  651. </div>
  652. }
  653. data={previews}
  654. columns={this.columns}
  655. />
  656. </div>
  657. );
  658. }
  659. renderSentence() {
  660. const { sentence = {} } = this.state;
  661. const { sentenceTrail } = this.props.user;
  662. if (sentence.code || sentenceTrail) {
  663. return this.renderSentenceArticle();
  664. }
  665. return this.renderInputCode();
  666. }
  667. renderSentenceArticle() {
  668. const {
  669. sentence = {},
  670. introduction,
  671. chapterSteps,
  672. chapterStep = 1,
  673. exerciseChapter = {},
  674. chapterMap = {},
  675. articleMap = {},
  676. trailArticles = [],
  677. paperFilterList = [],
  678. paperList = [],
  679. paperChecked,
  680. } = this.state;
  681. const { sentenceTrail } = this.props.user;
  682. let maxStep = 0;
  683. if (sentenceTrail) {
  684. // 试用只能访问第一step
  685. maxStep = 1;
  686. // 查找练习章节
  687. }
  688. // 减去前言计算chapter
  689. const chapter = introduction ? chapterStep - 1 : chapterStep;
  690. const chapterInfo = chapterMap[chapter] || {};
  691. let isExercise = false;
  692. if (chapterInfo && chapterInfo.exercise) {
  693. isExercise = true;
  694. }
  695. return (
  696. <div>
  697. {sentence.code && <div className="sentence-code">CODE: {sentence.code}</div>}
  698. {sentenceTrail && (
  699. <div className="sentence-code">
  700. CODE: <Link to="">去获取</Link>
  701. <a
  702. onClick={() => {
  703. this.setState({ sentenceModel: true });
  704. }}
  705. >
  706. 输入
  707. </a>
  708. </div>
  709. )}
  710. <Module>
  711. <Step
  712. list={chapterSteps}
  713. step={chapterStep}
  714. onClick={step => {
  715. this.setState({ chapterStep: step });
  716. }}
  717. message="请购买后访问"
  718. maxStep={maxStep}
  719. />
  720. </Module>
  721. {/* 正常前言 */}
  722. {sentence.code && chapter === 0 && (
  723. <List
  724. // title={chapterInfo.title}
  725. list={[introduction]}
  726. onClick={() => {
  727. this.sentenceRead();
  728. }}
  729. />
  730. )}
  731. {/* 正常文章 */}
  732. {sentence.code && chapter > 0 && !isExercise && (
  733. <List
  734. position={`Chapter${chapter}`}
  735. title={chapterInfo.title}
  736. list={articleMap[chapter]}
  737. onClick={part => {
  738. this.sentenceRead(part);
  739. }}
  740. />
  741. )}
  742. {/* 正常练习 */}
  743. {sentence.code && isExercise && (
  744. <ListTable
  745. position={`Chapter${chapter}`}
  746. title={chapterInfo.title}
  747. filters={[
  748. {
  749. type: 'radio',
  750. checked: paperChecked,
  751. list: [{ key: 0, title: '未完成' }, { key: 1, title: '已完成' }],
  752. onChange: item => {
  753. this.sentenceFilter(item.key);
  754. },
  755. },
  756. ]}
  757. data={paperFilterList}
  758. columns={this.sentenceColums}
  759. />
  760. )}
  761. {/* 试读文章 */}
  762. {sentenceTrail &&
  763. trailArticles.map(info => {
  764. return (
  765. <List
  766. position={info.value ? `Chapter${info.value}` : null}
  767. title={info.title}
  768. list={info.articles}
  769. onClick={part => {
  770. this.sentenceRead(part);
  771. }}
  772. />
  773. );
  774. })}
  775. {/* 试练 */}
  776. {sentenceTrail && (
  777. <ListTable
  778. position={`Chapter${exerciseChapter.value}`}
  779. title={exerciseChapter.title}
  780. data={paperList}
  781. columns={this.sentenceColums}
  782. />
  783. )}
  784. </div>
  785. );
  786. }
  787. renderInputCode() {
  788. const { sentenceError } = this.state;
  789. return (
  790. <Module className="code-module">
  791. <div className="title">输入《千行GMAT长难句》专属 Code,解锁在线练习功能。</div>
  792. <div className="input-block">
  793. <Input
  794. size="lager"
  795. placeholder="请输入CODE"
  796. onChange={value => {
  797. this.code = value;
  798. }}
  799. />
  800. <Button
  801. size="lager"
  802. onClick={() => {
  803. this.activeSentence();
  804. }}
  805. >
  806. 解锁
  807. </Button>
  808. {sentenceError && <div className="error">{sentenceError}</div>}
  809. </div>
  810. <div className="tip">
  811. <Link to="/" className="left link">
  812. 什么是CODE?
  813. </Link>
  814. <span>没有 CODE?</span>
  815. <Link to="/" className="link">
  816. 去获取 >>
  817. </Link>
  818. <a
  819. onClick={() => {
  820. this.trailSentence();
  821. }}
  822. className="right link"
  823. >
  824. 试用 >>
  825. </a>
  826. </div>
  827. </Module>
  828. );
  829. }
  830. renderInputCodeModel() {
  831. const { sentenceError } = this.state;
  832. return (
  833. <Modal visible closable={false} footer={false} title={false}>
  834. <div className="code-module-modal">
  835. <div className="title">请输入CODE</div>
  836. <div className="desc">
  837. <Input
  838. onChange={value => {
  839. this.code = value;
  840. }}
  841. />
  842. {sentenceError && <div className="error">{sentenceError}</div>}
  843. <div className="tip">
  844. <Link to="/" className="right link">
  845. 什么是CODE?
  846. </Link>
  847. </div>
  848. </div>
  849. <div className="btn-list">
  850. <AnswerButton size="lager" theme="confirm" width={150} onClick={() => this.activeSentence()}>
  851. 确认
  852. </AnswerButton>
  853. <AnswerButton
  854. size="lager"
  855. theme="cancel"
  856. width={150}
  857. onClick={() => this.setState({ sentenceModel: false })}
  858. >
  859. 取消
  860. </AnswerButton>
  861. </div>
  862. </div>
  863. </Modal>
  864. );
  865. }
  866. renderExercise() {
  867. const { exerciseProgress = [] } = this.state;
  868. return (
  869. <div>
  870. <Division col="2">
  871. {(exerciseProgress || []).map(struct => {
  872. const [first] = struct.children;
  873. let col = 3;
  874. if (first && first.type === 'paper') {
  875. col = 5;
  876. }
  877. return (
  878. <Panel
  879. title={struct.titleEn}
  880. message={struct.description}
  881. data={struct}
  882. col={col}
  883. onClick={item => {
  884. if (item.type === 'paper') {
  885. if (item.progress === 0) {
  886. Question.startLink('exercise', item);
  887. } else if (item.progress === 100) {
  888. Question.startLink('exercise', item);
  889. } else {
  890. Question.continueLink('exercise', item);
  891. }
  892. } else {
  893. this.exerciseList(item);
  894. }
  895. }}
  896. />
  897. );
  898. })}
  899. </Division>
  900. </div>
  901. );
  902. }
  903. }