123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405 |
- import React from 'react';
- import { Tooltip } from 'antd';
- import './index.less';
- import Page from '@src/containers/Page';
- import Icon from '../../../components/Icon';
- import Progress from '../../../components/Progress';
- import Assets from '../../../../../src/components/Assets';
- import { Sentence } from '../../../stores/sentence';
- import { Main } from '../../../stores/main';
- import { formatMoney, formatDate } from '../../../../../src/services/Tools';
- export default class extends Page {
- constructor(props) {
- super(props);
- this.page = 0;
- this.inited = false;
- this.timeout = null;
- this.articleMap = {};
- this.pageLine = 100;
- }
- initState() {
- return { article: {}, showJump: false, showMenu: false, currentPage: 0, totalPage: 0 };
- }
- init() {
- this.lastTime = new Date();
- Main.getSentence().then(result => {
- this.setState({ info: result });
- });
- }
- initData() {
- const { chapter = 0, part = 0 } = this.state.search;
- let { page = 0 } = this.state.search;
- const handler = this.refreshSentence();
- handler.then(() => {
- if (chapter > 0) {
- if (!part) {
- ({ page } = this.searchArticle(chapter, 1));
- } else {
- ({ page } = this.searchArticle(chapter, part));
- }
- }
- this.jumpPage(page);
- });
- }
- refreshSentence() {
- if (this.inited) return Promise.resolve();
- return Sentence.listArticle()
- .then(result => {
- const articleMap = {};
- let totalPage = 0;
- let maxChapter = 0;
- let introduction = null;
- result.forEach(article => {
- if (article.chapter === 0) introduction = article;
- if (!articleMap[article.chapter]) {
- articleMap[article.chapter] = [];
- if (article.chapter > maxChapter) maxChapter = article.chapter;
- }
- article.startPage = totalPage + 1;
- article.endPage = totalPage + article.pages;
- totalPage += article.pages;
- articleMap[article.chapter].push(article);
- });
- this.setState({ articleMap, totalPage, maxChapter });
- return introduction;
- })
- .then(introduction => {
- return Sentence.getInfo().then(result => {
- const map = {};
- // 添加前言
- if (introduction) {
- result.chapters.unshift({
- title: introduction.title,
- value: 0,
- });
- }
- result.chapters.forEach(row => {
- map[row.value] = row;
- });
- this.setState({ sentence: result, chapterMap: map });
- });
- })
- .then(() => {
- this.inited = true;
- });
- }
- refreshArticle(articleId) {
- if (this.articleMap[articleId]) return Promise.resolve(this.articleMap[articleId]);
- return Sentence.detailArticle(articleId).then(result => {
- this.articleMap[articleId] = result;
- return result;
- });
- }
- prevPage() {
- const { currentPage } = this.state;
- if (currentPage > 1) {
- this.jumpPage(currentPage - 1);
- }
- }
- nextPage() {
- const { sentence = {} } = this.state;
- const { currentPage, totalPage } = this.state;
- if (currentPage < totalPage) {
- this.jumpPage(currentPage + 1);
- } else {
- const code = !!sentence.code;
- if (!code) {
- this.setState({ article: null });
- }
- }
- }
- jumpPage(targetPage) {
- // 计算哪篇文章
- const { target, index, allow } = this.computeArticle(targetPage);
- if (!allow) {
- this.setState({ article: null });
- return;
- }
- this.page = targetPage;
- this.setState({ inputPage: targetPage });
- const { article = {} } = this.state;
- this.updateProgress(target, index, article);
- this.refreshArticle(target.id).then(row => {
- this.setState({ article: row, index, currentPage: targetPage });
- });
- }
- computeArticle(page) {
- const { articleMap, maxChapter, sentence } = this.state;
- let target = null;
- let index = 0;
- const allow = true || sentence.code || page <= sentence.trailPages;
- for (let i = 0; i <= maxChapter; i += 1) {
- const list = articleMap[i];
- if (!list || list.length === 0) continue;
- for (let j = 0; j < list.length; j += 1) {
- const article = list[j];
- if (article.endPage >= page) {
- target = article;
- index = page - article.startPage;
- // if (!sentence.code) {
- // if (!article.isTrail) {
- // allow = false;
- // } else if (index < article.trailStart - 1) {
- // allow = false;
- // } else if (index > article.trailEnd - 1) {
- // allow = false;
- // }
- // }
- return { target, index, allow };
- }
- }
- }
- return { allow };
- }
- searchArticle(chapter, part) {
- const { articleMap } = this.state;
- let target = null;
- let page = 0;
- if (!part) part = 1;
- const list = articleMap[chapter];
- for (let j = 0; j < list.length; j += 1) {
- const article = list[j];
- if (article.part === Number(part)) {
- target = article;
- page = article.startPage;
- // if (sentence.code) {
- // page = article.startPage;
- // } else {
- // // 试用章节页码
- // page = article.startPage + article.trailPage - 1;
- // }
- break;
- }
- }
- return { target, page };
- }
- updateProgress(target, index, current) {
- if (this.timeout) {
- clearTimeout(this.timeout);
- this.timeout = null;
- }
- const progress = ((index + 1) * 100) / target.pages;
- if (target.chapter === current.chapter && target.part === current.part) {
- Sentence.updateProgress(target.chapter, target.part, progress);
- } else {
- const now = new Date();
- const time = (now.getTime() - this.lastTime.getTime()) / 1000;
- this.lastTime = now;
- Sentence.updateProgress(target.chapter, target.part, progress, time, current.chapter, current.part);
- }
- this.timeout = setTimeout(() => {
- // 最长5分钟阅读时间
- Sentence.updateProgress(0, 0, 0, 5 * 60, target.chapter, target.part);
- this.lastTime = null;
- }, 5 * 60 * 1000);
- }
- renderView() {
- return (
- <div className="layout">
- {this.renderBody()}
- {this.renderRight()}
- {this.renderBottom()}
- {this.renderProgress()}
- </div>
- );
- }
- renderBody() {
- const { showMenu, article, index, chapterMap = {}, info = {}, sentence = {} } = this.state;
- return article ? (
- <div className="layout-body">
- <div className="crumb">千行长难句解析 >> {(chapterMap[article.chapter] || {}).title}</div>
- {article.chapter > 0 && <div className="title">{article.title}</div>}
- <div className="overload" style={{ top: index * -20 * this.pageLine }}>
- <div className="text" dangerouslySetInnerHTML={{ __html: article.content }} />
- </div>
- {showMenu && this.renderMenu()}
- </div>
- ) : (<div className="layout-body">
- <div className="free-over">
- <div className="free-over-title">试读已结束,购买后可继续阅读。</div>
- <div className="free-over-btn" onClick={() => {
- openLink(info.link);
- }}>{formatMoney(info.price)} | 立即购买</div>
- <div className="free-over-desc">
- {(sentence.comments || []).map(row => {
- return [
- <div className="free-over-desc-title">{row.nickname} {formatDate(row.createTime, 'YYYY-MM-DD')}</div>,
- <div className="free-over-desc-content">{row.content}</div>,
- ];
- })}
- </div>
- </div>
- </div>);
- }
- renderMenu() {
- const { sentence = {}, articleMap } = this.state;
- const { chapters = [] } = sentence;
- let page = 1;
- const code = !!sentence.code;
- const message = '购买后访问';
- return (
- <div className="layout-menu">
- <div className="title">目录</div>
- <div
- className="close"
- onClick={() => {
- this.setState({ showMenu: false });
- }}
- >
- x
- </div>
- <div className="chapter">
- {chapters.map(chapter => {
- if (chapter.exercise) {
- return [<div className={'chapter-item trail'}>{chapter.title}</div>];
- }
- chapter.startPage = page;
- const _item = code ? (
- <div
- className={'chapter-item'}
- onClick={() => {
- this.jumpPage(chapter.startPage);
- }}
- >
- {chapter.title}
- <div className="page">{chapter.startPage}</div>
- </div>
- ) : (<Tooltip title={message}>
- <div className={'chapter-item trail'}>
- {chapter.title}
- <div className="page">{chapter.startPage}</div>
- </div>
- </Tooltip>);
- const list = [_item];
- if (chapter.value) {
- (articleMap[chapter.value] || []).forEach(article => {
- // 得到下一章节page
- page += article.pages;
- const item = code ? (
- <div
- className={'part-item'}
- onClick={() => {
- if (code) this.jumpPage(article.startPage);
- }}
- >
- {article.title}
- <div className="page">{article.startPage}</div>
- </div>
- ) : (<Tooltip title={message}>
- <div className={'part-item trail'}>
- {article.title}
- <div className="page">{article.startPage}</div>
- </div>
- </Tooltip>);
- list.push(item);
- });
- }
- return list;
- })}
- </div>
- </div>
- );
- }
- renderRight() {
- return (
- <div className="layout-right">
- <div
- className="m-b-1"
- onClick={() => {
- this.setState({ showMenu: true });
- }}
- >
- <Icon name="menu" />
- </div>
- <div
- className="m-b-1"
- onClick={() => {
- this.prevPage();
- }}
- >
- <Icon name="down_turn" />
- </div>
- <div
- className="m-b-1"
- onClick={() => {
- this.nextPage();
- }}
- >
- <Icon name="up_turn" />
- </div>
- </div>
- );
- }
- renderBottom() {
- const { showJump, currentPage, totalPage } = this.state;
- return (
- <div className="layout-bottom">
- <span className="per">{totalPage ? parseInt((currentPage * 100) / totalPage, 10) : 0}%</span>
- <span className="num">
- {currentPage}/{totalPage}
- </span>
- <span className="btn">
- <Assets
- name={showJump ? 'unfold_icon_down' : 'unfold_icon_up'}
- onClick={() => {
- this.setState({ showJump: !showJump });
- }}
- />
- <div hidden={!showJump} className="jump">
- <span className="text">当前页</span>
- <input
- className="input"
- value={this.state.inputPage}
- onChange={e => {
- this.page = Number(e.target.value);
- this.setState({ inputPage: e.target.value });
- }}
- />
- <Assets
- name="yes_icon"
- onClick={() => {
- if (this.page < 1 || this.page > totalPage) return;
- this.jumpPage(this.page);
- }}
- />
- </div>
- </span>
- </div>
- );
- }
- renderProgress() {
- const { currentPage, totalPage } = this.state;
- return (
- <div className="layout-progress">
- <Progress
- size="small"
- theme="theme"
- radius={false}
- progress={totalPage ? parseInt((currentPage * 100) / totalPage, 10) : 0}
- />
- </div>
- );
- }
- }
|