Browse Source

简化

master
xuhuajiao 6 months ago
parent
commit
96b3b1b07b
  1. 6
      pages.json
  2. 273
      pages/lendCar/lendCar copy.vue
  3. 324
      pages/login/login copy 2.vue
  4. 252
      pages/login/login copy.vue
  5. 297
      pages/login/login-no-clear.vue
  6. 269
      pages/login/login-pay.vue
  7. 157
      subpkg/pages/activity-list/activity-list copy.vue
  8. 317
      subpkg/pages/collect-list/collect-list3.vue
  9. 335
      subpkg/pages/myLending/myLending copy.vue
  10. 480
      subpkg/pages/myLending/myLending-addpage.vue
  11. 225
      subpkg/pages/myLending/myLending-old.vue
  12. 246
      subpkg/pages/myLending/myLending-old2.vue
  13. 151
      subpkg/pages/reader-card/reader-card copy.vue
  14. 128
      subpkg/pages/scan-book/scan-book.vue

6
pages.json

@ -144,12 +144,6 @@
"style": {
"navigationBarTitleText": "个人资料"
}
},
{
"path": "pages/scan-book/scan-book",
"style": {
"navigationBarTitleText": ""
}
}
]
}

273
pages/lendCar/lendCar copy.vue

@ -1,273 +0,0 @@
<template>
<view class="item-container">
<!-- 图书数量 + 右侧设置按钮 -->
<view class="count-text">
<text>图书数量 ({{ bookList.length }})</text>
<text class="edit-btn" @click="toggleEdit">{{ editMode ? "完成" : "管理" }}</text>
</view>
<checkbox-group @change="checkboxChange">
<view class="car-list" v-for="item in bookList" :key="item.isbn">
<checkbox :value="item.isbn" :checked="item.checked" />
<view class="book-item-box">
<view class="item-box-left">
<image class="img-item" :src="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>
</view>
<view class="item-desc">
应还时间:{{ item.returndate || '暂无' }}
</view>
</view>
</view>
</view>
</checkbox-group>
<view class="bottom-placeholder"></view>
<view class="car-bottom">
<!-- 全选 -->
<view class="all-check" @click="handleAllCheck">
<checkbox :checked="isAllChecked" />
<text style="margin-left:6px">全选</text>
</view>
<!-- 根据编辑状态切换按钮:续借 / 删除 -->
<button
class="join-btn"
@click="editMode ? handleDelete() : handleRenew()"
:disabled="!hasChecked"
>
{{ editMode ? "删除选中" : "一键续借" }}
</button>
</view>
</view>
</template>
<script>
import { FetchInitScreenSetting } from '@/api/user';
import { FetchRdloanlist, FetchRenewbook } from '@/api/book';
export default {
data() {
return {
editMode: false,
bookList: [],
defaultCover: 'https://qiniu.aiyxlib.com/1606124577077',
screenConfig: {},
// 用数组维护选中项(更稳定)
checkedValues: []
};
},
onLoad() {
this.getConfigAndList();
},
computed: {
isAllChecked() {
return this.bookList.length > 0 && this.checkedValues.length === this.bookList.length;
},
hasChecked() {
return this.checkedValues.length > 0;
}
},
methods: {
async getConfigAndList() {
try {
const res = await FetchInitScreenSetting({ libcode: '1201' });
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 || ''
};
this.getLendingList();
} catch (err) {}
},
async getLendingList() {
try {
const params = {
...this.screenConfig,
rdid: uni.getStorageSync('currentReaderCard'),
};
const res = await FetchRdloanlist(params);
const result = typeof res.data === 'string' ? JSON.parse(res.data) : res.data;
this.bookList = result.loanlist || [];
// 切换列表时清空选中
this.checkedValues = [];
} catch (err) {
uni.showToast({ title: '加载失败', icon: 'none' });
}
},
toggleEdit() {
this.editMode = !this.editMode;
if (!this.editMode) {
this.checkedValues = [];
}
},
// ✅ 修复2:稳定的 checkbox 变更
checkboxChange(e) {
// 直接保存数组(字符串类型)
this.checkedValues = e.detail.value;
},
handleAllCheck() {
if (this.isAllChecked) {
// 取消全选
this.checkedValues = [];
} else {
// 全选:把所有 isbn 转字符串放进数组
this.checkedValues = this.bookList.map(item => String(item.isbn));
}
},
async handleRenew() {
// 从 checkedValues 过滤出对应图书
const checkedBooks = this.bookList.filter(item =>
this.checkedValues.includes(String(item.isbn))
);
if (checkedBooks.length === 0) {
uni.showToast({ title: '请选择要续借的图书', icon: 'none' });
return;
}
const barcodes = checkedBooks.map(item => item.barcode).join('|');
uni.showLoading({ title: '续借中...' });
try {
const params = {
...this.screenConfig,
rdid: uni.getStorageSync('currentReaderCard'),
barcode: barcodes,
logtype: '30007'
};
console.log('续借参数', params);
await FetchRenewbook(params);
uni.hideLoading();
uni.showToast({
title: `成功续借 ${checkedBooks.length} 本`,
icon: 'success'
});
this.getLendingList();
} catch (err) {
uni.hideLoading();
uni.showToast({ title: '续借失败', icon: 'none' });
}
},
handleDelete() {
// 只保留未选中的
this.bookList = this.bookList.filter(item =>
!this.checkedValues.includes(String(item.isbn))
);
uni.showToast({
title: `已删除 ${this.checkedValues.length} 本`,
icon: "success"
});
this.checkedValues = [];
this.editMode = false;
}
}
};
</script>
<style lang="scss" scss>
.item-container {
padding: 10px;
background: #f5f6f7;
min-height: 100vh;
}
.count-text {
margin-bottom: 10px;
display: flex;
justify-content: space-between;
align-items: center;
}
.edit-btn {
color: #01a4fe;
font-size: 14px;
}
.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;
.item-box-left {
margin-right: 12px;
.img-item {
width: 64px;
height: 90px;
border-radius: 6px;
}
}
.item-box-right {
flex: 1;
}
.item-title {
font-weight: bold;
margin-bottom: 4px;
}
.item-author {
font-size: 12px;
background: #f4f6fc;
padding: 2px 6px;
border-radius: 4px;
margin-right: 6px;
}
.item-desc {
font-size: 12px;
color: #999;
margin-top: 6px;
}
}
.bottom-placeholder {
height: 60px;
}
.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 5px rgba(0, 0, 0, 0.05);
}
.all-check {
display: flex;
align-items: center;
}
.join-btn {
font-size: 14px;
color: #fff;
margin: 0 !important;
background-color: #01a4fe !important;
border-radius: 23px;
padding: 0 30px;
&::after{
border: none !important;
}
&[disabled] {
background: #ccc !important;
}
}
</style>

324
pages/login/login copy 2.vue

@ -1,324 +0,0 @@
<template>
<view style="background-color: #fff; height: calc(100vh);">
<view class="user-info-section">
<!-- 点击获取头像 -->
<button open-type="chooseAvatar" @chooseavatar="onChooseAvatar" class="avatar-btn">
<image v-if="avatarUrl" :src="avatarUrl" class="avatar-img"></image>
<image v-else src="@/static/images/avatar.png" class="avatar-img"></image>
<text class="tip-text">点击选择头像</text>
</button>
</view>
<view class="form-box">
<view class="item">
<uni-icons class="form-icon" type="person" size="24"></uni-icons>
<view class="uni-input-wrapper">
<input
class="input"
type="nickname"
v-model="nickName"
placeholder="请输入昵称"
@input="handleInput('nickName')"
/>
<uni-icons
class="clear-icon"
v-if="clearIconStatus.nickName"
@click="clearInput('nickName')"
type="close"
size="20"
></uni-icons>
</view>
</view>
<view class="item">
<uni-icons class="form-icon" custom-prefix="iconfont" type="icon-duzhezheng" size="24"></uni-icons>
<view class="uni-input-wrapper">
<input
class="input"
placeholder="请输入读者证号"
v-model="queryvalue"
@input="handleInput('queryvalue')"
/>
<uni-icons
class="clear-icon"
v-if="clearIconStatus.queryvalue"
@click="clearInput('queryvalue')"
type="close"
size="20"
></uni-icons>
</view>
</view>
<view class="item">
<uni-icons class="form-icon" type="locked" size="24"></uni-icons>
<input class="input" placeholder="请输入密码" :password="!showPwd" v-model="rdpasswd" />
<uni-icons class="form-right-icon" :type="showPwd ? 'eye-slash' : 'eye'" size="20"
@click="togglePwd"></uni-icons>
</view>
<button class="login-btn" type="primary" @click="submit">绑定</button>
</view>
<view class="tips">
温馨提示:<br />
1、密码默认为 <text style="color:#e74c3c">身份证后6位</text>,如果身份证号最后一位为“X”,“X”需要大写;
</view>
</view>
</template>
<script>
import { FetchInitScreenSetting, FetchReaderList } from '@/api/user';
const TOKEN_KEY = 'token';
const USER_KEY = 'user-info';
export default {
data() {
return {
queryvalue: '',
rdpasswd: '',
showPwd: false,
screenConfig: {},
avatarUrl: '',
nickName: '',
// 清空按钮状态(支持多输入框)
clearIconStatus: {
nickName: false,
queryvalue: false
}
};
},
onLoad() {
this.getScreenSetting();
},
methods: {
// 获取微信头像
onChooseAvatar(e) {
console.log('获取微信头像',e)
this.avatarUrl = e.detail.avatarUrl;
},
// 统一监听输入(自动控制清空按钮显示隐藏)
handleInput(field) {
this.clearIconStatus[field] = this[field].trim().length > 0
},
// 统一清空输入框内容
clearInput(field) {
this[field] = ''
this.clearIconStatus[field] = false
},
// 切换密码显隐
togglePwd() {
this.showPwd = !this.showPwd;
},
async getScreenSetting() {
try {
const res = await FetchInitScreenSetting({ libcode: '1201' });
const data = res.data;
this.screenConfig = {
thirdUrl: data.open_lib_http?.context || '',
thirdAppid: data.open_lib_appId?.context || '',
thirdSecret: data.open_lib_secret?.context || '',
sm4Key: data.sm4_key?.context || ''
};
} catch (err) {
console.error('获取配置失败:', err);
}
},
async submit() {
// 去除首尾空格
this.nickName = this.nickName.trim()
this.queryvalue = this.queryvalue.trim()
if (!this.queryvalue || !this.rdpasswd) {
uni.showToast({ title: '请输入读者证号和密码', icon: 'none' });
return;
}
uni.showLoading({ title: '绑定中...' });
try {
const params = {
...this.screenConfig,
selecttype: 'rdid',
queryvalue: this.queryvalue,
rdpasswd: this.rdpasswd,
};
// {"code":200,"message":"操作成功","data":"{\"messagelist\":[{\"code\":\"R00138\",\"message\":\"未找到符合条件的读者!\"}],\"success\":false}","timestamp":1778154499544}
// {"code":200,"message":"操作成功","data":"{\"success\":true,\"pagedata\":[{\"rdClusterCode\":null,\"rdlib\":\"GD\",\"rdid\":\"420105198509200438\",\"rdcfstate\":1}]}","timestamp":1778154647854}
// {"code":200,"message":"操作成功","data":"{\"messageList\":[{\"R00131\":\"认证失败,系统存在该读者,密码不匹配!\"}],\"success\":true,\"pagedata\":\"\"}","timestamp":1778155520501}
const res = await FetchReaderList(params);
let result = {};
try {
result = JSON.parse(res.data);
} catch (e) {
uni.hideLoading();
uni.showToast({ title: '数据解析失败', icon: 'none' });
return;
}
// 统一提取错误信息
let errMsg = '';
if (result.messagelist?.length) {
const item = result.messagelist[0];
errMsg = item.message || Object.values(item)[0] || '认证失败';
}
if (result.messageList?.length) {
const item = result.messageList[0];
errMsg = item.message || Object.values(item)[0] || '认证失败';
}
// 成功判断
const realSuccess = result.success === true && result.pagedata?.length > 0;
if (realSuccess) {
const loginRes = {
token: 'reader-token-' + Date.now(),
user: {
nickName: this.nickName || '小图',
avatarUrl: this.avatarUrl,
cardNo: this.queryvalue
}
};
// uni.setStorageSync(TOKEN_KEY, loginRes.token);
// uni.setStorageSync(USER_KEY, loginRes.user);
// uni.hideLoading();
// uni.showToast({ title: '认证成功', icon: 'success' });
const openId = uni.getStorageSync("wx_login_code");
const bindParams = {
...this.screenConfig,
openid: openId,
bindValue: this.queryvalue,
bindType: 'rdid',
libcode: '1201',
}
const bindRes = await FetchBindReadCard(params);
setTimeout(() => {
uni.navigateBack();
}, 1500);
} else {
uni.hideLoading();
uni.showToast({
title: errMsg || '读者证或密码错误',
icon: 'none'
});
}
} catch (err) {
uni.hideLoading();
uni.showToast({ title: '网络异常', icon: 'none' });
}
}
}
};
</script>
<style lang="scss" scoped>
.user-info-section {
display: flex;
flex-direction: column;
align-items: center;
padding: 30px 0 0 0;
.avatar-btn {
background: transparent;
display: flex;
flex-direction: column;
align-items: center;
&::after{
border: none !important;
}
.avatar-img {
width: 60px;
height: 60px;
border-radius: 50%;
}
.tip-text {
font-size: 12px;
color: #999;
}
}
}
.form-box {
padding: 20px 15px;
.item {
width: 100%;
min-height: 44px;
border-radius: 22px;
background-color: #f7f7f7;
display: flex;
align-items: center;
margin-bottom: 15px;
.input {
flex: 1;
padding: 10px;
font-size: 14px;
}
}
.uni-input-wrapper{
flex: 1;
display: flex;
align-items: center;
.input {
flex: 1;
padding: 10px;
font-size: 14px;
}
.clear-icon{
padding: 0 12px;
color: #ccc;
}
}
}
.login-btn {
margin-top: 10px;
background-color: #01a4fe !important;
border-radius: 22px;
font-size: 16px;
height: 44px;
line-height: 44px;
}
.tips {
margin: 30px 20px;
font-size: 12px;
color: #333;
line-height: 20px;
}
.form-icon {
::v-deep .uni-icons {
margin-left: 10px;
color: #01a4fe !important;
}
}
.form-right-icon {
::v-deep .uni-icons {
margin-right: 10px;
}
}
</style>

252
pages/login/login copy.vue

@ -1,252 +0,0 @@
<template>
<view style="background-color: #fff; height: calc(100vh);">
<view class="top-bar">
<image class="top-bar-bg" src="@/static/images/mingqi-beij@2x.png" mode="aspectFill"></image>
<view class="library-info">
<image class="avatar" src="@/static/images/avatar.png" mode="aspectFill"></image>
<view class="library-name">葛店经济技术开发区图书馆</view>
</view>
</view>
<view class="form-box">
<view class="item">
<uni-icons class="form-icon" type="person" size="24"></uni-icons>
<input class="input" placeholder="请输入读者证号" v-model="queryvalue" />
</view>
<view class="item">
<uni-icons class="form-icon" type="locked" size="24"></uni-icons>
<input class="input" placeholder="请输入密码" :password="!showPwd" v-model="rdpasswd" />
<uni-icons class="form-right-icon" :type="showPwd ? 'eye-slash' : 'eye'" size="20"
@click="togglePwd"></uni-icons>
</view>
<button class="login-btn" type="primary" @click="submit">绑定</button>
</view>
<view class="tips">
温馨提示:<br />
1、密码默认为 <text style="color:#e74c3c">身份证后6位</text>,如果身份证号最后一位为“X”,“X”需要大写;
</view>
</view>
</template>
<script>
import { FetchInitScreenSetting, FetchReaderList } from '@/api/user';
const TOKEN_KEY = 'token';
const USER_KEY = 'user-info';
export default {
data() {
return {
queryvalue: '',
rdpasswd: '',
showPwd: false,
screenConfig: {}
};
},
onLoad() {
this.getScreenSetting();
},
methods: {
// 切换密码显隐
togglePwd() {
this.showPwd = !this.showPwd;
},
// 1: 页面加载时先获取大屏配置
async getScreenSetting() {
try {
const res = await FetchInitScreenSetting({ libcode: '1201' });
const data = res.data;
// 自动取出 4 个必须参数
this.screenConfig = {
thirdUrl: data.open_lib_http?.context || '',
thirdAppid: data.open_lib_appId?.context || '',
thirdSecret: data.open_lib_secret?.context || '',
sm4Key: data.sm4_key?.context || ''
};
console.log('已获取第三方配置:', this.screenConfig);
} catch (err) {
console.error('获取配置失败:', err);
}
},
// {"code":200,"message":"操作成功","data":"{\"messagelist\":[{\"code\":\"R00138\",\"message\":\"未找到符合条件的读者!\"}],\"success\":false}","timestamp":1778154499544}
// {"code":200,"message":"操作成功","data":"{\"success\":true,\"pagedata\":[{\"rdClusterCode\":null,\"rdlib\":\"GD\",\"rdid\":\"420105198509200438\",\"rdcfstate\":1}]}","timestamp":1778154647854}
// {"code":200,"message":"操作成功","data":"{\"messageList\":[{\"R00131\":\"认证失败,系统存在该读者,密码不匹配!\"}],\"success\":true,\"pagedata\":\"\"}","timestamp":1778155520501}
// 2:点击绑定 → 调用读者登录接口
async submit() {
if (!this.queryvalue || !this.rdpasswd) {
uni.showToast({ title: '请输入读者证号和密码', icon: 'none' });
return;
}
if (!this.screenConfig.thirdUrl) {
uni.showToast({ title: '图书馆配置加载中,请稍候重试', icon: 'none' });
return;
}
uni.showLoading({ title: '绑定中...' });
try {
const params = {
...this.screenConfig,
selecttype: 'rdid',
queryvalue: this.queryvalue,
rdpasswd: this.rdpasswd,
havecluster: ''
};
const res = await FetchReaderList(params);
// 1. 解析返回的JSON字符串
let result = {};
try {
result = JSON.parse(res.data);
} catch (e) {
uni.hideLoading();
uni.showToast({ title: '数据解析失败', icon: 'none' });
return;
}
// 2. 统一提取错误信息(兼容大小写、各种格式)
let errMsg = '';
// 兼容小写 messagelist
if (result.messagelist && result.messagelist.length > 0) {
const item = result.messagelist[0];
errMsg = item.message || Object.values(item)[0] || '绑定失败';
}
// 兼容大写 messageList
if (result.messageList && result.messageList.length > 0) {
const item = result.messageList[0];
errMsg = item.message || Object.values(item)[0] || '绑定失败';
}
// 3. 真正的成功判断:必须 success=true 并且 pagedata 有值
const realSuccess = result.success === true && result.pagedata && result.pagedata.length > 0;
if (realSuccess) {
const wxUser = uni.getStorageSync('wxUserInfo') || {};
const loginRes = {
token: 'reader-token-' + Date.now(),
user: {
nickName: wxUser.nickName || '读者',
avatarUrl: wxUser.avatarUrl || '',
cardNo: this.queryvalue
}
};
uni.setStorageSync('token', loginRes.token);
uni.setStorageSync('user-info', loginRes.user);
uni.hideLoading();
uni.showToast({ title: '绑定成功', icon: 'success' });
setTimeout(() => {
uni.navigateBack();
}, 1500);
} else {
uni.hideLoading();
uni.showToast({
title: errMsg || '读者证或密码错误',
icon: 'none'
});
}
} catch (err) {
uni.hideLoading();
console.error('绑定异常:', err);
uni.showToast({ title: '网络异常,请稍后重试', icon: 'none' });
}
}
}
};
</script>
<style lang="scss" scoped>
.top-bar {
position: relative;
width: 100%;
height: 220rpx;
.top-bar-bg {
position: absolute;
width: 100%;
height: 100%;
}
.library-info {
position: relative;
display: flex;
flex-direction: column;
align-items: center;
padding-top: 30rpx;
.avatar {
width: 100rpx;
height: 100rpx;
border-radius: 50%;
}
.library-name {
margin-top: 10rpx;
color: #fff;
font-size: 30rpx;
}
}
}
.form-box {
padding: 40rpx 30rpx;
.item {
width: 100%;
min-height: 80rpx;
border-radius: 40rpx;
background-color: #f7f7f7;
display: flex;
align-items: center;
margin-bottom: 30rpx;
.input {
flex: 1;
padding: 20rpx 20rpx;
font-size: 28rpx;
}
}
}
.login-btn {
margin-top: 20rpx;
background-color: #01a4fe !important;
border-radius: 40rpx;
font-size: 30rpx;
}
.tips {
margin: 60rpx 40rpx;
font-size: 24rpx;
color: #333;
line-height: 40rpx;
}
.form-icon {
::v-deep .uni-icons {
margin-left: 20rpx;
color: #01a4fe !important;
}
}
.form-right-icon {
::v-deep .uni-icons {
margin-right: 20rpx;
}
}
</style>

297
pages/login/login-no-clear.vue

@ -1,297 +0,0 @@
<template>
<view style="background-color: #fff; height: calc(100vh);">
<view class="user-info-section">
<!-- 点击获取头像 -->
<button open-type="chooseAvatar" @chooseavatar="onChooseAvatar" class="avatar-btn">
<image v-if="avatarUrl" :src="avatarUrl" class="avatar-img"></image>
<image v-else src="@/static/images/avatar.png" class="avatar-img"></image>
<text class="tip-text">点击选择头像</text>
</button>
</view>
<view class="form-box">
<view class="item">
<uni-icons class="form-icon" type="person" size="24"></uni-icons>
<view class="uni-input-wrapper">
<input class="input" type="nickname" v-model="nickName" @blur="onNicknameBlur" placeholder="请输入昵称" @input="clearInput" />
<uni-icons class="clear-icon" v-if="showClearIcon" @click="clearIcon" type="close" size="20"></uni-icons>
</view>
</view>
<view class="item">
<uni-icons class="form-icon" custom-prefix="iconfont" type="icon-duzhezheng" size="24"></uni-icons>
<view class="uni-input-wrapper">
<input class="input" placeholder="请输入读者证号" v-model="queryvalue" @input="clearInput" />
<uni-icons class="clear-icon" v-if="showClearIcon" @click="clearIcon" type="close" size="20"></uni-icons>
</view>
</view>
<view class="item">
<uni-icons class="form-icon" type="locked" size="24"></uni-icons>
<input class="input" placeholder="请输入密码" :password="!showPwd" v-model="rdpasswd" />
<uni-icons class="form-right-icon" :type="showPwd ? 'eye-slash' : 'eye'" size="20"
@click="togglePwd"></uni-icons>
</view>
<button class="login-btn" type="primary" @click="submit">绑定</button>
</view>
<view class="tips">
温馨提示:<br />
1、密码默认为 <text style="color:#e74c3c">身份证后6位</text>,如果身份证号最后一位为“X”,“X”需要大写;
</view>
</view>
</template>
<script>
import { FetchInitScreenSetting, FetchReaderList } from '@/api/user';
const TOKEN_KEY = 'token';
const USER_KEY = 'user-info';
export default {
data() {
return {
queryvalue: '',
rdpasswd: '',
showPwd: false,
screenConfig: {},
avatarUrl: '',
nickName: '',
showClearIcon: false,
};
},
onLoad() {
this.getScreenSetting();
},
methods: {
// 获取微信头像
onChooseAvatar(e) {
console.log('获取微信头像',e)
this.avatarUrl = e.detail.avatarUrl;
},
onNicknameBlur(e) {
this.nickName = e.detail.value.trim()
},
clearInput(event) {
this.queryvalue = event.detail.value;
if (event.detail.value.length > 0) {
this.showClearIcon = true;
} else {
this.showClearIcon = false;
}
},
clearIcon() {
this.queryvalue = '';
this.showClearIcon = false;
},
// 切换密码显隐
togglePwd() {
this.showPwd = !this.showPwd;
},
async getScreenSetting() {
try {
const res = await FetchInitScreenSetting({ libcode: '1201' });
const data = res.data;
this.screenConfig = {
thirdUrl: data.open_lib_http?.context || '',
thirdAppid: data.open_lib_appId?.context || '',
thirdSecret: data.open_lib_secret?.context || '',
sm4Key: data.sm4_key?.context || ''
};
} catch (err) {
console.error('获取配置失败:', err);
}
},
async submit() {
// if (!this.avatarUrl) {
// uni.showToast({ title: '请选择头像', icon: 'none' });
// return;
// }
// if (!this.nickName) {
// uni.showToast({ title: '请输入昵称', icon: 'none' });
// return;
// }
if (!this.queryvalue || !this.rdpasswd) {
uni.showToast({ title: '请输入读者证号和密码', icon: 'none' });
return;
}
uni.showLoading({ title: '绑定中...' });
try {
const params = {
...this.screenConfig,
selecttype: 'rdid',
queryvalue: this.queryvalue,
rdpasswd: this.rdpasswd,
};
const res = await FetchReaderList(params);
let result = {};
try {
result = JSON.parse(res.data);
} catch (e) {
uni.hideLoading();
uni.showToast({ title: '数据解析失败', icon: 'none' });
return;
}
// 2. 统一提取错误信息(兼容大小写、各种格式)
let errMsg = '';
// 兼容小写 messagelist
if (result.messagelist && result.messagelist.length > 0) {
const item = result.messagelist[0];
errMsg = item.message || Object.values(item)[0] || '绑定失败';
}
// 兼容大写 messageList
if (result.messageList && result.messageList.length > 0) {
const item = result.messageList[0];
errMsg = item.message || Object.values(item)[0] || '绑定失败';
}
// 3. 真正的成功判断:必须 success=true 并且 pagedata 有值
const realSuccess = result.success === true && result.pagedata && result.pagedata.length > 0;
console.log('this.avatarUrl',this.avatarUrl)
if (realSuccess) {
const loginRes = {
token: 'reader-token-' + Date.now(),
user: {
nickName: this.nickName || '小图',
avatarUrl: this.avatarUrl,
cardNo: this.queryvalue
}
};
uni.setStorageSync(TOKEN_KEY, loginRes.token);
uni.setStorageSync(USER_KEY, loginRes.user);
uni.hideLoading();
uni.showToast({ title: '绑定成功', icon: 'success' });
setTimeout(() => {
uni.navigateBack();
}, 1500);
} else {
uni.hideLoading();
uni.showToast({
title: errMsg || '读者证或密码错误',
icon: 'none'
});
}
} catch (err) {
uni.hideLoading();
uni.showToast({ title: '网络异常', icon: 'none' });
}
}
}
};
</script>
<style lang="scss" scoped>
.user-info-section {
display: flex;
flex-direction: column;
align-items: center;
padding: 30px 0 0 0;
.avatar-btn {
background: transparent;
display: flex;
flex-direction: column;
align-items: center;
&::after{
border: none !important;
}
.avatar-img {
width: 60px;
height: 60px;
border-radius: 50%;
}
.tip-text {
font-size: 12px;
color: #999;
}
}
// .nickname-box {
// width: 80%;
// margin-top: 10px;
// .nickname-input {
// width: 100%;
// padding: 10px 0;
// border-bottom: 1px solid #eee;
// text-align: center;
// font-size: 14px;
// }
// }
}
.form-box {
padding: 20px 15px;
.item {
width: 100%;
min-height: 40px;
border-radius: 20px;
background-color: #f7f7f7;
display: flex;
align-items: center;
margin-bottom: 15px;
.input {
flex: 1;
padding: 10px;
font-size: 14px;
}
}
.uni-input-wrapper{
flex: 1;
display: flex;
align-items: center;
.input {
flex: 1;
}
.clear-icon{
padding: 0 10px;
}
}
}
.login-btn {
margin-top: 10px;
background-color: #01a4fe !important;
border-radius: 20px;
font-size: 16px;
}
.tips {
margin: 30px 20px;
font-size: 12px;
color: #333;
line-height: 20px;
}
.form-icon {
::v-deep .uni-icons {
margin-left: 10px;
color: #01a4fe !important;
}
}
.form-right-icon {
::v-deep .uni-icons {
margin-right: 10px;
}
}
</style>

269
pages/login/login-pay.vue

@ -1,269 +0,0 @@
<template>
<view style="background-color: #fff; height: calc(100vh);">
<view class="top-bar">
<image class="top-bar-bg" src="@/static/images/mingqi-beij@2x.png" mode="aspectFill"></image>
<view class="library-info">
<image class="avatar" src="@/static/images/avatar.png" mode="aspectFill"></image>
<view class="library-name">葛店经济技术开发区图书馆</view>
</view>
</view>
<!-- 支付分状态展示 -->
<view class="pay-score-box" v-if="payScore">
<view class="score-item">
<uni-icons type="info" size="24" color="#01a4fe"></uni-icons>
<view class="score-text">
微信支付分:<text style="color:#e74c3c; font-size:16px; font-weight:bold;">{{ payScore }}</text> 分
</view>
</view>
<view class="score-desc" :style="{color: payScore >= 550 ? '#07c160' : '#e74c3c'}">
{{ payScore >= 550 ? '✅ 符合办证条件(≥550分)' : '❌ 支付分不足,暂无法办证' }}
</view>
</view>
<!-- 未获取支付分时显示授权按钮 -->
<view class="auth-box" v-else>
<button class="auth-btn" @click="getPayScore">
授权查询微信支付分(办证必备)
</button>
</view>
<!-- 表单:只有支付分达标才显示 -->
<view class="form-box" v-if="payScore && payScore >= 550">
<view class="item">
<uni-icons class="form-icon" type="person" size="24"></uni-icons>
<input class="input" placeholder="请输入读者证号" v-model="cardNo" />
</view>
<view class="item">
<uni-icons class="form-icon" type="locked" size="24"></uni-icons>
<input class="input" placeholder="请输入密码" :password="!showPwd" v-model="password" />
<uni-icons class="form-right-icon" :type="showPwd ? 'eye-slash' : 'eye'" size="20" @click="togglePwd"></uni-icons>
</view>
<button class="login-btn" type="primary" @click="submit">绑定</button>
</view>
<view class="tips">
温馨提示:<br />
1、密码默认为<text style="color:#e74c3c">身份证后6位</text>,X大写;<br />
2、微信支付分≥550分方可办理读者证。
</view>
</view>
</template>
<script>
const TOKEN_KEY = 'token';
const USER_KEY = 'user-info';
export default {
data() {
return {
cardNo: '',
password: '',
showPwd: false,
payScore: null, // 微信支付分
serviceId: '你的支付分service_id' // 微信支付分后台获取
}
},
onLoad() {
// 页面加载可自动查支付分(也可用户点击)
// this.getPayScore()
},
methods: {
togglePwd() {
this.showPwd = !this.showPwd
},
// 1. 获取微信支付分(核心)
async getPayScore() {
const _this = this
uni.showLoading({ title: '获取支付分中...' })
// 1) 后端获取支付分授权参数(必须后端签名)
uni.request({
url: 'https://你的后端.com/api/pay-score/auth',
method: 'POST',
data: {
openid: uni.getStorageSync('wxSignature') || '', // 用户openid
service_id: this.serviceId
},
success: (res) => {
if (res.data.code !== 200) {
uni.showToast({ title: res.data.msg || '获取失败', icon: 'none' })
return
}
const { package: packageStr, out_order_no } = res.data.data
// 2) 前端拉起支付分授权(微信原生API)
uni.requestPayment({
provider: 'wxpay',
type: 'payScore',
timeStamp: String(Date.now()),
package: packageStr,
// 以下由后端返回
signType: 'HMAC-SHA256',
paySign: res.data.data.paySign,
nonceStr: res.data.data.nonceStr,
success: () => {
// 3) 授权成功 → 查询分数
_this.queryPayScore(out_order_no)
},
fail: (err) => {
console.log('支付分授权失败', err)
uni.showToast({ title: '授权取消或失败', icon: 'none' })
},
complete: () => {
uni.hideLoading()
}
})
},
fail: () => {
uni.hideLoading()
uni.showToast({ title: '网络异常', icon: 'none' })
}
})
},
// 2. 查询支付分分数(后端接口)
async queryPayScore(outOrderNo) {
uni.showLoading({ title: '查询分数...' })
uni.request({
url: 'https://你的后端.com/api/pay-score/query',
method: 'POST',
data: { out_order_no: outOrderNo },
success: (res) => {
uni.hideLoading()
if (res.data.code === 200) {
this.payScore = res.data.data.score // 例如 680
} else {
uni.showToast({ title: '查询失败', icon: 'none' })
}
},
fail: () => {
uni.hideLoading()
uni.showToast({ title: '查询失败', icon: 'none' })
}
})
},
// 3. 原绑定逻辑(增加支付分判断)
async submit() {
// 先判断支付分
if (!this.payScore || this.payScore < 550) {
uni.showToast({ title: '支付分不足550,无法办证', icon: 'none' })
return
}
if (!this.cardNo || !this.password) {
uni.showToast({ title: '请输入读者证号和密码', icon: 'none' })
return
}
// 以下为原登录逻辑(略)
uni.showLoading({ title: '绑定中...' })
setTimeout(() => {
const res = {
token: 'login-token-' + Date.now(),
user: {
nickName: '读者',
cardNo: this.cardNo,
payScore: this.payScore // 可一并保存
}
}
uni.setStorageSync(TOKEN_KEY, res.token)
uni.setStorageSync(USER_KEY, res.user)
uni.hideLoading()
uni.showToast({ title: '绑定成功', icon: 'success' })
uni.navigateBack()
}, 800)
}
}
}
</script>
<style lang="scss" scoped>
.page {
padding: 40px 20px;
}
.form-box {
padding: 20px;
.item {
width: 100%;
min-height: 42px;
border-radius: 23px;
background-color: #f7f7f7;
border: 1px solid #f4f4f4;
display: flex;
flex-direction: row;
align-items: center;
justify-content: flex-start;
margin-bottom: 20px;
overflow: hidden;
.t-right-icon{
margin-right: 15px;
margin-left: 0;
}
.input {
flex: 1;
border-radius: 8px;
padding: 12px 15px;
font-size: 13px;
}
}
}
.login-btn {
margin-top: 20px;
background-color: #01a4fe;
border-radius: 23px;
}
.tips {
margin: 50px 30px;
font-size: 11px;
color: #000;
line-height: 22px;
}
.form-icon {
::v-deep .uni-icons{
margin-left: 10px;
color: #01a4fe !important;
}
}
.form-right-icon{
::v-deep .uni-icons{
margin-right: 10px;
}
}
/* 新增支付分样式 */
.pay-score-box {
padding: 20px;
margin: 10px 20px;
background: #f7f9ff;
border-radius: 12px;
}
.score-item {
display: flex;
align-items: center;
margin-bottom: 8px;
}
.score-text {
margin-left: 10px;
font-size: 15px;
}
.score-desc {
font-size: 13px;
padding-left: 34px;
}
.auth-box {
padding: 40px 20px;
text-align: center;
}
.auth-btn {
background: #01a4fe;
color: #fff;
border-radius: 23px;
padding: 12px 30px;
border: none;
}
</style>

157
subpkg/pages/activity-list/activity-list copy.vue

@ -1,157 +0,0 @@
<template>
<view class="activity-list">
<!-- 下拉刷新区域 -->
<scroll-view
scroll-y
refresher-enabled
:refresher-triggered="refreshing"
@refresherrefresh="onRefresh"
@scrolltolower="onLoadMore"
lower-threshold="100"
>
<view class="activity-item" v-for="(item, index) in activityList" @click="toActivityDetail(item)" :key="index">
<image class="activity-img" :src="item.imgUrl"></image>
<view class="activity-info">
<view class="activity-info-left">
<text class="title">{{ item.title }}</text>
<view class="time">
<uni-icons class="time-icon" custom-prefix="iconfont" type="icon-shijian" size="15"></uni-icons>
<text>{{ item.time }}</text>
</view>
</view>
<button
class="activity-btn"
:class="item.status === 0 ? 'disabled-btn' : ''"
type="primary"
:disabled="item.status === 0"
>
{{ item.status === 1 ? '立即参加' : '活动结束' }}
</button>
</view>
</view>
<!-- 加载提示 -->
<view class="load-tip" v-if="loading">
<text>加载中...</text>
</view>
<view class="load-tip" v-if="noMore">
<text>没有更多数据了</text>
</view>
</scroll-view>
</view>
</template>
<script>
export default {
data() {
return {
activityList: [], // 活动列表数据
refreshing: false, // 下拉刷新状态
loading: false, // 加载中
noMore: false, // 是否没有更多数据
page: 1, // 当前页码
pageSize: 5, // 每页条数
total: 20, // 模拟总数据量
};
},
onLoad() {
// 页面加载时获取第一页数据
this.getActivityList();
},
methods: {
/**
* 模拟请求活动列表接口
* @param {Number} page 页码
* @param {Number} pageSize 每页条数
*/
getActivityList() {
this.loading = true;
// 模拟网络请求延迟
setTimeout(() => {
// 生成模拟数据
const list = this.mockData(this.page, this.pageSize);
// 下拉刷新:直接替换数据
if (this.refreshing) {
this.activityList = list;
this.refreshing = false;
} else {
// 上拉加载:追加数据
this.activityList = [...this.activityList, ...list];
}
// 判断是否还有更多数据
if (this.activityList.length >= this.total) {
this.noMore = true;
} else {
this.noMore = false;
}
this.loading = false;
}, 1000);
},
/**
* 模拟生成活动数据
*/
mockData(page, pageSize) {
let arr = [];
for (let i = 0; i < pageSize; i++) {
const num = (page - 1) * pageSize + i + 1;
arr.push({
imgUrl: "https://qiniu.aiyxlib.com/1605060269830",
title: `活动标题${num}`,
time: "2025-11-03 00:00 ~2025-11-09 00:00",
status: num % 3 === 0 ? 0 : 1, // 0=结束 1=可参加
});
}
return arr;
},
/**
* 下拉刷新
*/
onRefresh() {
// 重置状态
this.page = 1;
this.noMore = false;
this.refreshing = true;
this.getActivityList();
},
/**
* 上拉加载更多
*/
onLoadMore() {
if (this.loading || this.noMore) return;
this.page++;
this.getActivityList();
},
toActivityDetail(item){
uni.navigateTo({
url: "/subpkg/pages/activity-detail/activity-detail?title=" + item.title
})
}
},
};
</script>
<style lang="scss" scoped>
.activity-list {
padding: 20px;
height: 100vh;
box-sizing: border-box;
}
scroll-view {
height: 100%;
}
.load-tip {
text-align: center;
padding: 20px;
color: #999;
font-size: 14px;
}
</style>

317
subpkg/pages/collect-list/collect-list3.vue

@ -1,317 +0,0 @@
<template>
<view class="collection-container">
<!-- 吸顶 Tab -->
<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>
<!-- 图书收藏 -->
<view class="recommendation-list">
<view
class="book-item"
v-for="(item, index) in listData.book"
:key="index"
@click="goToBookDetail(item)"
v-if="tabItem.status === 'book'"
>
<image class="book-cover" :src="item.cover"></image>
<view class="book-title">{{ item.title }}</view>
</view>
</view>
<!-- 活动收藏 -->
<view
class="activity-item"
v-for="(item, index) in listData.activity"
:key="index"
@click="toActivityDetail(item)"
v-if="tabItem.status === 'activity'"
>
<image class="activity-img" :src="item.imgUrl"></image>
<view class="activity-info">
<view class="activity-info-left">
<text class="title">{{ item.title }}</text>
<view class="time">
<uni-icons class="time-icon" custom-prefix="iconfont" type="time" size="15"></uni-icons>
<text>{{ item.time }}</text>
</view>
</view>
<button
class="activity-btn"
:class="item.status === 0 ? 'disabled-btn' : ''"
type="primary"
:disabled="item.status === 0"
>
{{ item.status === 1 ? '立即参加' : '活动结束' }}
</button>
</view>
</view>
</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";
export default {
components: { myTabs },
data() {
return {
tabData: [
{ label: "图书收藏", status: "book" },
{ label: "活动收藏", status: "activity" },
],
currentIndex: 0,
// 数据结构完全和借阅页一致
listData: {},
loadingMap: {},
loadMoreStatusMap: {},
pageMap: {},
sizeMap: {},
hasMoreMap: {},
swiperHeightData: {},
currentSwiperHeight: 400,
currentPageScrollTop: 0,
isRefreshing: false,
};
},
onLoad() {
this.initDataStructure();
const firstTab = this.getCurrentTab();
this.getListData(firstTab.status);
},
onShow() {
const tabIndex = uni.getStorageSync("switch_tab_index");
if (tabIndex !== "") {
this.currentIndex = Number(tabIndex);
const currentTab = this.getCurrentTab();
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) {
if (!isRefresh && this.listData[statusKey]?.length > 0) {
this.loadingMap[statusKey] = false;
return;
}
this.loadingMap[statusKey] = true;
this.pageMap[statusKey] = 1;
try {
const res = await this.getCollectData(statusKey);
this.listData[statusKey] = res;
this.hasMoreMap[statusKey] = res.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(() => 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;
try {
const newData = await this.getCollectData(statusKey);
if (newData.length > 0) {
this.listData[statusKey] = [...this.listData[statusKey], ...newData];
this.hasMoreMap[statusKey] = newData.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(() => this.calcSwiperHeight(statusKey), 0);
},
// 模拟接口(图书/活动收藏)
getCollectData(type) {
return new Promise((resolve) => {
setTimeout(() => {
if (type === "book") {
resolve([
{ isbn: "1", title: "JavaScript高级程序设计", cover: "https://qiniu.aiyxlib.com/1606124577077" },
{ isbn: "2", title: "Vue3实战", cover: "https://qiniu.aiyxlib.com/1606124577077" },
{ isbn: "3", title: "深入理解计算机系统", cover: "https://qiniu.aiyxlib.com/1606124577077" },
{ isbn: "1", title: "JavaScript高级程序设计", cover: "https://qiniu.aiyxlib.com/1606124577077" }
]);
} else {
resolve([
{ imgUrl: "https://qiniu.aiyxlib.com/1605060269830", title: "读书分享会", time: "2025-11-03", status: 1 },
{ imgUrl: "https://qiniu.aiyxlib.com/1605060269830", title: "作者见面会", time: "2025-10-01", status: 0 },
]);
}
}, 300);
});
},
refreshList(statusKey) {
this.getListData(statusKey, true);
},
// 计算swiper高度
calcSwiperHeight(statusKey) {
const selector = statusKey === "book" ? ".book-item" : ".activity-item";
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 + 10, 0);
this.swiperHeightData[statusKey] = total;
this.currentSwiperHeight = total;
}).exec();
},
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;
},
goToBookDetail(item) {
uni.navigateTo({ url: `/subpkg/pages/book-detail/book-detail?isbn=${item.isbn}` });
},
toActivityDetail(item) {
uni.navigateTo({ url: `/subpkg/pages/activity-detail/activity-detail?title=${item.title}` });
},
},
};
</script>
<style lang="scss" scoped>
.collection-container {
background-color: #f5f5f5;
min-height: 100vh;
.tab-sticky {
position: sticky;
top: 0;
z-index: 99;
background: #fff;
}
.swiper {
width: 100%;
min-height: 300px;
}
.list-wrapper {
padding: 10px;
}
.empty {
text-align: center;
padding: 100px 0;
color: #999;
font-size: 14px;
}
.recommendation-list{
flex-wrap: wrap;
justify-content: flex-start;
.book-item {
margin-bottom: 16px;
}
}
}
::v-deep .uni-load-more {
height: auto !important;
}
</style>

335
subpkg/pages/myLending/myLending copy.vue

@ -1,335 +0,0 @@
<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>
<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 { FetchInitScreenSetting } from '@/api/user';
import { FetchHistoryloan, FetchRdloanlist } from '@/api/book';
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 {
screenConfig: {},
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: {},
loadMoreStatusMap: {},
pageMap: {},
sizeMap: {},
hasMoreMap: {},
swiperHeightData: {},
currentSwiperHeight: 400,
currentPageScrollTop: 0,
isRefreshing: false
};
},
onLoad() {
this.getScreenSetting();
this.initDataStructure();
},
onShow() {
const tabIndex = uni.getStorageSync('switch_tab_index');
if (tabIndex !== undefined && tabIndex !== '') {
this.currentIndex = Number(tabIndex);
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: {
async getScreenSetting() {
try {
const res = await FetchInitScreenSetting({ libcode: '1201' });
const data = res.data;
this.screenConfig = {
thirdUrl: data.open_lib_http?.context || '',
thirdAppid: data.open_lib_appId?.context || '',
thirdSecret: data.open_lib_secret?.context || '',
sm4Key: data.sm4_key?.context || ''
};
} catch (err) {
console.error('获取配置失败:', err);
}
},
// 获取 在借中 列表(字段已对齐)
async getRealBorrowList(statusKey) {
this.loadingMap[statusKey] = true;
try {
const rdid = uni.getStorageSync('currentReaderCard');
const params = { ...this.screenConfig, rdid };
const res = await FetchRdloanlist(params);
const result = typeof res.data === 'string' ? JSON.parse(res.data) : res.data;
let loanList = result.loanlist || [];
// 对齐字段:loandate → loantime,returndate → returntime
loanList = loanList.map(item => {
const loantime = item.loandate || '';
const returntime = item.returndate || '';
const now = new Date();
const returnDate = new Date(returntime);
const diffDay = Math.ceil((returnDate - now) / (1000 * 3600 * 24));
let returnBook = 0;
if (diffDay < 0) {
returnBook = 3;
} else if (diffDay <= 3) {
returnBook = 1;
} else {
returnBook = 0;
}
return {
...item,
loantime,
returntime,
startTime: loantime,
returnTime: returntime,
returnBook
};
});
console.log('当前借阅', loanList);
this.listData['lending'] = loanList;
this.listData['expiring'] = loanList.filter(i => i.returnBook === 1);
this.listData['expired'] = loanList.filter(i => i.returnBook === 3);
this.hasMoreMap[statusKey] = false;
this.loadMoreStatusMap[statusKey] = "no-more";
} catch (err) {
console.error('获取在借列表失败', err);
this.listData[statusKey] = [];
} finally {
this.loadingMap[statusKey] = false;
this.isRefreshing = false;
uni.stopPullDownRefresh();
setTimeout(() => this.calcSwiperHeight(statusKey), 0);
}
},
// 获取 全部/历史 列表
async getHistoryList(statusKey) {
this.loadingMap[statusKey] = true;
try {
const rdid = uni.getStorageSync('currentReaderCard');
const params = { ...this.screenConfig, rdid, logtype:'30002' };
const res = await FetchHistoryloan(params);
const result = typeof res.data === 'string' ? JSON.parse(res.data) : res.data;
let loanList = result.hloanlist || [];
loanList = loanList.map(item => {
return {
...item,
returnTime: item.returntime || '',
startTime: item.loantime || item.optime || '',
returnBook: 2
};
});
console.log('历史借阅', loanList);
this.listData[statusKey] = loanList;
this.hasMoreMap[statusKey] = false;
this.loadMoreStatusMap[statusKey] = "no-more";
} catch (err) {
console.error('获取历史列表失败', err);
this.listData[statusKey] = [];
} finally {
this.loadingMap[statusKey] = false;
this.isRefreshing = false;
uni.stopPullDownRefresh();
setTimeout(() => this.calcSwiperHeight(statusKey), 0);
}
},
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) {
if (statusKey === 'all') {
await this.getHistoryList(statusKey);
}
if (statusKey === 'lending') {
await this.getRealBorrowList(statusKey);
}
if (statusKey === 'expiring' || statusKey === 'expired') {
await this.getRealBorrowList('lending');
this.loadingMap[statusKey] = false;
}
},
loadMoreList(statusKey) {
this.loadMoreStatusMap[statusKey] = "no-more";
},
refreshList(statusKey) {
this.getListData(statusKey);
},
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();
},
tabClick(index) {
this.currentIndex = index;
const tab = this.getCurrentTab();
if (this.currentPageScrollTop > 100) {
uni.pageScrollTo({ scrollTop: 0, duration: 100 });
}
this.getListData(tab.status);
},
onSwiperChange(e) {
if (e.detail.source === "touch") {
this.currentIndex = e.detail.current;
}
},
onSwiperEnd() {
const tab = this.getCurrentTab();
this.getListData(tab.status);
},
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>

480
subpkg/pages/myLending/myLending-addpage.vue

@ -1,480 +0,0 @@
<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.loadingMap[firstTab.status] = true;
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.loadingMap[currentTab.status] = true;
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, false);
this.$set(this.loadMoreStatusMap, key, "");
this.$set(this.pageMap, key, 1);
// 在借中每页4条,其他tab每页10条
this.$set(this.sizeMap, key, key === 'lending' ? 4 : 10);
this.$set(this.hasMoreMap, key, true);
});
},
getCurrentTab() {
return this.tabData[this.currentIndex];
},
// 处理借阅数据
processBorrowData(list) {
return 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;
});
},
// 获取列表(首次/刷新)
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]);
console.log('data',data)
let list = data.records || [];
list = this.processBorrowData(list);
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();
this.$nextTick(() => {
this.calcSwiperHeight(statusKey);
});
}
},
// 加载更多
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 = this.processBorrowData(newList);
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] = "";
} finally {
this.$nextTick(() => {
this.calcSwiperHeight(statusKey);
});
}
},
// 下拉刷新
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" // 提前归还 → 准时
},
{
imgCover: "https://qiniu.aiyxlib.com/1606124577077",
title: "水浒传",
nickname: "施耐庵",
publish: "人民文学出版社",
isbn: "1005",
desc: '精心提炼20种GPT提问方法及指令,从入门到进阶再到精通,100个。',
startTime: "2026-03-05",
returnTime: "2026-05-05",
realityTime: ""
},
{
imgCover: "https://qiniu.aiyxlib.com/1606124577077",
title: "三国演义",
nickname: "罗贯中",
publish: "人民文学出版社",
isbn: "1006",
desc: '精心提炼20种GPT提问方法及指令,从入门到进阶再到精通,100个。',
startTime: "2026-03-15",
returnTime: "2026-05-15",
realityTime: ""
},
{
imgCover: "https://qiniu.aiyxlib.com/1606124577077",
title: "活着",
nickname: "余华",
publish: "作家出版社",
isbn: "1007",
desc: '精心提炼20种GPT提问方法及指令,从入门到进阶再到精通,100个。',
startTime: "2026-03-20",
returnTime: "2026-05-20",
realityTime: ""
},
{
imgCover: "https://qiniu.aiyxlib.com/1606124577077",
title: "围城",
nickname: "钱钟书",
publish: "人民文学出版社",
isbn: "1008",
desc: '精心提炼20种GPT提问方法及指令,从入门到进阶再到精通,100个。',
startTime: "2026-03-25",
returnTime: "2026-05-25",
realityTime: ""
}
];
if (pageNum >= 4) { // 第4页开始返回空
return [];
}
// 为在借中tab生成更多数据
let list = [];
if (apiStatus === 0) { // 在借中
// 每页返回指定数量的数据
const start = (pageNum - 1) * pageSize;
const end = start + pageSize;
for (let i = start; i < end; i++) {
const item = base[i % base.length];
list.push({ ...item, isbn: item.isbn + pageNum + i });
}
} else {
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);
}
// 加上uni-load-more组件的高度
total += 80; // 估算uni-load-more组件的高度
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.loadingMap[tab.status] = true;
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.loadingMap[tab.status] = true;
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>

225
subpkg/pages/myLending/myLending-old.vue

@ -1,225 +0,0 @@
<template>
<view class="lending-container">
<!-- Tabs 吸顶 -->
<view class="tab-sticky">
<my-tabs
:tabData="tabData"
:defaultIndex="currentIndex"
:config="{ textColor: '#333' }"
@tabClick="tabClick"
/>
</view>
<!-- Swiper 内容区 -->
<swiper
class="swiper"
:current="currentIndex"
:style="{ height: currentSwiperHeight + 'px' }"
@animationfinish="onSwiperEnd"
@change="onSwiperChange"
duration="300"
>
<swiper-item class="swiper-item" v-for="(tabItem, tabIndex) in tabData" :key="tabIndex">
<view class="list-wrapper">
<!-- 加载中 -->
<uni-load-more status="loading" v-if="loadingMap[tabIndex]" />
<!-- 空数据 -->
<view class="empty" v-else-if="!listData[tabIndex] || listData[tabIndex].length === 0">
暂无借阅记录
</view>
<!-- 图书列表 -->
<block v-else>
<book-list-item
:class="'list-item-' + tabIndex"
v-for="(item, index) in listData[tabIndex]"
: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: "在借中" },
{ label: "将过期" },
{ label: "已过期" }
],
currentIndex: 0,
// 列表数据缓存
listData: {
0: [],
1: [],
2: []
},
// 每个页面的加载状态
loadingMap: {
0: true,
1: true,
2: true
},
// Swiper 高度自适应
currentSwiperHeight: 400,
swiperHeightData: {},
// 页面滚动
currentPageScrollTop: 0
};
},
onLoad() {
this.getListData(0);
},
onPageScroll(res) {
this.currentPageScrollTop = res.scrollTop;
},
methods: {
async getListData(index) {
this.loadingMap[index] = true;
setTimeout(() => {
const mockData = this.getMockListByStatus(index);
this.listData[index] = mockData;
this.loadingMap[index] = false;
this.$nextTick(() => {
this.calcSwiperHeight(index);
});
}, 600);
},
getMockListByStatus(status) {
const baseList = [
{
imgCover: "https://qiniu.aiyxlib.com/1606124577077",
title: "名侦探柯南",
nickname: "青山刚昌",
publish: "长春出版社",
isbn: "1001"
},
{
imgCover: "https://qiniu.aiyxlib.com/1606124577077",
title: "三体",
nickname: "刘慈欣",
publish: "重庆出版社",
isbn: "1002"
}
];
if (status === 0) return baseList;
if (status === 1) return [baseList[0]];
if (status === 2) return [];
return [];
},
calcSwiperHeight(index) {
const query = uni.createSelectorQuery().in(this);
query
.selectAll(`.list-item-${index}`)
.boundingClientRect((res) => {
let totalHeight = 0;
if (res && res.length) {
res.forEach((item) => {
totalHeight += item.height + 8;
});
} else {
totalHeight = 200;
}
this.swiperHeightData[index] = totalHeight;
this.currentSwiperHeight = totalHeight;
})
.exec();
},
tabClick(index) {
this.currentIndex = index;
if (this.currentPageScrollTop > 100) {
uni.pageScrollTo({ scrollTop: 100, duration: 100 });
}
if (!this.listData[index] || this.listData[index].length === 0) {
this.getListData(index);
} else {
this.currentSwiperHeight = this.swiperHeightData[index] || 400;
}
},
onSwiperChange(e) {
if (e.detail.source === "touch") {
this.currentIndex = e.detail.current;
}
},
onSwiperEnd() {
const index = this.currentIndex;
if (!this.listData[index] || this.listData[index].length === 0) {
this.getListData(index);
} else {
this.currentSwiperHeight = this.swiperHeightData[index] || 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>

246
subpkg/pages/myLending/myLending-old2.vue

@ -1,246 +0,0 @@
<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>

151
subpkg/pages/reader-card/reader-card copy.vue

@ -1,151 +0,0 @@
<template>
<view class="reader-card">
<image class="card-top-bg" src="@/static/images/card-img1.png" mode="widthFix" />
<view class="card-list">
<view
class="card-list-item"
v-for="(item, index) in cardList"
:key="item.id"
@click="handleSelectItem(item.bindValue)"
:class="{ active: selectedValue === item.bindValue }"
>
<image class="card-left-img" src="@/static/images/card-img2.png" mode="widthFix" />
<view class="card-right-info">
<text class="info-title">读者证号</text>
<text class="info-num">{{ item.bindValue }}</text>
</view>
<radio :value="item.bindValue" :checked="selectedValue === item.bindValue"/>
</view>
<view class="add-card-btn" @click="toAddReaderCard">
<uni-icons type="plus" size="20" color="#01a4fe"></uni-icons>
<text>新增读者证</text>
</view>
</view>
</view>
</template>
<script>
import { FetchFindAllReaderBindByOpenId, FetchSetDefaultReadCard } from '@/api/user';
const READLIST = 'reader-card-list';
export default {
data() {
return {
selectedValue: '',
cardList: [],
}
},
onLoad() {},
onShow() {
this.getBindReaderCardList();
},
methods: {
async getBindReaderCardList() {
try {
const openId = uni.getStorageSync('wx_login_code');
if (!openId) {
uni.showToast({ title: '未获取到用户信息', icon: 'none' });
return;
}
const data = { libcode: '1201', openId: openId };
const res = await FetchFindAllReaderBindByOpenId(data);
if (res.code === 200 && res.data.length > 0) {
this.cardList = res.data;
uni.setStorageSync(READLIST, res.data);
// 优先 bindDefault = true
const defaultCard = this.cardList.find(item => item.bindDefault === true);
this.selectedValue = defaultCard ? defaultCard.bindValue : this.cardList[0].bindValue;
} else {
this.cardList = [];
uni.setStorageSync(READLIST, []);
this.selectedValue = '';
}
} catch (err) {
this.cardList = uni.getStorageSync(READLIST) || [];
const defaultCard = this.cardList.find(item => item.bindDefault === true);
this.selectedValue = defaultCard ? defaultCard.bindValue : this.cardList[0]?.bindValue || '';
}
},
// 点击整个卡片 → 触发切换
handleSelectItem(value) {
const oldValue = this.selectedValue;
const openId = uni.getStorageSync('wx_login_code');
if (value === oldValue) return;
uni.showModal({
title: '提示',
content: '确定切换默认读者证吗?',
success: async (res) => {
if (!res.confirm) {
this.selectedValue = oldValue;
return;
}
try {
const result = await FetchSetDefaultReadCard({
bindType: 'rdid',
bindValue: value,
libcode: '1201',
openid: openId
});
if (result.code === 200) {
this.selectedValue = value;
uni.setStorageSync('currentReaderCard', value);
uni.showToast({ title: '默认证切换成功', icon: 'success' });
} else {
this.selectedValue = oldValue;
uni.showToast({ title: result.msg || '切换失败', icon: 'none' });
}
} catch (err) {
this.selectedValue = oldValue;
uni.showToast({ title: '切换失败', icon: 'none' });
}
}
});
},
toAddReaderCard() {
uni.navigateTo({ url: "/pages/login/login" });
},
}
}
</script>
<style lang="scss" scoped>
.reader-card{
position: relative;
min-height: 100vh;
background-color: #f5f5f5;
.card-top-bg{
position: absolute;
left: 0;
top: 0;
width: 100%;
display: block;
}
.add-card-btn {
display: flex;
align-items: center;
justify-content: center;
margin-top: 15px;
height: 44px;
border: 1px dashed #01a4fe;
border-radius: 8px;
color: #01a4fe;
font-size: 15px;
uni-icons {
margin-right: 6px;
}
}
}
</style>

128
subpkg/pages/scan-book/scan-book.vue

@ -1,128 +0,0 @@
<template>
<view class="container">
<!-- 扫码按钮 -->
<button type="primary" @click="scanCode" class="scan-btn">
扫码识别图书
</button>
<!-- 图书信息展示 -->
<view class="book-info" v-if="bookData.title">
<image :src="bookData.image" class="book-cover" mode="widthFix"></image>
<view class="book-title">{{ bookData.title }}</view>
<view class="book-desc">作者:{{ bookData.author }}</view>
<view class="book-desc">出版社:{{ bookData.publisher }}</view>
<view class="book-desc">出版日期:{{ bookData.pubdate }}</view>
<view class="book-desc">ISBN:{{ isbn }}</view>
</view>
<!-- 无数据提示 -->
<view class="empty" v-else>
请点击按钮扫描图书条形码
</view>
</view>
</template>
<script>
export default {
data() {
return {
isbn: '', // 扫码得到的ISBN
bookData: {} // 图书信息
}
},
methods: {
// 调用微信扫码
async scanCode() {
try {
// 只允许扫条形码(图书ISBN)
const res = await uni.scanCode({
onlyFromCamera: true,
scanType: ['barCode']
})
// res.result 就是扫码得到的ISBN编码
// this.isbn = res.result
this.isbn = '9787020167319'
console.log('扫码结果:', this.isbn)
// 根据ISBN查询图书信息
this.getBookInfo()
} catch (err) {
uni.showToast({
title: '扫码失败',
icon: 'none'
})
}
},
// 查询图书信息(免费公开接口)
async getBookInfo() {
uni.showLoading({ title: '查询中...' })
try {
this.bookData = {
title: "活着",
author: "余华",
publisher: "人民文学出版社",
pubdate: "2012-08-01",
image: "https://qiniu.aiyxlib.com/1606124577077"
};
uni.hideLoading();
uni.showToast({ title: "查询成功" });
// const res = await uni.request({
// url: 'https://api.jike.xyz/situ/book/isbn/' + this.isbn,
// method: 'GET'
// })
// if (res.data.code === 200) {
// this.bookData = res.data.data
// uni.showToast({ title: '查询成功' })
// } else {
// uni.showToast({ title: '未查询到图书信息', icon: 'none' })
// }
} catch (err) {
uni.showToast({ title: '查询失败', icon: 'none' })
} finally {
uni.hideLoading()
}
}
}
}
</script>
<style scoped>
.container {
padding: 40rpx;
}
.scan-btn {
margin-bottom: 40rpx;
}
.book-info {
display: flex;
flex-direction: column;
align-items: center;
}
.book-cover {
width: 300rpx;
height: 420rpx;
margin-bottom: 20rpx;
border: 1rpx solid #eee;
}
.book-title {
font-size: 36rpx;
font-weight: bold;
margin-bottom: 15rpx;
}
.book-desc {
font-size: 28rpx;
color: #666;
margin-bottom: 10rpx;
}
.empty {
text-align: center;
color: #999;
margin-top: 100rpx;
}
</style>
Loading…
Cancel
Save