- 新增作家相关接口和页面,包括保存或更新作家信息、获取作家信息等 - 新增书籍相关接口和页面,包括获取我的作品、添加或修改作品、增加或修改章节等 - 新增评论相关接口和页面,包括获取评论列表、删除评论、回复评论等 - 新增订单相关接口和页面,包括创建订单、支付订单、查询礼物详情等 - 优化部分页面样式和功能,修复已知问题master
| @ -1,44 +1,29 @@ | |||
| // 书架相关接口 | |||
| const api = { | |||
| // 批量移除我阅读过的数据根据书籍标识 | |||
| // 批量移除我书架过的数据根据书籍标识 | |||
| batchRemoveReadBook: { | |||
| url: '/all_book/batchRemoveReadBook', | |||
| method: 'GET', | |||
| auth: true, | |||
| }, | |||
| // 获取我的作品带分页 | |||
| getMyBookPage: { | |||
| url: '/all_book/getMyBookPage', | |||
| method: 'GET', | |||
| auth: true, | |||
| }, | |||
| // 获取我阅读过的书籍列表带分页 | |||
| // 获取我书架过的书籍列表带分页 | |||
| getReadBookPage: { | |||
| url: '/all_book/getReadBookPage', | |||
| method: 'GET', | |||
| auth: true, | |||
| }, | |||
| // 移除我阅读过的书籍根据书籍标识 | |||
| // 移除我书架过的书籍根据书籍标识 | |||
| removeReadBook: { | |||
| url: '/all_book/removeReadBook', | |||
| method: 'GET', | |||
| auth: true, | |||
| }, | |||
| // 添加作品或者修改作品 | |||
| saveOrUpdateBook: { | |||
| url: '/all_book/saveOrUpdateBook', | |||
| method: 'POST', | |||
| auth: true, | |||
| limit: 800, | |||
| showLoading: true, | |||
| }, | |||
| // 增加或修改作品章节 | |||
| saveOrUpdateCatalog: { | |||
| url: '/all_book/saveOrUpdateCatalog', | |||
| // 增加书架记录 | |||
| addReadBook: { | |||
| url: '/all_book/addReadBook', | |||
| method: 'POST', | |||
| auth: true, | |||
| limit: 800, | |||
| }, | |||
| } | |||
| @ -0,0 +1,46 @@ | |||
| const api = { | |||
| // 根据书本标识获取书本评论列表 | |||
| getBookCommentList: { | |||
| url: '/my_comment/getCommentList', | |||
| method: 'GET', | |||
| }, | |||
| // 删除评论信息 | |||
| deleteComment: { | |||
| url: '/my_comment/deleteComment', | |||
| method: 'GET', | |||
| }, | |||
| // 获取我的评论列表 | |||
| getMyCommentList: { | |||
| url: '/my_comment/getMyCommentList', | |||
| method: 'GET', | |||
| }, | |||
| // 获取我的评论数 | |||
| getMyCommentNum: { | |||
| url: '/my_comment/getMyCommentNum', | |||
| method: 'GET', | |||
| }, | |||
| // 回复评论信息 | |||
| replyComment: { | |||
| url: '/my_comment/replyComment', | |||
| method: 'POST', | |||
| }, | |||
| // 保存评论信息 | |||
| saveComment: { | |||
| url: '/my_comment/saveComment', | |||
| method: 'POST', | |||
| }, | |||
| // 更新评论已读状态 | |||
| updateCommentRead: { | |||
| url: '/my_comment/updateCommentRead', | |||
| method: 'POST', | |||
| }, | |||
| // 获取评论详情 | |||
| getCommentDetail: { | |||
| url: '/my_comment/getCommentDetail', | |||
| method: 'POST', | |||
| }, | |||
| } | |||
| export default api | |||
| @ -0,0 +1,33 @@ | |||
| // 书架相关接口 | |||
| const api = { | |||
| // 获取我的作品带分页 | |||
| getMyBookPage: { | |||
| url: '/my_book/getMyShopPage', | |||
| method: 'GET', | |||
| auth: true, | |||
| }, | |||
| // 添加作品或者修改作品 | |||
| saveOrUpdateBook: { | |||
| url: '/my_book/saveOrUpdateShop', | |||
| method: 'POST', | |||
| auth: true, | |||
| limit: 800, | |||
| showLoading: true, | |||
| }, | |||
| // 增加或修改作品章节 | |||
| saveOrUpdateCatalog: { | |||
| url: '/my_book/saveOrUpdateShopNovel', | |||
| method: 'POST', | |||
| auth: true, | |||
| limit: 800, | |||
| }, | |||
| // 获取我的小说章节列表带分页 | |||
| getMyShopNovelPage : { | |||
| url: '/my_book/getMyShopNovelPage', | |||
| method: 'POST', | |||
| auth: true, | |||
| }, | |||
| } | |||
| export default api | |||
| @ -0,0 +1,42 @@ | |||
| const api = { | |||
| // 创建订单 | |||
| createOrder : { | |||
| url: '/my_order/createOrder', | |||
| method: 'POST', | |||
| auth: true, | |||
| limit: 800, | |||
| }, | |||
| // 查询礼物详情 | |||
| getGiftDetail : { | |||
| url: '/my_order/getGiftDetail', | |||
| method: 'GET', | |||
| auth: true, | |||
| }, | |||
| // 查询互动打赏礼物信息列表 | |||
| getInteractionGiftList : { | |||
| url: '/my_order/getInteractionGiftList', | |||
| method: 'GET', | |||
| auth: true, | |||
| }, | |||
| // 查询我的礼物包订单列表 | |||
| getMyGiftList : { | |||
| url: '/my_order/getMyGiftList', | |||
| method: 'GET', | |||
| auth: true, | |||
| }, | |||
| // 支付订单 | |||
| payOrder : { | |||
| url: '/my_order/payOrder', | |||
| method: 'POST', | |||
| auth: true, | |||
| }, | |||
| // 支付成功 | |||
| paySuccess : { | |||
| url: '/my_order/paySuccess', | |||
| method: 'POST', | |||
| auth: true, | |||
| }, | |||
| } | |||
| export default api | |||
| @ -0,0 +1,52 @@ | |||
| const api = { | |||
| // 点击更多任务 | |||
| clickMoreTask: { | |||
| url: '/my_task/clickMoreTask', | |||
| method: 'POST', | |||
| auth: true, | |||
| limit : 1000, | |||
| showLoading: true, | |||
| }, | |||
| // 点击签到任务 | |||
| clickSignTask: { | |||
| url: '/my_task/clickSignTask', | |||
| method: 'GET', | |||
| auth: true, | |||
| limit : 1000, | |||
| showLoading: true, | |||
| }, | |||
| // 获取更多任务列表 | |||
| getMoreTaskList: { | |||
| url: '/my_task/getMoreTaskList', | |||
| method: 'GET', | |||
| auth: true, | |||
| }, | |||
| // 获取更多任务记录列表 | |||
| getMoreTaskRecordPage: { | |||
| url: '/my_task/getMoreTaskRecordPage', | |||
| method: 'GET', | |||
| auth: true, | |||
| }, | |||
| // 获取我的推荐票数 | |||
| getMyRecommendTicketNum: { | |||
| url: '/my_task/getMyRecommendTicketNum', | |||
| method: 'GET', | |||
| auth: true, | |||
| }, | |||
| // 获取我的推荐任务列表 | |||
| getSignTaskList: { | |||
| url: '/my_task/getSignTaskList', | |||
| method: 'GET', | |||
| auth: true, | |||
| }, | |||
| // 获取我的推荐任务记录列表 | |||
| getSignTaskRecordPage: { | |||
| url: '/my_task/getSignTaskRecordPage', | |||
| method: 'GET', | |||
| auth: true, | |||
| }, | |||
| } | |||
| export default api | |||
| @ -0,0 +1,18 @@ | |||
| const api = { | |||
| // 填写或修改笔名以及简介成为作家 | |||
| saveOrUpdateWriter : { | |||
| url: '/my_writer/saveOrUpdateWriter', | |||
| method: 'POST', | |||
| auth: true, | |||
| limit: 800, | |||
| }, | |||
| // 查询我的笔名以及简介 | |||
| getMyWriter : { | |||
| url: '/my_writer/getMyWriter', | |||
| method: 'GET', | |||
| auth: true, | |||
| }, | |||
| } | |||
| export default api | |||
| @ -1,102 +0,0 @@ | |||
| <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> | |||
| @ -0,0 +1,231 @@ | |||
| <template> | |||
| <view class="chapter-container"> | |||
| <navbar title="章节列表" leftClick @leftClick="$utils.navigateBack"/> | |||
| <view class="tabs"> | |||
| <uv-tabs :list="tabs" | |||
| :activeStyle="{color : '#0A2463', fontWeight : 600}" | |||
| lineColor="#0A2463" | |||
| :inactiveStyle="{color: '#0A2463'}" | |||
| lineHeight="8rpx" | |||
| lineWidth="50rpx" | |||
| :scrollable="false" | |||
| :current="activeTab" | |||
| @click="clickTabs"></uv-tabs> | |||
| </view> | |||
| <view class="box"> | |||
| <view class="chapter-list" > | |||
| <view class="draft-header"> | |||
| <text class="draft-title">{{ activeTab === 0 ? '草稿箱章节' : '已发布章节' }}</text> | |||
| <text class="delete-btn" @click="reverseList">{{ queryParams.reverse ? '正序' : '倒序' }}</text> | |||
| </view> | |||
| <view | |||
| class="chapter-item" | |||
| v-for="(chapter, index) in list" | |||
| :key="chapter.id" | |||
| @click="editChapter(chapter)"> | |||
| <view class="chapter-info"> | |||
| <text class="chapter-title">章节名</text> | |||
| <text class="chapter-number">{{ chapter.title }}</text> | |||
| </view> | |||
| <uv-icon name="arrow-right" color="#999" size="28"></uv-icon> | |||
| </view> | |||
| </view> | |||
| </view> | |||
| <view class="bottom-actions"> | |||
| <button class="btn-settings" @click="handleSettings">设置作品</button> | |||
| <button class="btn-new" @click="addNewChapter">新建章节</button> | |||
| </view> | |||
| </view> | |||
| </template> | |||
| <script> | |||
| import mixinsList from '@/mixins/list.js' | |||
| export default { | |||
| mixins: [mixinsList], | |||
| components: { | |||
| }, | |||
| data() { | |||
| return { | |||
| tabs : [ | |||
| { | |||
| name : '草稿箱', | |||
| index : 1 | |||
| }, | |||
| { | |||
| name : '已发布', | |||
| index : 0 | |||
| } | |||
| ], | |||
| activeTab: 0, | |||
| mixinsListApi : 'getMyShopNovelPage', | |||
| id : 0, | |||
| } | |||
| }, | |||
| onLoad(options) { | |||
| this.queryParams.reverse = 0 | |||
| // 根据tab参数决定初始tab | |||
| if (options.activeTab) { | |||
| this.activeTab = options.activeTab; | |||
| } | |||
| if(options.id){ | |||
| this.queryParams.bookId = options.id | |||
| this.id = options.id | |||
| this.queryParams.status = this.activeTab | |||
| } | |||
| }, | |||
| methods: { | |||
| clickTabs(tab) { | |||
| this.activeTab = tab.index; | |||
| this.queryParams.status = this.activeTab | |||
| this.list = [] | |||
| this.getData() | |||
| }, | |||
| reverseList() { | |||
| this.queryParams.reverse = [1, 0][this.queryParams.reverse] | |||
| this.getData() | |||
| }, | |||
| addNewChapter() { | |||
| uni.navigateTo({ | |||
| url: '/pages_order/author/editor?id=' + this.id | |||
| }) | |||
| }, | |||
| editChapter(chapter) { | |||
| uni.navigateTo({ | |||
| url: '/pages_order/author/editor?cid=' + chapter.id + '&id=' + this.id | |||
| }) | |||
| }, | |||
| handleSettings() { | |||
| uni.navigateTo({ | |||
| url: '/pages_order/novel/createNovel?id=' + this.id | |||
| }) | |||
| }, | |||
| deleteAll() { | |||
| uni.showModal({ | |||
| title: '提示', | |||
| content: '确定要删除所有草稿章节吗?', | |||
| success: (res) => { | |||
| if (res.confirm) { | |||
| // 调用删除草稿API | |||
| this.$api.deleteAllDrafts({bookId: this.id}).then(res => { | |||
| if (res.code === 200) { | |||
| this.$utils.showToast('删除成功') | |||
| this.getData() | |||
| } | |||
| }) | |||
| } | |||
| } | |||
| }) | |||
| } | |||
| } | |||
| } | |||
| </script> | |||
| <style lang="scss" scoped> | |||
| .chapter-container { | |||
| min-height: 100vh; | |||
| background-color: #f7f7f7; | |||
| padding-bottom: 70px; | |||
| .tabs{ | |||
| background-color: #fff; | |||
| } | |||
| .box{ | |||
| padding: 20rpx 40rpx; | |||
| background-color: #fff; | |||
| margin: 50rpx 40rpx; | |||
| border-radius: 20rpx; | |||
| .draft-header { | |||
| display: flex; | |||
| justify-content: space-between; | |||
| align-items: center; | |||
| padding-bottom: 20rpx; | |||
| border-bottom: 1px dashed #eee; | |||
| margin-bottom: 10rpx; | |||
| .draft-title { | |||
| font-size: 30rpx; | |||
| color: #000; | |||
| font-weight: 900; | |||
| } | |||
| .delete-btn { | |||
| font-size: 28rpx; | |||
| color: #999; | |||
| } | |||
| } | |||
| .chapter-list { | |||
| .chapter-item { | |||
| display: flex; | |||
| justify-content: space-between; | |||
| align-items: center; | |||
| padding: 15px 0; | |||
| border-bottom: 1px solid #eee; | |||
| .chapter-info { | |||
| .chapter-title { | |||
| font-size: 14px; | |||
| color: #999; | |||
| margin-bottom: 5px; | |||
| display: block; | |||
| } | |||
| .chapter-number { | |||
| font-size: 16px; | |||
| color: #333; | |||
| display: block; | |||
| } | |||
| } | |||
| .icon-arrow { | |||
| color: #999; | |||
| font-size: 16px; | |||
| } | |||
| } | |||
| } | |||
| } | |||
| .bottom-actions { | |||
| padding-bottom: calc(env(safe-area-inset-bottom) + 30rpx); | |||
| position: fixed; | |||
| bottom: 0; | |||
| left: 0; | |||
| right: 0; | |||
| display: flex; | |||
| padding: 10px 15px; | |||
| background: #fff; | |||
| box-shadow: 0 -2px 6px rgba(0, 0, 0, 0.05); | |||
| button { | |||
| flex: 1; | |||
| height: 40px; | |||
| border-radius: 20px; | |||
| font-size: 16px; | |||
| margin: 0 5px; | |||
| &.btn-settings { | |||
| background: #fff; | |||
| border: 1px solid #ddd; | |||
| color: #333; | |||
| } | |||
| &.btn-new { | |||
| background: #2b4acb; | |||
| color: #fff; | |||
| border: none; | |||
| } | |||
| } | |||
| } | |||
| } | |||
| </style> | |||
| @ -0,0 +1,246 @@ | |||
| <template> | |||
| <view class="comments-page"> | |||
| <navbar title="评论详情" leftClick @leftClick="$utils.navigateBack" /> | |||
| <!-- 书本名称 --> | |||
| <view class="book-title-area"> | |||
| <view class="book-title-label">书本名称</view> | |||
| <view class="book-title">《{{ bookTitle }}》</view> | |||
| </view> | |||
| <!-- 主评论卡片 --> | |||
| <view class="comment-card" v-if="comment.hanHaiMember"> | |||
| <view class="comment-header"> | |||
| <image class="avatar" :src="comment.hanHaiMember.headImage" mode="aspectFill" /> | |||
| <view class="user-info"> | |||
| <text class="username">{{ comment.hanHaiMember.nickName }}</text> | |||
| </view> | |||
| </view> | |||
| <view class="comment-content">{{ comment.comment }}</view> | |||
| <view class="comment-footer"> | |||
| <text class="comment-time">{{ comment.createTime }}</text> | |||
| </view> | |||
| </view> | |||
| <!-- 全部评论列表 --> | |||
| <view class="all-reply-area"> | |||
| <view class="all-reply-header">回复 · {{ comment.children.length }}</view> | |||
| <view class="reply-list"> | |||
| <view class="reply-item" v-for="(item, idx) in comment.children" :key="idx"> | |||
| <image class="reply-avatar" :src="item.hanHaiMember.headImage" mode="aspectFill" /> | |||
| <view class="reply-main"> | |||
| <view class="reply-username">{{ item.hanHaiMember.nickName }}</view> | |||
| <view class="reply-content">{{ item.comment }}</view> | |||
| <view class="reply-time">{{ item.createTime }}</view> | |||
| </view> | |||
| </view> | |||
| <uv-empty mode="list" v-if="comment.children.length == 0"></uv-empty> | |||
| </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: {}, | |||
| id : 0, | |||
| } | |||
| }, | |||
| onLoad({id}) { | |||
| this.id = id | |||
| }, | |||
| onShow() { | |||
| this.getData() | |||
| }, | |||
| methods: { | |||
| async getData(){ | |||
| this.comment = await this.$fetch('getCommentDetail', { | |||
| commentId : this.id, | |||
| }) | |||
| this.bookTitle = this.comment.commonShop.name | |||
| }, | |||
| goToRespond() { | |||
| uni.navigateTo({ | |||
| url: '/pages_order/comment/respondComments?id=' + this.id | |||
| }) | |||
| }, | |||
| } | |||
| } | |||
| </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: 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; | |||
| padding-bottom: 20rpx; | |||
| } | |||
| .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> | |||
| @ -0,0 +1,255 @@ | |||
| <template> | |||
| <view class="respond-comments-page"> | |||
| <!-- 顶部导航栏 --> | |||
| <navbar title="回复评论" leftClick @leftClick="$utils.navigateBack" /> | |||
| <!-- 原评论展示 --> | |||
| <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"> | |||
| <text class="emoji-icon">💬</text> | |||
| {{ comment.replyCount }} | |||
| </text> | |||
| </view> --> | |||
| <commentItem :item="comment" noClick/> | |||
| </view> | |||
| <!-- 回复输入区 --> | |||
| <view class="reply-area"> | |||
| <view class="form-label-row"> | |||
| <text class="required-star">*</text> | |||
| <text class="form-label">回复内容</text> | |||
| </view> | |||
| <textarea v-model="replyContent" class="review-textarea custom-placeholder full-textarea" | |||
| placeholder="请输入书评内容" /> | |||
| </view> | |||
| <!-- 底部提交按钮 --> | |||
| <view class="reply-footer"> | |||
| <button class="submit-btn" :disabled="!replyContent.trim()" @click="submitReview">发送</button> | |||
| </view> | |||
| </view> | |||
| </template> | |||
| <script> | |||
| import commentItem from '../components/comment/commentItem.vue' | |||
| export default { | |||
| components: { | |||
| commentItem, | |||
| }, | |||
| data() { | |||
| return { | |||
| comment: {}, | |||
| replyContent: '', | |||
| id : 0, | |||
| } | |||
| }, | |||
| onLoad({id}) { | |||
| this.id = id | |||
| this.getData() | |||
| }, | |||
| methods: { | |||
| async getData(){ | |||
| this.comment = await this.$fetch('getCommentDetail', { | |||
| commentId : this.id, | |||
| }) | |||
| }, | |||
| async submitReview() { | |||
| if (!this.replyContent.trim()) { | |||
| uni.showToast({ | |||
| title: '请输入回复内容', | |||
| icon: 'none' | |||
| }) | |||
| return | |||
| } | |||
| await this.$fetch('replyComment', { | |||
| commentId : this.id, | |||
| content : this.replyContent, | |||
| }) | |||
| uni.showToast({ | |||
| title: '回复成功', | |||
| icon: 'none' | |||
| }) | |||
| 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; | |||
| line-height: 1; | |||
| } | |||
| .emoji-icon { | |||
| margin-right: 4rpx; | |||
| font-size: 22rpx; | |||
| line-height: 1; | |||
| display: inline-block; | |||
| vertical-align: middle; | |||
| } | |||
| .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; | |||
| .review-textarea { | |||
| width: 100%; | |||
| min-height: 320rpx; | |||
| border: none; | |||
| background: transparent; | |||
| font-size: 28rpx; | |||
| color: #333; | |||
| resize: none; | |||
| outline: none; | |||
| margin-top: 20rpx; | |||
| } | |||
| .review-textarea.custom-placeholder::placeholder { | |||
| color: #d2d2d2; | |||
| font-size: 26rpx; | |||
| } | |||
| } | |||
| .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> | |||
| @ -0,0 +1,197 @@ | |||
| <template> | |||
| <view class="review-page"> | |||
| <!-- 顶部导航栏 --> | |||
| <navbar title="写书评" leftClick @leftClick="$utils.navigateBack" /> | |||
| <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: '' | |||
| }, | |||
| id: 0 | |||
| } | |||
| }, | |||
| onLoad(options) { | |||
| // 通过路由参数获取书名 | |||
| this.bookTitle = options.title || '未知书名' | |||
| this.id = options.id || 0 | |||
| this.getDateil() | |||
| }, | |||
| methods: { | |||
| getDateil(){ | |||
| this.$fetch('getBookDetail', { | |||
| id : this.id | |||
| }).then(res => { | |||
| this.bookTitle = res.name | |||
| }) | |||
| }, | |||
| async submitReview() { | |||
| if(this.$utils.verificationAll(this.form, { | |||
| content : '请输入书评内容' | |||
| })){ | |||
| return | |||
| } | |||
| await this.$fetch('saveComment', { | |||
| bookId : this.id, | |||
| content : this.form.content, | |||
| }) | |||
| uni.showToast({ | |||
| title: '发布成功', | |||
| icon: 'none' | |||
| }) | |||
| setTimeout(() => { | |||
| uni.navigateBack() | |||
| }, 1000) | |||
| } | |||
| } | |||
| } | |||
| </script> | |||
| <style scoped lang="scss"> | |||
| .review-page { | |||
| min-height: 100vh; | |||
| background: #f7f7f7; | |||
| display: flex; | |||
| flex-direction: column; | |||
| } | |||
| .review-content { | |||
| background: #fff; | |||
| margin: 0 24rpx; | |||
| margin-top: 24rpx; | |||
| border-radius: 16rpx; | |||
| padding: 32rpx 24rpx 24rpx 24rpx; | |||
| display: flex; | |||
| flex-direction: column; | |||
| flex: 1; | |||
| padding-bottom: 140rpx; | |||
| .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> | |||
| @ -1,141 +0,0 @@ | |||
| # 小说阅读相关组件 | |||
| 本目录包含小说阅读页面使用的各种公共组件,所有组件都支持暗色主题模式。 | |||
| ## 组件列表 | |||
| ### 1. 订阅弹窗 (subscriptionPopup.vue) | |||
| 中心弹出的订阅章节提示弹窗。 | |||
| #### 使用方法 | |||
| ```html | |||
| <template> | |||
| <subscriptionPopup ref="subscriptionPopup" @subscribe="handleSubscribe"/> | |||
| </template> | |||
| <script> | |||
| import subscriptionPopup from 'pages_order/components/novel/subscriptionPopup.vue' | |||
| export default { | |||
| components: { | |||
| subscriptionPopup | |||
| }, | |||
| methods: { | |||
| showSubscriptionPopup() { | |||
| this.$refs.subscriptionPopup.open() | |||
| }, | |||
| handleSubscribe() { | |||
| // 处理订阅事件 | |||
| } | |||
| } | |||
| } | |||
| </script> | |||
| ``` | |||
| ### 2. 支付弹窗 (paymentPopup.vue) | |||
| 从底部弹出的支付方式选择弹窗。 | |||
| #### 使用方法 | |||
| ```html | |||
| <template> | |||
| <paymentPopup ref="paymentPopup" @subscribe="handleSubscribe" @close="handleClose"/> | |||
| </template> | |||
| <script> | |||
| import paymentPopup from 'pages_order/components/novel/paymentPopup.vue' | |||
| export default { | |||
| components: { | |||
| paymentPopup | |||
| }, | |||
| methods: { | |||
| showPaymentPopup() { | |||
| this.$refs.paymentPopup.open() | |||
| }, | |||
| handleSubscribe() { | |||
| // 处理订阅事件 | |||
| }, | |||
| handleClose() { | |||
| // 处理关闭事件 | |||
| } | |||
| } | |||
| } | |||
| </script> | |||
| ``` | |||
| ### 3. 章节目录弹窗 (chapterPopup.vue) | |||
| 显示小说章节列表的弹窗。 | |||
| #### 使用方法 | |||
| ```html | |||
| <template> | |||
| <chapterPopup ref="chapterPopup"/> | |||
| </template> | |||
| <script> | |||
| import chapterPopup from 'pages_order/components/novel/chapterPopup.vue' | |||
| export default { | |||
| components: { | |||
| chapterPopup | |||
| }, | |||
| methods: { | |||
| showChapterList() { | |||
| this.$refs.chapterPopup.open() | |||
| } | |||
| } | |||
| } | |||
| </script> | |||
| ``` | |||
| ### 4. 评分弹窗 (novelVotePopup.vue) | |||
| 用于评价小说的弹窗组件。 | |||
| ## 暗色主题支持 | |||
| 所有组件都支持暗色主题模式,当系统切换到暗色模式或用户手动切换时,组件会自动应用暗色样式。 | |||
| ### 主题切换 | |||
| 主题的状态保存在Vuex中,可以通过以下方式切换: | |||
| ```js | |||
| // 切换主题模式 | |||
| this.$store.commit('toggleThemeMode') | |||
| // 设置为特定模式 | |||
| this.$store.commit('setThemeMode', 'dark') // 或 'light' | |||
| // 获取当前主题模式 | |||
| const isDarkMode = this.$store.getters.isDarkMode | |||
| ``` | |||
| ### 使用主题混入器 | |||
| 为了方便在组件中使用主题相关功能,可以引入主题混入器: | |||
| ```js | |||
| import themeMixin from '@/mixins/themeMode.js' | |||
| export default { | |||
| mixins: [themeMixin], | |||
| // 然后可以直接使用以下属性和方法 | |||
| created() { | |||
| console.log(this.isDarkMode) // 是否为暗色模式 | |||
| console.log(this.currentTheme) // 'light' 或 'dark' | |||
| // 切换主题 | |||
| this.toggleThemeMode() | |||
| } | |||
| } | |||
| ``` | |||
| 详细的文档请参考 `doc/dark-mode-guide.md`。 | |||
| @ -0,0 +1,83 @@ | |||
| <template> | |||
| <view class="rank-item"> | |||
| <view class="rank-left"> | |||
| <image src="@/pages_order/static/top/4.png" class="rank-icon" /> | |||
| <image src="@/pages_order/static/book/dj.png" class="rank-num-img" /> | |||
| <image class="avatar" src="@/pages_order/static/book/bd.png" mode="aspectFill" /> | |||
| <view class="name">发源于</view> | |||
| </view> | |||
| <view class="rank-right"> | |||
| <view class="score">200亲密值</view> | |||
| <view class="level">护书使者 五级</view> | |||
| </view> | |||
| </view> | |||
| </template> | |||
| <script> | |||
| export default { | |||
| name: 'RankListItem', | |||
| props: { | |||
| } | |||
| } | |||
| </script> | |||
| <style lang="scss" scoped> | |||
| .rank-item { | |||
| display: flex; | |||
| align-items: center; | |||
| justify-content: space-between; | |||
| background: linear-gradient(to bottom, #FBEBC5, #FDF9DD); | |||
| border-radius: 16rpx; | |||
| margin-bottom: 18rpx; | |||
| box-shadow: 0 2rpx 8rpx 0 rgba(184, 110, 59, 0.06); | |||
| padding: 24rpx; | |||
| .rank-left { | |||
| display: flex; | |||
| align-items: center; | |||
| .rank-icon, | |||
| .rank-num-img { | |||
| width: 60rpx; | |||
| height: 60rpx; | |||
| margin-right: 10rpx; | |||
| } | |||
| .avatar { | |||
| width: 60rpx; | |||
| height: 60rpx; | |||
| border-radius: 50%; | |||
| margin-right: 14rpx; | |||
| object-fit: cover; | |||
| } | |||
| .name { | |||
| font-size: 26rpx; | |||
| color: #222; | |||
| font-weight: 500; | |||
| } | |||
| } | |||
| .rank-right { | |||
| display: flex; | |||
| flex-direction: column; | |||
| align-items: flex-end; | |||
| gap: 10rpx; | |||
| .score { | |||
| font-size: 22rpx; | |||
| color: #684427; | |||
| } | |||
| .level { | |||
| font-size: 20rpx; | |||
| color: #C28E00; | |||
| background: #FFE8A8; | |||
| border-radius: 8rpx; | |||
| padding: 2rpx 10rpx; | |||
| margin-top: 6rpx; | |||
| font-weight: 500; | |||
| } | |||
| } | |||
| } | |||
| </style> | |||
| @ -1,212 +0,0 @@ | |||
| <template> | |||
| <view class="respond-comments-page"> | |||
| <!-- 顶部导航栏 --> | |||
| <navbar title="回复评论" leftClick @leftClick="$utils.navigateBack" /> | |||
| <!-- 原评论展示 --> | |||
| <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"> | |||
| <text class="emoji-icon">💬</text> | |||
| {{ 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; | |||
| line-height: 1; | |||
| } | |||
| .emoji-icon { | |||
| margin-right: 4rpx; | |||
| font-size: 22rpx; | |||
| line-height: 1; | |||
| display: inline-block; | |||
| vertical-align: middle; | |||
| } | |||
| .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> | |||
| @ -1,189 +0,0 @@ | |||
| <template> | |||
| <view class="review-page"> | |||
| <!-- 顶部导航栏 --> | |||
| <navbar title="写书评" leftClick @leftClick="$utils.navigateBack" /> | |||
| <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> | |||
| @ -1,279 +1,242 @@ | |||
| <template> | |||
| <view class="tipping-page"> | |||
| <!-- 顶部导航栏 --> | |||
| <navbar title="读者亲密值榜单" leftClick @leftClick="$utils.navigateBack" /> | |||
| <!-- 榜单前三名 --> | |||
| <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> | |||
| <view class="tipping-page"> | |||
| <!-- 顶部导航栏 --> | |||
| <navbar title="读者亲密值榜单" bgColor="#ac4b2c" color="#fff" leftClick @leftClick="$utils.navigateBack" /> | |||
| <!-- 榜单前三名 --> | |||
| <view class="top-three"> | |||
| <view class="top-item second" v-if="topList[1]"> | |||
| <view class="avatar-frame"> | |||
| <image class="frame-img" src="/pages_order/static/top/top1.png" mode="aspectFit"></image> | |||
| <image class="avatar" :src="topList[1].avatar" mode="aspectFill"></image> | |||
| </view> | |||
| <view class="badge"> | |||
| <image class="badge-img" src="/pages_order/static/book/dj.png" mode="aspectFit"></image> | |||
| </view> | |||
| <view class="name">{{ topList[1].name }}</view> | |||
| <view class="score">{{ topList[1].num }} 亲密值</view> | |||
| <view class="level">护书使者 四级</view> | |||
| </view> | |||
| <view class="top-item first" v-if="topList[0]"> | |||
| <view class="avatar-frame"> | |||
| <image class="frame-img" src="/pages_order/static/top/top1.png" mode="aspectFit"></image> | |||
| <image class="avatar" :src="topList[0].avatar" mode="aspectFill"></image> | |||
| </view> | |||
| <view class="badge"> | |||
| <image class="badge-img" :src="topList[0].icon" mode="aspectFit"></image> | |||
| </view> | |||
| <view class="name">{{ topList[0].name }}</view> | |||
| <view class="score">{{ topList[0].num }} 亲密值</view> | |||
| <view class="level">护书使者 五级</view> | |||
| </view> | |||
| <view class="top-item third" v-if="topList[2]"> | |||
| <view class="avatar-frame"> | |||
| <image class="frame-img" src="/pages_order/static/top/top1.png" mode="aspectFit"></image> | |||
| <image class="avatar" :src="topList[2].avatar" mode="aspectFill"></image> | |||
| </view> | |||
| <view class="badge"> | |||
| <image class="badge-img" src="/pages_order/static/book/dj.png" mode="aspectFit"></image> | |||
| </view> | |||
| <view class="name">{{ topList[2].name }}</view> | |||
| <view class="score">{{ topList[2].num }} 亲密值</view> | |||
| <view class="level">护书使者 三级</view> | |||
| </view> | |||
| </view> | |||
| <!-- 榜单列表 --> | |||
| <view class="rank-list" | |||
| v-if="topList.length > 3"> | |||
| <RankListItem v-for="(item, idx) in topList" | |||
| v-if="idx > 2" | |||
| :key="item.id" /> | |||
| </view> | |||
| <!-- 底部按钮 --> | |||
| <view class="bottom-btn-area"> | |||
| <button class="tipping-btn" | |||
| @click="$utils.navigateTo('pages_order/novel/Giftbox?id=' + bookId)" | |||
| >互动打赏</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' | |||
| ] | |||
| } | |||
| } | |||
| } | |||
| import RankListItem from '../components/novel/RankListItem.vue' | |||
| export default { | |||
| components: { | |||
| RankListItem | |||
| }, | |||
| data() { | |||
| return { | |||
| topList: [], | |||
| rankList: [], | |||
| bookId: 0, | |||
| } | |||
| }, | |||
| onLoad(options) { | |||
| this.bookId = options.id; | |||
| }, | |||
| onShow() { | |||
| this.getTopList(); | |||
| }, | |||
| methods: { | |||
| getTopList() { | |||
| this.$fetch('getIntimacyRankList', { | |||
| bookId: this.bookId, | |||
| pageNo: 1, | |||
| pageSize: 10 | |||
| }).then(res => { | |||
| this.topList = res.records; | |||
| }); | |||
| } | |||
| } | |||
| } | |||
| </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; | |||
| } | |||
| } | |||
| .tipping-page { | |||
| min-height: 100vh; | |||
| // background: linear-gradient(180deg, #b86e3b 0%, #e6b07c 100%); | |||
| background-color: #ac4b2c; | |||
| padding-bottom: 40rpx; | |||
| } | |||
| .top-three { | |||
| display: flex; | |||
| justify-content: center; | |||
| align-items: flex-end; | |||
| margin: 80rpx 0 200rpx 0; | |||
| padding: 0 30rpx; | |||
| gap: 20rpx; | |||
| .top-item { | |||
| display: flex; | |||
| flex-direction: column; | |||
| align-items: center; | |||
| position: relative; | |||
| border-top-left-radius: 110rpx; | |||
| border-top-right-radius: 110rpx; | |||
| gap: 10rpx; | |||
| padding-bottom: 20rpx; | |||
| &.first { | |||
| z-index: 3; | |||
| margin-top: -50rpx; | |||
| background: linear-gradient(to bottom, #F8DA87, #F8DA8700); | |||
| border: 1px solid #fff; | |||
| } | |||
| &.second { | |||
| z-index: 2; | |||
| background: linear-gradient(to bottom, #C2C9CD, #C2C9CD00); | |||
| border: 1px solid #fff; | |||
| top: 80rpx; | |||
| } | |||
| &.third { | |||
| z-index: 2; | |||
| background: linear-gradient(to bottom, #834941, #83494100); | |||
| border: 1px solid #fff; | |||
| top: 80rpx; | |||
| } | |||
| .avatar-frame { | |||
| position: relative; | |||
| width: 220rpx; | |||
| height: 220rpx; | |||
| margin-top: -50rpx; | |||
| .frame-img { | |||
| position: absolute; | |||
| width: 120%; | |||
| height: 120%; | |||
| z-index: 2; | |||
| top: 50%; | |||
| left: 50%; | |||
| transform: translate(-50%, -50%); | |||
| } | |||
| .avatar { | |||
| position: absolute; | |||
| top: 50%; | |||
| left: 50%; | |||
| transform: translate(-50%, -50%); | |||
| width: 80%; | |||
| height: 80%; | |||
| border-radius: 50%; | |||
| z-index: 1; | |||
| } | |||
| } | |||
| .badge { | |||
| width: 100rpx; | |||
| height: 100rpx; | |||
| z-index: 3; | |||
| .badge-img { | |||
| width: 100%; | |||
| height: 100%; | |||
| } | |||
| } | |||
| .name { | |||
| margin-top: 10rpx; | |||
| font-size: 36rpx; | |||
| color: #FFFFFF; | |||
| font-weight: bold; | |||
| } | |||
| .score { | |||
| font-size: 28rpx; | |||
| color: #FFFFFF; | |||
| margin-top: 6rpx; | |||
| } | |||
| .level { | |||
| position: absolute; | |||
| bottom: -40rpx; | |||
| width: 102%; | |||
| text-align: center; | |||
| padding: 6rpx 20rpx; | |||
| background-color: #FFCC33; | |||
| border-radius: 6rpx; | |||
| font-size: 26rpx; | |||
| color: #8B4513; | |||
| font-weight: bold; | |||
| box-sizing: border-box; | |||
| } | |||
| } | |||
| } | |||
| .rank-list { | |||
| background: #FAE5BE; | |||
| margin: 0 24rpx; | |||
| padding: 20rpx; | |||
| border-radius: 20rpx; | |||
| display: flex; | |||
| flex-direction: column; | |||
| gap: 20rpx; | |||
| } | |||
| .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> | |||
| @ -1,166 +0,0 @@ | |||
| <template> | |||
| <view class="chapter-container"> | |||
| <navbar title="章节列表" leftClick @leftClick="$utils.navigateBack"/> | |||
| <view class="tabs"> | |||
| <uv-tabs :list="tabs" | |||
| :activeStyle="{color : '#0A2463', fontWeight : 600}" | |||
| lineColor="#0A2463" | |||
| :inactiveStyle="{color: '#0A2463'}" | |||
| lineHeight="8rpx" | |||
| lineWidth="50rpx" | |||
| :scrollable="false" | |||
| :current="activeTab" | |||
| @click="clickTabs"></uv-tabs> | |||
| </view> | |||
| <view class="chapter-list"> | |||
| <view class="chapter-item" v-for="(chapter, index) in chapters" :key="index" @click="editChapter(chapter)"> | |||
| <view class="chapter-info"> | |||
| <text class="chapter-title">章节名</text> | |||
| <text class="chapter-number">第{{index + 1}}章</text> | |||
| </view> | |||
| <uv-icon name="arrow-right" color="#999" size="28"></uv-icon> | |||
| </view> | |||
| </view> | |||
| <view class="bottom-actions"> | |||
| <button class="btn-settings" @click="handleSettings">设置作品</button> | |||
| <button class="btn-new" @click="addNewChapter">新建章节</button> | |||
| </view> | |||
| </view> | |||
| </template> | |||
| <script> | |||
| import mixinsList from '@/mixins/list.js' | |||
| export default { | |||
| mixins: [mixinsList], | |||
| components: { | |||
| }, | |||
| data() { | |||
| return { | |||
| tabs : [ | |||
| { | |||
| name : '已发布', | |||
| }, | |||
| { | |||
| name : '草稿箱', | |||
| } | |||
| ], | |||
| activeTab: 0, | |||
| mixinsListApi : 'getBookCatalogList', | |||
| id : 0, | |||
| } | |||
| }, | |||
| onLoad(options) { | |||
| // 根据tab参数决定初始tab | |||
| if (options.activeTab) { | |||
| this.activeTab = options.activeTab; | |||
| } | |||
| if(options.id){ | |||
| this.queryParams.bookId = options.id | |||
| this.id = options.id | |||
| this.queryParams.status = this.activeTab | |||
| } | |||
| }, | |||
| methods: { | |||
| clickTabs(tab) { | |||
| this.activeTab = tab.index; | |||
| this.queryParams.status = this.activeTab | |||
| this.getData() | |||
| }, | |||
| addNewChapter() { | |||
| uni.navigateTo({ | |||
| url: '/pages_order/author/editor?id=' + this.id | |||
| }) | |||
| }, | |||
| editChapter(chapter) { | |||
| uni.navigateTo({ | |||
| url: '/pages_order/author/editor?cid=' + chapter.id + '&id=' + this.id | |||
| }) | |||
| }, | |||
| handleSettings() { | |||
| uni.navigateTo({ | |||
| url: '/pages_order/novel/createNovel?type=edit' | |||
| }) | |||
| } | |||
| } | |||
| } | |||
| </script> | |||
| <style lang="scss" scoped> | |||
| .chapter-container { | |||
| min-height: 100vh; | |||
| background-color: #fff; | |||
| padding-bottom: 70px; | |||
| .chapter-list { | |||
| padding: 20rpx 40rpx; | |||
| background-color: rgb(255, 254, 254); | |||
| margin: 50rpx 40rpx; | |||
| border-radius: 3%; | |||
| .chapter-item { | |||
| display: flex; | |||
| justify-content: space-between; | |||
| align-items: center; | |||
| padding: 15px 0; | |||
| border-bottom: 1px solid #eee; | |||
| .chapter-info { | |||
| .chapter-title { | |||
| font-size: 14px; | |||
| color: #999; | |||
| margin-bottom: 5px; | |||
| display: block; | |||
| } | |||
| .chapter-number { | |||
| font-size: 16px; | |||
| color: #333; | |||
| display: block; | |||
| } | |||
| } | |||
| .icon-arrow { | |||
| color: #999; | |||
| font-size: 16px; | |||
| } | |||
| } | |||
| } | |||
| .bottom-actions { | |||
| padding-bottom: calc(env(safe-area-inset-bottom) + 30rpx); | |||
| position: fixed; | |||
| bottom: 0; | |||
| left: 0; | |||
| right: 0; | |||
| display: flex; | |||
| padding: 10px 15px; | |||
| background: #fff; | |||
| box-shadow: 0 -2px 6px rgba(0, 0, 0, 0.05); | |||
| button { | |||
| flex: 1; | |||
| height: 40px; | |||
| border-radius: 20px; | |||
| font-size: 16px; | |||
| margin: 0 5px; | |||
| &.btn-settings { | |||
| background: #fff; | |||
| border: 1px solid #ddd; | |||
| color: #333; | |||
| } | |||
| &.btn-new { | |||
| background: #2b4acb; | |||
| color: #fff; | |||
| border: none; | |||
| } | |||
| } | |||
| } | |||
| } | |||
| </style> | |||
| @ -1,250 +0,0 @@ | |||
| <template> | |||
| <view class="comments-page"> | |||
| <navbar title="评论详情" leftClick @leftClick="$utils.navigateBack" /> | |||
| <!-- 书本名称 --> | |||
| <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: { | |||
| 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> | |||
| @ -1,39 +0,0 @@ | |||
| <template> | |||
| <div class="agreement-check"> | |||
| <input type="checkbox" v-model="checked" @change="$emit('update:checked', checked)" id="agreement" /> | |||
| <label for="agreement"> | |||
| <slot /> | |||
| </label> | |||
| </div> | |||
| </template> | |||
| <script> | |||
| export default { | |||
| name: 'AgreementCheck', | |||
| props: { | |||
| checked: { | |||
| type: Boolean, | |||
| default: false | |||
| } | |||
| } | |||
| } | |||
| </script> | |||
| <style scoped> | |||
| .agreement-check { | |||
| display: flex; | |||
| align-items: center; | |||
| font-size: 14px; | |||
| color: #b3b3b3; | |||
| margin-top: 16px; | |||
| } | |||
| .agreement-check input[type='checkbox'] { | |||
| margin-right: 6px; | |||
| accent-color: #183b6b; | |||
| } | |||
| .agreement-check a { | |||
| color: #183b6b; | |||
| text-decoration: underline; | |||
| margin: 0 2px; | |||
| } | |||
| </style> | |||
| @ -1,28 +0,0 @@ | |||
| <template> | |||
| <uv-icon name="arrow-left" :size="44" :color="color" @click="goBack" /> | |||
| </template> | |||
| <script> | |||
| export default { | |||
| name: 'BackArrow', | |||
| props: { | |||
| size: { | |||
| type: [String, Number], | |||
| default: 56 | |||
| }, | |||
| color: { | |||
| type: String, | |||
| default: '#333' | |||
| } | |||
| }, | |||
| methods: { | |||
| goBack() { | |||
| this.$emit('back'); | |||
| if (this.$listeners.back) return; | |||
| if (typeof uni !== 'undefined' && uni.navigateBack) { | |||
| uni.navigateBack(); | |||
| } | |||
| } | |||
| } | |||
| } | |||
| </script> | |||
| @ -1,45 +0,0 @@ | |||
| <template> | |||
| <button :class="['login-btn', type]" @click="$emit('click')"> | |||
| <slot /> | |||
| </button> | |||
| </template> | |||
| <script> | |||
| export default { | |||
| name: 'LoginButton', | |||
| props: { | |||
| type: { | |||
| type: String, | |||
| default: '' | |||
| } | |||
| } | |||
| } | |||
| </script> | |||
| <style scoped> | |||
| .login-btn { | |||
| width: 80%; | |||
| height: 44px; | |||
| border-radius: 22px; | |||
| font-size: 18px; | |||
| font-weight: 500; | |||
| margin: 12px 0; | |||
| outline: none; | |||
| border: none; | |||
| cursor: pointer; | |||
| display: flex; | |||
| align-items: center; | |||
| justify-content: center; | |||
| transition: background 0.2s; | |||
| } | |||
| .login-btn.primary { | |||
| background: #183b6b; | |||
| color: #fff; | |||
| } | |||
| .login-btn.secondary { | |||
| background: #fff; | |||
| color: #183b6b; | |||
| border: 2px solid #183b6b; | |||
| position: relative; | |||
| } | |||
| </style> | |||
| @ -0,0 +1,463 @@ | |||
| <template> | |||
| <!-- 小说文本页面 --> | |||
| <view class="reader-container" :class="{'dark-mode': isDarkMode}"> | |||
| <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="isDarkMode ? '#ccc' : '#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" @click="toggleThemeMode"> | |||
| <view class="bar-icon"> | |||
| <uv-icon :name="isDarkMode ? 'eye' : 'eye-fill'"></uv-icon> | |||
| </view> | |||
| <text class="bar-label">{{ isDarkMode ? '白天' : '夜间' }}</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> | |||
| <!-- 使用封装的订阅弹窗组件 --> | |||
| <subscriptionPopup ref="subscriptionPopup" @subscribe="goToSubscription"/> | |||
| <novelVotePopup ref="novelVotePopup"/> | |||
| <chapterPopup ref="chapterPopup" /> | |||
| </view> | |||
| </template> | |||
| <script> | |||
| import chapterPopup from '../components/novel/chapterPopup.vue' | |||
| import novelVotePopup from '../components/novel/novelVotePopup.vue' | |||
| import subscriptionPopup from '../components/novel/subscriptionPopup.vue' | |||
| import themeMixin from '@/mixins/themeMode.js' // 导入主题混合器 | |||
| export default { | |||
| components: { | |||
| chapterPopup, | |||
| novelVotePopup, | |||
| subscriptionPopup, | |||
| }, | |||
| mixins: [themeMixin], // 使用主题混合器 | |||
| data() { | |||
| return { | |||
| isFullScreen: false, | |||
| popupShown: 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.$refs.subscriptionPopup.open(); | |||
| 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.$refs.subscriptionPopup.open(); | |||
| 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; | |||
| &.dark-mode { | |||
| background: #1a1a1a; | |||
| .top-controls { | |||
| background: rgba(34, 34, 34, 0.98); | |||
| box-shadow: 0 2rpx 10rpx rgba(0, 0, 0, 0.2); | |||
| .controls-inner { | |||
| .center { | |||
| .title { | |||
| color: #eee; | |||
| } | |||
| .chapter { | |||
| color: #bbb; | |||
| } | |||
| } | |||
| } | |||
| .progress-bar { | |||
| background: #333; | |||
| .progress-inner { | |||
| background: #4a90e2; | |||
| } | |||
| } | |||
| } | |||
| .chapter-content { | |||
| color: #ccc; | |||
| .chapter-content-item { | |||
| .chapter-title { | |||
| color: #eee; | |||
| } | |||
| .paragraph-content { | |||
| .paragraph { | |||
| color: #bbb; | |||
| } | |||
| } | |||
| } | |||
| } | |||
| .bottom-bar { | |||
| background: #222; | |||
| box-shadow: 0 -2rpx 10rpx rgba(0, 0, 0, 0.2); | |||
| .bottom-left { | |||
| .bar-item { | |||
| .bar-label { | |||
| color: #999; | |||
| } | |||
| } | |||
| } | |||
| .bottom-right { | |||
| .outline-btn { | |||
| background: #222; | |||
| color: #4a90e2; | |||
| border: 2rpx solid #4a90e2; | |||
| .btn-text { | |||
| color: #4a90e2; | |||
| border-bottom: 2rpx solid #4a90e2; | |||
| } | |||
| } | |||
| } | |||
| } | |||
| } | |||
| .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, background-color 0.3s ease; | |||
| 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; | |||
| transition: color 0.3s ease, background-color 0.3s ease; | |||
| .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; | |||
| transition: color 0.3s ease; | |||
| } | |||
| .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; | |||
| transition: color 0.3s ease; | |||
| } | |||
| } | |||
| } | |||
| &.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, background-color 0.3s ease; | |||
| &.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; | |||
| display: flex; | |||
| align-items: center; | |||
| justify-content: center; | |||
| } | |||
| .bar-label { | |||
| font-size: 22rpx; | |||
| color: #b3b3b3; | |||
| margin-top: 2rpx; | |||
| transition: color 0.3s ease; | |||
| } | |||
| } | |||
| } | |||
| .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; | |||
| transition: background-color 0.3s ease, color 0.3s ease, border-color 0.3s ease; | |||
| .btn-text { | |||
| font-weight: bold; | |||
| color: #223a7a; | |||
| font-size: 28rpx; | |||
| border-bottom: 2rpx solid #223a7a; | |||
| padding-bottom: 2rpx; | |||
| transition: color 0.3s ease, border-color 0.3s ease; | |||
| } | |||
| } | |||
| } | |||
| } | |||
| } | |||
| </style> | |||