图书馆小程序
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.

346 lines
8.8 KiB

2 months ago
1 month ago
2 months ago
1 month ago
1 month ago
2 months ago
1 month ago
1 month ago
1 month ago
2 months ago
1 month ago
2 months ago
1 month ago
2 months ago
1 month ago
2 months ago
1 month ago
2 months ago
1 month ago
2 months ago
1 month ago
2 months ago
1 month ago
2 months ago
1 month ago
2 months ago
1 month ago
2 months ago
2 months ago
1 month ago
2 months ago
1 month ago
2 months ago
1 month ago
1 month ago
2 months ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
2 months ago
1 month ago
2 months ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
2 months ago
1 month ago
1 month ago
2 months ago
1 month ago
2 months ago
1 month ago
2 months ago
1 month ago
2 months ago
2 months ago
1 month ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
1 month ago
2 months ago
1 month ago
2 months ago
  1. <template>
  2. <view class="detail-container">
  3. <!-- 书籍头部信息 -->
  4. <view class="article-detail-container">
  5. <view class="article-detail-left">
  6. <view class="article-detail-title">{{ bookInfo.title || bookInfo.name || '暂无书名' }}</view>
  7. <view class="article-detail-info">
  8. <view class="article-detail-author">作者{{ bookInfo.author || '暂无' }}</view>
  9. <view class="article-detail-time">出版社{{ bookInfo.publisher || '暂无' }}</view>
  10. <view class="article-detail-time">ISBN{{ bookInfo.isbn || '暂无' }}</view>
  11. <view class="article-detail-time">出版时间{{ bookInfo.pubdate || '暂无' }}</view>
  12. </view>
  13. </view>
  14. <view class="article-detail-right">
  15. <image
  16. class="img-item"
  17. :src="bookInfo.cover || '/static/images/default-book.png'"
  18. mode="widthFix"
  19. @error="onImgError"
  20. ></image>
  21. </view>
  22. </view>
  23. <!-- 馆藏信息 -->
  24. <view class="book-store-info">
  25. <view class="book-store-info-item">
  26. <text class="store-txt1">{{ holdingsData.length || 0 }}</text>
  27. <text class="store-txt2">馆藏总数</text>
  28. </view>
  29. <view class="book-store-info-item">
  30. <text class="store-txt1">{{ inLibraryCount }}</text>
  31. <text class="store-txt2">在馆</text>
  32. </view>
  33. <view class="book-store-info-item">
  34. <text class="store-txt1">{{ lendCount }}</text>
  35. <text class="store-txt2">借出</text>
  36. </view>
  37. </view>
  38. <!-- 索书号/条码号 -->
  39. <view class="store-info-list">
  40. <view class="store-info-item" v-for="(item, index) in holdingsData" :key="index">
  41. <view>
  42. <text>索书号</text>
  43. <text>{{ item.callno || '暂无' }}</text>
  44. </view>
  45. <view>
  46. <text>条码号</text>
  47. <text>{{ item.barcode || '暂无' }}</text>
  48. </view>
  49. <view>
  50. <text>馆藏状态</text>
  51. <text>{{ getStateText(item.state) }}</text>
  52. </view>
  53. </view>
  54. </view>
  55. <!-- TAB 选项卡 -->
  56. <view class="content-tab">
  57. <view class="tab-item" :class="{active: currentTab === 0}" @click="currentTab = 0">
  58. 内容简介
  59. </view>
  60. <view class="tab-item" :class="{active: currentTab === 1}" @click="currentTab = 1">
  61. 作者简介
  62. </view>
  63. </view>
  64. <!-- TAB 内容 -->
  65. <view class="content-item" v-if="currentTab === 0">
  66. {{ bookInfo.summary || bookInfo.explain || '暂无内容简介' }}
  67. </view>
  68. <view class="content-item" v-if="currentTab === 1">
  69. {{ bookInfo.authorbio || '暂无作者简介' }}
  70. </view>
  71. <view class="detail-bottom">
  72. <button open-type="share" class="handle-btn">
  73. <uni-icons custom-prefix="iconfont" type="icon-fenxiang01" size="20"></uni-icons>
  74. <text class="share-text">分享</text>
  75. </button>
  76. <button class="handle-btn" @click="toggleCollect">
  77. <uni-icons :type="isCollected ? 'heart-filled' : 'heart'" size="20" color="#ff4444"></uni-icons>
  78. <text class="share-text">{{ isCollected ? '已收藏' : '收藏' }}</text>
  79. </button>
  80. </view>
  81. </view>
  82. </template>
  83. <script>
  84. import { FetchInitScreenSetting } from '@/api/user';
  85. import { FetchFindbookByQuery } from '@/api/book';
  86. import config from '@/utils/config';
  87. export default {
  88. data() {
  89. return {
  90. baseUrl: config.baseUrl,
  91. currentTab: 0,
  92. isCollected: false,
  93. bookrecno: '',
  94. opacUrl: '',
  95. bookInfo: {}, // 图书基础信息(biblios)
  96. holdingsData: [], // 馆藏列表(holdings)
  97. };
  98. },
  99. onLoad(options) {
  100. // 1. 首页/列表页 直接带完整 bookData 过来,优先走这个
  101. if (options.bookData) {
  102. const bookData = JSON.parse(decodeURIComponent(options.bookData));
  103. this.bookInfo = bookData;
  104. this.bookrecno = bookData.bookrecno || "";
  105. // 没有馆藏数据就置空
  106. this.holdingsData = [];
  107. this.checkCollectStatus();
  108. return;
  109. }
  110. // 2. 检索页只传 bookrecno,请求接口拿详情
  111. this.bookrecno = options.bookrecno || "";
  112. this.getOpacUrl();
  113. },
  114. computed: {
  115. // 在馆数量
  116. inLibraryCount() {
  117. return this.holdingsData.filter(item => item.state === 2).length;
  118. },
  119. // 借出数量(新增)
  120. lendCount() {
  121. return this.holdingsData.filter(item => item.state === 3).length;
  122. },
  123. // 第一个馆藏状态
  124. firstStateText() {
  125. if (this.holdingsData.length === 0) return '无馆藏';
  126. return this.getStateText(this.holdingsData[0].state);
  127. }
  128. },
  129. methods: {
  130. onImgError(e) {
  131. e.target.src = "/static/images/default-book.png";
  132. },
  133. // 获取配置
  134. async getOpacUrl() {
  135. try {
  136. const res = await FetchInitScreenSetting({ libcode: '1201' });
  137. this.opacUrl = res.data.opac_url?.context || '';
  138. this.getBookDetail();
  139. } catch (err) {}
  140. },
  141. // 获取图书详情(适配新接口结构)
  142. async getBookDetail() {
  143. if (!this.bookrecno || !this.opacUrl) return;
  144. uni.showLoading({ title: '加载中...' });
  145. try {
  146. const params = {
  147. opacUrl: this.opacUrl,
  148. bookrecno: this.bookrecno
  149. };
  150. const res = await FetchFindbookByQuery(params);
  151. const apiData = res.data || {};
  152. this.bookInfo = apiData.biblios || {};
  153. this.holdingsData = apiData.holdings || [];
  154. this.checkCollectStatus();
  155. } catch (err) {
  156. console.log(err);
  157. } finally {
  158. uni.hideLoading();
  159. }
  160. },
  161. // 馆藏状态文字
  162. getStateText(state) {
  163. const map = {
  164. 1: '编目',
  165. 2: '在馆',
  166. 3: '借出',
  167. 4: '丢失',
  168. 5: '剔除',
  169. 6: '交换',
  170. 7: '赠送',
  171. 8: '装订',
  172. 9: '锁定',
  173. 10: '预借',
  174. 12: '清点'
  175. }
  176. return map[state] || '未知';
  177. },
  178. // 收藏
  179. checkCollectStatus() {
  180. const list = uni.getStorageSync('collectList') || [];
  181. this.isCollected = list.includes(this.bookrecno);
  182. },
  183. toggleCollect() {
  184. let list = uni.getStorageSync('collectList') || [];
  185. if (this.isCollected) {
  186. list = list.filter(i => i !== this.bookrecno);
  187. uni.showToast({ title: '取消收藏', icon: 'success' });
  188. } else {
  189. list.push(this.bookrecno);
  190. uni.showToast({ title: '收藏成功', icon: 'success' });
  191. }
  192. uni.setStorageSync('collectList', list);
  193. this.isCollected = !this.isCollected;
  194. }
  195. },
  196. onShareAppMessage() {
  197. return {
  198. title: this.bookInfo.title || '图书详情',
  199. path: '/subpkg/pages/book-detail/book-detail?bookrecno=' + this.bookrecno,
  200. imageUrl: this.bookInfo.cover
  201. };
  202. }
  203. };
  204. </script>
  205. <style lang="scss" scoped>
  206. .detail-container {
  207. padding: 15px;
  208. background-color: #f5f5f5;
  209. min-height: 100vh;
  210. padding-bottom: 60px;
  211. }
  212. .article-detail-container {
  213. display: flex;
  214. background-color: #fff;
  215. border-radius: 10px;
  216. padding: 20px;
  217. margin-bottom: 12px;
  218. }
  219. .article-detail-left {
  220. flex: 1;
  221. }
  222. .article-detail-title {
  223. font-size: 20px;
  224. font-weight: bold;
  225. color: #333;
  226. margin-bottom: 10px;
  227. }
  228. .article-detail-info {
  229. font-size: 14px;
  230. color: #666;
  231. line-height: 1.5;
  232. }
  233. .article-detail-right {
  234. width: 110px;
  235. height: 150px;
  236. margin-left: 15px;
  237. }
  238. .article-detail-right image {
  239. width: 100%;
  240. height: 100%;
  241. border-radius: 6px;
  242. box-shadow: 0 2px 8px rgba(0,0,0,0.1);
  243. }
  244. .book-store-info {
  245. background-color: #fff;
  246. border-radius: 10px;
  247. padding: 20px;
  248. display: flex;
  249. justify-content: space-around;
  250. margin-bottom: 12px;
  251. }
  252. .book-store-info-item {
  253. display: flex;
  254. flex-direction: column;
  255. align-items: center;
  256. }
  257. .store-txt1 {
  258. font-size: 22px;
  259. font-weight: bold;
  260. color: #2b85e4;
  261. }
  262. .store-txt2 {
  263. font-size: 13px;
  264. color: #999;
  265. margin-top: 4px;
  266. }
  267. .store-info-list {
  268. background-color: #fff;
  269. border-radius: 10px;
  270. padding: 20px;
  271. margin-bottom: 15px;
  272. }
  273. .store-info-item {
  274. display: flex;
  275. justify-content: space-between;
  276. font-size: 14px;
  277. color: #666;
  278. margin-bottom: 10px;
  279. }
  280. .store-info-item view {
  281. display: flex;
  282. flex-direction: column;
  283. align-items: center;
  284. }
  285. .content-tab {
  286. display: flex;
  287. background-color: #fff;
  288. border-radius: 10px;
  289. overflow: hidden;
  290. margin-bottom: 12px;
  291. }
  292. .tab-item {
  293. flex: 1;
  294. text-align: center;
  295. padding: 15px 0;
  296. font-size: 15px;
  297. color: #666;
  298. position: relative;
  299. }
  300. .tab-item.active {
  301. color: #2b85e4;
  302. font-weight: bold;
  303. }
  304. .tab-item.active::after {
  305. content: "";
  306. position: absolute;
  307. bottom: 0;
  308. left: 50%;
  309. transform: translateX(-50%);
  310. width: 30px;
  311. height: 3px;
  312. background-color: #2b85e4;
  313. border-radius: 2px;
  314. }
  315. .content-item {
  316. background-color: #fff;
  317. border-radius: 10px;
  318. padding: 20px;
  319. font-size: 15px;
  320. color: #333;
  321. line-height: 1.6;
  322. }
  323. .detail-bottom{
  324. justify-content: space-around;
  325. }
  326. </style>