21 changed files with 1834 additions and 1097 deletions
-
72api/book.js
-
8components/book-list-item/book-list-item.vue
-
111components/lending-list-item/lending-list-item.vue
-
6pages.json
-
35pages/home/home.vue
-
273pages/lendCar/lendCar copy.vue
-
293pages/lendCar/lendCar.vue
-
279pages/search/search.vue
-
1pages/user/user.vue
-
4static/iconfont.css
-
BINstatic/iconfont.ttf
-
BINstatic/images/default-book.png
-
157subpkg/pages/activity-list/activity-list copy.vue
-
125subpkg/pages/activity-list/activity-list.vue
-
204subpkg/pages/book-detail/book-detail.vue
-
8subpkg/pages/book-list/book-list.vue
-
76subpkg/pages/feedback-list/feedback-list.vue
-
335subpkg/pages/myLending/myLending copy.vue
-
538subpkg/pages/myLending/myLending.vue
-
217subpkg/pages/ranking/ranking.vue
-
189subpkg/pages/unbind-card/unbind-card.vue
@ -0,0 +1,273 @@ |
|||
<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> |
|||
|
After Width: 756 | Height: 1063 | Size: 8.1 KiB |
@ -0,0 +1,157 @@ |
|||
<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> |
|||
@ -0,0 +1,335 @@ |
|||
<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,189 +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> |
|||
|
|||
<button |
|||
class="unbind-btn" |
|||
:disabled="!selectedValue" |
|||
:class="{disabled: !selectedValue}" |
|||
@click="handleUnbind" |
|||
> |
|||
确认解绑 |
|||
</button> |
|||
</view> |
|||
</template> |
|||
|
|||
<script> |
|||
import { |
|||
FetchFindAllReaderBindByOpenId, |
|||
FetchUnbindReadCard, |
|||
FetchSetDefaultReadCard |
|||
} from '@/api/user'; |
|||
|
|||
const READLIST = 'reader-card-list'; |
|||
|
|||
export default { |
|||
data() { |
|||
return { |
|||
selectedValue: '', |
|||
cardList: [], |
|||
} |
|||
}, |
|||
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; |
|||
} |
|||
}, |
|||
|
|||
// 确认解绑 |
|||
async handleUnbind() { |
|||
if (!this.selectedValue) { |
|||
uni.showToast({ title: '请选择要解绑的读者证', icon: 'none' }); |
|||
return; |
|||
} |
|||
|
|||
const openId = uni.getStorageSync('wx_login_code'); |
|||
if (!openId) return; |
|||
|
|||
// 判断:当前解绑的是不是【默认证】 |
|||
const unbindItem = this.cardList.find(item => item.bindValue === this.selectedValue); |
|||
const isUnbindDefault = unbindItem?.bindDefault === true; |
|||
|
|||
uni.showModal({ |
|||
title: '提示', |
|||
content: `确定要解绑读者证【${this.selectedValue}】吗?`, |
|||
success: async (res) => { |
|||
if (!res.confirm) return; |
|||
|
|||
try { |
|||
// 1. 执行解绑 |
|||
const result = await FetchUnbindReadCard({ |
|||
bindType: "rdid", |
|||
bindValue: this.selectedValue, |
|||
libcode: "1201", |
|||
openid: openId |
|||
}); |
|||
|
|||
if (result.code !== 200) { |
|||
uni.showToast({ title: result.msg || '解绑失败', icon: 'none' }); |
|||
return; |
|||
} |
|||
|
|||
uni.showToast({ title: '解绑成功', icon: 'success' }); |
|||
this.selectedValue = ''; |
|||
|
|||
// 刷新列表 |
|||
await this.getBindReaderCardList(); |
|||
|
|||
if (isUnbindDefault && this.cardList.length > 0) { |
|||
const newDefaultCard = this.cardList[0]; |
|||
const newValue = newDefaultCard.bindValue; |
|||
|
|||
const setResult = await FetchSetDefaultReadCard({ |
|||
bindType: 'rdid', |
|||
bindValue: newValue, |
|||
libcode: '1201', |
|||
openid: openId |
|||
}); |
|||
|
|||
if (setResult.code === 200) { |
|||
uni.setStorageSync('currentReaderCard', newValue); |
|||
// 刷新列表,让界面显示新默认 |
|||
await this.getBindReaderCardList(); |
|||
uni.showToast({ title: '已自动设置默认证', icon: 'success' }); |
|||
} |
|||
} |
|||
|
|||
if (this.cardList.length === 0) { |
|||
setTimeout(() => { |
|||
uni.navigateBack(); |
|||
}, 1500); |
|||
} |
|||
|
|||
} catch (err) { |
|||
uni.showToast({ title: '解绑失败', icon: 'none' }); |
|||
} |
|||
} |
|||
}); |
|||
}, |
|||
} |
|||
} |
|||
</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; |
|||
} |
|||
|
|||
.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; |
|||
&.disabled{ |
|||
background-color: #ccc; |
|||
} |
|||
} |
|||
} |
|||
</style> |
|||
Write
Preview
Loading…
Cancel
Save
Reference in new issue