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">
-
@@ -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'
+ });
+ }
+ },
},
}