index.js 32 KB

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