import React from 'react'; import ReactDOM from 'react-dom'; import { Carousel } from 'antd'; import { Link } from 'react-router-dom'; import Fullscreen from 'react-fullscreen-crossbrowser'; import './index.less'; import Page from '@src/containers/Page'; import { formatSeconds, formatPercent, formatDate, sortListWithOrder } 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 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 Page { initState() { return { step: 0, hideAnalysis: true, analysisTab: 'official', showAnswer: false, noteField: AskTarget[0].key, showIds: false, // question: { // content: { // typeset: 'one', // }, // // questionType: 'awa', // answer: { // subject: [[{ text: 'like', uuid: 'hKyz' }]], // options: ['parallel'], // }, // stem: "<p><span uuid='kBJe'>I</span> <span uuid='hKyz'>like</span> <span uuid='fQXh'>book</span></p>", // }, // userQuestion: { // userAnswer: { // subject: [{ text: 'I', uuid: 'kBJe' }], // options: ['compare'], // }, // no: 2, // }, // paper: { // title: '长难句练习', // questionNumber: 20, // }, // report: { // paperModule: 'sentence', // }, }; } initData() { const { id } = this.params; Question.getDetailById(id).then(userQuestion => { const { question, questionNos, paper, note, report, setting } = userQuestion; let { questionNo } = userQuestion; if (!questionNo) ([questionNo] = questionNos); if (!question.answer) question.answer = { questions: [] }; if (!question.answerDistributed) question.answerDistributed = { questions: [] }; if (!userQuestion.userAnswer) userQuestion.userAnswer = { questions: [] }; if ((report.setting || {}).disorder) { const { content } = question; // 还原做题顺序 content.questions.forEach((q, i) => { q.select = sortListWithOrder(question.select, setting.questions[i]); }); question.answer.questions.forEach((q, i) => { Object.keys(q).forEach((k) => { if (q[k]) q[k] = sortListWithOrder(q[k], setting.questions[i]); }); }); question.answerDistributed.questions.forEach((q, i) => { Object.keys(q).forEach((k) => { if (q[k]) q[k] = sortListWithOrder(q[k], setting.questions[i]); }); }); userQuestion.userAnswer.questions.forEach((q, i) => { Object.keys(q).forEach((k) => { if (q[k]) q[k] = sortListWithOrder(q[k], setting.questions[i]); }); }); } this.setState({ userQuestion, question, questionNo, note, paper, questionNos }); }); } prevQuestion() { const { userQuestion } = this.state; if (userQuestion.no === 1) return; Question.getDetailByNo(userQuestion.reportId, userQuestion.no - 1).then((r) => { linkTo(`/paper/question/${r.id}`); }); } nextQuestion() { const { userQuestion } = this.state; if (userQuestion.questionNumber === userQuestion.no) return; Question.getDetailByNo(userQuestion.reportId, userQuestion.no + 1).then((r) => { linkTo(`/paper/question/${r.id}`); }); } submitAsk() { const { userQuestion = {}, ask = {} } = this.state; if (ask.originContent === '' || ask.content === '' || ask.target === '') return; My.addQuestionAsk(ask.target, userQuestion.questionModule, ask.originContent, ask.content).then(() => { this.setState({ askModal: false, askOkModal: true }); }).catch(err => { this.setState({ askError: err.message }); }); } submitFeedbackError() { const { feedback = {}, question = {}, questionNo = {} } = this.state; if (feedback.originContent === '' || feedback.content === '' || feedback.target === '') return; My.addFeedbackErrorQuestion(question.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 = {}, note = {} } = this.state; My.updateQuestionNote(userQuestion.questionModule, userQuestion.questionNoId, 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 = {} } = this.state; if (!userQuestion.collect) { My.addQuestionCollect(userQuestion.questionModule, userQuestion.questionNoId).then(() => { userQuestion.collect = true; this.setState({ userQuestion }); }); } else { My.delQuestionCollect(userQuestion.questionModule, userQuestion.questionNoId).then(() => { userQuestion.collect = false; this.setState({ userQuestion }); }); } } formatStem(text) { if (!text) return ''; const { showAnswer, question = { content: {} }, userQuestion } = this.state; 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; } renderView() { return ( <Fullscreen enabled={this.state.isFullscreenEnabled} onChange={isFullscreenEnabled => this.setState({ isFullscreenEnabled })} > {this.renderDetail()} </Fullscreen> ); } renderDetail() { const { report = {} } = this.state; switch (report.paperModule) { case 'sentence': return <Sentence {...this.state} flow={this} scene='answer' />; default: return <div className='base'>{this.renderBase()}</div>; } } renderBase() { const { questionStatus, userQuestion = {}, questionNo = {}, paper = {}, showIds, questionNos = [] } = this.state; return <div className="layout" onClick={() => { if (showIds) this.setState({ showIds: false }); }}> <div className="layout-header"> <div className="left"> <div className="no">No.{userQuestion.no}</div> <div className="title"><Assets name='book' />{paper.title}13</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>ID:{row.title}</p>)} </div>} </div> </div> <div className="right"> <span className="b"> 用时:<span dangerouslySetInnerHTML={{ __html: formatSeconds(userQuestion.userTime).replace(/([0-9]+)([msh])/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]+)([msh])/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> <div className="layout-body">{this.renderBody()}</div> <div className="layout-footer"> <div className="left"> <Icon name={this.state.isFullscreenEnabled ? 'sceen-restore' : 'sceen-full'} onClick={() => this.toggleFullscreen()} /> </div> <div className="center"> <AnswerButton className="item" onClick={() => this.setState({ noteModal: true })}>笔记</AnswerButton> <AnswerButton className="item" onClick={() => { if (questionStatus) { this.setState({ askModal: true }); } else { this.setState({ askFailModal: true }); } }}>提问</AnswerButton> <AnswerButton className="item" onClick={() => this.setState({ feedbackModal: true })}>纠错</AnswerButton> </div> <div className="right"> <Icon name="prev" onClick={() => this.prevQuestion()} /> <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.state; 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: {} }, analysisTab } = this.state; const { typeset = 'one' } = question.content; const { hideAnalysis } = this.state; const show = typeset === 'one' ? true : !hideAnalysis; return ( <div className={`block block-analysis two-analysis ${show ? 'show' : ''}`}> <Tabs type="division" active={analysisTab} 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 { analysisTab, question = {}, userQuestion = {} } = this.state; const { asks = [], associations = [] } = userQuestion; 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: {} }, showAnswer, userQuestion = {} } = this.state; const { questions = [], type, typeset = 'one' } = question.content; console.log(userQuestion); return <div className="block block-answer"> {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: {} }, showAnswer, step } = this.state; const { typeset = 'one' } = question.content; const { steps = [] } = question.content; return ( <div className="block block-content"> {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 { showAnswer, userQuestion = { detail: {}, userAnswer: {} } } = 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]+)([msh])/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> ); } }