|
|
- <template>
- <!-- 书架页面 -->
- <view class="page">
- <!-- 头部标签切换 -->
- <view class="header" :style="{ paddingTop: `${statusBarHeight}px` }">
- <view class="header-content">
- <view class="tab-container">
- <view class="tab" :class="{'active': activeTab === 'read'}" @click="switchTab('read')">阅读</view>
- <view class="tab" :class="{'active': activeTab === 'work'}" @click="switchTab('work')">作品</view>
- </view>
- </view>
- </view>
-
- <!-- 书籍列表 - 阅读模式 -->
- <view class="novel-grid" v-if="activeTab === 'read' && !isEditMode">
- <view class="novel-row" v-for="(row, rowIndex) in novelRows" :key="rowIndex">
- <view class="novel-item"
- v-for="(novel, index) in row"
- :key="novel.id"
- @click="toNovelDetail(novel.id)"
- @longpress="enterEditMode">
- <novel-item
- :book="novel"
- horizontal="true"
- :style="{ width: '220rpx' }">
- </novel-item>
-
- <view class="novel-tag" v-if="novel.tag">{{novel.tag}}</view>
- <view class="novel-original" v-if="novel.isOriginal">
- <text>原创</text>
- </view>
- </view>
- </view>
- </view>
-
- <!-- 作品列表 - 作品模式 -->
- <view class="works-container" v-if="activeTab === 'work' && !isEditMode">
- <!-- 顶部创建区域 -->
- <view class="works-header">
- <new-work-item @click="createNewWork" @settings="toReaderSettings" />
- </view>
-
- <!-- 作品列表 -->
- <view class="works-content">
- <work-item
- v-for="work in worksList"
- :key="work.id"
- :work="work"
- @click="toWorkDetail(work.id)"
- @longpress="enterEditMode"
- />
-
- <!-- 空状态提示 -->
- <view class="empty-works" v-if="worksList.length === 0">
- <text class="empty-text">你还没有创建作品</text>
- <text class="empty-tips">点击左上角"+"创建你的第一部作品吧</text>
- </view>
- </view>
- </view>
-
- <!-- 编辑模式 - 阅读 -->
- <view class="novel-grid edit-mode" v-if="activeTab === 'read' && isEditMode">
- <view class="novel-row" v-for="(row, rowIndex) in novelRows" :key="rowIndex">
- <view class="novel-item"
- v-for="(novel, index) in row"
- :key="novel.id"
- @click="toggleSelect(novel, 'novel')">
- <view class="item-checkbox" v-if="selectedItems.includes(novel.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>
-
- <novel-item
- :book="novel"
- horizontal="true"
- :style="{ width: '220rpx', opacity: selectedItems.includes(novel.id) ? '0.8' : '1' }">
- </novel-item>
-
- <view class="novel-tag" v-if="novel.tag">{{novel.tag}}</view>
- <view class="novel-original" v-if="novel.isOriginal">
- <text>原创</text>
- </view>
- </view>
- </view>
- </view>
-
- <!-- 编辑模式 - 作品 -->
- <view class="works-container edit-mode" v-if="activeTab === 'work' && isEditMode">
- <view class="works-content">
- <view
- class="work-item-wrapper"
- v-for="work in worksList"
- :key="work.id"
- @click="toggleSelect(work, 'work')"
- >
- <work-item
- :work="work"
- :style="{ opacity: selectedItems.includes(work.id) ? '0.8' : '1' }"
- />
- </view>
- </view>
- </view>
-
- <!-- 底部操作栏 -->
- <view class="bottom-action-bar" v-if="isEditMode">
- <view class="action-item" @click="exitEditMode">
- <view class="action-icon">
- <uv-icon name="reload" size="40" color="#666"></uv-icon>
- </view>
- <text>取消</text>
- </view>
- <view class="action-item" @click="selectAll">
- <view class="action-icon">
- <uv-icon name="grid-fill" size="40" color="#ff9900"></uv-icon>
- </view>
- <text>全选</text>
- </view>
- <view class="action-item" @click="removeSelected">
- <view class="action-icon">
- <uv-icon name="trash-fill" size="40" color="#f56c6c"></uv-icon>
- </view>
- <text>{{activeTab === 'read' ? '移出书架' : '删除'}}</text>
- </view>
- </view>
-
- <tabber select="bookshelf" v-if="!isEditMode"/>
- </view>
- </template>
-
- <script>
- import tabber from '@/components/base/tabbar.vue'
- import novelItem from '@/components/novel/novelItem.vue'
- import workItem from '@/components/novel/workItem.vue'
- import newWorkItem from '@/components/novel/newWorkItem.vue'
- import { mapGetters } from 'vuex'
- export default {
- components : {
- tabber,
- novelItem,
- workItem,
- newWorkItem
- },
- computed : {
- ...mapGetters(['userShop']),
- // 将小说列表分成每行3个的二维数组
- novelRows() {
- const rows = [];
- const itemsPerRow = 3;
-
- for (let i = 0; i < this.novels.length; i += itemsPerRow) {
- rows.push(this.novels.slice(i, i + itemsPerRow));
- }
-
- return rows;
- }
- },
- data() {
- return {
- statusBarHeight: 0, // 状态栏高度
- navBarHeight: 0, // 导航栏高度
- activeTab: 'read',
- isEditMode: false,
- selectedItems: [], // 统一选中项
- novels: [
- {
- id: '1',
- title: '我是半妖',
- cover: 'https://tse4-mm.cn.bing.net/th/id/OIP-C.iUyxJ_fxLjjX3kEBjteXWwAAAA?rs=1&pid=ImgDetMain',
- author: '炎兰',
- desc: '都市玄幻小说,主角获得半妖化能力,通过吸收妖气不断变强...',
- tags: ['玄幻', '都市', '热血'],
- status: '连载中'
- },
- {
- id: '2',
- title: '兽王进化:从被小萝莉召唤开始',
- cover: 'https://tse4-mm.cn.bing.net/th/id/OIP-C.iUyxJ_fxLjjX3kEBjteXWwAAAA?rs=1&pid=ImgDetMain',
- author: '九灵',
- desc: '一场意外让主角获得兽王血脉,开始了进化之路...',
- tags: ['奇幻', '冒险'],
- isOriginal: true,
- status: '连载中'
- },
- {
- id: '3',
- title: '魔法少女纯爷们',
- cover: 'https://tse4-mm.cn.bing.net/th/id/OIP-C.iUyxJ_fxLjjX3kEBjteXWwAAAA?rs=1&pid=ImgDetMain',
- author: '烟火',
- desc: '一个普通男孩意外获得魔法少女的力量,开始了奇妙冒险...',
- tags: ['搞笑', '奇幻'],
- status: '已完结'
- },
- {
- id: '4',
- title: '我是一条小青龙',
- cover: 'https://tse4-mm.cn.bing.net/th/id/OIP-C.iUyxJ_fxLjjX3kEBjteXWwAAAA?rs=1&pid=ImgDetMain',
- author: '东升',
- desc: '重生为一条小青龙,主角在修仙世界中成长的故事...',
- tags: ['仙侠', '修真'],
- tag: '独家',
- status: '连载中'
- },
- {
- id: '5',
- title: '女帝:别闹,朕怀孕了!',
- cover: 'https://tse4-mm.cn.bing.net/th/id/OIP-C.iUyxJ_fxLjjX3kEBjteXWwAAAA?rs=1&pid=ImgDetMain',
- author: '君临',
- desc: '一代女帝意外穿越成了皇帝,却发现自己怀孕了...',
- tags: ['宫廷', '穿越'],
- isOriginal: true,
- status: '连载中'
- },
- {
- id: '6',
- title: '中国式应酬——应酬是门技术活',
- cover: 'https://tse4-mm.cn.bing.net/th/id/OIP-C.iUyxJ_fxLjjX3kEBjteXWwAAAA?rs=1&pid=ImgDetMain',
- author: '商业顾问',
- desc: '一本教你如何在商业场合应对各种应酬的实用指南...',
- tags: ['商业', '实用'],
- status: '已完结'
- },
- {
- id: '7',
- title: '苏世民:我的经验与教训',
- cover: 'https://tse4-mm.cn.bing.net/th/id/OIP-C.iUyxJ_fxLjjX3kEBjteXWwAAAA?rs=1&pid=ImgDetMain',
- author: '苏世民',
- desc: '黑石集团创始人苏世民的商业回忆录...',
- tags: ['传记', '商业'],
- status: '已完结'
- },
- {
- id: '8',
- title: '认知觉醒:开启自我改变的原动力',
- cover: 'https://tse4-mm.cn.bing.net/th/id/OIP-C.iUyxJ_fxLjjX3kEBjteXWwAAAA?rs=1&pid=ImgDetMain',
- author: '周岭',
- desc: '帮助你打破思维局限,重塑认知结构的心理学著作...',
- tags: ['心理', '自助'],
- status: '已完结'
- },
- {
- id: '9',
- title: '纳瓦尔宝典',
- cover: 'https://tse4-mm.cn.bing.net/th/id/OIP-C.iUyxJ_fxLjjX3kEBjteXWwAAAA?rs=1&pid=ImgDetMain',
- author: 'Naval',
- desc: '硅谷天使投资人纳瓦尔·拉维坎特的人生智慧...',
- tags: ['哲学', '投资'],
- status: '已完结'
- }
- ],
- // 作品列表数据
- worksList: [
- {
- id: '9',
- title: '纳瓦尔宝典',
- cover: 'https://tse4-mm.cn.bing.net/th/id/OIP-C.iUyxJ_fxLjjX3kEBjteXWwAAAA?rs=1&pid=ImgDetMain',
- author: 'Naval',
- desc: '硅谷天使投资人纳瓦尔·拉维坎特的人生智慧...',
- tags: ['哲学', '投资'],
- status: '已完结'
- }
- ] // 清空初始数据,改为动态加载
- }
- },
- onLoad() {
- // 获取系统信息
- const systemInfo = uni.getSystemInfoSync();
- this.statusBarHeight = systemInfo.statusBarHeight;
-
- // 检查是否需要切换到作品标签
- const activeTab = uni.getStorageSync('activeBookshelfTab')
- if (activeTab === 'work') {
- this.activeTab = 'work'
- uni.removeStorageSync('activeBookshelfTab')
- }
-
- // 监听切换到作品标签的事件
- uni.$on('switchToWork', () => {
- this.activeTab = 'work'
- })
-
- // #ifdef MP-WEIXIN || MP-QQ || MP-TOUTIAO
- const menuButtonInfo = uni.getMenuButtonBoundingClientRect();
- const navBarHeight = (menuButtonInfo.top - systemInfo.statusBarHeight) * 2 + menuButtonInfo.height + systemInfo.statusBarHeight;
- this.navBarHeight = navBarHeight;
- // #endif
- },
- onShow() {
-
- // 检查是否需要弹窗
- 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() {
- // 移除事件监听
- uni.$off('switchToWork')
- },
- methods: {
- // 切换标签
- switchTab(tab) {
- this.activeTab = tab;
-
- // 退出编辑模式
- this.exitEditMode();
- },
-
- // 跳转到小说阅读页
- toNovelDetail(id) {
- uni.navigateTo({
- url: '/pages_order/novel/readnovels?id=' + id
- })
- },
-
- // 跳转到作品详情页
- toWorkDetail(id) {
- uni.navigateTo({
- url: '/pages/work/detail?id=' + id
- })
- },
-
- // 创建新作品
- createNewWork() {
- uni.navigateTo({
- url: '/pages_order/novel/createNovel'
- })
- },
-
- // 跳转到读者成就设置
- toReaderSettings() {
- uni.navigateTo({
- url: '/pages_order/novel/ReaderAchievement'
- })
- },
-
- // 进入编辑模式
- enterEditMode() {
- this.isEditMode = true;
- this.selectedItems = [];
- },
-
- // 退出编辑模式
- exitEditMode() {
- this.isEditMode = false;
- this.selectedItems = [];
- },
-
- // 切换选择状态
- toggleSelect(item, type) {
- const index = this.selectedItems.indexOf(item.id);
- if (index === -1) {
- this.selectedItems.push(item.id);
- } else {
- this.selectedItems.splice(index, 1);
- }
- },
-
- // 全选
- selectAll() {
- if (this.activeTab === 'read') {
- // 已经全选,则取消全选
- if (this.selectedItems.length === this.novels.length) {
- this.selectedItems = [];
- } else {
- // 全选所有小说
- this.selectedItems = this.novels.map(novel => novel.id);
- }
- } else {
- // 已经全选,则取消全选
- if (this.selectedItems.length === this.worksList.length) {
- this.selectedItems = [];
- } else {
- // 全选所有作品
- this.selectedItems = this.worksList.map(work => work.id);
- }
- }
- },
-
- // 移除选中的项目
- removeSelected() {
- if (this.selectedItems.length === 0) {
- uni.showToast({
- title: '请先选择项目',
- icon: 'none'
- });
- return;
- }
-
- const title = this.activeTab === 'read' ? '移出书架' : '删除作品';
- const content = this.activeTab === 'read'
- ? `确定要将选中的${this.selectedItems.length}本小说移出书架吗?`
- : `确定要删除选中的${this.selectedItems.length}部作品吗?`;
-
- uni.showModal({
- title: '提示',
- content: content,
- success: (res) => {
- if (res.confirm) {
- if (this.activeTab === 'read') {
- // 移除选中的小说
- this.novels = this.novels.filter(novel => !this.selectedItems.includes(novel.id));
- uni.showToast({
- title: '移除成功',
- icon: 'success'
- });
- } else {
- // 删除选中的作品
- this.worksList = this.worksList.filter(work => !this.selectedItems.includes(work.id));
- // 保存更新后的作品列表
- uni.setStorageSync('worksList', this.worksList)
- uni.showToast({
- title: '删除成功',
- icon: 'success'
- });
- }
-
- this.selectedItems = [];
-
- // 如果没有数据了,退出编辑模式
- if ((this.activeTab === 'read' && this.novels.length === 0) ||
- (this.activeTab === 'work' && this.worksList.length === 0)) {
- this.exitEditMode();
- }
- }
- }
- });
- },
- // 加载作品列表
- loadWorksList() {
- const savedWorks = uni.getStorageSync('worksList') || []
- this.worksList = savedWorks
- }
- }
- }
- </script>
-
- <style scoped lang="scss">
- .page {
- background-color: #ffffff;
- min-height: 100vh;
- position: relative;
- padding-bottom: calc(120rpx + env(safe-area-inset-bottom));
- box-sizing: border-box;
- }
-
- .header {
- display: flex;
- flex-direction: column;
- justify-content: flex-end;
- position: sticky;
- top: 0;
- z-index: 100;
- background-color: #ffffff;
- box-sizing: border-box;
- width: 100%;
- border-bottom: 1rpx solid #f5f5f5;
- padding-top: constant(safe-area-inset-top); /* iOS 11.0 */
- padding-top: env(safe-area-inset-top); /* iOS 11.2+ */
-
- .header-content {
- display: flex;
- justify-content: center;
- align-items: center;
- padding: 20rpx 30rpx;
- padding-bottom: 24rpx;
- width: 100%;
- }
-
- .tab-container {
- display: flex;
- align-items: center;
- font-size: 34rpx;
-
- .tab {
- margin-right: 40rpx;
- color: #999;
- position: relative;
- padding: 10rpx 0;
-
- &.active {
- color: #000;
- font-weight: bold;
- font-size: 36rpx;
-
- &::after {
- content: '';
- position: absolute;
- bottom: 0;
- left: 50%;
- transform: translateX(-50%);
- width: 40rpx;
- height: 6rpx;
- background-color: #000;
- border-radius: 3rpx;
- }
- }
- }
- }
-
- .header-right {
- display: flex;
- align-items: center;
-
- .header-icon {
- margin-left: 30rpx;
- height: 80rpx;
- display: flex;
- align-items: center;
- justify-content: center;
-
- text {
- font-size: 28rpx;
- color: #333;
- }
- }
- }
- }
-
- .novel-grid {
- padding: 20rpx;
- padding-top: 30rpx;
- padding-bottom: env(safe-area-inset-bottom);
- box-sizing: border-box;
-
- .novel-row {
- display: flex;
- justify-content: space-between;
- margin-bottom: 40rpx;
-
- .novel-item {
- width: 31%;
- position: relative;
-
- .novel-tag {
- position: absolute;
- top: 10rpx;
- right: 10rpx;
- background-color: rgba(0, 0, 0, 0.6);
- color: #fff;
- font-size: 20rpx;
- padding: 4rpx 10rpx;
- border-radius: 6rpx;
- z-index: 1;
- }
-
- .novel-original {
- position: absolute;
- top: 10rpx;
- right: 10rpx;
- background-color: #ff9900;
- color: #fff;
- font-size: 20rpx;
- padding: 4rpx 10rpx;
- border-radius: 6rpx;
- z-index: 1;
- }
- }
- }
- }
-
- /* 作品列表相关样式 */
- .works-container {
- padding: 30rpx;
-
- .works-header {
- margin-bottom: 30rpx;
- }
-
- .works-content {
- display: flex;
- flex-direction: column;
-
- .work-item-wrapper {
- position: relative;
- margin-bottom: 20rpx;
- }
-
- .empty-works {
- width: 100%;
- padding: 100rpx 0;
- display: flex;
- flex-direction: column;
- align-items: center;
- justify-content: center;
-
- .empty-text {
- font-size: 32rpx;
- color: #666;
- margin-bottom: 20rpx;
- }
-
- .empty-tips {
- font-size: 28rpx;
- color: #999;
- }
- }
- }
- }
-
- .bottom-action-bar {
- position: fixed;
- bottom: 0;
- left: 0;
- width: 100%;
- height: calc(120rpx + env(safe-area-inset-bottom));
- background-color: #fff;
- border-top: 1rpx solid #eee;
- display: flex;
- justify-content: space-around;
- align-items: center;
- z-index: 99;
- padding-bottom: env(safe-area-inset-bottom);
-
- .action-item {
- display: flex;
- flex-direction: column;
- align-items: center;
- justify-content: center;
-
- .action-icon {
- width: 80rpx;
- height: 80rpx;
- display: flex;
- align-items: center;
- justify-content: center;
- }
-
- text {
- font-size: 24rpx;
- color: #666;
- margin-top: 8rpx;
- }
- }
- }
-
- // 编辑模式公共样式
- .edit-mode {
- .item-checkbox {
- position: absolute;
- top: 10rpx;
- left: 10rpx;
- z-index: 10;
- background-color: rgba(255,255,255,0.8);
- border-radius: 50%;
- width: 40rpx;
- height: 40rpx;
- display: flex;
- align-items: center;
- justify-content: center;
-
- .checkbox-inner {
- width: 40rpx;
- height: 40rpx;
- background-color: #1989fa;
- border-radius: 50%;
- display: flex;
- align-items: center;
- justify-content: center;
- }
-
- .checkbox-inner-no {
- width: 40rpx;
- height: 40rpx;
- background-color: #fff;
- border-radius: 50%;
- display: flex;
- align-items: center;
- justify-content: center;
- }
- }
- }
- </style>
|