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

499 lines
11 KiB

<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';
import { loadBookCoversBase64 } from '@/utils/bookCover';
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
}));
// 加载封面
await this.loadCoversForList();
} 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;
},
/**
* 加载封面
*/
async loadCoversForList() {
await loadBookCoversBase64(this.bookList, (index, coverUrl) => {
if (this.bookList[index]) {
this.$set(this.bookList[index], 'cover', coverUrl);
}
});
},
/**
* 判断是否逾期
*/
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>