index.js 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331
  1. import React, { Component } from 'react';
  2. import './index.less';
  3. import Assets from '@src/components/Assets';
  4. import { formatSecond, formatPercent, formatSeconds } from '@src/services/Tools';
  5. import Icon from '../../../../components/Icon';
  6. import Button from '../../../../components/Button';
  7. import Switch from '../../../../components/Switch';
  8. import Tabs from '../../../../components/Tabs';
  9. import Progress from '../../../../components/Progress';
  10. import HardInput from '../../../../components/HardInput';
  11. import AnswerCheckbox from '../../../../components/AnswerCheckbox';
  12. import { SentenceOption } from '../../../../../Constant';
  13. import { Question } from '../../../../stores/question';
  14. export default class extends Component {
  15. constructor(props) {
  16. super(props);
  17. // 确保可以自身进行答案显示,外部也可以直接显示答案
  18. // 将props转入state
  19. this.state = {
  20. analysisTab: 'qx',
  21. focusKey: 'subject',
  22. scene: this.props.scene || 'answer',
  23. userQuestion: this.props.userQuestion,
  24. question: this.props.question,
  25. };
  26. const { question, userQuestion } = this.props;
  27. if (this.state.scene === 'answer') {
  28. this.state.stem = this.formatStem(question.stem, userQuestion.userAnswer, question.answer);
  29. } else {
  30. this.state.stem = question.stem;
  31. }
  32. }
  33. showAnswer() {
  34. const { userQuestion } = this.state;
  35. Question.getDetailById(userQuestion.id).then(result => {
  36. const { question } = result;
  37. this.setState({
  38. userQuestion: result,
  39. question: result.question,
  40. scene: 'answer',
  41. stem: this.formatStem(question.stem, result.userAnswer, question.answer),
  42. });
  43. });
  44. }
  45. addTarget(target) {
  46. const uuid = target.getAttribute('uuid');
  47. if (!uuid) return;
  48. const text = target.innerText;
  49. const { focusKey, answer = {}, question } = this.state;
  50. if (!answer[focusKey]) answer[focusKey] = [];
  51. if (answer[focusKey].filter(row => row.uuid === uuid).length > 0) return;
  52. answer[focusKey].push({
  53. text,
  54. uuid,
  55. });
  56. this.setState({
  57. answer,
  58. stem: this.formatStem(question.stem, answer),
  59. });
  60. }
  61. removeTarget(key, target) {
  62. const { answer = {}, question } = this.state;
  63. if (!answer[key]) return;
  64. answer[key] = answer[key].filter(row => row.uuid !== target.uuid);
  65. this.setState({
  66. answer,
  67. stem: this.formatStem(question.stem, answer),
  68. });
  69. }
  70. next() {
  71. const { flow } = this.props;
  72. const { scene } = this.state;
  73. if (scene === 'question') {
  74. const { answer } = this.state;
  75. flow.submit(answer).then(() => {
  76. this.showAnswer();
  77. });
  78. } else if (scene === 'answer') {
  79. flow.next();
  80. }
  81. }
  82. formatStem(stem, userAnswer, answer) {
  83. // userAnswer 添加蓝色字, 错误的添加红色背景
  84. // answer 添加绿色背景
  85. const uuidMap = {};
  86. const show = !!answer;
  87. answer = answer || {};
  88. userAnswer = userAnswer || {};
  89. Object.keys(userAnswer).forEach((key) => {
  90. if (key === 'options') return;
  91. const u = userAnswer[key];
  92. const a = answer[key] && answer[key].length > 0 ? answer[key][0] : [];
  93. const map = {};
  94. a.forEach((row) => {
  95. if (!uuidMap[row.uuid]) uuidMap[row.uuid] = [];
  96. uuidMap[row.uuid].push('true');
  97. map[row.uuid] = row;
  98. });
  99. u.forEach((row) => {
  100. if (!uuidMap[row.uuid]) uuidMap[row.uuid] = [];
  101. uuidMap[row.uuid].push('user');
  102. if (show && !map[row.uuid]) uuidMap[row.uuid].push('false');
  103. });
  104. });
  105. Object.keys(uuidMap).forEach(uuid => {
  106. stem = stem.replace(`uuid='${uuid}'`, `uuid='${uuid}' class='${uuidMap[uuid].join(' ')}'`);
  107. });
  108. return stem;
  109. }
  110. renderHeader() {
  111. const { mode } = this.props;
  112. switch (mode) {
  113. case 'process':
  114. return this.renderProcessHeader();
  115. default:
  116. return this.renderQuestionHeader();
  117. }
  118. }
  119. renderProcessHeader() {
  120. const { userQuestion, singleTime, paper, flow } = this.props;
  121. return <div className="layout-header">
  122. <div className="left">
  123. <div className="title">{paper.title}</div>
  124. </div>
  125. <div className="right">
  126. <div className="text"><Assets name='timecost_icon' />Time cost {formatSecond(userQuestion.userTime || singleTime)}</div>
  127. <Icon name="star" active={userQuestion.collect} onClick={() => flow.toggleCollect()} />
  128. </div>
  129. </div>;
  130. }
  131. renderQuestionHeader() {
  132. const { userQuestion, questionNo, paper, flow } = this.props;
  133. return <div className="layout-header">
  134. <div className="left">
  135. <div className="title">{paper.title}</div>
  136. </div>
  137. <div className="right">
  138. <span className="b">
  139. 用时:<span dangerouslySetInnerHTML={{ __html: formatSeconds(userQuestion.userTime).replace(/([0-9]+)([msh])/g, '<span class="s">$1</span>$2') }} />
  140. {/* 用时:<span className="s">1</span>m<span className="s">39</span>s */}
  141. </span>
  142. <span className="b">
  143. 全站:<span dangerouslySetInnerHTML={{ __html: formatSeconds(questionNo.totalTime / questionNo.totalNumber).replace(/([0-9]+)([msh])/g, '<span class="s">$1</span>$2') }} />
  144. {/* 全站:<span className="s">1</span>m<span className="s">39</span>s */}
  145. </span>
  146. <span className="b">
  147. <span className="s">{formatPercent(questionNo.totalCorrect, questionNo.totalNumber)}</span>%
  148. </span>
  149. <Icon name="star" active={userQuestion.collect} onClick={() => flow.toggleCollect()} />
  150. </div>
  151. </div>;
  152. }
  153. render() {
  154. const { flow, paper, userQuestion } = this.props;
  155. return (
  156. <div id='paper-process-sentence'>
  157. <div className="layout">
  158. {this.renderHeader()}
  159. {this.renderBody()}
  160. <div className="layout-footer">
  161. <div className="left">
  162. <Icon name={this.props.isFullscreenEnabled ? 'sceen-restore' : 'sceen-full'} onClick={() => flow.toggleFullscreen()} />
  163. </div>
  164. <div className="center">
  165. <div className="p">
  166. <Progress theme="theme" progress={formatPercent(userQuestion.no, paper.questionNumber)} />
  167. </div>
  168. <div className="t">{userQuestion.no}/{paper.questionNumber}</div>
  169. </div>
  170. <div className="right">
  171. <Button size="lager" radius onClick={() => {
  172. this.next();
  173. }}>
  174. Next <Assets name="next_icon" />
  175. </Button>
  176. </div>
  177. </div>
  178. </div>
  179. </div>
  180. );
  181. }
  182. renderBody() {
  183. const { scene } = this.state;
  184. switch (scene) {
  185. case 'question':
  186. return this.renderQuestion();
  187. case 'answer':
  188. return this.renderAnswer();
  189. default:
  190. return null;
  191. }
  192. }
  193. renderQuestion() {
  194. const { focusKey, answer = {}, stem } = this.state;
  195. return (
  196. <div className="layout-body">
  197. <div className="title"><Icon name="question" />请分别找出句子中的主语,谓语和宾语,并做出逻辑关系判断。</div>
  198. <div className="desc" dangerouslySetInnerHTML={{ __html: stem }} onClick={(e) => {
  199. this.addTarget(e.target);
  200. }} />
  201. <div className="label">主语</div>
  202. <div className="input">
  203. <HardInput
  204. focus={focusKey === 'subject'}
  205. list={answer.subject || []}
  206. onClick={() => {
  207. this.setState({ focusKey: 'subject' });
  208. }}
  209. onDelete={(item) => {
  210. this.removeTarget('subject', item);
  211. }}
  212. />
  213. </div>
  214. <div className="label">谓语</div>
  215. <div className="input">
  216. <HardInput
  217. focus={focusKey === 'predicate'}
  218. list={answer.predicate || []}
  219. onClick={() => {
  220. this.setState({ focusKey: 'predicate' });
  221. }}
  222. onDelete={(item) => {
  223. this.removeTarget('predicate', item);
  224. }}
  225. />
  226. </div>
  227. <div className="label">宾语</div>
  228. <div className="input">
  229. <HardInput
  230. focus={focusKey === 'object'}
  231. list={answer.object || []}
  232. onClick={() => {
  233. this.setState({ focusKey: 'object' });
  234. }}
  235. onDelete={(item) => {
  236. this.removeTarget('object', item);
  237. }}
  238. />
  239. </div>
  240. <div className="select">
  241. <div className="select-title">本句存在以下哪种逻辑关系?(可多选)</div>
  242. <AnswerCheckbox list={SentenceOption} selected={answer.options} onChange={(values) => {
  243. answer.options = values;
  244. this.setState({ answer });
  245. }} />
  246. </div>
  247. </div>
  248. );
  249. }
  250. renderAnswer() {
  251. const { mode } = this.props;
  252. const { analysisTab, question, userQuestion, stem, showAnswer } = this.state;
  253. const { userAnswer = {} } = userQuestion;
  254. const { answer } = question;
  255. return <div className="layout-body">
  256. {mode === 'question' ? <Switch checked={showAnswer} onChange={(value) => {
  257. this.setState({ showAnswer: value });
  258. }}>{showAnswer ? '显示答案' : '关闭答案'}</Switch> : <div className="title"><Icon name="question" />请分别找出句子中的主语,谓语和宾语,并做出逻辑关系判断。</div>}
  259. <div className="desc" dangerouslySetInnerHTML={{ __html: stem }} />
  260. <div className="label">主语</div>
  261. <div className="input">
  262. <HardInput
  263. show={showAnswer}
  264. list={userAnswer.subject || []}
  265. answer={answer.subject}
  266. />
  267. </div>
  268. <div className="label">谓语</div>
  269. <div className="input">
  270. <HardInput
  271. show={showAnswer}
  272. list={userAnswer.predicate || []}
  273. answer={answer.predicate}
  274. />
  275. </div>
  276. <div className="label">宾语</div>
  277. <div className="input">
  278. <HardInput
  279. show={showAnswer}
  280. list={userAnswer.object || []}
  281. answer={answer.object}
  282. />
  283. </div>
  284. <div className="select">
  285. <div className="select-title">本句存在以下哪种逻辑关系?(可多选)</div>
  286. <AnswerCheckbox show={showAnswer} list={SentenceOption} selected={userAnswer.options} answer={answer.options} />
  287. </div>
  288. {showAnswer && <div className="analysis">
  289. <Tabs
  290. type="division"
  291. active={analysisTab}
  292. tabs={[{ key: 'qx', name: '解析详情' }, { key: 'chinese', name: '中文语意' }]}
  293. onChange={(key) => {
  294. this.setState({ analysisTab: key });
  295. }}
  296. />
  297. {this.renderText()}
  298. </div>}
  299. </div>;
  300. }
  301. renderText() {
  302. const { analysisTab, question = {}, questionNo = {} } = this.state;
  303. let content;
  304. switch (analysisTab) {
  305. case 'chinese':
  306. content = <div className="detail-block text-block" dangerouslySetInnerHTML={{ __html: questionNo.chineseContent }} />;
  307. break;
  308. case 'qx':
  309. content = <div className="detail-block text-block" dangerouslySetInnerHTML={{ __html: question.qxContent }} />;
  310. break;
  311. default:
  312. break;
  313. }
  314. return content;
  315. }
  316. }