From 85f7b2ba6f2138acb02321b168d69ee16e36a807 Mon Sep 17 00:00:00 2001 From: huliyong <2783385703@qq.com> Date: Wed, 2 Jul 2025 18:51:08 +0800 Subject: [PATCH] =?UTF-8?q?feat(=E9=98=85=E8=AF=BB=E5=8A=9F=E8=83=BD):=20?= =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E6=89=B9=E9=87=8F=E8=AE=A2=E9=98=85=E5=92=8C?= =?UTF-8?q?=E9=98=85=E8=AF=BB=E8=AE=B0=E5=BD=95=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 在订阅弹窗中新增批量订阅功能,支持选择章节数量 - 添加获取用户阅读记录的API接口 - 根据阅读记录自动跳转到上次阅读章节 - 优化付费章节标识,区分已付费和未付费状态 - 修复书籍全选功能在阅读列表中的逻辑错误 - 调整本地开发环境的API基础地址 --- api/model/bookshelf.js | 5 + config.js | 4 +- pages/index/bookshelf.vue | 4 +- pages/index/category.vue | 1 - pages_order/author/createNovel.vue | 2 +- pages_order/components/novel/chapterPopup.vue | 17 +- pages_order/components/novel/subscriptionPopup.vue | 256 ++++++++++++++++++++- pages_order/novel/novelDetail.vue | 35 ++- pages_order/novel/readnovels.vue | 83 ++++++- 9 files changed, 393 insertions(+), 14 deletions(-) diff --git a/api/model/bookshelf.js b/api/model/bookshelf.js index d8e6ef8..62c8cf2 100644 --- a/api/model/bookshelf.js +++ b/api/model/bookshelf.js @@ -42,6 +42,11 @@ const api = { method: 'POST', auth: true, }, + // 根据书籍id获取当前阅读章节 + getReadChapterByBookId: { + url: '/all_book/getReadChapterByBookId', + method: 'GET', + }, } export default api \ No newline at end of file diff --git a/config.js b/config.js index d5532e7..0b239b6 100644 --- a/config.js +++ b/config.js @@ -8,13 +8,13 @@ import uvUI from '@/uni_modules/uv-ui-tools' Vue.use(uvUI); // 当前环境 -const type = 'prod' +const type = 'local' // 环境配置 const config = { local : { - baseUrl : 'http://127.0.0.1:8003/novel-admin', + baseUrl : 'http://127.0.0.1:8002/novel-admin', }, dev : { baseUrl : 'http://h5.xzaiyp.top/novel-admin', diff --git a/pages/index/bookshelf.vue b/pages/index/bookshelf.vue index 4523c03..5818068 100644 --- a/pages/index/bookshelf.vue +++ b/pages/index/bookshelf.vue @@ -332,11 +332,11 @@ selectAll() { if (this.activeTab === 'read') { // 已经全选,则取消全选 - if (this.selectedItems.length === this.novels.length) { + if (this.selectedItems.length === this.list.length) { this.selectedItems = []; } else { // 全选所有小说 - this.selectedItems = this.novels.map(novel => novel.id); + this.selectedItems = this.list.map(novel => novel.id); } } else { // 已经全选,则取消全选 diff --git a/pages/index/category.vue b/pages/index/category.vue index 8154678..fb22f21 100644 --- a/pages/index/category.vue +++ b/pages/index/category.vue @@ -109,7 +109,6 @@ }, clickTabs({index}){ this.current = index - this.currentChildren = 0 // 更新对应分类的书籍列表 this.getData() }, diff --git a/pages_order/author/createNovel.vue b/pages_order/author/createNovel.vue index f8ef004..1213fe4 100644 --- a/pages_order/author/createNovel.vue +++ b/pages_order/author/createNovel.vue @@ -192,7 +192,7 @@ }) return } - if (!this.formData.shopClass) { + if (this.classList.length === 0) { uni.showToast({ title: '请选择作品分类', icon: 'none' diff --git a/pages_order/components/novel/chapterPopup.vue b/pages_order/components/novel/chapterPopup.vue index ec23f61..2c963df 100644 --- a/pages_order/components/novel/chapterPopup.vue +++ b/pages_order/components/novel/chapterPopup.vue @@ -17,7 +17,8 @@ :class="['catalog-item theme-transition', {active: idx == currentIndex}]"> {{ item.title }} - 付费 + + 付费 @@ -172,6 +173,15 @@ padding: 2rpx 18rpx; margin-left: 16rpx; } + + .paid-tag { + background: #e8f5e8; + color: #4caf50; + border-radius: 20rpx; + font-size: 24rpx; + padding: 2rpx 18rpx; + margin-left: 16rpx; + } } } @@ -212,6 +222,11 @@ background: rgba(255, 153, 0, 0.2); color: #ff9900; } + + .paid-tag { + background: rgba(76, 175, 80, 0.2); + color: #4caf50; + } } } } diff --git a/pages_order/components/novel/subscriptionPopup.vue b/pages_order/components/novel/subscriptionPopup.vue index abf874a..e15be37 100644 --- a/pages_order/components/novel/subscriptionPopup.vue +++ b/pages_order/components/novel/subscriptionPopup.vue @@ -7,10 +7,40 @@ :customStyle="popupStyle"> {{ title }} - + + + - - + + + + + + + 选择订阅章节数量 + + 从当前章节开始,可订阅 {{ availableChaptersCount }} 章 + + + 暂无需要付费的章节 + + + + 连续订阅 {{ batchCount }} 章 + + + + + + + + + + + @@ -28,9 +58,27 @@ type: String, default: '这是付费章节 需要订阅后才能阅读' }, + chapterList: { + type: Array, + default: () => [] + }, + currentChapter: { + type: Object, + default: () => ({}) + }, + currentIndex: { + type: Number, + default: 0 + }, + bookId: { + type: [String, Number], + default: '' + } }, data() { return { + showBatchDialog: false, + batchCount: 5 } }, computed: { @@ -42,6 +90,23 @@ 'border-radius': '24rpx', 'min-width': '500rpx' } + }, + // 计算从当前章节开始可以订阅的章节数量 + availableChaptersCount() { + if (!this.chapterList.length || this.currentIndex < 0) return 0; + + let count = 0; + for (let i = this.currentIndex; i < this.chapterList.length; i++) { + const chapter = this.chapterList[i]; + if (chapter.isPay === 'Y' && !chapter.pay) { + count++; + } + } + return count; + }, + // 限制批量订阅数量不能超过可订阅章节数 + maxBatchCount() { + return Math.max(1, this.availableChaptersCount); } }, methods: { @@ -59,6 +124,49 @@ handleSubscribe() { this.$emit('subscribe'); this.close(); + }, + + // 处理视频解锁按钮点击 + handleVideoUnlock() { + this.$emit('videoUnlock'); + this.close(); + }, + + // 显示批量订阅选择界面 + showBatchSubscribe() { + // 根据可订阅章节数设置合理的默认值 + this.batchCount = Math.min(5, this.maxBatchCount); + this.showBatchDialog = true; + }, + + // 取消批量订阅 + cancelBatchSubscribe() { + this.showBatchDialog = false; + }, + + // 处理批量订阅 + handleBatchSubscribe() { + this.$emit('batchSubscribe', this.batchCount); + this.showBatchDialog = false; + }, + + // 减少批量订阅数量 + decreaseBatchCount() { + if (this.batchCount > 1) { + this.batchCount--; + } + }, + + // 增加批量订阅数量 + increaseBatchCount() { + if (this.batchCount < this.maxBatchCount) { + this.batchCount++; + } + }, + + // 验证批量订阅数量 + validateBatchCount() { + this.batchCount = Math.max(1, Math.min(this.maxBatchCount, parseInt(this.batchCount))); } } } @@ -113,6 +221,100 @@ } } } + + .batch-subscribe-content { + .batch-title { + font-size: 28rpx; + color: #333; + margin-bottom: 16rpx; + text-align: center; + } + + .batch-info { + font-size: 24rpx; + color: #666; + margin-bottom: 32rpx; + text-align: center; + } + + .batch-current-info { + font-size: 24rpx; + color: #666; + text-align: center; + margin-bottom: 16rpx; + } + + .batch-counter { + display: flex; + align-items: center; + justify-content: center; + gap: 16rpx; + margin-bottom: 32rpx; + + .counter-btn { + width: 64rpx; + height: 64rpx; + border-radius: 50%; + background: #f5f5f5; + border: none; + font-size: 32rpx; + display: flex; + align-items: center; + justify-content: center; + color: #333; + + &:disabled { + opacity: 0.5; + cursor: not-allowed; + } + } + + .counter-input { + width: 120rpx; + height: 64rpx; + text-align: center; + border: 1px solid #ddd; + border-radius: 8rpx; + font-size: 28rpx; + } + } + + .batch-btns { + display: flex; + gap: 24rpx; + justify-content: center; + + .batch-cancel-btn { + background: #f5f5f5; + color: #666; + border-radius: 32rpx; + font-size: 28rpx; + padding: 0 32rpx; + border: none; + } + + .batch-confirm-btn { + background: #ff9800; + color: #fff; + border-radius: 32rpx; + font-size: 28rpx; + padding: 0 32rpx; + border: none; + + &:disabled { + background: #ccc; + color: #666; + cursor: not-allowed; + } + } + } + + .no-chapters-tip { + font-size: 24rpx; + color: #999; + text-align: center; + } + } } &.dark-mode { @@ -143,6 +345,54 @@ } } } + + .batch-subscribe-content { + .batch-title { + color: $dark-text-color-primary; + } + + .batch-info { + color: $dark-text-color-tertiary; + } + + .batch-current-info { + color: $dark-text-color-primary; + } + + .batch-counter { + .counter-btn { + background: $dark-bg-color-tertiary; + color: $dark-text-color-primary; + } + + .counter-input { + background: $dark-bg-color-secondary; + border: 1px solid $dark-border-color; + color: $dark-text-color-primary; + } + } + + .batch-btns { + .batch-cancel-btn { + background: $dark-bg-color-tertiary; + color: $dark-text-color-secondary; + } + + .batch-confirm-btn { + background: #ff9800; + color: #fff; + + &:disabled { + background: $dark-bg-color-tertiary; + color: $dark-text-color-tertiary; + } + } + } + + .no-chapters-tip { + color: $dark-text-color-tertiary; + } + } } } } diff --git a/pages_order/novel/novelDetail.vue b/pages_order/novel/novelDetail.vue index bbbe042..67c9d74 100644 --- a/pages_order/novel/novelDetail.vue +++ b/pages_order/novel/novelDetail.vue @@ -228,6 +228,7 @@ chapterList : [],//章节列表 isBooshelf : false, + readingRecord: {}, // 用户的阅读记录 } }, computed: {}, @@ -243,6 +244,7 @@ this.isAddBook() if(this.isLogin){ this.getAchievement() + this.getReadingRecord() } }, methods: { @@ -338,7 +340,26 @@ bookId: this.id }) }, + // 获取用户的阅读记录 + async getReadingRecord() { + try { + const res = await this.$fetch('getReadChapterByBookId', { + bookId : this.id + }); + + this.readingRecord = res || {}; + } catch (error) { + console.error('获取阅读记录失败:', error); + this.readingRecord = {}; + } + }, toRead() { + // 判断用户是否已登录 + if (!uni.getStorageSync('token')) { + this.$utils.toLogin(); + return; + } + if (!this.fastCatalog) { uni.showToast({ title: '暂无章节', @@ -347,9 +368,17 @@ return } - uni.navigateTo({ - url: `/pages_order/novel/readnovels?cid=${this.fastCatalog.id}&id=${this.id}` - }) + // 如果有阅读记录(novelId存在),直接跳转到该章节 + if (this.readingRecord && this.readingRecord.id) { + uni.navigateTo({ + url: `/pages_order/novel/readnovels?cid=${this.readingRecord.id}&id=${this.id}` + }); + } else { + // 没有阅读记录,跳转到第一章 + uni.navigateTo({ + url: `/pages_order/novel/readnovels?cid=${this.fastCatalog.id}&id=${this.id}` + }); + } }, selectChapter({item, index}){ uni.navigateTo({ diff --git a/pages_order/novel/readnovels.vue b/pages_order/novel/readnovels.vue index 02e5593..da03d08 100644 --- a/pages_order/novel/readnovels.vue +++ b/pages_order/novel/readnovels.vue @@ -66,8 +66,14 @@ + @subscribe="goToSubscription" + @batchSubscribe="handleBatchSubscribe" + @videoUnlock="handleVideoUnlock" /> @@ -116,6 +122,9 @@ return -1 }, + currentChapterInfo() { + return this.chapterList.find(chapter => chapter.id == this.cid) || {} + } }, onLoad({ id, @@ -269,6 +278,78 @@ this.$refs.subscriptionPopup.close() } }, + + // 处理批量订阅 + async handleBatchSubscribe(batchCount) { + try { + // 获取从当前章节开始的连续章节ID + const chapterIds = []; + const startIndex = this.currentIndex; + + for (let i = 0; i < batchCount && (startIndex + i) < this.chapterList.length; i++) { + const chapter = this.chapterList[startIndex + i]; + if (chapter.isPay === 'Y' && !chapter.pay) { + chapterIds.push(chapter.id); + } + } + + if (chapterIds.length === 0) { + uni.showToast({ + title: '没有需要购买的章节', + icon: 'none' + }); + return; + } + + // 调用批量订阅接口 + await this.$fetch('buyNovel', { + bookId: this.id, + novelId: chapterIds.join(',') + }); + + uni.showToast({ + title: `成功订阅${chapterIds.length}章`, + icon: 'success' + }); + + // 刷新章节列表状态 + this.getBookCatalogList(); + this.isPay = false; + this.updateSub(); + + } catch (error) { + console.error('批量订阅失败:', error); + uni.showToast({ + title: '订阅失败,请重试', + icon: 'none' + }); + } + }, + + // 处理视频解锁 + async handleVideoUnlock() { + try { + await this.$fetch('openBookCatalog', { + bookId: this.id, + catalogId: this.cid + }); + + uni.showToast({ + title: '视频解锁成功', + icon: 'success' + }); + + this.isPay = false; + this.updateSub(); + + } catch (error) { + console.error('视频解锁失败:', error); + uni.showToast({ + title: '解锁失败,请重试', + icon: 'none' + }); + } + }, }, }