index.vue 6.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296
  1. <template>
  2. <Theme>
  3. <view class="wrap">
  4. <Navbar fixed leftShow leftIconColor="var(--bg)">
  5. <template #center>
  6. <view class="nav_title">
  7. <trans _t="店铺" />
  8. </view>
  9. </template>
  10. </Navbar>
  11. <!-- 搜索栏 - 固定在顶部 -->
  12. <view class="search-section">
  13. <view class="search-box">
  14. <Search
  15. v-model="searchValue"
  16. border="none"
  17. @input="onSearchInput"
  18. @click="onSearchClick"
  19. :placeholder="t('搜索店铺')"
  20. >
  21. <template #prefix>
  22. <i class="icon-font icon-search"></i>
  23. </template>
  24. </Search>
  25. </view>
  26. </view>
  27. <!-- 店铺列表 - 使用封装的List组件 -->
  28. <view class="content">
  29. <List
  30. ref="listRef"
  31. :defaultParams="defaultParams"
  32. @datas="getList"
  33. :isLoading="false"
  34. >
  35. <template #item="{ item }">
  36. <shopItem :item="item" @click="toShopDetail" />
  37. </template>
  38. </List>
  39. </view>
  40. </view>
  41. <Tabbar page="shop" />
  42. </Theme>
  43. </template>
  44. <script setup>
  45. import { ref, reactive, watch } from "vue";
  46. import Tabbar from "@/components/tabbar";
  47. import Navbar from "@/components/navbar";
  48. import Search from "@/components/input";
  49. import List from "@/components/list";
  50. import shopItem from "./components/shop_item";
  51. import { t } from "@/locale";
  52. import { onShow } from "@dcloudio/uni-app";
  53. // 搜索值
  54. const searchValue = ref("");
  55. // List组件引用
  56. const listRef = ref(null);
  57. // 默认参数
  58. const defaultParams = reactive({
  59. keyWord: "",
  60. });
  61. // 店铺数据
  62. const shops = ref([
  63. {
  64. id: 1,
  65. name: "时尚潮流店",
  66. description: "专注时尚潮流服饰,品质保证",
  67. avatar: "/static/shop/shop1.png",
  68. rating: 4.8,
  69. sales: 1234,
  70. distance: "1.2km",
  71. status: "online",
  72. tags: ["时尚", "潮流", "品质"],
  73. },
  74. {
  75. id: 2,
  76. name: "美妆护肤专营",
  77. description: "专业美妆护肤产品,正品保证",
  78. avatar: "/static/shop/shop2.png",
  79. rating: 4.9,
  80. sales: 2567,
  81. distance: "0.8km",
  82. status: "online",
  83. tags: ["美妆", "护肤", "正品"],
  84. },
  85. {
  86. id: 3,
  87. name: "数码科技馆",
  88. description: "最新数码产品,科技前沿",
  89. avatar: "/static/shop/shop3.png",
  90. rating: 4.7,
  91. sales: 1890,
  92. distance: "2.1km",
  93. status: "offline",
  94. tags: ["数码", "科技", "新品"],
  95. },
  96. {
  97. id: 4,
  98. name: "家居生活馆",
  99. description: "温馨家居,品质生活",
  100. avatar: "/static/shop/shop4.png",
  101. rating: 4.6,
  102. sales: 987,
  103. distance: "1.5km",
  104. status: "online",
  105. tags: ["家居", "生活", "温馨"],
  106. },
  107. {
  108. id: 5,
  109. name: "运动健身中心",
  110. description: "专业运动装备,健康生活",
  111. avatar: "/static/shop/shop5.png",
  112. rating: 4.5,
  113. sales: 1456,
  114. distance: "3.2km",
  115. status: "online",
  116. tags: ["运动", "健身", "健康"],
  117. },
  118. {
  119. id: 6,
  120. name: "美食天地",
  121. description: "特色美食,味蕾享受",
  122. avatar: "/static/shop/shop6.png",
  123. rating: 4.9,
  124. sales: 3456,
  125. distance: "0.5km",
  126. status: "online",
  127. tags: ["美食", "特色", "美味"],
  128. },
  129. {
  130. id: 7,
  131. name: "母婴用品店",
  132. description: "专业母婴用品,呵护宝宝",
  133. avatar: "/static/shop/shop7.png",
  134. rating: 4.8,
  135. sales: 2134,
  136. distance: "1.8km",
  137. status: "online",
  138. tags: ["母婴", "用品", "专业"],
  139. },
  140. {
  141. id: 8,
  142. name: "图书文具店",
  143. description: "学习用品,知识宝库",
  144. avatar: "/static/shop/shop8.png",
  145. rating: 4.4,
  146. sales: 678,
  147. distance: "2.5km",
  148. status: "offline",
  149. tags: ["图书", "文具", "学习"],
  150. },
  151. {
  152. id: 9,
  153. name: "宠物用品店",
  154. description: "宠物用品,关爱毛孩子",
  155. avatar: "/static/shop/shop9.png",
  156. rating: 4.7,
  157. sales: 1234,
  158. distance: "1.9km",
  159. status: "online",
  160. tags: ["宠物", "用品", "关爱"],
  161. },
  162. ]);
  163. // 过滤后的店铺列表
  164. const filteredShops = ref([]);
  165. // 搜索输入
  166. const onSearchInput = (value) => {
  167. searchValue.value = value;
  168. defaultParams.keyWord = value;
  169. // 触发列表刷新
  170. if (listRef.value) {
  171. listRef.value.handleRefresh();
  172. }
  173. };
  174. // 搜索点击
  175. const onSearchClick = () => {
  176. console.log("搜索:", searchValue.value);
  177. };
  178. // 获取列表数据
  179. const getList = (data) => {
  180. // 模拟搜索过滤
  181. if (searchValue.value.trim()) {
  182. const keyword = searchValue.value.toLowerCase();
  183. filteredShops.value = shops.value.filter(
  184. (shop) =>
  185. shop.name.toLowerCase().includes(keyword) ||
  186. shop.description.toLowerCase().includes(keyword) ||
  187. shop.tags.some((tag) => tag.toLowerCase().includes(keyword))
  188. );
  189. } else {
  190. filteredShops.value = shops.value;
  191. }
  192. // 设置到List组件
  193. if (listRef.value) {
  194. listRef.value.setList(filteredShops.value);
  195. }
  196. };
  197. // 跳转到店铺详情
  198. const toShopDetail = (shop) => {
  199. uni.navigateTo({
  200. url: `/pagesBuyer/shop/detail?id=${shop.id}&name=${shop.name}`,
  201. });
  202. };
  203. onShow(() => {
  204. // 页面显示时初始化数据
  205. getList();
  206. });
  207. </script>
  208. <style lang="less" scoped>
  209. @import url("@/style.less");
  210. .wrap {
  211. min-height: 100vh;
  212. background: var(--bg);
  213. display: flex;
  214. flex-direction: column;
  215. .nav_title {
  216. color: var(--black);
  217. font-size: 36rpx;
  218. font-weight: bold;
  219. }
  220. .search-section {
  221. position: sticky;
  222. top: 0;
  223. z-index: 10;
  224. padding: 20rpx 30rpx;
  225. background: var(--light);
  226. border-bottom: 1rpx solid var(--border);
  227. .search-box {
  228. :deep(.u-input) {
  229. padding: 0 20px 0 16px !important;
  230. background: var(--inputBg);
  231. border-radius: 20rpx;
  232. .u-input__content__field-wrapper__field {
  233. height: 47px;
  234. }
  235. }
  236. .icon-search {
  237. color: var(--text);
  238. font-size: 40rpx;
  239. }
  240. }
  241. }
  242. .content {
  243. flex: 1;
  244. padding: 20rpx 30rpx;
  245. padding-bottom: calc(90rpx + constant(safe-area-inset-bottom));
  246. padding-bottom: calc(90rpx + env(safe-area-inset-bottom));
  247. }
  248. .empty-state {
  249. display: flex;
  250. flex-direction: column;
  251. align-items: center;
  252. justify-content: center;
  253. height: 60vh;
  254. .empty-image {
  255. width: 200rpx;
  256. height: 200rpx;
  257. margin-bottom: 32rpx;
  258. }
  259. .empty-text {
  260. font-size: 32rpx;
  261. color: var(--text-01);
  262. margin-bottom: 16rpx;
  263. }
  264. .empty-desc {
  265. font-size: 24rpx;
  266. color: var(--text-01);
  267. }
  268. }
  269. }
  270. </style>