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

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