index.js 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349
  1. import React, { Component } from 'react';
  2. import ReactDOM from 'react-dom';
  3. import './index.less';
  4. import { Checkbox } from 'antd';
  5. import Assets from '@src/components/Assets';
  6. import { formatSeconds, formatSecond, getMap } from '@src/services/Tools';
  7. import Icon from '../../../../components/Icon';
  8. import Button from '../../../../components/Button';
  9. import Navigation from '../../../../components/Navigation';
  10. import Answer from '../../../../components/Answer';
  11. import Calculator from '../../../../components/Calculator';
  12. import AnswerSelect from '../../../../components/AnswerSelect';
  13. import AnswerTable from '../../../../components/AnswerTable';
  14. import Editor from '../../../../components/Editor';
  15. import { QuestionType } from '../../../../../Constant';
  16. const QuestionTypeMap = getMap(QuestionType, 'value');
  17. export default class extends Component {
  18. constructor(props) {
  19. super(props);
  20. this.state = {
  21. showTime: true,
  22. showNo: true,
  23. showCalculator: false,
  24. disorder: false,
  25. order: [],
  26. step: 0,
  27. answer: {},
  28. modal: null,
  29. };
  30. }
  31. onChangeQuestion(index, value) {
  32. const { question } = this.props;
  33. const { content } = question;
  34. const { answer = {} } = this.state;
  35. const type = content.type === 'double' ? 'double' : 'single';
  36. if (!answer.questions) {
  37. answer.questions = content.questions.map(() => {
  38. return {};
  39. });
  40. }
  41. answer.questions[index] = { [type]: value };
  42. console.log(answer);
  43. this.setState({ answer });
  44. }
  45. onChangeAwa(value) {
  46. const { answer = {} } = this.state;
  47. answer.awa = value;
  48. this.setState({ answer });
  49. }
  50. showConfirm(title, desc, cb) {
  51. this.showModal('confirm', title, desc, cb);
  52. }
  53. showToast(title, desc, cb) {
  54. this.showModal('toast', title, desc, cb);
  55. }
  56. showModal(type, title, desc, cb) {
  57. this.setState({ modal: { type, title, desc, cb } });
  58. }
  59. checkAnswer() {
  60. const { question } = this.props;
  61. const { answer } = this.state;
  62. let result = null;
  63. if (question.questionType === 'awa' && !answer.awa) result = 'Please answer the question first.';
  64. if (result) return this.showToast(null, result);
  65. return true;
  66. }
  67. hideModal(b) {
  68. if (b) {
  69. const { modal = {} } = this.state;
  70. if (modal.cb) modal.cb();
  71. }
  72. this.setState({ modal: null });
  73. }
  74. formatStrem(text) {
  75. if (!text) return '';
  76. const { question = { content: {} } } = this.props;
  77. const { table = {}, questions = [] } = question.content;
  78. text = text.replace(/#select#/g, "<span class='#select#' />");
  79. text = text.replace(/#table#/g, "<span class='#table#' />");
  80. setTimeout(() => {
  81. const selectList = document.getElementsByClassName('#select#');
  82. const tableList = document.getElementsByClassName('#table#');
  83. for (let i = 0; i < selectList.length; i += 1) {
  84. if (!questions[i]) break;
  85. ReactDOM.render(
  86. <AnswerSelect list={questions[i].select} type={'single'} onChange={v => this.onChangeQuestion(i, v)} />,
  87. selectList[i],
  88. );
  89. }
  90. if (table.row && table.col && table.header) {
  91. const columns = table.header.map((title, index) => {
  92. return { title, key: index };
  93. });
  94. for (let i = 0; i < tableList.length; i += 1) {
  95. ReactDOM.render(<AnswerTable list={columns} columns={columns} data={table.data} />, tableList[i]);
  96. }
  97. }
  98. }, 1);
  99. return text;
  100. }
  101. next() {
  102. const { flow } = this.props;
  103. const { answer } = this.state;
  104. if (this.checkAnswer()) {
  105. flow.submit(answer)
  106. .then(() => {
  107. flow.next();
  108. });
  109. }
  110. }
  111. render() {
  112. const { modal } = this.state;
  113. const { scene, paper } = this.props;
  114. let content = null;
  115. switch (scene) {
  116. case 'start':
  117. content = paper.paperModule === 'examination' ? this.renderExaminationStart() : this.renderExerciseStart();
  118. break;
  119. case 'relax':
  120. content = this.renderRelax();
  121. break;
  122. default:
  123. content = this.renderDetail();
  124. break;
  125. }
  126. return <div id='paper-process-base'>
  127. {content}
  128. {modal ? this.renderModal() : ''}
  129. </div>;
  130. }
  131. renderContent() {
  132. const { question = { content: {} } } = this.props;
  133. const { step } = this.state;
  134. const { steps = [] } = question.content;
  135. return (
  136. <div className="block block-content">
  137. {steps.length > 0 && <Navigation theme='process' list={question.content.steps} active={step} onChange={(v) => this.setState({ step: v })} />}
  138. <div className="text" dangerouslySetInnerHTML={{ __html: this.formatStrem(steps.length > 0 ? steps[step].stem : question.stem) }} />
  139. </div>
  140. );
  141. }
  142. renderAnswer() {
  143. const { question = { content: {} } } = this.props;
  144. const { questions = [], type } = question.content;
  145. if (type === 'inline') return '';
  146. return (
  147. <div className="block block-answer">
  148. {question.questionType === 'awa' && <Editor onChange={v => this.onChangeAwa(v)} />}
  149. {questions.map((item, index) => {
  150. return (
  151. <div>
  152. <div className="text m-b-2">{item.description}</div>
  153. <Answer
  154. list={item.select}
  155. type={type}
  156. first={item.first}
  157. second={item.second}
  158. direction={item.direction}
  159. onChange={v => this.onChangeQuestion(index, v)}
  160. />
  161. </div>
  162. );
  163. })}
  164. </div>
  165. );
  166. }
  167. renderDetail() {
  168. const { paper, userQuestion, question = { content: {} }, singleTime, stageTime, flow } = this.props;
  169. if (!userQuestion.id) return null;
  170. const { showCalculator, showTime, showNo } = this.state;
  171. const { typeset = 'one' } = question.content;
  172. return (
  173. <div className="layout">
  174. <div className="fixed">
  175. {QuestionTypeMap[question.questionType].long}
  176. {question.questionType === 'ir' && <Assets
  177. className="calculator-icon"
  178. name="calculator_icon"
  179. onClick={() => this.setState({ showCalculator: !showCalculator })}
  180. />}
  181. {/* <Assets className="collect-icon" name="collect_icon" onClick={() => {
  182. flow.toggleCollect();
  183. }} /> */}
  184. <div className="collect-icon"><Icon name="star" active={userQuestion.collect} onClick={() => flow.toggleCollect()} /></div>
  185. </div>
  186. <Calculator show={showCalculator} />
  187. <div className="layout-header">
  188. <div className="title">{paper.title}</div>
  189. <div className="right">
  190. <div className="block" onClick={() => {
  191. this.setState({ showTime: !showTime });
  192. }}>
  193. <Assets name="timeleft_icon" />
  194. {showTime && stageTime && `Time left ${formatSecond(stageTime)}`}
  195. {showTime && singleTime && `Time cost ${formatSecond(singleTime)}`}
  196. </div>
  197. <div className="block" onClick={() => {
  198. this.setState({ showNo: !showNo });
  199. }}>
  200. <Assets name="subjectnumber_icon" />
  201. {showNo && `${userQuestion.no} of ${paper.questionNumber}`}
  202. </div>
  203. </div>
  204. </div>
  205. <div className={'layout-body'}>
  206. <div className={typeset}>
  207. {this.renderContent()}
  208. {this.renderAnswer()}
  209. </div>
  210. </div>
  211. <div className="layout-footer">
  212. <div className="help">
  213. <Assets name="help_icon" />
  214. Help
  215. </div>
  216. <div className="full">
  217. <Assets name="fullscreen_icon" onClick={() => flow.toggleFullscreen()} />
  218. </div>
  219. <div className="next" onClick={() => this.next()}>
  220. Next
  221. <Assets name="next_icon" />
  222. </div>
  223. </div>
  224. </div>
  225. );
  226. }
  227. renderExaminationStart() {
  228. const { disorder } = this.state;
  229. const { paper, flow } = this.props;
  230. return (
  231. <div className="start">
  232. <div className="bg" />
  233. <div className="fixed-content">
  234. <div className="title">{paper.title}</div>
  235. <div className="desc">
  236. <div className="block">
  237. <div className="desc-title">
  238. <Assets name="subject_icon" />
  239. 题目总数
  240. </div>
  241. <div className="desc-info">{paper.questionNumer}</div>
  242. </div>
  243. <div className="block">
  244. <div className="desc-title">
  245. <Assets name="time_icon" />
  246. 建议用时
  247. </div>
  248. <div className="desc-info">{formatSeconds(paper.time)}</div>
  249. </div>
  250. </div>
  251. <div className="tip">
  252. <Checkbox className="m-r-1" checked={disorder} onChange={() => this.setState({ disorder: !disorder })} />
  253. 题目选项乱序显示
  254. </div>
  255. <div className="submit">
  256. <Button size="lager" radius onClick={() => flow.start({ disorder })}>
  257. 开始练习
  258. </Button>
  259. </div>
  260. </div>
  261. </div>
  262. );
  263. }
  264. renderExerciseStart() {
  265. const { disorder } = this.state;
  266. const { paper, flow } = this.props;
  267. return (
  268. <div className="start">
  269. <div className="bg" />
  270. <div className="fixed-content">
  271. <div className="title">{paper.title}</div>
  272. <div className="desc">
  273. <div className="block">
  274. <div className="desc-title">
  275. <Assets name="subject_icon" />
  276. 题目总数
  277. </div>
  278. <div className="desc-info">{paper.questionNumber}</div>
  279. </div>
  280. <div className="block">
  281. <div className="desc-title">
  282. <Assets name="time_icon" />
  283. 建议用时
  284. </div>
  285. <div className="desc-info">{formatSeconds(paper.time)}</div>
  286. </div>
  287. </div>
  288. <div className="tip">
  289. <Checkbox className="m-r-1" checked={disorder} onChange={() => this.setState({ disorder: !disorder })} />
  290. 题目选项乱序显示
  291. </div>
  292. <div className="submit">
  293. <Button size="lager" radius onClick={() => flow.start({ disorder })}>
  294. 开始练习
  295. </Button>
  296. </div>
  297. </div>
  298. </div>
  299. );
  300. }
  301. renderRelax() {
  302. return <div />;
  303. }
  304. renderModal() {
  305. const { modal } = this.state;
  306. return (
  307. <div className="modal">
  308. <div className="mask" />
  309. <div className="body">
  310. <div className="title">{modal.title}</div>
  311. <div className="desc">{modal.desc}</div>
  312. {modal.type === 'confirm' ? (
  313. <div className="btn-list">
  314. <div className="btn" onClick={() => this.hideModal(true)}>
  315. <span className="t-d-l">Y</span>es
  316. </div>
  317. <div className="btn" onClick={() => this.hideModal(false)}>
  318. <span className="t-d-l">N</span>o
  319. </div>
  320. </div>
  321. ) : (<div className="btn-list">
  322. <div className="btn" onClick={() => this.hideModal(true)}>
  323. <span className="t-d-l">O</span>k
  324. </div>
  325. </div>)}
  326. </div>
  327. </div>
  328. );
  329. }
  330. }