index.js 33 KB

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