index.tsx 9.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234
  1. import React, { useState, useEffect, useRef } from 'react';
  2. import { View, Text, ScrollView, Button } from '@tarojs/components';
  3. import { observer, inject } from 'mobx-react';
  4. import './index.less';
  5. import TopNavBar from '@src/components/TopNavBar/index';
  6. import Taro, { useDidHide } from '@tarojs/taro';
  7. import BookConfigPuP from '@src/components/bookConfigPup';
  8. import { BookStore } from '@src/store/book';
  9. import useApi from '@src/Hook/useApi';
  10. import { UpLoading } from '@src/components/PupPetry/Loading';
  11. import { getReadLog, setReadLog } from '@src/utils/loginSto';
  12. interface Config {
  13. size: 'pre_15' | 'pre_17' | 'pre_19' | 'pre_21' | 'pre_23' | 'pre_25' | 'pre_27' | 'pre_29' | any;
  14. off_on: boolean;
  15. bg: 'bg_b' | 'bg_h' | 'bg_l' | 'bg_hh';
  16. lh: 'lh_72' | 'lh_84' | 'lh_96';
  17. }
  18. type PageStateProps = {
  19. store: {
  20. bookStore: BookStore,
  21. indexStore,
  22. appInfoStore
  23. }
  24. };
  25. interface BookArticleProps extends PageStateProps { }
  26. const BookArticle: React.FC<BookArticleProps> = ({ store }) => {
  27. const [isLoad, setIsLoad] = useState(false)
  28. const { bookStore, indexStore, appInfoStore } = store
  29. const { openBookData } = bookStore
  30. const [bookConfig, setBookConfig] = useState<Config>({ size: 'pre_21', off_on: true, bg: 'bg_b', lh: 'lh_72' });
  31. const [pup, setPup] = useState(false);
  32. const { getBookContent } = useApi(appInfoStore)
  33. const [currentScrollId, setCurrentScrollId] = useState('');
  34. const newReadLogIdRef = useRef("");
  35. // 页面显示的操作请求
  36. useEffect(() => {
  37. // ComponentDidMount
  38. try {
  39. Taro.getStorage({
  40. key: 'bookConfig',
  41. success: (res) => {
  42. setBookConfig(JSON.parse(res.data));
  43. },
  44. fail: () => {
  45. setConfig({ size: 'pre_21', off_on: true, bg: 'bg_b', lh: 'lh_72' });
  46. }
  47. });
  48. } catch (e) {
  49. console.error('Error loading book config:', e);
  50. }
  51. //假如存在阅读记录获取对应章节的条数
  52. let readLogId = getReadLog(openBookData?.bookId)
  53. getBookContent({ pageNum: 1, pageSize: readLogId ? Number(readLogId?.split('-')[1]) : 2 }).then(res => {
  54. //继续阅读时跳转到上次阅读的段落
  55. console.log("上次阅读到readLogId", readLogId)
  56. setCurrentScrollId(readLogId)
  57. })
  58. }, []);
  59. // 页面卸载的操作
  60. useEffect(() => {
  61. return () => {
  62. console.log("卸载", newReadLogIdRef.current);
  63. reportRead(); // 上报
  64. };
  65. }, [])
  66. // 这里可以填写上报逻辑
  67. const reportRead = () => {
  68. console.log("上报", newReadLogIdRef.current)
  69. openBookData && setReadLog({ [openBookData.bookId]: newReadLogIdRef.current })
  70. };
  71. //设置配置
  72. const setConfig = (data: Config) => {
  73. if (data) {
  74. setBookConfig(data);
  75. Taro.setStorage({
  76. key: 'bookConfig',
  77. data: JSON.stringify(data),
  78. success: (res: any) => {
  79. console.log('添加bookConfig===>成功!', res);
  80. },
  81. fail: (err) => {
  82. console.log('添加bookConfig===>失败', err);
  83. }
  84. });
  85. // 控制自义定头部背景
  86. store.bookStore.setData({ bookConfig: data });
  87. }
  88. };
  89. /**计算字体 */
  90. const setSize = (parms: number) => {
  91. let num = bookConfig.size.split('_');
  92. if (num[1] && parms) {
  93. setConfig({ ...bookConfig, size: num[0] + '_' + (Number(num[1]) + 2) });
  94. } else {
  95. setConfig({ ...bookConfig, size: num[0] + '_' + (Number(num[1]) - 2) });
  96. }
  97. };
  98. /**设置背景 */
  99. const setBg = (bg: 'bg_b' | 'bg_h' | 'bg_l' | 'bg_hh') => {
  100. setConfig({ ...bookConfig, bg });
  101. if (!bookConfig.off_on) {
  102. setOff(bg);
  103. }
  104. };
  105. /**设置行高*/
  106. const setLh = (lh: 'lh_72' | 'lh_84' | 'lh_96') => {
  107. setConfig({ ...bookConfig, lh });
  108. };
  109. /**关灯 */
  110. const setOff = (bg?: 'bg_b' | 'bg_h' | 'bg_l' | 'bg_hh') => {
  111. const newOffOn = !bookConfig.off_on;
  112. const newConfig = bg ? { ...bookConfig, bg, off_on: newOffOn } : { ...bookConfig, off_on: newOffOn };
  113. setConfig(newConfig);
  114. Taro.setNavigationBarColor({
  115. frontColor: newOffOn ? '#000000' : '#ffffff',
  116. backgroundColor: newOffOn ? '#fff' : '#111111'
  117. });
  118. };
  119. /**菜单 */
  120. const togglePup = () => {
  121. setPup(!pup);
  122. };
  123. //设置setNavigationBarColor
  124. useEffect(() => {
  125. Taro.setNavigationBarColor({
  126. frontColor: bookConfig.off_on ? '#000000' : '#ffffff',
  127. backgroundColor: bookConfig.off_on ? '#fff' : '#111'
  128. });
  129. }, [bookConfig.off_on]);
  130. const onLoad = async () => {
  131. if (openBookData?.contentData) {
  132. let { size, current, total, records } = openBookData.contentData
  133. if (records.length < total) {
  134. console.log("加载")
  135. setIsLoad(true)
  136. await getBookContent({ pageNum: 1, pageSize: size + 2 }).then(res => {
  137. setIsLoad(false)
  138. })
  139. } else {
  140. Taro.showToast({
  141. title: '没有更多了~~',
  142. duration: 1000,
  143. icon: 'none'
  144. })
  145. }
  146. }
  147. }
  148. // 计算阅读的位置,存放以备下次阅读的时候跳转到对应位置
  149. const handleScroll = () => {
  150. const query = Taro.createSelectorQuery();
  151. query.selectAll('.shrot-text').boundingClientRect(); // 选择所有子元素
  152. query.selectViewport().scrollOffset(); // 获取视口的滚动位置
  153. query.exec(res => {
  154. const items = res[0]; // 子元素的位置信息
  155. const scrollTop = res[1].scrollTop + indexStore.navHeight; // 视口的滚动距离
  156. // 遍历所有子元素,判断哪个子元素在可视区域
  157. for (let item of items) {
  158. if (item.top <= scrollTop && item.bottom >= scrollTop) {
  159. if (openBookData?.bookId) {
  160. newReadLogIdRef.current = item.id//存放当前可视的段落ID
  161. }
  162. break;
  163. }
  164. }
  165. });
  166. };
  167. const { size, bg, off_on, lh } = bookConfig;
  168. return (
  169. <ScrollView
  170. lowerThreshold={indexStore.navHeight}
  171. refresherTriggered={isLoad}
  172. onScrollToLower={onLoad}
  173. style={{ height: `calc(100vh - ${indexStore.navHeight}px)` }}
  174. scrollY={true}
  175. refresherDefaultStyle='black'
  176. scrollWithAnimation={false}
  177. scrollIntoView={currentScrollId}
  178. onScroll={handleScroll}
  179. >
  180. <View className={`book_article ${off_on ? bg : 'bg_hh'} ${off_on ? 'cl_h' : 'cl_b'}`} onClick={togglePup}>
  181. <View className='header'>
  182. <View className='btns'>
  183. <View className='left'>
  184. <Text
  185. onClick={() => { size !== 'pre_15' && setSize(0) }}
  186. className={!off_on ? size === 'pre_15' ? 'cl_e5' : '' : size === 'pre_15' ? 'cl_d6' : ''}
  187. >字小</Text>
  188. <Text
  189. onClick={() => { size !== 'pre_29' && setSize(1) }}
  190. className={!off_on ? size === 'pre_29' ? 'cl_e5' : '' : size === 'pre_29' ? 'cl_d6' : ''}
  191. >字大</Text>
  192. </View>
  193. <View className='right'>
  194. <Text onClick={() => { setOff() }}>{off_on ? '关灯' : '开灯'}</Text>
  195. <Text onClick={togglePup}>菜单</Text>
  196. </View>
  197. </View>
  198. </View>
  199. {
  200. openBookData?.contentData?.records?.map(item => {
  201. let arr = item.paragraphInfo.content?.match(/[^\n]*\n?/g);
  202. return <View
  203. id={'paragraphNo-' + item.paragraphInfo.paragraphNo}
  204. key={item.paragraphInfo.paragraphNo}
  205. className={`pre ${size} ${lh}`}
  206. >
  207. {
  208. arr?.map((str, index) => {
  209. // let strArr = str?.match(/[^。]*。/g)
  210. return <Text className='shrot-text' id={'paragraphNo-' + item.paragraphInfo.paragraphNo + '-' + index} key={index}>{
  211. str
  212. }</Text>
  213. })
  214. }
  215. </View>
  216. })
  217. }
  218. {pup && <BookConfigPuP {...bookConfig} off_on={off_on} setOff={setOff} setSize={setSize} setBg={setBg} setLh={setLh} />}
  219. </View>
  220. {
  221. isLoad && <UpLoading />
  222. }
  223. </ScrollView>
  224. );
  225. };
  226. export default inject('store')(observer(TopNavBar(BookArticle, { isToBack: true, isReloadBook: true })));