Browse Source

0509

master
xuhuajiao 1 month ago
parent
commit
6d02ede2ec
  1. 72
      api/book.js
  2. 8
      components/book-list-item/book-list-item.vue
  3. 111
      components/lending-list-item/lending-list-item.vue
  4. 6
      pages.json
  5. 35
      pages/home/home.vue
  6. 273
      pages/lendCar/lendCar copy.vue
  7. 289
      pages/lendCar/lendCar.vue
  8. 277
      pages/search/search.vue
  9. 1
      pages/user/user.vue
  10. 4
      static/iconfont.css
  11. BIN
      static/iconfont.ttf
  12. BIN
      static/images/default-book.png
  13. 157
      subpkg/pages/activity-list/activity-list copy.vue
  14. 125
      subpkg/pages/activity-list/activity-list.vue
  15. 202
      subpkg/pages/book-detail/book-detail.vue
  16. 8
      subpkg/pages/book-list/book-list.vue
  17. 76
      subpkg/pages/feedback-list/feedback-list.vue
  18. 335
      subpkg/pages/myLending/myLending copy.vue
  19. 530
      subpkg/pages/myLending/myLending.vue
  20. 217
      subpkg/pages/ranking/ranking.vue
  21. 189
      subpkg/pages/unbind-card/unbind-card.vue

72
api/book.js

@ -1,6 +1,7 @@
import request from '../utils/request'; import request from '../utils/request';
// 查询读者当前借阅(群) // 查询读者当前借阅(群)
// http://192.168.99.63:14000/api/screenSetting/rdloanlist?rdid=420703GD0000748&sm4Key=86ACEF6CE6A65A4A&thirdAppid=feitian_gdlib&thirdSecret=edi56b6p5hi69dwk03so86uv2olhszqp&thirdUrl=http://218.200.95.251:8088/openlib
export function FetchRdloanlist(data) { export function FetchRdloanlist(data) {
return request({ return request({
url: '/api/screenSetting/rdloanlist', url: '/api/screenSetting/rdloanlist',
@ -8,6 +9,25 @@ export function FetchRdloanlist(data) {
}) })
} }
// 查询读者历史借阅
// http://192.168.99.63:14000/api/screenSetting/historyloan?rdid=420703GD0000748&sm4Key=86ACEF6CE6A65A4A&thirdAppid=feitian_gdlib&thirdSecret=edi56b6p5hi69dwk03so86uv2olhszqp&thirdUrl=http://218.200.95.251:8088/openlib
export function FetchHistoryloan(data) {
return request({
url: '/api/screenSetting/historyloan',
data
})
}
// 续借(群)
// http://192.168.99.63:14000/api/screenSetting/renewbook?barcode=420703GD00004461&logtype=30007&opuser=JH001&rdid=420703GD0000748&sm4Key=86ACEF6CE6A65A4A&thirdAppid=feitian_gdlib&thirdSecret=edi56b6p5hi69dwk03so86uv2olhszqp&thirdUrl=http://218.200.95.251:8088/openlib
export function FetchRenewbook(data) {
return request({
url: '/api/screenSetting/renewbook',
data
})
}
// 图书推荐 // 图书推荐
export function FetchInitScreenBookRecommend(data) { export function FetchInitScreenBookRecommend(data) {
@ -17,10 +37,50 @@ export function FetchInitScreenBookRecommend(data) {
}) })
} }
// 图书检索接口
// http://192.168.99.63:14000/api/screenSetting/bookSearch?opacUrl=http%3A%2F%2F218.200.95.251%3A8081%2Fopac%2F&page=1&query=%E5%BE%AE%E4%BF%A1&rows=10&scWay=dim&searchWay=title&sortOrder=desc&sortWay=score
// opacUrl / page:'1' / query / rows:'10' / scWay / searchWay / sortOrder desc / sortWay
export function FetchBookSearch(data) {
return request({
url: '/api/screenSetting/bookSearch',
data
})
}
// 书目记录号获取书目信息接口
// /api/screenSetting/findbookByQuery?opacUrl=http://218.200.95.251:8081/opac/&bookrecno=
export function FetchFindbookByQuery(data) {
return request({
url: '/api/screenSetting/findbookByQuery',
data
})
}
// 通过isbn获取图书封面
export function FetchCoverByISBN(data) {
return request({
url: '/dxhtsg/getCoverByISBN',
data
})
}
// 文献排行榜
// /api/screenSetting/sync36
// 读者借阅排行榜
export function FetchBookRanking(data) {
return request({
url: '/api/screenSetting/sync36',
data
})
}
// 馆藏量
// /api/screenSetting/sync35
// export function FetchInitScreenBookRecommend(data) {
// return request({
// url: '/qyzt/getNewBook',
// data
// })
// }
// 累计借出
// /api/screenSetting/sync82

8
components/book-list-item/book-list-item.vue

@ -9,12 +9,13 @@
/> --> /> -->
<image <image
class="img-item" class="img-item"
:src="data.imgPath ? baseUrl + '/api/fileRelevant/getImg?imgType=2&imgId=' + data.imgPath : '' "
:src="data.cover || data.imgCover || (data.imgPath ? baseUrl + '/api/fileRelevant/getImg?imgType=2&imgId=' + data.imgPath : '/static/images/default-book.png')"
mode="scaleToFill" mode="scaleToFill"
@error="onImgError"
></image> ></image>
</view> </view>
<view class="item-box-right"> <view class="item-box-right">
<view class="item-title line-clamp-2">{{ data.name || '暂无标题' }}</view>
<view class="item-title line-clamp-2">{{ data.title || data.name || '暂无标题' }}</view>
<text class="item-author">{{ data.author || '佚名' }}</text> <text class="item-author">{{ data.author || '佚名' }}</text>
<text class="item-publish">{{ data.publisher || '暂无出版社数据' }}</text> <text class="item-publish">{{ data.publisher || '暂无出版社数据' }}</text>
<!-- <view class="item-desc line-clamp-2">{{ data.desc || '暂无简介' }}</view> --> <!-- <view class="item-desc line-clamp-2">{{ data.desc || '暂无简介' }}</view> -->
@ -45,6 +46,9 @@ export default {
} }
}, },
methods: { methods: {
onImgError(e) {
e.target.src = "/static/images/default-book.png";
},
hotNumber(num) { hotNumber(num) {
if (!num) return "0"; if (!num) return "0";
if (num >= 10000) { if (num >= 10000) {

111
components/lending-list-item/lending-list-item.vue

@ -4,31 +4,39 @@
<view class="item-box-left"> <view class="item-box-left">
<image <image
class="img-item" class="img-item"
:src="data.imgCover"
:src="data.cover || data.imgCover || '/static/images/default-book.png'"
mode="scaleToFill" mode="scaleToFill"
@error="onImgError"
/> />
</view> </view>
<view class="item-box-right"> <view class="item-box-right">
<view class="item-title line-clamp-2">{{ data.title || '暂无标题' }}</view> <view class="item-title line-clamp-2">{{ data.title || '暂无标题' }}</view>
<view> <view>
<text class="item-author">{{ data.nickname || '佚名' }}</text>
<text class="item-publish">{{ data.publish || '暂无出版社数据' }}</text>
<text class="item-author">{{ data.author || '佚名' }}</text>
</view> </view>
<view class="item-desc line-clamp-2">{{ data.desc || '暂无简介' }}</view>
<view class="item-desc line-clamp-2">{{ data.localname || data.oplocalname }}</view>
</view> </view>
</view> </view>
<view class="lending-info"> <view class="lending-info">
<view class="lending-time"> <view class="lending-time">
<text>借阅开始时间{{ data.startTime || '' }}</text>
<text>最后归还时间{{ data.returnTime || '' }}</text>
<text v-if="data.realityTime" style="color: #FF3871;">实际归还时间{{ data.realityTime }}</text>
<text>借书时间{{ data.loantime || '' }}</text>
<text>应还时间{{ data.returntime || '' }}</text>
</view> </view>
<!-- 临期 1 -->
<image v-if="data.returnBook === 1" class="lending-status" src="@/static/images/mylending-img3.png" mode="heightFix" />
<!-- 准时 2 -->
<image v-if="data.returnBook === 2" class="lending-status" src="@/static/images/mylending-img2.png" mode="heightFix" />
<!-- 逾期 3 -->
<image v-if="data.returnBook === 3" class="lending-status" src="@/static/images/mylending-img1.png" mode="heightFix" />
<!-- 只在 在借中 显示状态 -->
<image
v-if="isLending && status === 'overdue'"
class="lending-status"
src="@/static/images/mylending-img1.png"
mode="heightFix"
/>
<image
v-if="isLending && status === 'warning'"
class="lending-status"
src="@/static/images/mylending-img3.png"
mode="heightFix"
/>
</view> </view>
</view> </view>
</template> </template>
@ -40,10 +48,44 @@ export default {
data: { data: {
type: Object, type: Object,
required: true required: true
},
//
isLending: {
type: Boolean,
default: false
}
},
computed: {
status() {
if (!this.isLending || !this.data.returntime) return '';
const now = Date.now();
const returnTime = new Date(this.data.returntime).getTime();
//
const diffTime = returnTime - now;
//
const day = diffTime / (1000 * 60 * 60 * 24);
// 1.
if (day < 0) {
return 'overdue';
}
// 2. 3
if (day <= 3) {
return 'warning';
}
// 3. 3
return '';
} }
}, },
methods: { methods: {
}
onImgError(e) {
e.target.src = "/static/images/default-book.png";
}
},
}; };
</script> </script>
@ -54,40 +96,44 @@ export default {
border-radius: 6px; border-radius: 6px;
border-bottom: 1px solid #f4f4f4; border-bottom: 1px solid #f4f4f4;
margin-bottom: 10px; margin-bottom: 10px;
.book-item-box { .book-item-box {
display: flex; display: flex;
justify-content: flex-start; justify-content: flex-start;
align-items: flex-start; align-items: flex-start;
.item-box-left { .item-box-left {
margin-right: 10px; margin-right: 10px;
.img-item{
.img-item {
width: 64px; width: 64px;
height: 90px; height: 90px;
border-radius: 5px; border-radius: 5px;
} }
} }
.item-box-right { .item-box-right {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
justify-content: flex-start; justify-content: flex-start;
flex: 1; flex: 1;
.item-title { .item-title {
font-size: 15px; font-size: 15px;
font-weight: bold; font-weight: bold;
color: #000; color: #000;
padding-bottom: 6px;
margin-bottom: 6px;
} }
.item-author,
.item-publish {
.item-author {
font-size: 12px; font-size: 12px;
color: #191A1A; color: #191A1A;
padding: 2px 4px; padding: 2px 4px;
border-radius: 2px; border-radius: 2px;
background-color: #F4F6FC; background-color: #F4F6FC;
}
.item-author{
margin-right: 6px; margin-right: 6px;
} }
.item-desc { .item-desc {
font-size: 12px; font-size: 12px;
color: #191A1A; color: #191A1A;
@ -96,20 +142,39 @@ export default {
} }
} }
} }
.lending-info{
.lending-info {
display: flex; display: flex;
justify-content: space-between; justify-content: space-between;
align-items: center; align-items: center;
padding: 10px 10px 10px 0; padding: 10px 10px 10px 0;
.lending-time{
.lending-time {
font-size: 12px; font-size: 12px;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
justify-content: flex-start; justify-content: flex-start;
line-height: 22px; line-height: 22px;
} }
.lending-status{
.lending-status {
height: 50px; height: 50px;
} }
.type-tag {
padding: 4px 10px;
border-radius: 12px;
font-size: 12px;
color: #fff;
font-weight: bold;
}
.tag-lend {
background-color: #409eff;
}
.tag-return {
background-color: #ff3871;
}
} }
</style> </style>

6
pages.json

@ -150,12 +150,6 @@
"style": { "style": {
"navigationBarTitleText": "" "navigationBarTitleText": ""
} }
},
{
"path": "pages/unbind-card/unbind-card",
"style": {
"navigationBarTitleText": ""
}
} }
] ]
} }

35
pages/home/home.vue

@ -16,7 +16,7 @@
</view> </view>
<view class="status-label">在借中</view> <view class="status-label">在借中</view>
</view> </view>
<view class="status-card" @click="toBorrowPage(2)">
<!-- <view class="status-card" @click="toBorrowPage(2)">
<view class="status-icon icon-color2" > <view class="status-icon icon-color2" >
<uni-icons custom-prefix="iconfont" type="icon-zuofei05" size="22"></uni-icons> <uni-icons custom-prefix="iconfont" type="icon-zuofei05" size="22"></uni-icons>
</view> </view>
@ -27,12 +27,12 @@
<uni-icons custom-prefix="iconfont" type="icon-yuqi04" size="22"></uni-icons> <uni-icons custom-prefix="iconfont" type="icon-yuqi04" size="22"></uni-icons>
</view> </view>
<view class="status-label">已过期</view> <view class="status-label">已过期</view>
</view>
<view class="status-card" @click="toBorrowCar">
</view> -->
<view class="status-card" @click="toBorrowPage(0)">
<view class="status-icon icon-color4" > <view class="status-icon icon-color4" >
<uni-icons custom-prefix="iconfont" type="icon-a-zhidu4" size="22"></uni-icons> <uni-icons custom-prefix="iconfont" type="icon-a-zhidu4" size="22"></uni-icons>
</view> </view>
<view class="status-label">借阅清单</view>
<view class="status-label">历史借阅</view>
</view> </view>
</view> </view>
@ -54,19 +54,25 @@
</view> </view>
<view class="menu-label">书目检索</view> <view class="menu-label">书目检索</view>
</view> </view>
<view class="menu-item">
<view class="menu-item" @click="onToLendCar">
<view class="menu-icon"> <view class="menu-icon">
<uni-icons custom-prefix="iconfont" type="icon-z_renew_normal" size="24"></uni-icons> <uni-icons custom-prefix="iconfont" type="icon-z_renew_normal" size="24"></uni-icons>
</view> </view>
<view class="menu-label">图书续借</view> <view class="menu-label">图书续借</view>
</view> </view>
<view class="menu-item" >
<!-- <view class="menu-item" >
<view class="menu-icon"> <view class="menu-icon">
<uni-icons custom-prefix="iconfont" type="icon-guanwaizhuanjie1" size="26"></uni-icons> <uni-icons custom-prefix="iconfont" type="icon-guanwaizhuanjie1" size="26"></uni-icons>
</view> </view>
<view class="menu-label">图书转借</view> <view class="menu-label">图书转借</view>
</view> -->
<view class="menu-item" @click="toLibraryCard">
<view class="menu-icon">
<uni-icons custom-prefix="iconfont" type="icon-duzhezheng" size="26"></uni-icons>
</view>
<view class="menu-label">电子证</view>
</view> </view>
<view class="menu-item" @click="toRanking">
<view class="menu-item" @click="toRanking">
<view class="menu-icon"> <view class="menu-icon">
<uni-icons custom-prefix="iconfont" type="icon-paihangbang" size="22"></uni-icons> <uni-icons custom-prefix="iconfont" type="icon-paihangbang" size="22"></uni-icons>
</view> </view>
@ -84,12 +90,6 @@
</view> </view>
<view class="menu-label">读者留言</view> <view class="menu-label">读者留言</view>
</view> </view>
<view class="menu-item" @click="toLibraryCard">
<view class="menu-icon">
<uni-icons custom-prefix="iconfont" type="icon-duzhezheng" size="26"></uni-icons>
</view>
<view class="menu-label">电子证</view>
</view>
<view class="menu-item" @click="onToUser"> <view class="menu-item" @click="onToUser">
<view class="menu-icon"> <view class="menu-icon">
<uni-icons custom-prefix="iconfont" type="icon-yonghuxinxi-gerenxinxi" size="20"></uni-icons> <uni-icons custom-prefix="iconfont" type="icon-yonghuxinxi-gerenxinxi" size="20"></uni-icons>
@ -262,8 +262,9 @@ export default {
} }
}, },
goToBookDetail(item) { goToBookDetail(item) {
//
uni.navigateTo({ uni.navigateTo({
url: "/subpkg/pages/book-detail/book-detail?isbn=" + item.isbn
url: "/subpkg/pages/book-detail/book-detail?bookData=" + encodeURIComponent(JSON.stringify(item))
}) })
}, },
@ -279,6 +280,12 @@ export default {
url: "/pages/search/search" url: "/pages/search/search"
}); });
}, },
//
onToLendCar() {
uni.switchTab({
url: "/pages/lendCar/lendCar"
});
},
// //
toCheckLogin() { toCheckLogin() {
uni.navigateTo({ uni.navigateTo({

273
pages/lendCar/lendCar copy.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>

289
pages/lendCar/lendCar.vue

@ -1,151 +1,170 @@
<template> <template>
<view class="item-container"> <view class="item-container">
<!-- 图书数量 + 右侧设置按钮 -->
<view class="count-text"> <view class="count-text">
<text>图书数量 ({{ bookList.length }})</text> <text>图书数量 ({{ bookList.length }})</text>
<text class="edit-btn" @click="toggleEdit">{{ editMode ? "完成" : "管理" }}</text>
</view> </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="item.imgCover" 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.nickname || '佚名' }}</text>
<text class="item-publish">{{ item.publish || '暂无出版社' }}</text>
</view>
<view class="item-desc line-clamp-2">{{ item.desc || '暂无简介' }}</view>
<!-- 购物车式列表 不用 checkbox-group-->
<view class="car-list" v-for="item in bookList" :key="item.barcode">
<checkbox :checked="item.checked" @click="toggleItem(item)" />
<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>
<view class="item-desc">应还时间{{ item.returndate || '暂无' }}</view>
</view> </view>
</view> </view>
</checkbox-group>
</view>
<view class="bottom-placeholder"></view> <view class="bottom-placeholder"></view>
<view class="car-bottom"> <view class="car-bottom">
<!-- 全选 --> <!-- 全选 -->
<view class="all-check" @click="handleAllCheck">
<view class="all-check" @click="toggleAllCheck">
<checkbox :checked="isAllChecked" /> <checkbox :checked="isAllChecked" />
<text style="margin-left:6px">全选</text> <text style="margin-left:6px">全选</text>
</view> </view>
<!-- 根据编辑状态切换按钮续借 / 删除 -->
<button
class="join-btn"
@click="editMode ? handleDelete() : handleRenew()"
:disabled="!hasChecked"
>
{{ editMode ? "删除选中" : "一键续借" }}
<button class="join-btn" @click="handleRenew" :disabled="!hasChecked">
一键续借
</button> </button>
</view> </view>
</view> </view>
</template> </template>
<script> <script>
import { FetchInitScreenSetting } from '@/api/user';
import { FetchRdloanlist, FetchRenewbook } from '@/api/book';
export default { export default {
data() { data() {
return { return {
editMode: false, // /
bookList: [
{
imgCover: "https://qiniu.aiyxlib.com/1606124577077",
title: "名侦探柯南",
nickname: "青山刚昌",
publish: "长春出版社",
isbn: "1001",
desc: "测试图书简介",
checked: true,
},
{
imgCover: "https://qiniu.aiyxlib.com/1606124577077",
title: "测试图书",
nickname: "测试作者",
publish: "测试出版社",
isbn: "1002",
desc: "测试简介",
checked: true,
},
{
imgCover: "https://qiniu.aiyxlib.com/1606124577077",
title: "测试图书",
nickname: "测试作者",
publish: "测试出版社",
isbn: "1003",
desc: "测试简介",
checked: false,
}
],
bookList: [],
defaultCover: '/static/images/default-book.png',
screenConfig: {},
}; };
}, },
onLoad() {
this.getConfigAndList();
},
computed: { computed: {
//
//
isAllChecked() { isAllChecked() {
return this.bookList.every((item) => item.checked);
return this.bookList.length > 0 && this.bookList.every(item => item.checked);
}, },
// //
hasChecked() { hasChecked() {
return this.bookList.some((item) => item.checked);
},
return this.bookList.some(item => item.checked);
}
}, },
methods: { methods: {
//
toggleEdit() {
this.editMode = !this.editMode;
// 退
if (!this.editMode) {
this.bookList.forEach(item => item.checked = false);
}
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 || '',
opuser: res.data.op_user?.context || 'JH001',
};
this.getLendingList();
} catch (err) {}
}, },
checkboxChange(e) {
console.log("checkbox选中value:", e.detail.value);
const values = e.detail.value;
const items = this.bookList;
for (let i = 0, lenI = items.length; i < lenI; ++i) {
items[i].checked = false;
for (let j = 0, lenJ = values.length; j < lenJ; ++j) {
if (items[i].isbn === values[j]) {
items[i].checked = true;
break;
}
}
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;
// checked: false
this.bookList = (result.loanlist || []).map(item => ({
...item,
checked: false
}));
} catch (err) {
uni.showToast({ title: '加载失败', icon: 'none' });
} }
this.bookList = items;
}, },
handleAllCheck() {
//
toggleItem(item) {
item.checked = !item.checked;
},
// /
toggleAllCheck() {
const isAll = this.isAllChecked; const isAll = this.isAllChecked;
this.bookList.forEach((item) => {
this.bookList.forEach(item => {
item.checked = !isAll; item.checked = !isAll;
}); });
}, },
// //
handleRenew() {
const checkedBooks = this.bookList.filter((item) => item.checked);
uni.showToast({
title: `已续借 ${checkedBooks.length}`,
icon: "success",
});
},
async handleRenew() {
const checkedBooks = this.bookList.filter(item => item.checked);
if (checkedBooks.length === 0) {
uni.showToast({ title: '请选择图书', icon: 'none' });
return;
}
//
handleDelete() {
const beforeLen = this.bookList.length;
//
this.bookList = this.bookList.filter(item => !item.checked);
const deleteCount = beforeLen - this.bookList.length;
const barcodes = checkedBooks.map(item => item.barcode).join('|');
uni.showLoading({ title: '续借中...' });
uni.showToast({
title: `已删除 ${deleteCount}`,
icon: "success"
});
try {
const params = {
...this.screenConfig,
rdid: uni.getStorageSync('currentReaderCard'),
barcode: barcodes,
logtype: '30007'
};
const res = await FetchRenewbook(params);
// data
const result = typeof res.data === 'string' ? JSON.parse(res.data) : res.data;
uni.hideLoading();
if (result.success === true) {
//
uni.showToast({
title: `续借成功!`,
icon: 'success'
});
//
this.getLendingList();
} else {
//
let msg = '续借失败';
if (result.messagelist && result.messagelist.length > 0) {
msg = result.messagelist[0].message;
}
uni.showToast({
title: msg,
icon: 'none',
duration: 3000
});
}
} catch (err) {
uni.hideLoading();
uni.showToast({ title: '网络异常,续借失败', icon: 'none' });
console.error('续借异常:', err);
}
}, },
},
}
}; };
</script> </script>
@ -157,13 +176,8 @@ export default {
} }
.count-text { .count-text {
margin-bottom: 10px; margin-bottom: 10px;
display: flex;
justify-content: space-between; /* 数量左,设置右 */
align-items: center;
}
.edit-btn {
color: #01a4fe;
font-size: 14px; font-size: 14px;
color: #333;
} }
.car-list { .car-list {
display: flex; display: flex;
@ -188,58 +202,27 @@ export default {
border-radius: 6px; border-radius: 6px;
} }
} }
.item-box-right {
flex: 1;
}
.item-title {
font-weight: bold;
margin-bottom: 4px;
}
.item-author,
.item-publish {
font-size: 12px;
background: #f4f6fc;
padding: 2px 6px;
border-radius: 4px;
margin-right: 6px;
}
.item-desc {
font-size: 12px;
color: #999;
margin-top: 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;
}
.bottom-placeholder { height: 60px; }
.car-bottom { .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;
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 { .join-btn {
font-size: 14px;
color: #fff;
margin: 0 !important;
background-color: #01a4fe !important;
border-radius: 23px;
font-size: 14px; color: #fff;
background: #01a4fe !important; border-radius: 23px;
padding: 0 30px; padding: 0 30px;
&::after{
border: none !important;
}
&[disabled] {
background: #ccc !important;
}
&::after{ border: none !important; }
&[disabled] { background: #ccc !important; }
} }
</style> </style>

277
pages/search/search.vue

@ -32,141 +32,210 @@
</view> </view>
<view class="search-btn-container" @click="onSearch"> <view class="search-btn-container" @click="onSearch">
<button class="search-btn" type="primary" >搜索</button>
<button class="search-btn" type="primary">搜索</button>
</view> </view>
<!-- 有数据才显示 -->
<view class="filter-container">
<view class="total-reslut" v-if="listData.length > 0">
搜索到 {{ total }} 条记录 {{ totalPage }} 当前第 {{ page }}
</view>
<view>筛选</view>
</view>
<view class="filter-container">
<view class="total-reslut" v-if="listData.length > 0">
搜索到 {{ total }} 条记录 {{ totalPage }} 当前第 {{ page }}
</view>
<!-- <view>筛选</view> -->
</view>
</view>
<view style="padding-top: 154px;" v-if="listData.length > 0">
<scroll-view
scroll-y
style="height: calc(100vh - 80px);"
@scrolltolower="onScrollLower"
>
<book-list-item
class="hot-list-item"
v-for="(item, index) in listData"
:key="index"
:data="item"
:ranking="index + 1"
@click="onItemClick(item)"
></book-list-item>
<view class="load-more" v-if="total > listData.length">
上拉加载更多...
</view>
<view class="load-more" v-if="total <= listData.length && listData.length > 0">
没有更多数据了
</view>
</scroll-view>
</view> </view>
<!-- 有数据才显示列表 -->
<view style="padding-top: 154px;" v-if="listData.length > 0">
<scroll-view
scroll-y
style="height: calc(100vh - 80px);"
@scrolltolower="onScrollLower"
>
<book-list-item
class="hot-list-item"
v-for="(item, index) in listData"
:key="index"
:data="item"
:ranking="index + 1"
@click="onItemClick(item)"
></book-list-item>
<view class="load-more" v-if="total > listData.length">
上拉加载更多...
</view>
<view class="load-more" v-if="total <= listData.length && listData.length > 0">
没有更多数据了
</view>
</scroll-view>
</view>
<!-- 空状态 -->
<view class="empty-box" v-if="isSearched && listData.length === 0">
<text>没有检索到相关数据</text>
</view>
</view> </view>
</template> </template>
<script> <script>
import { FetchInitScreenSetting } from '@/api/user';
import { FetchBookSearch } from '@/api/book';
import BookListItem from "@/components/book-list-item/book-list-item.vue"; import BookListItem from "@/components/book-list-item/book-list-item.vue";
export default { export default {
components: {
BookListItem
},
components: { BookListItem },
data() { data() {
return { return {
selectValue: '', selectValue: '',
selectValueText: '', selectValueText: '',
selectIndex: 0, selectIndex: 0,
value:'',
value: '',
range: [ range: [
{ value: 0, text: "任意词" },
{ value: 1, text: "题名" },
{ value: 2, text: "著者" },
{ value: 3, text: "ISBN" },
{ value: 4, text: "主题" },
{ value: 5, text: "出版社" },
{ value: 6, text: "分类号" }
// { value: '', text: "" },
{ value: 'title', text: "题名" },
{ value: 'title200a', text: "正题名" },
{ value: 'author', text: "著者" },
{ value: 'isbn', text: "ISBN" },
{ value: 'subject', text: "主题" },
{ value: 'publisher', text: "出版社" },
{ value: 'class', text: "分类号" },
{ value: 'ctrlno', text: "控制号" },
{ value: 'orderno', text: "订购号" },
{ value: 'callno', text: "索书号" },
], ],
rangeText: [], rangeText: [],
listData:[],
opacUrl: '',
listData: [],
size: 10, size: 10,
page: 1, page: 1,
total: 0, total: 0,
totalPage: 0
totalPage: 0,
isSearched: false
}; };
}, },
onLoad() { onLoad() {
this.rangeText = this.range.map(item => item.text); this.rangeText = this.range.map(item => item.text);
this.selectValue = this.range[0].value; this.selectValue = this.range[0].value;
this.selectValueText = this.range[0].text; this.selectValueText = this.range[0].text;
this.getOpacUrl();
}, },
methods: { methods: {
// opacUrl
async getOpacUrl() {
try {
const res = await FetchInitScreenSetting({ libcode: '1201' });
this.opacUrl = res.data.opac_url?.context || '';
} catch (err) {
console.error('获取配置失败', err);
}
},
//
onPickerChange(e) { onPickerChange(e) {
const index = e.detail.value; const index = e.detail.value;
this.selectIndex = index; this.selectIndex = index;
this.selectValue = this.range[index].value; this.selectValue = this.range[index].value;
this.selectValueText = this.range[index].text; this.selectValueText = this.range[index].text;
}, },
async getBookList() {
uni.showLoading({ title: '搜索中...' });
await new Promise(resolve => setTimeout(resolve, 500));
const mockList = [];
for (let i = 0; i < this.size; i++) {
const idx = (this.page - 1) * this.size + i + 1;
mockList.push({
id: idx,
title: `穆尔西医生带你走出孤独`,
desc: `关键词:${this.value}|检索类型:${this.selectValueText}`,
nickname: `穆尔西`,
imgCover:'http://wxtest.twgbz.cn/spider/bookcover/978-7-5546-1700-7',
publish: "中国青年出版社",
});
}
//
async getBookList() {
if (!this.value) {
uni.showToast({ title: '请输入搜索内容', icon: 'none' });
return;
}
if (!this.opacUrl) {
uni.showToast({ title: '未获取到检索配置', icon: 'none' });
return;
}
this.total = 37;
this.totalPage = 4;
uni.showLoading({ title: '搜索中...' });
if (this.page === 1) {
this.listData = mockList;
} else {
this.listData = [...this.listData, ...mockList];
}
uni.hideLoading();
},
try {
const params = {
opacUrl: this.opacUrl,
page: this.page.toString(),
query: this.value,
rows: this.size.toString(),
scWay: 'dim',
searchWay: this.selectValue,
sortOrder: 'desc',
sortWay: 'score'
};
const res = await FetchBookSearch(params);
let result = {};
if (typeof res.data === 'string') {
try {
result = JSON.parse(res.data);
} catch (e) {}
} else {
result = res.data || {};
}
// {}
const isEmptyData = Object.keys(result).length === 0;
// list & total
let list = [];
let total = 0;
if (!isEmptyData) {
list = result.data || [];
total = Number(result.total || 0);
}
this.total = total;
this.totalPage = Math.ceil(this.total / this.size);
if (this.page === 1) {
this.listData = list;
} else {
this.listData = [...this.listData, ...list];
}
this.isSearched = true;
} catch (err) {
this.listData = [];
uni.showToast({ title: '搜索失败', icon: 'none' });
} finally {
uni.hideLoading();
}
},
//
onScrollLower() { onScrollLower() {
if (this.listData.length >= this.total) return; if (this.listData.length >= this.total) return;
this.page++; this.page++;
this.getBookList(); this.getBookList();
}, },
//
onSearch() { onSearch() {
this.page = 1; this.page = 1;
this.listData = [];
this.getBookList(); this.getBookList();
uni.showToast({ title: '搜索:' + this.value, icon: 'none' });
}, },
//
onItemClick(item) { onItemClick(item) {
// uni.showToast({ title: '' + item.title, icon: 'none' });
uni.navigateTo({ uni.navigateTo({
url: "/subpkg/pages/book-detail/book-detail?isbn=" + item.isbn
url: "/subpkg/pages/book-detail/book-detail?bookrecno=" + item.bookrecno
}) })
},
onFocus() {},
onBlur() {},
},
//
onClear() { onClear() {
this.value = ''; this.value = '';
this.listData = []; //
this.listData = [];
this.total = 0; this.total = 0;
this.totalPage = 0;
this.isSearched = false;
}, },
onFocus() {},
onBlur() {},
onCancel() {}, onCancel() {},
onInput(val) { this.value = val; } onInput(val) { this.value = val; }
} }
}
};
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
@ -175,25 +244,28 @@ export default {
flex-direction: column; flex-direction: column;
padding: 15px 20px; padding: 15px 20px;
background: #fff; background: #fff;
position: fixed;
top: 0;
left: 0;
right: 0;
z-index: 99;
height: 124px;
.my-search-wrapper{
position: fixed;
top: 0;
left: 0;
right: 0;
z-index: 99;
height: 124px;
.my-search-wrapper {
display: flex; display: flex;
align-items: center; align-items: center;
width: 100%; width: 100%;
padding: 5px 0;
padding: 5px 0;
background-color: #f7f7f7; background-color: #f7f7f7;
border-radius: 36px; border-radius: 36px;
overflow: hidden; overflow: hidden;
} }
.picker-select { .picker-select {
width: 60px; width: 60px;
border-right: 1px solid #c9c9c9; border-right: 1px solid #c9c9c9;
} }
.picker-display { .picker-display {
display: flex; display: flex;
align-items: center; align-items: center;
@ -202,23 +274,28 @@ export default {
font-size: 14px; font-size: 14px;
color: #333; color: #333;
} }
.my-search-bar { .my-search-bar {
flex: 1; flex: 1;
::v-deep .uni-searchbar { ::v-deep .uni-searchbar {
padding: 0 !important; padding: 0 !important;
background: transparent !important; background: transparent !important;
} }
} }
.search-btn-container{
.search-btn-container {
width: 100%; width: 100%;
margin-top: 10px; margin-top: 10px;
.search-btn{
.search-btn {
background-color: #01a4fe; background-color: #01a4fe;
font-size: 15px; font-size: 15px;
border-radius: 20px; border-radius: 20px;
} }
} }
} }
.filter-container { .filter-container {
display: flex; display: flex;
justify-content: flex-end; justify-content: flex-end;
@ -226,17 +303,29 @@ export default {
font-size: 13px; font-size: 13px;
color: #666; color: #666;
} }
.load-more { .load-more {
text-align: center; text-align: center;
padding: 10px; padding: 10px;
font-size: 13px; font-size: 13px;
color: #999; color: #999;
} }
.total-reslut{
flex: 1;
line-height: 20px;
font-size: 13px;
color: #01a4fe;
font-weight: 400;
.total-reslut {
flex: 1;
line-height: 20px;
font-size: 13px;
color: #01a4fe;
font-weight: 400;
}
.empty-box {
display: flex;
justify-content: center;
align-items: center;
height: 50vh;
font-size: 14px;
color: #999;
padding-top: 100px;
} }
</style> </style>

1
pages/user/user.vue

@ -263,7 +263,6 @@ export default {
font-weight: 40; font-weight: 40;
color: #fff; color: #fff;
line-height: 17px; line-height: 17px;
opacity: 0.9;
} }
} }
} }

4
static/iconfont.css

@ -11,6 +11,10 @@
-moz-osx-font-smoothing: grayscale; -moz-osx-font-smoothing: grayscale;
} }
.icon-kongshuju:before {
content: "\e61c";
}
.icon-tuichu:before { .icon-tuichu:before {
content: "\e7ed"; content: "\e7ed";
} }

BIN
static/iconfont.ttf

BIN
static/images/default-book.png

After

Width: 756  |  Height: 1063  |  Size: 8.1 KiB

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

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

125
subpkg/pages/activity-list/activity-list.vue

@ -9,15 +9,17 @@
@scrolltolower="onLoadMore" @scrolltolower="onLoadMore"
lower-threshold="100" lower-threshold="100"
> >
<!-- 活动列表 -->
<view class="activity-item" v-for="(item, index) in activityList" @click="toActivityDetail(item)" :key="index"> <view class="activity-item" v-for="(item, index) in activityList" @click="toActivityDetail(item)" :key="index">
<image class="activity-img" :src="item.imgUrl"></image> <image class="activity-img" :src="item.imgUrl"></image>
<view class="activity-info"> <view class="activity-info">
<view class="activity-info-left"> <view class="activity-info-left">
<text class="title">{{ item.title }}</text> <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 class="time">
<uni-icons class="time-icon" custom-prefix="iconfont" type="icon-shijian" size="15"></uni-icons>
<text>{{ item.time }}</text>
</view>
</view> </view>
<button <button
class="activity-btn" class="activity-btn"
@ -30,11 +32,17 @@
</view> </view>
</view> </view>
<!-- 空状态没有活动时显示 -->
<view class="empty-box" v-if="activityList.length === 0 && !loading">
<uni-icons custom-prefix="iconfont" type="icon-kongshuju" size="80"></uni-icons>
<text style="margin-top: 20px;">暂无活动敬请期待~</text>
</view>
<!-- 加载提示 --> <!-- 加载提示 -->
<view class="load-tip" v-if="loading"> <view class="load-tip" v-if="loading">
<text>加载中...</text> <text>加载中...</text>
</view> </view>
<view class="load-tip" v-if="noMore">
<view class="load-tip" v-if="noMore && activityList.length > 0">
<text>没有更多数据了</text> <text>没有更多数据了</text>
</view> </view>
</scroll-view> </scroll-view>
@ -55,33 +63,24 @@ export default {
}; };
}, },
onLoad() { onLoad() {
//
this.getActivityList(); this.getActivityList();
}, },
methods: { methods: {
/**
* 模拟请求活动列表接口
* @param {Number} page 页码
* @param {Number} pageSize 每页条数
*/
getActivityList() { getActivityList() {
this.loading = true; this.loading = true;
//
setTimeout(() => { setTimeout(() => {
//
const list = this.mockData(this.page, this.pageSize);
//
// const list = this.mockData(this.page, this.pageSize);
const list = []; //
//
if (this.refreshing) { if (this.refreshing) {
this.activityList = list; this.activityList = list;
this.refreshing = false; this.refreshing = false;
} else { } else {
//
this.activityList = [...this.activityList, ...list]; this.activityList = [...this.activityList, ...list];
} }
//
if (this.activityList.length >= this.total) { if (this.activityList.length >= this.total) {
this.noMore = true; this.noMore = true;
} else { } else {
@ -89,12 +88,9 @@ export default {
} }
this.loading = false; this.loading = false;
}, 1000);
}, 500);
}, },
/**
* 模拟生成活动数据
*/
mockData(page, pageSize) { mockData(page, pageSize) {
let arr = []; let arr = [];
for (let i = 0; i < pageSize; i++) { for (let i = 0; i < pageSize; i++) {
@ -103,36 +99,30 @@ export default {
imgUrl: "https://qiniu.aiyxlib.com/1605060269830", imgUrl: "https://qiniu.aiyxlib.com/1605060269830",
title: `活动标题${num}`, title: `活动标题${num}`,
time: "2025-11-03 00:00 ~2025-11-09 00:00", time: "2025-11-03 00:00 ~2025-11-09 00:00",
status: num % 3 === 0 ? 0 : 1, // 0= 1=
status: num % 3 === 0 ? 0 : 1,
}); });
} }
return arr; return arr;
}, },
/**
* 下拉刷新
*/
onRefresh() { onRefresh() {
//
this.page = 1; this.page = 1;
this.noMore = false; this.noMore = false;
this.refreshing = true; this.refreshing = true;
this.getActivityList(); this.getActivityList();
}, },
/**
* 上拉加载更多
*/
onLoadMore() { onLoadMore() {
if (this.loading || this.noMore) return; if (this.loading || this.noMore) return;
this.page++; this.page++;
this.getActivityList(); this.getActivityList();
}, },
toActivityDetail(item){
uni.navigateTo({
url: "/subpkg/pages/activity-detail/activity-detail?title=" + item.title
})
}
toActivityDetail(item){
uni.navigateTo({
url: "/subpkg/pages/activity-detail/activity-detail?title=" + item.title
});
}
}, },
}; };
</script> </script>
@ -148,10 +138,77 @@ scroll-view {
height: 100%; height: 100%;
} }
/* 空状态 */
.empty-box {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
height: calc(100vh - 150rpx);
color: #999;
font-size: 15px;
}
.load-tip { .load-tip {
text-align: center; text-align: center;
padding: 20px; padding: 20px;
color: #999; color: #999;
font-size: 14px; font-size: 14px;
} }
.activity-item {
background: #fff;
border-radius: 12px;
margin-bottom: 15px;
overflow: hidden;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.05);
.activity-img {
width: 100%;
height: 180rpx;
}
.activity-info {
padding: 15px;
display: flex;
justify-content: space-between;
align-items: center;
.activity-info-left {
flex: 1;
.title {
font-size: 16px;
font-weight: bold;
color: #333;
display: block;
margin-bottom: 8px;
}
.time {
display: flex;
align-items: center;
font-size: 12px;
color: #999;
.time-icon {
margin-right: 4px;
}
}
}
.activity-btn {
background-color: #01a4fe;
font-size: 14px;
border-radius: 30px;
padding: 0 15px;
height: 36px;
line-height: 36px;
}
.disabled-btn {
background-color: #ccc !important;
}
}
}
</style> </style>

202
subpkg/pages/book-detail/book-detail.vue

@ -3,48 +3,57 @@
<!-- 书籍头部信息 --> <!-- 书籍头部信息 -->
<view class="article-detail-container"> <view class="article-detail-container">
<view class="article-detail-left"> <view class="article-detail-left">
<view class="article-detail-title">名侦探柯南</view>
<view class="article-detail-title">{{ bookInfo.title || bookInfo.name || '暂无书名' }}</view>
<view class="article-detail-info"> <view class="article-detail-info">
<view class="article-detail-author">青山刚昌 </view>
<view class="article-detail-time">长春出版社</view>
<view class="article-detail-time">ISBN7-80664-373-7</view>
<view class="article-detail-time">出版时间2023</view>
<view class="article-detail-author">作者{{ bookInfo.author || '暂无' }}</view>
<view class="article-detail-time">出版社{{ bookInfo.publisher || '暂无' }}</view>
<view class="article-detail-time">ISBN{{ bookInfo.isbn || '暂无' }}</view>
<!-- <view class="article-detail-time">出版时间{{ bookInfo.publishdate || '暂无' }}</view> -->
</view> </view>
</view> </view>
<view class="article-detail-right"> <view class="article-detail-right">
<!-- <image
:src="bookInfo.imgPath
? baseUrl + '/api/fileRelevant/getImg?imgType=2&imgId=' + bookInfo.imgPath
: bookInfo.cover || '/static/images/default-book.png'"
mode="widthFix"
/> -->
<image <image
src="https://qiniu.aiyxlib.com/1606124577077"
class="img-item"
:src="bookInfo.cover || (bookInfo.imgPath ? baseUrl + '/api/fileRelevant/getImg?imgType=2&imgId=' + bookInfo.imgPath : '/static/images/default-book.png')"
mode="widthFix" mode="widthFix"
/>
@error="onImgError"
></image>
</view> </view>
</view> </view>
<!-- 馆藏信息 --> <!-- 馆藏信息 -->
<view class="book-store-info"> <view class="book-store-info">
<view class="book-store-info-item"> <view class="book-store-info-item">
<text class="store-txt1">39</text>
<text class="store-txt2">总馆藏量</text>
<text class="store-txt1">{{ bookInfo.orglib || '暂无' }}</text>
<text class="store-txt2">所属馆</text>
</view> </view>
<view class="book-store-info-item"> <view class="book-store-info-item">
<text class="store-txt1">38</text>
<text class="store-txt1">{{ bookInfo.loancount || 0 }}</text>
<text class="store-txt2">在馆</text> <text class="store-txt2">在馆</text>
</view> </view>
<!-- 编目=1,在馆=2,借出=3,丢失=4,剔除=5,交换=6,赠送=7,装订=8,锁定=9,预借=10 清点=12 -->
<view class="book-store-info-item"> <view class="book-store-info-item">
<text class="store-txt1">1</text>
<text class="store-txt2">借出</text>
<text class="store-txt1">{{ stateText }}</text>
<text class="store-txt2">馆藏状态</text>
</view> </view>
</view> </view>
<!-- 索书号/条码号 --> <!-- 索书号/条码号 -->
<view class="store-info-list">
<view class="store-info-item">
<view v-if="bookInfo.collection" class="store-info-list">
<view class="store-info-item" v-for="(item, index) in bookInfo.collection" :key="index">
<view> <view>
<text>索书号</text> <text>索书号</text>
<text>J238.7/5</text>
<text>{{ item.callno || '暂无' }}</text>
</view> </view>
<view> <view>
<text>条码号</text> <text>条码号</text>
<text>14018019711</text>
<text>{{ item.barcode || '暂无' }}</text>
</view> </view>
</view> </view>
</view> </view>
@ -61,83 +70,142 @@
<!-- TAB 内容 --> <!-- TAB 内容 -->
<view class="content-item" v-if="currentTab === 0"> <view class="content-item" v-if="currentTab === 0">
名侦探柯南是日本漫画家青山刚昌创作的著名侦探漫画作品讲述了高中生侦探工藤新一被灌下毒药后身体缩小化名为江户川柯南秘密调查黑暗组织并解决无数离奇案件的故事作品兼具推理冒险情感与幽默元素是全球极具影响力的经典漫画
{{ bookInfo.summary || bookInfo.explain || '暂无内容简介' }}
</view> </view>
<view class="content-item" v-if="currentTab === 1"> <view class="content-item" v-if="currentTab === 1">
青山刚昌日本著名漫画家1963 年生于鸟取县代表作包括名侦探柯南魔术快斗他擅长推理题材与人物塑造画风细腻剧情严谨作品在全球多个国家出版深受各年龄层读者喜爱
{{ bookInfo.authorbio || '暂无作者简介' }}
</view> </view>
<view class="detail-bottom"> <view class="detail-bottom">
<!-- <view class="detail-left"> -->
<!-- #ifdef MP-WEIXIN -->
<button open-type="share" class="handle-btn">
<uni-icons custom-prefix="iconfont" type="icon-fenxiang01" size="20"></uni-icons>
<text class="share-text">分享</text>
</button>
<!-- #endif -->
<button class="handle-btn" @click="toggleCollect">
<!-- 已收藏 / 未收藏 自动切换图标 -->
<uni-icons :type="isCollected ? 'heart-filled' : 'heart'" size="20" color="#ff4444"></uni-icons>
<text class="share-text">{{ isCollected ? '已收藏' : '收藏' }}</text>
</button>
</view>
<!-- <button class="join-btn">
<text>我要借书</text>
</button> -->
<!-- </view> -->
<button open-type="share" class="handle-btn">
<uni-icons custom-prefix="iconfont" type="icon-fenxiang01" size="20"></uni-icons>
<text class="share-text">分享</text>
</button>
<button class="handle-btn" @click="toggleCollect">
<uni-icons :type="isCollected ? 'heart-filled' : 'heart'" size="20" color="#ff4444"></uni-icons>
<text class="share-text">{{ isCollected ? '已收藏' : '收藏' }}</text>
</button>
</view>
</view> </view>
</template> </template>
<script> <script>
import { FetchInitScreenSetting } from '@/api/user';
import { FetchFindbookByQuery } from '@/api/book';
import config from '@/utils/config';
export default { export default {
data() { data() {
return { return {
currentTab: 0, // 0= 1=
isCollected: false, //
bookIsbn: "" // ISBN
baseUrl: config.baseUrl,
currentTab: 0,
isCollected: false,
bookrecno: '',
opacUrl: '',
bookInfo: {} //
}; };
}, },
onLoad(options) { onLoad(options) {
// ISBN
this.bookIsbn = options.isbn || "7-80664-373-7";
//
this.checkCollectStatus();
//
if (options.bookData) {
const bookData = JSON.parse(decodeURIComponent(options.bookData));
this.bookInfo = bookData; //
this.bookrecno = bookData.bookrecno || "";
this.checkCollectStatus();
return;
}
// bookrecno
this.bookrecno = options.bookrecno || "";
this.getOpacUrl();
},
computed: {
//
stateText() {
const state = this.bookInfo.state || 0
const map = {
1: '编目',
2: '在馆',
3: '借出',
4: '丢失',
5: '剔除',
6: '交换',
7: '赠送',
8: '装订',
9: '锁定',
10: '预借',
12: '清点'
}
return map[state] || '未知'
}
}, },
methods: { methods: {
//
checkCollectStatus() {
const collectList = uni.getStorageSync("collectList") || [];
this.isCollected = collectList.includes(this.bookIsbn);
onImgError(e) {
e.target.src = "/static/images/default-book.png";
},
//
async getOpacUrl() {
try {
const res = await FetchInitScreenSetting({ libcode: '1201' });
this.opacUrl = res.data.opac_url?.context || '';
this.getBookDetail();
} catch (err) {}
}, },
// /
toggleCollect() {
let collectList = uni.getStorageSync("collectList") || [];
//
async getBookDetail() {
if (!this.bookrecno || !this.opacUrl) return;
uni.showLoading({ title: '加载中...' });
try {
const params = {
opacUrl: this.opacUrl,
bookrecno: this.bookrecno
};
const res = await FetchFindbookByQuery(params);
//
let detail = {};
if (typeof res.data === 'string') {
detail = JSON.parse(res.data);
} else {
detail = res.data || {};
}
this.bookInfo = detail;
this.checkCollectStatus();
} catch (err) {
} finally {
uni.hideLoading();
}
},
//
checkCollectStatus() {
const list = uni.getStorageSync('collectList') || [];
this.isCollected = list.includes(this.bookrecno);
},
toggleCollect() {
let list = uni.getStorageSync('collectList') || [];
if (this.isCollected) { if (this.isCollected) {
//
collectList = collectList.filter((item) => item !== this.bookIsbn);
uni.showToast({ title: "取消收藏成功", icon: "success" });
list = list.filter(i => i !== this.bookrecno);
uni.showToast({ title: '取消收藏', icon: 'success' });
} else { } else {
//
collectList.push(this.bookIsbn);
uni.showToast({ title: "收藏成功", icon: "success" });
list.push(this.bookrecno);
uni.showToast({ title: '收藏成功', icon: 'success' });
} }
//
uni.setStorageSync("collectList", collectList);
//
uni.setStorageSync('collectList', list);
this.isCollected = !this.isCollected; this.isCollected = !this.isCollected;
} }
}, },
//
onShareAppMessage() {
return {
title: '名侦探柯南',
path: '/subpkg/pages/book-detail/book-detail?isbn=7-80664-373-7',
imageUrl: 'https://qiniu.aiyxlib.com/1606124577077'
};
}
onShareAppMessage() {
return {
title: this.bookInfo.title || '图书详情',
path: '/subpkg/pages/book-detail/book-detail?bookrecno=' + this.bookrecno,
imageUrl: this.bookInfo.cover
};
}
}; };
</script> </script>

8
subpkg/pages/book-list/book-list.vue

@ -62,12 +62,12 @@ export default {
} }
}, },
//
goToDetail(item) { goToDetail(item) {
//
uni.navigateTo({ uni.navigateTo({
url: "/subpkg/pages/book-detail/book-detail?isbn=" + item.isbn
});
}
url: "/subpkg/pages/book-detail/book-detail?bookData=" + encodeURIComponent(JSON.stringify(item))
})
},
} }
}; };
</script> </script>

76
subpkg/pages/feedback-list/feedback-list.vue

@ -2,6 +2,14 @@
<view class="feedback-page"> <view class="feedback-page">
<!-- 留言列表 --> <!-- 留言列表 -->
<view class="feedback-list"> <view class="feedback-list">
<!-- 空状态无留言时显示 -->
<view class="empty-box" v-if="feedbackList.length === 0">
<uni-icons style="margin-left: 20px;" custom-prefix="iconfont" type="icon-kongshuju" size="80" color="#ccc"></uni-icons>
<text style="margin-top: 20px;">暂无留言</text>
</view>
<!-- 有数据时显示列表 -->
<view <view
class="feedback-item" class="feedback-item"
v-for="(item, index) in feedbackList" v-for="(item, index) in feedbackList"
@ -12,7 +20,7 @@
<view class="user-info"> <view class="user-info">
<image <image
class="avatar" class="avatar"
:src="item.avatar || '@/static/images/avatar.png'"
:src="item.avatar || '/static/images/avatar.png'"
mode="aspectFill" mode="aspectFill"
></image> ></image>
<text class="username">{{ item.nickname }}</text> <text class="username">{{ item.nickname }}</text>
@ -37,8 +45,8 @@
</view> </view>
</view> </view>
<!-- 写留言按钮 -->
<view class="write-btn-box">
<!-- 写留言按钮 有数据才显示 -->
<view class="write-btn-box" v-if="feedbackList.length > 0">
<button class="write-btn" @click="goWriteComment">写留言</button> <button class="write-btn" @click="goWriteComment">写留言</button>
</view> </view>
</view> </view>
@ -48,58 +56,17 @@
export default { export default {
data() { data() {
return { return {
//
feedbackList: [
{
nickname: "用户1",
avatar: "/static/images/avatar.png",
content: "这篇文章充满了激情,从字里行间能体会到作者的喜爱之情,全文层次清晰,语句流畅,叙事生动具体,趣味性强。",
createTime: "2026-04-23 10:23:01",
likeNum: 12,
isLike: false
},
{
nickname: "热情读者",
avatar: "/static/images/avatar.png",
content: "写得非常棒!情感真挚,结构完整,非常有感染力,值得大家阅读学习。",
createTime: "2026-04-23 11:10:05",
likeNum: 8,
isLike: false
},
{
nickname: "热情读者",
avatar: "/static/images/avatar.png",
content: "写得非常棒!情感真挚,结构完整,非常有感染力,值得大家阅读学习。",
createTime: "2026-04-23 11:10:05",
likeNum: 8,
isLike: false
},
{
nickname: "热情读者",
avatar: "/static/images/avatar.png",
content: "写得非常棒!情感真挚,结构完整,非常有感染力,值得大家阅读学习。",
createTime: "2026-04-23 11:10:05",
likeNum: 8,
isLike: false
}
]
feedbackList: [] //
// feedbackList: []
}; };
}, },
methods: { methods: {
//
likeComment(index) { likeComment(index) {
const item = this.feedbackList[index]; const item = this.feedbackList[index];
if (item.isLike) {
//
item.likeNum--;
} else {
//
item.likeNum++;
}
item.isLike ? item.likeNum-- : item.likeNum++;
item.isLike = !item.isLike; item.isLike = !item.isLike;
}, },
//
goWriteComment() { goWriteComment() {
uni.navigateTo({ uni.navigateTo({
url: '/subpkg/pages/feedback/feedback' url: '/subpkg/pages/feedback/feedback'
@ -119,6 +86,18 @@ export default {
.feedback-list { .feedback-list {
height: calc(100vh - 80px); height: calc(100vh - 80px);
overflow-y: scroll; overflow-y: scroll;
padding: 10px;
}
/* 空状态 */
.empty-box {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
height: calc(100vh - 200px);
color: #999;
font-size: 15px;
} }
/* 单条留言 */ /* 单条留言 */
@ -130,7 +109,6 @@ export default {
box-shadow: 0 1px 6px rgba(0, 0, 0, 0.06); box-shadow: 0 1px 6px rgba(0, 0, 0, 0.06);
} }
/* 顶部:头像 + 用户名 + 点赞 */
.feedback-item-top { .feedback-item-top {
display: flex; display: flex;
justify-content: space-between; justify-content: space-between;
@ -156,7 +134,6 @@ export default {
font-weight: 500; font-weight: 500;
} }
/* 点赞 */
.like-box { .like-box {
display: flex; display: flex;
align-items: center; align-items: center;
@ -168,7 +145,6 @@ export default {
margin-left: 4px; margin-left: 4px;
} }
/* 留言内容 */
.feedback-content { .feedback-content {
line-height: 1.6; line-height: 1.6;
} }

335
subpkg/pages/myLending/myLending copy.vue

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

530
subpkg/pages/myLending/myLending.vue

@ -1,5 +1,6 @@
<template> <template>
<view class="lending-container"> <view class="lending-container">
<!-- 固定只有两个 tab -->
<view class="tab-sticky"> <view class="tab-sticky">
<my-tabs <my-tabs
:tabData="tabData" :tabData="tabData"
@ -16,32 +17,45 @@
@animationfinish="onSwiperEnd" @animationfinish="onSwiperEnd"
@change="onSwiperChange" @change="onSwiperChange"
> >
<swiper-item v-for="(tabItem, idx) in tabData" :key="idx">
<!-- 全部历史借阅 -->
<swiper-item>
<view class="list-wrapper"> <view class="list-wrapper">
<!-- 首次加载 -->
<uni-load-more status="loading" v-if="loadingMap[tabItem.status]" />
<uni-load-more status="loading" v-if="loadingAll" />
<!-- 空数据 -->
<view class="empty" v-else-if="!listData[tabItem.status] || listData[tabItem.status].length === 0">
暂无{{ tabItem.label }}记录
<view class="empty" v-else-if="allList.length === 0">
暂无全部记录
</view> </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>
<!-- 上拉加载更多 -->
<lending-list-item
class="list-item-all"
v-for="(item, index) in allList"
:key="index"
:data="item"
:is-lending="false"
/>
<uni-load-more <uni-load-more
v-if="listData[tabItem.status] && listData[tabItem.status].length > 0 && !loadingMap[tabItem.status]"
:status="loadMoreStatusMap[tabItem.status]"
v-if="allList.length > 0 && hasMoreAll"
:status="loadMoreStatusAll"
/>
</view>
</swiper-item>
<!-- 在借中 -->
<swiper-item>
<view class="list-wrapper">
<uni-load-more status="loading" v-if="loadingLending" />
<view class="empty" v-else-if="lendingList.length === 0">
暂无在借记录
</view>
<lending-list-item
class="list-item-lending"
v-for="(item, index) in lendingList"
:key="index"
:data="item"
:is-lending="true"
/> />
</view> </view>
</swiper-item> </swiper-item>
@ -50,6 +64,8 @@
</template> </template>
<script> <script>
import { FetchInitScreenSetting } from '@/api/user';
import { FetchHistoryloan, FetchRdloanlist, FetchCoverByISBN } from '@/api/book';
import myTabs from "@/components/my-tabs/my-tabs.vue"; import myTabs from "@/components/my-tabs/my-tabs.vue";
import lendingListItem from "@/components/lending-list-item/lending-list-item.vue"; import lendingListItem from "@/components/lending-list-item/lending-list-item.vue";
@ -57,382 +73,228 @@ export default {
components: { myTabs, lendingListItem }, components: { myTabs, lendingListItem },
data() { data() {
return { return {
screenConfig: {},
tabData: [ tabData: [
{ label: "全部", status: "all", apiStatus: -1 },
{ label: "在借中", status: "lending", apiStatus: 0 },
{ label: "将过期", status: "expiring", apiStatus: 1 },
{ label: "已过期", status: "expired", apiStatus: 2 },
{ label: '历史借阅', status: 'all' },
{ label: '在借中', status: 'lending' },
], ],
currentIndex: 0, currentIndex: 0,
listData: {},
loadingMap: {}, // / loading
loadMoreStatusMap: {}, // loading / no-more / ""
pageMap: {}, //
sizeMap: {}, //
hasMoreMap: {}, //
//
allList: [],
loadingAll: false,
pageAll: 1,
hasMoreAll: true,
loadMoreStatusAll: '',
//
lendingList: [],
loadingLending: false,
swiperHeightData: {}, swiperHeightData: {},
currentSwiperHeight: 400, currentSwiperHeight: 400,
currentPageScrollTop: 0,
isRefreshing: false //
}; };
}, },
onLoad() {
this.initDataStructure();
// tab
const firstTab = this.getCurrentTab();
if (firstTab) {
this.getListData(firstTab.status);
}
},
onShow() {
// tabIndex
const tabIndex = uni.getStorageSync('switch_tab_index');
if (tabIndex !== undefined && tabIndex !== '') {
this.currentIndex = Number(tabIndex);
onLoad() {
this.getScreenSetting();
},
// tab
const currentTab = this.getCurrentTab();
if (currentTab) {
this.getListData(currentTab.status);
onShow() {
const tabIndex = uni.getStorageSync('switch_tab_index');
if (tabIndex !== '') {
this.currentIndex = Number(tabIndex);
uni.removeStorageSync('switch_tab_index');
} }
},
//
uni.removeStorageSync('switch_tab_index');
}
},
//
onPullDownRefresh() { onPullDownRefresh() {
this.isRefreshing = true;
const currentTab = this.getCurrentTab();
this.refreshList(currentTab.status);
if (this.currentIndex === 0) {
this.getAllList(true);
} else {
this.getLendingList();
}
}, },
//
onReachBottom() { onReachBottom() {
const currentTab = this.getCurrentTab();
this.loadMoreList(currentTab.status);
},
onPageScroll(res) {
this.currentPageScrollTop = res.scrollTop;
if (this.currentIndex === 0 && this.hasMoreAll) {
this.getAllList();
}
}, },
methods: { methods: {
//
initDataStructure() {
this.tabData.forEach(tab => {
const key = tab.status;
this.$set(this.listData, key, []);
this.$set(this.loadingMap, key, true);
this.$set(this.loadMoreStatusMap, key, "");
this.$set(this.pageMap, key, 1);
this.$set(this.sizeMap, key, 10);
this.$set(this.hasMoreMap, key, true);
});
//
async getBookListWithCovers(bookList) {
const bookWithCovers = await Promise.all(
bookList.map(async (item) => {
if (!item.isbn) return { ...item, cover: '' };
const isbn = item.isbn.replace(/\-/g, '').trim();
try {
const coverRes = await FetchCoverByISBN({ isbn });
return { ...item, cover: coverRes || '' };
} catch (e) {
console.error(`获取ISBN:${isbn}封面失败`, e);
return { ...item, cover: '' };
}
})
);
return bookWithCovers;
}, },
getCurrentTab() {
return this.tabData[this.currentIndex];
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 || ''
};
// tab
this.getAllList(true);
} catch (err) {}
}, },
// /
async getListData(statusKey, isRefresh = false) {
const tab = this.tabData.find(item => item.status === statusKey);
if (!tab) return;
async getAllList(isRefresh = false) {
if (this.loadingAll) return;
this.loadingAll = true;
if (!isRefresh && this.listData[statusKey]?.length > 0) {
this.loadingMap[statusKey] = false;
return;
if (isRefresh) {
this.pageAll = 1;
this.allList = [];
this.hasMoreAll = true;
this.loadMoreStatusAll = '';
} }
this.loadingMap[statusKey] = true;
this.pageMap[statusKey] = 1;
try { try {
const data = await this.fetchBorrowList(tab.apiStatus, this.pageMap[statusKey], this.sizeMap[statusKey]);
let list = data.records || [];
list = 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;
});
this.listData[statusKey] = list;
this.hasMoreMap[statusKey] = list.length === this.sizeMap[statusKey];
this.loadMoreStatusMap[statusKey] = this.hasMoreMap[statusKey] ? "" : "no-more";
const params = {
...this.screenConfig,
rdid: uni.getStorageSync('currentReaderCard'),
logtype: '30002',
page: this.pageAll,
rows: 10,
};
const res = await FetchHistoryloan(params);
const result = typeof res.data === 'string' ? JSON.parse(res.data) : res.data;
let list = result.hloanlist || [];
//
// list = await this.getBookListWithCovers(list);
this.allList = [...this.allList, ...list];
this.hasMoreAll = list.length === 10;
this.loadMoreStatusAll = this.hasMoreAll ? '' : 'no-more';
if (this.hasMoreAll) this.pageAll++;
} catch (err) { } catch (err) {
this.listData[statusKey] = [];
} finally { } finally {
this.loadingMap[statusKey] = false;
this.isRefreshing = false;
this.loadingAll = false;
uni.stopPullDownRefresh(); uni.stopPullDownRefresh();
setTimeout(async () => {
this.calcSwiperHeight(statusKey);
}, 0);
setTimeout(() => {
this.calcHeight('all');
}, 100);
} }
}, },
//
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);
async getLendingList() {
this.loadingLending = true;
try { try {
const data = await this.fetchBorrowList(tab.apiStatus, this.pageMap[statusKey], this.sizeMap[statusKey]);
let newList = data.records || [];
newList = newList.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;
});
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";
}
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;
let list = result.loanlist || [];
//
// list = await this.getBookListWithCovers(list);
this.lendingList = list.map(item => ({
...item,
loantime: item.loandate || '',
returntime: item.returndate || '',
}));
} catch (err) { } catch (err) {
this.loadMoreStatusMap[statusKey] = "";
}
setTimeout(async () => {
this.calcSwiperHeight(statusKey);
}, 0);
},
//
refreshList(statusKey) {
this.getListData(statusKey, true);
},
//
async fetchBorrowList(apiStatus, pageNum, pageSize) {
return new Promise((resolve) => {
} finally {
this.loadingLending = false;
uni.stopPullDownRefresh();
setTimeout(() => { 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" //
}
];
if (pageNum >= 3) { // 3
return [];
}
const 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 [];
this.calcHeight('lending');
}, 100);
} }
}, },
// swiper
calcSwiperHeight(statusKey) {
const selector = `.list-item-${statusKey}`;
//
calcHeight(type) {
const selector = `.list-item-${type}`;
const query = uni.createSelectorQuery().in(this); const query = uni.createSelectorQuery().in(this);
query.selectAll(selector).boundingClientRect((res) => { query.selectAll(selector).boundingClientRect((res) => {
let total = 200;
if (res?.length) {
total = res.reduce((t, h) => t + h.height + 8, 0);
let totalHeight = 200;
if (res && res.length > 0) {
totalHeight = res.reduce((sum, rect) => sum + rect.height, 0);
totalHeight += res.length * 10 + 40;
} }
this.swiperHeightData[statusKey] = total;
this.currentSwiperHeight = total;
this.swiperHeightData[type] = totalHeight;
this.currentSwiperHeight = totalHeight;
}).exec(); }).exec();
}, },
// tab
tabClick(index) { tabClick(index) {
this.currentIndex = index; this.currentIndex = index;
const tab = this.getCurrentTab();
if (this.currentPageScrollTop > 100) {
uni.pageScrollTo({ scrollTop: 0, duration: 100 });
}
if (!this.listData[tab.status]?.length) {
this.getListData(tab.status);
if (index === 0) {
if (this.allList.length === 0) {
this.getAllList(true);
} else {
this.currentSwiperHeight = this.swiperHeightData.all || 600;
}
} else { } else {
this.currentSwiperHeight = this.swiperHeightData[tab.status] || 400;
if (this.lendingList.length === 0) {
this.getLendingList();
} else {
this.currentSwiperHeight = this.swiperHeightData.lending || 600;
}
} }
}, },
onSwiperChange(e) { onSwiperChange(e) {
if (e.detail.source === "touch") {
this.currentIndex = e.detail.current;
}
if (e.detail.source === 'touch') this.currentIndex = e.detail.current;
}, },
onSwiperEnd() { onSwiperEnd() {
const tab = this.getCurrentTab();
if (!this.listData[tab.status]?.length) {
this.getListData(tab.status);
} else {
this.currentSwiperHeight = this.swiperHeightData[tab.status] || 400;
}
this.tabClick(this.currentIndex);
}, },
goToDetail(item) {
uni.navigateTo({
url: `/subpkg/pages/book-detail/book-detail?isbn=${item.isbn}`
});
}
} }
}; };
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
.lending-container { .lending-container {
background-color: #f5f5f5;
background: #f5f5f5;
min-height: 100vh; 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;
.tab-sticky {
position: sticky;
top: 0;
z-index: 99;
background: #fff;
}
.swiper {
width: 100%;
transition: height 0.2s ease;
}
.list-wrapper {
padding: 10px;
box-sizing: border-box;
}
.empty {
text-align: center;
padding: 100px 0;
color: #999;
} }
</style> </style>

217
subpkg/pages/ranking/ranking.vue

@ -9,13 +9,19 @@
<view class="ranking-header-txt"> <view class="ranking-header-txt">
<view class="ranking-title"> <view class="ranking-title">
<uni-icons custom-prefix="iconfont" type="icon-paihangbang2" size="20"></uni-icons> <uni-icons custom-prefix="iconfont" type="icon-paihangbang2" size="20"></uni-icons>
<text class="title-text">借阅排行</text>
<text class="title-text">读者借阅排行</text>
</view> </view>
<text class="ranking-header-tip">由本图书馆累计借阅而定期生成排行榜单</text>
<!-- <text class="ranking-num">{{ bookList.length }}</text> -->
<text class="ranking-header-tip">由本图书馆近30天读者借阅次数生成排行榜单</text>
</view> </view>
</view> </view>
<view class="ranking-list"> <view class="ranking-list">
<!-- 空状态 -->
<view class="empty-box" v-if="rankingData.length === 0">
<uni-icons type="cube" size="70" color="#ccc"></uni-icons>
<text>暂无读者排行数据</text>
</view>
<view <view
class="ranking-item" class="ranking-item"
:class="[ :class="[
@ -23,7 +29,7 @@
index === 1 ? 'two-item' : '', index === 1 ? 'two-item' : '',
index === 2 ? 'three-item' : '' index === 2 ? 'three-item' : ''
]" ]"
v-for="(item, index) in bookList"
v-for="(item, index) in rankingData"
:key="index" :key="index"
> >
<!-- 第一名 --> <!-- 第一名 -->
@ -32,129 +38,106 @@
<uni-icons class="ranking-icon" v-if="index === 1" custom-prefix="iconfont" type="icon-TOP" size="26" color="#a2b2c3"></uni-icons> <uni-icons class="ranking-icon" v-if="index === 1" custom-prefix="iconfont" type="icon-TOP" size="26" color="#a2b2c3"></uni-icons>
<!-- 第三名 --> <!-- 第三名 -->
<uni-icons class="ranking-icon" v-if="index === 2" custom-prefix="iconfont" type="icon-TOP1" size="26" color="#D0BA9D"></uni-icons> <uni-icons class="ranking-icon" v-if="index === 2" custom-prefix="iconfont" type="icon-TOP1" size="26" color="#D0BA9D"></uni-icons>
<!-- 4~15 显示数字 -->
<!-- 4~10 显示数字 -->
<view v-if="index >= 3" class="ranking-common-icon"> <view v-if="index >= 3" class="ranking-common-icon">
<uni-icons custom-prefix="iconfont" type="icon-tag" size="28" color="#8899ab"></uni-icons> <uni-icons custom-prefix="iconfont" type="icon-tag" size="28" color="#8899ab"></uni-icons>
<text class="common-num">{{ index + 1 }}</text> <text class="common-num">{{ index + 1 }}</text>
</view> </view>
<!-- 书籍封面 -->
<!-- 书籍封面 -->
<view class="ranking-item-img"> <view class="ranking-item-img">
<image <image
class="book-cover" class="book-cover"
:src="item.cover"
:src="item.cover || defaultCover"
mode="scaleToFill" mode="scaleToFill"
@error="onImgError"
/> />
</view> </view>
<!-- 书籍信息 --> <!-- 书籍信息 -->
<view class="ranking-book-info"> <view class="ranking-book-info">
<text class="book-info-title">{{ item.title }}</text>
<text class="book-info-name">{{ item.author }}</text>
<text class="book-info-desc line-clamp-3 ">{{ item.desc }}</text>
<text class="book-info-title">{{ item.TITLE || '暂无书名' }}</text>
<text class="book-info-name">{{ item.AUTHOR || '佚名' }}</text>
<text class="book-info-desc line-clamp-3">
借阅次数{{ item.TOTALNUM || 0 }}
</text>
</view> </view>
</view> </view>
</view> </view>
</view> </view>
</template> </template>
<script> <script>
export default {
data() {
return {
bookList: [
{
cover: "https://qiniu.aiyxlib.com/1606124577077",
title: "名侦探柯南",
author: "青山刚昌",
desc: "《名侦探柯南》是日本漫画家青山刚昌创作的著名侦探漫画作品,讲述了高中生侦探工藤新一被灌下毒药后身体缩小,化名为江户川柯南,秘密调查黑暗组织,并解决无数离奇案件的故事。作品兼具推理、冒险、情感与幽默元素,是全球极具影响力的经典漫画。"
},
{
cover: "https://qiniu.aiyxlib.com/1606124577077",
title: "海贼王",
author: "尾田荣一郎",
desc: "《海贼王》讲述了主角蒙奇·D·路飞为了成为海贼王,与伙伴们在伟大航路上冒险,寻找传说中的One Piece的故事。"
},
{
cover: "https://qiniu.aiyxlib.com/1606124577077",
title: "火影忍者",
author: "岸本齐史",
desc: "《火影忍者》讲述了鸣人从小被孤立,通过努力成为忍者,最终成为英雄守护村子的故事。"
},
{
cover: "https://qiniu.aiyxlib.com/1606124577077",
title: "三体",
author: "刘慈欣",
desc: "《三体》是刘慈欣创作的长篇科幻小说,讲述了人类与三体文明之间的生死博弈。"
},
{
cover: "https://qiniu.aiyxlib.com/1606124577077",
title: "盗墓笔记",
author: "南派三叔",
desc: "《盗墓笔记》讲述了吴邪等人在古墓中探险,揭开一系列神秘谜团的故事。"
},
{
cover: "https://qiniu.aiyxlib.com/1606124577077",
title: "斗罗大陆",
author: "唐家三少",
desc: "《斗罗大陆》讲述了唐三穿越到斗罗大陆,一步步成为魂师巅峰的故事。"
},
{
cover: "https://qiniu.aiyxlib.com/1606124577077",
title: "斗破苍穹",
author: "天蚕土豆",
desc: "《斗破苍穹》讲述了萧炎从废柴少年逆袭成为斗气大陆最强者的故事。"
},
{
cover: "https://qiniu.aiyxlib.com/1606124577077",
title: "西游记",
author: "吴承恩",
desc: "《西游记》是中国古典四大名著之一,讲述了唐僧师徒西天取经的故事。"
},
{
cover: "https://qiniu.aiyxlib.com/1606124577077",
title: "红楼梦",
author: "曹雪芹",
desc: "《红楼梦》以贾史王薛四大家族为背景,讲述了贾宝玉与林黛玉的爱情悲剧。"
},
{
cover: "https://qiniu.aiyxlib.com/1606124577077",
title: "水浒传",
author: "施耐庵",
desc: "《水浒传》讲述了108位好汉在梁山起义,最终接受招安的故事。"
},
{
cover: "https://qiniu.aiyxlib.com/1606124577077",
title: "三国演义",
author: "罗贯中",
desc: "《三国演义》描写了从东汉末年到西晋初年的群雄割据与英雄争霸。"
},
{
cover: "https://qiniu.aiyxlib.com/1606124577077",
title: "沉默的大多数",
author: "王小波",
desc: "《沉默的大多数》是王小波的杂文精选,探讨了社会、文化与人性。"
},
{
cover: "https://qiniu.aiyxlib.com/1606124577077",
title: "活着",
author: "余华",
desc: "《活着》讲述了福贵在苦难中坚守生命的故事。"
},
{
cover: "https://qiniu.aiyxlib.com/1606124577077",
title: "人间失格",
author: "太宰治",
desc: "《人间失格》讲述了主人公叶藏从青年到颓废堕落的人生历程。"
},
{
cover: "https://qiniu.aiyxlib.com/1606124577077",
title: "解忧杂货店",
author: "东野圭吾",
desc: "《解忧杂货店》讲述了一家神奇杂货店帮助人们解决烦恼的温暖故事。"
}
]
};
}
}
import { FetchInitScreenSetting } from '@/api/user';
import { FetchBookRanking } from '@/api/book';
export default {
data() {
return {
rankingData: [],
defaultCover: '/static/images/default-book.png',
};
},
onLoad() {
this.getScreenConfig();
},
methods: {
async getScreenConfig() {
try {
const res = await FetchInitScreenSetting({ libcode: '1201' });
this.getReadRanking(res.data);
} catch (err) {
console.log(err)
}
},
getFormattedDate(date) {
const year = date.getFullYear();
const month = date.getMonth() + 1;
const day = date.getDate();
return `${year}-${month < 10 ? '0' + month : month}-${day < 10 ? '0' + day : day}`;
},
getReadRanking(result) {
const currentDate = new Date()
currentDate.setDate(currentDate.getDate() - 30)
const year = currentDate.getFullYear()
const month = currentDate.getMonth() + 1
const day = currentDate.getDate()
const formattedDate = `${year}-${month < 10 ? '0' + month : month}-${day < 10 ? '0' + day : day}`
const params = {
'libcode': '1201',
'starttime': formattedDate,
'endtime': this.getFormattedDate(new Date()),
'rownum': 10,
'thirdAppid': result.open_lib_appId?.context || '',
'thirdSecret': result.open_lib_secret?.context || '',
'thirdUrl': result.open_lib_http?.context || ''
}
FetchBookRanking(params).then(res => {
console.log('排行接口返回数据', res)
const innerStr = res.data;
const resultJson = JSON.parse(innerStr);
console.log(resultJson)
if (resultJson.success && resultJson.resultlist.length > 0) {
//
this.rankingData = resultJson.resultlist.sort((a, b) => b.TOTALNUM - a.TOTALNUM);
} else {
this.rankingData = [];
}
}).catch(error => {
console.error('排行接口错误', error)
this.rankingData = [];
})
}
}
}
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
@ -186,10 +169,9 @@
color: #01a4fe !important; color: #01a4fe !important;
} }
.title-text{ .title-text{
// = +
text-shadow: 0 2px 4px rgba(0, 0, 0, 0.15), text-shadow: 0 2px 4px rgba(0, 0, 0, 0.15),
0 1px 2px rgba(0, 0, 0, 0.1); 0 1px 2px rgba(0, 0, 0, 0.1);
letter-spacing: 1px; /* 文字间距更舒服 */
letter-spacing: 1px;
margin-left: 8px; margin-left: 8px;
} }
} }
@ -208,7 +190,20 @@
margin-top: -30px; margin-top: -30px;
z-index: 999; z-index: 999;
padding: 20px 0; padding: 20px 0;
min-height: 60vh;
} }
/* 空状态 */
.empty-box {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
height: 50vh;
color: #999;
font-size: 15px;
}
.ranking-item{ .ranking-item{
position: relative; position: relative;
display: flex; display: flex;
@ -258,8 +253,6 @@
position: absolute; position: absolute;
right: 10px; right: 10px;
top: -10px; top: -10px;
// width: 64px;
// height: 28px;
} }
.ranking-common-icon{ .ranking-common-icon{
position: absolute; position: absolute;

189
subpkg/pages/unbind-card/unbind-card.vue

@ -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>
Loading…
Cancel
Save