search-result.vue 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420
  1. <template>
  2. <view class="search-result-page">
  3. <image class="top-bg-image" src="/pages-sell/static/top-bg.png" mode="widthFix"></image>
  4. <Navbar title="书嗨" :titleSize="36" :title-color="navbarTitleColor" :back-icon-color="navbarIconColor"
  5. :background="navbarBackground"></Navbar>
  6. <!-- Custom Header -->
  7. <view class="search-bar-wrapper">
  8. <u-search style="width: 100%;" v-model="keyword" placeholder="书名/作者/ISBN" :show-action="true"
  9. action-text="反馈" :action-style="{ color: '#fff' }" bg-color="#fff" shape="round" :clearabled="true"
  10. search-icon="/pages-sell/static/search-icon.png" @custom="toFeedback" @search="onSearch"></u-search>
  11. </view>
  12. <!-- Banner -->
  13. <view class="banner-section">
  14. <image src="/pages-sell/static/top-banner.png" class="top-banner" mode="widthFix"></image>
  15. </view>
  16. <!-- Content Area -->
  17. <view class="content-area">
  18. <!-- Everyone is watching -->
  19. <view class="section-block" v-if="hotBook && hotBook.isbn">
  20. <text class="section-title">大家都在看</text>
  21. <HotRecommendItem :item="hotBook" @add-cart="addToCart"></HotRecommendItem>
  22. </view>
  23. <!-- Filter Bar -->
  24. <view class="filter-bar">
  25. <view class="filter-item" :class="{ active: sort === 1 }" @click="handleSort(1)">
  26. <text>综合</text>
  27. <view class="sort-icons">
  28. <u-icon name="arrow-down-fill" :color="sort === 1 ? '#38C148' : '#cccccc'" size="14"></u-icon>
  29. </view>
  30. </view>
  31. <view class="filter-item" :class="{ active: sort === 4 || sort === 5 }" @click="handleSort('sales')">
  32. <text>销量</text>
  33. <view class="sort-icons">
  34. <u-icon name="arrow-up-fill" :color="sort === 4 ? '#38C148' : '#cccccc'" size="14"></u-icon>
  35. <u-icon name="arrow-down-fill" :color="sort === 5 ? '#38C148' : '#cccccc'" size="14"></u-icon>
  36. </view>
  37. </view>
  38. <view class="filter-item" :class="{ active: sort === 2 || sort === 3 }" @click="handleSort('price')">
  39. <text>价格</text>
  40. <view class="sort-icons">
  41. <u-icon name="arrow-up-fill" :color="sort === 2 ? '#38C148' : '#cccccc'" size="14"></u-icon>
  42. <u-icon name="arrow-down-fill" :color="sort === 3 ? '#38C148' : '#cccccc'" size="14"></u-icon>
  43. </view>
  44. </view>
  45. <view class="filter-item" @click="showFilter = true">
  46. <text>筛选</text>
  47. <image src="/pages-sell/static/search-result/icon-filter.png" class="icon-filter"></image>
  48. </view>
  49. </view>
  50. <!-- Book List -->
  51. <view class="book-list">
  52. <RecommendItem v-for="(item, index) in bookList" :key="index" :item="item" @add-cart="addToCart"
  53. :show-desc="false">
  54. </RecommendItem>
  55. <u-loadmore :status="loadStatus" margin-top="30" margin-bottom="30"></u-loadmore>
  56. </view>
  57. </view>
  58. <!-- Filter Popup -->
  59. <u-popup v-model="showFilter" mode="bottom">
  60. <view class="filter-popup">
  61. <view class="filter-content">
  62. <view class="filter-title">价格区间</view>
  63. <view class="price-inputs">
  64. <u-input v-model="tempMinPrice" type="number" placeholder="最低价" border="surround"></u-input>
  65. <view class="divider">-</view>
  66. <u-input v-model="tempMaxPrice" type="number" placeholder="最高价" border="surround"></u-input>
  67. </view>
  68. </view>
  69. <view class="filter-footer">
  70. <view class="btn reset" @click="resetFilter">重置</view>
  71. <view class="btn confirm" @click="confirmFilter">确定</view>
  72. </view>
  73. </view>
  74. </u-popup>
  75. </view>
  76. </template>
  77. <script>
  78. import RecommendItem from '../components/recommend-item/index.vue';
  79. import HotRecommendItem from '../components/hot-recommend-item/index.vue';
  80. import Navbar from '@/components/navbar/navbar.vue';
  81. export default {
  82. components: {
  83. RecommendItem,
  84. HotRecommendItem,
  85. Navbar,
  86. },
  87. data() {
  88. return {
  89. keyword: '',
  90. pageNum: 1,
  91. pageSize: 10,
  92. hotBook: {},
  93. bookList: [],
  94. loadStatus: 'loadmore',
  95. navbarBackground: 'transparent',
  96. navbarTitleColor: '#ffffff',
  97. navbarIconColor: '#ffffff',
  98. sort: 1, // 1-综合 2-价格升序 3-价格降序 4-销量升序 5-销量降序
  99. showFilter: false,
  100. tempMinPrice: '',
  101. tempMaxPrice: '',
  102. minPrice: '',
  103. maxPrice: ''
  104. }
  105. },
  106. onPullDownRefresh() {
  107. this.pageNum = 1;
  108. this.loadStatus = 'loading';
  109. this.loadData();
  110. },
  111. onReachBottom() {
  112. if (this.loadStatus === 'nomore') return;
  113. this.pageNum++;
  114. this.loadStatus = 'loading';
  115. this.loadData();
  116. },
  117. onPageScroll(e) {
  118. if (e.scrollTop > 50) {
  119. this.navbarBackground = '#ffffff';
  120. this.navbarTitleColor = '#000000';
  121. this.navbarIconColor = '#000000';
  122. } else {
  123. this.navbarBackground = 'transparent';
  124. this.navbarTitleColor = '#ffffff';
  125. this.navbarIconColor = '#ffffff';
  126. }
  127. },
  128. onLoad(options) {
  129. this.keyword = options.keyword ? decodeURIComponent(options.keyword) : '';
  130. this.loadData();
  131. },
  132. methods: {
  133. loadData() {
  134. const params = {
  135. keyword: this.keyword,
  136. pageNum: this.pageNum,
  137. pageSize: this.pageSize,
  138. sort: this.sort
  139. };
  140. if (this.minPrice) params.minPrice = this.minPrice;
  141. if (this.maxPrice) params.maxPrice = this.maxPrice;
  142. this.$u.api.getSearchKeywordAjax(params).then(res => {
  143. uni.stopPullDownRefresh();
  144. const rows = res.data.rows || [];
  145. if (this.pageNum === 1) {
  146. this.bookList = rows;
  147. } else {
  148. this.bookList = [...this.bookList, ...rows];
  149. }
  150. if (rows.length < this.pageSize) {
  151. this.loadStatus = 'nomore';
  152. } else {
  153. this.loadStatus = 'loadmore';
  154. }
  155. }).catch(() => {
  156. uni.stopPullDownRefresh();
  157. this.loadStatus = 'loadmore';
  158. });
  159. if (this.pageNum === 1) {
  160. this.$u.api.getSearchRecommendAjax().then(res => {
  161. if (res.code == 200) {
  162. res.data.discountDesc = res.data.productPrice ? (res.data.productPrice / res.data.price) + '折' : '';
  163. res.data.discountPrice = res.data.productPrice ? res.data.price - res.data.productPrice : '';
  164. this.hotBook = res.data;
  165. }
  166. });
  167. }
  168. },
  169. handleSort(type) {
  170. if (type === 1) {
  171. if (this.sort === 1) return;
  172. this.sort = 1;
  173. } else if (type === 'price') {
  174. if (this.sort === 2) {
  175. this.sort = 3;
  176. } else {
  177. this.sort = 2;
  178. }
  179. } else if (type === 'sales') {
  180. if (this.sort === 5) {
  181. this.sort = 4;
  182. } else {
  183. this.sort = 5;
  184. }
  185. }
  186. this.pageNum = 1;
  187. this.loadStatus = 'loading';
  188. this.loadData();
  189. },
  190. resetFilter() {
  191. this.tempMinPrice = '';
  192. this.tempMaxPrice = '';
  193. },
  194. confirmFilter() {
  195. this.minPrice = this.tempMinPrice;
  196. this.maxPrice = this.tempMaxPrice;
  197. this.showFilter = false;
  198. this.pageNum = 1;
  199. this.loadStatus = 'loading';
  200. this.loadData();
  201. },
  202. goBack() {
  203. uni.navigateBack();
  204. },
  205. toFeedback() {
  206. uni.navigateTo({
  207. url: '/pages-sell/pages/feedback'
  208. });
  209. },
  210. onSearch(val) {
  211. console.log('Search:', val);
  212. this.keyword = val;
  213. this.pageNum = 1;
  214. this.loadData();
  215. },
  216. addToCart(data) {
  217. // data now contains { product, quality, quantity } from the popup
  218. console.log('Added to cart:', data);
  219. uni.showToast({
  220. title: '已加入购物车',
  221. icon: 'success'
  222. });
  223. }
  224. }
  225. }
  226. </script>
  227. <style lang="scss" scoped>
  228. .search-result-page {
  229. min-height: 100vh;
  230. background-color: #F6F6F6;
  231. font-family: 'Source Han Sans SC', sans-serif;
  232. position: relative;
  233. }
  234. /* 顶部大背景图 */
  235. .top-bg-image {
  236. position: fixed;
  237. top: 0;
  238. left: 0;
  239. width: 100%;
  240. z-index: 0;
  241. display: block;
  242. }
  243. .search-bar-wrapper {
  244. position: relative;
  245. z-index: 2;
  246. display: flex;
  247. align-items: center;
  248. padding: 0 30rpx;
  249. height: 88rpx;
  250. width: 100%;
  251. }
  252. .banner-section {
  253. position: relative;
  254. width: 100%;
  255. z-index: 1;
  256. padding: 10rpx 30rpx;
  257. .top-banner {
  258. width: 100%;
  259. display: block;
  260. }
  261. }
  262. .content-area {
  263. position: relative;
  264. z-index: 2;
  265. padding: 30rpx 30rpx;
  266. border-radius: 30rpx 30rpx 0 0;
  267. min-height: 500rpx;
  268. .section-block {
  269. margin-bottom: 30rpx;
  270. background-color: #fff;
  271. padding: 30rpx 24rpx;
  272. border-radius: 20rpx;
  273. .section-title {
  274. font-size: 32rpx;
  275. font-weight: bold;
  276. color: #333;
  277. margin-bottom: 20rpx;
  278. display: block;
  279. }
  280. }
  281. }
  282. .filter-bar {
  283. display: flex;
  284. justify-content: space-between;
  285. align-items: center;
  286. margin-bottom: 20rpx;
  287. .filter-item {
  288. display: flex;
  289. align-items: center;
  290. font-size: 28rpx;
  291. color: #666;
  292. &.active {
  293. color: #333;
  294. font-weight: bold;
  295. .triangle-icon {
  296. border-top-color: #333;
  297. }
  298. }
  299. text {
  300. margin-right: 6rpx;
  301. }
  302. .triangle-icon {
  303. width: 0;
  304. height: 0;
  305. border-left: 8rpx solid transparent;
  306. border-right: 8rpx solid transparent;
  307. border-top: 10rpx solid #666;
  308. }
  309. .sort-icons {
  310. display: flex;
  311. flex-direction: column;
  312. margin-left: 4rpx;
  313. justify-content: center;
  314. gap: 4rpx;
  315. u-icon {
  316. margin: -2rpx 0;
  317. }
  318. }
  319. .icon-filter {
  320. width: 24rpx;
  321. height: 24rpx;
  322. margin-left: 4rpx;
  323. }
  324. }
  325. }
  326. .book-list {
  327. background: #fff;
  328. border-radius: 20rpx;
  329. padding: 0 24rpx;
  330. padding-bottom: 40rpx;
  331. }
  332. .filter-popup {
  333. background-color: #fff;
  334. height: 100%;
  335. display: flex;
  336. flex-direction: column;
  337. .filter-content {
  338. flex: 1;
  339. padding: 30rpx;
  340. .filter-title {
  341. font-size: 30rpx;
  342. font-weight: bold;
  343. color: #333;
  344. margin-bottom: 30rpx;
  345. }
  346. .price-inputs {
  347. display: flex;
  348. align-items: center;
  349. justify-content: space-between;
  350. u-input {
  351. flex: 1;
  352. }
  353. .divider {
  354. margin: 0 20rpx;
  355. color: #999;
  356. }
  357. }
  358. }
  359. .filter-footer {
  360. display: flex;
  361. height: 100rpx;
  362. .btn {
  363. flex: 1;
  364. display: flex;
  365. align-items: center;
  366. justify-content: center;
  367. font-size: 32rpx;
  368. &.reset {
  369. background-color: #fff;
  370. color: #666;
  371. border-top: 1rpx solid #eee;
  372. }
  373. &.confirm {
  374. background-color: #38C148;
  375. color: #fff;
  376. }
  377. }
  378. }
  379. }
  380. </style>