图书馆小程序
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.
 
 
 
 
 

246 lines
6.3 KiB

<template>
<view class="lending-container">
<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>
<book-list-item
:class="'list-item-' + tabItem.status"
v-for="(item, index) in listData[tabItem.status]"
:key="index"
:data="item"
:ranking="index + 1"
@click="goToDetail(item)"
/>
</block>
</view>
</swiper-item>
</swiper>
</view>
</template>
<script>
import myTabs from "@/components/my-tabs/my-tabs.vue";
import bookListItem from "@/components/book-list-item/book-list-item.vue";
export default {
components: { myTabs, bookListItem },
data() {
return {
tabData: [
{ label: "全部", status: "all", apiStatus: -1 },
{ label: "在借中", status: "lending", apiStatus: 0 },
{ label: "将过期", status: "expiring", apiStatus: 1 },
{ label: "已过期", status: "expired", apiStatus: 2 },
],
currentIndex: 0,
listData: {},
loadingMap: {},
swiperHeightData: {},
currentSwiperHeight: 400,
currentPageScrollTop: 0
};
},
onLoad() {
// 初始化数据结构
this.initDataStructure();
// 加载第一个tab的数据
const firstTab = this.getCurrentTab();
if (firstTab) {
this.getListData(firstTab.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);
});
},
// 获取当前选中的tab对象
getCurrentTab() {
return this.tabData[this.currentIndex];
},
// 获取列表数据
async getListData(statusKey) {
// 避免重复请求
if (this.listData[statusKey]?.length > 0) return;
this.loadingMap[statusKey] = true;
// 找到对应的tab配置
const tabConfig = this.tabData.find(tab => tab.status === statusKey);
const apiStatus = tabConfig?.apiStatus ?? 0;
// 调用API
const data = await this.fetchBorrowList(apiStatus);
this.listData[statusKey] = data;
this.loadingMap[statusKey] = false;
// 计算高度
this.$nextTick(() => {
this.calcSwiperHeight(statusKey);
});
},
// API请求(可独立到 api 文件)
async fetchBorrowList(apiStatus) {
// 模拟接口请求
return new Promise((resolve) => {
setTimeout(() => {
resolve(this.getMockData(apiStatus));
}, 600);
});
},
// Mock数据(模拟后端返回)
getMockData(apiStatus) {
const baseList = [
{
imgCover: "https://qiniu.aiyxlib.com/1606124577077",
title: "名侦探柯南",
nickname: "青山刚昌",
publish: "长春出版社",
isbn: "1001"
},
{
imgCover: "https://qiniu.aiyxlib.com/1606124577077",
title: "三体",
nickname: "刘慈欣",
publish: "重庆出版社",
isbn: "1002"
}
];
// 根据状态返回不同数据
switch(apiStatus) {
case 0: return baseList;
case 1: return [baseList[0]];
case 2: return [];
case -1: return baseList;
default: return [];
}
},
// 计算swiper高度
calcSwiperHeight(statusKey) {
const selector = `.list-item-${statusKey}`;
const query = uni.createSelectorQuery().in(this);
query.selectAll(selector).boundingClientRect((res) => {
let totalHeight = 200; // 默认高度
if (res && res.length) {
totalHeight = res.reduce((sum, item) => sum + item.height + 8, 0);
}
this.swiperHeightData[statusKey] = totalHeight;
this.currentSwiperHeight = totalHeight;
}).exec();
},
// tab点击事件
tabClick(index) {
this.currentIndex = index;
const currentTab = this.getCurrentTab();
// 滚动到顶部
if (this.currentPageScrollTop > 100) {
uni.pageScrollTo({ scrollTop: 100, duration: 100 });
}
// 按需加载数据
const statusKey = currentTab.status;
if (!this.listData[statusKey] || this.listData[statusKey].length === 0) {
this.getListData(statusKey);
} else {
this.currentSwiperHeight = this.swiperHeightData[statusKey] || 400;
}
},
onSwiperChange(e) {
if (e.detail.source === "touch") {
this.currentIndex = e.detail.current;
}
},
onSwiperEnd() {
const currentTab = this.getCurrentTab();
const statusKey = currentTab.status;
if (!this.listData[statusKey] || this.listData[statusKey].length === 0) {
this.getListData(statusKey);
} else {
this.currentSwiperHeight = this.swiperHeightData[statusKey] || 400;
}
},
goToDetail(item) {
uni.navigateTo({
url: `/subpkg/pages/book-detail/book-detail?isbn=${item.isbn}`
});
}
}
};
</script>
<style lang="scss" scoped>
.lending-container {
background-color: #f5f5f5;
min-height: 100vh;
.tab-sticky {
position: sticky;
top: 0;
z-index: 99;
background: #fff;
}
.swiper {
width: 100%;
min-height: 300px;
}
.swiper-item {
width: 100%;
height: 100%;
}
.list-wrapper {
padding: 10px;
box-sizing: border-box;
}
.empty {
text-align: center;
padding: 100px 0;
color: #999;
font-size: 14px;
}
}
</style>