|  | @@ -0,0 +1,225 @@
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +import React, { useState, useEffect } from 'react';
 | 
	
		
			
				|  |  | +import { View, Text, ScrollView, Button } from '@tarojs/components';
 | 
	
		
			
				|  |  | +import { observer, inject } from 'mobx-react';
 | 
	
		
			
				|  |  | +import './index.less';
 | 
	
		
			
				|  |  | +import TopNavBar from '@src/components/TopNavBar/index';
 | 
	
		
			
				|  |  | +import Taro from '@tarojs/taro';
 | 
	
		
			
				|  |  | +import BookConfigPuP from '@src/components/bookConfigPup';
 | 
	
		
			
				|  |  | +import { BookStore } from '@src/store/book';
 | 
	
		
			
				|  |  | +import useApi from '@src/Hook/useApi';
 | 
	
		
			
				|  |  | +import { UpLoading } from '@src/components/PupPetry/Loading';
 | 
	
		
			
				|  |  | +import { getReadLog, setReadLog } from '@src/utils/loginSto';
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +interface Config {
 | 
	
		
			
				|  |  | +    size: 'pre_15' | 'pre_17' | 'pre_19' | 'pre_21' | 'pre_23' | 'pre_25' | 'pre_27' | 'pre_29' | any;
 | 
	
		
			
				|  |  | +    off_on: boolean;
 | 
	
		
			
				|  |  | +    bg: 'bg_b' | 'bg_h' | 'bg_l' | 'bg_hh';
 | 
	
		
			
				|  |  | +    lh: 'lh_72' | 'lh_84' | 'lh_96';
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +type PageStateProps = {
 | 
	
		
			
				|  |  | +    store: {
 | 
	
		
			
				|  |  | +        bookStore: BookStore,
 | 
	
		
			
				|  |  | +        indexStore,
 | 
	
		
			
				|  |  | +        appInfoStore
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +};
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +interface BookArticleProps extends PageStateProps { }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +const BookArticle: React.FC<BookArticleProps> = ({ store }) => {
 | 
	
		
			
				|  |  | +    const [isLoad, setIsLoad] = useState(false)
 | 
	
		
			
				|  |  | +    const { bookStore, indexStore, appInfoStore } = store
 | 
	
		
			
				|  |  | +    const { openBookData } = bookStore
 | 
	
		
			
				|  |  | +    const [bookConfig, setBookConfig] = useState<Config>({ size: 'pre_21', off_on: true, bg: 'bg_b', lh: 'lh_72' });
 | 
	
		
			
				|  |  | +    const [pup, setPup] = useState(false);
 | 
	
		
			
				|  |  | +    const { getBookContent } = useApi(appInfoStore)
 | 
	
		
			
				|  |  | +    const [currentScrollId, setCurrentScrollId] = useState('');
 | 
	
		
			
				|  |  | +    useEffect(() => {
 | 
	
		
			
				|  |  | +        // ComponentDidMount
 | 
	
		
			
				|  |  | +        try {
 | 
	
		
			
				|  |  | +            Taro.getStorage({
 | 
	
		
			
				|  |  | +                key: 'bookConfig',
 | 
	
		
			
				|  |  | +                success: (res) => {
 | 
	
		
			
				|  |  | +                    setBookConfig(JSON.parse(res.data));
 | 
	
		
			
				|  |  | +                },
 | 
	
		
			
				|  |  | +                fail: () => {
 | 
	
		
			
				|  |  | +                    setConfig({ size: 'pre_21', off_on: true, bg: 'bg_b', lh: 'lh_72' });
 | 
	
		
			
				|  |  | +                }
 | 
	
		
			
				|  |  | +            });
 | 
	
		
			
				|  |  | +        } catch (e) {
 | 
	
		
			
				|  |  | +            console.error('Error loading book config:', e);
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  | +        //假如存在阅读记录获取对应章节的条数
 | 
	
		
			
				|  |  | +        let readLogId = getReadLog(openBookData?.bookId)
 | 
	
		
			
				|  |  | +        getBookContent({ pageNum: 1, pageSize:readLogId? Number(readLogId?.split('-')[1]) : 2 }).then(res => {
 | 
	
		
			
				|  |  | +            //继续阅读时跳转到上次阅读的段落
 | 
	
		
			
				|  |  | +            console.log(readLogId)
 | 
	
		
			
				|  |  | +            setCurrentScrollId(readLogId)
 | 
	
		
			
				|  |  | +        })
 | 
	
		
			
				|  |  | +        return () => {
 | 
	
		
			
				|  |  | +            console.log("卸载");
 | 
	
		
			
				|  |  | +            reportRead(); // 上报
 | 
	
		
			
				|  |  | +        };
 | 
	
		
			
				|  |  | +    }, []);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    const reportRead = () => {
 | 
	
		
			
				|  |  | +        // 这里可以填写上报逻辑
 | 
	
		
			
				|  |  | +    };
 | 
	
		
			
				|  |  | +    //设置配置
 | 
	
		
			
				|  |  | +    const setConfig = (data: Config) => {
 | 
	
		
			
				|  |  | +        if (data) {
 | 
	
		
			
				|  |  | +            setBookConfig(data);
 | 
	
		
			
				|  |  | +            Taro.setStorage({
 | 
	
		
			
				|  |  | +                key: 'bookConfig',
 | 
	
		
			
				|  |  | +                data: JSON.stringify(data),
 | 
	
		
			
				|  |  | +                success: (res: any) => {
 | 
	
		
			
				|  |  | +                    console.log('添加bookConfig===>成功!', res);
 | 
	
		
			
				|  |  | +                },
 | 
	
		
			
				|  |  | +                fail: (err) => {
 | 
	
		
			
				|  |  | +                    console.log('添加bookConfig===>失败', err);
 | 
	
		
			
				|  |  | +                }
 | 
	
		
			
				|  |  | +            });
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +            // 控制自义定头部背景
 | 
	
		
			
				|  |  | +            store.bookStore.setData({ bookConfig: data });
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  | +    };
 | 
	
		
			
				|  |  | +    /**计算字体 */
 | 
	
		
			
				|  |  | +    const setSize = (parms: number) => {
 | 
	
		
			
				|  |  | +        let num = bookConfig.size.split('_');
 | 
	
		
			
				|  |  | +        if (num[1] && parms) {
 | 
	
		
			
				|  |  | +            setConfig({ ...bookConfig, size: num[0] + '_' + (Number(num[1]) + 2) });
 | 
	
		
			
				|  |  | +        } else {
 | 
	
		
			
				|  |  | +            setConfig({ ...bookConfig, size: num[0] + '_' + (Number(num[1]) - 2) });
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  | +    };
 | 
	
		
			
				|  |  | +    /**设置背景 */
 | 
	
		
			
				|  |  | +    const setBg = (bg: 'bg_b' | 'bg_h' | 'bg_l' | 'bg_hh') => {
 | 
	
		
			
				|  |  | +        setConfig({ ...bookConfig, bg });
 | 
	
		
			
				|  |  | +        if (!bookConfig.off_on) {
 | 
	
		
			
				|  |  | +            setOff(bg);
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  | +    };
 | 
	
		
			
				|  |  | +    /**设置行高*/
 | 
	
		
			
				|  |  | +    const setLh = (lh: 'lh_72' | 'lh_84' | 'lh_96') => {
 | 
	
		
			
				|  |  | +        setConfig({ ...bookConfig, lh });
 | 
	
		
			
				|  |  | +    };
 | 
	
		
			
				|  |  | +    /**关灯 */
 | 
	
		
			
				|  |  | +    const setOff = (bg?: 'bg_b' | 'bg_h' | 'bg_l' | 'bg_hh') => {
 | 
	
		
			
				|  |  | +        const newOffOn = !bookConfig.off_on;
 | 
	
		
			
				|  |  | +        const newConfig = bg ? { ...bookConfig, bg, off_on: newOffOn } : { ...bookConfig, off_on: newOffOn };
 | 
	
		
			
				|  |  | +        setConfig(newConfig);
 | 
	
		
			
				|  |  | +        Taro.setNavigationBarColor({
 | 
	
		
			
				|  |  | +            frontColor: newOffOn ? '#000000' : '#ffffff',
 | 
	
		
			
				|  |  | +            backgroundColor: newOffOn ? '#fff' : '#111111'
 | 
	
		
			
				|  |  | +        });
 | 
	
		
			
				|  |  | +    };
 | 
	
		
			
				|  |  | +    /**菜单 */
 | 
	
		
			
				|  |  | +    const togglePup = () => {
 | 
	
		
			
				|  |  | +        setPup(!pup);
 | 
	
		
			
				|  |  | +    };
 | 
	
		
			
				|  |  | +    //设置setNavigationBarColor
 | 
	
		
			
				|  |  | +    useEffect(() => {
 | 
	
		
			
				|  |  | +        Taro.setNavigationBarColor({
 | 
	
		
			
				|  |  | +            frontColor: bookConfig.off_on ? '#000000' : '#ffffff',
 | 
	
		
			
				|  |  | +            backgroundColor: bookConfig.off_on ? '#fff' : '#111'
 | 
	
		
			
				|  |  | +        });
 | 
	
		
			
				|  |  | +    }, [bookConfig.off_on]);
 | 
	
		
			
				|  |  | +    const onLoad = async () => {
 | 
	
		
			
				|  |  | +        if (openBookData?.contentData) {
 | 
	
		
			
				|  |  | +            let { size, current, total, records } = openBookData.contentData
 | 
	
		
			
				|  |  | +            if (records.length < total) {
 | 
	
		
			
				|  |  | +                console.log("加载")
 | 
	
		
			
				|  |  | +                setIsLoad(true)
 | 
	
		
			
				|  |  | +                await getBookContent({ pageNum: 1, pageSize: size + 2 }).then(res => {
 | 
	
		
			
				|  |  | +                    setIsLoad(false)
 | 
	
		
			
				|  |  | +                })
 | 
	
		
			
				|  |  | +            } else {
 | 
	
		
			
				|  |  | +                Taro.showToast({
 | 
	
		
			
				|  |  | +                    title: '没有更多了~~',
 | 
	
		
			
				|  |  | +                    duration: 1000,
 | 
	
		
			
				|  |  | +                    icon: 'none'
 | 
	
		
			
				|  |  | +                })
 | 
	
		
			
				|  |  | +            }
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +    // 计算阅读的位置,存放以备下次阅读的时候跳转到对应位置
 | 
	
		
			
				|  |  | +    const handleScroll = () => {
 | 
	
		
			
				|  |  | +        const query = Taro.createSelectorQuery();
 | 
	
		
			
				|  |  | +        query.selectAll('.shrot-text').boundingClientRect(); // 选择所有子元素
 | 
	
		
			
				|  |  | +        query.selectViewport().scrollOffset(); // 获取视口的滚动位置
 | 
	
		
			
				|  |  | +        query.exec(res => {
 | 
	
		
			
				|  |  | +            const items = res[0]; // 子元素的位置信息
 | 
	
		
			
				|  |  | +            const scrollTop = res[1].scrollTop + indexStore.navHeight; // 视口的滚动距离
 | 
	
		
			
				|  |  | +            // 遍历所有子元素,判断哪个子元素在可视区域
 | 
	
		
			
				|  |  | +            for (let item of items) {
 | 
	
		
			
				|  |  | +                if (item.top <= scrollTop && item.bottom >= scrollTop) {
 | 
	
		
			
				|  |  | +                    if (openBookData?.bookId) {
 | 
	
		
			
				|  |  | +                        setReadLog({ [openBookData.bookId]: item.id })
 | 
	
		
			
				|  |  | +                    }
 | 
	
		
			
				|  |  | +                    break;
 | 
	
		
			
				|  |  | +                }
 | 
	
		
			
				|  |  | +            }
 | 
	
		
			
				|  |  | +        });
 | 
	
		
			
				|  |  | +    };
 | 
	
		
			
				|  |  | +    const { size, bg, off_on, lh } = bookConfig;
 | 
	
		
			
				|  |  | +    return (
 | 
	
		
			
				|  |  | +        <ScrollView
 | 
	
		
			
				|  |  | +            lowerThreshold={indexStore.navHeight}
 | 
	
		
			
				|  |  | +            refresherTriggered={isLoad}
 | 
	
		
			
				|  |  | +            onScrollToLower={onLoad}
 | 
	
		
			
				|  |  | +            style={{ height: `calc(100vh - ${indexStore.navHeight}px)` }}
 | 
	
		
			
				|  |  | +            scrollY={true}
 | 
	
		
			
				|  |  | +            refresherDefaultStyle='black'
 | 
	
		
			
				|  |  | +            scrollWithAnimation={true}
 | 
	
		
			
				|  |  | +            scrollIntoView={currentScrollId}
 | 
	
		
			
				|  |  | +            onScroll={handleScroll}
 | 
	
		
			
				|  |  | +        >
 | 
	
		
			
				|  |  | +            <View className={`book_article ${off_on ? bg : 'bg_hh'} ${off_on ? 'cl_h' : 'cl_b'}`} onClick={togglePup}>
 | 
	
		
			
				|  |  | +                <View className='header'>
 | 
	
		
			
				|  |  | +                    <View className='btns'>
 | 
	
		
			
				|  |  | +                        <View className='left'>
 | 
	
		
			
				|  |  | +                            <Text
 | 
	
		
			
				|  |  | +                                onClick={() => { size !== 'pre_15' && setSize(0) }}
 | 
	
		
			
				|  |  | +                                className={!off_on ? size === 'pre_15' ? 'cl_e5' : '' : size === 'pre_15' ? 'cl_d6' : ''}
 | 
	
		
			
				|  |  | +                            >字小</Text>
 | 
	
		
			
				|  |  | +                            <Text
 | 
	
		
			
				|  |  | +                                onClick={() => { size !== 'pre_29' && setSize(1) }}
 | 
	
		
			
				|  |  | +                                className={!off_on ? size === 'pre_29' ? 'cl_e5' : '' : size === 'pre_29' ? 'cl_d6' : ''}
 | 
	
		
			
				|  |  | +                            >字大</Text>
 | 
	
		
			
				|  |  | +                        </View>
 | 
	
		
			
				|  |  | +                        <View className='right'>
 | 
	
		
			
				|  |  | +                            <Text onClick={() => { setOff() }}>{off_on ? '关灯' : '开灯'}</Text>
 | 
	
		
			
				|  |  | +                            <Text onClick={togglePup}>菜单</Text>
 | 
	
		
			
				|  |  | +                        </View>
 | 
	
		
			
				|  |  | +                    </View>
 | 
	
		
			
				|  |  | +                </View>
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +                {
 | 
	
		
			
				|  |  | +                    openBookData?.contentData?.records?.map(item => {
 | 
	
		
			
				|  |  | +                        let arr = item.paragraphInfo.content?.split(/\s/ig)
 | 
	
		
			
				|  |  | +                        return <View
 | 
	
		
			
				|  |  | +                            id={'paragraphNo-' + item.paragraphInfo.paragraphNo}
 | 
	
		
			
				|  |  | +                            key={item.paragraphInfo.paragraphNo}
 | 
	
		
			
				|  |  | +                            className={`pre ${size} ${lh}`}
 | 
	
		
			
				|  |  | +                        >
 | 
	
		
			
				|  |  | +                            {
 | 
	
		
			
				|  |  | +                                arr?.map((str, index) => {
 | 
	
		
			
				|  |  | +                                    return <Text className='shrot-text' id={'paragraphNo-' + item.paragraphInfo.paragraphNo + '-' + index} key={index}>{str}</Text>
 | 
	
		
			
				|  |  | +                                })
 | 
	
		
			
				|  |  | +                            }
 | 
	
		
			
				|  |  | +                        </View>
 | 
	
		
			
				|  |  | +                    })
 | 
	
		
			
				|  |  | +                }
 | 
	
		
			
				|  |  | +                {pup && <BookConfigPuP {...bookConfig} off_on={off_on} setOff={setOff} setSize={setSize} setBg={setBg} setLh={setLh} />}
 | 
	
		
			
				|  |  | +            </View>
 | 
	
		
			
				|  |  | +            {
 | 
	
		
			
				|  |  | +                isLoad && <UpLoading />
 | 
	
		
			
				|  |  | +            }
 | 
	
		
			
				|  |  | +        </ScrollView>
 | 
	
		
			
				|  |  | +    );
 | 
	
		
			
				|  |  | +};
 | 
	
		
			
				|  |  | +export default inject('store')(observer(TopNavBar(BookArticle, { isToBack: true, isReloadBook: true })));
 |