index.js 31 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924
  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: {} }, detail } = this.props;
  366. const { typeset = 'one' } = question.content;
  367. const { hideAnalysis, showAnswer } = this.state;
  368. const show = typeset === 'one' ? true : !hideAnalysis;
  369. return (
  370. <div className="layout-content">
  371. <div className={typeset}>
  372. {this.renderContent()}
  373. {typeset === 'two' && (
  374. <div className="block">
  375. <div className="block-answer">
  376. {detail && (
  377. <Switch
  378. checked={showAnswer}
  379. onChange={value => {
  380. this.setState({ showAnswer: value });
  381. }}
  382. >
  383. {showAnswer ? '显示答案' : '关闭答案'}
  384. </Switch>
  385. )}
  386. {this.renderAnswer()}
  387. </div>
  388. </div>
  389. )}
  390. {question.questionType === 'awa' && this.renderAWA()}
  391. </div>
  392. {question.questionType !== 'awa' && this.renderAnalysis()}
  393. {typeset === 'two' && question.questionType !== 'awa' && (
  394. <div className="fixed-analysis" onClick={() => this.setState({ hideAnalysis: !hideAnalysis })}>
  395. {show ? '收起解析 >' : '查看解析 <'}
  396. </div>
  397. )}
  398. </div>
  399. );
  400. }
  401. renderAnalysis() {
  402. const { question = { content: {} }, detail } = this.props;
  403. const { typeset = 'one' } = question.content;
  404. const { hideAnalysis, analysisTab } = this.state;
  405. const show = typeset === 'one' ? true : !hideAnalysis;
  406. const { showAnswer } = this.state;
  407. return (<div className="block">
  408. <div className={`block-analysis two-analysis ${show ? 'show' : ''}`}>
  409. <Tabs
  410. type="division"
  411. active={analysisTab}
  412. space={2}
  413. tabs={[
  414. { key: 'official', name: '官方解析' },
  415. { key: 'qx', name: '千行解析' },
  416. { key: 'association', name: '题源联想' },
  417. { key: 'qa', name: '相关回答' },
  418. ]}
  419. onChange={key => {
  420. this.setState({ analysisTab: key });
  421. }}
  422. />
  423. <div className="detail">
  424. {typeset === 'two' && (
  425. <div className="block">
  426. <div className="block-answer">
  427. {detail && <Switch
  428. checked={showAnswer}
  429. onChange={value => {
  430. this.setState({ showAnswer: value });
  431. }}
  432. >
  433. {showAnswer ? '显示答案' : '关闭答案'}
  434. </Switch>
  435. }
  436. {this.renderAnswer()}
  437. </div>
  438. </div>
  439. )}
  440. {this.renderText()}
  441. </div>
  442. </div></div>
  443. );
  444. }
  445. renderText() {
  446. const { question = {}, userQuestion = {} } = this.props;
  447. const { asks = [], associations = [] } = userQuestion;
  448. const { analysisTab } = this.state;
  449. let content;
  450. switch (analysisTab) {
  451. case 'official':
  452. content = (
  453. <div className="detail-block "><div className="block-text" dangerouslySetInnerHTML={{ __html: question.officialContent }} /></div>
  454. );
  455. break;
  456. case 'qx':
  457. content = <div className="detail-block "><div className="block-text" dangerouslySetInnerHTML={{ __html: question.qxContent }} /></div>;
  458. break;
  459. case 'association':
  460. content = (
  461. <div className="detail-block">
  462. <Carousel>
  463. {associations.map(association => {
  464. const { questions = [], type } = association.content || {};
  465. return <div className="block-text">
  466. <div dangerouslySetInnerHTML={{ __html: this.formatOtherStem(association) }} />
  467. {questions.map((item, index) => {
  468. return (
  469. <div>
  470. <div className="text m-b-2" dangerouslySetInnerHTML={{ __html: item.description }} />
  471. <AnswerList
  472. answer={(question.answer || { questions: [] }).questions[index]}
  473. list={item.select}
  474. type={type}
  475. first={item.first}
  476. second={item.second}
  477. direction={item.direction}
  478. />
  479. </div>
  480. );
  481. })}
  482. </div>;
  483. })}
  484. </Carousel>
  485. </div>
  486. );
  487. break;
  488. case 'qa':
  489. content = (
  490. <div className="detail-block ">
  491. <div className="block-answer">
  492. {asks.map((ask, index) => {
  493. return <OtherAnswer key={index} data={ask} />;
  494. })}
  495. </div>
  496. </div>
  497. );
  498. break;
  499. default:
  500. break;
  501. }
  502. return content;
  503. }
  504. renderAnswer() {
  505. const { question = { content: {} }, userQuestion = {} } = this.props;
  506. const { questions = [], type } = question.content;
  507. const { showAnswer } = this.state;
  508. return questions.map((item, index) => {
  509. return (
  510. <div>
  511. <div className="text m-b-2" dangerouslySetInnerHTML={{ __html: item.description }} />
  512. <AnswerList
  513. show={showAnswer}
  514. selected={(userQuestion.userAnswer || { questions: [] }).questions[index]}
  515. answer={(question.answer || { questions: [] }).questions[index]}
  516. distributed={(question.answerDistributed || { questions: [] }).questions[index]}
  517. list={item.select}
  518. type={type}
  519. first={item.first}
  520. second={item.second}
  521. direction={item.direction}
  522. />
  523. </div>
  524. );
  525. });
  526. }
  527. renderContent() {
  528. const { question = { content: {} }, detail } = this.props;
  529. const { typeset = 'one' } = question.content;
  530. const { steps = [] } = question.content;
  531. const { showAnswer, step } = this.state;
  532. return (<div className="block">
  533. <div className="block-content">
  534. {detail && typeset === 'one' && question.questionType !== 'awa' && (
  535. <Switch
  536. checked={showAnswer}
  537. onChange={value => {
  538. this.setState({ showAnswer: value });
  539. }}
  540. >
  541. {showAnswer ? '显示答案' : '关闭答案'}
  542. </Switch>
  543. )}
  544. {question.questionType === 'awa' && <h2>Analytical Writing Assessment</h2>}
  545. {steps.length > 0 && (
  546. <Navigation
  547. theme="detail"
  548. list={question.content.steps}
  549. active={step}
  550. onChange={v => this.setState({ step: v })}
  551. />
  552. )}
  553. <div
  554. className="text"
  555. dangerouslySetInnerHTML={{ __html: this.formatStem(steps.length > 0 ? steps[step].stem : question.stem) }}
  556. />
  557. {typeset === 'one' && question.questionType !== 'awa' && this.renderAnswer()}
  558. </div></div>
  559. );
  560. }
  561. renderAWA() {
  562. const { userQuestion = { detail: {}, userAnswer: {} } } = this.state;
  563. const { showAnswer } = this.state;
  564. return (<div className="block">
  565. <div className="block-awa">
  566. <Switch
  567. checked={showAnswer}
  568. onChange={value => {
  569. this.setState({ showAnswer: value });
  570. }}
  571. >
  572. {showAnswer ? '显示答案' : '关闭答案'}
  573. </Switch>
  574. <div className="body">
  575. <h2>Your Response</h2>
  576. {showAnswer && (
  577. <div className="detail">
  578. <div className="info">
  579. <span className="b">
  580. 用时:
  581. <span
  582. dangerouslySetInnerHTML={{
  583. __html: formatSeconds(userQuestion.userTime).replace(
  584. /([0-9]+)(m|min|h|hour|s)/g,
  585. '<span class="s">$1</span>$2',
  586. ),
  587. }}
  588. />
  589. {/* 用时:<span className="s">1</span>m<span className="s">39</span>s */}
  590. </span>
  591. <span className="b">
  592. 单词数:<span className="s">{Number((userQuestion.detail || {}).words || 0)}</span>词
  593. </span>
  594. </div>
  595. <div className="content-awa" dangerouslySetInnerHTML={{ __html: userQuestion.userAnswer.awa || '' }} />
  596. </div>
  597. )}
  598. {!showAnswer && <div className="show-awa">选择「显示答案」查看自己的作文</div>}
  599. </div>
  600. </div></div>
  601. );
  602. }
  603. renderAsk() {
  604. const { ask = {} } = this.state;
  605. return (
  606. <div className="modal ask">
  607. <div className="mask" />
  608. <div className="body">
  609. <div className="title">提问</div>
  610. <div className="desc">
  611. <div className="select-inline">
  612. 我想对
  613. <Select
  614. excludeSelf
  615. size="small"
  616. theme="white"
  617. value={ask.target}
  618. list={AskTarget}
  619. onChange={item => {
  620. ask.target = item.value;
  621. this.setState({ ask });
  622. }}
  623. />
  624. 进行提问
  625. </div>
  626. <div className="label">有疑问的具体内容是:</div>
  627. <textarea
  628. className="textarea"
  629. value={ask.originContent}
  630. placeholder="请复制粘贴有疑问的内容。"
  631. onChange={e => {
  632. ask.originContent = e.target.value;
  633. this.setState({ ask });
  634. }}
  635. />
  636. <div className="label">针对以上内容的问题是:</div>
  637. <textarea
  638. className="textarea"
  639. value={ask.content}
  640. placeholder="提问频率高的问题会被优先回答哦。"
  641. onChange={e => {
  642. ask.content = e.target.value;
  643. this.setState({ ask });
  644. }}
  645. />
  646. </div>
  647. <div className="bottom">
  648. <AnswerButton theme="cancel" size="lager" onClick={() => this.setState({ askModal: false })}>
  649. 取消
  650. </AnswerButton>
  651. <AnswerButton size="lager" onClick={() => this.submitAsk()}>
  652. 提交
  653. </AnswerButton>
  654. </div>
  655. </div>
  656. </div>
  657. );
  658. }
  659. renderAskOk() {
  660. return (
  661. <div className="modal ask-ok">
  662. <div className="mask" />
  663. <div className="body">
  664. <div className="title">提问</div>
  665. <div className="content">
  666. <div className="left">
  667. <div className="text">已提交成功!</div>
  668. <div className="text">关注公众号,老师回答后会立即收到通知。</div>
  669. <div className="text">我们也会通过站内信的方式通知你。</div>
  670. <div className="small">
  671. 成为学员享受极速答疑特权。<Link to="">了解更多</Link>
  672. </div>
  673. </div>
  674. <div className="right">
  675. <Assets name="qrcode" />
  676. <div className="text">扫码关注公众号</div>
  677. <div className="text">千行GMAT</div>
  678. </div>
  679. </div>
  680. <div className="confirm">
  681. <AnswerButton
  682. size="lager"
  683. theme="confirm"
  684. onClick={() => {
  685. this.setState({ askOkModal: false });
  686. }}
  687. >
  688. 好的,知道了
  689. </AnswerButton>
  690. </div>
  691. </div>
  692. </div>
  693. );
  694. }
  695. renderAskFail() {
  696. return (
  697. <div className="modal ask-ok">
  698. <div className="mask" />
  699. <div className="body">
  700. <div className="title">提问</div>
  701. <div className="content">
  702. <div className="left">
  703. <div className="text">提问功能正在维护中。</div>
  704. <div className="text">可先查阅“相关问答” 或 成为学员享受极速 答疑特权。</div>
  705. <Link to="/">了解更多></Link>
  706. </div>
  707. <div className="right">
  708. <Assets name="qrcode" />
  709. <div className="text">扫码关注公众号</div>
  710. <div className="text">千行GMAT</div>
  711. </div>
  712. </div>
  713. <div className="confirm">
  714. <AnswerButton
  715. size="lager"
  716. theme="confirm"
  717. onClick={() => {
  718. this.setState({ askFailModal: false });
  719. }}
  720. >
  721. 好的,知道了
  722. </AnswerButton>
  723. </div>
  724. </div>
  725. </div>
  726. );
  727. }
  728. renderFeedbackError() {
  729. const { feedback = {} } = this.state;
  730. return (
  731. <div className="modal error">
  732. <div className="mask" />
  733. <div className="body">
  734. <div className="title">纠错</div>
  735. <div className="desc">
  736. <div className="select-inline">
  737. 我想对
  738. <Select
  739. excludeSelf
  740. size="small"
  741. theme="white"
  742. value={feedback.target}
  743. list={AskTarget}
  744. onChange={item => {
  745. feedback.target = item.value;
  746. this.setState({ feedback });
  747. }}
  748. />
  749. 进行提问
  750. </div>
  751. <div className="label">错误内容是:</div>
  752. <textarea
  753. className="textarea"
  754. value={feedback.originContent}
  755. placeholder="你可以适当扩大复制范围以使我们准确定位,感谢。"
  756. onChange={(e) => {
  757. feedback.originContent = e.target.value;
  758. this.setState({ feedback });
  759. }}
  760. />
  761. <div className="label">应该改为:</div>
  762. <textarea
  763. className="textarea"
  764. value={feedback.content}
  765. placeholder="只需提供正确内容即可"
  766. onChange={(e) => {
  767. feedback.content = e.target.value;
  768. this.setState({ feedback });
  769. }} />
  770. </div>
  771. <div className="bottom">
  772. <AnswerButton
  773. theme="cancel"
  774. size="lager"
  775. onClick={() => {
  776. this.setState({ feedbackModal: false });
  777. }}
  778. >
  779. 取消
  780. </AnswerButton>
  781. <AnswerButton
  782. size="lager"
  783. onClick={() => {
  784. this.submitFeedbackError();
  785. }}
  786. >
  787. 提交
  788. </AnswerButton>
  789. </div>
  790. </div>
  791. </div>
  792. );
  793. }
  794. renderFeedbackErrorOk() {
  795. return (
  796. <div className="modal error-ok">
  797. <div className="mask" />
  798. <div className="body">
  799. <div className="title">纠错</div>
  800. <div className="content">
  801. <div className="left">
  802. <div className="text">
  803. <Assets name="right" svg />
  804. 已提交成功!
  805. </div>
  806. <div className="text">感谢您的耐心反馈,我们会尽快核实并以站内信的方式告知结果。</div>
  807. <div className="text">您也可以关注公众号及时获取结果。</div>
  808. </div>
  809. <div className="right">
  810. <Assets name="qrcode" />
  811. <div className="text">扫码关注公众号</div>
  812. <div className="text">千行GMAT</div>
  813. </div>
  814. </div>
  815. <div className="confirm">
  816. <AnswerButton
  817. size="lager"
  818. theme="confirm"
  819. onClick={() => {
  820. this.setState({ feedbackOkModal: false });
  821. }}
  822. >
  823. 好的,知道了
  824. </AnswerButton>
  825. </div>
  826. </div>
  827. </div>
  828. );
  829. }
  830. renderNote() {
  831. const { noteField, note = {} } = this.state;
  832. return (
  833. <div className="modal note">
  834. <div className="mask" />
  835. <div className="body">
  836. <div className="title">笔记</div>
  837. <div className="content">
  838. <div className="tabs">
  839. {AskTarget.map(item => {
  840. return (
  841. <div
  842. className={`tab ${noteField === item.key ? 'active' : ''}`}
  843. onClick={() => {
  844. this.setState({ noteField: item.key });
  845. }}
  846. >
  847. <div className="text">{item.label}</div>
  848. <div className="date">{note[`${item.key}Time`] ? formatDate(note[`${item.key}Time`]) : ''}</div>
  849. </div>
  850. );
  851. })}
  852. </div>
  853. <div className="input">
  854. <textarea
  855. className="textarea"
  856. value={note[`${noteField}Content`] || ''}
  857. placeholder="记下笔记,方便以后复习"
  858. onChange={e => {
  859. note[`${noteField}Time`] = new Date();
  860. note[`${noteField}Content`] = e.target.value;
  861. this.setState({ note });
  862. }}
  863. />
  864. <div className="bottom">
  865. <AnswerButton
  866. theme="cancel"
  867. size="lager"
  868. onClick={() => {
  869. this.setState({ noteModal: false });
  870. }}
  871. >
  872. 取消
  873. </AnswerButton>
  874. <AnswerButton
  875. size="lager"
  876. onClick={() => {
  877. this.submitNote();
  878. }}
  879. >
  880. 编辑
  881. </AnswerButton>
  882. <AnswerButton
  883. size="lager"
  884. onClick={() => {
  885. this.submitNote(true);
  886. }}
  887. >
  888. 保存
  889. </AnswerButton>
  890. </div>
  891. </div>
  892. </div>
  893. </div>
  894. </div>
  895. );
  896. }
  897. }