doum
2026-04-30 7a0b33a5f2e0ba589bf35a1b8d896700a21f94a4
small-program/pages/index/index.vue
@@ -1,140 +1,337 @@
<template>
   <view class="index-page">
      <view class="top-gradient-bg" :style="{ backgroundImage: 'url(' + backgroundImage + ')' }"></view>
      <image class="top-gradient-bg" src="/static/image/bg_home@2x.png" mode="aspectFill"></image>
      <view class="top-hero">
         <view :style="{ height: statusbarHeight + 'px' }"></view>
         <view class="hero-bar" :style="{ height: navHeight + 'px' }">
            <view class="location-chip" @tap="handleLocation">
            <view class="location-chip" @click="chooseLocation">
               <image src="/static/icon/home1_ic_location@2x.png" mode="aspectFit"></image>
               <text>{{ currentAddress }}</text>
               <u-icon name="arrow-right" size="14" color="#ffffff"></u-icon>
            </view>
            <view class="hero-actions">
               <view class="hero-action action-pill">
                  <u-icon name="more-dot-fill" size="34" color="#2c2c2c"></u-icon>
               </view>
               <view class="hero-action action-ring">
                  <u-icon name="scan" size="28" color="#2c2c2c"></u-icon>
               </view>
            </view>
         </view>
         <view class="search-box" @tap="goStoragePage">
         <view class="search-box" @click="goStoragePage">
            <image src="/static/icon/home_ic_search@2x.png" mode="aspectFit"></image>
            <text class="search-text">搜索寄存点名称或地址</text>
         </view>
         <view class="banner-card hero-banner">
            <view class="banner-tag">2027春运保障计划</view>
            <view class="banner-copy">
               <text>轻松出行</text>
               <text>行李先行</text>
            </view>
            <image class="banner-image" mode="aspectFill"></image>
            <swiper class="banner-swiper" :indicator-dots="true" :autoplay="true" :interval="3000" :duration="500" :circular="true" :indicator-color="'rgba(255, 255, 255, 0.5)'" :indicator-active-color="'#ffffff'">
                <swiper-item v-for="(item, index) in bannerList" :key="index" @click="jumpBannerDetail(item)">
                    <image class="banner-image" :src="item.imgurlFull" mode="aspectFill"></image>
                </swiper-item>
            </swiper>
         </view>
      </view>
      <view class="page-body">
         <view class="service-grid">
            <view class="service-card deposit-card">
            <view class="service-card deposit-card" @click="jumpxiadan">
               <image class="service-image" src="/static/image/home_ic_jicun@2x.png" mode="aspectFit"></image>
            </view>
            <view class="service-card retrieve-card">
            <view class="service-card retrieve-card"  @click="jumpOrderList(3)">
               <image class="service-image" src="/static/image/home_ic_qujian@2x.png" mode="aspectFit"></image>
            </view>
         </view>
         <view class="notice-card">
         <view class="notice-card" v-if="ingOrder && ingOrder.orderId" @click="jumpOrderDetail(ingOrder.orderId)">
            <view class="notice-icon-wrap">
               <image src="/static/icon/home_ic_daizhifu@2x.png" mode="aspectFit"></image>
            </view>
            <view class="notice-copy">
               <text class="notice-title">待支付</text>
               <text class="notice-text">请在 10 分钟内完成支付,超时订单将自动取消</text>
               <text class="notice-title">{{ingOrder.statusDesc || ''}}</text>
               <text class="notice-text">{{ingOrder.tip || ''}}</text>
            </view>
         </view>
         <view class="section-head">
            <text class="section-title">推荐寄存点</text>
            <text class="section-more">查看更多</text>
            <text class="section-more" @click="jumpJC">查看更多</text>
         </view>
         <view class="recommend-list">
            <view v-for="(item, index) in pointList" :key="index" class="point-card" @tap="goStoragePage">
               <view class="point-thumb">
                  <image class="point-thumb-image" mode="aspectFill"></image>
               </view>
               <view class="point-main">
                  <view class="point-head">
                     <text class="point-name">{{ item.name }}</text>
                     <text class="point-distance">{{ item.distance }}</text>
            <template v-if="pointList.length > 0">
               <view v-for="(item, index) in pointList" :key="index" class="point-card" @click="goShopDetails(item)">
                  <view class="point-thumb">
                     <image class="point-thumb-image" :src="item.coverImg" mode="widthFix"></image>
                  </view>
                  <view class="point-address">
                     <image src="/static/icon/home_ic_location3@2x.png" mode="aspectFit"></image>
                     <text>{{ item.address }}</text>
                  <view class="point-main">
                     <view class="point-head">
                        <text class="point-name">{{ item.name ||'' }}</text>
                        <text class="point-distance">{{ item.distanceText  ||''}}</text>
                     </view>
                     <view class="point-address">
                        <image src="/static/icon/home_ic_location3@2x.png" mode="aspectFit"></image>
                        <text>{{ item.address ||''}}</text>
                     </view>
                     <text class="point-time">{{ item.shopHours ||'' }}</text>
                  </view>
                  <text class="point-time">{{ item.time }}</text>
               </view>
            </view>
            </template>
            <template v-else>
               <view class="no-data">
                  <image src="/static/image/default_nodata_white@2x.png" mode="widthFix"></image>
               </view>
            </template>
         </view>
      </view>
      <view class="location-toast">
      <view class="location-toast" v-if="!latitude">
         <view class="toast-copy">
            <text class="toast-title">未授权定位</text>
            <text class="toast-text">我们无法获得您当前位置信息为您推荐附近寄存点</text>
         </view>
         <view class="toast-btn" @tap="handleLocation">开启定位</view>
         <view class="toast-btn" @click="handleLocation">开启定位</view>
      </view>
      <auth-login :show="showLogin" @close="showLogin = false"></auth-login>
      <custom-tabbar></custom-tabbar>
   </view>
</template>
<script>
   import { mapState } from 'vuex'
   import amapFile from '@/utils/amap-wx.130.js'
   import CustomTabbar from '@/components/custom-tabbar/custom-tabbar.vue'
   export default {
      components: {
         CustomTabbar
      },
      computed: {
         ...mapState(['navHeight', 'statusbarHeight', 'address']),
         ...mapState(['navHeight', 'statusbarHeight', 'address', 'latitude', 'cityId', 'longitude', 'token']),
         currentAddress() {
            return this.address && this.address !== '定位中' ? this.address : '获取定位'
         }
      },
      data() {
         return {
            backgroundImage: require('@/static/image/bg_home@2x.png'),
            pointList: [
               {
                  name: '中铁快运南站旗舰店',
                  address: '合肥南站负一层100号',
                  time: '周一至周日 7:00~23:00',
                  distance: '239m'
               },
               {
                  name: '中铁快运合肥火车站',
                  address: '合肥火车站一层12号',
                  time: '周一至周日 7:00~23:00',
                  distance: '12.8km'
               },
               {
                  name: '合肥火车站北广场',
                  address: '合肥火车站一层12号',
                  time: '周一至周日 7:00~23:00',
                  distance: '13.1km'
               }
            ]
            bannerList: [],
            pointList: [],
            ingOrder: null,
            showLogin: false,
            pendingNavigate: null,
            lastLocationAuth: null
         }
      },
      async onLoad() {
         await this.$onLaunched
         await this.getBannerList()
         if (this.cityId && this.latitude && this.longitude) {
            await this.getNearbyShopList()
         }
      },
      onShow() {
         this.ingOrder =null
         this.getIngorder()
         this.checkLocationAuth()
      },
      watch: {
         token(newToken) {
            if (newToken && this.pendingNavigate) {
               const fn = this.pendingNavigate
               this.pendingNavigate = null
               this.showLogin = false
               fn()
            }
         }
      },
      methods: {
         handleLocation() {
            uni.showToast({
               title: '定位功能待接入',
         async checkLocationAuth() {
            const _this = this
            uni.getSetting({
               success(res) {
                  const authLocation = res.authSetting['scope.userLocation']
                  if (_this.lastLocationAuth !== null && _this.lastLocationAuth !== authLocation) {
                     if (authLocation) {
                        _this.positioning()
                     }
                  }
                  _this.lastLocationAuth = authLocation
               }
            })
         },
         async getIngorder() {
            var that =this
            let res = await that.$u.api.getActiveOrderTip()
            if (res && res.code === 200) {
               this.ingOrder = res.data
            }
         },
         jumpBannerDetail(item) {
            if(!item.content || item.content =='' || item.type==0){
               return
            }
            if (item.type == 1) {
               uni.navigateTo({
                  url: '/shop/pages/article-details/article-details?id=' + item.id
               })
            } else if(item.type == 2) {
               uni.navigateTo({
                  url: '/shop/pages/webview/webview?url=' + item.content
               })
            }
         },
         jumpOrderDetail(id){
            uni.navigateTo({
               url:'/pages/delivery-order-detail/delivery-order-detail?userType=0&id='+id
            })
         },
         jumpOrderList(status){
            if (!this.token) {
               this.pendingNavigate = () => this.jumpOrderList(status)
               this.showLogin = true
               return
            }
            uni.setStorageSync("orderStatus",status)
            uni.switchTab({
               url: '/pages/itinerary/itinerary'
            })
         },
         jumpxiadan() {
            if (!this.token) {
               this.pendingNavigate = () => this.jumpxiadan()
               this.showLogin = true
               return
            }
            if (!this.cityId) return uni.showToast({
               title: '当前城市暂未开通',
               icon: 'none'
            })
            uni.navigateTo({
               url: '/pages/luggage-storage/luggage-storage'
            })
         },
         async getBannerList() {
            const res = await this.$u.api.getBannerList({ position: 0 })
            if (res.code === 200) {
               this.bannerList = res.data || []
            }
         },
         async getNearbyShopList() {
            const res = await this.$u.api.getNearbyShopList({
               capacity: 5,
               page: 1,
               model: {
                  latitude: this.latitude,
                  longitude: this.longitude,
                  cityId: this.cityId,
                  sortType: 1
               }
            })
            if (res.code === 200) {
               this.pointList = res.data.records || []
            }
         },
         jumpJC() {
            if (!this.cityId) return uni.showToast({
               title: '当前城市暂未开通',
               icon: 'none'
            })
            uni.navigateTo({
               url: '/pages/storage-point/storage-point'
            })
         },
         handleLocation() {
            var that = this;
            uni.openSetting({
               success: (res) => {
                  if (res.authSetting['scope.userLocation']) {
                     that.positioning()
                  }
               }
            });
         },
         chooseLocation() {
            var that = this;
            uni.chooseLocation({
               type: 'gcj02',
               success: async function(addr) {
                  console.log(addr, '==================uniapp选择位置成功');
                  if (addr.errMsg === 'chooseLocation:ok' && addr.latitude) {
                     const locParam = { latitude: addr.latitude, longitude: addr.longitude };
                     var myAmapFun = new amapFile.AMapWX({ key: that.$gaodeMapKey });
                     myAmapFun.getRegeo({
                        location: addr.longitude + ',' + addr.latitude,
                        success: async function(data) {
                           console.log(data, '==================选择地址逆解析');
                           let info = data[0];
                           locParam.province = info.province;
                           locParam.adcode = info.regeocodeData.addressComponent.adcode
                           var ta = addr.name || '地址获取失败';
                           locParam.address = ta
                           const resCity = await that.$u.api.getCityByName({ code: locParam.adcode })
                           if (resCity.code === 200 && resCity.data) {
                              locParam.cityId = resCity.data.id
                              that.$store.commit('setPosition', locParam)
                              that.pointList = []
                              that.getNearbyShopList()
                           } else {
                              that.$store.commit('setPosition', locParam)
                              that.$store.commit('clearCityId')
                              that.pointList = []
                           }
                        },
                        fail: function(err) {
                           console.error('获取位置失败===========', err);
                           that.$store.commit('setPosition', locParam)
                           that.pointList = []
                           that.getNearbyShopList()
                        }
                     });
                  }
               },
               fail: function(err) {
                  console.error('选择位置失败===========', err);
               }
            });
         },
         // 定位
         positioning() {
            var that = this;
            uni.getLocation({
               type: 'gcj02',
               highAccuracyExpireTime: 3000,
               isHighAccuracy: true,
               success: function (addr) {
                  const locParam = { latitude: addr.latitude, longitude: addr.longitude };
                  var myAmapFun = new amapFile.AMapWX({ key: that.$gaodeMapKey });
                  myAmapFun.getRegeo({
                     location: addr.longitude + ',' + addr.latitude,
                     success: async function(data) {
                        console.log(data, '==================获取地址');
                        let info = data[0];
                        locParam.province = info.province;
                        locParam.adcode = info.regeocodeData.addressComponent.adcode;
                        locParam.area = info.district;
                        locParam.street = info.street;
                        var ta = info.name || '地址获取失败';
                        locParam.address = ta
                        const resCity = await that.$u.api.getCityByName({ code: locParam.adcode })
                        if (resCity.code === 200) {
                           locParam.cityId = resCity.data.id
                        }
                        that.$store.commit('setPosition', locParam)
                        that.getNearbyShopList()
                        that.$isResolve()
                     },
                     fail: (err) => {
                        that.$isResolve()
                     }
                  });
               }
            });
         },
         goShopDetails(item){
            uni.navigateTo({
               url: '/pages/storage-point-detail/storage-point-detail?id='+item.id
            })
         },
         goStoragePage() {
            if (!this.cityId) return uni.showToast({
               title: '当前城市暂未开通',
               icon: 'none'
            })
            uni.navigateTo({
               url: '/pages/storage-point/storage-point'
            })
@@ -146,9 +343,7 @@
<style lang="scss" scoped>
   .index-page {
      position: relative;
      min-height: 100vh;
      background: #f5f7fb;
      padding-bottom: 160rpx;
      padding-bottom: 30rpx;
   }
   .top-gradient-bg {
@@ -185,6 +380,7 @@
      gap: 6rpx;
      max-width: 360rpx;
      image {
         flex-shrink: 0;
         width: 36rpx;
         height: 36rpx;
         margin-right: 8rpx;
@@ -197,26 +393,6 @@
         font-size: 32rpx;
         color: #FFFFFF;
      }
   }
   .hero-actions {
      display: flex;
      align-items: center;
      padding: 6rpx 10rpx;
      border-radius: 999rpx;
      background: rgba(255, 255, 255, 0.7);
   }
   .hero-action {
      width: 52rpx;
      height: 52rpx;
      display: flex;
      align-items: center;
      justify-content: center;
   }
   .action-ring {
      border-left: 1rpx solid rgba(44, 44, 44, 0.12);
   }
   .search-box {
@@ -247,70 +423,35 @@
   .banner-card {
      position: relative;
      height: 310rpx;
      padding: 28rpx 26rpx;
      height: 320rpx;
      padding: 0;
      border-radius: 20rpx;
      overflow: hidden;
      background: linear-gradient(180deg, #bde7ff 0%, #e9f9ff 38%, #90d16e 100%);
      box-sizing: border-box;
   }
   .banner-card::before,
   .banner-card::after {
      content: '';
      position: absolute;
      top: 28rpx;
      width: 140rpx;
      height: 78rpx;
      background: rgba(255, 255, 255, 0.62);
      border-radius: 999rpx;
   .banner-swiper {
      height: 100%;
   }
   .banner-card::before {
      left: -34rpx;
   .banner-swiper ::deep .uni-swiper-dot {
      width: 30rpx;
      height: 6rpx;
      border-radius: 3rpx;
   }
   .banner-card::after {
      right: -24rpx;
   .banner-swiper ::deep .uni-swiper-dot-active {
      background-color: #ffffff;
   }
   .banner-tag {
      display: inline-flex;
      align-items: center;
      height: 46rpx;
      padding: 0 18rpx;
      border-radius: 24rpx;
      background: #ff7d51;
      font-size: 24rpx;
      font-weight: 600;
      color: #ffffff;
   }
.banner-swiper swiper-item {
   height: 100%;
}
   .banner-copy {
      position: relative;
      z-index: 1;
      width: 196rpx;
      margin-top: 44rpx;
      padding: 20rpx 18rpx;
      border: 4rpx solid rgba(255, 255, 255, 0.7);
      border-radius: 18rpx;
      background: rgba(112, 175, 92, 0.28);
      text {
         display: block;
         font-size: 48rpx;
         font-weight: 700;
         line-height: 1.25;
         color: #ffffff;
      }
   }
   .banner-image {
      position: absolute;
      right: 10rpx;
      bottom: 12rpx;
      width: 320rpx;
      height: 220rpx;
   }
.banner-image {
   width: 100%;
   height: 100%;
}
   .service-grid {
      display: grid;
@@ -403,14 +544,26 @@
   }
   .recommend-list {
      margin-top: 14rpx;
      margin-top: 32rpx;
   }
   .no-data {
      width: 100%;
      margin-top: 80rpx;
      display: flex;
      align-items: center;
      justify-content: center;
      image {
         width: 320rpx;
         height: 320rpx;
      }
   }
   .point-card {
      display: flex;
      gap: 18rpx;
      padding: 24rpx;
      margin-bottom: 18rpx;
      margin-bottom: 20rpx;
      border-radius: 18rpx;
      background: #f4f7fc;
   }
@@ -422,7 +575,6 @@
      height: 104rpx;
      border-radius: 12rpx;
      overflow: hidden;
      background: #dfe7f3;
   }
   .point-thumb-image {