前端-胡立永 2 weeks ago
parent
commit
8c4c72fa4d
5 changed files with 581 additions and 36 deletions
  1. +17
    -5
      components/novel/workItem.vue
  2. +1
    -1
      config.js
  3. +20
    -8
      pages_order/novel/ReaderAchievement.vue
  4. +1
    -1
      pages_order/novel/Tipping.vue
  5. +542
    -21
      pages_order/novel/readnovels.vue

+ 17
- 5
components/novel/workItem.vue View File

@ -62,6 +62,10 @@
return statusMap[this.work.status] || 'ongoing';
},
statusText() {
// 使 dictText使
if (this.work.status_dictText) {
return this.work.status_dictText;
}
const textMap = {
'0': '连载中',
'1': '已完结'
@ -70,19 +74,23 @@
},
toolStatusClass() {
const toolStatusMap = {
'0': 'ongoing',
'1': 'completed',
'2': 'error',
'0': 'completed',
'1': 'error',
// '2': 'error',
};
return toolStatusMap[this.work.toolStatus] || '';
},
toolStatusText() {
// 使 dictText使
if (this.work.toolStatus_dictText) {
return this.work.toolStatus_dictText;
}
const textMap = {
'0': '设置审核中',
'1': '设置审核通过',
'2': '设置审核不通过'
};
return textMap[this.work.toolStatus] || '连载中';
return textMap[this.work.toolStatus] || '';
},
bookStatusClass() {
const bookStatusMap = {
@ -93,12 +101,16 @@
return bookStatusMap[this.work.bookStatus] || '';
},
bookStatusText() {
// 使 dictText使
if (this.work.bookStatus_dictText) {
return this.work.bookStatus_dictText;
}
const textMap = {
'0': '发布审核中',
'1': '发布审核通过',
'2': '发布审核不通过'
};
return textMap[this.work.bookStatus] || '连载中';
return textMap[this.work.bookStatus] || '';
},
},
methods: {


+ 1
- 1
config.js View File

@ -8,7 +8,7 @@ import uvUI from '@/uni_modules/uv-ui-tools'
Vue.use(uvUI);
// 当前环境
const type = 'prod'
const type = 'local'
// 环境配置


+ 20
- 8
pages_order/novel/ReaderAchievement.vue View File

@ -63,9 +63,10 @@
</view>
</view>
<view class="bottom-btn-area">
<button class="submit-btn" :disabled="isPending" :class="{disabled: isPending, pending: isPending}"
<button class="submit-btn" :disabled="isPending"
:class="{disabled: isPending}"
@click="submit">
{{ isPending ? '设置' : '提交申请' }}
{{ form.id ? '设置' : '提交申请' }}
</button>
</view>
</view>
@ -88,7 +89,7 @@
},
methods: {
async getDetail(){
this.form = await this.$fetch('getAchievement')
this.form = await this.$fetch('getAchievement') || {}
this.isPending = this.form.status == 0
},
async submit() {
@ -118,14 +119,25 @@
// await Promise.all(arr)
if(this.$utils.verificationAll(this.form, {
oneName : '请填写所有成就名称',
twoName : '请填写所有成就名称',
threeName : '请填写所有成就名称',
oneName : '请填写' + this.list[0].title,
twoName : '请填写' + this.list[1].title,
threeName : '请填写' + this.list[2].title,
})){
return
}
let data = {
oneName : this.form.oneName,
twoName : this.form.twoName,
threeName : this.form.threeName,
status : 0
}
if(this.form.id){
data.id = this.form.id
}
await this.$fetch('setAchievementName', this.form)
await this.$fetch('setAchievementName', data)
uni.showToast({
title: '提交成功',
@ -259,7 +271,7 @@
}
.submit-btn.disabled {
background: #807a7a;
background: #aaa;
color: #fff;
}


+ 1
- 1
pages_order/novel/Tipping.vue View File

@ -36,7 +36,7 @@
</view>
<view class="name">{{
topList[0].hanHaiMember
&& topList[0].hanHaiMember.nickName
&& topList[0].hanHaiMember.nickName || '用户'
}}</view>
<view class="score">{{ topList[0].num }} 亲密值</view>
<view class="level">{{


+ 542
- 21
pages_order/novel/readnovels.vue View File

@ -22,8 +22,8 @@
<scroll-view class="chapter-content" :class="{'full-content': isFullScreen}"
@scroll="handleScroll" @tap="handleContentClick">
<view class="chapter-content" :class="{'full-content': isFullScreen}"
@tap="handleContentClick">
<view class="chapter-content-item">
<view class="chapter-title">{{ currentChapter }}</view>
@ -32,9 +32,28 @@
{{ paragraph }}
</view>
</view>
<!-- 加载更多提示区域 -->
<view class="load-more-area" v-if="autoLoadNext && !isPay">
<view class="load-more-content" v-if="!isAutoLoading && hasNextChapter && !isAtBottom">
<view class="load-more-line"></view>
<text class="load-more-text">滚动到底部停留2秒自动加载下一章</text>
<view class="load-more-line"></view>
</view>
<view class="waiting-content" v-else-if="!isAutoLoading && hasNextChapter && isAtBottom">
<uv-icon name="time" color="#ff6b6b" size="30rpx"></uv-icon>
<text class="waiting-text">正在底部停留 {{ bottomStayTime.toFixed(1) }}s / 2.0s</text>
<text class="tip-text">向上滚动可取消</text>
</view>
<view class="loading-content" v-else-if="isAutoLoading">
<uv-icon name="clock" color="#4a90e2" size="30rpx"></uv-icon>
<text class="loading-text">{{ countdown }}秒后自动跳转下一章</text>
<text class="cancel-text" @tap="cancelAutoLoad">点击取消</text>
</view>
</view>
</view>
</scroll-view>
</view>
<view class="bottom-bar" :class="{'bottom-bar-hidden': isFullScreen}">
<view class="bottom-left">
@ -48,6 +67,18 @@
</view>
<text class="bar-label">{{ isDarkMode ? '白天' : '夜间' }}</text>
</view>
<view class="bar-item" @click="toggleAutoLoad">
<view class="bar-icon">
<uv-icon :name="autoLoadNext ? 'play-circle-fill' : 'play-circle'"></uv-icon>
</view>
<text class="bar-label">{{ autoLoadNext ? '自动' : '手动' }}</text>
</view>
<!-- <view class="bar-item" @click="resetReadingPosition">
<view class="bar-icon">
<uv-icon name="rewind-left"></uv-icon>
</view>
<text class="bar-label">重读</text>
</view> -->
</view>
<view class="bottom-right">
<button class="outline-btn"
@ -110,7 +141,23 @@
//
isPay : false,
isBooshelf : false,
isBooshelf : false,
//
autoLoadNext: true, //
lastScrollTop: 0, //
isAutoLoading: false, //
autoLoadTimer: null, //
countdown: 3, //
scrollThrottle: null, //
triggerChecked: false, //
bottomStayTime: 0, //
bottomCheckTimer: null, //
isAtBottom: false, //
//
savePositionThrottle: null, //
restorePositionTimer: null, //
}
},
computed: {
@ -124,6 +171,9 @@
},
currentChapterInfo() {
return this.chapterList.find(chapter => chapter.id == this.cid) || {}
},
hasNextChapter() {
return this.currentIndex >= 0 && this.currentIndex < this.chapterList.length - 1
}
},
onLoad({
@ -139,6 +189,28 @@
this.getBookCatalogList()
this.isAddBook()
},
onPageScroll(e) {
const scrollTop = e.scrollTop;
//
if (scrollTop > 100) { // 100rpx
this.saveReadingPosition(scrollTop);
}
//
if (!this.autoLoadNext || this.isAutoLoading || this.isPay || !this.hasNextChapter || this.triggerChecked) {
return;
}
//
if (this.scrollThrottle) {
clearTimeout(this.scrollThrottle);
}
this.scrollThrottle = setTimeout(() => {
this.checkAutoLoadTrigger(scrollTop);
}, 100);
},
mounted() {
//
this.isFullScreen = true;
@ -183,12 +255,54 @@
//
this.updateReadProgress()
//
//
this.lastScrollTop = 0;
if (this.autoLoadTimer) {
clearInterval(this.autoLoadTimer);
this.autoLoadTimer = null;
}
if (this.scrollThrottle) {
clearTimeout(this.scrollThrottle);
this.scrollThrottle = null;
}
if (this.savePositionThrottle) {
clearTimeout(this.savePositionThrottle);
this.savePositionThrottle = null;
}
if (this.restorePositionTimer) {
clearTimeout(this.restorePositionTimer);
this.restorePositionTimer = null;
}
this.clearBottomTimer(); //
this.isAutoLoading = false;
this.countdown = 3;
this.triggerChecked = false; //
this.isAtBottom = false; //
this.bottomStayTime = 0; //
//
this.$nextTick(() => {
uni.pageScrollTo({
scrollTop: 0,
duration: 0
});
//
const key = this.getReadingPositionKey();
let hasSavedPosition = false;
try {
const positionData = uni.getStorageSync(key);
hasSavedPosition = positionData && positionData.scrollTop > 100;
} catch (error) {
console.warn('检查保存位置失败:', error);
}
if (hasSavedPosition) {
//
this.restoreReadingPosition();
} else {
//
uni.pageScrollTo({
scrollTop: 0,
duration: 0
});
}
})
})
},
@ -205,12 +319,258 @@
handleContentClick() {
this.toggleFullScreen();
},
handleScroll(e) {
// scroll-viewscroll-view
// onPageScroll
},
toggleFullScreen() {
this.isFullScreen = !this.isFullScreen
},
//
checkAutoLoadTrigger(scrollTop) {
// 使
uni.createSelectorQuery().select('.chapter-content').boundingClientRect((rect) => {
if (rect) {
const windowHeight = uni.getSystemInfoSync().windowHeight;
const contentBottom = rect.bottom;
const distanceToBottom = contentBottom - windowHeight;
// 100rpx
const isNearBottom = distanceToBottom < 100;
if (isNearBottom) {
//
if (!this.isAtBottom) {
this.isAtBottom = true;
this.bottomStayTime = 0;
console.log('到达章节底部,开始计时...');
this.startBottomTimer();
}
} else {
//
if (this.isAtBottom) {
this.isAtBottom = false;
this.bottomStayTime = 0;
this.clearBottomTimer();
console.log('离开章节底部,重置计时');
}
}
}
this.lastScrollTop = scrollTop;
}).exec();
},
//
startBottomTimer() {
this.clearBottomTimer(); //
this.bottomCheckTimer = setInterval(() => {
//
if (this.isAtBottom) {
this.bottomStayTime += 0.1; // 100ms0.1
console.log('底部停留时间:', this.bottomStayTime.toFixed(1) + 's');
// 2
if (this.bottomStayTime >= 2) {
console.log('底部停留足够时间,触发自动加载');
this.clearBottomTimer();
this.autoLoadNextChapter();
}
} else {
//
this.clearBottomTimer();
}
}, 100);
},
//
clearBottomTimer() {
if (this.bottomCheckTimer) {
clearInterval(this.bottomCheckTimer);
this.bottomCheckTimer = null;
}
},
// key
getReadingPositionKey() {
return `novel_reading_position_${this.id}_${this.cid}`;
},
//
saveReadingPosition(scrollTop) {
//
if (this.savePositionThrottle) {
clearTimeout(this.savePositionThrottle);
}
this.savePositionThrottle = setTimeout(() => {
const key = this.getReadingPositionKey();
const positionData = {
scrollTop: scrollTop,
timestamp: Date.now(),
chapterTitle: this.currentChapter
};
try {
uni.setStorageSync(key, positionData);
console.log('保存阅读位置:', scrollTop, '章节:', this.currentChapter);
} catch (error) {
console.warn('保存阅读位置失败:', error);
}
}, 1000); // 1
},
//
restoreReadingPosition() {
const key = this.getReadingPositionKey();
try {
const positionData = uni.getStorageSync(key);
if (positionData && positionData.scrollTop > 0) {
console.log('恢复阅读位置:', positionData.scrollTop, '章节:', positionData.chapterTitle);
// 5
const now = Date.now();
const saveTime = positionData.timestamp || 0;
const shouldShowToast = (now - saveTime) > 5 * 60 * 1000; // 5
// DOM
this.$nextTick(() => {
//
this.restorePositionTimer = setTimeout(() => {
uni.pageScrollTo({
scrollTop: positionData.scrollTop,
duration: 0
});
// 5
if (shouldShowToast) {
uni.showToast({
title: '已恢复阅读位置',
icon: 'none',
duration: 1500
});
}
}, 500);
});
}
} catch (error) {
console.warn('恢复阅读位置失败:', error);
}
},
//
clearReadingPosition() {
const key = this.getReadingPositionKey();
try {
uni.removeStorageSync(key);
console.log('清除阅读位置记录:', this.currentChapter);
} catch (error) {
console.warn('清除阅读位置失败:', error);
}
},
//
resetReadingPosition() {
uni.showModal({
title: '重新阅读',
content: '确定要清除当前章节的阅读记录并回到开头吗?',
confirmText: '确定',
cancelText: '取消',
success: (res) => {
if (res.confirm) {
//
this.clearReadingPosition();
//
uni.pageScrollTo({
scrollTop: 0,
duration: 0
});
uni.showToast({
title: '已重置到章节开头',
icon: 'success',
duration: 1500
});
}
}
});
},
//
autoLoadNextChapter() {
if (this.isAutoLoading || !this.hasNextChapter || this.triggerChecked) return;
this.triggerChecked = true; //
this.isAutoLoading = true;
this.countdown = 3;
//
if (this.autoLoadTimer) {
clearInterval(this.autoLoadTimer);
}
console.log('开始自动加载下一章倒计时');
//
this.startCountdown();
},
//
startCountdown() {
const countdownInterval = setInterval(() => {
this.countdown--;
if (this.countdown <= 0) {
clearInterval(countdownInterval);
//
this.clearReadingPosition();
this.nextChapter(1);
this.isAutoLoading = false;
this.countdown = 3;
}
}, 1000);
// interval便
this.autoLoadTimer = countdownInterval;
},
//
cancelAutoLoad() {
if (this.autoLoadTimer) {
clearInterval(this.autoLoadTimer);
this.autoLoadTimer = null;
}
if (this.savePositionThrottle) {
clearTimeout(this.savePositionThrottle);
this.savePositionThrottle = null;
}
if (this.restorePositionTimer) {
clearTimeout(this.restorePositionTimer);
this.restorePositionTimer = null;
}
this.clearBottomTimer(); //
this.isAutoLoading = false;
this.countdown = 3;
this.triggerChecked = false; //
this.isAtBottom = false; //
this.bottomStayTime = 0; //
uni.showToast({
title: '已取消自动跳转',
icon: 'none'
});
},
//
toggleAutoLoad() {
this.autoLoadNext = !this.autoLoadNext;
//
if (!this.autoLoadNext && this.isAutoLoading) {
this.cancelAutoLoad();
}
uni.showToast({
title: this.autoLoadNext ? '已开启自动加载' : '已关闭自动加载',
icon: 'none'
});
},
async goToSubscription() {
await this.$fetch('buyNovel', {
@ -225,6 +585,9 @@
item,
index
}) {
// ID
const previousCid = this.cid;
this.cid = item.id
this.isFullScreen = true
this.getBookCatalogDetail()
@ -241,6 +604,28 @@
this.cid = this.chapterList[index].id
this.isFullScreen = true
this.getBookCatalogDetail()
//
this.isAutoLoading = false;
this.triggerChecked = false;
if (this.autoLoadTimer) {
clearInterval(this.autoLoadTimer);
this.autoLoadTimer = null;
}
if (this.scrollThrottle) {
clearTimeout(this.scrollThrottle);
this.scrollThrottle = null;
}
if (this.savePositionThrottle) {
clearTimeout(this.savePositionThrottle);
this.savePositionThrottle = null;
}
if (this.restorePositionTimer) {
clearTimeout(this.restorePositionTimer);
this.restorePositionTimer = null;
}
this.clearBottomTimer(); //
this.isAtBottom = false; //
this.bottomStayTime = 0; //
},
addToBookshelf() {
this.$fetch('addReadBook', {
@ -329,18 +714,18 @@
//
async handleVideoUnlock() {
try {
await this.$fetch('openBookCatalog', {
bookId: this.id,
catalogId: this.cid
});
// await this.$fetch('openBookCatalog', {
// bookId: this.id,
// catalogId: this.cid
// });
uni.showToast({
title: '视频解锁成功',
icon: 'success'
title: '暂未开放',
icon: 'none'
});
this.isPay = false;
this.updateSub();
// this.isPay = false;
// this.updateSub();
} catch (error) {
console.error('视频解锁失败:', error);
@ -351,6 +736,26 @@
}
},
},
beforeDestroy() {
//
if (this.autoLoadTimer) {
clearInterval(this.autoLoadTimer);
this.autoLoadTimer = null;
}
if (this.scrollThrottle) {
clearTimeout(this.scrollThrottle);
this.scrollThrottle = null;
}
if (this.savePositionThrottle) {
clearTimeout(this.savePositionThrottle);
this.savePositionThrottle = null;
}
if (this.restorePositionTimer) {
clearTimeout(this.restorePositionTimer);
this.restorePositionTimer = null;
}
this.clearBottomTimer(); //
}
}
</script>
@ -432,6 +837,45 @@
}
}
}
.load-more-area {
.load-more-content {
.load-more-line {
background: linear-gradient(to right, transparent, #444, transparent);
}
.load-more-text {
color: #666;
}
}
.waiting-content {
background: #4a3728;
border-color: #6b5b47;
.waiting-text {
color: #ff8a80;
}
.tip-text {
color: #999;
}
}
.loading-content {
background: #2a2a2a;
.loading-text {
color: #4a90e2;
}
.cancel-text {
color: #999;
}
}
}
}
.top-controls {
@ -650,5 +1094,82 @@
}
}
}
/* 加载更多区域样式 */
.load-more-area {
margin-top: 60rpx;
padding: 40rpx 0;
width: 100%;
.load-more-content {
display: flex;
align-items: center;
justify-content: center;
.load-more-line {
flex: 1;
height: 2rpx;
background: linear-gradient(to right, transparent, #e0e0e0, transparent);
}
.load-more-text {
font-size: 24rpx;
color: #999;
margin: 0 30rpx;
white-space: nowrap;
}
}
.waiting-content {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding: 20rpx;
background: #fff3cd;
border-radius: 16rpx;
margin: 0 60rpx;
border: 2rpx solid #ffeaa7;
.waiting-text {
font-size: 28rpx;
color: #ff6b6b;
margin: 10rpx 0 5rpx 0;
font-weight: 500;
}
.tip-text {
font-size: 22rpx;
color: #666;
font-style: italic;
}
}
.loading-content {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding: 20rpx;
background: #f8f9fa;
border-radius: 16rpx;
margin: 0 60rpx;
.loading-text {
font-size: 28rpx;
color: #4a90e2;
margin: 10rpx 0 5rpx 0;
font-weight: 500;
}
.cancel-text {
font-size: 22rpx;
color: #666;
text-decoration: underline;
}
}
}
}
</style>

Loading…
Cancel
Save