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

377 lines
9.2 KiB

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
2 months ago
1 month 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
2 months ago
1 month ago
2 months ago
1 month ago
2 months ago
1 month ago
2 months 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
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
1 month ago
1 month 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
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
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
1 month ago
2 months ago
  1. <template>
  2. <view style="background-color: #fff; padding-bottom:5px;">
  3. <view class="my-search-container">
  4. <view class="my-search-wrapper">
  5. <picker
  6. mode="selector"
  7. :range="rangeText"
  8. :value="selectIndex"
  9. @change="onPickerChange"
  10. class="picker-select"
  11. >
  12. <view class="picker-display">
  13. {{ selectValueText || '请选择分类' }}
  14. <uni-icons type="arrowdown" size="14" color="#999" style="margin-left: 4px;" />
  15. </view>
  16. </picker>
  17. <uni-search-bar
  18. class="my-search-bar"
  19. @confirm="onSearch"
  20. @focus="onFocus"
  21. @blur="onBlur"
  22. @clear="onClear"
  23. @cancel="onCancel"
  24. @input="onInput"
  25. cancelButton="none"
  26. bgColor="#f7f7f7"
  27. v-model="value"
  28. placeholder="请输入搜索内容"
  29. >
  30. <uni-icons slot="clearIcon" type="clear" color="#999999" />
  31. </uni-search-bar>
  32. </view>
  33. <view class="search-btn-container" @click="onSearch">
  34. <button class="search-btn" type="primary">搜索</button>
  35. </view>
  36. <view class="filter-container">
  37. <view class="total-reslut" v-if="listData.length > 0">
  38. 搜索到 {{ total }} 条记录 {{ totalPage }} 当前第 {{ page }}
  39. </view>
  40. <!-- <view>筛选</view> -->
  41. </view>
  42. </view>
  43. <view style="padding-top: 154px;" v-if="listData.length > 0">
  44. <scroll-view
  45. scroll-y
  46. style="height: calc(100vh - 80px);"
  47. @scrolltolower="onScrollLower"
  48. >
  49. <book-list-item
  50. class="hot-list-item"
  51. v-for="(item, index) in listData"
  52. :key="index"
  53. :data="item"
  54. :ranking="index + 1"
  55. @click="onItemClick(item)"
  56. ></book-list-item>
  57. <view class="load-more" v-if="total > listData.length">
  58. 上拉加载更多...
  59. </view>
  60. <view class="load-more" v-if="total <= listData.length && listData.length > 0">
  61. 没有更多数据了
  62. </view>
  63. </scroll-view>
  64. </view>
  65. <!-- 空状态 -->
  66. <view class="empty" v-if="isSearched && listData.length === 0">
  67. <uni-icons style="margin-left: 20px;" custom-prefix="iconfont" type="icon-kongshuju" size="80" color="#ccc"></uni-icons>
  68. <text style="margin-top: 20px;">没有检索到相关数据</text>
  69. </view>
  70. </view>
  71. </template>
  72. <script>
  73. import { FetchInitScreenSetting } from '@/api/user';
  74. import { FetchBookSearch, FetchFindAllBookCollectionByOpenId } from '@/api/book';
  75. import BookListItem from "@/components/book-list-item/book-list-item.vue";
  76. import config from '@/utils/config';
  77. import { getOpenId } from '@/utils/storage';
  78. import { loadBookCoversBase64 } from '@/utils/bookCover';
  79. export default {
  80. components: { BookListItem },
  81. data() {
  82. return {
  83. selectValue: '',
  84. selectValueText: '',
  85. selectIndex: 0,
  86. value: '',
  87. range: [
  88. { value: '', text: "任意词" },
  89. { value: 'title', text: "题名" },
  90. { value: 'title200a', text: "正题名" },
  91. { value: 'author', text: "著者" },
  92. { value: 'isbn', text: "ISBN" },
  93. { value: 'subject', text: "主题" },
  94. { value: 'publisher', text: "出版社" },
  95. { value: 'class', text: "分类号" },
  96. { value: 'ctrlno', text: "控制号" },
  97. { value: 'orderno', text: "订购号" },
  98. { value: 'callno', text: "索书号" },
  99. ],
  100. rangeText: [],
  101. opacUrl: '',
  102. listData: [],
  103. size: 10,
  104. page: 1,
  105. total: 0,
  106. totalPage: 0,
  107. isSearched: false
  108. };
  109. },
  110. onLoad() {
  111. this.rangeText = this.range.map(item => item.text);
  112. this.selectValue = this.range[0].value;
  113. this.selectValueText = this.range[0].text;
  114. this.getOpacUrl();
  115. },
  116. // 每次进入页面都会执行
  117. onShow() {
  118. // this.resetSearchData();
  119. },
  120. methods: {
  121. // 重置搜索数据(每次进入页面清空)
  122. resetSearchData() {
  123. this.value = ''; // 清空搜索框
  124. this.listData = []; // 清空列表
  125. this.page = 1; // 重置页码
  126. this.total = 0; // 清空总数
  127. this.totalPage = 0; // 清空总页数
  128. this.isSearched = false; // 重置搜索状态
  129. // 不重置选择器,保留用户习惯
  130. },
  131. // 获取opacUrl
  132. async getOpacUrl() {
  133. try {
  134. const res = await FetchInitScreenSetting({ libcode: config.LIB_CODE });
  135. this.opacUrl = res.data.opac_url?.context || '';
  136. } catch (err) {
  137. console.error('获取配置失败', err);
  138. }
  139. },
  140. // 切换检索类型
  141. onPickerChange(e) {
  142. const index = e.detail.value;
  143. this.selectIndex = index;
  144. this.selectValue = this.range[index].value;
  145. this.selectValueText = this.range[index].text;
  146. },
  147. async getBookList() {
  148. if (!this.value) {
  149. uni.showToast({ title: '请输入搜索内容', icon: 'none' });
  150. return;
  151. }
  152. if (!this.opacUrl) {
  153. uni.showToast({ title: '未获取到检索配置', icon: 'none' });
  154. return;
  155. }
  156. uni.showLoading({ title: '搜索中...' });
  157. try {
  158. const params = {
  159. opacUrl: this.opacUrl,
  160. page: this.page.toString(),
  161. query: this.value,
  162. rows: this.size.toString(),
  163. scWay: 'dim',
  164. searchWay: this.selectValue,
  165. sortOrder: 'desc',
  166. sortWay: 'score'
  167. };
  168. const res = await FetchBookSearch(params);
  169. // console.log('res',res)
  170. // 精准适配你当前返回格式
  171. const apiData = res.data || {};
  172. const list = apiData.bookList || [];
  173. // 赋值总数、总页数
  174. this.total = apiData.totalCount || 0;
  175. this.totalPage = Math.ceil(this.total / this.size);
  176. // 分页拼接列表
  177. if (this.page === 1) {
  178. this.listData = list;
  179. } else {
  180. this.listData = [...this.listData, ...list];
  181. }
  182. this.isSearched = true;
  183. // 加载封面
  184. this.loadCoversForList();
  185. } catch (err) {
  186. console.error('搜索接口异常', err);
  187. this.listData = [];
  188. this.isSearched = true;
  189. } finally {
  190. uni.hideLoading();
  191. }
  192. },
  193. // 上拉加载
  194. onScrollLower() {
  195. if (this.listData.length >= this.total) return;
  196. this.page++;
  197. this.getBookList();
  198. },
  199. // 搜索
  200. onSearch() {
  201. this.page = 1;
  202. this.listData = [];
  203. this.getBookList();
  204. },
  205. // 加载封面
  206. async loadCoversForList() {
  207. await loadBookCoversBase64(this.listData, (index, coverUrl) => {
  208. if (this.listData[index]) {
  209. this.$set(this.listData[index], 'cover', coverUrl);
  210. }
  211. });
  212. },
  213. // 进入详情
  214. async onItemClick(item) {
  215. console.log('item', item);
  216. let isCollected = false;
  217. let collectId = null;
  218. try {
  219. const openId = await getOpenId();
  220. if (openId) {
  221. const res = await FetchFindAllBookCollectionByOpenId({
  222. libcode: config.LIB_CODE,
  223. openId: openId,
  224. page: 0,
  225. size: 100
  226. });
  227. if (res.code === 200) {
  228. const collectList = res.data.content || [];
  229. const bookrecno = String(item.bookrecno);
  230. // 查找匹配的收藏记录
  231. const matchedItem = collectList.find(collectItem => String(collectItem.bookrecno) === bookrecno);
  232. if (matchedItem) {
  233. isCollected = true;
  234. collectId = matchedItem.id; // 获取收藏记录的 id
  235. }
  236. }
  237. }
  238. } catch (err) {
  239. console.error('获取收藏列表失败', err);
  240. }
  241. // 创建包含收藏信息的新对象
  242. const itemWithCollectInfo = {
  243. ...item,
  244. id: collectId // 添加收藏记录的 id
  245. };
  246. console.log('itemWithCollectInfo',itemWithCollectInfo)
  247. const isCollectedParam = isCollected ? '&isCollected=true' : '';
  248. uni.navigateTo({
  249. url: "/subpkg/pages/book-detail/book-detail?searchData=" + encodeURIComponent(JSON.stringify(itemWithCollectInfo)) + isCollectedParam
  250. });
  251. },
  252. // 清空搜索
  253. onClear() {
  254. this.value = '';
  255. this.listData = [];
  256. this.total = 0;
  257. this.totalPage = 0;
  258. this.isSearched = false;
  259. },
  260. onFocus() {},
  261. onBlur() {},
  262. onCancel() {},
  263. onInput(val) { this.value = val; }
  264. }
  265. };
  266. </script>
  267. <style lang="scss" scoped>
  268. .my-search-container {
  269. display: flex;
  270. flex-direction: column;
  271. padding: 15px 20px;
  272. background: #fff;
  273. position: fixed;
  274. top: 0;
  275. left: 0;
  276. right: 0;
  277. z-index: 99;
  278. height: 124px;
  279. .my-search-wrapper {
  280. display: flex;
  281. align-items: center;
  282. width: 100%;
  283. padding: 5px 0;
  284. background-color: #f7f7f7;
  285. border-radius: 36px;
  286. overflow: hidden;
  287. }
  288. .picker-select {
  289. width: 60px;
  290. border-right: 1px solid #c9c9c9;
  291. }
  292. .picker-display {
  293. display: flex;
  294. align-items: center;
  295. justify-content: center;
  296. padding: 6px 0;
  297. font-size: 14px;
  298. color: #333;
  299. }
  300. .my-search-bar {
  301. flex: 1;
  302. ::v-deep .uni-searchbar {
  303. padding: 0 !important;
  304. background: transparent !important;
  305. }
  306. }
  307. .search-btn-container {
  308. width: 100%;
  309. margin-top: 10px;
  310. .search-btn {
  311. background-color: #01a4fe;
  312. font-size: 15px;
  313. border-radius: 20px;
  314. }
  315. }
  316. }
  317. .filter-container {
  318. display: flex;
  319. justify-content: flex-end;
  320. padding-top: 10px;
  321. font-size: 13px;
  322. color: #666;
  323. }
  324. .load-more {
  325. text-align: center;
  326. padding: 10px;
  327. font-size: 13px;
  328. color: #999;
  329. }
  330. .total-reslut {
  331. flex: 1;
  332. line-height: 20px;
  333. font-size: 13px;
  334. color: #01a4fe;
  335. font-weight: 400;
  336. }
  337. .empty {
  338. height: 50vh;
  339. padding-top: 100px;
  340. }
  341. </style>