行业知识
uniapp仿追书神器的小说阅读器小程序(附源码)
简介
基于mpvue的小说阅读器小程序,已隐去API,数据脱敏,仅供学习交流使用,如有侵权,请及时联系。
效果展示
项目结构说明
.├── dist # 打包后的代码,也是实际上传小程序的代码├── build # webpack配置文件├── config # 环境配置文件├── project.config.json # 项目配置文件├── src # 程序源文件│ ├── main.js # 入口文件│ ├── components # 可复用的组件(Presentational Components)│ ├── styles # 样式文件│ ├── store # Vuex Store文件│ └── pages # 页面文件
特性
- 搜索小说
- 分享小说,最低层级可至指定章节
- 阅读小说章节
- 追更
- 更多。。。
部分源码
首页
添加你喜欢的小说 import BookItem from '@/components/BookItem';import { getImgSrc, formatTime } from '@/utils';import store from '@/store';export default { data() { return { refreshing: false, // 是否在刷新 }; }, components: { BookItem, }, computed: { // 书架列表 bookList() { return store.state.bookCase; } }, methods: { formatTime, getImgSrc, // 跳转搜索历史页 gotoSearchPage() { wx.navigateTo({ url: '/pages/history/main' }); }, // 跳转详情页面 gotoChapterPage(bookId) { wx.navigateTo({ url: `/pages/chapter/main?bookId=${bookId}` }); store.dispatch('confirmUpdate', bookId); }, // 从书架移除 removeBook(book) { wx.showModal({ title: '提示', content: `确认从书架移除 ${book.title} ?`, success(res) { if (res.confirm) { store.dispatch('removeFromBookCase', book._id); } } }); }, }, // 下拉刷新的时候查询有无更新 async onPullDownRefresh() { this.refreshing = true; const ids = this.bookList.map(book => book._id) || []; await store.dispatch('fetchBookUpdate', ids.join(',')); this.refreshing = false; wx.stopPullDownRefresh(); }, onLoad() { store.dispatch('getBookCase'); setTimeout(() => { if (this.bookList && this.bookList.length >= 1) { wx.startPullDownRefresh(); } }, 500); }}; 搜索历史
大家都在搜 换一批
- {{item.word}}
- {{item}}
详情页
{{bookDetails.title}}
{{bookDetails.author}} | {{bookDetails.minorCate}} | {{wordCount}}字
{{updatedTime}}
开始阅读 +追更新
-
追更人数
{{bookDetails.latelyFollower}} -
读者存留率
{{bookDetails.retentionRatio}}% -
日更新字数
{{bookDetails.serializeWordCount}}
简介
猜你喜欢
{{book.title}}
import store from '@/store';import { getImgSrc, formatTime } from '@/utils';export default { data() { return { a: 1 }; }, methods: { getImgSrc, formatTime, // 跳转搜索页面 gotoSearchPage(query = '') { wx.navigateTo({ url: `/pages/search/main?search=${query}` }); }, // 跳转章节页 gotoChapterPage(bookId = '') { wx.navigateTo({ url: `/pages/chapter/main?bookId=${bookId}` }); }, // 跳转详情页,同一个页面不能跳转? // 用的同一实例? // 数据会变掉? gotoDetailsPage(bookId) { if (bookId) { wx.navigateTo({ url: `/pages/details/main?bookId=${bookId}` }); } }, // 获取详情及推荐 getBookDetails(bookId) { store.dispatch('fetchBookDetails', bookId); store.dispatch('fetchBookLikes', bookId); }, // 追更,加入书柜 async afterMore(bookId) { // 获取书源 const sources = await store.dispatch('fetchBookSource', bookId); // 默认第一个书源,查章节目录 if (Array.isArray(sources) && sources.length > 0) { const currentSource = sources[0]; const { chapters } = await store.dispatch('fetchChapterList', currentSource._id); // 默认第一章节 if (Array.isArray(chapters) && chapters.length > 0) { const currentChapter = chapters[0]; store.dispatch('addToBookCase', { ...this.bookDetails, currentSource, currentChapter }); wx.showToast({ title: '已添加至书架', icon: 'success', duration: 1500 }); } } } }, computed: { // 书籍详情 bookDetails() { return store.state.bookDetails; }, // 猜你喜欢 booksLike() { return store.state.booksLike; }, // 字数 wordCount() { const count = this.bookDetails.wordCount; if (count > 10000) { return `${Math.ceil(count / 10000)}万`; } return count; }, // 更新时间 updatedTime() { return formatTime(this.bookDetails.updated); }, // 封面转过之后的数据(mpvue不支持在 template 内使用 methods 中的函数,尴尬) bookDetailsConver() { return getImgSrc(this.bookDetails.cover); }, // 猜你喜欢数据封面转换 booksLikeConver() { return this.booksLike.map(book => ({ ...book, cover: getImgSrc(book.cover) })); }, // 简介转换 longIntroConver() { try { // 暂时想不到其他法子,先把\n|\r转成标签 return (this.bookDetails.longIntro || '').replace(/\n|\r/g, '
'); } catch (e) { return ''; } }, // 书架 bookCase() { return store.state.bookCase; }, // 是否被加入到书架了 isAddToBookCase() { return this.bookCase.findIndex(book => book._id === this.bookDetails._id) !== -1; } }, onLoad() { const { bookId } = this.$root.$mp.query; if (bookId) { this.getBookDetails(bookId); } // 如果没有书架数据的话,就去获取 if (this.bookCase.length === 0) { store.dispatch('getBookCase'); } }, // 分享 onShareAppMessage() { return { title: `好友向你推荐了一本好书——${this.bookDetails.title}`, path: `/pages/details/main?bookId=${this.bookDetails._id}`, imageUrl: this.bookDetailsConver }; }};
控制中心
上一章 下一章
- 书架
- {{isNightMode? "白天" : "夜间"}}
- 设置
- 目录
- 换源
目录(共{{chaptersSectionCount}}章)
{{chapterSectionArray[currentChapterSectionIndex]}}- {{item.title}}
标签 return (this.chapterDetails.cpContent || this.chapterDetails.body).replace(/\n|\r/g, '
'); } catch (e) { return ''; } }, // 章节目录分段范围 // ['1-100','101-200',....] chapterSectionArray() { const totalArrayCount = Math.ceil(this.chaptersSectionCount / CHAPTER_SECTION_COUNT); return Array.from({ length: totalArrayCount }).map((section, index) => `${(index * CHAPTER_SECTION_COUNT) + 1} - ${Math.min((index + 1) * CHAPTER_SECTION_COUNT, this.chaptersSectionCount)}`); }, }, watch: { showFooterBar(value) { if (!value) { this.showSettingPanel = false; } }, isKeepLight(value) { wx.setKeepScreenOn({ keepScreenOn: value }); }, // 监听showDirectory变化,需要重新计算currentChapterSectionIndex,并还原 showDirectory(value) { // 只在展示的时候还原 if (value) { this.currentChapterSectionIndex = Math.floor(this.currentPageIndex / CHAPTER_SECTION_COUNT); } else { // 只在关闭的时候隐藏底部操作栏 this.showFooterBar = false; } }, // 监听当前章节段落序号,改变章节段落 currentChapterSectionIndex(value) { this.currentChapterSection = _chaptersSection[value]; } }, methods: { // 回首页 gotoHome() { wx.redirectTo({ url: '/pages/index/main' }); }, // 跳转哪一章 gotoTargeChapter(index) { this.showDirectory = false; this.showFooterBar = false; this.currentPageIndex = +index; // 先清空章节内容 store.dispatch('resetChapterDetails'); const chapter = _chapterListData.chapters[index]; if (chapter) { chapter._index = index; store.dispatch('fetchChapterDetails', chapter.link).then(() => { wx.pageScrollTo({ scrollTop: 0, duration: 300 }); }); // 更改titleBar文案 wx.setNavigationBarTitle({ title: chapter.title }); if (this.bookInBookCase) { store.dispatch('addToBookCase', { ...this.bookInBookCase, currentChapter: chapter, __order: false // 倒序插入 }); } } }, gotoTargeChapterFromItem(item, index) { // 计算将要跳往哪一章 // eslint-disable-next-line const targetChapterIndex = (this.currentChapterSectionIndex * CHAPTER_SECTION_COUNT) + Number(index); this.gotoTargeChapter(targetChapterIndex); }, onPageClick() { // 切换底部显示 this.showFooterBar = !this.showFooterBar; }, // 切换目录 toggleCategoryList() { this.showDirectory = !this.showDirectory; }, // 切换夜间/白天模式 toggleNightOrDay() { this.isNightMode = !this.isNightMode; }, // 切换设置面板 toggleSettingPanel() { this.showSettingPanel = !this.showSettingPanel; }, // 跳转换源页面 gotoSourcePage(bookId) { this.showFooterBar = false; // 如果没有加入书架,不允许换源 if (!this.bookInBookCase) { wx.showToast({ title: '加入书架才能换源哦~', icon: 'none', duration: 2000 }); } else { wx.navigateTo({ url: `/pages/source/main?bookId=${bookId}` }); } }, // 改变亮度 changeLightNess(e) { const { value } = e.target; if (typeof value === 'number') { this.lightness = value; wx.setScreenBrightness({ value }); } }, // 改变字体大小 changeFontSize(e) { const { value } = e.target; if (typeof value === 'number') { this.chapterFontSize = value; wx.setStorage({ key: CHAPTER_FONT_SIZE, data: value }); } }, // 保持屏幕常亮 toggleScreenLight(e) { const { value } = e.target; this.isKeepLight = value; wx.setStorage({ key: SCREEN_IS_LIGHT, data: value }); }, // 章节序列切换区间 chapterSectionArrayChange(e) { this.currentChapterSectionIndex = e.target.value; } }, onLoad() { // 更改titleBar标题 wx.setNavigationBarColor({ frontColor: '#ffffff', backgroundColor: '#333333', animation: { duration: 150, timingFunc: 'easeIn' } }); // 获取屏幕亮度 wx.getScreenBrightness({ success: ({ value }) => { if (typeof value === 'number') { this.lightness = value; } } }); // 是否常亮 wx.getStorage({ key: SCREEN_IS_LIGHT, success: ({ data }) => { this.isKeepLight = data; } }); // 获取之前设置的字体大小 wx.getStorage({ key: CHAPTER_FONT_SIZE, success: ({ data }) => { this.chapterFontSize = data; } }); // 获取书架数据 store.dispatch('getBookCase'); const { from } = this.$root.$mp.query; // 如果是从其他地方跳转过来的,比如,分享页 if (from) { this.fromOtherPlace = true; } }, onShow() { const { bookId, sourceId, chapterIndex, from } = this.$root.$mp.query; this.bookInBookCase = this.bookCase.find(book => book._id === bookId); const bookInBookCase = this.bookInBookCase; // eslint-disable-line // 如果重新进来的时候,当前小说是否跟hide之前一致的,一致就不重新获取 // 什么时候不一致?当更改书源的时候 if (_lastChapter && isEqual(bookInBookCase, _lastChapter)) { return; } _lastChapter = bookInBookCase; if (bookId) { store.dispatch('fetchBookSource', bookId) .then((result) => { // 默认拿第一个书源 let source = result[0]; // 如果是从分享页进来的 if (from === 'share' && sourceId) { source = result.find(r => r._id === sourceId) || source; } else if (bookInBookCase && bookInBookCase.currentSource) { source = bookInBookCase.currentSource; } // 只在当前页面临时保存书源 _currentSource = source; return store.dispatch('fetchChapterList', source._id); }).then((result) => { _chapterListData = result; // 只保留必要的属性,减少数据量 _chapterListData.chapters = keepUsefulAttributeInArray(_chapterListData.chapters, ['_index', 'link', 'title', 'id']); // 给章节分段 _chaptersSection = _chunk(_chapterListData.chapters, CHAPTER_SECTION_COUNT); this.currentChapterSection = _chaptersSection[this.currentChapterSectionIndex]; // 章节ID this.chapterListDataId = _chapterListData._id; // 章节数量 this.chaptersSectionCount = _chapterListData.chapters.length; // 默认第一章 let chapter = result.chapters[0]; /* eslint-disable */ // 如果是从分享页进来的 if (from === 'share' && chapterIndex) { this.currentPageIndex = +chapterIndex; chapter = result.chapters[+chapterIndex]; } // 如果已收藏,本地有数据的话 else if (bookInBookCase && bookInBookCase.currentChapter && bookInBookCase.currentChapter._index) { let { _index } = bookInBookCase.currentChapter; // 如果之前的章节比现在书源的总章节还要大, 则最大不能超过该书源的总章节 if (_index > result.chapters.length - 1) { _index = result.chapters.length - 1; } this.currentPageIndex = _index; chapter = result.chapters[_index]; } /* eslint-enable */ // 更改titleBar文案 wx.setNavigationBarTitle({ title: chapter.title }); store.dispatch('fetchChapterDetails', chapter.link); }); } }, onHide() { this.showDirectory = false; this.showFooterBar = false; }, // 分享 onShareAppMessage() { const { title, _id, cover } = this.bookInBookCase || this.bookDetails; let chapterTitle = _get(this.chapterDetails, 'title'); // 有的小说获取到的title是字符串的点.,此时拿书架保存的当前章节的title if (chapterTitle.length chapterTitle = _get(_chapterListData.chapters[this.currentPageIndex], 'title'); } return { title: `${title} - ${chapterTitle}——Deny阅读`, path: `/pages/chapter/main?from=share&bookId=${_id}&sourceId=${_currentSource._id}&chapterIndex=${this.currentPageIndex}`, imageUrl: getImgSrc(cover) }; }, onUnload() { this.showDirectory = false; this.showFooterBar = false; this.currentPageIndex = 0; this.currentChapterSectionIndex = 0; _currentSource = null; _lastChapter = null; }};
下一篇:服务器主板大机箱怎么设置?