<template>
|
|
<!-- 小说文本页面 -->
|
|
<view class="reader-container">
|
|
<view class="top-controls" :class="{'top-controls-hidden': isFullScreen}">
|
|
<view class="controls-inner">
|
|
<view class="left">
|
|
<uv-icon name="arrow-left" @click="$utils.navigateBack" color="#333" size="46rpx"></uv-icon>
|
|
</view>
|
|
<view class="center">
|
|
<text class="title">{{ novelTitle }}</text>
|
|
<text class="chapter">{{ currentChapter }}</text>
|
|
</view>
|
|
<!-- <view class="right">
|
|
<uv-icon name="more-dot-fill" color="#333" size="46rpx"></uv-icon>
|
|
</view> -->
|
|
</view>
|
|
<view class="progress-bar">
|
|
<view class="progress-inner" :style="{width: readProgress + '%'}"></view>
|
|
</view>
|
|
</view>
|
|
|
|
<scroll-view scroll-y class="chapter-content" :class="{'full-content': isFullScreen}"
|
|
@scroll="handleScroll" @tap="handleContentClick">
|
|
|
|
<view class="chapter-content-item">
|
|
<view class="chapter-title">第1章 重回2004</view>
|
|
<view class="paragraph-content">
|
|
<view class="paragraph" v-for="(paragraph, index) in paragraphs" :key="index">
|
|
{{ paragraph }}
|
|
</view>
|
|
</view>
|
|
</view>
|
|
|
|
</scroll-view>
|
|
|
|
<view class="bottom-bar" :class="{'bottom-bar-hidden': isFullScreen}">
|
|
<view class="bottom-left">
|
|
<view class="bar-item">
|
|
<view class="bar-icon"> <uv-icon name="plus"></uv-icon> </view>
|
|
<text class="bar-label">加入书架</text>
|
|
</view>
|
|
<view class="bar-item">
|
|
<view class="bar-icon"> <uv-icon name="eye-fill"></uv-icon> </view>
|
|
<text class="bar-label">夜间</text>
|
|
</view>
|
|
</view>
|
|
<view class="bottom-right">
|
|
<button class="outline-btn"><text class="btn-text">上一章</text></button>
|
|
<button class="outline-btn" @click="$refs.chapterPopup.open()"><text class="btn-text">目录</text></button>
|
|
<button class="outline-btn"><text class="btn-text">下一章</text></button>
|
|
</view>
|
|
</view>
|
|
|
|
<uv-popup v-model="showPopup" mode="center" :closeOnClickOverlay="true">
|
|
<view
|
|
style="padding: 48rpx 32rpx; text-align: center; background: #fff; border-radius: 24rpx; min-width: 500rpx;">
|
|
<view style="font-size: 32rpx; font-weight: bold; color: #222; margin-bottom: 24rpx;">这是付费章节 需要订阅后才能阅读
|
|
</view>
|
|
<view style="font-size: 26rpx; color: #999; margin-bottom: 40rpx;">订阅后可继续阅读本章内容</view>
|
|
<view style="display: flex; justify-content: center; gap: 24rpx;">
|
|
<button
|
|
style="background: #ff9800; color: #fff; border-radius: 32rpx; font-size: 28rpx; padding: 0 32rpx; border: none;"
|
|
@click="goToSubscription">订阅本章</button>
|
|
<button
|
|
style="background: #fff3e0; color: #ff9800; border-radius: 32rpx; font-size: 28rpx; padding: 0 32rpx; border: 1px solid #ff9800;">观看视频解锁</button>
|
|
<button
|
|
style="background: #fff; color: #ff9800; border-radius: 32rpx; font-size: 28rpx; padding: 0 32rpx; border: 1px solid #ff9800;">批量订阅</button>
|
|
</view>
|
|
</view>
|
|
</uv-popup>
|
|
|
|
<view v-if="showPayPopup" class="pay-popup-mask" @click="showPayPopup = false">
|
|
<view class="pay-popup" @click.stop>
|
|
<view class="pay-title">这是付费章节 需要订阅后才能阅读</view>
|
|
<view class="pay-desc">订阅后可继续阅读本章内容</view>
|
|
<view class="pay-btns">
|
|
<button class="pay-btn" @click="goToSubscription">订阅本章</button>
|
|
<button class="pay-btn pay-btn-video">观看视频解锁</button>
|
|
<button class="pay-btn pay-btn-batch">批量订阅</button>
|
|
</view>
|
|
</view>
|
|
</view>
|
|
|
|
<novelVotePopup ref="novelVotePopup"/>
|
|
|
|
<chapterPopup ref="chapterPopup" />
|
|
</view>
|
|
</template>
|
|
|
|
<script>
|
|
import chapterPopup from '../components/novel/chapterPopup.vue'
|
|
import novelVotePopup from '../components/novel/novelVotePopup.vue'
|
|
export default {
|
|
components: {
|
|
chapterPopup,
|
|
novelVotePopup,
|
|
},
|
|
data() {
|
|
return {
|
|
isFullScreen: false,
|
|
showPopup: false,
|
|
popupShown: false, // 只弹一次
|
|
showPayPopup: false,
|
|
novelTitle: "这游戏也太真实了",
|
|
currentChapter: "第1章 重回2004",
|
|
readProgress: 15, // 阅读进度百分比
|
|
paragraphs: [
|
|
"华东地区某个不知名街区,2004年冬。",
|
|
"天还没有亮,王明就起床了。他要去赶早市,今天是进货的日子,错过了就要等下周。他轻轻地穿好衣服,不想吵醒还在熟睡的妻子。",
|
|
"天气比想象中冷,他裹紧了身上那件略显破旧的棉袄。出门前,他看了眼床头那个旧闹钟,四点半,还算准时。",
|
|
"街上几乎没有人,只有零星的几辆三轮车和面包车正往市场方向驶去。王明加快了脚步,他知道好位置都是先到先得。",
|
|
"这一年,互联网刚刚开始在中国普及,但对于像王明这样的小摊贩来说,生活并没有什么变化。每天起早贪黑,挣扎在温饱线上。",
|
|
"然而就在今天,他的生活将迎来一场他从未预料到的变化。",
|
|
"市场入口处,一个陌生人递给他一张名片,上面写着:\"电子产品批发,价格优惠\"。王明随手接过,塞进了口袋,继续往里走。",
|
|
"他不知道的是,这张小小的名片,将成为改变他命运的第一步。",
|
|
"几个小时后,当他收摊准备回家时,他偶然摸到了那张名片。出于好奇,他决定去看看。",
|
|
"名片上的地址在城市的另一边,是一个他从未去过的工业区。坐了将近一个小时的公交车,他终于找到了那个地方。",
|
|
"那是一个不起眼的仓库,门口停着几辆货车。王明犹豫了一下,还是推门走了进去。",
|
|
"里面的景象让他震惊。货架上整齐地摆放着各种电子产品:MP3播放器、数码相机、U盘……这些在当时都是新奇而昂贵的物品。",
|
|
"\"您是新顾客吧?\"一个中年男人走过来,热情地招呼道。",
|
|
"\"是的,我看到了你的名片。\"王明有些拘谨地回答。",
|
|
"\"那您来得正是时候,我们刚收到一批新货,价格特别优惠。\"",
|
|
"王明被带到一个展示台前,上面摆着几个小巧的设备。\"这是最新款的MP3,容量大,音质好,在市场上很受欢迎。\"",
|
|
"王明拿起一个仔细端详。他虽然没什么文化,但做生意的直觉告诉他,这东西可能有市场。",
|
|
"\"多少钱一个?\"他问道。",
|
|
"\"批发价150元,零售价可以卖到300元以上。\"",
|
|
"王明心里快速计算着。他今天的存款只有3000元,如果全买这个,可以拿20个。要是真能卖出去,就是3000元的利润。",
|
|
"但风险也很大。万一卖不出去,这可是他半年的积蓄啊。",
|
|
"就在他犹豫的时候,旁边传来一个熟悉的声音:\"老王,你也来这进货啊?\"",
|
|
"是他在市场上认识的李东。李东比他年轻,做生意也比他精明。",
|
|
"\"你觉得这东西怎么样?\"王明问道。",
|
|
"\"那必须相当不错啊。我上周进了一批,三天就卖光了。现在是过节嘛,年轻人喜欢这些新鲜玩意儿。\"李东拍了拍他的肩膀,\"要我说,你应该赶紧进一批,过了这个村可就没这个店了。\"",
|
|
"看到李东的信心,王明心里的天平开始倾斜。",
|
|
"\"那...好吧,给我来20个。\"他终于下定决心,从口袋里掏出了钱。",
|
|
"这一决定,将彻底改变他的人生轨迹...",
|
|
]
|
|
}
|
|
},
|
|
methods: {
|
|
handleContentClick() {
|
|
this.toggleFullScreen();
|
|
},
|
|
handleScroll(e) {
|
|
// 获取滚动位置
|
|
const scrollTop = e.detail.scrollTop;
|
|
|
|
// 滚动不触发导航栏显示,只处理付费章节弹窗
|
|
if (scrollTop > 50 && !this.popupShown) {
|
|
this.showPopup = true;
|
|
this.popupShown = true;
|
|
}
|
|
},
|
|
toggleFullScreen() {
|
|
this.isFullScreen = !this.isFullScreen
|
|
},
|
|
goToSubscription() {
|
|
uni.navigateTo({
|
|
url: '/pages_order/novel/SubscriptionInformation'
|
|
})
|
|
}
|
|
},
|
|
mounted() {
|
|
// 初始设置为全屏模式
|
|
this.isFullScreen = true;
|
|
|
|
// #ifdef H5
|
|
if (typeof window !== 'undefined') {
|
|
window.onscroll = () => {
|
|
const scrollTop = document.documentElement.scrollTop || document.body.scrollTop;
|
|
if (scrollTop > 50 && !this.popupShown) {
|
|
this.showPopup = true;
|
|
this.popupShown = true;
|
|
}
|
|
};
|
|
}
|
|
// #endif
|
|
},
|
|
beforeDestroy() {
|
|
// #ifdef H5
|
|
if (typeof window !== 'undefined') {
|
|
window.onscroll = null;
|
|
}
|
|
// #endif
|
|
},
|
|
onLoad() {
|
|
// 可接收小说id、章节id等参数
|
|
}
|
|
}
|
|
</script>
|
|
|
|
<style lang="scss" scoped>
|
|
.reader-container {
|
|
min-height: 100vh;
|
|
background: #fff;
|
|
display: flex;
|
|
flex-direction: column;
|
|
position: relative;
|
|
overflow: hidden;
|
|
|
|
.top-controls {
|
|
position: fixed;
|
|
top: 0;
|
|
left: 0;
|
|
right: 0;
|
|
background: rgba(255, 255, 255, 0.98);
|
|
padding-top: calc(var(--status-bar-height) + 10rpx);
|
|
z-index: 100;
|
|
transform: translateY(0);
|
|
transition: transform 0.3s ease-in-out, opacity 0.3s ease-in-out;
|
|
box-shadow: 0 2rpx 10rpx rgba(0, 0, 0, 0.05);
|
|
|
|
.controls-inner {
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: space-between;
|
|
padding: 20rpx 32rpx;
|
|
position: relative;
|
|
|
|
.left {
|
|
width: 100rpx;
|
|
display: flex;
|
|
justify-content: flex-start;
|
|
align-items: center;
|
|
}
|
|
|
|
.center {
|
|
flex: 1;
|
|
display: flex;
|
|
flex-direction: column;
|
|
align-items: center;
|
|
justify-content: center;
|
|
|
|
.title {
|
|
font-size: 32rpx;
|
|
font-weight: 500;
|
|
color: #333;
|
|
margin-bottom: 4rpx;
|
|
white-space: nowrap;
|
|
overflow: hidden;
|
|
text-overflow: ellipsis;
|
|
max-width: 320rpx;
|
|
}
|
|
|
|
.chapter {
|
|
font-size: 24rpx;
|
|
color: #666;
|
|
white-space: nowrap;
|
|
overflow: hidden;
|
|
text-overflow: ellipsis;
|
|
max-width: 320rpx;
|
|
}
|
|
}
|
|
|
|
.right {
|
|
width: 100rpx;
|
|
display: flex;
|
|
justify-content: flex-end;
|
|
align-items: center;
|
|
}
|
|
}
|
|
|
|
.progress-bar {
|
|
height: 4rpx;
|
|
background: #f0f0f0;
|
|
width: 100%;
|
|
position: relative;
|
|
|
|
.progress-inner {
|
|
height: 100%;
|
|
background: #4a90e2;
|
|
transition: width 0.3s;
|
|
}
|
|
}
|
|
|
|
&.top-controls-hidden {
|
|
transform: translateY(-100%);
|
|
opacity: 0;
|
|
}
|
|
}
|
|
|
|
.chapter-content {
|
|
flex: 1;
|
|
padding: 0 32rpx;
|
|
font-size: 28rpx;
|
|
color: #222;
|
|
line-height: 2.2;
|
|
padding-top: 160rpx; /* 为导航栏预留空间,不随状态变化 */
|
|
padding-bottom: 180rpx;
|
|
width: 100%;
|
|
box-sizing: border-box;
|
|
overflow-x: hidden;
|
|
|
|
.chapter-content-item {
|
|
width: 100%;
|
|
|
|
.chapter-title {
|
|
font-size: 36rpx;
|
|
font-weight: bold;
|
|
margin: 20rpx 0 40rpx 0;
|
|
text-align: center;
|
|
word-break: break-word;
|
|
white-space: normal;
|
|
}
|
|
|
|
.paragraph-content {
|
|
width: 100%;
|
|
|
|
.paragraph {
|
|
text-indent: 2em;
|
|
margin-bottom: 30rpx;
|
|
line-height: 1.8;
|
|
font-size: 30rpx;
|
|
color: #333;
|
|
word-wrap: break-word;
|
|
word-break: normal;
|
|
white-space: normal;
|
|
}
|
|
}
|
|
}
|
|
|
|
&.full-content {
|
|
/* 不再修改顶部padding,保持内容位置不变 */
|
|
}
|
|
}
|
|
|
|
.bottom-bar {
|
|
position: fixed;
|
|
left: 0;
|
|
right: 0;
|
|
bottom: 0;
|
|
background: #fff;
|
|
display: flex;
|
|
justify-content: center;
|
|
align-items: center;
|
|
height: 180rpx;
|
|
box-shadow: 0 -2rpx 10rpx rgba(0, 0, 0, 0.05);
|
|
z-index: 10;
|
|
padding: 0 40rpx 10rpx 40rpx;
|
|
transform: translateY(0);
|
|
transition: transform 0.3s ease-in-out;
|
|
|
|
&.bottom-bar-hidden {
|
|
transform: translateY(100%);
|
|
}
|
|
|
|
.bottom-left {
|
|
display: flex;
|
|
align-items: flex-end;
|
|
gap: 48rpx;
|
|
|
|
.bar-item {
|
|
display: flex;
|
|
flex-direction: column;
|
|
align-items: center;
|
|
justify-content: flex-end;
|
|
|
|
.bar-icon {
|
|
width: 48rpx;
|
|
height: 48rpx;
|
|
margin-bottom: 4rpx;
|
|
margin-right: 1rpx;
|
|
}
|
|
|
|
.bar-label {
|
|
font-size: 22rpx;
|
|
color: #b3b3b3;
|
|
margin-top: 2rpx;
|
|
}
|
|
}
|
|
}
|
|
|
|
.bottom-right {
|
|
display: flex;
|
|
align-items: flex-end;
|
|
gap: 32rpx;
|
|
margin-left: 40rpx;
|
|
|
|
.outline-btn {
|
|
min-width: 110rpx;
|
|
padding: 0 28rpx;
|
|
height: 60rpx;
|
|
line-height: 60rpx;
|
|
background: #fff;
|
|
color: #223a7a;
|
|
border: 2rpx solid #223a7a;
|
|
border-radius: 32rpx;
|
|
font-size: 28rpx;
|
|
font-weight: bold;
|
|
margin: 0;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
|
|
.btn-text {
|
|
font-weight: bold;
|
|
color: #223a7a;
|
|
font-size: 28rpx;
|
|
border-bottom: 2rpx solid #223a7a;
|
|
padding-bottom: 2rpx;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
.pay-popup-mask {
|
|
position: fixed;
|
|
left: 0;
|
|
top: 0;
|
|
right: 0;
|
|
bottom: 0;
|
|
background: rgba(0, 0, 0, 0.5);
|
|
z-index: 10000;
|
|
|
|
.pay-popup {
|
|
position: absolute;
|
|
left: 0;
|
|
right: 0;
|
|
bottom: 0;
|
|
background: #232323;
|
|
border-top-left-radius: 24rpx;
|
|
border-top-right-radius: 24rpx;
|
|
min-width: 500rpx;
|
|
width: 100%;
|
|
padding: 48rpx 32rpx 32rpx 32rpx;
|
|
text-align: center;
|
|
color: #fff;
|
|
box-sizing: border-box;
|
|
|
|
.pay-title {
|
|
font-size: 32rpx;
|
|
font-weight: bold;
|
|
color: #fff;
|
|
margin-bottom: 24rpx;
|
|
margin-right: 50rpx;
|
|
word-wrap: break-word;
|
|
white-space: normal;
|
|
}
|
|
|
|
.pay-desc {
|
|
font-size: 26rpx;
|
|
color: #999;
|
|
margin-bottom: 40rpx;
|
|
margin-right: 50rpx;
|
|
word-wrap: break-word;
|
|
white-space: normal;
|
|
}
|
|
|
|
.pay-btns {
|
|
display: flex;
|
|
flex-wrap: wrap;
|
|
justify-content: center;
|
|
gap: 24rpx;
|
|
margin-right: 50rpx;
|
|
|
|
.pay-btn {
|
|
background: #ff9800;
|
|
color: #fff;
|
|
border-radius: 32rpx;
|
|
font-size: 28rpx;
|
|
padding: 0 32rpx;
|
|
border: none;
|
|
margin-bottom: 16rpx;
|
|
word-break: keep-all;
|
|
|
|
&.pay-btn-video,
|
|
&.pay-btn-batch {
|
|
background: #ff9800;
|
|
color: #fff;
|
|
border-radius: 32rpx;
|
|
font-size: 28rpx;
|
|
padding: 0 32rpx;
|
|
border: none;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
</style>
|