index.js 32 KB

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