|
|
<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 } from '@/api/book';import config from '@/utils/config';
export default { data() { return { baseUrl: config.baseUrl, currentTab: 0, isCollected: false, bookrecno: '', opacUrl: '', fromRecommend: false, bookInfo: {}, 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; }
// 2. 检索页只传 bookrecno,请求接口拿详情
this.bookrecno = options.bookrecno || ""; 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 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);
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() { const list = uni.getStorageSync('collectList') || []; this.isCollected = list.includes(this.bookrecno); }, toggleCollect() { let list = uni.getStorageSync('collectList') || []; if (this.isCollected) { list = list.filter(i => i !== this.bookrecno); uni.showToast({ title: '取消收藏', icon: 'success' }); } else { list.push(this.bookrecno); uni.showToast({ title: '收藏成功', icon: 'success' }); } uni.setStorageSync('collectList', list); this.isCollected = !this.isCollected; } },
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>
|