index.js 33 KB

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