index.js 32 KB

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