search.js 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383
  1. "use strict";
  2. const common_vendor = require("../../common/vendor.js");
  3. const utils_api = require("../../utils/api.js");
  4. const _sfc_main = {
  5. data() {
  6. return {
  7. searchKeyword: "",
  8. isFocus: false,
  9. showSearchResults: false,
  10. searchHistory: [],
  11. popularSearches: [],
  12. searchResults: [],
  13. isLoading: false,
  14. currentPage: 1,
  15. pageSize: 20,
  16. hasMore: true,
  17. mode: "book",
  18. // 'book' or 'audio'
  19. userInfo: null
  20. };
  21. },
  22. onLoad(options) {
  23. if (options.mode === "audio") {
  24. this.mode = "audio";
  25. }
  26. try {
  27. const userInfo = common_vendor.index.getStorageSync("userInfo");
  28. if (userInfo && userInfo.id) {
  29. this.userInfo = userInfo;
  30. }
  31. } catch (e) {
  32. common_vendor.index.__f__("error", "at pages/search/search.vue:150", "获取用户信息失败", e);
  33. }
  34. this.loadSearchHistory();
  35. this.loadPopularSearches();
  36. if (options.keyword) {
  37. this.searchKeyword = decodeURIComponent(options.keyword);
  38. this.performSearch(this.searchKeyword);
  39. }
  40. },
  41. methods: {
  42. handleInput(e) {
  43. this.searchKeyword = e.detail.value;
  44. },
  45. handleFocus() {
  46. this.isFocus = true;
  47. },
  48. handleBlur() {
  49. this.isFocus = false;
  50. },
  51. handleSearch() {
  52. if (this.searchKeyword.trim()) {
  53. this.performSearch(this.searchKeyword.trim());
  54. }
  55. },
  56. handleCancel() {
  57. if (this.showSearchResults) {
  58. this.showSearchResults = false;
  59. this.searchKeyword = "";
  60. this.searchResults = [];
  61. } else {
  62. common_vendor.index.navigateBack({
  63. delta: 1
  64. });
  65. }
  66. },
  67. searchByHistory(keyword) {
  68. this.searchKeyword = keyword;
  69. this.performSearch(keyword);
  70. },
  71. searchByTag(keyword) {
  72. this.searchKeyword = keyword;
  73. this.performSearch(keyword);
  74. },
  75. performSearch(keyword) {
  76. if (!keyword || !keyword.trim()) {
  77. return;
  78. }
  79. this.saveToHistory(keyword);
  80. this.showSearchResults = true;
  81. this.currentPage = 1;
  82. this.hasMore = true;
  83. this.searchDispatcher(keyword, 1);
  84. },
  85. async loadPopularSearches() {
  86. try {
  87. const res = await utils_api.getPopularKeywords(10);
  88. if (res && res.code === 200 && res.data && Array.isArray(res.data)) {
  89. this.popularSearches = res.data;
  90. }
  91. } catch (e) {
  92. common_vendor.index.__f__("error", "at pages/search/search.vue:226", "加载热门搜索失败:", e);
  93. this.popularSearches = ["西游记", "三体", "大侦探", "窗边的小豆豆"];
  94. }
  95. },
  96. async searchDispatcher(keyword, page = 1) {
  97. if (this.mode === "audio") {
  98. await this.searchAudio(keyword, page);
  99. } else {
  100. await this.searchBook(keyword, page);
  101. }
  102. },
  103. async searchBook(keyword, page = 1) {
  104. if (!keyword || !keyword.trim()) {
  105. this.searchResults = [];
  106. return;
  107. }
  108. try {
  109. this.isLoading = true;
  110. const res = await utils_api.searchBooks(keyword.trim(), page, this.pageSize);
  111. if (res && res.code === 200 && res.data) {
  112. const pageResult = res.data;
  113. const books = pageResult.list || pageResult.data || [];
  114. const processed = books.map((b) => ({
  115. id: b.id,
  116. title: b.title || "",
  117. author: b.author || "未知作者",
  118. desc: b.desc || b.brief || b.introduction || "",
  119. cover: b.cover || b.image || "",
  120. image: b.image || b.cover || ""
  121. }));
  122. if (page === 1)
  123. this.searchResults = processed;
  124. else
  125. this.searchResults = this.searchResults.concat(processed);
  126. const total = pageResult.total || 0;
  127. this.hasMore = this.searchResults.length < total;
  128. this.currentPage = page;
  129. } else {
  130. if (page === 1)
  131. this.searchResults = [];
  132. common_vendor.index.showToast({ title: res.message || "搜索失败", icon: "none" });
  133. }
  134. } catch (e) {
  135. common_vendor.index.__f__("error", "at pages/search/search.vue:270", "搜索书籍失败:", e);
  136. if (page === 1)
  137. this.searchResults = [];
  138. common_vendor.index.showToast({ title: e.message || "搜索失败,请重试", icon: "none" });
  139. } finally {
  140. this.isLoading = false;
  141. }
  142. },
  143. async searchAudio(keyword, page = 1) {
  144. if (!keyword || !keyword.trim()) {
  145. this.searchResults = [];
  146. return;
  147. }
  148. try {
  149. this.isLoading = true;
  150. const res = await utils_api.searchAudiobooks({ keyword: keyword.trim(), page, size: this.pageSize });
  151. if (res && res.code === 200 && res.data) {
  152. const pageResult = res.data;
  153. const list = pageResult.list || [];
  154. const processed = list.map((a) => ({
  155. id: a.id,
  156. title: a.title || "",
  157. author: a.narrator || a.author || "未知主播",
  158. desc: a.brief || a.desc || "",
  159. cover: a.image || a.cover || "",
  160. image: a.image || a.cover || "",
  161. isAudio: true
  162. }));
  163. if (page === 1)
  164. this.searchResults = processed;
  165. else
  166. this.searchResults = this.searchResults.concat(processed);
  167. const total = pageResult.total || 0;
  168. this.hasMore = this.searchResults.length < total;
  169. this.currentPage = page;
  170. } else {
  171. if (page === 1)
  172. this.searchResults = [];
  173. common_vendor.index.showToast({ title: res.message || "搜索失败", icon: "none" });
  174. }
  175. } catch (e) {
  176. common_vendor.index.__f__("error", "at pages/search/search.vue:306", "搜索听书失败:", e);
  177. if (page === 1)
  178. this.searchResults = [];
  179. common_vendor.index.showToast({ title: e.message || "搜索失败,请重试", icon: "none" });
  180. } finally {
  181. this.isLoading = false;
  182. }
  183. },
  184. async saveToHistory(keyword) {
  185. if (!this.userInfo || !this.userInfo.id) {
  186. const index = this.searchHistory.findIndex(
  187. (item) => (typeof item === "string" ? item : item.keyword) === keyword
  188. );
  189. if (index > -1) {
  190. this.searchHistory.splice(index, 1);
  191. }
  192. this.searchHistory.unshift(keyword);
  193. if (this.searchHistory.length > 10) {
  194. this.searchHistory = this.searchHistory.slice(0, 10);
  195. }
  196. try {
  197. common_vendor.index.setStorageSync("searchHistory", this.searchHistory);
  198. } catch (e) {
  199. common_vendor.index.__f__("error", "at pages/search/search.vue:329", "保存搜索历史失败", e);
  200. }
  201. return;
  202. }
  203. try {
  204. await utils_api.recordSearchHistory(this.userInfo.id, keyword);
  205. await this.loadSearchHistory();
  206. } catch (e) {
  207. common_vendor.index.__f__("error", "at pages/search/search.vue:340", "保存搜索历史失败", e);
  208. }
  209. },
  210. async loadSearchHistory() {
  211. if (!this.userInfo || !this.userInfo.id) {
  212. try {
  213. const history = common_vendor.index.getStorageSync("searchHistory");
  214. if (history && Array.isArray(history)) {
  215. this.searchHistory = history.map(
  216. (item) => typeof item === "string" ? item : item.keyword
  217. );
  218. }
  219. } catch (e) {
  220. common_vendor.index.__f__("error", "at pages/search/search.vue:354", "加载搜索历史失败", e);
  221. }
  222. return;
  223. }
  224. try {
  225. const res = await utils_api.getSearchHistory(this.userInfo.id, 10);
  226. if (res && res.code === 200 && res.data) {
  227. this.searchHistory = res.data.map((item) => item.keyword);
  228. }
  229. } catch (e) {
  230. common_vendor.index.__f__("error", "at pages/search/search.vue:366", "加载搜索历史失败", e);
  231. try {
  232. const history = common_vendor.index.getStorageSync("searchHistory");
  233. if (history && Array.isArray(history)) {
  234. this.searchHistory = history.map(
  235. (item) => typeof item === "string" ? item : item.keyword
  236. );
  237. }
  238. } catch (err) {
  239. common_vendor.index.__f__("error", "at pages/search/search.vue:376", "从本地存储加载搜索历史失败", err);
  240. }
  241. }
  242. },
  243. async clearHistory() {
  244. common_vendor.index.showModal({
  245. title: "提示",
  246. content: "确定要清空搜索历史吗?",
  247. success: async (res) => {
  248. if (res.confirm) {
  249. if (this.userInfo && this.userInfo.id) {
  250. try {
  251. common_vendor.index.showLoading({
  252. title: "清空中...",
  253. mask: true
  254. });
  255. const result = await utils_api.clearSearchHistory(this.userInfo.id);
  256. common_vendor.index.hideLoading();
  257. if (result && result.code === 200) {
  258. this.searchHistory = [];
  259. common_vendor.index.showToast({
  260. title: "已清空",
  261. icon: "success"
  262. });
  263. } else {
  264. common_vendor.index.showToast({
  265. title: result.message || "清空失败",
  266. icon: "none"
  267. });
  268. }
  269. } catch (e) {
  270. common_vendor.index.hideLoading();
  271. common_vendor.index.__f__("error", "at pages/search/search.vue:411", "清空搜索历史失败", e);
  272. common_vendor.index.showToast({
  273. title: "清空失败,请重试",
  274. icon: "none"
  275. });
  276. }
  277. } else {
  278. this.searchHistory = [];
  279. try {
  280. common_vendor.index.removeStorageSync("searchHistory");
  281. common_vendor.index.showToast({
  282. title: "已清空",
  283. icon: "success"
  284. });
  285. } catch (e) {
  286. common_vendor.index.__f__("error", "at pages/search/search.vue:427", "清空搜索历史失败", e);
  287. }
  288. }
  289. }
  290. }
  291. });
  292. },
  293. loadMore() {
  294. if (!this.isLoading && this.hasMore && this.searchKeyword.trim()) {
  295. this.searchDispatcher(this.searchKeyword.trim(), this.currentPage + 1);
  296. }
  297. },
  298. goToBookDetail(book) {
  299. if (!book || !book.id) {
  300. common_vendor.index.showToast({ title: "信息不完整", icon: "none" });
  301. return;
  302. }
  303. if (this.mode === "audio" || book.isAudio) {
  304. common_vendor.index.navigateTo({ url: `/pages/listen-detail/listen-detail?audiobookId=${book.id}` });
  305. } else {
  306. common_vendor.index.navigateTo({ url: `/pages/book-detail/book-detail?bookId=${book.id}` });
  307. }
  308. },
  309. handleImageError(index) {
  310. if (this.searchResults[index]) {
  311. this.searchResults[index].cover = "https://via.placeholder.com/200x300?text=No+Image";
  312. this.searchResults[index].image = "https://via.placeholder.com/200x300?text=No+Image";
  313. }
  314. }
  315. }
  316. };
  317. function _sfc_render(_ctx, _cache, $props, $setup, $data, $options) {
  318. return common_vendor.e({
  319. a: $data.isFocus,
  320. b: common_vendor.o([($event) => $data.searchKeyword = $event.detail.value, (...args) => $options.handleInput && $options.handleInput(...args)]),
  321. c: common_vendor.o((...args) => $options.handleSearch && $options.handleSearch(...args)),
  322. d: common_vendor.o((...args) => $options.handleFocus && $options.handleFocus(...args)),
  323. e: common_vendor.o((...args) => $options.handleBlur && $options.handleBlur(...args)),
  324. f: $data.searchKeyword,
  325. g: common_vendor.o((...args) => $options.handleCancel && $options.handleCancel(...args)),
  326. h: !$data.showSearchResults
  327. }, !$data.showSearchResults ? common_vendor.e({
  328. i: $data.searchHistory.length > 0
  329. }, $data.searchHistory.length > 0 ? {
  330. j: common_vendor.o((...args) => $options.clearHistory && $options.clearHistory(...args)),
  331. k: common_vendor.f($data.searchHistory, (item, index, i0) => {
  332. return {
  333. a: common_vendor.t(item),
  334. b: index,
  335. c: common_vendor.o(($event) => $options.searchByHistory(item), index)
  336. };
  337. })
  338. } : {}, {
  339. l: common_vendor.f($data.popularSearches, (item, index, i0) => {
  340. return {
  341. a: common_vendor.t(item),
  342. b: index,
  343. c: common_vendor.o(($event) => $options.searchByTag(item), index)
  344. };
  345. })
  346. }) : {}, {
  347. m: $data.showSearchResults
  348. }, $data.showSearchResults ? common_vendor.e({
  349. n: $data.searchResults.length > 0
  350. }, $data.searchResults.length > 0 ? {
  351. o: common_vendor.t($data.searchResults.length)
  352. } : {}, {
  353. p: $data.isLoading && $data.searchResults.length === 0
  354. }, $data.isLoading && $data.searchResults.length === 0 ? {} : $data.searchResults.length > 0 ? {
  355. r: common_vendor.f($data.searchResults, (book, index, i0) => {
  356. return common_vendor.e({
  357. a: book.cover || book.image,
  358. b: common_vendor.o(($event) => $options.handleImageError(index), book.id || index),
  359. c: common_vendor.t(book.title),
  360. d: book.desc
  361. }, book.desc ? {
  362. e: common_vendor.t(book.desc)
  363. } : {}, {
  364. f: book.author
  365. }, book.author ? {
  366. g: common_vendor.t(book.author)
  367. } : {}, {
  368. h: book.id || index,
  369. i: common_vendor.o(($event) => $options.goToBookDetail(book), book.id || index)
  370. });
  371. })
  372. } : !$data.isLoading ? {} : {}, {
  373. q: $data.searchResults.length > 0,
  374. s: !$data.isLoading,
  375. t: $data.isLoading && $data.searchResults.length > 0
  376. }, $data.isLoading && $data.searchResults.length > 0 ? {} : !$data.hasMore && $data.searchResults.length > 0 ? {} : {}, {
  377. v: !$data.hasMore && $data.searchResults.length > 0,
  378. w: common_vendor.o((...args) => $options.loadMore && $options.loadMore(...args))
  379. }) : {});
  380. }
  381. const MiniProgramPage = /* @__PURE__ */ common_vendor._export_sfc(_sfc_main, [["render", _sfc_render], ["__scopeId", "data-v-c10c040c"]]);
  382. wx.createPage(MiniProgramPage);
  383. //# sourceMappingURL=../../../.sourcemap/mp-weixin/pages/search/search.js.map