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
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>
|