You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
317 lines
9.4 KiB
317 lines
9.4 KiB
<template>
|
|
<view class="collection-container">
|
|
<!-- 吸顶 Tab -->
|
|
<view class="tab-sticky">
|
|
<my-tabs
|
|
:tabData="tabData"
|
|
:defaultIndex="currentIndex"
|
|
:config="{ textColor: '#333' }"
|
|
@tabClick="tabClick"
|
|
/>
|
|
</view>
|
|
|
|
<!-- 滑动内容区 -->
|
|
<swiper
|
|
class="swiper"
|
|
:current="currentIndex"
|
|
:style="{ height: currentSwiperHeight + 'px' }"
|
|
@animationfinish="onSwiperEnd"
|
|
@change="onSwiperChange"
|
|
>
|
|
<swiper-item v-for="(tabItem, idx) in tabData" :key="idx">
|
|
<view class="list-wrapper">
|
|
<!-- 加载中 -->
|
|
<uni-load-more status="loading" v-if="loadingMap[tabItem.status]" />
|
|
|
|
<!-- 空数据 -->
|
|
<view class="empty" v-else-if="!listData[tabItem.status] || listData[tabItem.status].length === 0">
|
|
暂无{{ tabItem.label }}收藏
|
|
</view>
|
|
|
|
<!-- 列表 -->
|
|
<block v-else>
|
|
<!-- 图书收藏 -->
|
|
<view class="recommendation-list">
|
|
<view
|
|
class="book-item"
|
|
v-for="(item, index) in listData.book"
|
|
:key="index"
|
|
@click="goToBookDetail(item)"
|
|
v-if="tabItem.status === 'book'"
|
|
>
|
|
<image class="book-cover" :src="item.cover"></image>
|
|
<view class="book-title">{{ item.title }}</view>
|
|
</view>
|
|
</view>
|
|
|
|
<!-- 活动收藏 -->
|
|
<view
|
|
class="activity-item"
|
|
v-for="(item, index) in listData.activity"
|
|
:key="index"
|
|
@click="toActivityDetail(item)"
|
|
v-if="tabItem.status === 'activity'"
|
|
>
|
|
<image class="activity-img" :src="item.imgUrl"></image>
|
|
<view class="activity-info">
|
|
<view class="activity-info-left">
|
|
<text class="title">{{ item.title }}</text>
|
|
<view class="time">
|
|
<uni-icons class="time-icon" custom-prefix="iconfont" type="time" size="15"></uni-icons>
|
|
<text>{{ item.time }}</text>
|
|
</view>
|
|
</view>
|
|
<button
|
|
class="activity-btn"
|
|
:class="item.status === 0 ? 'disabled-btn' : ''"
|
|
type="primary"
|
|
:disabled="item.status === 0"
|
|
>
|
|
{{ item.status === 1 ? '立即参加' : '活动结束' }}
|
|
</button>
|
|
</view>
|
|
</view>
|
|
</block>
|
|
|
|
<!-- 加载更多 -->
|
|
<uni-load-more
|
|
v-if="listData[tabItem.status] && listData[tabItem.status].length > 0 && !loadingMap[tabItem.status]"
|
|
:status="loadMoreStatusMap[tabItem.status]"
|
|
/>
|
|
</view>
|
|
</swiper-item>
|
|
</swiper>
|
|
</view>
|
|
</template>
|
|
|
|
<script>
|
|
import myTabs from "@/components/my-tabs/my-tabs.vue";
|
|
|
|
export default {
|
|
components: { myTabs },
|
|
data() {
|
|
return {
|
|
tabData: [
|
|
{ label: "图书收藏", status: "book" },
|
|
{ label: "活动收藏", status: "activity" },
|
|
],
|
|
currentIndex: 0,
|
|
|
|
// 数据结构完全和借阅页一致
|
|
listData: {},
|
|
loadingMap: {},
|
|
loadMoreStatusMap: {},
|
|
pageMap: {},
|
|
sizeMap: {},
|
|
hasMoreMap: {},
|
|
|
|
swiperHeightData: {},
|
|
currentSwiperHeight: 400,
|
|
currentPageScrollTop: 0,
|
|
isRefreshing: false,
|
|
};
|
|
},
|
|
|
|
onLoad() {
|
|
this.initDataStructure();
|
|
const firstTab = this.getCurrentTab();
|
|
this.getListData(firstTab.status);
|
|
},
|
|
|
|
onShow() {
|
|
const tabIndex = uni.getStorageSync("switch_tab_index");
|
|
if (tabIndex !== "") {
|
|
this.currentIndex = Number(tabIndex);
|
|
const currentTab = this.getCurrentTab();
|
|
this.getListData(currentTab.status);
|
|
uni.removeStorageSync("switch_tab_index");
|
|
}
|
|
},
|
|
|
|
onPullDownRefresh() {
|
|
this.isRefreshing = true;
|
|
const currentTab = this.getCurrentTab();
|
|
this.refreshList(currentTab.status);
|
|
},
|
|
|
|
onReachBottom() {
|
|
const currentTab = this.getCurrentTab();
|
|
this.loadMoreList(currentTab.status);
|
|
},
|
|
|
|
onPageScroll(res) {
|
|
this.currentPageScrollTop = res.scrollTop;
|
|
},
|
|
|
|
methods: {
|
|
initDataStructure() {
|
|
this.tabData.forEach((tab) => {
|
|
const key = tab.status;
|
|
this.$set(this.listData, key, []);
|
|
this.$set(this.loadingMap, key, true);
|
|
this.$set(this.loadMoreStatusMap, key, "");
|
|
this.$set(this.pageMap, key, 1);
|
|
this.$set(this.sizeMap, key, 10);
|
|
this.$set(this.hasMoreMap, key, true);
|
|
});
|
|
},
|
|
|
|
getCurrentTab() {
|
|
return this.tabData[this.currentIndex];
|
|
},
|
|
|
|
// 获取列表
|
|
async getListData(statusKey, isRefresh = false) {
|
|
if (!isRefresh && this.listData[statusKey]?.length > 0) {
|
|
this.loadingMap[statusKey] = false;
|
|
return;
|
|
}
|
|
|
|
this.loadingMap[statusKey] = true;
|
|
this.pageMap[statusKey] = 1;
|
|
|
|
try {
|
|
const res = await this.getCollectData(statusKey);
|
|
this.listData[statusKey] = res;
|
|
this.hasMoreMap[statusKey] = res.length === this.sizeMap[statusKey];
|
|
this.loadMoreStatusMap[statusKey] = this.hasMoreMap[statusKey] ? "" : "no-more";
|
|
} catch (err) {
|
|
this.listData[statusKey] = [];
|
|
} finally {
|
|
this.loadingMap[statusKey] = false;
|
|
this.isRefreshing = false;
|
|
uni.stopPullDownRefresh();
|
|
setTimeout(() => this.calcSwiperHeight(statusKey), 0);
|
|
}
|
|
},
|
|
|
|
// 加载更多
|
|
async loadMoreList(statusKey) {
|
|
if (this.loadingMap[statusKey] || !this.hasMoreMap[statusKey] || this.isRefreshing) return;
|
|
|
|
this.loadMoreStatusMap[statusKey] = "loading";
|
|
this.pageMap[statusKey] += 1;
|
|
|
|
try {
|
|
const newData = await this.getCollectData(statusKey);
|
|
if (newData.length > 0) {
|
|
this.listData[statusKey] = [...this.listData[statusKey], ...newData];
|
|
this.hasMoreMap[statusKey] = newData.length === this.sizeMap[statusKey];
|
|
this.loadMoreStatusMap[statusKey] = this.hasMoreMap[statusKey] ? "" : "no-more";
|
|
} else {
|
|
this.hasMoreMap[statusKey] = false;
|
|
this.loadMoreStatusMap[statusKey] = "no-more";
|
|
}
|
|
} catch (err) {
|
|
this.loadMoreStatusMap[statusKey] = "";
|
|
}
|
|
setTimeout(() => this.calcSwiperHeight(statusKey), 0);
|
|
},
|
|
|
|
// 模拟接口(图书/活动收藏)
|
|
getCollectData(type) {
|
|
return new Promise((resolve) => {
|
|
setTimeout(() => {
|
|
if (type === "book") {
|
|
resolve([
|
|
{ isbn: "1", title: "JavaScript高级程序设计", cover: "https://qiniu.aiyxlib.com/1606124577077" },
|
|
{ isbn: "2", title: "Vue3实战", cover: "https://qiniu.aiyxlib.com/1606124577077" },
|
|
{ isbn: "3", title: "深入理解计算机系统", cover: "https://qiniu.aiyxlib.com/1606124577077" },
|
|
{ isbn: "1", title: "JavaScript高级程序设计", cover: "https://qiniu.aiyxlib.com/1606124577077" }
|
|
]);
|
|
} else {
|
|
resolve([
|
|
{ imgUrl: "https://qiniu.aiyxlib.com/1605060269830", title: "读书分享会", time: "2025-11-03", status: 1 },
|
|
{ imgUrl: "https://qiniu.aiyxlib.com/1605060269830", title: "作者见面会", time: "2025-10-01", status: 0 },
|
|
]);
|
|
}
|
|
}, 300);
|
|
});
|
|
},
|
|
|
|
refreshList(statusKey) {
|
|
this.getListData(statusKey, true);
|
|
},
|
|
|
|
// 计算swiper高度
|
|
calcSwiperHeight(statusKey) {
|
|
const selector = statusKey === "book" ? ".book-item" : ".activity-item";
|
|
const query = uni.createSelectorQuery().in(this);
|
|
query.selectAll(selector).boundingClientRect((res) => {
|
|
let total = 200;
|
|
if (res?.length) total = res.reduce((t, h) => t + h.height + 10, 0);
|
|
this.swiperHeightData[statusKey] = total;
|
|
this.currentSwiperHeight = total;
|
|
}).exec();
|
|
},
|
|
|
|
tabClick(index) {
|
|
this.currentIndex = index;
|
|
const tab = this.getCurrentTab();
|
|
if (this.currentPageScrollTop > 100) uni.pageScrollTo({ scrollTop: 0, duration: 100 });
|
|
if (!this.listData[tab.status]?.length) this.getListData(tab.status);
|
|
else this.currentSwiperHeight = this.swiperHeightData[tab.status] || 400;
|
|
},
|
|
|
|
onSwiperChange(e) {
|
|
if (e.detail.source === "touch") this.currentIndex = e.detail.current;
|
|
},
|
|
|
|
onSwiperEnd() {
|
|
const tab = this.getCurrentTab();
|
|
if (!this.listData[tab.status]?.length) this.getListData(tab.status);
|
|
else this.currentSwiperHeight = this.swiperHeightData[tab.status] || 400;
|
|
},
|
|
|
|
goToBookDetail(item) {
|
|
uni.navigateTo({ url: `/subpkg/pages/book-detail/book-detail?isbn=${item.isbn}` });
|
|
},
|
|
|
|
toActivityDetail(item) {
|
|
uni.navigateTo({ url: `/subpkg/pages/activity-detail/activity-detail?title=${item.title}` });
|
|
},
|
|
},
|
|
};
|
|
</script>
|
|
|
|
<style lang="scss" scoped>
|
|
.collection-container {
|
|
background-color: #f5f5f5;
|
|
min-height: 100vh;
|
|
|
|
.tab-sticky {
|
|
position: sticky;
|
|
top: 0;
|
|
z-index: 99;
|
|
background: #fff;
|
|
}
|
|
|
|
.swiper {
|
|
width: 100%;
|
|
min-height: 300px;
|
|
}
|
|
|
|
.list-wrapper {
|
|
padding: 10px;
|
|
}
|
|
|
|
.empty {
|
|
text-align: center;
|
|
padding: 100px 0;
|
|
color: #999;
|
|
font-size: 14px;
|
|
}
|
|
.recommendation-list{
|
|
flex-wrap: wrap;
|
|
justify-content: flex-start;
|
|
.book-item {
|
|
margin-bottom: 16px;
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
::v-deep .uni-load-more {
|
|
height: auto !important;
|
|
}
|
|
</style>
|