| 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>    );  }}
 |