|
|
<template> <view class="item-container"> <!-- 加载状态 --> <view v-if="isLoading" class="loading-container"> <uni-icons type="loading" size="40" color="#01a4fe" spin></uni-icons> <text class="loading-text">加载中...</text> </view>
<!-- 内容区域 --> <view v-else> <!-- 图书数量提示 --> <view v-if="bookList.length > 0" class="count-text"> <text>图书数量 ({{ bookList.length }})</text> </view>
<!-- 借阅列表 --> <view class="car-list" v-for="item in bookList" :key="item.barcode"> <checkbox :checked="item.checked" @click="toggleItem(item)" class="checkbox" /> <view class="book-item-box" @click="toggleItem(item)"> <view class="item-box-left"> <image class="img-item" :src="item.cover || defaultCover" mode="aspectFill" /> </view> <view class="item-box-right"> <view class="item-title line-clamp-2">{{ item.title || '暂无标题' }}</view> <view class="tag-box"> <text class="item-author">{{ item.author || '佚名' }}</text> <!-- 索书号 --> <text v-if="item.callno" class="item-callno">{{ item.callno }}</text> </view> <view class="item-desc"> <text class="return-label">应还时间:</text> <text :class="isOverdue(item.returndate) ? 'overdue-text' : ''">{{ item.returndate || '暂无' }}</text> </view> </view> </view> </view>
<!-- 空状态 --> <view class="empty" v-if="bookList.length === 0"> <uni-icons style="margin-left: 20px;" custom-prefix="iconfont" type="icon-kongshuju" size="80" color="#ccc"></uni-icons> <text class="empty-text">暂无借阅图书</text> </view> </view>
<!-- 底部占位 --> <view class="bottom-placeholder"></view>
<!-- 底部操作栏 --> <view class="car-bottom"> <view class="all-check" @click="toggleAllCheck"> <checkbox :checked="isAllChecked" /> <text style="margin-left:6px">全选</text> </view>
<button class="join-btn" @click="handleRenew" :disabled="!hasChecked"> 一键续借 </button> </view> </view></template>
<script>import { FetchInitScreenSetting } from '@/api/user';import { FetchRdloanlist, FetchRenewbook } from '@/api/book';import { getCurrentReaderCard } from '@/utils/storage';import config from '@/utils/config';
export default { data() { return { bookList: [], defaultCover: '/static/images/default-book.png', screenConfig: {}, isLoading: false, hasFetchedConfig: false }; },
onShow() { this.initPage(); },
computed: { isAllChecked() { return this.bookList.length > 0 && this.bookList.every(item => item.checked); }, hasChecked() { return this.bookList.some(item => item.checked); } },
methods: { /** * 初始化页面 */ async initPage() { // 如果已经获取过配置且有数据,直接刷新列表
if (this.hasFetchedConfig && Object.keys(this.screenConfig).length > 0) { await this.getLendingList(); return; }
await this.getConfigAndList(); },
/** * 获取配置并加载列表 */ async getConfigAndList() { this.isLoading = true; uni.showLoading({ title: '加载中...' });
try { // 1. 获取配置
await this.getScreenSetting();
// 2. 获取读者证
const currentReaderCard = await getCurrentReaderCard(); if (!currentReaderCard) { this.handleNoReaderCard(); return; }
// 3. 加载借阅列表
await this.getLendingList(); this.hasFetchedConfig = true;
} catch (err) { console.error('初始化失败:', err); uni.showToast({ title: '初始化失败', icon: 'none' }); } finally { this.isLoading = false; uni.hideLoading(); } },
/** * 获取屏幕配置 */ async getScreenSetting() { const res = await FetchInitScreenSetting({ libcode: config.LIB_CODE }); this.screenConfig = { thirdUrl: res.data.open_lib_http?.context || '', thirdAppid: res.data.open_lib_appId?.context || '', thirdSecret: res.data.open_lib_secret?.context || '', sm4Key: res.data.sm4_key?.context || '', opuser: res.data.op_user?.context || 'JH001', }; },
/** * 处理未绑定读者证情况 */ handleNoReaderCard() { this.bookList = []; uni.showModal({ title: '提示', content: '请先绑定读者证', confirmText: '去绑定', cancelText: '取消', success: (res) => { if (res.confirm) { uni.navigateTo({ url: "/pages/login/login" }); } else { uni.switchTab({ url: "/pages/home/home" }); } } }); },
/** * 获取借阅列表 */ async getLendingList() { uni.showLoading({ title: '加载中...' });
try { const currentReaderCard = await getCurrentReaderCard(); if (!currentReaderCard) { this.bookList = []; return; }
const params = { ...this.screenConfig, rdid: currentReaderCard.bindValue, };
const res = await FetchRdloanlist(params); const result = typeof res.data === 'string' ? JSON.parse(res.data) : res.data;
// 添加选中状态和封面处理
this.bookList = (result.loanlist || []).map(item => ({ ...item, checked: false, cover: this.getBookCover(item) }));
} catch (err) { console.error('获取借阅列表失败:', err); uni.showToast({ title: '加载失败', icon: 'none' }); this.bookList = []; } finally { uni.hideLoading(); } },
/** * 获取图书封面(如果有) */ getBookCover(item) { // 如果有封面信息,返回封面路径
if (item.coverurl) { return item.coverurl; } // 如果有图片相关信息,可以尝试构建封面URL
if (item.imgId) { return `/api/fileRelevant/getImg?imgType=2&imgId=${item.imgId}`; } return null; },
/** * 判断是否逾期 */ isOverdue(returndate) { if (!returndate) return false; const today = new Date(); const returnDate = new Date(returndate); return returnDate < today; },
/** * 切换单个选中状态 */ toggleItem(item) { item.checked = !item.checked; },
/** * 全选/取消全选 */ toggleAllCheck() { const isAll = this.isAllChecked; this.bookList.forEach(item => { item.checked = !isAll; }); },
/** * 一键续借 */ async handleRenew() { const checkedBooks = this.bookList.filter(item => item.checked); if (checkedBooks.length === 0) { uni.showToast({ title: '请选择图书', icon: 'none' }); return; }
uni.showLoading({ title: '续借中...' });
try { const currentReaderCard = await getCurrentReaderCard(); if (!currentReaderCard) { uni.hideLoading(); uni.showToast({ title: '请先绑定读者证', icon: 'none' }); return; }
const barcodes = checkedBooks.map(item => item.barcode).join('|'); const params = { ...this.screenConfig, rdid: currentReaderCard.bindValue, barcode: barcodes, logtype: '30007' };
const res = await FetchRenewbook(params); const result = typeof res.data === 'string' ? JSON.parse(res.data) : res.data;
await this.handleRenewResult(result);
} catch (err) { console.error('续借异常:', err); uni.hideLoading(); uni.showToast({ title: '网络异常,续借失败', icon: 'none' }); } },
/** * 处理续借结果 */ async handleRenewResult(result) { uni.hideLoading();
if (result.success === true) { uni.showToast({ title: '续借成功!', icon: 'success' }); // 刷新列表
await this.getLendingList(); } else { let msg = '续借失败'; if (result.messagelist && result.messagelist.length > 0) { msg = result.messagelist[0].message; } uni.showToast({ title: msg, icon: 'none', duration: 3000 }); } } }};</script>
<style lang="scss" scoped>.item-container { padding: 10px; background: #f5f6f7; min-height: 100vh;}
/* 加载状态 */.loading-container { display: flex; flex-direction: column; align-items: center; justify-content: center; height: 60vh;}
.loading-text { margin-top: 16px; font-size: 14px; color: #999;}
/* 数量提示 */.count-text { margin-bottom: 10px; font-size: 14px; color: #333;}
/* 列表项 */.car-list { display: flex; align-items: flex-start; margin-bottom: 10px;}
.checkbox { margin-top: 15px; padding: 0 10px;}
.book-item-box { flex: 1; background: #fff; border-radius: 8px; padding: 12px; display: flex; box-shadow: 0 2px 8px rgba(0, 0, 0, 0.06);}
.item-box-left { margin-right: 12px; flex-shrink: 0;}
.img-item { width: 64px; height: 90px; border-radius: 6px; background: #f5f5f5;}
.item-box-right { flex: 1; display: flex; flex-direction: column; overflow: hidden;}
.item-title { font-weight: bold; font-size: 15px; color: #333; margin-bottom: 6px; line-height: 1.4;}
.tag-box { display: flex; flex-wrap: wrap; gap: 6px; margin-bottom: 8px;}
.item-author { font-size: 12px; background: #f4f6fc; color: #666; padding: 2px 8px; border-radius: 4px;}
.item-callno { font-size: 12px; background: #fff3e0; color: #ff9800; padding: 2px 8px; border-radius: 4px;}
.item-desc { font-size: 13px; color: #999; margin-top: auto;}
.return-label { color: #999;}
.overdue-text { color: #ff4444; font-weight: bold;}
/* 空状态 */.empty { height: calc(100vh - 140px);}
.empty-text { margin-top: 20px; font-size: 14px;}
/* 底部占位 */.bottom-placeholder { height: 80px;}
/* 底部操作栏 */.car-bottom { position: fixed; left: 0; bottom: 0; right: 0; background: #fff; padding: 12px 15px; display: flex; justify-content: space-between; align-items: center; box-shadow: 0 -2px 10px rgba(0, 0, 0, 0.05);}
.all-check { display: flex; align-items: center; font-size: 14px; color: #333;}
.join-btn { font-size: 15px; color: #fff; background: #01a4fe !important; border-radius: 23px; padding: 0 30px; height: 40px; &::after { border: none !important; } &[disabled] { background: #ccc !important; }}</style>
|