123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618 |
- import React, { Component } from 'react';
- import ReactDOM from 'react-dom';
- import './index.less';
- import { Checkbox, Icon as AntDIcon, Tooltip } from 'antd';
- import Assets from '@src/components/Assets';
- import { formatSeconds, formatMinuteSecond, getMap } from '@src/services/Tools';
- import Icon from '../../../../components/Icon';
- import Button from '../../../../components/Button';
- import Navigation from '../../../../components/Navigation';
- import Answer from '../../../../components/Answer';
- import Calculator from '../../../../components/Calculator';
- import AnswerSelect from '../../../../components/AnswerSelect';
- import AnswerTable from '../../../../components/AnswerTable';
- import Editor from '../../../../components/Editor';
- import { QuestionType, ExaminationOrder } from '../../../../../Constant';
- const QuestionTypeMap = getMap(QuestionType, 'value');
- export default class extends Component {
- constructor(props) {
- super(props);
- this.state = {
- showCalculator: false,
- disorder: false,
- order: [],
- step: 0,
- answer: {},
- modal: null,
- };
- }
- onChangeQuestion(index, value) {
- const { question } = this.props;
- const { content } = question;
- const { answer = {} } = this.state;
- const type = content.type === 'double' ? 'double' : 'single';
- if (!answer.questions) {
- answer.questions = content.questions.map(() => {
- return {};
- });
- }
- answer.questions[index] = { [type]: value };
- console.log(answer);
- this.setState({ answer });
- }
- onChangeAwa(value) {
- const { answer = {} } = this.state;
- answer.awa = value;
- this.setState({ answer });
- }
- showConfirm(title, desc, width, cb) {
- this.showModal('confirm', title, desc, width, cb);
- }
- showToast(title, desc, width, cb) {
- this.showModal('toast', title, desc, width, cb);
- }
- showModal(type, title, desc, width, cb) {
- this.setState({ modal: { type, title, desc, width, cb } });
- }
- timeOut(cb) {
- this.showToast('Time Expired', 'Time has expired for this section. \n Click OK to continue.', 440, cb);
- }
- checkAnswer() {
- const { question } = this.props;
- const { content } = question;
- const { answer } = this.state;
- let title = null;
- let desc = null;
- let width = null;
- if (question.questionType === 'awa') {
- if (!answer.awa) {
- desc = 'Please answer the question first.';
- }
- } else {
- let flag = false;
- if (!answer || !answer.questions) {
- flag = true;
- } else if (content.type === 'double') {
- answer.questions.forEach((row, index) => {
- if (flag) return;
- if (!row.double) {
- flag = true;
- return;
- }
- const { direction } = content.questions[index];
- switch (direction) {
- case 'landscape':
- flag = row.double.filter(r => (r || []).filter(rr => rr).length > 0).length < row.double.length;
- break;
- case 'portrait':
- flag = [0, 1].map(r => row.double.filter(rr => rr && rr[r]).length > 0) < 2;
- break;
- default:
- }
- });
- } else if (content.type === 'single') {
- answer.questions.forEach((row) => {
- if (flag) return;
- if (!row.single) {
- flag = true;
- return;
- }
- flag = row.single.filter(r => r).length === 0;
- });
- }
- if (flag) {
- title = 'Answer Required';
- desc = 'You can not continue with this question unanswered. ';
- width = 430;
- }
- }
- if (title || desc) return this.showToast(title, desc, width);
- return true;
- }
- hideModal(b) {
- if (b) {
- const { modal = {} } = this.state;
- if (modal.cb) modal.cb();
- }
- this.setState({ modal: null });
- }
- formatStrem(text) {
- if (!text) return '';
- const { question = { content: {} } } = this.props;
- const { table = {}, questions = [] } = question.content;
- text = text.replace(/#select#/g, "<span class='#select#' />");
- text = text.replace(/#table#/g, "<span class='#table#' />");
- setTimeout(() => {
- const selectList = document.getElementsByClassName('#select#');
- const tableList = document.getElementsByClassName('#table#');
- for (let i = 0; i < selectList.length; i += 1) {
- if (!questions[i]) break;
- ReactDOM.render(
- <AnswerSelect list={questions[i].select} type={'single'} onChange={v => this.onChangeQuestion(i, v)} />,
- selectList[i],
- );
- }
- if (table.row && table.col && table.header) {
- const columns = table.header.map((title, index) => {
- return { title, key: index };
- });
- for (let i = 0; i < tableList.length; i += 1) {
- ReactDOM.render(<AnswerTable list={columns} columns={columns} data={table.data} />, tableList[i]);
- }
- }
- }, 1);
- return text;
- }
- showHelp() {
- this.setState({ showHelp: true });
- }
- next() {
- const { flow } = this.props;
- const { answer } = this.state;
- if (this.checkAnswer()) {
- this.showConfirm('Answer Confirmation', 'Click Yes to confirm your answer and continue to the next \n question. ', 560, () => {
- flow.submit(answer).then(() => {
- return flow.next();
- });
- });
- }
- }
- stage() {
- const { flow } = this.props;
- flow.relaxToNextStage();
- }
- render() {
- const { modal, showHelp } = this.state;
- const { scene, paper } = this.props;
- let content = null;
- switch (scene) {
- case 'start':
- content = paper.paperModule === 'examination' ? this.renderExaminationStart() : this.renderExerciseStart();
- break;
- case 'relax':
- content = this.renderRelax();
- break;
- default:
- content = this.renderDetail();
- break;
- }
- return (
- <div id="paper-process-base">
- {content}
- {modal ? this.renderModal() : ''}
- {showHelp ? this.renderHelp() : ''}
- </div>
- );
- }
- renderContent() {
- const { question = { content: {} } } = this.props;
- const { step } = this.state;
- const { steps = [], typeset = 'one', type } = question.content;
- return (
- <div className="block block-content">
- {steps.length > 0 && (
- <Navigation
- theme="process"
- list={question.content.steps}
- active={step}
- onChange={v => this.setState({ step: v })}
- />
- )}
- <div
- className="text"
- style={{ paddingBottom: typeset === 'one' && type !== 'inline' ? '' : '100px' }}
- dangerouslySetInnerHTML={{ __html: this.formatStrem(steps.length > 0 ? steps[step].stem : question.stem) }}
- />
- </div>
- );
- }
- renderAnswer() {
- const { question = { content: {} } } = this.props;
- const { questions = [], type } = question.content;
- if (type === 'inline') return '';
- return (
- <div className="block block-answer">
- {question.questionType === 'awa' && <Editor onChange={v => this.onChangeAwa(v)} />}
- {questions.map((item, index) => {
- return (
- <div>
- <div className="text m-b-2" dangerouslySetInnerHTML={{ __html: item.description }} />
- <Answer
- list={item.select}
- type={type}
- first={item.first}
- second={item.second}
- direction={item.direction}
- onChange={v => this.onChangeQuestion(index, v)}
- />
- </div>
- );
- })}
- </div>
- );
- }
- renderDetail() {
- const { paper, userQuestion, question = { content: {} }, totalTime, stageTime, totalNumber, flow, showTime, showNo } = this.props;
- if (!userQuestion.id) return null;
- const { showCalculator } = this.state;
- const { typeset = 'one' } = question.content;
- return (
- <div className="layout">
- <div className="fixed">
- {QuestionTypeMap[question.questionType].long}
- {question.questionType === 'ir' && (
- <Assets
- className="calculator-icon"
- name="calculator_icon"
- onClick={() => this.setState({ showCalculator: !showCalculator })}
- />
- )}
- {/* <Assets className="collect-icon" name="collect_icon" onClick={() => {
- flow.toggleCollect();
- }} /> */}
- <div className="collect-icon">
- <Icon name="star" active={userQuestion.collect} onClick={() => flow.toggleCollect()} />
- </div>
- </div>
- <Calculator show={showCalculator} />
- <div className="layout-header">
- <div className="title">{paper.title}</div>
- <div className="right">
- <div
- className="block"
- onClick={() => {
- flow.switchTime();
- }}
- >
- <Assets name="timeleft_icon" />
- {showTime && stageTime && `Time left ${formatMinuteSecond(stageTime)}`}
- {showTime && !stageTime && totalTime && `Time cost ${formatMinuteSecond(totalTime)}`}
- </div>
- <div
- className="block"
- onClick={() => {
- flow.switchNo();
- }}
- >
- <Assets name="subjectnumber_icon" />
- {showNo && `${userQuestion.stageNo} of ${totalNumber}`}
- </div>
- </div>
- </div>
- <div className={'layout-body'}>
- <div className={typeset}>
- {this.renderContent()}
- {this.renderAnswer()}
- </div>
- </div>
- <div className="layout-footer">
- <div className="help" onClick={() => this.showHelp()}>
- <Assets name="help_icon" />
- Help
- </div>
- <div className="full">
- <Assets name="fullscreen_icon" onClick={() => flow.toggleFullscreen()} />
- </div>
- <div className="next" onClick={() => this.next()}>
- Next
- <Assets name="next_icon" />
- </div>
- </div>
- </div>
- );
- }
- renderExaminationStart() {
- // const { paper, userQuestion, singleTime, stageTime, flow } = this.props;
- const { paper, flow, startTime, setting, showTime } = this.props;
- const { disorder = true, order } = setting;
- return (
- <div className="layout">
- <div className="fixed" />
- <div className="layout-header">
- <div className="title">{paper.title}</div>
- <div className="right">
- <div
- className="block"
- onClick={() => {
- flow.switchTime();
- }}
- >
- <Assets name="timeleft_icon" />
- {showTime && startTime && `Time left ${formatMinuteSecond(startTime)}`}
- </div>
- {/* <div
- className="block"
- onClick={() => {
- this.setState({ showNo: !showNo });
- }}
- >
- <Assets name="subjectnumber_icon" />
- {showNo && `${userQuestion.no} of ${paper.questionNumber}`}
- </div> */}
- </div>
- </div>
- <div className={'layout-body'}>{paper.isAdapt > 1 ? this.renderExaminationStartCAT() : this.renderExaminationStartDefault()}</div>
- <div className="layout-footer">
- <div className="help" onClick={() => this.showHelp()}>
- <Assets name="help_icon" />
- Help
- </div>
- <div className="full">
- <Assets name="fullscreen_icon" onClick={() => flow.toggleFullscreen()} />
- </div>
- <div className="next" onClick={() => flow.start({ disorder: paper.finishTimes > 0 ? disorder : false, order: order.filter(row => row) })}>
- Next
- <Assets name="next_icon" />
- </div>
- </div>
- </div>
- );
- }
- renderExerciseStart() {
- const { paper, flow, setting } = this.props;
- const { disorder = true } = setting;
- return (
- <div className="start">
- <div className="bg" />
- <div className="fixed-content">
- <div className="title">{paper.title}</div>
- <div className="desc">
- <div className="block">
- <div className="desc-title">
- <Assets name="subject_icon" />
- 题目总数
- </div>
- <div className="desc-info">{paper.questionNumber}</div>
- </div>
- <div className="block">
- <div className="desc-title">
- <Assets name="time_icon" />
- 建议用时
- </div>
- <div className="desc-info">{formatSeconds(paper.time)}</div>
- </div>
- </div>
- {paper.finishTimes > 0 && <div className="tip">
- <Checkbox className="m-r-1" checked={disorder} onChange={() => flow.setSetting({ disorder: !disorder })} />
- 题目选项乱序显示
- </div>}
- <div className="submit">
- <Button size="lager" radius onClick={() => flow.start({ disorder: paper.finishTimes > 0 ? disorder : false })}>
- 开始练习
- </Button>
- </div>
- </div>
- </div>
- );
- }
- renderExaminationStartCAT() {
- const { paper, flow, setting } = this.props;
- const { disorder = true, order = [], orderIndex } = setting;
- return (
- <div className="exercise-start default">
- <div className="title">Section Ordering</div>
- <div className="desc">Select the order in which the exam sections are to be administered.</div>
- <div className="desc tip">
- NOTE: You have 1 minute to make your selection. If you do not make your selection within 1 minute, the first
- option listed will be selected and you will view the exam in the following order: Analytical Writing
- Assessment, Integrated Reasoning, Quantitative, Verbal.
- </div>
- <div className="desc">
- Once you select your section order, you must view ALL questions in each section, in the order you have
- selected, before moving on to the next section. You will NOT be able to return to this screen.
- </div>
- <div className="block-list">
- {ExaminationOrder.map((row, index) => {
- return <div className="block-item">
- <div className="block-title" onClick={() => {
- flow.setSetting({ order: row.value, orderIndex: index });
- }}>
- <div className="block-title-border">
- {orderIndex === index && <AntDIcon type="check" />}
- <span>{row.label}</span>
- </div>
- </div>
- {row.list.map((r, i) => {
- return <div className="block-text">{i + 1}.{r.label} </div>;
- })}
- </div>;
- })}
- </div>
- <div className="bottom">
- {paper.finishTimes > 0 && <div className="text">
- <Checkbox checked={disorder} onChange={() => flow.setSetting({ disorder: !disorder })} /> 题目选项乱序显示
- </div>}
- <div className="text">
- Click{' '}
- <div className="next" onClick={() => flow.start({ disorder: paper.finishTimes > 0 ? disorder : false, order: order.filter(row => row) })}>
- Next
- <Assets name="next_icon" />
- </div>{' '}
- button to start the exam. You will begin the GMAT exam on the next screen.{' '}
- </div>
- </div>
- </div>
- );
- }
- renderExaminationStartDefault() {
- const { paper, flow, setting } = this.props;
- const { disorder = true, order = [], orderIndex } = setting;
- const tmp = order.filter(row => row);
- return (
- <div className="exercise-start cat">
- <div className="title">Section Ordering</div>
- <div className="block-list">
- {ExaminationOrder.map((row, index) => {
- return <div className="block-item" onClick={() => {
- flow.setSetting({ order: row.value, orderIndex: index });
- }}>
- <div className={orderIndex === index ? 'block-item-body active' : 'block-item-body'}>
- {orderIndex === index && <AntDIcon type="check" style={{ color: '#fff' }} />}
- {row.list.map((r, i) => {
- return <div className="block-text" onClick={() => {
- if (order.indexOf(r.value) >= 0) {
- // 取消
- if (tmp.length > 1) {
- order[i] = '';
- }
- } else {
- // 选中
- order[i] = r.value;
- }
- console.log(order);
- flow.setSetting({ order });
- }}>
- <Checkbox checked={orderIndex === index ? order.indexOf(r.value) >= 0 : false} /> {r.label}{' '}
- </div>;
- })}
- </div>
- </div>;
- })}
- </div>
- <div className="bottom">
- {paper.finishTimes > 0 && <div className="text">
- <Checkbox checked={disorder} onChange={() => flow.setSetting({ disorder: !disorder })} /> 题目选项乱序显示
- </div>}
- <div className="text">
- Click{' '}
- <div className="next" onClick={() => flow.start({ disorder: paper.finishTimes > 0 ? disorder : false, order: order.filter(row => row) })}>
- Next
- <Assets name="next_icon" />
- </div>{' '}
- button to start the exam. You will begin the GMAT exam on the next screen.{' '}
- </div>
- <div className="text tip">*实战可选择考试顺序但无法选择考试内容。</div>
- </div>
- </div>
- );
- }
- renderRelax() {
- const { paper, stageTime, flow, showTime } = this.props;
- return (
- <div className="layout">
- <div className="layout-header">
- <div className="title">{paper.title}</div>
- <div className="right">
- <div
- className="block"
- onClick={() => {
- flow.switchTime();
- }}
- >
- <Assets name="timeleft_icon" />
- {showTime && stageTime && `Time left ${formatMinuteSecond(stageTime)}`}
- {/* {showTime && singleTime && `Time cost ${formatMinuteSecond(singleTime)}`} */}
- </div>
- {/* <div
- className="block"
- onClick={() => {
- this.setState({ showNo: !showNo });
- }}
- >
- <Assets name="subjectnumber_icon" />
- {showNo && `${userQuestion.no} of ${paper.questionNumber}`}
- </div> */}
- </div>
- </div>
- <div className={'layout-body'}>
- <div className="relax">
- <div className="title">
- Optional Break <Tooltip title={'点击「Next」可继续考试'} trigger="click"><Icon name="question" /></Tooltip>
- </div>
- <div className="time" dangerouslySetInnerHTML={{ __html: formatMinuteSecond(stageTime).split(':').map(row => row.replace(/([0-9])/g, '<div class="block">$1</div>')).join('<div class="div">:</div>') }} />
- </div>
- </div>
- <div className="layout-footer">
- <div className="help" onClick={() => this.showHelp()}>
- <Assets name="help_icon" />
- Help
- </div>
- <div className="full">
- <Assets name="fullscreen_icon" onClick={() => flow.toggleFullscreen()} />
- </div>
- <div className="next" onClick={() => this.stage()}>
- Next
- <Assets name="next_icon" />
- </div>
- </div>
- </div>
- );
- }
- renderModal() {
- const { modal } = this.state;
- return (
- <div className="modal">
- <div className="mask" />
- <div style={{ width: modal.width }} className="body">
- <div className="title">{modal.title}</div>
- <div className="desc">{modal.desc}</div>
- {modal.type === 'confirm' && (
- <div className="btn-list">
- <div className="btn" onClick={() => this.hideModal(true)}>
- <span className="t-d-l">Y</span>es
- </div>
- <div className="btn" onClick={() => this.hideModal(false)}>
- <span className="t-d-l">N</span>o
- </div>
- </div>
- )}
- {modal.type === 'toast' && (<div className="btn-list">
- <div className="btn" onClick={() => this.hideModal(true)}>
- <span className="t-d-l">O</span>k
- </div>
- </div>)}
- </div>
- </div>
- );
- }
- renderHelp() {
- return (
- <div className="modal show-help">
- <div className="mask" />
- <div style={{ width: 540 }} className="body">
- <div className="title">
- <div className="help">
- <Assets name="help_icon" />Help
- </div>
- <div className="close"><Assets name="close" onClick={() => this.setState({ showHelp: false })} /></div>
- </div>
- <div className="desc">
- <ul>
- <li>点击右上角的时钟图标<div className="icon"><Assets name="timeleft_icon" /></div>可以打开和关闭计时器(与实战一致);</li>
- <li>点击右上角的进度条<div className="icon"><Assets name="subjectnumber_icon" /></div>可以打开和关闭当前进度(与实战一致);</li>
- <li>点击右下角<div className="next">Next<Assets name="next_icon" /></div>进入下一题(与实战一致);</li>
- <li>点击右上角的<div className="icon"><Icon name="star" /></div>可以收藏本题,可至“个人中心-收藏” 查看。</li>
- </ul>
- </div>
- </div>
- </div>
- );
- }
- }
|