小工具知识网

当前位置: 首页 > 行业知识 > uniapp仿追书神器的小说阅读器小程序(附源码)

行业知识

uniapp仿追书神器的小说阅读器小程序(附源码)

2026-11-06 14:50:52 来源:互联网转载

简介

基于mpvue的小说阅读器小程序,已隐去API,数据脱敏,仅供学习交流使用,如有侵权,请及时联系。

效果展示

项目结构说明

.├── dist                     # 打包后的代码,也是实际上传小程序的代码├── build                    # webpack配置文件├── config                   # 环境配置文件├── project.config.json      # 项目配置文件├── src                      # 程序源文件│   ├── main.js              # 入口文件│   ├── components           # 可复用的组件(Presentational Components)│   ├── styles               # 样式文件│   ├── store                # Vuex Store文件│   └── pages                # 页面文件

特性

  1. 搜索小说
  2. 分享小说,最低层级可至指定章节
  3. 阅读小说章节
  4. 追更
  5. 更多。。。

部分源码

首页

                                                添加你喜欢的小说      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}}
关闭 // /pages/chapter/main?from=share&bookId=${书籍ID}&sourceId=${书源ID}&chapterIndex=${章节号}import store from '@/store';import isEqual from 'lodash/isEqual';import _chunk from 'lodash/chunk';import _get from 'lodash/get';import { CHAPTER_FONT_SIZE, SCREEN_IS_LIGHT, CHAPTER_SECTION_COUNT } from '@/utils/constants';import { keepUsefulAttributeInArray, getImgSrc } from '@/utils';// 临时变量,书源let _currentSource = null;// 临时变量,保存页面onHide之前的数据,与onShow之后做对比,如果一直就不重新获取let _lastChapter = null;// 保存章节数据,不放在data中,防止数据过大,造成卡顿let _chapterListData = { chapters: []};// 保存章节分段数据let _chaptersSection = [];export default { data() { return { CHAPTER_SECTION_COUNT, fromOtherPlace: false, // 是否从其他地方跳转过来 showDirectory: false, // 是否展示目录 showFooterBar: false, // 是否展示底部菜单 isNightMode: false, // 是否是夜间模式 currentPageIndex: 0, // 当前查看第几章 bookInBookCase: null, // 书籍对应的书架的内容 lightness: 0, // 亮度 showSettingPanel: false, // 是否展示设置面板 chapterFontSize: 14, // 默认字体大小 isKeepLight: false, // 屏幕是否保持常亮 currentChapterSectionIndex: 0, // 当前处在章节分段第几段 chapterListDataId: 0, // 章节ID chaptersSectionCount: 0, // 章节数量 currentChapterSection: [], // 当前查看的章节段落 }; }, computed: { // 书架内容 bookCase() { return store.state.bookCase; }, // 书籍详情 bookDetails() { return store.state.bookDetails; }, // 书籍对应的书架的内容 bookInBookCase() { const { bookId } = this.$root.$mp.query; return this.bookCase.find(book => book._id === bookId); }, // 书源 sourceList() { return store.state.sourceList; }, // 章节内容 chapterDetails() { return store.state.chapterDetails; }, // 章节内容转换 chapterDetailsConver() { try { // 暂时想不到其他法子,先把\n|\r转成

标签 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; }};

上一篇:揭秘汇编语言编程,深度解析32汇编源码

下一篇:服务器主板大机箱怎么设置?