14 changed files with 0 additions and 3460 deletions
-
6pages.json
-
273pages/lendCar/lendCar copy.vue
-
324pages/login/login copy 2.vue
-
252pages/login/login copy.vue
-
297pages/login/login-no-clear.vue
-
269pages/login/login-pay.vue
-
157subpkg/pages/activity-list/activity-list copy.vue
-
317subpkg/pages/collect-list/collect-list3.vue
-
335subpkg/pages/myLending/myLending copy.vue
-
480subpkg/pages/myLending/myLending-addpage.vue
-
225subpkg/pages/myLending/myLending-old.vue
-
246subpkg/pages/myLending/myLending-old2.vue
-
151subpkg/pages/reader-card/reader-card copy.vue
-
128subpkg/pages/scan-book/scan-book.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> |
|
||||
@ -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> |
|
||||
@ -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> |
|
||||
@ -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> |
|
||||
@ -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> |
|
||||
@ -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> |
|
||||
@ -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> |
|
||||
@ -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> |
|
||||
@ -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> |
|
||||
@ -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> |
|
||||
@ -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> |
|
||||
@ -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> |
|
||||
@ -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> |
|
||||
Write
Preview
Loading…
Cancel
Save
Reference in new issue