You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
495 lines
14 KiB
495 lines
14 KiB
<template>
|
|
<view class="detail-container">
|
|
<!-- 书籍头部信息 -->
|
|
<view class="article-detail-container">
|
|
<view class="article-detail-left">
|
|
<view class="article-detail-title">{{ bookInfo.title || bookInfo.name || '暂无书名' }}</view>
|
|
<view class="article-detail-info">
|
|
<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.pubdate || '暂无' }}</view>
|
|
</view>
|
|
</view>
|
|
<view class="article-detail-right">
|
|
<image
|
|
class="img-item"
|
|
:src="bookInfo.cover || bookInfo.base64Cover || '/static/images/default-book.png'"
|
|
mode="widthFix"
|
|
@error="onImgError"
|
|
></image>
|
|
</view>
|
|
</view>
|
|
|
|
<!-- 馆藏信息 -->
|
|
<view v-if="holdingsData.length!==0" class="book-store-info">
|
|
<view class="book-store-info-item">
|
|
<text class="store-txt1">{{ holdingsData.length || 0 }}</text>
|
|
<text class="store-txt2">馆藏总数</text>
|
|
</view>
|
|
<view class="book-store-info-item">
|
|
<text class="store-txt1">{{ inLibraryCount }}</text>
|
|
<text class="store-txt2">在馆(本)</text>
|
|
</view>
|
|
<view class="book-store-info-item">
|
|
<text class="store-txt1">{{ lendCount }}</text>
|
|
<text class="store-txt2">借出(本)</text>
|
|
</view>
|
|
</view>
|
|
|
|
<!-- 索书号/条码号 -->
|
|
<view v-if="holdingsData.length!==0" class="store-info-list">
|
|
<view class="store-info-item" v-for="(item, index) in holdingsData" :key="index">
|
|
<view>
|
|
<text class="info-item-title">条码号</text>
|
|
<text>{{ item.barcode || '暂无' }}</text>
|
|
</view>
|
|
<view >
|
|
<text class="info-item-title">索书号</text>
|
|
<text>{{ item.callno || '暂无' }}</text>
|
|
</view>
|
|
<view>
|
|
<text class="info-item-title">馆藏状态</text>
|
|
<text>{{ getStateText(item.state) }}</text>
|
|
</view>
|
|
<view>
|
|
<text class="info-item-title">所在馆</text>
|
|
<text>{{ getLibraryName(item.orglib) }}</text>
|
|
</view>
|
|
<view>
|
|
<text class="info-item-title">当前馆藏地</text>
|
|
<text>{{ getLocationName(item.orglocal) }}</text>
|
|
</view>
|
|
</view>
|
|
</view>
|
|
|
|
<!-- TAB 选项卡 -->
|
|
<view class="content-tab">
|
|
<view class="tab-item" :class="{active: currentTab === 0}" @click="currentTab = 0">
|
|
内容简介
|
|
</view>
|
|
<view class="tab-item" :class="{active: currentTab === 1}" @click="currentTab = 1">
|
|
作者简介
|
|
</view>
|
|
</view>
|
|
|
|
<!-- TAB 内容 -->
|
|
<view class="content-item" v-if="currentTab === 0">
|
|
{{ bookInfo.summary || bookInfo.explain || '暂无内容简介' }}
|
|
</view>
|
|
<view class="content-item" v-if="currentTab === 1">
|
|
{{ bookInfo.authorbio || '暂无作者简介' }}
|
|
</view>
|
|
|
|
<view class="detail-bottom">
|
|
<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 v-if="!fromRecommend" 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>
|
|
</template>
|
|
|
|
<script>
|
|
import { FetchInitScreenSetting } from '@/api/user';
|
|
import { FetchFindbookByQuery, FetchDictionaryTree,FetchCollectionBook,FetchCancelCollectionBook } from '@/api/book';
|
|
import { getOpenId } from '@/utils/storage';
|
|
import config from '@/utils/config';
|
|
import { fetchBookCoverBase64 } from '@/utils/bookCover';
|
|
|
|
export default {
|
|
data() {
|
|
return {
|
|
baseUrl: config.baseUrl,
|
|
currentTab: 0,
|
|
isCollected: false,
|
|
bookrecno: '',
|
|
opacUrl: '',
|
|
fromRecommend: false,
|
|
bookInfo: {},
|
|
searchListInfo: {},
|
|
holdingsData: [],
|
|
dictionaryTree: {},
|
|
libraryMap: {},
|
|
locationMap: {}
|
|
};
|
|
},
|
|
onLoad(options) {
|
|
// 1. 首页/列表页 直接带完整 bookData 过来,优先走这个
|
|
if (options.bookData) {
|
|
const bookData = JSON.parse(decodeURIComponent(options.bookData));
|
|
this.bookInfo = bookData;
|
|
this.bookrecno = bookData.bookrecno || "";
|
|
this.fromRecommend = options.fromRecommend === 'true';
|
|
this.holdingsData = [];
|
|
this.checkCollectStatus();
|
|
return;
|
|
}
|
|
console.log('options',options)
|
|
// 2. 检索页只传 bookrecno,请求接口拿详情
|
|
if(options.searchData){
|
|
const bookData = JSON.parse(decodeURIComponent(options.searchData));
|
|
this.searchListInfo = bookData;
|
|
this.bookrecno = bookData.bookrecno || "";
|
|
this.bookInfo = bookData;
|
|
|
|
}
|
|
|
|
// 3. 如果从收藏列表进入,直接设置为已收藏状态
|
|
if (options.isCollected === 'true') {
|
|
this.isCollected = true;
|
|
}
|
|
|
|
this.fromRecommend = false;
|
|
this.getOpacUrl();
|
|
this.getDictionaryTree();
|
|
},
|
|
computed: {
|
|
// 在馆数量
|
|
// 馆藏状态,编目=1,在馆=2,借出=3,丢失=4,剔除=5,交换=6,赠送=7,装订=8,锁定=9,预借=10, 清点=12
|
|
inLibraryCount() {
|
|
return this.holdingsData.filter(item => item.state === 2).length;
|
|
},
|
|
// 借出数量(新增)
|
|
lendCount() {
|
|
return this.holdingsData.filter(item => item.state === 3).length;
|
|
},
|
|
// 第一个馆藏状态
|
|
firstStateText() {
|
|
if (this.holdingsData.length === 0) return '无馆藏';
|
|
return this.getStateText(this.holdingsData[0].state);
|
|
}
|
|
},
|
|
methods: {
|
|
onImgError(e) {
|
|
e.target.src = "/static/images/default-book.png";
|
|
},
|
|
// 加载图书封面
|
|
async loadBookCover() {
|
|
console.log('this.bookInfo',this.bookInfo)
|
|
if (!this.bookInfo.isbn) return;
|
|
|
|
try {
|
|
const coverUrl = await fetchBookCoverBase64(this.bookInfo.isbn);
|
|
if (coverUrl) {
|
|
this.$set(this.bookInfo, 'cover', coverUrl);
|
|
}
|
|
} catch (error) {
|
|
console.error('获取封面失败:', error);
|
|
}
|
|
},
|
|
// 获取字典项 - 馆代码
|
|
async getDictionaryTree() {
|
|
try {
|
|
const res = await FetchDictionaryTree();
|
|
console.log('dictionaryTree',res);
|
|
const data = res.data || [];
|
|
|
|
// 1. 找到分馆字典项
|
|
const fgItem = data.find(item => item.dictionaryCode === 'FG' && item.dictionaryName === '分馆');
|
|
if (fgItem && fgItem.childDictionarys) {
|
|
// 建立分馆映射:dictionaryCode -> dictionaryName
|
|
this.libraryMap = {};
|
|
fgItem.childDictionarys.forEach(child => {
|
|
this.libraryMap[child.dictionaryCode] = child.dictionaryName;
|
|
});
|
|
}
|
|
|
|
// 2. 找到馆藏地点字典项
|
|
const gcdItem = data.find(item => item.dictionaryCode === 'GCD' && item.dictionaryName === '馆藏地点');
|
|
if (gcdItem && gcdItem.childDictionarys) {
|
|
// 建立馆藏地点映射:dictionaryCode -> dictionaryName
|
|
this.locationMap = {};
|
|
gcdItem.childDictionarys.forEach(child => {
|
|
this.locationMap[child.dictionaryCode] = child.dictionaryName;
|
|
});
|
|
}
|
|
|
|
this.dictionaryTree = data;
|
|
} catch (err) {
|
|
console.error('获取字典树失败', err);
|
|
}
|
|
},
|
|
// 获取配置
|
|
async getOpacUrl() {
|
|
try {
|
|
const res = await FetchInitScreenSetting({ libcode: config.LIB_CODE });
|
|
this.opacUrl = res.data.opac_url?.context || '';
|
|
this.getBookDetail();
|
|
} catch (err) {}
|
|
},
|
|
|
|
// 获取图书详情
|
|
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);
|
|
// console.log('bookrecno详情',res);
|
|
|
|
const apiData = res.data || {};
|
|
this.bookInfo = apiData.biblios || {};
|
|
// console.log('bookrecno-bookInfo详情',this.bookInfo);
|
|
this.holdingsData = apiData.holdings || [];
|
|
// console.log('bookrecno-holdingsData详情',this.holdingsData);
|
|
|
|
// 加载封面
|
|
await this.loadBookCover();
|
|
|
|
// 检查收藏状态
|
|
this.checkCollectStatus();
|
|
} catch (err) {
|
|
console.error(err);
|
|
} finally {
|
|
uni.hideLoading();
|
|
}
|
|
},
|
|
|
|
// 馆藏状态文字
|
|
getStateText(state) {
|
|
const map = {
|
|
1: '编目',
|
|
2: '在馆',
|
|
3: '借出',
|
|
4: '丢失',
|
|
5: '剔除',
|
|
6: '交换',
|
|
7: '赠送',
|
|
8: '装订',
|
|
9: '锁定',
|
|
10: '预借',
|
|
12: '清点'
|
|
}
|
|
return map[state] || '未知';
|
|
},
|
|
|
|
// 获取所在馆名称
|
|
getLibraryName(orglib) {
|
|
if (!orglib) return '葛店经济技术开发区图书馆';
|
|
return this.libraryMap[orglib] || orglib || '葛店经济技术开发区图书馆';
|
|
},
|
|
|
|
// 获取馆藏地名称
|
|
getLocationName(orglocal) {
|
|
if (!orglocal) return '葛店图书馆';
|
|
return this.locationMap[orglocal] || orglocal || '葛店图书馆';
|
|
},
|
|
|
|
// 收藏状态检查(如果已经通过参数传入则跳过)
|
|
checkCollectStatus() {
|
|
if (this.isCollected) return;
|
|
const list = uni.getStorageSync('collectList') || [];
|
|
this.isCollected = list.includes(this.bookrecno);
|
|
},
|
|
async toggleCollect() {
|
|
const openId = await getOpenId();
|
|
|
|
if (!openId) {
|
|
uni.showToast({ title: '获取用户信息失败', icon: 'none' });
|
|
return;
|
|
}
|
|
|
|
if (this.isCollected) {
|
|
try {
|
|
console.log(' this.searchListInfo.id',this.searchListInfo.id);
|
|
const res = await FetchCancelCollectionBook({id: this.searchListInfo.id});
|
|
if (res.code === 200) {
|
|
this.isCollected = false;
|
|
// 清空收藏记录 id,避免影响后续收藏操作
|
|
if (this.searchListInfo) {
|
|
this.searchListInfo.id = null;
|
|
}
|
|
// 设置取消收藏标记,通知收藏列表页面刷新
|
|
uni.setStorageSync('needRefreshCollect', true);
|
|
uni.showToast({ title: '取消收藏', icon: 'success' });
|
|
} else {
|
|
uni.showToast({ title: res.message || '取消收藏失败', icon: 'none' });
|
|
}
|
|
} catch (err) {
|
|
console.error('取消收藏图书失败', err);
|
|
uni.showToast({ title: '取消收藏失败', icon: 'none' });
|
|
}
|
|
|
|
} else {
|
|
try {
|
|
const params = {
|
|
...this.searchListInfo,
|
|
openid: openId,
|
|
libcode: config.LIB_CODE
|
|
};
|
|
const res = await FetchCollectionBook(params);
|
|
if (res.code === 200) {
|
|
this.isCollected = true;
|
|
// 保存收藏记录的 id,供后续取消收藏使用
|
|
if (res.data && res.data.id) {
|
|
this.searchListInfo.id = res.data.id;
|
|
}
|
|
uni.showToast({ title: '收藏成功', icon: 'success' });
|
|
} else {
|
|
uni.showToast({ title: res.message || '收藏失败', icon: 'none' });
|
|
}
|
|
} catch (err) {
|
|
console.error('收藏图书失败', err);
|
|
uni.showToast({ title: '收藏失败', icon: 'none' });
|
|
}
|
|
}
|
|
}
|
|
},
|
|
onShareAppMessage() {
|
|
return {
|
|
title: this.bookInfo.title || '图书详情',
|
|
path: '/subpkg/pages/book-detail/book-detail?bookrecno=' + this.bookrecno,
|
|
imageUrl: this.bookInfo.cover
|
|
};
|
|
}
|
|
};
|
|
</script>
|
|
|
|
<style lang="scss" scoped>
|
|
.detail-container {
|
|
padding: 15px;
|
|
background-color: #f5f5f5;
|
|
min-height: 100vh;
|
|
padding-bottom: 60px;
|
|
}
|
|
|
|
.article-detail-container {
|
|
display: flex;
|
|
background-color: #fff;
|
|
border-radius: 10px;
|
|
padding: 20px;
|
|
margin-bottom: 12px;
|
|
}
|
|
.article-detail-left {
|
|
flex: 1;
|
|
}
|
|
.article-detail-title {
|
|
font-size: 20px;
|
|
font-weight: bold;
|
|
color: #333;
|
|
margin-bottom: 10px;
|
|
}
|
|
.article-detail-info {
|
|
font-size: 14px;
|
|
color: #666;
|
|
line-height: 1.5;
|
|
}
|
|
.article-detail-right {
|
|
width: 110px;
|
|
height: 150px;
|
|
margin-left: 15px;
|
|
}
|
|
.article-detail-right image {
|
|
width: 100%;
|
|
height: 100%;
|
|
border-radius: 6px;
|
|
box-shadow: 0 2px 8px rgba(0,0,0,0.1);
|
|
}
|
|
|
|
.book-store-info {
|
|
background-color: #fff;
|
|
border-radius: 10px;
|
|
padding: 20px;
|
|
display: flex;
|
|
justify-content: space-around;
|
|
margin-bottom: 12px;
|
|
}
|
|
.book-store-info-item {
|
|
display: flex;
|
|
flex-direction: column;
|
|
align-items: center;
|
|
}
|
|
.store-txt1 {
|
|
font-size: 22px;
|
|
font-weight: bold;
|
|
color: #2b85e4;
|
|
}
|
|
.store-txt2 {
|
|
font-size: 13px;
|
|
color: #999;
|
|
margin-top: 4px;
|
|
}
|
|
|
|
.store-info-list {
|
|
background-color: #fff;
|
|
border-radius: 10px;
|
|
padding: 10px;
|
|
margin-bottom: 15px;
|
|
}
|
|
.store-info-item {
|
|
display: flex;
|
|
flex-direction: column;
|
|
font-size: 14px;
|
|
color: #666;
|
|
padding: 10px;
|
|
margin-bottom: 10px;
|
|
border: 1px solid #eee;
|
|
border-radius: 10px;
|
|
&:last-child {
|
|
margin-bottom: 0;
|
|
}
|
|
}
|
|
.store-info-item view {
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: flex-start;
|
|
line-height: 28px;
|
|
.info-item-title{
|
|
width: 80px;
|
|
font-weight: bold;
|
|
color: #333;
|
|
}
|
|
}
|
|
|
|
.content-tab {
|
|
display: flex;
|
|
background-color: #fff;
|
|
border-radius: 10px;
|
|
overflow: hidden;
|
|
margin-bottom: 12px;
|
|
}
|
|
.tab-item {
|
|
flex: 1;
|
|
text-align: center;
|
|
padding: 15px 0;
|
|
font-size: 15px;
|
|
color: #666;
|
|
position: relative;
|
|
}
|
|
.tab-item.active {
|
|
color: #2b85e4;
|
|
font-weight: bold;
|
|
}
|
|
.tab-item.active::after {
|
|
content: "";
|
|
position: absolute;
|
|
bottom: 0;
|
|
left: 50%;
|
|
transform: translateX(-50%);
|
|
width: 30px;
|
|
height: 3px;
|
|
background-color: #2b85e4;
|
|
border-radius: 2px;
|
|
}
|
|
|
|
.content-item {
|
|
background-color: #fff;
|
|
border-radius: 10px;
|
|
padding: 20px;
|
|
font-size: 15px;
|
|
color: #333;
|
|
line-height: 1.6;
|
|
}
|
|
.detail-bottom{
|
|
justify-content: space-around;
|
|
}
|
|
</style>
|