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

332 lines
7.5 KiB

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