123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607 |
- import React, { Component } from 'react';
- import ReactDOM from 'react-dom';
- import { Carousel, Tooltip } from 'antd';
- import { Link } from 'react-router-dom';
- import Fullscreen from 'react-fullscreen-crossbrowser';
- import './index.less';
- import { formatSeconds, formatPercent, formatDate } from '@src/services/Tools';
- import Assets from '@src/components/Assets';
- import Navigation from '../../../../components/Navigation';
- import Tabs from '../../../../components/Tabs';
- import Icon from '../../../../components/Icon';
- import Switch from '../../../../components/Switch';
- import Select from '../../../../components/Select';
- import { Button } from '../../../../components/Button';
- import AnswerSelect from '../../../../components/AnswerSelect';
- import AnswerList from '../../../../components/AnswerList';
- import AnswerButton from '../../../../components/AnswerButton';
- import AnswerTable from '../../../../components/AnswerTable';
- import OtherAnswer from '../../../../components/OtherAnswer';
- import { AskTarget } from '../../../../../Constant';
- import { Question } from '../../../../stores/question';
- import { My } from '../../../../stores/my';
- import Sentence from '../../process/sentence';
- export default class extends Component {
- constructor(props) {
- super(props);
- this.state = {
- step: 0,
- hideAnalysis: true,
- analysisTab: 'official',
- showAnswer: false,
- noteField: AskTarget[0].key,
- showIds: false,
- };
- }
- prevQuestion() {
- const { userQuestion } = this.props;
- if (userQuestion.no === 1) return;
- Question.getDetailByNo(userQuestion.reportId, userQuestion.no - 1).then((r) => {
- linkTo(`/paper/question/${r.id}`);
- });
- }
- nextQuestion() {
- const { userQuestion } = this.props;
- if (userQuestion.questionNumber === userQuestion.no) return;
- Question.getDetailByNo(userQuestion.reportId, userQuestion.no + 1).then((r) => {
- linkTo(`/paper/question/${r.id}`);
- });
- }
- submitAsk() {
- const { userQuestion, questionNo = {} } = this.props;
- const { ask = {} } = this.state;
- if (ask.originContent === '' || ask.content === '' || ask.target === '') return;
- My.addQuestionAsk(userQuestion.id, ask.target, userQuestion.questionModule, questionNo.id, ask.originContent, ask.content).then(() => {
- this.setState({ askModal: false, askOkModal: true });
- }).catch(err => {
- this.setState({ askError: err.message });
- });
- }
- submitFeedbackError() {
- const { userQuestion = {}, questionNo = {} } = this.props;
- const { feedback = {} } = this.state;
- if (feedback.originContent === '' || feedback.content === '' || feedback.target === '') return;
- My.addFeedbackErrorQuestion(userQuestion.questionModule, questionNo.id, questionNo.title, feedback.target, feedback.originContent, feedback.content).then(() => {
- this.setState({ feedbackModal: false, feedbackOkModal: true });
- }).catch(err => {
- this.setState({ feedbackError: err.message });
- });
- }
- submitNote(close) {
- const { userQuestion = {}, questionNo = {} } = this.props;
- const { note = {} } = this.state;
- My.updateQuestionNote(userQuestion.questionModule, questionNo.id, note).then(() => {
- if (close) this.setState({ noteModal: false });
- }).catch(err => {
- this.setState({ noteError: err.message });
- });
- }
- toggleFullscreen() {
- const { isFullscreenEnabled } = this.state;
- this.setState({ isFullscreenEnabled: !isFullscreenEnabled });
- }
- toggleCollect() {
- const { userQuestion = {}, questionNo = {}, flow } = this.props;
- if (!userQuestion.collect) {
- My.addQuestionCollect(userQuestion.questionModule, questionNo.id).then(() => {
- userQuestion.collect = true;
- flow.setState({ userQuestion });
- });
- } else {
- My.delQuestionCollect(userQuestion.questionModule, questionNo.id).then(() => {
- userQuestion.collect = false;
- flow.setState({ userQuestion });
- });
- }
- }
- switchNo(no) {
- linkTo(`/question/detail/${no.id}`);
- }
- formatStem(text) {
- if (!text) return '';
- const { showAnswer, question = { content: {} }, userQuestion } = 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'}
- selected={(userQuestion.userAnswer || { questions: [] }).questions[i]}
- answer={(question.answer || { questions: [] }).questions[i]}
- fix
- show={showAnswer} />,
- 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;
- }
- render() {
- return (
- <Fullscreen
- enabled={this.state.isFullscreenEnabled}
- onChange={isFullscreenEnabled => this.setState({ isFullscreenEnabled })}
- >
- {this.renderDetail()}
- </Fullscreen>
- );
- }
- renderDetail() {
- const { paper = {} } = this.props;
- switch (paper.paperModule) {
- case 'sentence':
- return <Sentence {...this.props} {...this.state} flow={this} scene='answer' mode='question' />;
- default:
- return <div className='base'>{this.renderBase()}</div>;
- }
- }
- renderHeader() {
- const { userQuestion = {}, questionNo = {}, paper = {}, report = {}, questionNos = [], question = {}, info, detail } = this.props;
- const { showIds } = this.state;
- return <div className={'layout-header'}>
- {detail && <div className="left">
- {paper.paperModule && paper.paperModule !== 'examination' && <div className="btn"><Button radius onClick={() => {
- linkTo(`/paper/report/${report.id}`);
- }}>返回练习报告</Button></div>}
- {paper.paperModule && paper.paperModule === 'examination' && <div className="btn"><Button radius onClick={() => {
- linkTo(`/paper/report/${report.id}`);
- }}>返回成绩单</Button></div>}
- <div className="no">No.{userQuestion.stageNo || userQuestion.no}</div>
- <div className="title"><Assets name='book' />{paper.title}</div>
- </div>}
- <div className="center">
- <div className="menu-wrap">
- ID:{questionNo.title}
- {questionNos && questionNos.length > 0 && <Icon name="more" onClick={() => {
- this.setState({ showIds: true });
- }} />}
- {showIds && <div className='menu-content'>
- <p>题源汇总</p>
- {(questionNos || []).map((row) => <p onClick={() => info && this.switchNo(row)}>ID:{row.title}</p>)}
- </div>}
- </div>
- </div>
- <div className="right" hidden={question.questionType === 'awa'}>
- {detail && <span className="b" hidden={!userQuestion.id}>
- 用时:<span dangerouslySetInnerHTML={{ __html: formatSeconds(userQuestion.userTime).replace(/([0-9]+)(m|min|h|hour|s)/g, '<span class="s">$1</span>$2') }} />
- {/* 用时:<span className="s">1</span>m<span className="s">39</span>s */}
- </span>}
- <span className="b">
- 全站:<span dangerouslySetInnerHTML={{ __html: formatSeconds(questionNo.totalTime / questionNo.totalNumber).replace(/([0-9]+)(m|min|h|hour|s)/g, '<span class="s">$1</span>$2') }} />
- {/* 全站:<span className="s">1</span>m<span className="s">39</span>s */}
- </span>
- <span className="b">
- <span className="s">{formatPercent(questionNo.totalCorrect, questionNo.totalNumber)}</span>%
- </span>
- <Icon name="question" />
- <Icon name="star" active={userQuestion.collect} onClick={() => this.toggleCollect()} />
- </div>
- </div>;
- }
- renderBase() {
- const { questionStatus, userQuestion = {}, paper = {}, detail } = this.props;
- const { showIds } = this.state;
- return <div className={`layout ${paper.paperModule}`} onClick={() => {
- if (showIds) this.setState({ showIds: false });
- }}>
- {this.renderHeader()}
- <div className="layout-body">{this.renderBody()}</div>
- <div className="layout-footer">
- <div className="left">
- <Tooltip overlayClassName='gray' placement='top' title='全屏'>
- <a>
- <Icon name={this.state.isFullscreenEnabled ? 'sceen-restore' : 'sceen-full'} onClick={() => this.toggleFullscreen()} />
- </a>
- </Tooltip>
- </div>
- <div className="center">
- <AnswerButton className="item" onClick={() => this.setState({ noteModal: true })}>笔记</AnswerButton>
- {questionStatus >= 0 && <AnswerButton className="item" onClick={() => {
- if (questionStatus > 0) {
- this.setState({ askModal: true, ask: { target: AskTarget[0].value } });
- } else {
- this.setState({ askFailModal: true });
- }
- }}>提问</AnswerButton>}
- <AnswerButton className="item" onClick={() => this.setState({ feedbackModal: true })}>纠错</AnswerButton>
- </div>
- {detail && <div className="right">
- {userQuestion.no !== 1 && <Icon name="prev" onClick={() => this.prevQuestion()} />}
- {userQuestion.questionNumber !== userQuestion.no && <Icon name="next" onClick={() => this.nextQuestion()} />}
- </div>}
- </div>
- {this.state.askModal && this.renderAsk()}
- {this.state.askOkModal && this.renderAskOk()}
- {this.state.askFailModal && this.renderAskFail()}
- {this.state.feedbackModal && this.renderFeedbackError()}
- {this.state.feedbackOkModal && this.renderFeedbackErrorOk()}
- {this.state.noteModal && this.renderNote()}
- </div>;
- }
- renderBody() {
- const { question = { content: {} } } = this.props;
- const { typeset = 'one' } = question.content;
- const { hideAnalysis } = this.state;
- const show = typeset === 'one' ? true : !hideAnalysis;
- return (
- <div className="layout-content">
- <div className='two'>
- {this.renderContent()}
- {question.questionType !== 'awa' && this.renderAnswer()}
- {question.questionType === 'awa' && this.renderAWA()}
- </div>
- {question.questionType !== 'awa' && this.renderAnalysis()}
- {typeset === 'two' && question.questionType !== 'awa' && (
- <div className="fixed-analysis" onClick={() => this.setState({ hideAnalysis: !hideAnalysis })}>
- {show ? '收起解析 >' : '查看解析 <'}
- </div>
- )}
- </div>
- );
- }
- renderAnalysis() {
- const { question = { content: {} } } = this.props;
- const { typeset = 'one' } = question.content;
- const { hideAnalysis, analysisTab } = this.state;
- const show = typeset === 'one' ? true : !hideAnalysis;
- return (
- <div className={`block block-analysis two-analysis ${show ? 'show' : ''}`}>
- <Tabs
- type="division"
- active={analysisTab}
- space={2}
- tabs={[
- { key: 'official', name: '官方解析' },
- { key: 'qx', name: '千行解析' },
- { key: 'association', name: '题源联想' },
- { key: 'qa', name: '相关回答' },
- ]}
- onChange={(key) => {
- this.setState({ analysisTab: key });
- }}
- />
- <div className="detail">
- {typeset === 'two' && this.renderAnswer()}
- {this.renderText()}
- </div>
- </div>
- );
- }
- renderText() {
- const { question = {}, userQuestion = {} } = this.props;
- const { asks = [], associations = [] } = userQuestion;
- const { analysisTab } = this.state;
- let content;
- switch (analysisTab) {
- case 'official':
- content = <div className="detail-block text-block" dangerouslySetInnerHTML={{ __html: question.officialContent }} />;
- break;
- case 'qx':
- content = <div className="detail-block text-block" dangerouslySetInnerHTML={{ __html: question.qxContent }} />;
- break;
- case 'association':
- content = <div className="detail-block">
- <Carousel>
- {associations.map(association => {
- return <div className="text-block" dangerouslySetInnerHTML={{ __html: association.stem }} />;
- })}
- </Carousel>
- </div>;
- break;
- case 'qa':
- content = <div className="detail-block answer-block">
- {asks.map((ask, index) => {
- return <OtherAnswer key={index} data={ask} />;
- })}
- </div>;
- break;
- default:
- break;
- }
- return content;
- }
- renderAnswer() {
- const { question = { content: {} }, userQuestion = {}, detail } = this.props;
- const { questions = [], type, typeset = 'one' } = question.content;
- const { showAnswer } = this.state;
- return <div className="block block-answer">
- {detail && typeset === 'two' ? <Switch checked={showAnswer} onChange={(value) => {
- this.setState({ showAnswer: value });
- }}>{showAnswer ? '显示答案' : '关闭答案'}</Switch> : ''}
- {questions.map((item, index) => {
- return (
- <div>
- <div className="text m-b-2">{item.description}</div>
- <AnswerList
- show={showAnswer}
- selected={(userQuestion.userAnswer || { questions: [] }).questions[index]}
- answer={(question.answer || { questions: [] }).questions[index]}
- distributed={(question.answerDistributed || { questions: [] }).questions[index]}
- list={item.select}
- type={type}
- first={item.first}
- second={item.second}
- direction={item.direction}
- />
- </div>
- );
- })}
- </div>;
- }
- renderContent() {
- const { question = { content: {} }, detail } = this.props;
- const { typeset = 'one' } = question.content;
- const { steps = [] } = question.content;
- const { showAnswer, step } = this.state;
- return (
- <div className="block block-content">
- {detail && typeset === 'one' && question.questionType !== 'awa' ? <Switch checked={showAnswer} onChange={(value) => {
- this.setState({ showAnswer: value });
- }}>{showAnswer ? '显示答案' : '关闭答案'}</Switch> : ''}
- {question.questionType === 'awa' && <h2>Analytical Writing Assessment</h2>}
- {steps.length > 0 && <Navigation theme='detail' list={question.content.steps} active={step} onChange={(v) => this.setState({ step: v })} />}
- <div className="text" style={{ height: 2000 }} dangerouslySetInnerHTML={{ __html: this.formatStem(steps.length > 0 ? steps[step].stem : question.stem) }} />
- </div>
- );
- }
- renderAWA() {
- const { userQuestion = { detail: {}, userAnswer: {} } } = this.state;
- const { showAnswer } = this.state;
- return <div className="block block-awa">
- <Switch checked={showAnswer} onChange={(value) => {
- this.setState({ showAnswer: value });
- }}>{showAnswer ? '显示答案' : '关闭答案'}</Switch>
- <div className="body">
- <h2>Your Response</h2>
- {showAnswer && <div className='detail'>
- <div className='info'>
- <span className="b">
- 用时:<span dangerouslySetInnerHTML={{ __html: formatSeconds(userQuestion.userTime).replace(/([0-9]+)(m|min|h|hour|s)/g, '<span class="s">$1</span>$2') }} />
- {/* 用时:<span className="s">1</span>m<span className="s">39</span>s */}
- </span>
- <span className="b">
- 单词数:<span className="s">{Number((userQuestion.detail || {}).words || 0)}</span>词
- </span>
- </div>
- <div className='content-awa' dangerouslySetInnerHTML={{ __html: userQuestion.userAnswer.awa || '' }} />
- </div>}
- {!showAnswer && <div className='show-awa'>选择「显示答案」查看自己的作文</div>}
- </div>
- </div>;
- }
- renderAsk() {
- const { ask = {} } = this.state;
- return (
- <div className="modal ask">
- <div className="mask" />
- <div className="body">
- <div className="title">提问</div>
- <div className="desc">
- <div className="select-inline">我想对<Select excludeSelf size="small" theme="white" value={ask.target} list={AskTarget} onChange={(item) => {
- ask.target = item.value;
- this.setState({ ask });
- }} />进行提问</div>
- <div className="label">有疑问的具体内容是:</div>
- <textarea className="textarea" value={ask.originContent} placeholder="请复制粘贴有疑问的内容。" onChange={(e) => {
- ask.originContent = e.target.value;
- this.setState({ ask });
- }} />
- <div className="label">针对以上内容的问题是:</div>
- <textarea className="textarea" value={ask.content} placeholder="提问频率高的问题会被优先回答哦。" onChange={(e) => {
- ask.content = e.target.value;
- this.setState({ ask });
- }} />
- </div>
- <div className="bottom">
- <AnswerButton theme="cancel" size="lager" onClick={() => this.setState({ askModal: false })}>
- 取消
- </AnswerButton>
- <AnswerButton size="lager" onClick={() => this.submitAsk()}>提交</AnswerButton>
- </div>
- </div>
- </div>
- );
- }
- renderAskOk() {
- return (
- <div className="modal ask-ok">
- <div className="mask" />
- <div className="body">
- <div className="title">提问</div>
- <div className="content">
- <div className="left">
- <div className="text">已提交成功!</div>
- <div className="text">关注公众号,老师回答后会立即收到通知。</div>
- <div className="text">我们也会通过站内信的方式通知你。</div>
- <div className="small">成为学员享受极速答疑特权。<Link>了解更多</Link></div>
- </div>
- <div className="right">
- <div className="text">扫码关注公众号</div>
- <div className="text">千行GMAT</div>
- </div>
- </div>
- <div className="confirm">
- <AnswerButton size="lager" theme="confirm" onClick={() => {
- this.setState({ askOkModal: false });
- }}>
- 好的,知道了
- </AnswerButton>
- </div>
- </div>
- </div>
- );
- }
- renderAskFail() {
- return (
- <div className="modal ask-ok">
- <div className="mask" />
- <div className="body">
- <div className="title">提问</div>
- <div className="content">
- <div className="left">
- <div className="text">提问功能正在维护中。</div>
- <div className="text">可先查阅“相关问答” 或 成为学员享受极速 答疑特权。</div>
- <Link to="/">了解更多></Link>
- </div>
- <div className="right">
- <div className="text">扫码关注公众号</div>
- <div className="text">千行GMAT</div>
- </div>
- </div>
- <div className="confirm">
- <AnswerButton size="lager" theme="confirm" onClick={() => {
- this.setState({ askFailModal: false });
- }}>
- 好的,知道了
- </AnswerButton>
- </div>
- </div>
- </div>
- );
- }
- renderFeedbackError() {
- const { feedback = {} } = this.state;
- return (
- <div className="modal error">
- <div className="mask" />
- <div className="body">
- <div className="title">纠错</div>
- <div className="desc">
- <div className="select-inline">我想对<Select excludeSelf size="small" theme="white" value={feedback.target} list={AskTarget} onChange={(item) => {
- feedback.target = item.value;
- this.setState({ feedback });
- }} />进行提问</div>
- <div className="label">错误内容是:</div>
- <textarea className="textarea" value={feedback.originContent} placeholder="你可以适当扩大复制范围以使我们准确定位,感谢。" />
- <div className="label">应该改为:</div>
- <textarea className="textarea" placeholder="只需提供正确内容即可" />
- </div>
- <div className="bottom">
- <AnswerButton theme="cancel" size="lager" onClick={() => {
- this.setState({ feedbackModal: false });
- }}>
- 取消
- </AnswerButton>
- <AnswerButton size="lager" onClick={() => {
- this.submitFeedbackError();
- }}>提交</AnswerButton>
- </div>
- </div>
- </div>
- );
- }
- renderFeedbackErrorOk() {
- return (
- <div className="modal error-ok">
- <div className="mask" />
- <div className="body">
- <div className="title">纠错</div>
- <div className="content">
- <div className="left">
- <div className="text"><Assets name='right' svg />已提交成功!</div>
- <div className="text">感谢您的耐心反馈,我们会尽快核实并以站内信的方式告知结果。</div>
- <div className="text">您也可以关注公众号及时获取结果。</div>
- </div>
- <div className="right">
- <div className="text">扫码关注公众号</div>
- <div className="text">千行GMAT</div>
- </div>
- </div>
- <div className="confirm">
- <AnswerButton size="lager" theme="confirm" onClick={() => {
- this.setState({ feedbackOkModal: false });
- }}>
- 好的,知道了
- </AnswerButton>
- </div>
- </div>
- </div>
- );
- }
- renderNote() {
- const { noteField, note = {} } = this.state;
- return (
- <div className="modal note">
- <div className="mask" />
- <div className="body">
- <div className="title">笔记</div>
- <div className="content">
- <div className="tabs">
- {AskTarget.map(item => {
- return (
- <div className={`tab ${noteField === item.key ? 'active' : ''}`} onClick={() => {
- this.setState({ noteField: item.key });
- }}>
- <div className="text">{item.label}</div>
- <div className="date">{note[`${item.key}Time`] ? formatDate(note[`${item.key}Time`]) : ''}</div>
- </div>
- );
- })}
- </div>
- <div className="input">
- <textarea className="textarea" value={note[`${noteField}Content`] || ''} placeholder="记下笔记,方便以后复习" onChange={(e) => {
- note[`${noteField}Time`] = new Date();
- note[`${noteField}Content`] = e.target.value;
- this.setState({ note });
- }} />
- <div className="bottom">
- <AnswerButton theme="cancel" size="lager" onClick={() => {
- this.setState({ noteModal: false });
- }}>
- 取消
- </AnswerButton>
- <AnswerButton size="lager" onClick={() => {
- this.submitNote();
- }}>编辑</AnswerButton>
- <AnswerButton size="lager" onClick={() => {
- this.submitNote(true);
- }}>保存</AnswerButton>
- </div>
- </div>
- </div>
- </div>
- </div>
- );
- }
- }
|