20 changed files with 3076 additions and 523 deletions
-
17api/book.js
-
150api/user.js
-
22components/book-list-item/book-list-item.vue
-
6pages.json
-
411pages/home/home.vue
-
324pages/login/login copy 2.vue
-
252pages/login/login copy.vue
-
297pages/login/login-no-clear.vue
-
269pages/login/login-pay.vue
-
409pages/login/login.vue
-
366pages/user/user.vue
-
2store/modules/user.js
-
11subpkg/pages/book-detail/book-detail.vue
-
74subpkg/pages/book-list/book-list.vue
-
8subpkg/pages/feedback-list/feedback-list.vue
-
480subpkg/pages/myLending/myLending-addpage.vue
-
212subpkg/pages/reader-card/reader-card.vue
-
245subpkg/pages/unbind-card/unbind-card.vue
-
14utils/config.js
-
30utils/request.js
@ -0,0 +1,17 @@ |
|||
import request from '../utils/request'; |
|||
|
|||
// 图书推荐
|
|||
export function FetchInitScreenBookRecommend(data) { |
|||
return request({ |
|||
url: '/api/screenSetting/initScreenBookRecommend', |
|||
data |
|||
}) |
|||
} |
|||
|
|||
|
|||
// export function FetchInitScreenBookRecommend(data) {
|
|||
// return request({
|
|||
// url: '/qyzt/getNewBook',
|
|||
// data
|
|||
// })
|
|||
// }
|
|||
@ -1,53 +1,157 @@ |
|||
import request from '../utils/request'; |
|||
|
|||
/** |
|||
* 用户登录 |
|||
*/ |
|||
export function login(data) { |
|||
// 获取第三方url/appid/Secret/sm4_key
|
|||
export function FetchInitScreenSetting(data) { |
|||
return request({ |
|||
url: '/sys/login', |
|||
url: '/api/screenSetting/initScreenSetting', |
|||
data |
|||
}) |
|||
} |
|||
|
|||
//根据微信code 获取openId
|
|||
export function FetchOpenId(data) { |
|||
return request({ |
|||
url: '/api/weixin/getOpenId', |
|||
data |
|||
}); |
|||
} |
|||
// 根据openid获取所有绑定读者证列表
|
|||
// ?libcode=&openId=
|
|||
export function FetchFindAllReaderBindByOpenId(data) { |
|||
return request({ |
|||
url: '/api/weixin/findAllReaderBindByOpenId', |
|||
data |
|||
}); |
|||
} |
|||
|
|||
// 根据openid获取用户信息
|
|||
// ?libcode=&openId=
|
|||
export function FetchFindAllReaderByOpenId(data) { |
|||
return request({ |
|||
url: '/api/weixin/findReaderByOpenId', |
|||
data |
|||
}); |
|||
} |
|||
|
|||
|
|||
// 读者认证
|
|||
export function FetchReaderList(data) { |
|||
return request({ |
|||
url: '/api/screenSetting/searchreaderlist2', |
|||
data |
|||
}); |
|||
} |
|||
|
|||
// 用户绑定读者证
|
|||
// {
|
|||
// "openid":"小程序用户openID",
|
|||
// "bindValue":"绑定值",
|
|||
// "bindType":"绑定方式(rdid 读者证号 rdname读者姓名 rdcertify身份证号 othercardno其他编号 workcardno工作/学生证号 rdphone电话 rdloginid手机 cardid附属卡(IC卡))",
|
|||
// "libcode":"馆代码"
|
|||
// }
|
|||
export function FetchBindReadCard(data) { |
|||
return request({ |
|||
url: '/api/weixin/bindReadCard', |
|||
method: 'POST', |
|||
data |
|||
}); |
|||
} |
|||
|
|||
/** |
|||
* 关注用户 |
|||
*/ |
|||
export function userFollow(data) { |
|||
// 绑定用户
|
|||
// {
|
|||
// "avatar": "头像",
|
|||
// "libcode": "馆代码",
|
|||
// "nickname": "昵称",
|
|||
// "openid": ""
|
|||
// }
|
|||
export function FetchBindRead(data) { |
|||
return request({ |
|||
url: '/user/follow', |
|||
url: '/api/weixin/bindReader', |
|||
method: 'POST', |
|||
data |
|||
}); |
|||
} |
|||
|
|||
/** |
|||
* 发表评论 |
|||
*/ |
|||
export function userArticleComment(data) { |
|||
// 设置默认读者证
|
|||
// {
|
|||
// "bindType": "string",
|
|||
// "bindValue": "string",
|
|||
// "libcode": "string",
|
|||
// "openid": "string"
|
|||
// }
|
|||
export function FetchSetDefaultReadCard(data) { |
|||
return request({ |
|||
url: '/user/article/comment', |
|||
url: '/api/weixin/setDefaultReadCard', |
|||
method: 'POST', |
|||
data |
|||
}); |
|||
} |
|||
|
|||
|
|||
/** |
|||
* 用户点赞 |
|||
*/ |
|||
export function userPraise(data) { |
|||
// 用户解绑读者证
|
|||
// {
|
|||
// "bindType": "string",
|
|||
// "bindValue": "string",
|
|||
// "libcode": "string",
|
|||
// "openid": "string"
|
|||
// }
|
|||
export function FetchUnbindReadCard(data) { |
|||
return request({ |
|||
url: '/user/praise', |
|||
url: '/api/weixin/unbindReadCard', |
|||
method: 'POST', |
|||
data |
|||
}); |
|||
} |
|||
|
|||
|
|||
/** |
|||
* 用户收藏 |
|||
* 用户登录 |
|||
*/ |
|||
export function userCollect(data) { |
|||
export function login(data) { |
|||
return request({ |
|||
url: '/user/collect', |
|||
url: '/sys/login', |
|||
method: 'POST', |
|||
data |
|||
}); |
|||
} |
|||
|
|||
/** |
|||
* 关注用户 |
|||
*/ |
|||
// export function userFollow(data) {
|
|||
// return request({
|
|||
// url: '/user/follow',
|
|||
// data
|
|||
// });
|
|||
// }
|
|||
|
|||
/** |
|||
* 发表评论 |
|||
*/ |
|||
// export function userArticleComment(data) {
|
|||
// return request({
|
|||
// url: '/user/article/comment',
|
|||
// method: 'POST',
|
|||
// data
|
|||
// });
|
|||
// }
|
|||
|
|||
/** |
|||
* 用户点赞 |
|||
*/ |
|||
// export function userPraise(data) {
|
|||
// return request({
|
|||
// url: '/user/praise',
|
|||
// data
|
|||
// });
|
|||
// }
|
|||
|
|||
/** |
|||
* 用户收藏 |
|||
*/ |
|||
// export function userCollect(data) {
|
|||
// return request({
|
|||
// url: '/user/collect',
|
|||
// data
|
|||
// });
|
|||
// }
|
|||
@ -0,0 +1,324 @@ |
|||
<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> |
|||
@ -0,0 +1,252 @@ |
|||
<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> |
|||
@ -0,0 +1,297 @@ |
|||
<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> |
|||
@ -0,0 +1,269 @@ |
|||
<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,142 +1,395 @@ |
|||
<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"> |
|||
<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> |
|||
<!-- <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" type="person" size="24" ></uni-icons> |
|||
<input class="input" placeholder="请输入读者证号" v-model="cardNo" /> |
|||
<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="password" /> |
|||
<uni-icons class="form-right-icon" :type="showPwd ? 'eye-slash' : 'eye'" size="20" @click="togglePwd"></uni-icons> |
|||
<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”需要大写; |
|||
温馨提示:<br /> |
|||
1、密码默认为 <text style="color:#e74c3c">身份证后6位</text>,如果身份证号最后一位为“X”,“X”需要大写; |
|||
</view> |
|||
</view> |
|||
</template> |
|||
|
|||
<script> |
|||
const TOKEN_KEY = 'token'; |
|||
const USER_KEY = 'user-info'; |
|||
|
|||
// 引入绑定接口 |
|||
import { FetchInitScreenSetting, FetchReaderList, FetchBindReadCard,FetchFindAllReaderBindByOpenId } from '@/api/user'; |
|||
import config from '@/utils/config'; |
|||
const READLIST = 'reader-card-list'; |
|||
// const USER_KEY = 'user-info'; |
|||
export default { |
|||
data() { |
|||
return { |
|||
cardNo: '', |
|||
password: '', |
|||
showPwd: false // 控制密码显示隐藏,默认隐藏 |
|||
} |
|||
queryvalue: '', |
|||
rdpasswd: '', |
|||
showPwd: false, |
|||
screenConfig: {}, |
|||
avatarUrl: '', |
|||
nickName: '', |
|||
// 清空按钮状态(支持多输入框) |
|||
clearIconStatus: { |
|||
nickName: false, |
|||
queryvalue: false |
|||
} |
|||
}; |
|||
}, |
|||
|
|||
onLoad() { |
|||
this.getScreenSetting(); |
|||
}, |
|||
|
|||
methods: { |
|||
// 切换密码显示隐藏 |
|||
// 获取微信头像 + 上传到服务器 |
|||
onChooseAvatar(e) { |
|||
console.log('获取微信头像', e) |
|||
// 临时文件路径 |
|||
const tempFilePath = e.detail.avatarUrl; |
|||
|
|||
// 开始上传 |
|||
uni.uploadFile({ |
|||
url: config.baseUrl +'/api/fileRelevant/uploadWxAvatarImg', // 你的后端上传地址 |
|||
filePath: tempFilePath, |
|||
name: 'file', // 后端接收的文件字段名,保持一致! |
|||
header: { |
|||
"Content-Type": "multipart/form-data" |
|||
}, |
|||
success: (res) => { |
|||
console.log("上传成功原始返回:", res); |
|||
let realAvatar = ''; |
|||
try { |
|||
const data = JSON.parse(res.data); |
|||
console.log('解析成功:', data) |
|||
realAvatar = data.data || data.url || ''; // 按你后端字段改 |
|||
} catch (e) { |
|||
realAvatar = res.data; // 如果直接返回字符串URL |
|||
} |
|||
if (realAvatar) { |
|||
console.log("永久头像URL:", realAvatar); |
|||
this.avatarUrl = config.baseUrl + '/api/fileRelevant/getImg?imgType=5&imgId=' + realAvatar |
|||
} else { |
|||
uni.showToast({ title: '头像上传失败', icon: 'none' }); |
|||
} |
|||
}, |
|||
fail: (err) => { |
|||
console.log("上传失败", err); |
|||
uni.showToast({ title: '头像上传失败', icon: 'none' }); |
|||
} |
|||
}); |
|||
}, |
|||
|
|||
// 统一监听输入(自动控制清空按钮显示隐藏) |
|||
handleInput(field) { |
|||
this.clearIconStatus[field] = this[field].trim().length > 0 |
|||
}, |
|||
|
|||
// 统一清空输入框内容 |
|||
clearInput(field) { |
|||
this[field] = '' |
|||
this.clearIconStatus[field] = false |
|||
}, |
|||
|
|||
// 切换密码显隐 |
|||
togglePwd() { |
|||
this.showPwd = !this.showPwd |
|||
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.cardNo || !this.password) { |
|||
uni.showToast({ title: '请输入读者证号和密码', icon: 'none' }) |
|||
return |
|||
// 去除首尾空格 |
|||
this.nickName = this.nickName.trim() |
|||
this.queryvalue = this.queryvalue.trim() |
|||
|
|||
if (!this.queryvalue || !this.rdpasswd) { |
|||
uni.showToast({ title: '请输入读者证号和密码', icon: 'none' }); |
|||
return; |
|||
} |
|||
|
|||
uni.showLoading({ title: '绑定中...' }); |
|||
|
|||
console.log('this.cardNo',this.cardNo) |
|||
console.log('this.password',this.password) |
|||
// 取出微信信息 |
|||
const wxUser = uni.getStorageSync('wxUserInfo') || {} |
|||
console.log('wxUser',wxUser) |
|||
const wxSign = uni.getStorageSync('wxSignature') || 'mock-openid' |
|||
console.log('wxSign',wxSign) |
|||
|
|||
uni.showLoading({ title: '登录中...' }) |
|||
|
|||
// 模拟调用 /sys/login 接口 |
|||
setTimeout(() => { |
|||
// 模拟返回 |
|||
const res = { |
|||
token: 'login-token-' + Date.now(), |
|||
user: { |
|||
nickName: wxUser.nickName || '读者', |
|||
avatarUrl: wxUser.avatarUrl || '', |
|||
cardNo: this.cardNo |
|||
} |
|||
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; |
|||
} |
|||
|
|||
// 保存登录状态 |
|||
uni.setStorageSync(TOKEN_KEY, res.token) |
|||
uni.setStorageSync(USER_KEY, res.user) |
|||
// 统一提取错误信息 |
|||
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) { |
|||
|
|||
uni.hideLoading() |
|||
uni.showToast({ title: '登录成功', icon: 'success' }) |
|||
|
|||
|
|||
// 返回 user 页面 |
|||
uni.navigateBack() |
|||
}, 800) |
|||
// 开始绑定 |
|||
const openId = uni.getStorageSync("wx_login_code"); |
|||
|
|||
const bindParams = { |
|||
openid: openId, |
|||
bindValue: this.queryvalue, |
|||
bindType: 'rdid', |
|||
libcode: '1201', |
|||
} |
|||
// 调用绑定接口 |
|||
const bindRes = await FetchBindReadCard(bindParams); |
|||
console.log('绑定结果', bindRes); |
|||
|
|||
if(bindRes.code === 200 ){ |
|||
|
|||
uni.hideLoading(); |
|||
uni.showToast({ |
|||
title: bindRes.data || '读者证绑定成功', |
|||
icon: 'success' |
|||
}); |
|||
|
|||
// const loginRes = { |
|||
// token: 'reader-token-' + Date.now(), |
|||
// user: { |
|||
// nickName: this.nickName || '小图', |
|||
// avatarUrl: this.avatarUrl |
|||
// } |
|||
// }; |
|||
|
|||
// uni.setStorageSync(USER_KEY, loginRes.user); |
|||
const data = { |
|||
libcode: '1201', |
|||
openId: openId |
|||
} |
|||
FetchFindAllReaderBindByOpenId(data).then(res => { |
|||
console.log('获取读者证列表',res) |
|||
if (res.code === 200 && res.data.length > 0) { |
|||
uni.setStorageSync(READLIST, res.data); |
|||
} else { |
|||
uni.setStorageSync(READLIST, []); |
|||
} |
|||
}) |
|||
.catch(err => { |
|||
console.error('获取读者证列表失败:', err); |
|||
}) |
|||
|
|||
// 绑定成功后返回上一页 |
|||
setTimeout(() => { |
|||
uni.navigateBack(); |
|||
}, 1500); |
|||
}else{ |
|||
// 绑定失败提示 |
|||
uni.hideLoading() |
|||
uni.showToast({ |
|||
title: bindResult.message || '绑定失败,请重试', |
|||
icon: 'none' |
|||
}) |
|||
} |
|||
} else { |
|||
uni.hideLoading(); |
|||
uni.showToast({ |
|||
title: errMsg || '读者证或密码错误', |
|||
icon: 'none' |
|||
}); |
|||
} |
|||
} catch (err) { |
|||
console.error('绑定流程异常:', err) |
|||
uni.hideLoading(); |
|||
uni.showToast({ title: '网络异常或绑定失败', icon: 'none' }); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
}; |
|||
</script> |
|||
|
|||
<style lang="scss" scoped> |
|||
.page { |
|||
padding: 40px 20px; |
|||
.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; |
|||
padding: 20px 15px; |
|||
|
|||
.item { |
|||
width: 100%; |
|||
min-height: 42px; |
|||
border-radius: 23px; |
|||
min-height: 44px; |
|||
border-radius: 22px; |
|||
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; |
|||
margin-bottom: 15px; |
|||
.input { |
|||
flex: 1; |
|||
padding: 10px; |
|||
font-size: 14px; |
|||
} |
|||
} |
|||
|
|||
.uni-input-wrapper{ |
|||
flex: 1; |
|||
display: flex; |
|||
align-items: center; |
|||
|
|||
.input { |
|||
flex: 1; |
|||
border-radius: 8px; |
|||
padding: 12px 15px; |
|||
font-size: 13px; |
|||
padding: 10px; |
|||
font-size: 14px; |
|||
} |
|||
.clear-icon{ |
|||
padding: 0 12px; |
|||
color: #ccc; |
|||
} |
|||
} |
|||
} |
|||
|
|||
|
|||
.login-btn { |
|||
margin-top: 20px; |
|||
background-color: #01a4fe; |
|||
border-radius: 23px; |
|||
margin-top: 10px; |
|||
background-color: #01a4fe !important; |
|||
border-radius: 22px; |
|||
font-size: 16px; |
|||
height: 44px; |
|||
line-height: 44px; |
|||
} |
|||
|
|||
.tips { |
|||
margin: 50px 30px; |
|||
font-size: 11px; |
|||
color: #000; |
|||
line-height: 22px; |
|||
margin: 30px 20px; |
|||
font-size: 12px; |
|||
color: #333; |
|||
line-height: 20px; |
|||
} |
|||
|
|||
.form-icon { |
|||
::v-deep .uni-icons{ |
|||
::v-deep .uni-icons { |
|||
margin-left: 10px; |
|||
color: #01a4fe !important; |
|||
} |
|||
} |
|||
.form-right-icon{ |
|||
::v-deep .uni-icons{ |
|||
|
|||
.form-right-icon { |
|||
::v-deep .uni-icons { |
|||
margin-right: 10px; |
|||
} |
|||
} |
|||
@ -0,0 +1,480 @@ |
|||
<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> |
|||
@ -0,0 +1,245 @@ |
|||
<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> |
|||
<uni-icons |
|||
type="checkmark" |
|||
size="24" |
|||
color="#01a4fe" |
|||
v-if="selectedValue === item.bindValue" |
|||
></uni-icons> |
|||
</view> |
|||
|
|||
<!-- <view class="add-card-btn" @click="toAddReaderCard"> |
|||
<uni-icons type="plus" size="20" color="#01a4fe"></uni-icons> |
|||
<text>新增读者证</text> |
|||
</view> --> |
|||
</view> |
|||
|
|||
<!-- 底部解绑按钮 --> |
|||
<button |
|||
class="unbind-btn" |
|||
:disabled="!selectedValue" |
|||
:class="{disabled: !selectedValue}" |
|||
@click="handleUnbind" |
|||
> |
|||
确认解绑 |
|||
</button> |
|||
</view> |
|||
</template> |
|||
|
|||
<script> |
|||
import { |
|||
FetchFindAllReaderBindByOpenId, |
|||
FetchUnbindReadCard |
|||
} 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) return; |
|||
|
|||
const res = await FetchFindAllReaderBindByOpenId({ |
|||
libcode: '1201', |
|||
openId: openId |
|||
}); |
|||
|
|||
if (res.code === 200) { |
|||
this.cardList = res.data; |
|||
uni.setStorageSync(READLIST, res.data); |
|||
} else { |
|||
this.cardList = []; |
|||
uni.setStorageSync(READLIST, []); |
|||
} |
|||
} catch (err) { |
|||
this.cardList = uni.getStorageSync(READLIST) || []; |
|||
} |
|||
}, |
|||
|
|||
// 点击选择 |
|||
handleSelectItem(value) { |
|||
// 重复点击取消选择 |
|||
if (this.selectedValue === value) { |
|||
this.selectedValue = ''; |
|||
} else { |
|||
this.selectedValue = value; |
|||
} |
|||
}, |
|||
|
|||
// 确认解绑 |
|||
handleUnbind() { |
|||
if (!this.selectedValue) { |
|||
uni.showToast({ title: '请选择要解绑的读者证', icon: 'none' }); |
|||
return; |
|||
} |
|||
|
|||
const openId = uni.getStorageSync('wx_login_code'); |
|||
if (!openId) return; |
|||
|
|||
uni.showModal({ |
|||
title: '提示', |
|||
content: `确定要解绑【${this.selectedValue}】吗?`, |
|||
success: async (res) => { |
|||
if (res.confirm) { |
|||
try { |
|||
// 解绑接口参数 完全按你要求 |
|||
const result = await FetchUnbindReadCard({ |
|||
bindType: "rdid", |
|||
bindValue: this.selectedValue, |
|||
libcode: "1201", |
|||
openid: openId |
|||
}); |
|||
|
|||
if (result.code === 200) { |
|||
uni.showToast({ title: '解绑成功', icon: 'success' }); |
|||
this.selectedValue = ''; |
|||
this.getBindReaderCardList(); // 刷新列表 |
|||
} else { |
|||
uni.showToast({ title: result.msg || '解绑失败', icon: 'none' }); |
|||
} |
|||
} catch (err) { |
|||
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; |
|||
} |
|||
|
|||
.card-list{ |
|||
position: absolute; |
|||
left: 0; |
|||
top: 60px; |
|||
width: calc(100% - 24px); |
|||
height: calc(100vh - 195px); |
|||
overflow-y: auto; |
|||
padding: 12px; |
|||
z-index: 999; |
|||
|
|||
.card-list-item{ |
|||
display: flex; |
|||
justify-content: space-between; |
|||
align-items: center; |
|||
padding: 10px; |
|||
margin-bottom: 12px; |
|||
border: 2px solid #C6C6E2; |
|||
border-radius: 8px; |
|||
background: rgba(241,241,249,0.4); |
|||
cursor: pointer; |
|||
transition: all 0.2s; |
|||
|
|||
&.active{ |
|||
border-color: #01a4fe; |
|||
background: rgba(1, 164, 254, 0.1); |
|||
} |
|||
|
|||
.card-left-img{ |
|||
width: 80px; |
|||
margin-right: 12px; |
|||
display: block; |
|||
} |
|||
|
|||
.card-right-info{ |
|||
flex: 1; |
|||
display: flex; |
|||
flex-direction: column; |
|||
|
|||
.info-title{ |
|||
font-size: 16px; |
|||
color: #333; |
|||
font-weight: bold; |
|||
padding-bottom: 12px; |
|||
} |
|||
|
|||
.info-num{ |
|||
font-size: 14px; |
|||
color: #999; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
.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; |
|||
} |
|||
} |
|||
|
|||
.unbind-btn{ |
|||
position: fixed; |
|||
bottom: 40px; |
|||
left: 0; |
|||
right: 0; |
|||
width: calc(100% - 40px); |
|||
margin: 0 auto; |
|||
padding: 4px 0; |
|||
color: #fff; |
|||
background-color: #01a4fe; |
|||
border-radius: 23px; |
|||
font-size: 16px; |
|||
border: none; |
|||
&.disabled{ |
|||
background-color: #ccc; |
|||
} |
|||
} |
|||
} |
|||
</style> |
|||
@ -0,0 +1,14 @@ |
|||
const CONFIG = { |
|||
// 开发环境配置
|
|||
development: { |
|||
// assetsPath: 'https://m.siccat.com/public/static/images/', // 静态资源路径
|
|||
baseUrl: 'http://192.168.99.63:14000', // 后台接口请求地址
|
|||
}, |
|||
// 生产环境配置
|
|||
production: { |
|||
// assetsPath: 'https://m.siccat.com/public/static/images/', // 静态资源路径
|
|||
baseUrl: 'http://192.168.99.63:14000', // 后台接口请求地址
|
|||
} |
|||
|
|||
}; |
|||
export default CONFIG[process.env.NODE_ENV]; |
|||
Write
Preview
Loading…
Cancel
Save
Reference in new issue