city.vue 9.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407
  1. <template>
  2. <view class="container">
  3. <uv-sticky :top="0" :customNavHeight="0">
  4. <uv-navbar title="本地生活" leftIconSize="0" :border="false" bgColor="#fbcd17"
  5. titleStyle="font-weight: 700;color: #1A1A1A;" placeholder :fixed="false"></uv-navbar>
  6. <view class="search-top">
  7. <view class="city-text" @click="regionSelectionRef.open()">
  8. <uv-icon name="map" color="#000000" size="28rpx"></uv-icon>
  9. <view>{{ cityName }}</view>
  10. <uv-icon name="arrow-right" color="#000000" size="30rpx"></uv-icon>
  11. </view>
  12. <view class="searchBox">
  13. <uv-search v-model="param.businessName" shape="round" :clearabled="false" :showAction="false" height="80rpx"
  14. bg-color="#f7f7f7" border-color="#D9D9D9" laceholder-color="#CCCCCC"
  15. search-icon="/static/image/order/icon_search.png" @search="initData(param)">
  16. <template #suffix>
  17. <view class="search-action" @click.stop="initData(param)">搜索</view>
  18. </template>
  19. </uv-search>
  20. </view>
  21. </view>
  22. </uv-sticky>
  23. <!-- <view style="height: 86rpx"></view> -->
  24. <view class="u-skeleton">
  25. <view class="banner" v-if="bannerList.length > 0 || skeletonShow">
  26. <!-- <image src="/static/shop/banner.png" mode="aspectFill"></image> -->
  27. <uv-swiper class="u-skeleton-fillet" :list="bannerList" keyName="image" height="360rpx"
  28. :indicatorMode="bannerList.length > 1 ? 'round' : 'none'" bgColor="transparent"
  29. @click="bannerClick"></uv-swiper>
  30. </view>
  31. <view class="menu" v-if="iconList.length > 0 || skeletonShow">
  32. <swiper class="swiper u-skeleton-fillet" :indicator-dots="true" :autoplay="false" :current="current"
  33. :style="{ height: menuHeight }" @change="swiperChange">
  34. <swiper-item class="swiper-item" v-for="(el, k) in iconList" :key="k">
  35. <view class="item" v-for="(v, i) in el" :key="i" @click.stop="goShop(v)">
  36. <view class="img">
  37. <image class="u-skeleton-fillet" :src="v.categoryIcon" mode="widthFix"></image>
  38. </view>
  39. <text class="u-skeleton-fillet">{{ v.name || "加载中" }}</text>
  40. </view>
  41. </swiper-item>
  42. </swiper>
  43. </view>
  44. <view class="con-box">
  45. <view class="store-bg">
  46. <image class="store-bg-image" src="/static/store.png" mode="">
  47. </image>
  48. <view class="store-bg-btn" @click.stop="goShop()">
  49. <text class="text">查看更多</text>
  50. <uv-icon name="arrow-right" color="#fbbd1c" size="24rpx"></uv-icon>
  51. </view>
  52. </view>
  53. <view class="items">
  54. <ShopItem v-for="(v) in RowsList" :key="v.businessId" :v="v" />
  55. </view>
  56. </view>
  57. <view>
  58. <noData v-if="RowsList.length <= 0" :config="{ top: 2 }"></noData>
  59. </view>
  60. <uv-skeleton :loading="skeletonShow" :animation="true" bgColor="#FFF"></uv-skeleton>
  61. </view>
  62. <!-- 地区选择 -->
  63. <RegionSelection ref="regionSelectionRef" :hierarchy="2" @confirm="addressConfirm" />
  64. </view>
  65. </template>
  66. <script setup>
  67. import { computed, reactive, ref } from "vue";
  68. import { onLoad, onPullDownRefresh } from "@dcloudio/uni-app";
  69. import { getAdList_Api } from "@/api/index.js";
  70. import { getBusinessList, getBusinessCategoryList } from "@/api/shop.js";
  71. import ShopItem from "../nearbyShop/components/ShopItem.vue";
  72. import $util from "@/util/index";
  73. const RowsList = ref([]);
  74. const regionSelectionRef = ref(null);
  75. const cityName = ref("武汉市");
  76. const param = reactive({
  77. businessName: undefined,
  78. // cityCode: 420100, //武汉市
  79. });
  80. const iconList = ref([]);
  81. const bannerList = ref([]);
  82. const current = ref(0);
  83. const skeletonShow = ref(true);
  84. const menuHeight = computed(() => {
  85. const items = iconList.value[current.value] || [];
  86. return items.length > 5 ? "350rpx" : "150rpx";
  87. });
  88. const goShop = (item = {}) => {
  89. uni.navigateTo({
  90. url:
  91. "/pages/nearbyShop/list?id=" +
  92. (item.categoryId || "") +
  93. "&cityName=" +
  94. cityName.value +
  95. "&cityCode=" +
  96. (param.cityCode || ""),
  97. });
  98. };
  99. // 获取banner广告
  100. const getBanner = () => {
  101. getAdList_Api("local_life_banner").then((res) => {
  102. if (res && res.code == 200) {
  103. bannerList.value = res.data || [];
  104. } else {
  105. bannerList.value = [];
  106. }
  107. });
  108. };
  109. // 分组
  110. const chunk = (arr, size) => {
  111. const res = [];
  112. for (let i = 0; i < arr.length; i += size) res.push(arr.slice(i, i + size));
  113. return res;
  114. };
  115. // 获取分类列表
  116. const getTypeList = () => {
  117. getBusinessCategoryList().then((res) => {
  118. if (res && res.code == 200) {
  119. const list = res.data || [];
  120. iconList.value = chunk(list, 10);
  121. current.value = 0;
  122. } else {
  123. iconList.value = [];
  124. }
  125. });
  126. };
  127. // 获取当前位置
  128. const getLocation = () =>
  129. new Promise((resolve, reject) => {
  130. uni.getLocation({
  131. type: "gcj02",
  132. success: resolve,
  133. fail: reject,
  134. });
  135. });
  136. const swiperChange = (e) => {
  137. if (!e) return;
  138. current.value = e.detail.current;
  139. };
  140. // 地区选择确认
  141. const addressConfirm = (e = {}) => {
  142. // console.log(e);
  143. const picked = e.areaName || e.cityName || e.provinceName;
  144. cityName.value = picked || cityName.value;
  145. Object.assign(param, { ...e });
  146. initData(param);
  147. };
  148. // 轮播图点击事件
  149. const bannerClick = (index) => {
  150. const item = bannerList.value[index] || {};
  151. // console.log(item);
  152. $util.imgLink(item);
  153. };
  154. const initData = (params) => {
  155. uni.showLoading({
  156. title: '加载中',
  157. mask: true
  158. })
  159. getBusinessList(params).then((res) => {
  160. uni.hideLoading();
  161. if (res && res.code == 200) {
  162. RowsList.value = res.data || [];
  163. } else {
  164. RowsList.value = [];
  165. }
  166. }).finally(() => {
  167. uni.stopPullDownRefresh();
  168. });
  169. }
  170. onLoad(() => {
  171. getBanner();
  172. getTypeList();
  173. try {
  174. getLocation().then((res) => {
  175. console.log("当前位置", res);
  176. param.userLongitude = res.longitude;
  177. param.userLatitude = res.latitude;
  178. initData(param);
  179. });
  180. } catch (error) {
  181. console.log(error);
  182. initData(param);
  183. }
  184. skeletonShow.value = false;
  185. // initData(param);
  186. });
  187. onPullDownRefresh(() => {
  188. getBanner();
  189. getTypeList();
  190. initData(param);
  191. });
  192. </script>
  193. <style lang="scss" scoped>
  194. .container {
  195. .search-top {
  196. // position: fixed;
  197. width: 100%;
  198. // height: 110rpx;
  199. // display: flex;
  200. // justify-content: space-between;
  201. // align-items: center;
  202. padding: 0 30rpx 26rpx 30rpx;
  203. background-color: #fbcd17;
  204. // z-index: 3;
  205. box-sizing: border-box;
  206. }
  207. .city-text {
  208. margin-right: 12rpx;
  209. font-size: 28rpx;
  210. font-family: PingFang SC, PingFang SC-Regular;
  211. font-weight: 400;
  212. text-align: center;
  213. color: #000000;
  214. display: flex;
  215. align-items: center;
  216. margin-bottom: 20rpx;
  217. view {
  218. max-width: 100rpx;
  219. white-space: nowrap;
  220. text-overflow: ellipsis;
  221. overflow: hidden;
  222. font-weight: 700;
  223. }
  224. image {
  225. width: 26rpx;
  226. height: 36rpx;
  227. margin-right: 10rpx;
  228. vertical-align: middle;
  229. }
  230. }
  231. .searchBox {
  232. position: relative;
  233. flex: 1;
  234. box-sizing: border-box;
  235. // width: 580rpx;
  236. // height: 80rpx;
  237. image {
  238. width: 35rpx;
  239. height: 35rpx;
  240. }
  241. .search-action {
  242. width: 124rpx;
  243. height: 70rpx;
  244. background: #fbd415;
  245. border-radius: 35rpx;
  246. font-size: 30rpx;
  247. font-family: PingFang SC, PingFang SC-Regular;
  248. font-weight: 400;
  249. color: #1a1a1a;
  250. display: flex;
  251. align-items: center;
  252. justify-content: center;
  253. margin-right: 0rpx;
  254. }
  255. }
  256. }
  257. .banner {
  258. width: 100%;
  259. height: 360rpx;
  260. // padding: 0rpx 30rpx 0;
  261. margin-bottom: 20rpx;
  262. box-sizing: border-box;
  263. image {
  264. width: 100%;
  265. height: 100%;
  266. }
  267. ::v-deep uni-swiper {
  268. height: 360rpx !important;
  269. box-sizing: border-box;
  270. }
  271. }
  272. .menu {
  273. display: flex;
  274. // justify-content: space-between;
  275. // align-items: center;
  276. flex-wrap: wrap;
  277. padding: 0 30rpx;
  278. margin: 22rpx 0;
  279. .swiper {
  280. width: 100%;
  281. height: auto;
  282. transition: all 0.3s;
  283. ::v-deep .uni-swiper-dots-horizontal {
  284. bottom: 0;
  285. .uni-swiper-dot-active {
  286. background-color: #fa6138;
  287. }
  288. }
  289. }
  290. .swiper-item {
  291. display: flex;
  292. flex-wrap: wrap;
  293. }
  294. .item {
  295. display: flex;
  296. flex-direction: column;
  297. align-items: center;
  298. width: 20%;
  299. margin-bottom: 20rpx;
  300. .img {
  301. width: 94rpx;
  302. height: 94rpx;
  303. margin-bottom: 13rpx;
  304. // border-radius: 50%;
  305. overflow: hidden;
  306. image {
  307. width: 100%;
  308. height: 100%;
  309. }
  310. }
  311. text {
  312. width: 100%;
  313. font-size: 26rpx;
  314. font-family: PingFang SC, PingFang SC-Regular;
  315. font-weight: 400;
  316. text-align: center;
  317. color: #1a1a1a;
  318. overflow: hidden;
  319. text-overflow: ellipsis;
  320. white-space: nowrap;
  321. }
  322. }
  323. // .item:nth-child(5n) {
  324. // margin-right: 0;
  325. // }
  326. }
  327. .con-box {
  328. // padding: 0 0;
  329. background: linear-gradient(180deg, #fff8e3, rgba(255, 249, 233, 0) 200rpx);
  330. .store-bg {
  331. width: 750rpx;
  332. height: 118rpx;
  333. display: flex;
  334. align-items: center;
  335. justify-content: space-between;
  336. padding: 30rpx;
  337. box-sizing: border-box;
  338. // background: url("/static/store_bg.png") no-repeat;
  339. background-size: 100% 117rpx;
  340. .store-bg-image {
  341. width: 141rpx;
  342. height: 33rpx;
  343. }
  344. .store-bg-btn {
  345. width: 165rpx;
  346. height: 58rpx;
  347. background: #ffffff;
  348. border-radius: 29rpx;
  349. display: flex;
  350. align-items: center;
  351. justify-content: center;
  352. font-size: 24rpx;
  353. font-weight: 400;
  354. color: #fbbd1c;
  355. }
  356. }
  357. .u-section {
  358. margin-bottom: 27rpx;
  359. ::v-deep .uicon-arrow-right:before {
  360. color: #666;
  361. }
  362. }
  363. .items {
  364. padding: 0 30rpx;
  365. }
  366. }
  367. </style>