index.js 29 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887
  1. import React, { Component } from 'react';
  2. import ReactDOM from 'react-dom';
  3. import { Carousel, Tooltip } from 'antd';
  4. import { Link } from 'react-router-dom';
  5. import Fullscreen from 'react-fullscreen-crossbrowser';
  6. import './index.less';
  7. import { formatSeconds, formatPercent, formatDate } from '@src/services/Tools';
  8. import Assets from '@src/components/Assets';
  9. import Navigation from '../../../../components/Navigation';
  10. import Tabs from '../../../../components/Tabs';
  11. import Icon from '../../../../components/Icon';
  12. import Switch from '../../../../components/Switch';
  13. import Select from '../../../../components/Select';
  14. import { Button } from '../../../../components/Button';
  15. import AnswerSelect from '../../../../components/AnswerSelect';
  16. import AnswerList from '../../../../components/AnswerList';
  17. import AnswerButton from '../../../../components/AnswerButton';
  18. import AnswerTable from '../../../../components/AnswerTable';
  19. import OtherAnswer from '../../../../components/OtherAnswer';
  20. import { AskTarget } from '../../../../../Constant';
  21. import { Question } from '../../../../stores/question';
  22. import { My } from '../../../../stores/my';
  23. import Sentence from '../../process/sentence';
  24. export default class extends Component {
  25. constructor(props) {
  26. super(props);
  27. this.state = {
  28. step: 0,
  29. hideAnalysis: true,
  30. analysisTab: 'official',
  31. showAnswer: false,
  32. noteField: AskTarget[0].key,
  33. showIds: false,
  34. };
  35. }
  36. prevQuestion() {
  37. const { userQuestion } = this.props;
  38. if (userQuestion.no === 1) return;
  39. Question.getDetailByNo(userQuestion.reportId, userQuestion.no - 1).then(r => {
  40. linkTo(`/paper/question/${r.id}`);
  41. });
  42. }
  43. nextQuestion() {
  44. const { userQuestion } = this.props;
  45. if (userQuestion.questionNumber === userQuestion.no) return;
  46. Question.getDetailByNo(userQuestion.reportId, userQuestion.no + 1).then(r => {
  47. linkTo(`/paper/question/${r.id}`);
  48. });
  49. }
  50. submitAsk() {
  51. const { userQuestion, questionNo = {} } = this.props;
  52. const { ask = {} } = this.state;
  53. if (!ask.originContent || !ask.content || !ask.target) return;
  54. My.addQuestionAsk(userQuestion.id, ask.target, questionNo.id, ask.originContent, ask.content).then(() => {
  55. this.setState({ askModal: false, askOkModal: true, ask: {} });
  56. }).catch(err => {
  57. this.setState({ askError: err.message, ask: {} });
  58. });
  59. }
  60. submitFeedbackError() {
  61. const { questionNo = {} } = this.props;
  62. const { feedback = {} } = this.state;
  63. if (!feedback.originContent || !feedback.content || !feedback.target) return;
  64. My.addFeedbackErrorQuestion(
  65. questionNo.id,
  66. questionNo.title,
  67. feedback.target,
  68. feedback.originContent,
  69. feedback.content,
  70. )
  71. .then(() => {
  72. this.setState({ feedbackModal: false, feedbackOkModal: true, feedback: {} });
  73. })
  74. .catch(err => {
  75. this.setState({ feedbackError: err.message, feedback: {} });
  76. });
  77. }
  78. submitNote(close) {
  79. const { questionNo = {} } = this.props;
  80. const { note = {} } = this.state;
  81. My.updateQuestionNote(questionNo.id, note)
  82. .then(() => {
  83. if (close) this.setState({ noteModal: false });
  84. })
  85. .catch(err => {
  86. this.setState({ noteError: err.message });
  87. });
  88. }
  89. toggleFullscreen() {
  90. const { isFullscreenEnabled } = this.state;
  91. this.setState({ isFullscreenEnabled: !isFullscreenEnabled });
  92. }
  93. toggleCollect() {
  94. const { userQuestion = {}, questionNo = {}, flow } = this.props;
  95. if (!userQuestion.collect) {
  96. My.addQuestionCollect(questionNo.id).then(() => {
  97. userQuestion.collect = true;
  98. flow.setState({ userQuestion });
  99. });
  100. } else {
  101. My.delQuestionCollect(questionNo.id).then(() => {
  102. userQuestion.collect = false;
  103. flow.setState({ userQuestion });
  104. });
  105. }
  106. }
  107. switchNo(no) {
  108. linkTo(`/question/detail/${no.id}`);
  109. }
  110. formatStem(text) {
  111. if (!text) return '';
  112. const { showAnswer, question = { content: {} }, userQuestion } = this.props;
  113. const { table = {}, questions = [] } = question.content;
  114. text = text.replace(/#select#/g, "<span class='#select#' />");
  115. text = text.replace(/#table#/g, "<span class='#table#' />");
  116. setTimeout(() => {
  117. const selectList = document.getElementsByClassName('#select#');
  118. const tableList = document.getElementsByClassName('#table#');
  119. for (let i = 0; i < selectList.length; i += 1) {
  120. if (!questions[i]) break;
  121. ReactDOM.render(
  122. <AnswerSelect
  123. list={questions[i].select}
  124. type={'single'}
  125. selected={(userQuestion.userAnswer || { questions: [] }).questions[i]}
  126. answer={(question.answer || { questions: [] }).questions[i]}
  127. fix
  128. show={showAnswer}
  129. />,
  130. selectList[i],
  131. );
  132. }
  133. if (table.row && table.col && table.header) {
  134. const columns = table.header.map((title, index) => {
  135. return { title, key: index };
  136. });
  137. for (let i = 0; i < tableList.length; i += 1) {
  138. ReactDOM.render(<AnswerTable list={columns} columns={columns} data={table.data} />, tableList[i]);
  139. }
  140. }
  141. }, 1);
  142. return text;
  143. }
  144. formatOtherStem(question) {
  145. if (!question.stem) return '';
  146. const { content = {}, stem } = question;
  147. const { table = {}, questions = [] } = content;
  148. let text = stem.replace(/#select#/g, `<span class='#select#${question.id}' />`);
  149. text = text.replace(/#table#/g, `<span class='#table#${question.id}' />`);
  150. setTimeout(() => {
  151. const selectList = document.getElementsByClassName(`#select#${question.id}`);
  152. const tableList = document.getElementsByClassName(`#table#${question.id}`);
  153. for (let i = 0; i < selectList.length; i += 1) {
  154. if (!questions[i]) break;
  155. ReactDOM.render(
  156. <AnswerSelect
  157. list={questions[i].select}
  158. type={'single'}
  159. // selected={(userQuestion.userAnswer || { questions: [] }).questions[i]}
  160. // answer={(question.answer || { questions: [] }).questions[i]}
  161. fix
  162. // show={showAnswer}
  163. />,
  164. selectList[i],
  165. );
  166. }
  167. if (table.row && table.col && table.header) {
  168. const columns = table.header.map((title, index) => {
  169. return { title, key: index };
  170. });
  171. for (let i = 0; i < tableList.length; i += 1) {
  172. ReactDOM.render(<AnswerTable list={columns} columns={columns} data={table.data} />, tableList[i]);
  173. }
  174. }
  175. }, 1);
  176. return text;
  177. }
  178. render() {
  179. return (
  180. <Fullscreen
  181. enabled={this.state.isFullscreenEnabled}
  182. onChange={isFullscreenEnabled => this.setState({ isFullscreenEnabled })}
  183. >
  184. {this.renderDetail()}
  185. </Fullscreen>
  186. );
  187. }
  188. renderDetail() {
  189. const { paper = {} } = this.props;
  190. switch (paper.paperModule) {
  191. case 'sentence':
  192. return <Sentence {...this.props} {...this.state} flow={this} scene="answer" mode="question" />;
  193. default:
  194. return <div className="base">{this.renderBase()}</div>;
  195. }
  196. }
  197. renderHeader() {
  198. const {
  199. userQuestion = {},
  200. questionNo = {},
  201. paper = {},
  202. report = {},
  203. questionNos = [],
  204. question = {},
  205. info,
  206. detail,
  207. } = this.props;
  208. const { showIds } = this.state;
  209. return (
  210. <div className={'layout-header'}>
  211. {detail && (
  212. <div className="left">
  213. {paper.paperModule && paper.paperModule !== 'examination' && (
  214. <div className="btn">
  215. <Button
  216. radius
  217. onClick={() => {
  218. linkTo(`/paper/report/${report.id}`);
  219. }}
  220. >
  221. 返回练习报告
  222. </Button>
  223. </div>
  224. )}
  225. {paper.paperModule && paper.paperModule === 'examination' && (
  226. <div className="btn">
  227. <Button
  228. radius
  229. onClick={() => {
  230. linkTo(`/paper/report/${report.id}`);
  231. }}
  232. >
  233. 返回成绩单
  234. </Button>
  235. </div>
  236. )}
  237. <div className="no">No.{userQuestion.stageNo || userQuestion.no}</div>
  238. <div className="title">
  239. <Assets name="book" />
  240. {paper.title}
  241. </div>
  242. </div>
  243. )}
  244. <div className="center">
  245. <div className="menu-wrap">
  246. ID:{questionNo.title}
  247. {questionNos && questionNos.length > 0 && (
  248. <Icon
  249. name="other"
  250. onClick={() => {
  251. this.setState({ showIds: true });
  252. }}
  253. />
  254. )}
  255. {showIds && (
  256. <div className="menu-content">
  257. <p>题源汇总</p>
  258. {(questionNos || []).map(row => (
  259. <p onClick={() => info && this.switchNo(row)}>ID:{row.title}</p>
  260. ))}
  261. </div>
  262. )}
  263. </div>
  264. </div>
  265. <div className="right" hidden={question.questionType === 'awa'}>
  266. {detail && (
  267. <span className="b" hidden={!userQuestion.id}>
  268. 用时:
  269. <span
  270. dangerouslySetInnerHTML={{
  271. __html: formatSeconds(userQuestion.userTime).replace(
  272. /([0-9]+)(m|min|h|hour|s)/g,
  273. '<span class="s">$1</span>$2',
  274. ),
  275. }}
  276. />
  277. {/* 用时:<span className="s">1</span>m<span className="s">39</span>s */}
  278. </span>
  279. )}
  280. <span className="b">
  281. 全站:
  282. <span
  283. dangerouslySetInnerHTML={{
  284. __html: formatSeconds(questionNo.totalTime / questionNo.totalNumber).replace(
  285. /([0-9]+)(m|min|h|hour|s)/g,
  286. '<span class="s">$1</span>$2',
  287. ),
  288. }}
  289. />
  290. {/* 全站:<span className="s">1</span>m<span className="s">39</span>s */}
  291. </span>
  292. <span className="b">
  293. <span className="s">{formatPercent(questionNo.totalCorrect, questionNo.totalNumber)}</span>%
  294. </span>
  295. <Icon name="question c-p" />
  296. <Icon name="star" active={userQuestion.collect} onClick={() => this.toggleCollect()} />
  297. </div>
  298. </div>
  299. );
  300. }
  301. renderBase() {
  302. const { questionStatus, userQuestion = {}, paper = {}, detail } = this.props;
  303. const { showIds } = this.state;
  304. return (
  305. <div
  306. className={`layout ${paper.paperModule}`}
  307. onClick={() => {
  308. if (showIds) this.setState({ showIds: false });
  309. }}
  310. >
  311. {this.renderHeader()}
  312. <div className="layout-body">{this.renderBody()}</div>
  313. <div className="layout-footer">
  314. <div className="left">
  315. <Tooltip overlayClassName="gray" placement="top" title="全屏">
  316. <a>
  317. <Icon
  318. name={this.state.isFullscreenEnabled ? 'sceen-restore' : 'sceen-full'}
  319. onClick={() => this.toggleFullscreen()}
  320. />
  321. </a>
  322. </Tooltip>
  323. </div>
  324. <div className="center">
  325. <AnswerButton className="item" onClick={() => this.setState({ noteModal: true })}>
  326. 笔记
  327. </AnswerButton>
  328. {questionStatus >= 0 && (
  329. <AnswerButton
  330. className="item"
  331. onClick={() => {
  332. if (questionStatus > 0) {
  333. this.setState({ askModal: true, ask: { target: AskTarget[0].value } });
  334. } else {
  335. this.setState({ askFailModal: true });
  336. }
  337. }}
  338. >
  339. 提问
  340. </AnswerButton>
  341. )}
  342. <AnswerButton className="item" onClick={() => this.setState({ feedbackModal: true, feedback: { position: AskTarget[0].value } })}>
  343. 纠错
  344. </AnswerButton>
  345. </div>
  346. {detail && (
  347. <div className="right">
  348. {userQuestion.no !== 1 && <Icon name="prev" onClick={() => this.prevQuestion()} />}
  349. {userQuestion.questionNumber !== userQuestion.no && (
  350. <Icon name="next" onClick={() => this.nextQuestion()} />
  351. )}
  352. </div>
  353. )}
  354. </div>
  355. {this.state.askModal && this.renderAsk()}
  356. {this.state.askOkModal && this.renderAskOk()}
  357. {this.state.askFailModal && this.renderAskFail()}
  358. {this.state.feedbackModal && this.renderFeedbackError()}
  359. {this.state.feedbackOkModal && this.renderFeedbackErrorOk()}
  360. {this.state.noteModal && this.renderNote()}
  361. </div>
  362. );
  363. }
  364. renderBody() {
  365. const { question = { content: {} } } = this.props;
  366. const { typeset = 'one' } = question.content;
  367. const { hideAnalysis } = this.state;
  368. const show = typeset === 'one' ? true : !hideAnalysis;
  369. return (
  370. <div className="layout-content">
  371. <div className="two">
  372. {this.renderContent()}
  373. {question.questionType !== 'awa' && this.renderAnswer()}
  374. {question.questionType === 'awa' && this.renderAWA()}
  375. </div>
  376. {question.questionType !== 'awa' && this.renderAnalysis()}
  377. {typeset === 'two' && question.questionType !== 'awa' && (
  378. <div className="fixed-analysis" onClick={() => this.setState({ hideAnalysis: !hideAnalysis })}>
  379. {show ? '收起解析 >' : '查看解析 <'}
  380. </div>
  381. )}
  382. </div>
  383. );
  384. }
  385. renderAnalysis() {
  386. const { question = { content: {} } } = this.props;
  387. const { typeset = 'one' } = question.content;
  388. const { hideAnalysis, analysisTab } = this.state;
  389. const show = typeset === 'one' ? true : !hideAnalysis;
  390. return (<div className="block">
  391. <div className={`block-analysis two-analysis ${show ? 'show' : ''}`}>
  392. <Tabs
  393. type="division"
  394. active={analysisTab}
  395. space={2}
  396. tabs={[
  397. { key: 'official', name: '官方解析' },
  398. { key: 'qx', name: '千行解析' },
  399. { key: 'association', name: '题源联想' },
  400. { key: 'qa', name: '相关回答' },
  401. ]}
  402. onChange={key => {
  403. this.setState({ analysisTab: key });
  404. }}
  405. />
  406. <div className="detail">
  407. {typeset === 'two' && this.renderAnswer()}
  408. {this.renderText()}
  409. </div>
  410. </div></div>
  411. );
  412. }
  413. renderText() {
  414. const { question = {}, userQuestion = {} } = this.props;
  415. const { asks = [], associations = [] } = userQuestion;
  416. const { analysisTab } = this.state;
  417. let content;
  418. switch (analysisTab) {
  419. case 'official':
  420. content = (
  421. <div className="detail-block "><div className="block-text" dangerouslySetInnerHTML={{ __html: question.officialContent }} /></div>
  422. );
  423. break;
  424. case 'qx':
  425. content = <div className="detail-block "><div className="block-text" dangerouslySetInnerHTML={{ __html: question.qxContent }} /></div>;
  426. break;
  427. case 'association':
  428. content = (
  429. <div className="detail-block">
  430. <Carousel>
  431. {associations.map(association => {
  432. return <div className="block-text" dangerouslySetInnerHTML={{ __html: this.formatOtherStem(association) }} />;
  433. })}
  434. </Carousel>
  435. </div>
  436. );
  437. break;
  438. case 'qa':
  439. content = (
  440. <div className="detail-block ">
  441. <div className="block-answer">
  442. {asks.map((ask, index) => {
  443. return <OtherAnswer key={index} data={ask} />;
  444. })}
  445. </div>
  446. </div>
  447. );
  448. break;
  449. default:
  450. break;
  451. }
  452. return content;
  453. }
  454. renderAnswer() {
  455. const { question = { content: {} }, userQuestion = {}, detail } = this.props;
  456. const { questions = [], type, typeset = 'one' } = question.content;
  457. const { showAnswer } = this.state;
  458. return (<div className="block">
  459. <div className="block-answer">
  460. {detail && typeset === 'two' && (
  461. <Switch
  462. checked={showAnswer}
  463. onChange={value => {
  464. this.setState({ showAnswer: value });
  465. }}
  466. >
  467. {showAnswer ? '显示答案' : '关闭答案'}
  468. </Switch>
  469. )}
  470. {questions.map((item, index) => {
  471. return (
  472. <div>
  473. <div className="text m-b-2">{item.description}</div>
  474. <AnswerList
  475. show={showAnswer}
  476. selected={(userQuestion.userAnswer || { questions: [] }).questions[index]}
  477. answer={(question.answer || { questions: [] }).questions[index]}
  478. distributed={(question.answerDistributed || { questions: [] }).questions[index]}
  479. list={item.select}
  480. type={type}
  481. first={item.first}
  482. second={item.second}
  483. direction={item.direction}
  484. />
  485. </div>
  486. );
  487. })}
  488. </div></div>
  489. );
  490. }
  491. renderContent() {
  492. const { question = { content: {} }, detail } = this.props;
  493. const { typeset = 'one' } = question.content;
  494. const { steps = [] } = question.content;
  495. const { showAnswer, step } = this.state;
  496. return (<div className="block">
  497. <div className="block-content">
  498. {detail && typeset === 'one' && question.questionType !== 'awa' && (
  499. <Switch
  500. checked={showAnswer}
  501. onChange={value => {
  502. this.setState({ showAnswer: value });
  503. }}
  504. >
  505. {showAnswer ? '显示答案' : '关闭答案'}
  506. </Switch>
  507. )}
  508. {question.questionType === 'awa' && <h2>Analytical Writing Assessment</h2>}
  509. {steps.length > 0 && (
  510. <Navigation
  511. theme="detail"
  512. list={question.content.steps}
  513. active={step}
  514. onChange={v => this.setState({ step: v })}
  515. />
  516. )}
  517. <div
  518. className="text"
  519. dangerouslySetInnerHTML={{ __html: this.formatStem(steps.length > 0 ? steps[step].stem : question.stem) }}
  520. />
  521. </div></div>
  522. );
  523. }
  524. renderAWA() {
  525. const { userQuestion = { detail: {}, userAnswer: {} } } = this.state;
  526. const { showAnswer } = this.state;
  527. return (<div className="block">
  528. <div className="block-awa">
  529. <Switch
  530. checked={showAnswer}
  531. onChange={value => {
  532. this.setState({ showAnswer: value });
  533. }}
  534. >
  535. {showAnswer ? '显示答案' : '关闭答案'}
  536. </Switch>
  537. <div className="body">
  538. <h2>Your Response</h2>
  539. {showAnswer && (
  540. <div className="detail">
  541. <div className="info">
  542. <span className="b">
  543. 用时:
  544. <span
  545. dangerouslySetInnerHTML={{
  546. __html: formatSeconds(userQuestion.userTime).replace(
  547. /([0-9]+)(m|min|h|hour|s)/g,
  548. '<span class="s">$1</span>$2',
  549. ),
  550. }}
  551. />
  552. {/* 用时:<span className="s">1</span>m<span className="s">39</span>s */}
  553. </span>
  554. <span className="b">
  555. 单词数:<span className="s">{Number((userQuestion.detail || {}).words || 0)}</span>词
  556. </span>
  557. </div>
  558. <div className="content-awa" dangerouslySetInnerHTML={{ __html: userQuestion.userAnswer.awa || '' }} />
  559. </div>
  560. )}
  561. {!showAnswer && <div className="show-awa">选择「显示答案」查看自己的作文</div>}
  562. </div>
  563. </div></div>
  564. );
  565. }
  566. renderAsk() {
  567. const { ask = {} } = this.state;
  568. return (
  569. <div className="modal ask">
  570. <div className="mask" />
  571. <div className="body">
  572. <div className="title">提问</div>
  573. <div className="desc">
  574. <div className="select-inline">
  575. 我想对
  576. <Select
  577. excludeSelf
  578. size="small"
  579. theme="white"
  580. value={ask.target}
  581. list={AskTarget}
  582. onChange={item => {
  583. ask.target = item.value;
  584. this.setState({ ask });
  585. }}
  586. />
  587. 进行提问
  588. </div>
  589. <div className="label">有疑问的具体内容是:</div>
  590. <textarea
  591. className="textarea"
  592. value={ask.originContent}
  593. placeholder="请复制粘贴有疑问的内容。"
  594. onChange={e => {
  595. ask.originContent = e.target.value;
  596. this.setState({ ask });
  597. }}
  598. />
  599. <div className="label">针对以上内容的问题是:</div>
  600. <textarea
  601. className="textarea"
  602. value={ask.content}
  603. placeholder="提问频率高的问题会被优先回答哦。"
  604. onChange={e => {
  605. ask.content = e.target.value;
  606. this.setState({ ask });
  607. }}
  608. />
  609. </div>
  610. <div className="bottom">
  611. <AnswerButton theme="cancel" size="lager" onClick={() => this.setState({ askModal: false })}>
  612. 取消
  613. </AnswerButton>
  614. <AnswerButton size="lager" onClick={() => this.submitAsk()}>
  615. 提交
  616. </AnswerButton>
  617. </div>
  618. </div>
  619. </div>
  620. );
  621. }
  622. renderAskOk() {
  623. return (
  624. <div className="modal ask-ok">
  625. <div className="mask" />
  626. <div className="body">
  627. <div className="title">提问</div>
  628. <div className="content">
  629. <div className="left">
  630. <div className="text">已提交成功!</div>
  631. <div className="text">关注公众号,老师回答后会立即收到通知。</div>
  632. <div className="text">我们也会通过站内信的方式通知你。</div>
  633. <div className="small">
  634. 成为学员享受极速答疑特权。<Link to="">了解更多</Link>
  635. </div>
  636. </div>
  637. <div className="right">
  638. <Assets name="qrcode" />
  639. <div className="text">扫码关注公众号</div>
  640. <div className="text">千行GMAT</div>
  641. </div>
  642. </div>
  643. <div className="confirm">
  644. <AnswerButton
  645. size="lager"
  646. theme="confirm"
  647. onClick={() => {
  648. this.setState({ askOkModal: false });
  649. }}
  650. >
  651. 好的,知道了
  652. </AnswerButton>
  653. </div>
  654. </div>
  655. </div>
  656. );
  657. }
  658. renderAskFail() {
  659. return (
  660. <div className="modal ask-ok">
  661. <div className="mask" />
  662. <div className="body">
  663. <div className="title">提问</div>
  664. <div className="content">
  665. <div className="left">
  666. <div className="text">提问功能正在维护中。</div>
  667. <div className="text">可先查阅“相关问答” 或 成为学员享受极速 答疑特权。</div>
  668. <Link to="/">了解更多></Link>
  669. </div>
  670. <div className="right">
  671. <Assets name="qrcode" />
  672. <div className="text">扫码关注公众号</div>
  673. <div className="text">千行GMAT</div>
  674. </div>
  675. </div>
  676. <div className="confirm">
  677. <AnswerButton
  678. size="lager"
  679. theme="confirm"
  680. onClick={() => {
  681. this.setState({ askFailModal: false });
  682. }}
  683. >
  684. 好的,知道了
  685. </AnswerButton>
  686. </div>
  687. </div>
  688. </div>
  689. );
  690. }
  691. renderFeedbackError() {
  692. const { feedback = {} } = this.state;
  693. return (
  694. <div className="modal error">
  695. <div className="mask" />
  696. <div className="body">
  697. <div className="title">纠错</div>
  698. <div className="desc">
  699. <div className="select-inline">
  700. 我想对
  701. <Select
  702. excludeSelf
  703. size="small"
  704. theme="white"
  705. value={feedback.target}
  706. list={AskTarget}
  707. onChange={item => {
  708. feedback.target = item.value;
  709. this.setState({ feedback });
  710. }}
  711. />
  712. 进行提问
  713. </div>
  714. <div className="label">错误内容是:</div>
  715. <textarea
  716. className="textarea"
  717. value={feedback.originContent}
  718. placeholder="你可以适当扩大复制范围以使我们准确定位,感谢。"
  719. onChange={(e) => {
  720. feedback.originContent = e.target.value;
  721. this.setState({ feedback });
  722. }}
  723. />
  724. <div className="label">应该改为:</div>
  725. <textarea
  726. className="textarea"
  727. value={feedback.content}
  728. placeholder="只需提供正确内容即可"
  729. onChange={(e) => {
  730. feedback.content = e.target.value;
  731. this.setState({ feedback });
  732. }} />
  733. </div>
  734. <div className="bottom">
  735. <AnswerButton
  736. theme="cancel"
  737. size="lager"
  738. onClick={() => {
  739. this.setState({ feedbackModal: false });
  740. }}
  741. >
  742. 取消
  743. </AnswerButton>
  744. <AnswerButton
  745. size="lager"
  746. onClick={() => {
  747. this.submitFeedbackError();
  748. }}
  749. >
  750. 提交
  751. </AnswerButton>
  752. </div>
  753. </div>
  754. </div>
  755. );
  756. }
  757. renderFeedbackErrorOk() {
  758. return (
  759. <div className="modal error-ok">
  760. <div className="mask" />
  761. <div className="body">
  762. <div className="title">纠错</div>
  763. <div className="content">
  764. <div className="left">
  765. <div className="text">
  766. <Assets name="right" svg />
  767. 已提交成功!
  768. </div>
  769. <div className="text">感谢您的耐心反馈,我们会尽快核实并以站内信的方式告知结果。</div>
  770. <div className="text">您也可以关注公众号及时获取结果。</div>
  771. </div>
  772. <div className="right">
  773. <Assets name="qrcode" />
  774. <div className="text">扫码关注公众号</div>
  775. <div className="text">千行GMAT</div>
  776. </div>
  777. </div>
  778. <div className="confirm">
  779. <AnswerButton
  780. size="lager"
  781. theme="confirm"
  782. onClick={() => {
  783. this.setState({ feedbackOkModal: false });
  784. }}
  785. >
  786. 好的,知道了
  787. </AnswerButton>
  788. </div>
  789. </div>
  790. </div>
  791. );
  792. }
  793. renderNote() {
  794. const { noteField, note = {} } = this.state;
  795. return (
  796. <div className="modal note">
  797. <div className="mask" />
  798. <div className="body">
  799. <div className="title">笔记</div>
  800. <div className="content">
  801. <div className="tabs">
  802. {AskTarget.map(item => {
  803. return (
  804. <div
  805. className={`tab ${noteField === item.key ? 'active' : ''}`}
  806. onClick={() => {
  807. this.setState({ noteField: item.key });
  808. }}
  809. >
  810. <div className="text">{item.label}</div>
  811. <div className="date">{note[`${item.key}Time`] ? formatDate(note[`${item.key}Time`]) : ''}</div>
  812. </div>
  813. );
  814. })}
  815. </div>
  816. <div className="input">
  817. <textarea
  818. className="textarea"
  819. value={note[`${noteField}Content`] || ''}
  820. placeholder="记下笔记,方便以后复习"
  821. onChange={e => {
  822. note[`${noteField}Time`] = new Date();
  823. note[`${noteField}Content`] = e.target.value;
  824. this.setState({ note });
  825. }}
  826. />
  827. <div className="bottom">
  828. <AnswerButton
  829. theme="cancel"
  830. size="lager"
  831. onClick={() => {
  832. this.setState({ noteModal: false });
  833. }}
  834. >
  835. 取消
  836. </AnswerButton>
  837. <AnswerButton
  838. size="lager"
  839. onClick={() => {
  840. this.submitNote();
  841. }}
  842. >
  843. 编辑
  844. </AnswerButton>
  845. <AnswerButton
  846. size="lager"
  847. onClick={() => {
  848. this.submitNote(true);
  849. }}
  850. >
  851. 保存
  852. </AnswerButton>
  853. </div>
  854. </div>
  855. </div>
  856. </div>
  857. </div>
  858. );
  859. }
  860. }