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

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