|
|
<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> <!-- @click="goToDetail(item)" --> <lending-list-item :class="'list-item-' + tabItem.status" v-for="(item, index) in listData[tabItem.status]" :key="index" :data="item" :ranking="index + 1" /> </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";import lendingListItem from "@/components/lending-list-item/lending-list-item.vue";
export default { components: { myTabs, lendingListItem }, 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: {}, // 首次加载/刷新 loading
loadMoreStatusMap: {}, // 加载更多状态:loading / no-more / ""
pageMap: {}, // 分页页码
sizeMap: {}, // 每页条数
hasMoreMap: {}, // 是否还有更多
swiperHeightData: {}, currentSwiperHeight: 400, currentPageScrollTop: 0, isRefreshing: false // 是否正在下拉刷新
}; },
onLoad() { this.initDataStructure(); // 加载第一个tab的数据
const firstTab = this.getCurrentTab(); if (firstTab) { this.getListData(firstTab.status); }},
onShow() { // 读取要切换的 tabIndex
const tabIndex = uni.getStorageSync('switch_tab_index'); if (tabIndex !== undefined && tabIndex !== '') { this.currentIndex = Number(tabIndex); // 加载对应 tab 数据
const currentTab = this.getCurrentTab(); if (currentTab) { 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) { const tab = this.tabData.find(item => item.status === statusKey); if (!tab) return;
if (!isRefresh && this.listData[statusKey]?.length > 0) { this.loadingMap[statusKey] = false; return; }
this.loadingMap[statusKey] = true; this.pageMap[statusKey] = 1;
try { const data = await this.fetchBorrowList(tab.apiStatus, this.pageMap[statusKey], this.sizeMap[statusKey]); let list = data.records || []; list = list.map(item => { // 已归还
if (item.realityTime) { if (new Date(item.realityTime) <= new Date(item.returnTime)) { item.returnBook = 2; // 准时归还
} else { item.returnBook = 3; // 逾期归还
} } else { // 未归还
const now = new Date(); const returnDate = new Date(item.returnTime); const diffDay = Math.ceil((returnDate - now) / (1000 * 3600 * 24)); if (diffDay < 0) { // 1. 已逾期(应还时间 < 当前时间)
item.returnBook = 3; }else if (diffDay <= 3) { // 2. 临期(剩余 0~3 天)
item.returnBook = 1; } else { // 3. 正常在借
item.returnBook = 0; } } return item; }); this.listData[statusKey] = list; this.hasMoreMap[statusKey] = list.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(async () => { 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;
const tab = this.tabData.find(item => item.status === statusKey); try { const data = await this.fetchBorrowList(tab.apiStatus, this.pageMap[statusKey], this.sizeMap[statusKey]); let newList = data.records || [];
newList = newList.map(item => { // 已归还
if (item.realityTime) { if (new Date(item.realityTime) <= new Date(item.returnTime)) { item.returnBook = 2; // 准时归还
} else { item.returnBook = 3; // 逾期归还
} } else { // 未归还
const now = new Date(); const returnDate = new Date(item.returnTime); const diffDay = Math.ceil((returnDate - now) / (1000 * 3600 * 24)); if (diffDay < 0) { // 1. 已逾期(应还时间 < 当前时间)
item.returnBook = 3; }else if (diffDay <= 3) { // 2. 临期(剩余 0~3 天)
item.returnBook = 1; } else { // 3. 正常在借
item.returnBook = 0; } } return item; }); if (newList.length > 0) { this.listData[statusKey] = [...this.listData[statusKey], ...newList]; this.hasMoreMap[statusKey] = newList.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(async () => { this.calcSwiperHeight(statusKey); }, 0); },
// 下拉刷新
refreshList(statusKey) { this.getListData(statusKey, true); },
// 模拟接口(带分页)
async fetchBorrowList(apiStatus, pageNum, pageSize) { return new Promise((resolve) => { setTimeout(() => { const mock = this.getMockData(apiStatus, pageNum, pageSize); resolve({ records: mock, total: mock.length * 3 }); }, 500); }); },
// 模拟分页数据
getMockData(apiStatus, pageNum, pageSize) { const base = [ { imgCover: "https://qiniu.aiyxlib.com/1606124577077", title: "名侦探柯南", nickname: "青山刚昌", publish: "长春出版社", isbn: "1001", desc: '精心提炼20种GPT提问方法及指令,从入门到进阶再到精通,100个。', startTime: "2026-03-01", returnTime: "2026-04-30", // 应还时间:还有10天左右 → 在借中
realityTime: "" }, { imgCover: "https://qiniu.aiyxlib.com/1606124577077", title: "三体", nickname: "刘慈慈", publish: "重庆出版社", isbn: "1002", desc: '精心提炼20种GPT提问方法及指令,从入门到进阶再到精通,100个。', startTime: "2026-03-10", returnTime: "2026-04-22", // 应还时间:剩2天 → 临期
realityTime: "" }, { imgCover: "https://qiniu.aiyxlib.com/1606124577077", title: "红楼梦", nickname: "曹雪芹", publish: "人民文学", isbn: "1003", desc: '精心提炼20种GPT提问方法及指令,从入门到进阶再到精通,100个。', startTime: "2026-02-01", returnTime: "2026-03-25", // 已过期 → 逾期
realityTime: "" }, { imgCover: "https://qiniu.aiyxlib.com/1606124577077", title: "西游记", nickname: "吴承恩", publish: "中华书局", isbn: "1004", desc: '精心提炼20种GPT提问方法及指令,从入门到进阶再到精通,100个。', startTime: "2026-02-10", returnTime: "2026-04-01", realityTime: "2026-03-31" // 提前归还 → 准时
} ];
if (pageNum >= 3) { // 第3页开始返回空
return []; }
const list = [...base]; if (pageNum > 1) { list.push(...base.map(item => ({ ...item, isbn: item.isbn + pageNum }))); }
switch (apiStatus) { case -1: return list; case 0: return list; case 1: return [list[0], list[1]]; case 2: return []; default: return []; } },
// 计算swiper高度
calcSwiperHeight(statusKey) { const selector = `.list-item-${statusKey}`; 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 + 8, 0); } this.swiperHeightData[statusKey] = total; this.currentSwiperHeight = total; }).exec(); },
// tab切换
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; } },
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; }}::v-deep .uni-load-more{ height: auto !important;}</style>
|