MrShi
2026-06-09 3f9032e92fdd383bfefc87a0bec9b242e1223851
app/pages/index/index.vue
@@ -6,12 +6,12 @@
               <image class="hall-page__avatar" :src="centerUserInfo.fullImgUrl || '/static/image/ic_pic@2x.png'" mode="aspectFill"></image>
               <text class="hall-page__name">{{ centerUserInfo.name }}</text>
            </view>
            <view class="hall-page__status" v-if="userInfo.auditStatus === 3" @click="openStatusPicker">
            <view class="hall-page__status" v-if="hasApprovedOfficial" @click="openStatusPicker">
               <view class="hall-page__status-dot" :class="{ 'hall-page__status-dot--offline': acceptingStatus === 0 }"></view>
               <text class="hall-page__status-text">{{ acceptingStatus === 1 ? '接单中' : '已下线' }}</text>
               <text class="hall-page__status-arrow">▼</text>
            </view>
            <view class="hall-page__user" style="opacity: 0;">
            <view class="hall-page__user" style="opacity: 0;">
               <image class="hall-page__avatar" :src="centerUserInfo.fullImgUrl || '/static/image/ic_pic@2x.png'" mode="aspectFill"></image>
               <text class="hall-page__name">{{ centerUserInfo.name }}</text>
            </view>
@@ -19,7 +19,7 @@
         <view class="hall-page__stats">
            <view v-for="item in stats" :key="item.label" class="hall-page__stat-item">
               <text class="hall-page__stat-value">{{ userInfo.auditStatus === 3 ? item.value : '-' }}</text>
               <text class="hall-page__stat-value">{{ hasApprovedOfficial ? item.value : '-' }}</text>
               <text class="hall-page__stat-label">{{ item.label }}</text>
            </view>
         </view>
@@ -27,7 +27,7 @@
         <view class="hall-page__tabs">
            <view v-for="tab in displayTabs" :key="tab.value" class="hall-page__tab" :class="{ 'hall-page__tab--active': activeTab === tab.value }" @click="activeTab = tab.value">
               <text class="hall-page__tab-text">{{ tab.label }}</text>
               <text v-if="tab.count" class="hall-page__tab-count">{{ userInfo.auditStatus === 3 ? tab.count : '' }}</text>
               <text v-if="tab.count" class="hall-page__tab-count">{{ hasApprovedOfficial ? tab.count : '' }}</text>
               <view v-if="activeTab === tab.value" class="hall-page__tab-line"></view>
            </view>   
            <view class="hall-page__filter" @click="toggleFilterPopup(true)">
@@ -63,17 +63,20 @@
         </view>
      </view>
      <scroll-view class="hall-page__body" scroll-y :style="bodyStyle">
         <view class="hall-page__verified" v-if="userInfo.auditStatus !== 3">
      <scroll-view class="hall-page__body" scroll-y :style="bodyStyle" @scrolltolower="handleScrollToLower">
         <view class="hall-page__verified" v-if="!hasApprovedOfficial">
            <image src="/static/image/default_unverified@2x.png" mode="widthFix"></image>
            <button @click="toDriverCertification">去认证</button>
         </view>
         <view v-else-if="userInfo.auditStatus === 3 && acceptingStatus === 1 && currentOrderList.length" class="hall-page__list">
         <view v-else-if="hasApprovedOfficial && acceptingStatus === 1 && currentOrderList.length" class="hall-page__list">
            <view v-for="(item, index) in currentOrderList" :key="item.id" class="order-card" @click="goToOrderDetail(item, index)">
               <view class="order-card__head">
                  <view class="order-card__time">
                  <view class="order-card__time" v-if="item.remainMinutes > 0">
                     <text class="order-card__time-main">{{ formatRemainTime(item.remainMinutes) }}</text>
                     <text class="order-card__time-sub">送达</text>
                  </view>
                  <view class="order-card__time" v-else>
                     <text class="order-card__time-main">配送已超时,请尽快送达</text>
                  </view>
                  <view v-if="activeTab === 'hall'" class="order-card__price-wrap">
                     <text class="order-card__price">¥{{ item.platformRewardAmount ? (item.driverFee + item.platformRewardAmount) / 100 : (item.driverFee / 100).toFixed(2) }}</text>
@@ -95,14 +98,20 @@
               <view class="order-card__route">
                  <view class="order-card__route-side">
                     <text class="order-card__distance-top">{{ item.depositDistance }}</text>
                     <view class="order-card__distance-block order-card__distance-block--top">
                        <text class="order-card__distance-value">{{ formatDistanceParts(item.depositDistance).value }}</text>
                        <text class="order-card__distance-unit">{{ formatDistanceParts(item.depositDistance).unit }}</text>
                     </view>
                     <view class="order-card__line"></view>
                     <text class="order-card__distance-bottom">{{ item.takeDistance }}</text>
                     <view class="order-card__distance-block order-card__distance-block--bottom">
                        <text class="order-card__distance-value">{{ formatDistanceParts(item.takeDistance).value }}</text>
                        <text class="order-card__distance-unit">{{ formatDistanceParts(item.takeDistance).unit }}</text>
                     </view>
                  </view>
                  <view class="order-card__route-main">
                     <view class="order-card__route-item">
                        <view class="order-card__route-texts">
                           <text class="order-card__route-title">{{ item.depositShopName }}</text>
                           <text class="order-card__route-title"><text class="order-card__route-badge-text order-card__route-badge-text--take">取</text>{{ item.depositShopName }}</text>
                           <text class="order-card__route-desc">{{ item.depositShopAddress }}</text>
                        </view>
                        <image src="/static/image/ic_daohang@2x.png" mode="widthFix" class="order-card__nav" @click.stop="navigateToAddress(item, 'deposit')"></image>
@@ -110,19 +119,19 @@
                     <view class="order-card__route-item order-card__route-item--destination">
                        <view class="order-card__route-texts">
                           <template v-if="item.takeShopId">
                              <text class="order-card__route-title">{{ item.takeName }}</text>
                              <text class="order-card__route-title"><text class="order-card__route-badge-text order-card__route-badge-text--send">送</text>{{ item.takeName }}</text>
                              <text class="order-card__route-desc">{{ item.takeAddress }}</text>
                           </template>
                           <text v-else class="order-card__route-title">{{ item.takeAddress }}</text>
                           <text v-else class="order-card__route-title"><text class="order-card__route-badge-text order-card__route-badge-text--send">送</text>{{ item.takeAddress }}</text>
                        </view>
                        <image src="/static/image/ic_daohang@2x.png" mode="widthFix" class="order-card__nav" @click.stop="navigateToAddress(item, 'take')"></image>
                     </view>
                  </view>
               </view>
               <view class="order-card__goods" v-if="item.items && item.items.length > 0">
                  <text class="order-card__goods-text">{{ item.items ? item.items.map(i => `${i.name}*${i.quantity}`).join('、') : '无' }}</text>
                  <text class="order-card__goods-arrow">⌄</text>
               <view class="order-card__goods" v-if="item.items && item.items.length > 0" @click.stop="toggleGoodsExpand(item.id)">
                  <text class="order-card__goods-text">{{ getGoodsText(item.items, item.id) }}</text>
                  <u-icon v-if="getGoodsText(item.items, item.id).length > 20" name="arrow-down" :class="{ 'order-card__goods-arrow--expanded': expandedGoodsIds.includes(item.id) }" size="12" color="#a4a9b1"></u-icon>
               </view>
               <view class="order-card__actions" :class="'order-card__actions--' + activeTab">
@@ -146,7 +155,7 @@
                           <text class="order-card__action-text">联系</text>
                        </view>
                     </view>
                     <button class="order-card__button order-card__button--code" hover-class="order-card__button--hover" @click.stop="handleShowPickupCode(item)">存件码</button>
                     <button class="order-card__button order-card__button--code" hover-class="order-card__button--hover" @click.stop="handleShowPickupCode(item)" v-if="item.takeShopId">存件码</button>
                  </template>
                  <button v-else class="order-card__button" hover-class="order-card__button--hover" @click.stop="handleGrabOrder(item)">立即抢单</button>
               </view>
@@ -252,6 +261,8 @@
<script>
   import { mapState } from 'vuex'
   import { checkLocationPermission } from '@/utils/utils'
   export default {
      data() {
         return {
@@ -272,12 +283,14 @@
            showFilterPopup: false,
            acceptingStatus: 0,
            showStatusPicker: false,
            hasApprovedOfficial: false,
            statusOptions: [
               { text: '上线', value: 1 },
               { text: '下线', value: 0 }
            ],
            centerUserInfo: {},
            activeTab: 'hall',
            expandedGoodsIds: [],
            categoryList: [],
            filterSections: [
               { key: 'sort', title: '排序', options: ['综合排序', '距离最近'] },
@@ -328,21 +341,20 @@
         this.scrollHeight = Math.max(windowHeight - this.headerHeight, 0)
         if (this.userInfo.auditStatus === 99) return;
         this.acceptingStatus = this.userInfo.acceptingStatus || 0
         this.loadOrdersByTab(this.activeTab)
         this.init()
         uni.$on('locationPermissionGranted', () => {
            this.init()
         })
         uni.$on('jiedanSuccess', () => {
            this.loadOrdersByTab(this.activeTab)
         })
      },
      onShow() {
         this.getCenterInfo()
         this.getCategoryListData()
         this.getActiveOrderCount()
      },
      onReachBottom() {
         if (this.acceptingStatus === 0) return;
         if (this.activeTab === 'hall') {
            this.hallPage++
            this.getHallOrders()
         }
         this.getUserInfoDetail()
      },
      computed: {
@@ -382,12 +394,87 @@
      },
      methods: {
         init() {
            console.log('index-init')
            this.$u.api.centerInfo().then(res => {
               if (res.code === 200) {
                  this.acceptingStatus = res.data.acceptingStatus
                  if (res.data.acceptingStatus === 1) {
                     this.loadOrdersByTab(this.activeTab)
                  }
               }
            })
         },
         handleScrollToLower() {
            if (this.acceptingStatus === 0) return;
            this.hallPage++
            if (this.activeTab === 'hall') {
               console.log('hall')
               this.getHallOrders()
            } else if (this.activeTab === 'pickup') {
               this.getPickupOrders()
            } else if (this.activeTab === 'delivering') {
               this.getDeliveringOrders()
            }
         },
         getUserInfoDetail() {
            this.$u.api.verifyDetail().then(res => {
               if (res.code === 200) {
                  this.hasApprovedOfficial = res.data.hasApprovedOfficial === true
               }
            })
         },
         formatRemainTime(minutes) {
            if (!minutes) return 0
            if (minutes === 0) {
               return '配送已超时'
            }
            if (minutes >= 60) {
               return (minutes / 60).toFixed(2) + '小时内'
            }
            return minutes + '分钟内'
         },
         getGoodsText(items, itemId) {
            if (!items || items.length === 0) return '无'
            const text = items.map(i => `${i.name}*${i.quantity}`).join('、')
            if (text.length > 20 && !this.expandedGoodsIds.includes(itemId)) {
               return text.substring(0, 20) + '...'
            }
            return text
         },
         toggleGoodsExpand(id) {
            const index = this.expandedGoodsIds.indexOf(id)
            if (index > -1) {
               this.expandedGoodsIds.splice(index, 1)
            } else {
               this.expandedGoodsIds.push(id)
            }
         },
         formatDistanceParts(distance) {
            if (distance === null || distance === undefined || distance === '') {
               return { value: '--', unit: '' }
            }
            if (typeof distance === 'number') {
               if (distance >= 1000) {
                  return { value: (distance / 1000).toFixed(1), unit: 'km' }
               }
               return { value: String(Math.round(distance)), unit: 'm' }
            }
            const text = String(distance).trim()
            const match = text.match(/^([\d.]+)\s*([a-zA-Z\u4e00-\u9fa5]*)$/)
            if (match) {
               return {
                  value: match[1],
                  unit: match[2] || ''
               }
            }
            return { value: text, unit: '' }
         },
         navigateToAddress(item, type) {
@@ -426,16 +513,24 @@
            })
         },
         getActiveOrderCount() {
            this.$u.api.activeOrderCount().then(res => {
               if (res.code === 200) {
                  this.activeOrderCount = res.data
            checkLocationPermission().then((granted) => {
               if (granted) {
                  console.log('已有位置权限-订单数量')
                  this.$u.api.activeOrderCount().then(res => {
                     if (res.code === 200) {
                        this.activeOrderCount = res.data
                     }
                  }).catch((err) => {
                     this.activeOrderCount = null
                  })
               } else {
                  console.log('还没有位置权限-订单数量')
               }
            }).catch((err) => {
               this.activeOrderCount = null
            })
         },
         handleGrabOrder(item) {
            console.log(item)
            this.selectedGrabOrder = item
            this.showGrabModal = true
         },
@@ -519,7 +614,7 @@
                  }
                  this.stats = [
                     { value: res.data.score, label: '服务分' },
                     { value: res.data.todayCommission, label: '今日预计佣金' },
                     { value: ((res.data.todayCommission / 100) || 0).toFixed(2), label: '今日预计佣金' },
                     { value: res.data.todayOrderCount, label: '今日接单' }
                  ]
               }
@@ -543,14 +638,17 @@
            if (this.userInfo.auditStatus === 99) return;
            if (this.acceptingStatus === 0) return;
            if (tab === 'hall') {
               console.log('抢单大厅')
               this.hallPage = 1
               this.hallHasMore = true
               this.orderList = []
               this.getHallOrders()
            } else if (tab === 'pickup') {
               console.log('待取货')
               this.pickupOrderList = []
               this.getPickupOrders()
            } else if (tab === 'delivering') {
               console.log('配送中')
               this.deliveringOrderList = []
               this.getDeliveringOrders()
            }
@@ -561,81 +659,103 @@
            if (this.hallLoading || !this.hallHasMore) {
               return
            }
            this.hallLoading = true
            let distance = null
            if (this.selectedFilters.distance !== '不限') {
               const distanceText = this.selectedFilters.distance
               if (distanceText.includes('km')) {
                  distance = parseInt(distanceText) * 1000
            checkLocationPermission().then((granted) => {
               if (granted) {
                  console.log('已有位置权限')
                  this.hallLoading = true
                  let distance = null
                  if (this.selectedFilters.distance !== '不限') {
                     const distanceText = this.selectedFilters.distance
                     if (distanceText.includes('km')) {
                        distance = parseInt(distanceText) * 1000
                     } else {
                        distance = parseInt(distanceText)
                     }
                  }
                  const sortTypeMap = {
                     '综合排序': 1,
                     '距离最近': 2
                  }
                  const sortType = this.selectedFilters.sort !== '不限' ? (sortTypeMap[this.selectedFilters.sort] || null) : null
                  let gradeId = null
                  if (this.selectedFilters.level !== '不限') {
                     const selectedCategory = this.categoryList.find(item => item.name === this.selectedFilters.level)
                     if (selectedCategory) {
                        gradeId = selectedCategory.id
                     }
                  }
                  this.$u.api.grabOrderHall({
                     capacity: this.hallPageSize,
                     page: this.hallPage,
                     model: {
                        distance: distance,
                        gradeId: gradeId,
                        sortType: sortType
                     }
                  }).then(res => {
                     this.hallLoading = false
                     if (res.code === 200) {
                        const list = res.data.records || []
                        this.orderList = this.hallPage === 1 ? list : this.orderList.concat(list)
                        this.hallHasMore = list.length >= this.hallPageSize
                     }
                  }).finally(() => {
                     this.hallLoading = false
                  })
               } else {
                  distance = parseInt(distanceText)
                  console.log('还没有位置权限')
               }
            }
            const sortTypeMap = {
               '综合排序': 1,
               '距离最近': 2
            }
            const sortType = this.selectedFilters.sort !== '不限' ? (sortTypeMap[this.selectedFilters.sort] || null) : null
            let gradeId = null
            if (this.selectedFilters.level !== '不限') {
               const selectedCategory = this.categoryList.find(item => item.name === this.selectedFilters.level)
               if (selectedCategory) {
                  gradeId = selectedCategory.id
               }
            }
            console.log('接单大厅:', { distance, gradeId, sortType })
            this.$u.api.grabOrderHall({
               capacity: this.hallPageSize,
               page: this.hallPage,
               model: {
                  distance: distance,
                  gradeId: gradeId,
                  sortType: sortType
               }
            }).then(res => {
               console.log('接单大厅', res)
               this.hallLoading = false
               if (res.code === 200) {
                  const list = res.data.records || []
                  this.orderList = this.hallPage === 1 ? list : this.orderList.concat(list)
                  this.hallHasMore = list.length >= this.hallPageSize
               }
            }).catch((err) => {
               this.hallLoading = false
            })
         },
         getPickupOrders() {
            if (this.acceptingStatus === 0) return;
            if (this.pickupLoading) return
            this.pickupLoading = true
            this.$u.api.activeOrders({ status: 3 }).then(res => {
               console.log('待取货:', res)
               this.pickupLoading = false
               if (res.code === 200) {
                  this.pickupOrderList = res.data.records || res.data || []
            checkLocationPermission().then((granted) => {
               if (granted) {
                  console.log('已有位置权限')
                  this.pickupLoading = true
                  this.$u.api.activeOrders({ status: 3 }).then(res => {
                     console.log('待取货:', res)
                     this.pickupLoading = false
                     if (res.code === 200) {
                        this.pickupOrderList = res.data.records || res.data || []
                     }
                  }).catch((err) => {
                     this.pickupLoading = false
                  })
               } else {
                  console.log('还没有位置权限')
               }
            }).catch((err) => {
               this.pickupLoading = false
            })
         },
         getDeliveringOrders() {
            if (this.acceptingStatus === 0) return;
            if (this.deliveringLoading) return
            this.deliveringLoading = true
            this.$u.api.activeOrders({ status: 4 }).then(res => {
               console.log('配送中:', res)
               this.deliveringLoading = false
               if (res.code === 200) {
                  this.deliveringOrderList = res.data || []
            checkLocationPermission().then((granted) => {
               if (granted) {
                  console.log('已有位置权限')
                  this.deliveringLoading = true
                  this.$u.api.activeOrders({ status: 4 }).then(res => {
                     console.log('配送中:', res)
                     this.deliveringLoading = false
                     if (res.code === 200) {
                        this.deliveringOrderList = res.data || []
                     }
                  }).catch((err) => {
                     this.deliveringLoading = false
                  })
               } else {
                  console.log('还没有位置权限')
               }
            }).catch((err) => {
               this.deliveringLoading = false
            })
         },
@@ -1434,29 +1554,56 @@
      }
      &__route-side {
         width: 64rpx;
         width: 70rpx;
         display: flex;
         flex-direction: column;
         align-items: center;
         flex-shrink: 0;
         padding: 14rpx 0 8rpx;
         border-radius: 26rpx;
         background: #f6f7f9;
         margin-right: 20rpx;
      }
      &__distance-top,
      &__distance-bottom {
         font-size: 22rpx;
         font-weight: 600;
         color: #555b66;
      &__distance-block {
         display: flex;
         flex-direction: column;
         align-items: center;
         line-height: 1;
         &--top {
            margin-bottom: 6rpx;
         }
         &--bottom {
            margin-top: 6rpx;
         }
      }
      &__distance-value {
         font-size: 26rpx;
         font-weight: 700;
         color: #333333;
         text-align: center;
      }
      &__distance-unit {
         margin-top: 6rpx;
         font-size: 18rpx;
         font-weight: 500;
         color: #8c939f;
         text-align: center;
         text-transform: lowercase;
      }
      &__line {
         position: relative;
         width: 4rpx;
         width: 100%;
         flex: 1;
         min-height: 86rpx;
         margin: 10rpx 0;
         border-radius: 999rpx;
         background: #d8dbe1;
         min-height: 62rpx;
         margin: 8rpx 0;
         background: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='20' height='26' viewBox='0 0 20 26'%3E%3Cpath fill='%238C939F' d='M10 0C5.03 0 1 3.99 1 8.92c0 6.31 7.28 12.87 8.07 13.57a1.4 1.4 0 0 0 1.86 0C11.72 21.79 19 15.23 19 8.92 19 3.99 14.97 0 10 0Zm0 12.24a3.32 3.32 0 1 1 0-6.64 3.32 3.32 0 0 1 0 6.64Z'/%3E%3C/svg%3E") center center no-repeat;
         background-size: 20rpx 26rpx;
         &::before,
         &::after {
@@ -1464,18 +1611,19 @@
            position: absolute;
            left: 50%;
            transform: translateX(-50%);
            width: 14rpx;
            height: 14rpx;
            border-radius: 50%;
            background: #6a6f79;
            width: 2rpx;
            border-radius: 999rpx;
            background: #cfd4dc;
         }
         &::before {
            top: -4rpx;
            top: 0;
            height: 18rpx;
         }
         &::after {
            bottom: -4rpx;
            bottom: 0;
            height: 24rpx;
         }
      }
@@ -1494,25 +1642,77 @@
         }
      }
      &__route-badge {
         width: 44rpx;
         height: 44rpx;
         border-radius: 50%;
         display: flex;
         align-items: center;
         justify-content: center;
         font-size: 24rpx;
         font-weight: 600;
         color: #ffffff;
         margin-right: 16rpx;
         flex-shrink: 0;
         &--take {
            background: #10B2FA;
         }
         &--send {
            background: #FF8A00;
         }
      }
      &__route-badge-text {
         display: inline-flex;
         align-items: center;
         justify-content: center;
         width: 36rpx;
         height: 36rpx;
         border-radius: 50%;
         font-size: 22rpx;
         font-weight: 600;
         color: #ffffff;
         margin-right: 8rpx;
         vertical-align: middle;
         &--take {
            background: #10B2FA;
         }
         &--send {
            background: #FF8A00;
         }
      }
      &__route-texts {
         flex: 1;
         min-width: 0;
      }
      &__route-title {
         width: 500rpx;
         display: block;
         font-size: 40rpx;
         font-weight: 700;
         color: #2d3139;
         line-height: 1.3;
         overflow: hidden;
         text-overflow: ellipsis;
         white-space: nowrap;
      }
      &__route-desc {
         width: 500rpx;
         display: block;
         margin-top: 8rpx;
         font-size: 28rpx;
         color: #9ea4ae;
         line-height: 1.4;
         overflow: hidden;
         text-overflow: ellipsis;
         white-space: nowrap;
      }
      &__nav {
@@ -1545,6 +1745,11 @@
         margin-left: 12rpx;
         font-size: 24rpx;
         color: #a4a9b1;
         transition: transform 0.3s;
         &--expanded {
            transform: rotate(180deg);
         }
      }
      &__button {