Browse Source

底部导航栏页面

master
Bobi 1 week ago
parent
commit
8f95bc8356
24 changed files with 3340 additions and 181 deletions
  1. +26
    -0
      components/novel/CatalogPopup.vue
  2. +93
    -0
      components/novel/RankListItem.vue
  3. +1
    -1
      components/novel/workItem.vue
  4. +36
    -0
      pages.json
  5. +24
    -38
      pages/index/bookshelf.vue
  6. +4
    -6
      pages/index/category.vue
  7. +8
    -8
      pages/index/center.vue
  8. +134
    -52
      pages_order/author/editor.vue
  9. +164
    -0
      pages_order/novel/Giftbox.vue
  10. +278
    -0
      pages_order/novel/Giftpurchases.vue
  11. +180
    -0
      pages_order/novel/Mycomment.vue
  12. +192
    -0
      pages_order/novel/ReaderAchievement.vue
  13. +206
    -0
      pages_order/novel/Respondcomments.vue
  14. +167
    -0
      pages_order/novel/Review.vue
  15. +228
    -0
      pages_order/novel/SubscriptionInformation.vue
  16. +233
    -0
      pages_order/novel/Tipping.vue
  17. +249
    -0
      pages_order/novel/Walletflow.vue
  18. +1
    -0
      pages_order/novel/announcement.vue
  19. +112
    -53
      pages_order/novel/chapterList.vue
  20. +232
    -0
      pages_order/novel/comments.vue
  21. +22
    -3
      pages_order/novel/createNovel.vue
  22. +116
    -0
      pages_order/novel/creator.vue
  23. +186
    -20
      pages_order/novel/novelDetail.vue
  24. +448
    -0
      pages_order/novel/readnovels.vue

+ 26
- 0
components/novel/CatalogPopup.vue View File

@ -0,0 +1,26 @@
<template>
<view v-if="show" style="position:fixed;top:200px;left:0;right:0;z-index:99999;background:#0f0;color:#fff;font-size:40rpx;text-align:center;">CatalogPopup弹窗测试</view>
</template>
<script>
export default {
name: 'CatalogPopup',
props: {
show: Boolean,
chapterList: Array,
currentIndex: Number,
orderAsc: Boolean,
top: {
type: String,
default: '196px'
},
minHeight: {
type: String,
default: 'calc(100vh - 196px)'
}
},
mounted() {
console.log('CatalogPopup mounted');
}
}
</script>

+ 93
- 0
components/novel/RankListItem.vue View File

@ -0,0 +1,93 @@
<template>
<view class="rank-item">
<view class="rank-left">
<image v-if="rankIcon" :src="rankIcon" class="rank-icon" />
<image v-else-if="rankNumImg" :src="rankNumImg" class="rank-num-img" />
<slot name="rankNum" v-else />
<image v-if="medal" :src="medal" class="medal" />
<image class="avatar" :src="avatar" mode="aspectFill" />
<view class="name">{{ name }}</view>
</view>
<view class="rank-right">
<view class="score">{{ score }} 亲密值</view>
<view class="level">{{ level }}</view>
</view>
</view>
</template>
<script>
export default {
name: 'RankListItem',
props: {
rankIcon: String, // 3
rankNumImg: String, // 4-10
medal: String, //
avatar: String,
name: String,
score: [String, Number],
level: {
type: String,
default: '护书使者 五级'
}
}
}
</script>
<style lang="scss" scoped>
.rank-item {
display: flex;
align-items: center;
justify-content: space-between;
background: #fffbe6;
border-radius: 16rpx;
margin-bottom: 18rpx;
box-shadow: 0 2rpx 8rpx 0 rgba(184,110,59,0.06);
padding: 0 24rpx;
height: 100rpx;
.rank-left {
display: flex;
align-items: center;
.rank-icon, .rank-num-img {
width: 38rpx;
height: 38rpx;
margin-right: 10rpx;
}
.medal {
width: 44rpx;
height: 44rpx;
margin-right: 10rpx;
}
.avatar {
width: 44rpx;
height: 44rpx;
border-radius: 50%;
margin-right: 14rpx;
border: 2rpx solid #ffd700;
object-fit: cover;
}
.name {
font-size: 26rpx;
color: #222;
font-weight: 500;
}
}
.rank-right {
display: flex;
flex-direction: column;
align-items: flex-end;
.score {
font-size: 22rpx;
color: #b86e3b;
}
.level {
font-size: 20rpx;
color: #fff;
background: #e6b07c;
border-radius: 8rpx;
padding: 2rpx 10rpx;
margin-top: 6rpx;
font-weight: 500;
}
}
}
</style>

+ 1
- 1
components/novel/workItem.vue View File

@ -27,7 +27,7 @@
</view>
<!-- 添加右箭头图标 -->
<text class="iconfont icon-arrow">&#xe65f;</text>
<!-- <text class="iconfont icon-arrow">&#xe65f;</text> -->
</view>
</template>


+ 36
- 0
pages.json View File

@ -69,6 +69,42 @@
},
{
"path": "novel/chapterList"
},
{
"path": "novel/ReaderAchievement"
},
{
"path": "novel/readnovels"
},
{
"path": "novel/SubscriptionInformation"
},
{
"path": "novel/Tipping"
},
{
"path": "novel/Review"
},
{
"path": "novel/comments"
},
{
"path": "novel/Respondcomments"
},
{
"path": "novel/Walletflow"
},
{
"path": "novel/creator"
},
{
"path": "novel/Giftbox"
},
{
"path": "novel/Giftpurchases"
},
{
"path": "novel/Mycomment"
}
]
}],


+ 24
- 38
pages/index/bookshelf.vue View File

@ -1,4 +1,5 @@
<template>
<!-- 书架页面 -->
<view class="page">
<!-- 头部标签切换 -->
<view class="header" :style="{ paddingTop: `${statusBarHeight}px` }">
@ -97,15 +98,6 @@
:key="work.id"
@click="toggleSelect(work, 'work')"
>
<view class="item-checkbox" v-if="selectedItems.includes(work.id)">
<view class="checkbox-inner">
<uv-icon name="checkmark" size="28" color="#ffffff"></uv-icon>
</view>
</view>
<view class="item-checkbox" v-else>
<view class="checkbox-inner-no">
</view>
</view>
<work-item
:work="work"
:style="{ opacity: selectedItems.includes(work.id) ? '0.8' : '1' }"
@ -270,8 +262,7 @@
this.statusBarHeight = systemInfo.statusBarHeight;
//
const savedWorks = uni.getStorageSync('worksList') || []
this.worksList = savedWorks
this.loadWorksList()
//
const activeTab = uni.getStorageSync('activeBookshelfTab')
@ -292,33 +283,23 @@
// #endif
},
onShow() {
//
const submittedWork = getApp().globalData.submittedWork
if (submittedWork) {
//
this.worksList.unshift(submittedWork)
//
uni.setStorageSync('worksList', this.worksList)
//
getApp().globalData.submittedWork = null
//
this.activeTab = 'work'
//
if (getApp().globalData.showSubmitSuccess) {
//
getApp().globalData.showSubmitSuccess = false
//
uni.showToast({
title: '提交成功,等待审核',
icon: 'none',
duration: 2000
})
}
}
//
this.loadWorksList()
this.isEditMode = false
this.selectedItems = []
//
const pages = getCurrentPages();
const current = pages[pages.length - 1];
if (current.options && current.options.fromPublish === '1') {
this.activeTab = 'work';
uni.showToast({
title: '发布成功',
icon: 'success'
});
//
delete current.options.fromPublish;
}
this.isEditMode = false;
this.selectedItems = [];
},
onUnload() {
//
@ -357,7 +338,7 @@
//
toReaderSettings() {
uni.navigateTo({
url: '/pages/work/reader-settings'
url: '/pages_order/novel/ReaderAchievement'
})
},
@ -452,6 +433,11 @@
}
}
});
},
//
loadWorksList() {
const savedWorks = uni.getStorageSync('worksList') || []
this.worksList = savedWorks
}
}
}


+ 4
- 6
pages/index/category.vue View File

@ -44,8 +44,6 @@
</uv-vtabs>
</view>
<kefu></kefu>
<!-- tabbar -->
<tabber select="category" />
</view>
@ -73,7 +71,7 @@
{
id: '1',
title: '我是半妖',
cover: 'https://bookcover.yuewen.com/qdbimg/349573/1033014772/150.webp',
cover: 'https://images.unsplash.com/photo-1506744038136-46273834b3fb?auto=format&fit=facearea&w=400&h=400',
author: '东方不败',
desc: '这是一部关于半妖少年成长的玄幻小说,讲述了主角在人妖两界的冒险故事。',
tags: ['玄幻', '冒险', '热血'],
@ -82,7 +80,7 @@
{
id: '2',
title: '兽王进化',
cover: 'https://bookcover.yuewen.com/qdbimg/349573/1033014772/150.webp',
cover: 'https://images.unsplash.com/photo-1519125323398-675f0ddb6308?auto=format&fit=facearea&w=400&h=400',
author: '天下霸唱',
desc: '一场意外让主角获得兽王血脉,开始了进化之路...',
tags: ['奇幻', '冒险'],
@ -91,7 +89,7 @@
{
id: '3',
title: '魔法少女纯爷们',
cover: 'https://bookcover.yuewen.com/qdbimg/349573/1033014772/150.webp',
cover: 'https://images.unsplash.com/photo-1465101046530-73398c7f28ca?auto=format&fit=facearea&w=400&h=400',
author: '南派三叔',
desc: '一个普通男孩意外获得魔法少女的力量,开始了奇妙冒险...',
tags: ['搞笑', '奇幻'],
@ -212,7 +210,7 @@
tempList.push({
id: categoryId + '-' + i,
title: '分类' + categoryId + '的书' + i,
cover: 'https://bookcover.yuewen.com/qdbimg/349573/1033014772/150.webp',
cover: 'https://images.unsplash.com/photo-1506744038136-46273834b3fb?auto=format&fit=facearea&w=400&h=400',
author: '作者' + categoryId,
desc: '这是分类' + categoryId + '的第' + i + '本书...',
tags: ['分类' + categoryId, '热门'],


+ 8
- 8
pages/index/center.vue View File

@ -20,14 +20,14 @@
<view class="section">
<view class="section-title">账户</view>
<view class="section-list">
<view class="section-item" @click="navigateTo('/pages/wallet')">
<view class="section-item" @click="navigateToPage('/pages_order/novel/Walletflow')">
<view class="section-item-left">
<uv-icon name="photo" size="40rpx" color="#333"></uv-icon>
<text>钱包提现</text>
<text>钱包流水</text>
</view>
<uv-icon name="arrow-right" size="36rpx" color="#999"></uv-icon>
</view>
<view class="section-item" @click="navigateTo('/pages/gift')">
<view class="section-item" @click="navigateToPage('/pages_order/novel/Giftbox')">
<view class="section-item-left">
<uv-icon name="gift" size="40rpx" color="#333"></uv-icon>
<text>礼物盒</text>
@ -41,7 +41,7 @@
<view class="section">
<view class="section-title">日常</view>
<view class="section-list">
<view class="section-item" @click="navigateTo('/pages/comments')">
<view class="section-item" @click="navigateToPage('/pages_order/novel/Mycomment')">
<view class="section-item-left">
<uv-icon name="chat" size="40rpx" color="#333"></uv-icon>
<text>我的评论</text>
@ -49,7 +49,7 @@
<view class="badge">294</view>
<uv-icon name="arrow-right" size="36rpx" color="#999"></uv-icon>
</view>
<view class="section-item" @click="navigateTo('/pages/tasks')">
<view class="section-item" @click="navigateToPage('/pages/tasks')">
<view class="section-item-left">
<uv-icon name="list" size="40rpx" color="#333"></uv-icon>
<text>任务中心</text>
@ -64,14 +64,14 @@
<view class="section">
<view class="section-title">设置</view>
<view class="section-list">
<view class="section-item" @click="navigateTo('/pages/author')">
<view class="section-item" @click="navigateToPage('/pages_order/novel/creator')">
<view class="section-item-left">
<uv-icon name="star" size="40rpx" color="#333"></uv-icon>
<text>申请成为作者</text>
</view>
<uv-icon name="arrow-right" size="36rpx" color="#999"></uv-icon>
</view>
<view class="section-item" @click="navigateTo('/pages/customer-service')">
<view class="section-item" @click="navigateToPage('/pages/customer-service')">
<view class="section-item-left">
<uv-icon name="star" size="40rpx" color="#333"></uv-icon>
<text>联系客服</text>
@ -79,7 +79,7 @@
</view>
<uv-icon name="arrow-right" size="36rpx" color="#999"></uv-icon>
</view>
<view class="section-item" @click="navigateTo('/pages/edit-info')">
<view class="section-item" @click="navigateToPage('/pages/edit-info')">
<view class="section-item-left">
<uv-icon name="edit-pen" size="40rpx" color="#333"></uv-icon>
<text>修改信息</text>


+ 134
- 52
pages_order/author/editor.vue View File

@ -1,25 +1,21 @@
<template>
<view class="page">
<navbar title="1531字"
leftClick @leftClick="$utils.navigateBack"/>
<view class="title">
<input type="text"
placeholder="请输入章节标题"
v-model="form.title"/>
<navbar title="新建章节" leftClick @leftClick="$utils.navigateBack" />
<view class="form-box top-fixed">
<view class="form-item">
<text class="required">*</text>
<text class="label">章节名称</text>
<input class="input" type="text" placeholder='请输入章节号与章节名。例如:"第十章天降奇缘"' v-model="form.title" />
</view>
<view class="form-item">
<text class="required">*</text>
<text class="label">章节内容</text>
<textarea class="textarea" placeholder="请输入章节内容" v-model="form.content" />
</view>
</view>
<editor id="editor"
class="ql-container"
placeholder="请输入正文..."
@statuschange="onStatusChange"
@input="onInput"
@ready="onEditorReady">
</editor>
<view class="plus">
发布
<view class="footer-btns">
<button class="btn save-btn" @click="onSave">保存</button>
<button class="btn publish-btn" @click="onPublish">发布</button>
</view>
</view>
</template>
@ -29,8 +25,9 @@
data() {
return {
formats: {},
form : {
title : '',
form: {
title: '',
content: '',
},
}
},
@ -109,42 +106,127 @@
})
}
})
}
},
onSave() {
//
const newChapter = {
title: this.form.title,
content: this.form.content
};
//
let chapters = uni.getStorageSync('chapters') || [];
chapters.push(newChapter);
uni.setStorageSync('chapters', chapters);
//
uni.redirectTo({
url: '/pages_order/novel/chapterList?fromSave=1'
});
},
onPublish() {
//
const newChapter = {
title: this.form.title,
content: this.form.content,
time: Date.now()
};
//
let publishedChapters = uni.getStorageSync('publishedChapters') || [];
publishedChapters.push(newChapter);
uni.setStorageSync('publishedChapters', publishedChapters);
// bookshelftab
uni.redirectTo({
url: '/pages/index/bookshelf?fromPublish=1'
});
},
}
}
</script>
<style scoped lang="scss">
.page{
.page {
margin-top: -920rpx;
margin-left: 30rpx;
margin-right: 30rpx;
min-height: 100vh;
background: #f7f8fa;
display: flex;
flex-direction: column;
justify-content: space-between;
}
.form-box {
background: #fff;
margin: 0;
padding: 0 32rpx 0 32rpx;
border-radius: 0;
position: relative;
}
.top-fixed {
margin-top: 0;
}
.form-item {
display: flex;
flex-direction: column;
margin-top: 32rpx;
position: relative;
}
.label {
font-size: 28rpx;
color: #222;
margin-bottom: 12rpx;
margin-left: 32rpx;
}
.required {
color: #f44;
position: absolute;
left: 0;
top: 0;
font-size: 32rpx;
.title{
padding: 30rpx;
background-color: #fff;
margin: 10rpx 0;
input{
}
}
.ql-container{
background-color: #fff;
padding: 30rpx;
width: 100%;
box-sizing: border-box;
min-height: calc(100vh - 300rpx);
line-height: 50rpx;
}
.plus{
background-color: $uni-color;
color: #fff;
position: fixed;
right: 20rpx;
top: 40%;
width: 100rpx;
height: 100rpx;
display: flex;
align-items: center;
justify-content: center;
border-radius: 25rpx;
}
}
.input {
border: none;
border-bottom: 1rpx solid #eee;
font-size: 28rpx;
padding: 16rpx 0;
background: transparent;
}
.textarea {
min-height: 180rpx;
border: none;
border-bottom: 1rpx solid #eee;
font-size: 28rpx;
padding: 16rpx 0;
background: transparent;
resize: none;
}
.footer-btns {
position: fixed;
left: 0;
bottom: 0;
width: 100vw;
background: #fff;
display: flex;
justify-content: space-between;
padding: 24rpx 48rpx 32rpx 48rpx;
box-sizing: border-box;
z-index: 10;
box-shadow: 0 -2rpx 12rpx rgba(0,0,0,0.03);
}
.btn {
flex: 1;
height: 80rpx;
font-size: 32rpx;
border-radius: 40rpx;
margin: 0 12rpx;
font-weight: 500;
}
.save-btn {
background: #fff;
color: #0a225f;
border: 2rpx solid #0a225f;
}
.publish-btn {
background: #0a225f;
color: #fff;
border: none;
}
</style>

+ 164
- 0
pages_order/novel/Giftbox.vue View File

@ -0,0 +1,164 @@
<template>
<!-- 礼物盒页面 -->
<view class="giftbox-container">
<!-- 顶部标题栏 -->
<uv-navbar title="礼物盒" :autoBack="true" fixed placeholder rightIcon="more-dot-fill" />
<!-- 礼物列表 -->
<view class="gift-list">
<view
v-for="(gift, idx) in gifts"
:key="gift.id"
:class="['gift-item', { selected: idx === selectedIndex }]"
@click="selectGift(idx)"
>
<view class="gift-img">
<text class="gift-emoji">{{ gift.emoji }}</text>
</view>
<view class="gift-name">{{ gift.name }}</view>
<view class="gift-info">
<text class="gift-count">x{{ gift.count }}</text>
<text class="gift-bean">{{ gift.price }}豆豆</text>
</view>
</view>
</view>
<!-- 底部操作栏 -->
<view class="giftbox-bottom">
<view class="giftbox-left">剩余{{ gifts[selectedIndex].count }} </view>
<view class="giftbox-buy">
<button class="buy-btn" @click="buyGift">购买</button>
<uv-number-box v-model="buyCount" :min="1" :max="gifts[selectedIndex].count" />
</view>
</view>
</view>
</template>
<script>
export default {
components: {
'uv-navbar': () => import('@/uni_modules/uv-navbar/components/uv-navbar/uv-navbar.vue'),
'uv-number-box': () => import('@/uni_modules/uv-number-box/components/uv-number-box/uv-number-box.vue'),
},
data() {
return {
gifts: [
{ id: 1, name: '小星星', emoji: '🌟', count: 6, price: 1 },
{ id: 2, name: '爱你哟', emoji: '🧡', count: 5, price: 1 },
{ id: 3, name: '加油鸭', emoji: '🦆', count: 5, price: 1 },
{ id: 4, name: '蓝色烟花', emoji: '🎆', count: 5, price: 1 },
{ id: 5, name: '沙滩椅', emoji: '🏖️', count: 5, price: 1 },
{ id: 6, name: '菠萝', emoji: '🍍', count: 5, price: 1 },
{ id: 7, name: '咖啡', emoji: '☕', count: 5, price: 1 },
{ id: 8, name: '早餐', emoji: '🥐', count: 5, price: 1 },
{ id: 9, name: '花花', emoji: '🌷', count: 5, price: 1 },
{ id: 10, name: '玫瑰', emoji: '🌹', count: 5, price: 1 },
{ id: 11, name: '玫瑰花', emoji: '🥀', count: 5, price: 1 },
{ id: 12, name: '跑车', emoji: '🏎️', count: 5, price: 1 },
],
selectedIndex: 0,
buyCount: 1,
}
},
methods: {
selectGift(idx) {
this.selectedIndex = idx
this.buyCount = 1
},
buyGift() {
//
const gift = this.gifts[this.selectedIndex]
uni.navigateTo({
url: `/pages_order/novel/Giftpurchases?name=${encodeURIComponent(gift.name)}&price=${gift.price}&count=${this.buyCount}`
})
},
},
}
</script>
<style scoped lang="scss">
.giftbox-container {
min-height: 100vh;
background: #f8f8f8;
padding-bottom: 120rpx;
}
.gift-list {
display: flex;
flex-wrap: wrap;
gap: 24rpx;
padding: 32rpx 16rpx 0 16rpx;
}
.gift-item {
width: 30%;
background: #fff;
border-radius: 18rpx;
box-shadow: 0 2rpx 8rpx rgba(0,0,0,0.03);
margin-bottom: 24rpx;
display: flex;
flex-direction: column;
align-items: center;
padding: 24rpx 0 16rpx 0;
border: 2rpx solid transparent;
transition: border 0.2s;
}
.gift-item.selected {
border: 2rpx solid #223a7a;
box-shadow: 0 4rpx 16rpx 0 rgba(34,58,122,0.08);
}
.gift-img {
width: 80rpx;
height: 80rpx;
display: flex;
align-items: center;
justify-content: center;
margin-bottom: 8rpx;
}
.gift-emoji {
font-size: 56rpx;
}
.gift-name {
font-size: 28rpx;
color: #222;
margin-bottom: 4rpx;
}
.gift-info {
display: flex;
align-items: center;
gap: 8rpx;
font-size: 22rpx;
color: #888;
}
.giftbox-bottom {
position: fixed;
left: 0;
right: 0;
bottom: 0;
background: #fff;
display: flex;
align-items: center;
justify-content: space-between;
height: 100rpx;
padding: 0 32rpx;
box-shadow: 0 -2rpx 10rpx rgba(0,0,0,0.05);
z-index: 10;
}
.giftbox-left {
font-size: 28rpx;
color: #666;
}
.giftbox-buy {
display: flex;
align-items: center;
gap: 18rpx;
}
.buy-btn {
background: #223a7a;
color: #fff;
font-size: 28rpx;
border-radius: 32rpx;
padding: 0 36rpx;
height: 68rpx;
line-height: 68rpx;
border: none;
}
</style>

+ 278
- 0
pages_order/novel/Giftpurchases.vue View File

@ -0,0 +1,278 @@
<template>
<!-- 礼物购买页面 -->
<view class="gift-purchase-page">
<!-- 顶部导航栏 -->
<uv-navbar title="礼物购买" :autoBack="true" fixed placeholder />
<!-- 礼物购买卡片 -->
<view class="card gift-card">
<view class="card-title">礼物购买</view>
<view class="form-row">
<view class="form-label">礼物名称</view>
<view class="form-value">{{ giftName }}</view>
</view>
<view class="divider"></view>
<view class="form-row">
<view class="form-label required"><text class="star">*</text> 购买数量</view>
<input class="form-input" placeholder="请输入数量" v-model="buyCount" type="number" :placeholder-style="'color:#bbb;'" :style="buyCount ? 'color:#222;' : ''" />
</view>
<view class="divider"></view>
<view class="form-row">
<view class="form-label">支付方式</view>
</view>
<view class="form-row">
<view class="balance">账户余额<text class="bean">{{ balance }} 豆豆</text></view>
</view>
</view>
<!-- 订单信息卡片 -->
<view class="card order-card">
<view class="order-title">订单信息</view>
<view class="row">
<view class="order-content">内容</view>
</view>
<view class="row total-row">
<view class="order-total-label">合计</view>
<view class="order-total"><text class="order-total-highlight">{{ totalPrice.toFixed(2) }}</text> </view>
</view>
<view class="order-divider"></view>
</view>
<!-- 提示信息 -->
<view class="tip-text">
请仔细核查并确认相关信息因用户个人疏忽导致的充值错误需由用户自行承担一旦完成充值概不退换
</view>
<!-- 底部购买按钮 -->
<view class="footer-bar">
<button class="buy-btn" @click="handleBuy">购买</button>
</view>
</view>
</template>
<script>
export default {
data() {
return {
giftName: '小星星',
price: 0.1, //
buyCount: '',
balance: 0, //
}
},
computed: {
totalPrice() {
const count = parseInt(this.buyCount, 10)
return isNaN(count) ? 0 : count * this.price
}
},
onLoad(query) {
//
if (query.name) this.giftName = decodeURIComponent(query.name)
if (query.price) this.price = Number(query.price)
if (query.count) this.buyCount = query.count
},
methods: {
handleBuy() {
if (!this.buyCount || parseInt(this.buyCount, 10) <= 0) {
uni.showToast({ title: '请输入购买数量', icon: 'none' })
return
}
//
uni.showToast({ title: `成功购买${this.buyCount}${this.giftName}`, icon: 'success' })
}
}
}
</script>
<style scoped lang="scss">
.gift-purchase-page {
min-height: 100vh;
background: #f8f8f8;
padding-bottom: 120rpx;
}
.card {
background: #fff;
border-radius: 18rpx;
margin: 24rpx 16rpx 0 16rpx;
padding: 24rpx 24rpx 10rpx 24rpx;
box-shadow: 0 2rpx 8rpx rgba(0,0,0,0.03);
}
.card-title {
font-size: 24rpx;
font-weight: bold;
color: #222;
margin-bottom: 18rpx;
}
.row {
display: flex;
align-items: center;
margin-bottom: 16rpx;
}
.label {
color: #888;
font-size: 26rpx;
width: 160rpx;
}
.label.required {
color: #222;
font-weight: 500;
}
.value {
color: #222;
font-size: 26rpx;
}
.input {
flex: 1;
border: none;
border-bottom: 1rpx solid #eee;
font-size: 26rpx;
padding: 8rpx 0;
background: transparent;
outline: none;
}
.order-card {
border-radius: 20px;
box-shadow: 0 2px 8px rgba(0,0,0,0.04);
padding: 24px 20px 16px 20px;
margin: 16px;
position: relative;
}
.order-title {
font-size: 18px;
font-weight: bold;
color: #222;
margin-bottom: 12px;
}
.order-content {
color: #bbb;
font-size: 15px;
margin-bottom: 8px;
}
.row.total-row {
display: flex;
align-items: center;
margin-bottom: 0;
}
.order-total-label {
color: #222;
font-size: 18px;
font-weight: 500;
}
.order-total {
color: #222;
font-size: 18px;
font-weight: 500;
margin-left: 8px;
}
.order-total-highlight {
color: #ff7e00;
font-size: 18px;
font-weight: bold;
margin: 0 2px;
}
.order-divider {
width: 100%;
height: 1px;
background: #f2f2f2;
margin-top: 16px;
}
.tip-text {
color: #bbb;
font-size: 22rpx;
margin: 32rpx 32rpx 0 32rpx;
line-height: 1.6;
}
.footer-bar {
position: fixed;
left: 0;
right: 0;
bottom: 90rpx;
background: #fff;
padding: 24rpx 32rpx 32rpx 32rpx;
box-shadow: 0 -2rpx 10rpx rgba(0,0,0,0.05);
z-index: 10;
}
.buy-btn {
width: 100%;
background: #223a7a;
color: #fff;
font-size: 32rpx;
border-radius: 32rpx;
height: 88rpx;
line-height: 88rpx;
border: none;
font-weight: 500;
}
.card.gift-card {
border-radius: 20px;
box-shadow: 0 2px 8px rgba(0,0,0,0.04);
padding: 24px 20px 16px 20px;
margin: 16px;
position: relative;
}
.card-title {
font-size: 18px;
font-weight: bold;
color: #222;
margin-bottom: 12px;
}
.form-row {
display: flex;
flex-direction: column;
align-items: flex-start;
min-height: 36px;
padding: 0;
margin-top: 5rpx;
}
.form-label {
color: #888;
font-size: 14px;
margin-bottom: 9px;
font-weight: 400;
margin-top: 5px;
}
.form-label.required {
color: #222;
font-weight: 500;
display: flex;
align-items: center;
}
.star {
color: #e94f7a;
margin-right: 2px;
font-size: 16px;
}
.form-value {
color: #0e0e0e;
font-size: 14px;
font-weight: 600;
margin-bottom: 2px;
}
.form-input {
flex: 1;
border: none;
background: transparent;
font-size: 14px;
outline: none;
padding: 0;
color: #222;
}
.divider {
height: 1px;
background: #f2f2f2;
margin: 0 0 0 0;
border: none;
}
.balance {
color: #0e0e0e;
font-size: 14px;
margin: 0 0 8px 0;
}
.bean {
color: #222;
font-weight: 600;
}
</style>

+ 180
- 0
pages_order/novel/Mycomment.vue View File

@ -0,0 +1,180 @@
<template>
<view class="my-comment-page">
<navbar title="我的评论" :leftClick="true" @leftClick="goBack" />
<view class="comment-section">
<view class="section-title">未读评论·{{ unreadComments.length }}</view>
<view v-for="(item, idx) in unreadComments" :key="'unread-' + idx" class="comment-card">
<uv-avatar :src="item.avatar" size="44" shape="circle" class="avatar" />
<view class="comment-main">
<view class="comment-header">
<text class="username">{{ item.username }}</text>
<text class="from">来自{{ item.bookTitle }}</text>
</view>
<view class="comment-content">{{ item.content }}</view>
<view class="comment-footer">
<text class="comment-time">{{ item.time }}</text>
<view class="reply-btn-wrap" @click="goToReply(item)">
<text class="reply-btn">回复</text>
</view>
</view>
</view>
</view>
</view>
<view class="comment-section history-section">
<view class="section-title">历史评论</view>
<view v-for="(item, idx) in historyComments" :key="'history-' + idx" class="comment-card">
<uv-avatar :src="item.avatar" size="44" shape="circle" class="avatar" />
<view class="comment-main">
<view class="comment-header">
<text class="username">{{ item.username }}</text>
<text class="from">来自{{ item.bookTitle }}</text>
</view>
<view class="comment-content">{{ item.content }}</view>
<view class="comment-footer">
<text class="comment-time">{{ item.time }}</text>
<view class="reply-btn-wrap" @click="goToReply(item)">
<text class="reply-btn">回复</text>
</view>
</view>
</view>
</view>
</view>
</view>
</template>
<script>
import navbar from '@/components/base/navbar.vue'
export default {
components: { navbar },
data() {
return {
unreadComments: [
{
avatar: 'https://tse4-mm.cn.bing.net/th/id/OIP-C.iUyxJ_fxLjjX3kEBjteXWwAAAA?rs=1&pid=ImgDetMain',
username: '方香橙',
bookTitle: '重生之财富滚滚',
content: '我是本书的作者方香橙,这是一本甜文爽文哒!请放心入坑,五星好评!女主又美有个性可爱,绝对不圣母,不傻白!男主身心干净深情独宠媳妇儿一个人...',
time: '2024.07.09'
},
{
avatar: 'https://tse4-mm.cn.bing.net/th/id/OIP-C.iUyxJ_fxLjjX3kEBjteXWwAAAA?rs=1&pid=ImgDetMain',
username: '战斗世界',
bookTitle: '重生之财富滚滚',
content: '这本书打破了我看甜文套路之前的观念女主真的太可爱太有趣和以往看过的一个NPC介绍有很大不同',
time: '2024.06.09'
},
{
avatar: 'https://tse4-mm.cn.bing.net/th/id/OIP-C.iUyxJ_fxLjjX3kEBjteXWwAAAA?rs=1&pid=ImgDetMain',
username: '战斗世界',
bookTitle: '重生之财富滚滚',
content: '这本书打破了我看甜文套路之前的观念女主真的太可爱太有趣和以往看过的一个NPC介绍有很大不同',
time: '2024.06.09'
}
],
historyComments: [
{
avatar: 'https://tse4-mm.cn.bing.net/th/id/OIP-C.iUyxJ_fxLjjX3kEBjteXWwAAAA?rs=1&pid=ImgDetMain',
username: '方香橙',
bookTitle: '重生之财富滚滚',
content: '我是本书的作者方香橙,这是一本甜文爽文哒!请放心入坑,五星好评!女主又美有个性可爱,绝对不圣母,不傻白!男主身心干净深情独宠媳妇儿一个人...',
time: '2024.07.09'
}
]
}
},
methods: {
goBack() {
uni.navigateBack()
},
goToReply(item) {
uni.navigateTo({ url: '/pages_order/novel/Respondcomments' })
}
}
}
</script>
<style scoped lang="scss">
.my-comment-page {
min-height: 100vh;
background: #f8f8f8;
display: flex;
flex-direction: column;
}
.comment-section {
background: #fff;
margin: 24rpx 24rpx 0 24rpx;
border-radius: 16rpx;
padding: 24rpx 24rpx 0 24rpx;
margin-bottom: 24rpx;
}
.section-title {
color: #222;
font-size: 28rpx;
font-weight: 500;
margin-bottom: 16rpx;
}
.comment-card {
display: flex;
align-items: flex-start;
margin-bottom: 32rpx;
}
.avatar {
width: 56rpx;
height: 56rpx;
border-radius: 50%;
margin-right: 16rpx;
flex-shrink: 0;
}
.comment-main {
flex: 1;
display: flex;
flex-direction: column;
}
.comment-header {
display: flex;
align-items: center;
gap: 12rpx;
margin-bottom: 4rpx;
}
.username {
font-size: 26rpx;
color: #222;
font-weight: 500;
}
.from {
font-size: 22rpx;
color: #bdbdbd;
}
.comment-content {
font-size: 26rpx;
color: #333;
margin-bottom: 12rpx;
}
.comment-footer {
display: flex;
align-items: center;
font-size: 22rpx;
color: #bdbdbd;
justify-content: space-between;
padding-right: 8rpx;
}
.comment-time {
color: #bdbdbd;
}
.reply-btn-wrap {
display: flex;
align-items: center;
cursor: pointer;
}
.reply-btn {
color: #223a6b;
font-weight: 500;
margin-left: 0;
font-size: 24rpx;
}
.history-section {
margin-top: 24rpx;
}
</style>

+ 192
- 0
pages_order/novel/ReaderAchievement.vue View File

@ -0,0 +1,192 @@
<template>
<view class="achievement-page">
<uv-navbar title="读者成就设置" fixed placeholder></uv-navbar>
<view class="achievement-card">
<view class="card-title-row">
<view class="card-title">阅读成就设置</view>
<view v-if="isPending" class="pending-tag">设置审核中</view>
</view>
<view class="achievement-list">
<view class="achievement-item">
<image class="badge-img" src="https://tse3-mm.cn.bing.net/th/id/OIP-C.wUsFZgl70iE4tI7b_HKaKgHaHa?w=166&h=180&c=7&r=0&o=5&dpr=1.1&pid=1.7" mode="aspectFill" />
<view class="input-area">
<view class="label-row">
<text class="required">*</text>
<text class="label">一级成就名称</text>
</view>
<input class="input" v-model="level1" placeholder="请输入" placeholder-class="input-placeholder" />
</view>
</view>
<view class="divider"></view>
<view class="achievement-item">
<image class="badge-img" src="https://tse3-mm.cn.bing.net/th/id/OIP-C.wUsFZgl70iE4tI7b_HKaKgHaHa?w=166&h=180&c=7&r=0&o=5&dpr=1.1&pid=1.7" mode="aspectFill" />
<view class="input-area">
<view class="label-row">
<text class="required">*</text>
<text class="label">二级成就名称</text>
</view>
<input class="input" v-model="level2" placeholder="请输入" placeholder-class="input-placeholder" />
</view>
</view>
<view class="divider"></view>
<view class="achievement-item">
<image class="badge-img" src="https://tse3-mm.cn.bing.net/th/id/OIP-C.wUsFZgl70iE4tI7b_HKaKgHaHa?w=166&h=180&c=7&r=0&o=5&dpr=1.1&pid=1.7" mode="aspectFill" />
<view class="input-area">
<view class="label-row">
<text class="required">*</text>
<text class="label">三级成就名称</text>
</view>
<input class="input" v-model="level3" placeholder="请输入" placeholder-class="input-placeholder" />
</view>
</view>
</view>
</view>
<view class="bottom-btn-area">
<button class="submit-btn" :disabled="isPending" :class="{disabled: isPending, pending: isPending}" @click="submit">
{{ isPending ? '设置' : '提交申请' }}
</button>
</view>
</view>
</template>
<script>
export default {
data() {
return {
level1: '',
level2: '',
level3: '',
isPending: false
}
},
methods: {
submit() {
if (!this.level1 || !this.level2 || !this.level3) {
uni.showToast({ title: '请填写所有成就名称', icon: 'none' })
return
}
this.isPending = true
//
}
}
}
</script>
<style scoped lang="scss">
.achievement-page {
min-height: 100vh;
background: #f7f8fa;
display: flex;
flex-direction: column;
}
.achievement-card {
background: #fff;
border-radius: 20rpx;
margin: 40rpx 32rpx 0 32rpx;
padding: 32rpx 24rpx;
box-shadow: 0 4rpx 24rpx 0 rgba(0,0,0,0.04);
}
.card-title-row {
display: flex;
align-items: center;
justify-content: space-between;
}
.card-title {
font-size: 32rpx;
font-weight: bold;
margin-bottom: 32rpx;
}
.achievement-list {
display: flex;
flex-direction: column;
gap: 0;
}
.achievement-item {
display: flex;
align-items: flex-start;
padding: 18rpx 0 10rpx 0;
}
.badge-img {
width: 72rpx;
height: 72rpx;
margin-right: 20rpx;
border-radius: 50%;
background: #f5f5f5;
object-fit: cover;
border: 2rpx solid #fff;
box-shadow: 0 2rpx 8rpx 0 rgba(0,0,0,0.04);
}
.input-area {
margin-top: 15rpx;
flex: 1;
display: flex;
flex-direction: column;
justify-content: flex-start;
}
.label-row {
display: flex;
align-items: center;
margin-bottom: 6rpx;
}
.required {
color: #e23d3d;
font-size: 28rpx;
margin-right: 4rpx;
}
.label {
font-size: 28rpx;
color: #222;
font-weight: bold;
}
.input {
width: 100%;
border: none;
border-bottom: 1.5rpx solid #ececec;
font-size: 28rpx;
background: transparent;
padding: 8rpx 0 6rpx 0;
margin-bottom: 2rpx;
}
.input-placeholder {
color: #d2d2d2;
font-size: 26rpx;
}
.divider {
height: 1rpx;
background: #f3f3f3;
margin: 0 0 0 92rpx;
}
.bottom-btn-area {
margin-top: auto;
padding: 48rpx 32rpx 32rpx 32rpx;
background: transparent;
}
.submit-btn {
width: 100%;
height: 88rpx;
background: #0a2e6d;
color: #fff;
font-size: 32rpx;
border-radius: 44rpx;
font-weight: bold;
letter-spacing: 2rpx;
transition: background 0.2s;
}
.submit-btn.disabled {
background: #807a7a;
color: #fff;
}
.submit-btn.pending {
background: #0a226d;
color: #fff;
}
.pending-tag {
background: #807a7a;
color: #fff;
font-size: 24rpx;
border-radius: 10rpx;
padding: 6rpx 24rpx;
margin-left: 20rpx;
margin-bottom: 32rpx;
}
</style>

+ 206
- 0
pages_order/novel/Respondcomments.vue View File

@ -0,0 +1,206 @@
<template>
<view class="respond-comments-page">
<!-- 顶部导航栏 -->
<navbar title="回复评论" :leftClick="true" @leftClick="goBack">
<template #left>
<uv-icon name="arrow-left" customPrefix="uvicon" size="22" color="#222" />
</template>
</navbar>
<!-- 原评论展示 -->
<view class="origin-comment-card">
<view class="comment-header">
<image class="avatar" :src="comment.avatar" mode="aspectFill" />
<view class="user-info">
<text class="username">{{ comment.username }}</text>
</view>
</view>
<view class="comment-content">{{ comment.content }}</view>
<view class="comment-footer">
<text class="comment-time">{{ comment.time }}</text>
<text class="comment-reply-count">
<uv-icon name="chat" customPrefix="uvicon" size="18" color="#bdbdbd" style="margin-right: 4rpx;" />
{{ comment.replyCount }}
</text>
</view>
</view>
<!-- 回复输入区 -->
<view class="reply-area">
<view class="form-label-row">
<text class="required-star">*</text>
<text class="form-label">回复内容</text>
</view>
<uv-input
v-model="replyContent"
type="text"
:maxlength="200"
placeholder="请输入回复内容"
border="surround"
clearable
class="reply-input"
/>
</view>
<!-- 底部提交按钮 -->
<view class="reply-footer">
<button class="submit-btn" :disabled="!replyContent.trim()" @click="submitReply">发送</button>
</view>
</view>
</template>
<script>
import navbar from '@/components/base/navbar.vue'
export default {
components: { navbar },
data() {
return {
comment: {
avatar: 'https://tse4-mm.cn.bing.net/th/id/OIP-C.iUyxJ_fxLjjX3kEBjteXWwAAAA?rs=1&pid=ImgDetMain',
username: '方香橙',
content: '我是本书的作者方香橙,这是一本甜文爽文哒!请放心入坑,五星好评!女主又美有个性可爱,绝对不圣母,不傻白!男主身心干净深情独宠媳妇儿一个人...',
time: '2024.07.09',
replyCount: 17
},
replyContent: ''
}
},
methods: {
goBack() {
uni.navigateBack()
},
submitReply() {
if (!this.replyContent.trim()) {
uni.showToast({ title: '请输入回复内容', icon: 'none' })
return
}
// API
uni.showToast({ title: '回复成功', icon: 'success' })
this.replyContent = ''
setTimeout(() => {
uni.navigateBack()
}, 1000)
}
}
}
</script>
<style scoped lang="scss">
.respond-comments-page {
min-height: 100vh;
background: #f8f8f8;
display: flex;
flex-direction: column;
}
.origin-comment-card {
background: #fff;
margin: 24rpx 24rpx 0 24rpx;
padding: 24rpx 24rpx 0 24rpx;
margin-bottom: 0;
border-radius: 0;
box-shadow: none;
padding-bottom: 0;
}
.comment-header {
display: flex;
align-items: center;
margin-bottom: 8rpx;
}
.avatar {
width: 56rpx;
height: 56rpx;
border-radius: 50%;
margin-right: 16rpx;
}
.user-info {
display: flex;
flex-direction: column;
}
.username {
font-size: 26rpx;
color: #222;
font-weight: 500;
}
.comment-content {
font-size: 26rpx;
color: #333;
margin-bottom: 12rpx;
}
.comment-footer {
display: flex;
align-items: center;
font-size: 22rpx;
color: #bdbdbd;
justify-content: space-between;
}
.comment-time {
color: #bdbdbd;
margin-top: 18rpx;
}
.comment-reply-count {
display: flex;
align-items: center;
font-size: 22rpx;
color: #bdbdbd;
}
.reply-area {
background: #fff;
margin: 0 24rpx 0 24rpx;
padding: 0 24rpx 24rpx 24rpx;
display: flex;
flex-direction: column;
border-radius: 0;
box-shadow: none;
margin-top: 0;
}
.form-label-row {
display: flex;
align-items: center;
margin-bottom: 8rpx;
margin-top: 50rpx;
}
.required-star {
color: #e23d3d;
font-size: 22rpx;
margin-right: 4rpx;
line-height: 1;
}
.form-label {
color: #222;
font-size: 26rpx;
font-weight: 400;
}
.reply-input {
margin-top: 12rpx;
border: none !important;
box-shadow: none !important;
}
.reply-footer {
position: fixed;
left: 0;
right: 0;
bottom: 90rpx;
background: #fff;
padding: 24rpx 32rpx 32rpx 32rpx;
box-shadow: 0 -2rpx 12rpx rgba(0,0,0,0.03);
z-index: 10;
}
.submit-btn {
width: 100%;
height: 80rpx;
background: #0a225f !important;
color: #fff !important;
font-size: 30rpx;
border-radius: 40rpx;
font-weight: 500;
letter-spacing: 2rpx;
border: none;
box-shadow: none;
margin: 0 auto;
display: block;
text-align: center;
line-height: 80rpx;
}
.submit-btn:disabled {
background: #bdbdbd;
color: #fff;
}
</style>

+ 167
- 0
pages_order/novel/Review.vue View File

@ -0,0 +1,167 @@
<template>
<view class="review-page">
<!-- 顶部导航栏 -->
<navbar title="写书评" :leftClick="true" @leftClick="goBack" />
<view class="review-content">
<view class="book-title-label">书本名称</view>
<view class="book-title">{{ bookTitle }}</view>
<view class="form-area flex-grow">
<view class="form-label-row">
<text class="required-star">*</text>
<text class="form-label">书评内容</text>
</view>
<textarea v-model="form.content" class="review-textarea custom-placeholder full-textarea" placeholder="请输入书评内容" />
</view>
</view>
<view class="review-footer">
<view class="footer-divider"></view>
<button class="submit-btn" @click="submitReview">发布</button>
</view>
</view>
</template>
<script>
import navbar from '@/components/base/navbar.vue'
export default {
components: { navbar },
data() {
return {
bookTitle: '',
form: {
content: ''
},
rules: {
content: [
{ required: true, message: '请输入书评内容', trigger: ['blur', 'change'] }
]
}
}
},
onLoad(options) {
//
this.bookTitle = options.title || '未知书名'
},
methods: {
goBack() {
uni.navigateBack()
},
submitReview() {
this.$refs.reviewForm.validate().then(() => {
// API
uni.showToast({ title: '发布成功', icon: 'success' })
setTimeout(() => {
uni.navigateBack()
}, 1000)
}).catch(() => {})
}
}
}
</script>
<style scoped lang="scss">
.review-page {
min-height: 100vh;
background: #f8f8f8;
display: flex;
flex-direction: column;
margin-top: -50rpx;
}
.review-content {
background: #fff;
margin: 24rpx 24rpx 0 24rpx;
border-radius: 16rpx;
padding: 32rpx 24rpx 24rpx 24rpx;
display: flex;
flex-direction: column;
flex: 1;
min-height: 0;
padding-bottom: 140rpx;
margin-top: calc(var(--status-bar-height, 0px) + 100rpx);
}
.flex-grow {
flex: 1 1 0;
display: flex;
flex-direction: column;
min-height: 0;
}
.book-title-label {
color: #bdbdbd;
font-size: 24rpx;
margin-bottom: 4rpx;
}
.book-title {
font-size: 28rpx;
color: #222;
margin-bottom: 36rpx;
border-bottom: 1px solid #ededed;
padding-bottom: 8rpx;
}
.form-area {
margin-top: 18rpx;
}
.form-label-row {
display: flex;
align-items: center;
margin-bottom: 8rpx;
}
.required-star {
color: #e23d3d;
font-size: 22rpx;
margin-right: 4rpx;
line-height: 1;
}
.form-label {
color: #222;
font-size: 26rpx;
font-weight: 400;
}
.review-textarea {
width: 100%;
min-height: 320rpx;
border: none;
background: transparent;
font-size: 28rpx;
color: #333;
resize: none;
margin-top: 0;
outline: none;
}
.review-textarea.custom-placeholder::placeholder {
color: #d2d2d2;
font-size: 26rpx;
}
.full-textarea {
flex: 1;
min-height: 0;
max-height: none;
box-sizing: border-box;
margin-bottom: 0;
}
.review-footer {
position: fixed;
left: 0;
right: 0;
bottom: 90rpx;
background: #fff;
padding: 24rpx 32rpx 32rpx 32rpx;
box-shadow: 0 -2rpx 12rpx rgba(0,0,0,0.03);
z-index: 10;
}
.footer-divider {
width: 100%;
height: 2rpx;
background: #f2f2f2;
margin-bottom: 24rpx;
}
.submit-btn {
width: 100%;
height: 80rpx;
background: #0a225f;
color: #fff;
font-size: 30rpx;
border-radius: 40rpx;
font-weight: 500;
letter-spacing: 2rpx;
}
</style>

+ 228
- 0
pages_order/novel/SubscriptionInformation.vue View File

@ -0,0 +1,228 @@
<template>
<view class="subscription-info-container">
<uv-navbar title="订阅信息" :autoBack="true" fixed placeholder titleStyle="color: #333; font-weight: 700;" :border="false" leftIconSize="46" />
<view class="content-area">
<view class="card">
<view class="section-title">订阅章节</view>
<view class="row row-split">
<template v-if="!showBatch">
<text class="label">章节名称</text>
<view class="row-content">
<text class="value">第1章 2004</text>
<text class="batch-link" @click="showBatch = true">批量订阅</text>
</view>
</template>
<template v-else>
<view class="batch-row">
<view class="batch-left">
<text class="star">*</text>
<text class="batch-title">批量订阅</text>
</view>
</view>
<view class="batch-input-row">
<input class="batch-input" v-model="batchCount" placeholder="请输入订阅数" placeholder-class="batch-placeholder" />
<text class="batch-action" @click="batchSubscribeAction">订阅本章</text>
</view>
<view class="batch-divider"></view>
</template>
</view>
<view class="row row-split">
<text class="label">章节信息</text>
<view class="row-content">
<text class="value">本次订阅消耗10 豆豆</text>
</view>
</view>
<view class="row">
<text class="label">支付方式</text>
<view class="row-content">
<text class="value">账户余额0 豆豆</text>
</view>
</view>
</view>
<view class="card order-info">
<view class="section-title">订单信息</view>
<view class="row">
<text class="label">内容</text>
</view>
<view class="row">
<text class="price"><span class="total-label">合计</span><span class="total-amount">0.10 </span></text>
</view>
</view>
<view class="tips">
请仔细检查并确认相关信息因用户个人疏忽导致的充值错误章节用户自行承担一旦完成充值概不退换
</view>
</view>
<button class="pay-btn" @click="payAndSubscribe">支付并订阅</button>
</view>
</template>
<script>
export default {
data() {
return {
showBatch: false,
batchCount: ''
}
},
methods: {
batchSubscribe() {
uni.showToast({ title: '批量订阅功能开发中', icon: 'none' })
},
batchSubscribeAction() {
// Implementation of batchSubscribeAction method
},
payAndSubscribe() {
uni.showToast({ title: '支付成功,已订阅', icon: 'success' })
//
}
}
}
</script>
<style scoped>
.subscription-info-container {
background: #f8f8f8;
height: 100vh;
overflow: hidden;
position: relative;
}
.content-area {
padding-bottom: 180rpx;
}
body {
overflow: hidden;
}
.card {
background: #fff;
border-radius: 24rpx;
margin: 32rpx 24rpx 0 24rpx;
padding: 32rpx 24rpx;
box-shadow: 0 2rpx 8rpx rgba(0,0,0,0.03);
}
.section-title {
font-size: 32rpx;
font-weight: bold;
color: #222;
margin-bottom: 24rpx;
}
.row {
display: flex;
flex-direction: column;
padding: 10rpx 0;
margin-top: 10rpx;
}
.row-split {
border-bottom: 1px solid #f0f0f0;
}
.label {
color: #888;
font-size: 26rpx;
margin-bottom: 4rpx;
margin-top: 20rpx;
}
.row-content {
display: flex;
flex-direction: row;
align-items: center;
justify-content: space-between;
}
.value {
color: #222;
font-size: 26rpx;
}
.batch-link {
color: #3478f6;
font-size: 24rpx;
margin-left: 24rpx;
}
.order-info {
margin-top: 32rpx;
}
.order-row {
flex-direction: row;
align-items: center;
justify-content: space-between;
padding: 10rpx 0;
}
.price {
font-size: 28rpx;
margin-left: 0;
}
.total-label {
color: #222;
}
.total-amount {
color: #ff6600;
}
.tips {
color: #bbb;
font-size: 22rpx;
margin: 32rpx 24rpx 0 24rpx;
line-height: 1.6;
}
.pay-btn {
position: fixed;
left: 24rpx;
right: 24rpx;
bottom: 90rpx;
background: #0a297e;
color: #fff;
font-size: 32rpx;
border-radius: 40rpx;
height: 88rpx;
line-height: 88rpx;
text-align: center;
font-weight: bold;
z-index: 100;
}
.batch-row {
display: flex;
align-items: center;
justify-content: space-between;
margin-bottom: 0;
}
.batch-left {
display: flex;
align-items: center;
}
.star {
color: #f5222d;
margin-right: 6rpx;
font-size: 26rpx;
}
.batch-title {
font-size: 26rpx;
color: #222;
font-weight: 500;
}
.batch-action {
color: #3478f6;
font-size: 24rpx;
margin-left: 24rpx;
}
.batch-input-row {
display: flex;
align-items: center;
margin-top: 4rpx;
}
.batch-input {
flex: 1;
border: none;
font-size: 24rpx;
color: #bbb;
background: transparent;
outline: none;
padding: 0;
height: 40rpx;
}
.batch-placeholder {
color: #bbb;
font-size: 24rpx;
}
.batch-divider {
height: 2rpx;
background: #f0f0f0;
width: 100%;
margin-top: 4rpx;
}
</style>

+ 233
- 0
pages_order/novel/Tipping.vue View File

@ -0,0 +1,233 @@
<template>
<view class="tipping-page">
<!-- 顶部导航栏 -->
<uv-navbar title="读者亲密值榜单" fixed placeholder></uv-navbar>
<!-- 榜单前三名 -->
<view class="top-three">
<view class="top-item second">
<image class="avatar" :src="topList[1].avatar" mode="aspectFill" />
<view class="name">{{ topList[1].name }}</view>
<view class="score">{{ topList[1].score }} 亲密值</view>
<view class="level">护书使者 五级</view>
</view>
<view class="top-item first">
<image class="avatar" :src="topList[0].avatar" mode="aspectFill" />
<view class="name">{{ topList[0].name }}</view>
<view class="score">{{ topList[0].score }} 亲密值</view>
<view class="level">护书使者 五级</view>
</view>
<view class="top-item third">
<image class="avatar" :src="topList[2].avatar" mode="aspectFill" />
<view class="name">{{ topList[2].name }}</view>
<view class="score">{{ topList[2].score }} 亲密值</view>
<view class="level">护书使者 五级</view>
</view>
</view>
<!-- 榜单列表 -->
<view class="rank-list">
<RankListItem
v-for="(item, idx) in rankList"
:key="item.id"
:rankIcon="idx < 3 ? rankIcons[idx] : ''"
:rankNumImg="idx >= 3 ? ('/static/rank-num-' + (idx+1) + '.png') : ''"
medal="/static/medal.png"
:avatar="item.avatar"
:name="item.name"
:score="item.score"
level="护书使者 五级"
/>
</view>
<!-- 底部按钮 -->
<view class="bottom-btn-area">
<button class="tipping-btn">互动打赏</button>
</view>
</view>
</template>
<script>
import RankListItem from '@/components/novel/RankListItem.vue'
export default {
components: { RankListItem },
data() {
return {
topList: [
{
avatar: 'https://tse4-mm.cn.bing.net/th/id/OIP-C.iUyxJ_fxLjjX3kEBjteXWwAAAA?rs=1&pid=ImgDetMain',
name: '周海',
score: 6785452
},
{
avatar: 'https://tse4-mm.cn.bing.net/th/id/OIP-C.iUyxJ_fxLjjX3kEBjteXWwAAAA?rs=1&pid=ImgDetMain',
name: '冯冉冉',
score: 6785452
},
{
avatar: 'https://tse4-mm.cn.bing.net/th/id/OIP-C.iUyxJ_fxLjjX3kEBjteXWwAAAA?rs=1&pid=ImgDetMain',
name: '南静',
score: 6785452
}
],
rankList: [
{ id: 4, avatar: 'https://tse4-mm.cn.bing.net/th/id/OIP-C.iUyxJ_fxLjjX3kEBjteXWwAAAA?rs=1&pid=ImgDetMain', name: '钱胡胡', score: 5325324 },
{ id: 5, avatar: 'https://tse4-mm.cn.bing.net/th/id/OIP-C.iUyxJ_fxLjjX3kEBjteXWwAAAA?rs=1&pid=ImgDetMain', name: '冯艺瑄', score: 4819704 },
{ id: 6, avatar: 'https://tse4-mm.cn.bing.net/th/id/OIP-C.iUyxJ_fxLjjX3kEBjteXWwAAAA?rs=1&pid=ImgDetMain', name: '王凡宏', score: 4696874 },
{ id: 7, avatar: 'https://tse4-mm.cn.bing.net/th/id/OIP-C.iUyxJ_fxLjjX3kEBjteXWwAAAA?rs=1&pid=ImgDetMain', name: '辛书萍', score: 3722953 },
{ id: 8, avatar: 'https://tse4-mm.cn.bing.net/th/id/OIP-C.iUyxJ_fxLjjX3kEBjteXWwAAAA?rs=1&pid=ImgDetMain', name: '李婷', score: 2872476 },
{ id: 9, avatar: 'https://tse4-mm.cn.bing.net/th/id/OIP-C.iUyxJ_fxLjjX3kEBjteXWwAAAA?rs=1&pid=ImgDetMain', name: '郑盈', score: 2464869 },
{ id: 10, avatar: 'https://tse4-mm.cn.bing.net/th/id/OIP-C.iUyxJ_fxLjjX3kEBjteXWwAAAA?rs=1&pid=ImgDetMain', name: '吴承联', score: 990238 }
],
rankIcons: [
'https://img.yzcdn.cn/vant/rank-1.png',
'https://img.yzcdn.cn/vant/rank-2.png',
'https://img.yzcdn.cn/vant/rank-3.png'
]
}
}
}
</script>
<style lang="scss" scoped>
.tipping-page {
min-height: 100vh;
background: linear-gradient(180deg, #b86e3b 0%, #e6b07c 100%);
padding-bottom: 40rpx;
}
.top-three {
display: flex;
justify-content: center;
align-items: flex-end;
margin: 40rpx 0 20rpx 0;
.top-item {
display: flex;
flex-direction: column;
align-items: center;
background: #fff7e0;
border-radius: 20rpx;
margin: 0 16rpx;
padding: 24rpx 18rpx 18rpx 18rpx;
box-shadow: 0 4rpx 16rpx 0 rgba(0,0,0,0.08);
position: relative;
width: 180rpx;
.avatar {
width: 90rpx;
height: 90rpx;
border-radius: 50%;
border: 4rpx solid #ffd700;
margin-bottom: 10rpx;
}
.name {
font-size: 28rpx;
font-weight: bold;
color: #b86e3b;
margin-bottom: 6rpx;
}
.score {
font-size: 24rpx;
color: #e6b07c;
margin-bottom: 4rpx;
}
.level {
font-size: 22rpx;
color: #b86e3b;
background: #ffe7b2;
border-radius: 10rpx;
padding: 2rpx 12rpx;
}
}
.first {
transform: scale(1.15);
z-index: 2;
background: #fffbe6;
box-shadow: 0 8rpx 24rpx 0 rgba(255,215,0,0.18);
}
.second, .third {
z-index: 1;
opacity: 0.95;
}
}
.rank-list {
background: transparent;
margin: 0 24rpx;
margin-top: 20rpx;
.rank-item {
display: flex;
align-items: center;
justify-content: space-between;
background: #fffbe6;
border-radius: 16rpx;
margin-bottom: 18rpx;
box-shadow: 0 2rpx 8rpx 0 rgba(184,110,59,0.06);
padding: 0 24rpx;
height: 100rpx;
.rank-left {
display: flex;
align-items: center;
.rank-icon, .rank-num-img {
width: 38rpx;
height: 38rpx;
margin-right: 10rpx;
}
.medal {
width: 44rpx;
height: 44rpx;
margin-right: 10rpx;
}
.avatar {
width: 44rpx;
height: 44rpx;
border-radius: 50%;
margin-right: 14rpx;
border: 2rpx solid #ffd700;
object-fit: cover;
}
.name {
font-size: 26rpx;
color: #222;
font-weight: 500;
}
}
.rank-right {
display: flex;
flex-direction: column;
align-items: flex-end;
.score {
font-size: 22rpx;
color: #b86e3b;
}
.level {
font-size: 20rpx;
color: #fff;
background: #e6b07c;
border-radius: 8rpx;
padding: 2rpx 10rpx;
margin-top: 6rpx;
font-weight: 500;
}
}
}
}
.bottom-btn-area {
margin: 40rpx 24rpx 90rpx 24rpx;
display: flex;
flex-direction: column;
gap: 24rpx;
.tipping-btn {
width: 100%;
height: 80rpx;
background: #fffbe6;
color: #b86e3b;
font-size: 32rpx;
border-radius: 40rpx;
font-weight: bold;
letter-spacing: 2rpx;
box-shadow: 0 4rpx 16rpx 0 rgba(184,110,59,0.12);
border: none;
}
}
</style>

+ 249
- 0
pages_order/novel/Walletflow.vue View File

@ -0,0 +1,249 @@
<!-- 钱包流水页面 -->
<template>
<view class="walletflow-page">
<!-- 顶部导航栏 -->
<uv-navbar title="钱包流水" :autoBack="true" fixed placeholder titleStyle="color: #333; font-weight: 700;" :border="false" />
<!-- 账户余额卡片 -->
<view class="balance-card">
<view class="balance-label">账户</view>
<view class="balance-row">
<text class="balance-amount">{{ balance }}</text>
<button class="recharge-btn" @click="goRecharge">充值</button>
</view>
</view>
<!-- tab和流水列表卡片 -->
<view class="flow-card">
<view class="tab-header">
<view :class="['tab-item', {active: activeTab === 0}]" @click="activeTab = 0">
充值
<view v-if="activeTab === 0" class="tab-underline"></view>
</view>
<view :class="['tab-item', {active: activeTab === 1}]" @click="activeTab = 1">
支付
<view v-if="activeTab === 1" class="tab-underline"></view>
</view>
</view>
<scroll-view scroll-y class="flow-list">
<view v-if="activeTab === 0">
<view class="flow-item" v-for="(item, idx) in rechargeList" :key="idx">
<view class="flow-item-row">
<view class="flow-item-left">
<view class="flow-title">{{ item.title }}</view>
<view class="flow-date">{{ item.date }}</view>
</view>
<view class="flow-amount plus">+{{ item.amount }}</view>
</view>
</view>
</view>
<view v-else>
<view class="flow-item" v-for="(item, idx) in payList" :key="idx">
<view class="flow-item-row">
<view class="flow-item-left">
<view class="flow-title">{{ item.title }}</view>
<view class="flow-date">{{ item.date }}</view>
</view>
<view class="flow-amount minus">-{{ item.amount }}</view>
</view>
</view>
</view>
</scroll-view>
</view>
</view>
</template>
<script>
export default {
data() {
return {
balance: 34532,
activeTab: 0,
rechargeList: [
{ title: '豆豆充值', date: '2025.03.18', amount: 55 },
{ title: '豆豆充值', date: '2025.03.18', amount: 55 },
{ title: '豆豆充值', date: '2025.03.18', amount: 55 },
{ title: '推荐票', date: '2025.03.18', amount: 5 },
{ title: '豆豆充值', date: '2025.03.18', amount: 55 },
{ title: '豆豆充值', date: '2025.03.18', amount: 55 },
{ title: '豆豆充值', date: '2025.03.18', amount: 55 },
{ title: '豆豆充值', date: '2025.03.18', amount: 55 },
{ title: '豆豆充值', date: '2025.03.18', amount: 55 },
{ title: '豆豆充值', date: '2025.03.18', amount: 55 },
{ title: '豆豆充值', date: '2025.03.18', amount: 55 },
{ title: '豆豆充值', date: '2025.03.18', amount: 55 },
{ title: '豆豆充值', date: '2025.03.18', amount: 55 },
{ title: '豆豆充值', date: '2025.03.18', amount: 55 },
],
payList: [
{ title: '章节支付', date: '2025.03.18', amount: 10 },
{ title: '章节支付', date: '2025.03.18', amount: 10 },
{ title: '章节支付', date: '2025.03.18', amount: 10 },
{ title: '章节支付', date: '2025.03.18', amount: 10 },
{ title: '章节支付', date: '2025.03.18', amount: 10 },
{ title: '章节支付', date: '2025.03.18', amount: 10 },
{ title: '章节支付', date: '2025.03.18', amount: 10 },
{ title: '章节支付', date: '2025.03.18', amount: 10 },
{ title: '章节支付', date: '2025.03.18', amount: 10 },
{ title: '章节支付', date: '2025.03.18', amount: 10 },
{ title: '章节支付', date: '2025.03.18', amount: 10 },
{ title: '章节支付', date: '2025.03.18', amount: 10 },
{ title: '章节支付', date: '2025.03.18', amount: 10 },
{ title: '章节支付', date: '2025.03.18', amount: 10 },
{ title: '章节支付', date: '2025.03.18', amount: 10 },
]
}
},
methods: {
goRecharge() {
uni.showToast({ title: '充值功能开发中', icon: 'none' })
}
}
}
</script>
<style lang="scss" scoped>
.walletflow-page {
min-height: 100vh;
background: linear-gradient(180deg, #f8f8fc 0%, #fff 100%);
padding-bottom: 30rpx;
}
.balance-card {
background: linear-gradient(90deg, #f7f2fa 0%, #fbeaf2 100%);
border-radius: 18rpx;
margin: 24rpx 12rpx 0 12rpx;
padding: 18rpx 24rpx 14rpx 24rpx;
box-shadow: none;
border: 1rpx solid #ede7ef;
position: relative;
display: flex;
flex-direction: column;
min-height: 130rpx;
justify-content: center;
.balance-label {
color: #bbb;
font-size: 26rpx;
margin-bottom: 8rpx;
}
.balance-row {
display: flex;
align-items: center;
margin-top: 0;
position: relative;
.balance-amount {
color: #e94f7a;
font-size: 48rpx;
font-weight: bold;
}
.recharge-btn {
position: absolute;
right: 0;
top: 50%;
transform: translateY(-50%);
background: linear-gradient(90deg, #ffb6c1 0%, #fa5a99 100%);
color: #fff;
font-size: 28rpx;
border-radius: 32rpx;
padding: 0 40rpx;
height: 56rpx;
line-height: 56rpx;
font-weight: 500;
border: none;
box-shadow: none;
display: flex;
align-items: center;
justify-content: center;
}
}
}
.flow-card {
background: #fff;
border-radius: 20rpx;
margin: 32rpx 16rpx 0 16rpx;
box-shadow: 0 4rpx 24rpx 0 rgba(0,0,0,0.06);
padding-bottom: 8rpx;
overflow: hidden;
}
.tab-header {
display: flex;
margin: 0;
background: #fff;
border-top-left-radius: 20rpx;
border-top-right-radius: 20rpx;
overflow: hidden;
.tab-item {
flex: 1;
text-align: center;
font-size: 30rpx;
color: #888;
padding: 0 0 18rpx 0;
font-weight: bold;
background: transparent;
position: relative;
&.active {
color: #223a7a;
font-weight: bold;
}
.tab-underline {
position: absolute;
left: 50%;
bottom: 0;
transform: translateX(-50%);
width: 44rpx;
height: 4rpx;
background: #223a7a;
border-radius: 2rpx;
margin-top: 4rpx;
}
}
}
.flow-list {
margin: 0;
padding: 0 16rpx;
max-height: 75vh;
background: #fff;
}
.flow-item {
border-bottom: 1px solid #f5f5f5;
padding: 18rpx 0 8rpx 0;
&:last-child {
border-bottom: none;
}
.flow-item-row {
display: flex;
align-items: flex-start;
justify-content: space-between;
padding-right: 45rpx;
padding-left: 15rpx;
}
.flow-item-left {
display: flex;
flex-direction: column;
align-items: flex-start;
.flow-title {
font-size: 28rpx;
color: #222;
font-weight: 500;
margin-bottom: 2rpx;
}
.flow-date {
color: #bbb;
font-size: 22rpx;
margin-top: 0;
}
}
.flow-amount {
font-size: 26rpx;
font-weight: 500;
margin-left: 24rpx;
margin-top: 2rpx;
&.plus {
color: #223a7a;
}
&.minus {
color: #e94f7a;
}
}
}
</style>

+ 1
- 0
pages_order/novel/announcement.vue View File

@ -1,4 +1,5 @@
<template>
<!-- 公告详情页面 -->
<view class="announcement-container">
<uv-navbar
title="公告详情"


+ 112
- 53
pages_order/novel/chapterList.vue View File

@ -1,17 +1,16 @@
<template>
<view class="chapter-container">
<view class="header">
<view class="nav-bar">
<view class="nav-bar" style="position: relative; height: 44px;">
<view class="back" @click="goBack">
<text class="iconfont icon-back">&#xe60e;</text>
</view>
<view class="tabs">
<view class="tab" :class="{ active: activeTab === 'draft' }" @click="switchTab('draft')">草稿箱</view>
<view class="tab" :class="{ active: activeTab === 'published' }" @click="switchTab('published')">已发布</view>
</view>
<view class="more">
<text class="iconfont">&#xe612;</text>
<uv-icon name="arrow-left" color="black" size="48"></uv-icon>
</view>
<view class="title">兽王进化从被小萝莉...</view>
<view class="right-icons"></view>
</view>
<view class="tabs">
<view class="tab" :class="{ active: activeTab === 'draft' }" @click="switchTab('draft')">草稿箱</view>
<view class="tab" :class="{ active: activeTab === 'published' }" @click="switchTab('published')">已发布</view>
</view>
</view>
@ -21,7 +20,7 @@
<text class="chapter-title">章节名</text>
<text class="chapter-number">{{index + 1}}</text>
</view>
<text class="iconfont icon-arrow">&#xe65f;</text>
<uv-icon name="arrow-right" color="#999" size="28"></uv-icon>
</view>
</view>
@ -37,16 +36,45 @@ export default {
data() {
return {
activeTab: 'draft',
chapters: Array(8).fill({}) // 8
chapters: [] //
}
},
onLoad(options) {
// tabtab
if (options && options.tab === 'published') {
this.activeTab = 'published';
} else {
this.activeTab = 'draft';
}
this.loadChapters();
//
if (options && options.fromSave === '1') {
uni.showToast({
title: '保存成功',
icon: 'success'
});
}
if (options && options.fromPublish === '1') {
uni.showToast({
title: '发布成功',
icon: 'success'
});
}
},
methods: {
loadChapters() {
if (this.activeTab === 'published') {
this.chapters = uni.getStorageSync('publishedChapters') || [];
} else {
this.chapters = uni.getStorageSync('chapters') || [];
}
},
goBack() {
uni.navigateBack()
},
switchTab(tab) {
this.activeTab = tab
// tab
this.activeTab = tab;
this.loadChapters();
},
addNewChapter() {
uni.navigateTo({
@ -75,58 +103,89 @@ export default {
.header {
background: #fff;
padding-top: var(--status-bar-height);
.nav-bar {
position: relative;
height: 44px;
display: flex;
align-items: center;
justify-content: space-between;
padding: 0 15px;
.back {
padding: 5px;
.icon-back {
font-size: 20px;
}
position: absolute;
left: 0;
top: 0;
bottom: 0;
width: 48px;
display: flex;
align-items: center;
justify-content: center;
z-index: 2;
}
.tabs {
.right-icons {
position: absolute;
right: 0;
top: 0;
bottom: 0;
width: 48px;
display: flex;
.tab {
padding: 0 15px;
font-size: 16px;
color: #666;
position: relative;
&.active {
color: #000;
font-weight: bold;
&::after {
content: '';
position: absolute;
bottom: -4px;
left: 50%;
transform: translateX(-50%);
width: 20px;
height: 2px;
background: #2b4acb;
border-radius: 1px;
}
align-items: center;
justify-content: center;
z-index: 2;
}
.title {
position: absolute;
left: 0;
right: 0;
top: 0;
bottom: 0;
margin: auto;
height: 44px;
display: flex;
align-items: center;
justify-content: center;
font-size: 16px;
font-weight: 500;
color: #222;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
text-align: center;
z-index: 1;
pointer-events: none;
}
}
.tabs {
display: flex;
margin-top: 4px;
margin-left: 120rpx;
justify-content: space-between;
.tab {
font-size: 15px;
color: #888;
margin-right: 120rpx;
position: relative;
&.active {
color: #223a7a;
font-weight: 600;
&::after {
content: '';
display: block;
width: 24px;
height: 3px;
background: #223a7a;
border-radius: 2px;
position: absolute;
left: 50%;
transform: translateX(-50%);
bottom: -6px;
}
}
}
.more {
padding: 5px;
}
}
}
.chapter-list {
padding: 0 15px;
padding: 20rpx 40rpx;
background-color: rgb(255, 254, 254);
margin: 50rpx 40rpx;
border-radius: 3%;
.chapter-item {
display: flex;
justify-content: space-between;


+ 232
- 0
pages_order/novel/comments.vue View File

@ -0,0 +1,232 @@
<template>
<view class="comments-page">
<!-- 顶部导航栏 -->
<navbar title="评论详情" :leftClick="true" @leftClick="goBack" />
<!-- 书本名称 -->
<view class="book-title-area">
<view class="book-title-label">书本名称</view>
<view class="book-title">{{ bookTitle }}</view>
</view>
<!-- 主评论卡片 -->
<view class="comment-card">
<view class="comment-header">
<image class="avatar" :src="comment.avatar" mode="aspectFill" />
<view class="user-info">
<text class="username">{{ comment.username }}</text>
</view>
</view>
<view class="comment-content">{{ comment.content }}</view>
<view class="comment-footer">
<text class="comment-time">{{ comment.time }}</text>
</view>
</view>
<!-- 全部评论列表 -->
<view class="all-reply-area">
<view class="all-reply-header">全部评论·{{ comment.replyCount }}</view>
<view class="reply-list">
<view class="reply-item" v-for="(item, idx) in replies" :key="idx">
<image class="reply-avatar" :src="item.avatar" mode="aspectFill" />
<view class="reply-main">
<view class="reply-username">{{ item.username }}</view>
<view class="reply-content">{{ item.content }}</view>
<view class="reply-time">{{ item.time }}</view>
</view>
</view>
</view>
</view>
<!-- 底部回复按钮 -->
<view class="reply-footer">
<button class="submit-btn" @click="goToRespond">回复评论</button>
</view>
</view>
</template>
<script>
import navbar from '@/components/base/navbar.vue'
export default {
components: { navbar },
data() {
return {
bookTitle: '这游戏也太真实了',
comment: {
avatar: 'https://tse4-mm.cn.bing.net/th/id/OIP-C.iUyxJ_fxLjjX3kEBjteXWwAAAA?rs=1&pid=ImgDetMain',
username: '方香橙',
content: '我是本书的作者方香橙,这是一本甜文爽文哒!请放心入坑,五星好评!女主又美有个性可爱,绝对不圣母,不傻白!男主身心干净深情独宠媳妇儿一个人...',
time: '2024.07.09',
replyCount: 17
},
replies: [
{
avatar: 'https://tse4-mm.cn.bing.net/th/id/OIP-C.iUyxJ_fxLjjX3kEBjteXWwAAAA?rs=1&pid=ImgDetMain',
username: '方香橙',
content: '我是本书的作者方香橙,这是一本甜文爽文哒!请放心入坑,五星好评!女主又美有个性可爱,绝对不圣母,不傻白!男主身心干净深情独宠媳妇儿一个人...',
time: '2024.07.09'
},
{
avatar: 'https://tse4-mm.cn.bing.net/th/id/OIP-C.iUyxJ_fxLjjX3kEBjteXWwAAAA?rs=1&pid=ImgDetMain',
username: '战斗世界',
content: '这本书打破了我看甜文套路之前的观念女主真的太可爱太有趣和以往看过的一个NPC介绍有很大不同',
time: '2024.07.09'
}
]
}
},
methods: {
goBack() {
uni.navigateBack()
},
goToRespond() {
uni.navigateTo({ url: '/pages_order/novel/Respondcomments' })
},
submitReply() {
uni.showToast({ title: '功能开发中', icon: 'none' })
}
}
}
</script>
<style scoped lang="scss">
.comments-page {
min-height: 100vh;
background: #f8f8f8;
display: flex;
flex-direction: column;
}
.book-title-area {
background: #fff;
margin: 24rpx 24rpx 0 24rpx;
border-radius: 16rpx;
padding: 24rpx 24rpx 0 24rpx;
}
.book-title-label {
color: #bdbdbd;
font-size: 24rpx;
margin-bottom: 4rpx;
}
.book-title {
font-size: 28rpx;
color: #222;
margin-bottom: 16rpx;
border-bottom: 1px solid #ededed;
padding-bottom: 8rpx;
}
.comment-card {
background: #fff;
margin: 0 24rpx 0 24rpx;
border-radius: 16rpx;
padding: 24rpx 24rpx 0 24rpx;
box-shadow: 0 2rpx 8rpx rgba(0,0,0,0.03);
margin-bottom: 0;
}
.comment-header {
display: flex;
align-items: center;
margin-bottom: 8rpx;
}
.avatar {
width: 56rpx;
height: 56rpx;
border-radius: 50%;
margin-right: 16rpx;
}
.user-info {
display: flex;
flex-direction: column;
}
.username {
font-size: 26rpx;
color: #222;
font-weight: 500;
}
.comment-content {
font-size: 26rpx;
color: #333;
margin-bottom: 12rpx;
}
.comment-footer {
display: flex;
align-items: center;
font-size: 22rpx;
color: #bdbdbd;
}
.comment-time {
color: #bdbdbd;
margin-top: 18rpx;
}
.all-reply-area {
background: #fff;
margin: 0 24rpx 0 24rpx;
border-radius: 16rpx;
padding: 24rpx 24rpx 16rpx 24rpx;
margin-top: 24rpx;
}
.all-reply-header {
color: #222;
font-size: 28rpx;
font-weight: 500;
margin-bottom: 16rpx;
}
.reply-list {
margin-top: 40rpx;
display: flex;
flex-direction: column;
gap: 50rpx;
}
.reply-item {
display: flex;
align-items: flex-start;
}
.reply-avatar {
width: 44rpx;
height: 44rpx;
border-radius: 50%;
margin-right: 16rpx;
flex-shrink: 0;
}
.reply-main {
flex: 1;
display: flex;
flex-direction: column;
}
.reply-username {
font-size: 24rpx;
color: #222;
font-weight: 500;
margin-bottom: 4rpx;
}
.reply-content {
font-size: 24rpx;
color: #333;
margin-bottom: 6rpx;
word-break: break-all;
}
.reply-time {
font-size: 20rpx;
color: #bdbdbd;
}
.reply-footer {
position: fixed;
left: 0;
right: 0;
bottom: 90rpx;
background: #fff;
padding: 24rpx 32rpx 32rpx 32rpx;
box-shadow: 0 -2rpx 12rpx rgba(0,0,0,0.03);
z-index: 10;
}
.submit-btn {
width: 100%;
height: 80rpx;
background: #0a225f;
color: #fff;
font-size: 30rpx;
border-radius: 40rpx;
font-weight: 500;
letter-spacing: 2rpx;
}
</style>

+ 22
- 3
pages_order/novel/createNovel.vue View File

@ -160,17 +160,36 @@ export default {
id: Date.now().toString(),
title: this.formData.title,
cover: this.formData.cover || 'https://bookcover.yuewen.com/qdbimg/349573/1033014772/150.webp',
readers: '8721',
status: 'ongoing',
type: this.formData.type,
description: this.formData.description,
status: this.formData.status,
readers: '0',
publishStatus: '发布审核中',
isOriginal: true
isOriginal: true,
chapters: [],
author: '当前用户', //
tags: [this.formData.type], //
updateTime: new Date().getTime() //
}
//
let worksList = uni.getStorageSync('worksList') || []
//
worksList.unshift(workData) // 使unshift
//
uni.setStorageSync('worksList', worksList)
//
getApp().globalData.submittedWork = workData
//
getApp().globalData.showSubmitSuccess = true
//
uni.showToast({
title: '创建成功',
icon: 'success'
})
//
setTimeout(() => {
//


+ 116
- 0
pages_order/novel/creator.vue View File

@ -0,0 +1,116 @@
<template>
<view class="creator-page">
<uv-navbar title="请成为创作者" fixed placeholder></uv-navbar>
<view class="form-card">
<view class="form-item">
<text class="required">*</text>
<text class="label">笔名</text>
<input v-model="penName" placeholder="请输入" class="input" placeholder-class="input-placeholder" />
</view>
<view class="form-item">
<text class="required">*</text>
<text class="label">简介</text>
<textarea v-model="intro" placeholder="请输入" class="textarea" placeholder-class="input-placeholder" />
</view>
</view>
<view class="footer-btn-area">
<button class="submit-btn" @click="submit">成为创作者</button>
</view>
</view>
</template>
<script>
export default {
data() {
return {
penName: '',
intro: ''
}
},
methods: {
submit() {
if (!this.penName) {
uni.showToast({ title: '请输入笔名', icon: 'none' })
return
}
if (!this.intro) {
uni.showToast({ title: '请输入简介', icon: 'none' })
return
}
//
uni.showToast({ title: '申请成功', icon: 'success' })
setTimeout(() => {
uni.navigateBack()
}, 800)
}
}
}
</script>
<style scoped lang="scss">
.creator-page {
min-height: 100vh;
background: #f7f8fa;
display: flex;
flex-direction: column;
}
.form-card {
background: #fff;
border-radius: 20rpx;
margin: 40rpx 32rpx 0 32rpx;
padding: 32rpx 24rpx;
box-shadow: 0 4rpx 24rpx 0 rgba(0,0,0,0.04);
}
.form-item {
display: flex;
flex-direction: column;
margin-bottom: 32rpx;
position: relative;
}
.required {
color: #e23d3d;
font-size: 28rpx;
margin-right: 4rpx;
}
.label {
font-size: 28rpx;
color: #222;
font-weight: bold;
margin-bottom: 12rpx;
}
.input, .textarea {
width: 100%;
height: 80rpx;
border: none;
border-bottom: 1.5rpx solid #ececec;
font-size: 28rpx;
background: transparent;
padding: 18rpx 0 12rpx 0;
margin-bottom: 2rpx;
}
.input-placeholder {
color: #d2d2d2;
font-size: 26rpx;
}
.textarea {
min-height: 120rpx;
resize: none;
}
.footer-btn-area {
margin-top: auto;
padding: 48rpx 32rpx 32rpx 32rpx;
background: transparent;
margin-bottom: 80rpx;
}
.submit-btn {
width: 100%;
height: 88rpx;
background: #0a2e6d;
color: #fff;
font-size: 32rpx;
border-radius: 44rpx;
font-weight: bold;
letter-spacing: 2rpx;
transition: background 0.2s;
}
</style>

+ 186
- 20
pages_order/novel/novelDetail.vue View File

@ -2,7 +2,11 @@
<!-- 小说详情页面 -->
<view class="novel-detail">
<!-- 顶部返回导航 -->
<navber></navber>
<uv-navbar title="小说详情" :autoBack="true" fixed placeholder titleStyle="color: #333; font-weight: 700;" :border="false">
<template #left>
<uv-icon name="arrow-left" size="46" color="#333" @click="goBack" />
</template>
</uv-navbar>
<!-- 小说基本信息 -->
<view class="novel-info">
@ -86,7 +90,7 @@
</view>
<!-- 目录 -->
<view class="novel-catalog">
<view class="novel-catalog" @click="handleShowCatalog">
<view class="catalog-header">
<view class="catalog-title">
<text class="title-icon">📖</text>
@ -98,6 +102,25 @@
</view>
</view>
</view>
<!-- 目录弹窗完整内容内联 -->
<view v-if="showCatalog" class="catalog-popup-fullscreen" style="top:196px;bottom:0;height:auto;min-height:calc(100vh - 196px);">
<view class="catalog-header">
<view class="header-left" @click.stop="showCatalog = false">
<uv-icon name="arrow-down" size="46" color="#333" @click="goBack" />
</view>
<view class="header-title">目录</view>
<view class="header-right" @click.stop="orderAsc = !orderAsc">倒序</view>
</view>
<scroll-view scroll-y class="catalog-list" style="padding-bottom:0;margin-bottom:0;">
<view v-for="(item, idx) in (orderAsc ? chapterList : [...chapterList].reverse())" :key="item.id" @click="selectChapter(orderAsc ? idx : chapterList.length - 1 - idx)"
:class="['catalog-item', {active: (orderAsc ? idx : chapterList.length - 1 - idx) === currentIndex}]">
<view class="item-main">
<text class="item-title">{{ item.title }}</text>
<text v-if="item.vip" class="vip-tag">付费</text>
</view>
</view>
</scroll-view>
</view>
<!-- 书评区域 -->
@ -121,7 +144,7 @@
<text class="content-text">我是本书的作者方香橙这是一本甜文爽文哒请放心入坑五星好评女主又美有个性可爱绝对不圣母不傻白男主身心干净深情独宠媳妇儿一个人...</text>
<view class="comment-footer">
<text class="comment-time">2024.07.09</text>
<view class="comment-likes">
<view class="comment-likes" @click="goToCommentReply">
<text class="like-icon">💬</text>
<text class="like-count">17</text>
</view>
@ -147,6 +170,10 @@
<text class="btn-icon">📚</text>
<text>加入书架</text>
</view>
<view class="action-btn" @click="goToGiftbox">
<text class="btn-icon">🎁</text>
<text>礼物盒</text>
</view>
<view class="action-btn" @click="toggleInteractive">
<text class="btn-icon">🎯</text>
<text>互动打赏</text>
@ -160,7 +187,13 @@
</template>
<script>
import catalogpopup from '@/components/novel/CatalogPopup.vue'
export default {
components: {
'uv-navbar': () => import('@/uni_modules/uv-navbar/components/uv-navbar/uv-navbar.vue'),
'uv-icon': () => import('@/uni_modules/uv-icon/components/uv-icon/uv-icon.vue'),
catalogpopup
},
data() {
return {
novelData: {
@ -172,10 +205,6 @@ export default {
coverUrl: 'https://tse4-mm.cn.bing.net/th/id/OIP-C.iUyxJ_fxLjjX3kEBjteXWwAAAA?rs=1&pid=ImgDetMain',
introduction: '这游戏也太真实了!\n模拟,模拟,模拟,这游戏……公空居然把自己做成了游戏的NPC?这是什么新型的套路啊!好了,不要慌了,作为的管理者人员都在忙碌着,都在工作人员中,只要我们努力就自己的事,下个月他又给一套全新的剧情开始,如何将模拟的开关全新的世界,去广阔的世界去探索吧!'
},
userInfo: {
avatar: '/static/images/avatar.jpg',
level: '67级达人'
},
isCollected: false,
chapterCount: 2814,
comments: [
@ -185,16 +214,47 @@ export default {
content: '我是全书中年龄最大的人,这是一本很好的文章!满满心头热,只要对你"安全"文案有兴趣的,都可以不虚,不害怕,不要想太多就去尝试阅读吧!',
time: '2024-07-09'
}
],
showCatalog: false,
orderAsc: true,
currentIndex: 0,
chapterList: [
{ id: 1, title: '第一章 重回2004', vip: false },
{ id: 2, title: '第二章 陈年旧恨', vip: false },
{ id: 3, title: '第三章 再相见', vip: false },
{ id: 4, title: '第四章 李东的邀请', vip: false },
{ id: 5, title: '第五章 小气的男', vip: false },
{ id: 6, title: '第六章 先送谁?', vip: false },
{ id: 7, title: '第七章 打听行情', vip: false },
{ id: 8, title: '第八章 省城探路', vip: false },
{ id: 9, title: '第九章 订货', vip: false },
{ id: 10, title: '第十章 第一桶金', vip: true },
{ id: 11, title: '第十一章 高富帅来袭', vip: true },
{ id: 12, title: '第十二章 故学后,挥场见!', vip: true },
{ id: 13, title: '第十三章 你来我往', vip: true },
{ id: 14, title: '第十四章 你来我往', vip: true },
{ id: 15, title: '第十五章 你来我往', vip: true },
{ id: 16, title: '第十六章 你来我往', vip: true },
{ id: 17, title: '第十七章 你来我往', vip: true },
{ id: 18, title: '第十八章 你来我往', vip: true }
]
}
},
computed: {
userInfo() {
return {
avatar: '/static/images/avatar.jpg',
level: '67级达人'
}
}
},
methods: {
goBack() {
uni.navigateBack()
},
startReading() {
uni.navigateTo({
url: '/pages/reader/reader'
url: `/pages_order/novel/readnovels?id=${this.novelData.id}`
})
},
toggleCollect() {
@ -214,7 +274,10 @@ export default {
})
},
goToWriteReview() {
// TODO:
//
uni.navigateTo({
url: `/pages_order/novel/Review?title=${encodeURIComponent(this.novelData.title)}`
})
},
addToBookshelf() {
// TODO:
@ -224,10 +287,26 @@ export default {
})
},
toggleInteractive() {
// TODO:
uni.showToast({
title: '打赏功能开发中',
icon: 'none'
//
uni.navigateTo({
url: `/pages_order/novel/Tipping?id=${this.novelData.id}`
})
},
goToGiftbox() {
uni.navigateTo({ url: '/pages_order/novel/Giftbox' })
},
handleShowCatalog() {
console.log('点击目录区域');
this.showCatalog = true;
},
selectChapter(idx) {
this.currentIndex = idx
this.showCatalog = false
// TODO:
},
goToCommentReply() {
uni.navigateTo({
url: '/pages_order/novel/comments'
})
}
}
@ -550,34 +629,33 @@ export default {
.comments-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 24rpx;
border-bottom: 2rpx solid #f5f5f5;
padding-bottom: 24rpx;
justify-content: flex-start;
}
.header-left {
display: flex;
align-items: center;
gap: 8rpx;
.title-icon {
font-size: 32rpx;
}
text {
display: flex;
align-items: center;
font-size: 32rpx;
font-weight: 500;
color: #333;
white-space: nowrap;
}
}
.header-right {
text {
font-size: 28rpx;
color: #666;
}
margin-left: auto;
}
.comment-list {
@ -661,6 +739,7 @@ export default {
align-items: center;
border-bottom: 2rpx solid #f5f5f5;
padding-bottom: 24rpx;
}
.catalog-title {
@ -745,4 +824,91 @@ export default {
border-radius: 40rpx;
border: none;
}
.catalog-popup-fullscreen {
position: fixed;
left: 0;
top: 196px;
width: 100vw;
background: #fff;
z-index: 9999;
border-top-left-radius: 32rpx;
border-top-right-radius: 32rpx;
border-bottom-left-radius: 0;
border-bottom-right-radius: 0;
display: flex;
flex-direction: column;
box-shadow: 0 8rpx 48rpx rgba(0,0,0,0.18);
overflow: hidden;
min-height: calc(100vh - 196px);
}
.catalog-header {
display: flex;
align-items: center;
justify-content: space-between;
padding: 0 24rpx;
height: 96rpx;
border-bottom: 1px solid #eee;
position: sticky;
top: 0;
background: #fff;
z-index: 2;
}
.header-left {
width: 60rpx;
display: flex;
align-items: center;
justify-content: flex-start;
}
.header-title {
flex: 1;
text-align: center;
font-size: 32rpx;
font-weight: bold;
color: #222;
}
.header-right {
color: #223a7a;
font-size: 28rpx;
font-weight: 500;
min-width: 80rpx;
text-align: right;
}
.catalog-list {
flex: 1;
overflow: auto;
padding-bottom: 40rpx;
}
.catalog-item {
padding: 0 32rpx;
min-height: 80rpx;
display: flex;
flex-direction: column;
justify-content: center;
border-bottom: 1px solid #f5f5f5;
background: #fff;
color: #222;
font-size: 30rpx;
position: relative;
}
.catalog-item.active {
color: #ff5a5f;
background: #fff7f7;
}
.item-main {
display: flex;
align-items: center;
gap: 16rpx;
}
.item-title {
font-size: 30rpx;
}
.vip-tag {
background: #ffe1b2;
color: #ff9900;
border-radius: 20rpx;
font-size: 24rpx;
padding: 2rpx 18rpx;
margin-left: 16rpx;
}
</style>

+ 448
- 0
pages_order/novel/readnovels.vue View File

@ -0,0 +1,448 @@
<template>
<!-- 小说文本页面 -->
<view class="reader-container" @click.native="toggleFullScreen">
<uv-navbar title="这游戏也太真实了" :autoBack="true" fixed placeholder titleStyle="color: #333; font-weight: 700;" :border="false">
<template #left>
<uv-icon name="arrow-left" size="46" color="#333" @click="$emit('back')" />
</template>
</uv-navbar>
<view class="chapter-title">第1章 2004</view>
<scroll-view scroll-y class="chapter-content" style="height: 70vh;" @touchend="handleTouchEnd" @scroll="handleScroll" @tap="handleContentClick">
<text>
华星华东地区某个省市区不知名街后居民
读朱自清的荷塘月色品读鲁迅的从百草园到三味书屋看梁实秋的雅舍!一篇篇经典散文想起来就像藏在脑海里的动画优雅唯美的画面好像夜晚的微风轻抚着凝望远方的脸颊!美文入心!感慨万千!下面是100篇名家经典散文摘抄敬请你的欣赏!
100篇名家经典散文摘抄
叶圣陶散文"名家经典珍藏"丛书之一收录了叶圣陶先生的散文精品数十篇这些作品内容丰富题材各异构思精巧文笔精巧语言幽默内蕴深厚风格恬淡充分显示了叶圣陶先生的文学功底及丰富的人生阅历从一个侧面反映了作者的思想感情及创作风格非常值得一读叶圣陶是20世纪中国一位杰出的作家教育家和出版家又是中国现代儿童文学创作的先行者作为散文家他早期和周作人朱自清共同成为文学研究会散文创作的中坚后来又成为开明派散文的代表其散文被一九三五年出版的中国新文学大系选录的篇数仅次于周作人鲁迅和朱自清
1朱自清荷塘月色片段
路上只我一个人背着手踱着这一片天地好像是我的;我也像超出了平常旳自己到了另一世界里我爱热闹也爱冷静;爱群居也爱独处像今晚上一个人在这苍茫旳月下什么都可以想什么都可以不想便觉是个自由的人白天里一定要做的事一定要说的话现在都可不理这是独处的妙处我且受用这无边的荷香月色好了
曲曲折折的荷塘上面弥望旳是田田的叶子叶子出水很高像亭亭旳舞女旳裙层层的叶子中间零星地点缀着些白花有袅娜(niǎo,nuó)地开着旳有羞涩地打着朵儿旳;正如一粒粒的明珠又如碧天里的星星又如刚出浴的美人微风过处送来缕缕清香仿佛远处高楼上渺茫的歌声似的这时候叶子与花也有一丝的颤动像闪电般霎时传过荷塘的那边去了叶子本是肩并肩密密地挨着这便宛然有了一道凝碧的波痕叶子底下是脉脉()的流水遮住了不能见一些颜色;而叶子却更见风致了
月光如流水一般静静地泻在这一片叶子和花上薄薄的青雾浮起在荷塘里叶子和花仿佛在牛乳中洗过一样;又像笼着轻纱的梦虽然是满月天上却有一层淡淡的云所以不能朗照;但我以为这恰是到了好处酣眠固不可少小睡也别有风味的月光是隔了树照过来的高处丛生的灌木落下参差的斑驳的黑影峭楞楞如鬼一般;弯弯的杨柳的稀疏的倩影却又像是画在荷叶上塘中的月色并不均匀;但光与影有着和谐的旋律如梵婀(ē)(英语violin小提琴的译音)上奏着的名曲
荷塘的四面远远近近高高低低都是树而杨柳最多这些树将一片荷塘重重围住;只在小路一旁漏着几段空隙像是特为月光留下的树色一例是阴阴的乍看像一团烟雾;但杨柳的丰姿便在烟雾里也辨得出树梢上隐隐约约的是一带远山只有些大意罢了树缝里也漏着一两点路灯光没精打采的是渴睡人的眼这时候最热闹的要数树上的蝉声与水里的蛙声;但热闹是它们的我什么也没有
2鲁迅从百草园到三味书屋片段
100篇名家经典散文摘抄
叶圣陶散文"名家经典珍藏"丛书之一收录了叶圣陶先生的散文精品数十篇这些作品内容丰富题材各异构思精巧文笔精巧语言幽默内蕴深厚风格恬淡充分显示了叶圣陶先生的文学功底及丰富的人生阅历从一个侧面反映了作者的思想感情及创作风格非常值得一读叶圣陶是20世纪中国一位杰出的作家教育家和出版家又是中国现代儿童文学创作的先行者作为散文家他早期和周作人朱自清共同成为文学研究会散文创作的中坚后来又成为开明派散文的代表其散文被一九三五年出版的中国新文学大系选录的篇数仅次于周作人鲁迅和朱自清
1朱自清荷塘月色片段
路上只我一个人背着手踱着这一片天地好像是我的;我也像超出了平常旳自己到了另一世界里我爱热闹也爱冷静;爱群居也爱独处像今晚上一个人在这苍茫旳月下什么都可以想什么都可以不想便觉是个自由的人白天里一定要做的事一定要说的话现在都可不理这是独处的妙处我且受用这无边的荷香月色好了
曲曲折折的荷塘上面弥望旳是田田的叶子叶子出水很高像亭亭旳舞女旳裙层层的叶子中间零星地点缀着些白花有袅娜(niǎo,nuó)地开着旳有羞涩地打着朵儿旳;正如一粒粒的明珠又如碧天里的星星又如刚出浴的美人微风过处送来缕缕清香仿佛远处高楼上渺茫的歌声似的这时候叶子与花也有一丝的颤动像闪电般霎时传过荷塘的那边去了叶子本是肩并肩密密地挨着这便宛然有了一道凝碧的波痕叶子底下是脉脉()的流水遮住了不能见一些颜色;而叶子却更见风致了
月光如流水一般静静地泻在这一片叶子和花上薄薄的青雾浮起在荷塘里叶子和花仿佛在牛乳中洗过一样;又像笼着轻纱的梦虽然是满月天上却有一层淡淡的云所以不能朗照;但我以为这恰是到了好处酣眠固不可少小睡也别有风味的月光是隔了树照过来的高处丛生的灌木落下参差的斑驳的黑影峭楞楞如鬼一般;弯弯的杨柳的稀疏的倩影却又像是画在荷叶上塘中的月色并不均匀;但光与影有着和谐的旋律如梵婀(ē)(英语violin小提琴的译音)上奏着的名曲
荷塘的四面远远近近高高低低都是树而杨柳最多这些树将一片荷塘重重围住;只在小路一旁漏着几段空隙像是特为月光留下的树色一例是阴阴的乍看像一团烟雾;但杨柳的丰姿便在烟雾里也辨得出树梢上隐隐约约的是一带远山只有些大意罢了树缝里也漏着一两点路灯光没精打采的是渴睡人的眼这时候最热闹的要数树上的蝉声与水里的蛙声;但热闹是它们的我什么也没有
2鲁迅从百草园到三味书屋片段
100篇名家经典散文摘抄
<!-- 这里填充章节内容后续可通过props或API获取 -->
</text>
</scroll-view>
<view class="bottom-bar" v-if="!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="showCatalog = true"><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>
<!-- 目录弹窗顶部196px边距底部无边距宽度100vw高度自适应顶部圆角底部无圆角 -->
<view v-if="showCatalog" class="catalog-popup-fullscreen" style="top:196px;bottom:0;height:auto;min-height:calc(100vh - 196px);">
<view class="catalog-header">
<view class="header-left" @click="showCatalog = false">
<uv-icon name="arrow-down" size="46" color="#333" @click="goBack" />
</view>
<view class="header-title">目录</view>
<view class="header-right" @click="reverseOrder">倒序</view>
</view>
<scroll-view scroll-y class="catalog-list" style="padding-bottom:0;margin-bottom:0;">
<view v-for="(item, idx) in displayList" :key="item.id" @click="selectChapter(idx)"
:class="['catalog-item', {active: idx === currentIndex}]">
<view class="item-main">
<text class="item-title">{{ item.title }}</text>
<text v-if="item.vip" class="vip-tag" @click.stop="showPayPopup = true">付费</text>
</view>
</view>
</scroll-view>
</view>
<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>
</view>
</template>
<script>
export default {
components: {
'uv-popup': () => import('@/uni_modules/uv-popup/components/uv-popup/uv-popup.vue')
},
data() {
return {
isFullScreen: false,
lastTap: 0,
showPopup: false,
popupShown: false, //
showCatalog: false,
orderAsc: true,
currentIndex: 0,
chapterList: [
{ id: 1, title: '第一章 重回2004', vip: false },
{ id: 2, title: '第二章 陈年旧恨', vip: false },
{ id: 3, title: '第三章 再相见', vip: false },
{ id: 4, title: '第四章 李东的邀请', vip: false },
{ id: 5, title: '第五章 小气的男', vip: false },
{ id: 6, title: '第六章 先送谁?', vip: false },
{ id: 7, title: '第七章 打听行情', vip: false },
{ id: 8, title: '第八章 省城探路', vip: false },
{ id: 9, title: '第九章 订货', vip: false },
{ id: 10, title: '第十章 第一桶金', vip: true },
{ id: 11, title: '第十一章 高富帅来袭', vip: true },
{ id: 12, title: '第十二章 故学后,挥场见!', vip: true },
{ id: 13, title: '第十三章 你来我往', vip: true },
{ id: 14, title: '第十四章 你来我往', vip: true },
{ id: 15, title: '第十五章 你来我往', vip: true },
{ id: 16, title: '第十六章 你来我往', vip: true },
{ id: 17, title: '第十七章 你来我往', vip: true },
{ id: 18, title: '第十八章 你来我往', vip: true },
],
showPayPopup: false
}
},
computed: {
displayList() {
return this.orderAsc ? this.chapterList : [...this.chapterList].reverse()
}
},
methods: {
handleContentClick() {
this.toggleFullScreen();
},
handleTouchEnd(e) {
this.toggleFullScreen();
},
handleScroll(e) {
//
const scrollTop = e.detail.scrollTop;
console.log('scrollTop:', scrollTop);
// 50
if (scrollTop > 50 && !this.popupShown) {
this.showPopup = true;
this.popupShown = true;
}
},
toggleFullScreen() {
this.isFullScreen = !this.isFullScreen
},
reverseOrder() {
this.orderAsc = !this.orderAsc
},
selectChapter(idx) {
this.currentIndex = this.orderAsc ? idx : this.chapterList.length - 1 - idx
this.showCatalog = false
// TODO:
},
goToSubscription() {
uni.navigateTo({
url: '/pages_order/novel/SubscriptionInformation'
})
}
},
mounted() {
// #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(options) {
// idid
// this.novelId = options.id
}
}
</script>
<style lang="scss" scoped>
.reader-container {
min-height: 100vh;
background: #fff;
display: flex;
flex-direction: column;
}
.chapter-title {
font-size: 32rpx;
font-weight: bold;
margin: 40rpx 32rpx 24rpx 32rpx;
}
.chapter-content {
flex: 1;
padding: 0 32rpx;
font-size: 28rpx;
color: #222;
line-height: 2.2;
margin-bottom: 120rpx;
}
.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;
}
.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;
}
.catalog-popup-fullscreen {
position: fixed;
left: 0;
top: 0;
width: 100vw;
height: 100vh;
background: #fff;
z-index: 9999;
border-top-left-radius: 32rpx;
border-top-right-radius: 32rpx;
border-bottom-left-radius: 0;
border-bottom-right-radius: 0;
display: flex;
flex-direction: column;
box-shadow: 0 8rpx 48rpx rgba(0,0,0,0.18);
overflow: hidden;
}
.catalog-header {
display: flex;
align-items: center;
justify-content: space-between;
padding: 0 24rpx;
height: 96rpx;
border-bottom: 1px solid #eee;
position: sticky;
top: 0;
background: #fff;
z-index: 2;
}
.header-left {
width: 60rpx;
display: flex;
align-items: center;
justify-content: flex-start;
}
.header-title {
flex: 1;
text-align: center;
font-size: 32rpx;
font-weight: bold;
color: #222;
}
.header-right {
color: #223a7a;
font-size: 28rpx;
font-weight: 500;
min-width: 80rpx;
text-align: right;
}
.catalog-list {
flex: 1;
overflow: auto;
padding-bottom: 40rpx;
}
.catalog-item {
padding: 0 32rpx;
min-height: 80rpx;
display: flex;
flex-direction: column;
justify-content: center;
border-bottom: 1px solid #f5f5f5;
background: #fff;
color: #222;
font-size: 30rpx;
position: relative;
}
.catalog-item.active {
color: #ff5a5f;
background: #fff7f7;
}
.item-main {
display: flex;
align-items: center;
gap: 16rpx;
}
.item-title {
font-size: 30rpx;
}
.vip-tag {
background: #ffe1b2;
color: #ff9900;
border-radius: 20rpx;
font-size: 24rpx;
padding: 2rpx 18rpx;
margin-left: 16rpx;
}
.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;
border-bottom-left-radius: 0;
border-bottom-right-radius: 0;
min-width: 500rpx;
width: 100%;
padding: 48rpx 32rpx 32rpx 32rpx;
text-align: center;
color: #fff;
}
.pay-title {
font-size: 32rpx;
font-weight: bold;
color: #fff;
margin-bottom: 24rpx;
margin-right: 50rpx;
}
.pay-desc {
font-size: 26rpx;
color: #999;
margin-bottom: 40rpx;
margin-right: 50rpx;
}
.pay-btns {
display: flex;
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;
}
.pay-btn-video {
background: #ff9800;
color: #fff;
border-radius: 32rpx;
font-size: 28rpx;
padding: 0 32rpx;
border: none;
}
.pay-btn-batch {
background: #ff9800;
color: #fff;
border-radius: 32rpx;
font-size: 28rpx;
padding: 0 32rpx;
border: none;
}
</style>

Loading…
Cancel
Save