page.js 27 KB

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