rk
2026-04-23 3696830fbaf2fd97d98020087abf20917e491bdc
app/pages/index/index.vue
@@ -1,6 +1,367 @@
<template>
   <view style="padding: 40rpx;">
      <button @click="speak('订单已收到,开始播报')">开始播报</button>
      <view class="hall-page">
         <view v-if="showOrderDetail && isStatusDetail" class="order-detail-map-layer" :style="{ top: statusBarHeight + 'px' }">
            <map
               class="order-detail-map-layer__map"
               :latitude="detailMap.center.latitude"
               :longitude="detailMap.center.longitude"
               :markers="detailMap.markers"
               :polyline="detailMap.polyline"
               :include-points="detailMap.includePoints"
               :scale="detailMap.scale"
               :enable-zoom="true"
               :enable-scroll="true"
            ></map>
         </view>
         <view class="hall-page__header" :style="{ paddingTop: statusBarHeight + 'px' }">
         <view class="hall-page__user-row">
            <view class="hall-page__user">
               <image class="hall-page__avatar" src="/static/image/login_bg@2x.png" mode="aspectFill"></image>
               <text class="hall-page__name">汤子新</text>
            </view>
            <view class="hall-page__status">
               <view class="hall-page__status-dot"></view>
               <text class="hall-page__status-text">接单中</text>
               <text class="hall-page__status-arrow">▼</text>
            </view>
            <view class="hall-page__user" style="opacity: 0;">
               <image class="hall-page__avatar" src="/static/image/login_bg@2x.png" mode="aspectFill"></image>
               <text class="hall-page__name">汤子新</text>
            </view>
         </view>
         <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">{{ item.value }}</text>
               <text class="hall-page__stat-label">{{ item.label }}</text>
            </view>
         </view>
         <view class="hall-page__tabs">
            <view v-for="tab in tabs" :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">{{ tab.count }}</text>
               <view v-if="activeTab === tab.value" class="hall-page__tab-line"></view>
            </view>
            <view class="hall-page__filter" @click="toggleFilterPopup(true)">
               <text class="hall-page__filter-text" :class="{ 'hall-page__filter-text--active': showFilterPopup }">筛选</text>
               <image :src="showFilterPopup ? '/static/image/ic_shaixuan_sel@2x.png' : '/static/image/ic_shaixuan@2x.png'" mode="widthFix" class="hall-page__filter-icon"></image>
            </view>
         </view>
      </view>
      <view v-if="showFilterPopup" class="filter-popup" :style="{ top: headerHeight + 'px', bottom: tabbarHeight + 'px' }" @click="toggleFilterPopup(false)">
         <view class="filter-popup__panel" @click.stop>
            <view v-for="section in filterSections" :key="section.key" class="filter-popup__section">
               <text class="filter-popup__title">{{ section.title }}</text>
               <view class="filter-popup__options">
                  <view
                     v-for="option in section.options"
                     :key="option"
                     class="filter-popup__option"
                     :class="{ 'filter-popup__option--active': selectedFilters[section.key] === option }"
                     @click="selectFilter(section.key, option)"
                  >
                     <text class="filter-popup__option-text">{{ option }}</text>
                  </view>
               </view>
            </view>
            <view class="filter-popup__actions">
               <button class="filter-popup__button filter-popup__button--reset" hover-class="filter-popup__button--hover" @click="resetFilters">重置</button>
               <button class="filter-popup__button filter-popup__button--confirm" hover-class="filter-popup__button--hover" @click="confirmFilters">确认</button>
            </view>
         </view>
      </view>
      <scroll-view class="hall-page__body" scroll-y :style="bodyStyle">
         <view v-if="currentOrderList.length" class="hall-page__list">
            <view v-for="item in currentOrderList" :key="item.id" class="order-card" @click="openDetailPopup(item)">
               <view class="order-card__head">
                  <view class="order-card__time">
                     <text class="order-card__time-main">{{ item.time }}</text>
                     <text class="order-card__time-sub">送达</text>
                  </view>
                  <view v-if="activeTab === 'hall'" class="order-card__price-wrap">
                     <text v-if="item.serialNo" class="order-card__serial">#{{ item.serialNo }}</text>
                     <text class="order-card__price">{{ item.price }}</text>
                  </view>
                  <view v-else class="order-card__price-wrap order-card__price-wrap--serial-only">
                     <text v-if="item.serialNo" class="order-card__serial">#{{ item.serialNo }}</text>
                  </view>
               </view>
               <view class="order-card__meta">
                  <view class="order-card__tags">
                     <view v-for="tag in item.tags" :key="tag.text" class="order-card__tag-wrap">
                        <image
                           v-if="getTagImage(tag.text)"
                           class="order-card__tag-icon"
                           :src="getTagImage(tag.text)"
                           mode="widthFix"
                        ></image>
                        <text v-else class="order-card__tag" :class="tag.type ? 'order-card__tag--' + tag.type : ''">{{ tag.text }}</text>
                     </view>
                  </view>
                  <text v-if="activeTab === 'hall'" class="order-card__extra">含加急¥{{ item.extra }}</text>
               </view>
               <view class="order-card__route">
                  <view class="order-card__route-side">
                     <text class="order-card__distance-top">{{ item.distanceTop }}</text>
                     <view class="order-card__line"></view>
                     <text class="order-card__distance-bottom">{{ item.distanceBottom }}</text>
                  </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.fromName }}</text>
                           <text class="order-card__route-desc">{{ item.fromAddress }}</text>
                        </view>
                        <image src="/static/image/ic_daohang@2x.png" mode="widthFix" class="order-card__nav"></image>
                     </view>
                     <view class="order-card__route-item order-card__route-item--destination">
                        <view class="order-card__route-texts">
                           <text class="order-card__route-title">{{ item.toName }}</text>
                           <text class="order-card__route-desc">{{ item.toAddress }}</text>
                        </view>
                        <image src="/static/image/ic_daohang@2x.png" mode="widthFix" class="order-card__nav"></image>
                     </view>
                  </view>
               </view>
               <view class="order-card__goods">
                  <text class="order-card__goods-text">{{ item.goods }}</text>
                  <text class="order-card__goods-arrow">⌄</text>
               </view>
               <view class="order-card__actions" :class="'order-card__actions--' + activeTab">
                  <template v-if="activeTab === 'pickup'">
                     <view class="order-card__icon-actions">
                        <view class="order-card__icon-action" @click.stop="show = true">
                        <image class="order-card__action-icon" src="/static/image/ic_cancle@2x.png" mode="aspectFit"></image>
                           <text class="order-card__action-text">取消</text>
                        </view>
                        <view class="order-card__icon-action" @click.stop>
                           <image class="order-card__action-icon" src="/static/image/ic_call@2x.png" mode="aspectFit"></image>
                           <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="show1 = true">取货码</button>
                  </template>
                  <template v-else-if="activeTab === 'delivering'">
                     <view class="order-card__icon-actions order-card__icon-actions--single">
                        <view class="order-card__icon-action" @click.stop>
                           <image class="order-card__action-icon" src="/static/image/ic_call@2x.png" mode="aspectFit"></image>
                           <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>存件码</button>
                  </template>
                  <button v-else class="order-card__button" hover-class="order-card__button--hover" @click.stop="openDetailPopup(item)">立即抢单</button>
               </view>
            </view>
         </view>
         <view v-else class="hall-page__empty">
            <image class="hall-page__empty-icon" src="/static/image/default_nodata_grey@2x.png" mode="aspectFit"></image>
         </view>
      </scroll-view>
      <!-- 取消订单 -->
      <u-modal
         :show="show"
         showCancelButton
         @cancel="show = false"
         cancelColor="#666666"
         confirmColor="#0055FF"
         title="取消订单确认">
         <view style="text-align: center;color: #333333;font-size: 28rpx;font-weight: 400;">
            您今日还可取消 X 次订单,次数用尽后今日将无法接单,是否确认取消?
         </view>
      </u-modal>
      <!-- 取货码 -->
      <u-popup :show="show1" round="20" mode="bottom">
         <view class="qrcode">
            <view class="qrcode-title">
               <image src="/static/image/ic_close@2x.png" mode="widthFix" style="opacity: 0;"></image>
               <text>取货码</text>
               <image src="/static/image/ic_close@2x.png" mode="widthFix" @click="show1 = false"></image>
            </view>
            <view class="qrcode-image">
               <image src="/static/logo.png" mode="widthFix"></image>
            </view>
            <view class="qrcode-btn" hover-class="qrcode-btn--hover" @click="show1 = false">关闭</view>
         </view>
      </u-popup>
      <!-- 订单详情 -->
      <u-popup :show="showOrderDetail" round="20" mode="bottom" :overlayStyle="{ background: 'rgba(0, 0, 0, 0.32)' }" @close="showOrderDetail = false">
         <view class="order-detail" :style="{ height: 'calc(100vh - ' + statusBarHeight + 'px)' }">
            <view v-if="isStatusDetail" class="order-detail__map-section">
               <view class="order-detail__map">
                  <view class="order-detail__map-placeholder"></view>
                  <view class="order-detail__map-bubble">{{ detailMap.tips }}</view>
               </view>
               <view class="order-detail__status-bar">
                  <view class="order-detail__status-left">
                     <image class="order-detail__status-icon" mode="aspectFit"></image>
                     <text class="order-detail__status-name">{{ detailOrder.statusText }}</text>
                  </view>
                  <view class="order-detail__status-right">
                     <text v-if="detailOrder.showCancelTag" class="order-detail__cancel-tag">取消订单</text>
                     <text class="order-detail__status-no">#{{ detailOrder.serialNo }}</text>
                  </view>
               </view>
            </view>
            <scroll-view class="order-detail__scroll" scroll-y>
               <view class="order-detail__content">
                  <view class="order-detail__head">
                     <view class="order-detail__time-wrap">
                        <text class="order-detail__time">{{ detailOrder.time }}</text>
                        <text class="order-detail__time-sub">送达</text>
                     </view>
                     <view class="order-detail__price-wrap">
                        <text class="order-detail__price">{{ detailOrder.price }}</text>
                        <text class="order-detail__extra">含加急¥{{ detailOrder.extra }}</text>
                     </view>
                  </view>
                  <view v-if="isStatusDetail" class="order-detail__qrcode-section">
                     <view class="order-detail__qrcode-box">
                        <image class="order-detail__qrcode-image" src="/static/logo.png" mode="aspectFit"></image>
                     </view>
                     <text class="order-detail__qrcode-value">{{ detailOrder.qrcodeValue }}</text>
                     <text class="order-detail__qrcode-label">{{ detailOrder.qrcodeLabel }}</text>
                  </view>
                  <view class="order-detail__tags">
                     <view v-for="tag in detailOrder.tags" :key="tag.text" class="order-detail__tag-wrap">
                        <image
                           v-if="getTagImage(tag.text)"
                           class="order-detail__tag-icon"
                           :src="getTagImage(tag.text)"
                           mode="widthFix"
                        ></image>
                        <text v-else class="order-detail__tag" :class="tag.type ? 'order-detail__tag--' + tag.type : ''">{{ tag.text }}</text>
                     </view>
                  </view>
                  <view class="order-detail__route">
                     <view class="order-detail__route-side">
                        <text class="order-detail__distance-top">{{ detailOrder.distanceTop }}</text>
                        <view class="order-detail__line"></view>
                        <text class="order-detail__distance-bottom">{{ detailOrder.distanceBottom }}</text>
                     </view>
                     <view class="order-detail__route-main">
                        <view class="order-detail__route-item">
                           <view class="order-detail__route-texts">
                              <text class="order-detail__route-title">{{ detailOrder.fromName }}</text>
                              <text class="order-detail__route-desc">{{ detailOrder.fromAddress }}</text>
                           </view>
                           <view class="order-detail__route-actions">
                              <image class="order-detail__route-icon" src="/static/image/ic_c1all@2x.png" mode="aspectFit"></image>
                              <image class="order-detail__route-icon" src="/static/image/ic_daohang@2x.png" mode="aspectFit"></image>
                           </view>
                        </view>
                        <view class="order-detail__route-item order-detail__route-item--destination">
                           <view class="order-detail__route-texts">
                              <text class="order-detail__route-title">{{ detailOrder.toName }}</text>
                              <text class="order-detail__route-desc">{{ detailOrder.toAddress }}</text>
                           </view>
                           <view class="order-detail__route-actions">
                              <image class="order-detail__route-icon order-detail__route-icon--phone" src="/static/image/ic_c1all@2x.png" mode="aspectFit"></image>
                              <image class="order-detail__route-icon" src="/static/image/ic_daohang@2x.png" mode="aspectFit"></image>
                           </view>
                        </view>
                     </view>
                  </view>
                  <view class="order-detail__section">
                     <text class="order-detail__section-title">客户信息</text>
                     <view class="order-detail__customer">
                        <text class="order-detail__customer-text">{{ detailOrder.customer.name }}(手机号{{ detailOrder.customer.phone }})</text>
                        <image class="order-detail__customer-icon" src="/static/image/ic_c1all@2x.png" mode="aspectFit"></image>
                     </view>
                  </view>
                  <view class="order-detail__section">
                     <text class="order-detail__section-title">物品清单(共{{ detailOrder.goodsList.length }}件)</text>
                     <view class="order-detail__goods-list">
                        <view v-for="goods in detailOrder.goodsList" :key="goods.name" class="order-detail__goods-item">
                           <text class="order-detail__goods-name">{{ goods.name }}</text>
                           <text class="order-detail__goods-count">x{{ goods.count }}</text>
                        </view>
                     </view>
                  </view>
                  <view class="order-detail__section order-detail__section--photos">
                     <text class="order-detail__section-title">物品信息</text>
                     <text class="order-detail__goods-category">{{ detailOrder.goodsCategory }}</text>
                     <view class="order-detail__photos">
                        <view v-for="(photo, index) in detailOrder.photos" :key="index" class="order-detail__photo-item">
                           <image class="order-detail__photo" :src="photo" mode="aspectFill"></image>
                        </view>
                     </view>
                  </view>
               </view>
            </scroll-view>
            <view class="order-detail__footer">
               <view class="order-detail__cancel" @click="showOrderDetail = false">
                  <image class="order-detail__cancel-icon" src="/static/image/ic_close2@2x.png" mode="aspectFit"></image>
               </view>
               <button v-if="!isStatusDetail" class="order-detail__confirm" hover-class="order-detail__confirm--hover">确认抢单</button>
               <button v-else class="order-detail__confirm order-detail__confirm--status" hover-class="order-detail__confirm--hover" @click="handleStatusAction">
                  <image class="order-detail__confirm-icon" src="/static/image/ic_photo@2x.png" mode="aspectFit"></image>
                  <text>{{ detailPopupType === 'pickup' ? '拍照取货' : '拍照送达' }}</text>
               </button>
            </view>
         </view>
      </u-popup>
      <u-popup :show="showPhotoDeliverPopup" round="20" mode="bottom">
         <view class="photo-deliver">
            <view class="photo-deliver__header">
               <image class="photo-deliver__close-placeholder" mode="aspectFit"></image>
               <text class="photo-deliver__title">拍照送达</text>
               <image class="photo-deliver__close" mode="aspectFit" @click="showPhotoDeliverPopup = false"></image>
            </view>
            <view class="photo-deliver__section">
               <view class="photo-deliver__label-row">
                  <text class="photo-deliver__label">拍摄送达照片</text>
                  <text class="photo-deliver__required">*</text>
                  <text class="photo-deliver__hint">最多3张照片</text>
               </view>
               <view class="photo-deliver__photos">
                  <view class="photo-deliver__upload-card">
                     <image class="photo-deliver__upload-icon" mode="aspectFit"></image>
                     <text class="photo-deliver__upload-text">点击拍照</text>
                  </view>
                  <view class="photo-deliver__preview-card">
                     <image class="photo-deliver__preview-image" mode="aspectFill"></image>
                     <view class="photo-deliver__preview-mask">
                        <text class="photo-deliver__preview-delete">删除</text>
                     </view>
                  </view>
               </view>
            </view>
            <view class="photo-deliver__section photo-deliver__section--remark">
               <text class="photo-deliver__remark-title">备注信息</text>
               <textarea class="photo-deliver__textarea" maxlength="200" placeholder="请输入" placeholder-style="color: #c7cbd3;" />
            </view>
            <button class="photo-deliver__submit" hover-class="photo-deliver__submit--hover" @click="showPhotoDeliverPopup = false">确认送达</button>
         </view>
      </u-popup>
   </view>
</template>
@@ -8,7 +369,315 @@
   export default {
      data() {
         return {
            tts: null // 原生TTS对象
            tts: null,
            show: false,
            show1: false,
            showPhotoDeliverPopup: false,
            showOrderDetail: false,
            detailPopupType: 'hall',
            routeInfo: null,
            statusBarHeight: 0,
            headerHeight: 0,
            tabbarHeight: 0,
            scrollHeight: 0,
            showFilterPopup: false,
            activeTab: 'hall',
            filterSections: [
               { key: 'sort', title: '排序', options: ['综合排序', '离我最近'] },
               { key: 'level', title: '物品等级', options: ['不限', '普通物品', '贵重物品', '大件物品', '特殊物品', '其他物品'] },
               { key: 'distance', title: '位置范围', options: ['不限', '500m', '1km', '2km', '3km', '4km', '5km', '6km', '7km'] }
            ],
            selectedFilters: {
               sort: '综合排序',
               level: '不限',
               distance: '不限'
            },
            detailOrder: {
               time: '45分钟内',
               price: '¥20.5',
               extra: '3.0',
               serialNo: 1,
               statusText: '抢单大厅',
               qrcodeValue: '767889',
               qrcodeLabel: '取货码',
               mapTips: '',
               showCancelTag: false,
               startPoint: {
                  latitude: 31.8269,
                  longitude: 117.2334
               },
               endPoint: {
                  latitude: 31.8435,
                  longitude: 117.2852
               },
               tags: [
                  { text: '标速达', type: 'blue' },
                  { text: '贵重物品', type: 'orange' }
               ],
               distanceTop: '349m',
               distanceBottom: '12.5km',
               fromName: '中铁快运南站旗舰店',
               fromAddress: '莲花路200号莲花产业园F栋401',
               toName: '佳苑巴黎都市3期10栋301室',
               toAddress: '洞庭湖路与湖北路交叉口西150米',
               customer: {
                  name: '刘先生',
                  phone: '2878'
               },
               goodsList: [
                  { name: '大件行李', count: 1 },
                  { name: '中件行李', count: 2 },
                  { name: '小件行李', count: 3 },
                  { name: '背包', count: 2 }
               ],
               goodsCategory: '文件',
               photos: ['/static/logo.png', '/static/logo.png', '/static/logo.png']
            },
            stats: [
               { value: '4.2', label: '服务分' },
               { value: '234.3', label: '今日预计佣金' },
               { value: '13', label: '今日接单' }
            ],
            tabs: [
               { label: '抢单大厅', value: 'hall' },
               { label: '待取货', value: 'pickup', count: 2 },
               { label: '配送中', value: 'delivering', count: 2 }
            ],
            orderList: [
               {
                  id: 1,
                  time: '45分钟内',
                  price: '¥20.5',
                  extra: '3.0',
                  startPoint: {
                     latitude: 31.829512,
                     longitude: 117.239211
                  },
                  endPoint: {
                     latitude: 31.841268,
                     longitude: 117.278695
                  },
                  tags: [
                     { text: '极速达', type: 'blue' },
                     { text: '贵重物品', type: 'orange' }
                  ],
                  distanceTop: '349m',
                  distanceBottom: '12.5km',
                  fromName: '中铁快运南站旗舰店',
                  fromAddress: '莲花路200号莲花产业园F栋401',
                  toName: '佳苑巴黎都市3期10栋301室',
                  toAddress: '洞庭湖路与湖北路交叉口西150米',
                  goods: '大件行李*1、中件行李*2、小件行李*3、背包*1'
               },
               {
                  id: 2,
                  time: '45分钟内',
                  price: '¥20.5',
                  extra: '3.0',
                  startPoint: {
                     latitude: 31.827106,
                     longitude: 117.232884
                  },
                  endPoint: {
                     latitude: 31.847331,
                     longitude: 117.289762
                  },
                  tags: [
                     { text: '极速达', type: 'red' },
                     { text: '大件物品', type: 'blue-light' }
                  ],
                  distanceTop: '349m',
                  distanceBottom: '12.5km',
                  fromName: '中铁快运南站旗舰店(合肥南站北广场',
                  fromAddress: '莲花路200号莲花产业园F栋401',
                  toName: '佳苑巴黎都市3期10栋301室',
                  toAddress: '洞庭湖路与湖北路交叉口西150米',
                  goods: '大件行李*1'
               },
               {
                  id: 3,
                  time: '45分钟内',
                  price: '¥20.5',
                  extra: '3.0',
                  startPoint: {
                     latitude: 31.823761,
                     longitude: 117.228947
                  },
                  endPoint: {
                     latitude: 31.838473,
                     longitude: 117.272513
                  },
                  tags: [
                     { text: '极速达', type: 'red' },
                     { text: '大件物品', type: 'blue-light' }
                  ],
                  distanceTop: '349m',
                  distanceBottom: '12.5km',
                  fromName: '中铁快运南站旗舰店(合肥南站北广场',
                  fromAddress: '莲花路200号莲花产业园F栋401',
                  toName: '佳苑巴黎都市3期10栋301室',
                  toAddress: '洞庭湖路与湖北路交叉口西150米',
                  goods: '大件行李*1'
               }
            ],
            pickupOrderList: [
               {
                  id: 101,
                  serialNo: 1,
                  statusText: '待取货',
                  qrcodeValue: '767889',
                  qrcodeLabel: '取货码',
                  mapTips: '',
                  showCancelTag: true,
                  startPoint: {
                     latitude: 31.829512,
                     longitude: 117.239211
                  },
                  endPoint: {
                     latitude: 31.841268,
                     longitude: 117.278695
                  },
                  time: '45分钟内',
                  price: '¥20.5',
                  extra: '3.0',
                  tags: [
                     { text: '标速达', type: 'blue' },
                     { text: '贵重物品', type: 'orange' }
                  ],
                  distanceTop: '349m',
                  distanceBottom: '12.5km',
                  fromName: '中铁快运南站旗舰店',
                  fromAddress: '莲花路200号莲花产业园F栋401',
                  toName: '小铁无忧存',
                  toAddress: '洞庭湖路与湖北路交叉口西150米',
                  goods: '大件行李*1、中件行李*2、小件行李*3、背包*1'
               }
            ],
            deliveringOrderList: [
               {
                  id: 201,
                  serialNo: 1,
                  statusText: '配送中',
                  qrcodeValue: '767889',
                  qrcodeLabel: '存件码',
                  mapTips: '',
                  showCancelTag: false,
                  startPoint: {
                     latitude: 31.827106,
                     longitude: 117.232884
                  },
                  endPoint: {
                     latitude: 31.847331,
                     longitude: 117.289762
                  },
                  time: '45分钟内',
                  price: '¥20.5',
                  extra: '3.0',
                  tags: [
                     { text: '标速达', type: 'blue' },
                     { text: '贵重物品', type: 'orange' }
                  ],
                  distanceTop: '349m',
                  distanceBottom: '12.5km',
                  fromName: '中铁快运南站旗舰店',
                  fromAddress: '莲花路200号莲花产业园F栋401',
                  toName: '小铁无忧存',
                  toAddress: '洞庭湖路与湖北路交叉口西150米',
                  goods: '大件行李*1、中件行李*2、小件行李*3、背包*1'
               }
            ]
         }
      },
      onLoad() {
         const systemInfo = uni.getSystemInfoSync()
         const safeBottom = systemInfo.safeAreaInsets ? systemInfo.safeAreaInsets.bottom || 0 : 0
         const windowHeight = systemInfo.windowHeight || 0
         this.statusBarHeight = systemInfo.statusBarHeight || 0
         this.headerHeight = this.statusBarHeight + uni.upx2px(308)
         this.tabbarHeight = uni.upx2px(100) + safeBottom
         this.scrollHeight = Math.max(windowHeight - this.headerHeight, 0)
      },
      computed: {
         isStatusDetail() {
            return this.detailPopupType === 'pickup' || this.detailPopupType === 'delivering'
         },
         detailMap() {
            const fallbackPoint = {
               latitude: 31.8269,
               longitude: 117.2334
            }
            const startPoint = this.detailOrder.startPoint || fallbackPoint
            const endPoint = this.detailOrder.endPoint || fallbackPoint
            const routePoints = this.routeInfo && this.routeInfo.points && this.routeInfo.points.length ? this.routeInfo.points : []
            const center = {
               latitude: (startPoint.latitude + endPoint.latitude) / 2,
               longitude: (startPoint.longitude + endPoint.longitude) / 2
            }
            const distanceKm = this.routeInfo && this.routeInfo.distanceKm ? this.routeInfo.distanceKm : 0
            const durationMinutes = this.routeInfo && this.routeInfo.durationMinutes ? this.routeInfo.durationMinutes : 0
            const tips = this.detailOrder.mapTips || (distanceKm ? `剩余${distanceKm.toFixed(1)}km,约${durationMinutes}分钟` : '路线规划中...')
            return {
               center,
               markers: [
                  {
                     id: 1,
                     latitude: startPoint.latitude,
                     longitude: startPoint.longitude,
                     iconPath: '/static/image/map_marker_start.svg',
                     width: 32,
                     height: 38,
                     anchor: {
                        x: 0.5,
                        y: 1
                     }
                  },
                  {
                     id: 2,
                     latitude: endPoint.latitude,
                     longitude: endPoint.longitude,
                     iconPath: '/static/image/map_marker_end.svg',
                     width: 32,
                     height: 38,
                     anchor: {
                        x: 0.5,
                        y: 1
                     }
                  }
               ],
               polyline: [
                  ...(routePoints.length ? [{
                     points: routePoints,
                     color: '#2A7FFF',
                     width: 8,
                     dottedLine: false,
                     arrowLine: true
                  }] : [])
               ],
               includePoints: routePoints.length ? routePoints : [startPoint, endPoint],
               scale: 12,
               tips
            }
         },
         currentOrderList() {
            const orderMap = {
               hall: this.orderList,
               pickup: this.pickupOrderList,
               delivering: this.deliveringOrderList
            }
            return orderMap[this.activeTab] || []
         },
         bodyStyle() {
            return {
               marginTop: this.headerHeight + 'px',
               height: this.scrollHeight + 'px'
            }
         }
      },
@@ -17,7 +686,193 @@
      },
      methods: {
         // ========== 初始化安卓原生语音 ==========
         calculateDistance(startPoint, endPoint) {
            if (!startPoint || !endPoint) {
               return 0
            }
            const toRad = (value) => value * Math.PI / 180
            const earthRadius = 6371
            const deltaLat = toRad(endPoint.latitude - startPoint.latitude)
            const deltaLng = toRad(endPoint.longitude - startPoint.longitude)
            const lat1 = toRad(startPoint.latitude)
            const lat2 = toRad(endPoint.latitude)
            const a = Math.sin(deltaLat / 2) * Math.sin(deltaLat / 2) + Math.cos(lat1) * Math.cos(lat2) * Math.sin(deltaLng / 2) * Math.sin(deltaLng / 2)
            const c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a))
            return earthRadius * c
         },
         estimateDuration(distanceKm) {
            if (!distanceKm) {
               return 1
            }
            const averageSpeedKmPerHour = 35
            return Math.max(1, Math.round(distanceKm / averageSpeedKmPerHour * 60))
         },
         handleStatusAction() {
            if (this.detailPopupType === 'delivering') {
               this.showOrderDetail = false
               this.showPhotoDeliverPopup = true
            }
         },
         openDetailPopup(item) {
            this.detailPopupType = this.activeTab
            this.routeInfo = null
            this.detailOrder = {
               ...this.detailOrder,
               ...item,
               customer: item.customer || this.detailOrder.customer,
               goodsList: item.goodsList || this.detailOrder.goodsList,
               goodsCategory: item.goodsCategory || this.detailOrder.goodsCategory,
               photos: item.photos || this.detailOrder.photos
            }
            this.showOrderDetail = true
            if (this.activeTab === 'pickup' || this.activeTab === 'delivering') {
               this.fetchDrivingRoute(this.detailOrder)
            }
         },
         fetchDrivingRoute(order) {
            if (!order || !order.startPoint || !order.endPoint) {
               return
            }
            const origin = `${order.startPoint.longitude},${order.startPoint.latitude}`
            const destination = `${order.endPoint.longitude},${order.endPoint.latitude}`
            uni.request({
               url: 'https://restapi.amap.com/v3/direction/driving',
               method: 'GET',
               data: {
                  key: 'e4d46c87adf151dca20060317592b1b6',
                  origin,
                  destination,
                  extensions: 'all',
                  strategy: 0,
                  output: 'json'
               },
               success: (response) => {
                  const path = response.data && response.data.route && response.data.route.paths && response.data.route.paths[0]
                  if (!path) {
                     this.routeInfo = this.buildMockRouteInfo(order.startPoint, order.endPoint)
                     return
                  }
                  const points = this.parseRoutePoints(path.steps || [], order.startPoint, order.endPoint)
                  const distanceKm = Number(path.distance || 0) / 1000
                  const durationMinutes = Math.max(1, Math.round(Number(path.duration || 0) / 60))
                  this.routeInfo = {
                     points,
                     distanceKm: distanceKm || this.calculateDistance(order.startPoint, order.endPoint),
                     durationMinutes
                  }
               },
               fail: () => {
                  this.routeInfo = this.buildMockRouteInfo(order.startPoint, order.endPoint)
               }
            })
         },
         buildMockRouteInfo(startPoint, endPoint) {
            const middleLatitude = (startPoint.latitude + endPoint.latitude) / 2
            const middleLongitude = (startPoint.longitude + endPoint.longitude) / 2
            const points = [
               startPoint,
               {
                  latitude: startPoint.latitude + (middleLatitude - startPoint.latitude) * 0.65,
                  longitude: startPoint.longitude + 0.008
               },
               {
                  latitude: middleLatitude + 0.004,
                  longitude: middleLongitude + 0.012
               },
               {
                  latitude: endPoint.latitude - 0.003,
                  longitude: endPoint.longitude - 0.008
               },
               endPoint
            ]
            const distanceKm = this.calculatePathDistance(points)
            const durationMinutes = this.estimateDuration(distanceKm)
            return {
               points,
               distanceKm,
               durationMinutes
            }
         },
         parseRoutePoints(steps, startPoint, endPoint) {
            const points = []
            steps.forEach((step) => {
               const polyline = step.polyline || ''
               polyline.split(';').forEach((pointText) => {
                  const [longitude, latitude] = pointText.split(',').map(Number)
                  if (!Number.isNaN(latitude) && !Number.isNaN(longitude)) {
                     points.push({ latitude, longitude })
                  }
               })
            })
            if (!points.length) {
               return [startPoint, endPoint]
            }
            return points
         },
         calculatePathDistance(points) {
            if (!points || points.length < 2) {
               return 0
            }
            let totalDistance = 0
            for (let index = 1; index < points.length; index += 1) {
               totalDistance += this.calculateDistance(points[index - 1], points[index])
            }
            return totalDistance
         },
         toggleFilterPopup(show) {
            this.showFilterPopup = show
         },
         selectFilter(key, option) {
            this.selectedFilters = {
               ...this.selectedFilters,
               [key]: option
            }
         },
         resetFilters() {
            this.selectedFilters = {
               sort: '综合排序',
               level: '不限',
               distance: '不限'
            }
         },
         confirmFilters() {
            this.showFilterPopup = false
         },
         getTagImage(tagText) {
            const tagImageMap = {
               '极速达': '/static/image/ic_jisuda@2x.png',
               '标速达': '/static/image/ic_biaosuda@2x.png'
            }
            return tagImageMap[tagText] || ''
         },
         initTTS() {
            if (uni.getSystemInfoSync().platform !== 'android') {
               console.log('仅支持安卓')
@@ -44,7 +899,6 @@
            }
         },
         // ========== 语音播报(核心方法) ==========
         speak(text) {
            if (!this.tts) {
               uni.showToast({
@@ -62,13 +916,11 @@
            }
         },
         // 停止播报
         stopSpeak() {
            if (this.tts) this.tts.stop()
         }
      },
      // 页面销毁时关闭语音
      onUnload() {
         if (this.tts) {
            this.tts.stop()
@@ -76,4 +928,1254 @@
         }
      }
   }
</script>
</script>
<style lang="scss" scoped>
   .hall-page {
      position: relative;
      height: 100vh;
      background: #f5f6f8;
      overflow: hidden;
      .order-detail-map-layer {
         position: fixed;
         left: 0;
         right: 0;
         height: 330rpx;
         z-index: 10081;
         overflow: hidden;
         border-top-left-radius: 28rpx;
         border-top-right-radius: 28rpx;
         &__map {
            width: 100%;
            height: 100%;
         }
      }
      .qrcode {
         padding: 36rpx 30rpx;
         box-sizing: border-box;
         .qrcode-title {
            width: 100%;
            display: flex;
            align-items: center;
            justify-content: space-between;
            image {
               width: 28rpx;
               height: 28rpx;
            }
            text {
               font-weight: 600;
               font-size: 32rpx;
               color: #111111;
            }
         }
         .qrcode-image {
            width: 100%;
            display: flex;
            align-items: center;
            justify-content: center;
            margin-top: 60rpx;
            image {
               width: 400rpx;
               height: 400rpx;
            }
         }
         .qrcode-btn {
            width: 100%;
            height: 88rpx;
            line-height: 88rpx;
            text-align: center;
            background: #106EFA;
            border-radius: 50rpx;
            font-weight: bold;
            font-size: 32rpx;
            color: #FFFFFF;
            margin-top: 68rpx;
            &--hover {
               opacity: 0.92;
               transform: translateY(2rpx);
            }
         }
      }
      .photo-deliver {
         padding: 32rpx 28rpx calc(env(safe-area-inset-bottom) + 28rpx);
         background: #ffffff;
         box-sizing: border-box;
         border-top-left-radius: 20rpx;
         border-top-right-radius: 20rpx;
         overflow: hidden;
         &__header {
            display: flex;
            align-items: center;
            justify-content: space-between;
         }
         &__title {
            font-size: 34rpx;
            font-weight: 700;
            color: #111111;
         }
         &__close,
         &__close-placeholder {
            width: 36rpx;
            height: 36rpx;
            flex-shrink: 0;
         }
         &__close-placeholder {
            opacity: 0;
         }
         &__section {
            margin-top: 56rpx;
            &--remark {
               margin-top: 46rpx;
            }
         }
         &__label-row {
            display: flex;
            align-items: center;
            flex-wrap: wrap;
         }
         &__label,
         &__remark-title {
            font-size: 28rpx;
            font-weight: 700;
            color: #23262d;
         }
         &__required {
            margin-left: 4rpx;
            font-size: 28rpx;
            font-weight: 700;
            color: #ff3b30;
         }
         &__hint {
            margin-left: 12rpx;
            font-size: 24rpx;
            color: #a8adb7;
         }
         &__photos {
            display: flex;
            gap: 18rpx;
            margin-top: 30rpx;
         }
         &__upload-card,
         &__preview-card {
            position: relative;
            width: 160rpx;
            height: 160rpx;
            border-radius: 8rpx;
            overflow: hidden;
         }
         &__upload-card {
            display: flex;
            flex-direction: column;
            align-items: center;
            justify-content: center;
            border: 2rpx dashed #c9ced6;
            background: #ffffff;
            box-sizing: border-box;
         }
         &__upload-icon {
            width: 52rpx;
            height: 52rpx;
         }
         &__upload-text {
            margin-top: 14rpx;
            font-size: 26rpx;
            color: #9da3ae;
         }
         &__preview-card {
            background: #eef1f5;
         }
         &__preview-image {
            width: 100%;
            height: 100%;
         }
         &__preview-mask {
            position: absolute;
            left: 0;
            right: 0;
            bottom: 0;
            display: flex;
            justify-content: center;
            align-items: center;
            height: 48rpx;
            background: rgba(0, 0, 0, 0.46);
         }
         &__preview-delete {
            font-size: 26rpx;
            color: #ffffff;
         }
         &__textarea {
            width: 100%;
            height: 110rpx;
            margin-top: 24rpx;
            padding: 28rpx 24rpx;
            border-radius: 12rpx;
            background: #f7f8fa;
            box-sizing: border-box;
            font-size: 30rpx;
            color: #2c3139;
         }
         &__submit {
            width: 100%;
            height: 88rpx;
            line-height: 88rpx;
            margin-top: 86rpx;
            border-radius: 50rpx;
            background: #106efa;
            font-size: 32rpx;
            font-weight: 700;
            color: #ffffff;
            border: 0;
            padding: 0;
            &::after {
               border: 0;
            }
            &--hover {
               opacity: 0.92;
            }
         }
      }
      .order-detail {
         display: flex;
         flex-direction: column;
         background: #ffffff;
         border-top-left-radius: 28rpx;
         border-top-right-radius: 28rpx;
         overflow: hidden;
         &__scroll {
            flex: 1;
            min-height: 0;
         }
         &__map-section {
            background: #ffffff;
         }
         &__map {
            position: relative;
            height: 330rpx;
            background: #eef5ff;
            overflow: hidden;
         }
         &__map-view {
            width: 100%;
            height: 100%;
         }
         &__map-placeholder {
            width: 100%;
            height: 100%;
         }
         &__map-bubble {
            position: absolute;
            left: 26rpx;
            bottom: 44rpx;
            padding: 12rpx 18rpx;
            border-radius: 12rpx;
            background: rgba(255, 255, 255, 0.96);
            box-shadow: 0 10rpx 20rpx rgba(33, 92, 182, 0.08);
            font-size: 26rpx;
            font-weight: 500;
            color: #2f6ff2;
         }
         &__status-bar {
            display: flex;
            justify-content: space-between;
            align-items: center;
            padding: 0 24rpx;
            height: 92rpx;
            background: #d9e8ff;
         }
         &__status-left,
         &__status-right {
            display: flex;
            align-items: center;
         }
         &__status-icon {
            width: 28rpx;
            height: 28rpx;
            margin-right: 12rpx;
            border-radius: 6rpx;
            background: #7ea7ef;
         }
         &__status-name,
         &__status-no {
            font-size: 34rpx;
            font-weight: 700;
            color: #2b3139;
         }
         &__cancel-tag {
            padding: 8rpx 16rpx;
            margin-right: 18rpx;
            border: 2rpx solid #6ea6ff;
            border-radius: 999rpx;
            font-size: 24rpx;
            color: #1d73ff;
            background: rgba(255, 255, 255, 0.72);
         }
         &__content {
            padding: 28rpx 24rpx 36rpx;
         }
         &__head {
            display: flex;
            justify-content: space-between;
            align-items: flex-start;
         }
         &__time-wrap {
            display: flex;
            align-items: center;
         }
         &__time {
            font-size: 42rpx;
            font-weight: 700;
            color: #ff8d27;
         }
         &__time-sub {
            margin-left: 8rpx;
            font-size: 28rpx;
            color: #a4a9b2;
         }
         &__price-wrap {
            display: flex;
            flex-direction: column;
            align-items: flex-end;
         }
         &__price {
            font-size: 44rpx;
            font-weight: 700;
            color: #ff3b30;
         }
         &__extra {
            margin-top: 6rpx;
            font-size: 24rpx;
            color: #a0a5af;
         }
         &__tags {
            display: flex;
            align-items: center;
            flex-wrap: wrap;
            gap: 10rpx;
            margin-top: 14rpx;
         }
         &__tag-wrap {
            display: flex;
            align-items: center;
         }
         &__tag-icon {
            width: 108rpx;
            height: 40rpx;
         }
         &__tag {
            padding: 4rpx 10rpx;
            border-radius: 8rpx;
            font-size: 22rpx;
            line-height: 1.2;
            border: 1rpx solid #2473f5;
            color: #2473f5;
            &--orange {
               border-color: #ff9c45;
               background: #ff9c45;
               color: #ffffff;
            }
         }
         &__route {
            display: flex;
            margin-top: 20rpx;
            padding-bottom: 26rpx;
            border-bottom: 1rpx solid #edf0f3;
         }
         &__route-side {
            width: 64rpx;
            display: flex;
            flex-direction: column;
            align-items: center;
            flex-shrink: 0;
         }
         &__distance-top,
         &__distance-bottom {
            font-size: 22rpx;
            font-weight: 600;
            color: #555b66;
            text-align: center;
         }
         &__line {
            position: relative;
            width: 4rpx;
            flex: 1;
            min-height: 92rpx;
            margin: 10rpx 0;
            border-radius: 999rpx;
            background: #d8dbe1;
            &::before,
            &::after {
               content: '';
               position: absolute;
               left: 50%;
               transform: translateX(-50%);
               width: 14rpx;
               height: 14rpx;
               border-radius: 50%;
               background: #6a6f79;
            }
            &::before {
               top: -4rpx;
            }
            &::after {
               bottom: -4rpx;
            }
         }
         &__route-main {
            flex: 1;
         }
         &__route-item {
            display: flex;
            justify-content: space-between;
            align-items: flex-start;
            gap: 18rpx;
            &--destination {
               margin-top: 24rpx;
            }
         }
         &__route-texts {
            flex: 1;
            min-width: 0;
         }
         &__route-title {
            display: block;
            font-size: 40rpx;
            font-weight: 700;
            color: #2d3139;
            line-height: 1.3;
         }
         &__route-desc {
            display: block;
            margin-top: 8rpx;
            font-size: 28rpx;
            color: #9ea4ae;
            line-height: 1.4;
         }
         &__route-actions {
            display: flex;
            align-items: center;
            gap: 12rpx;
            flex-shrink: 0;
         }
         &__route-icon,
         &__customer-icon {
            width: 48rpx;
            height: 48rpx;
            border: 2rpx dashed #c9ced6;
            border-radius: 8rpx;
            background: #f7f8fa;
         }
         &__cancel-icon {
            width: 40rpx;
            height: 40rpx;
         }
         &__qrcode-section {
            display: flex;
            flex-direction: column;
            align-items: center;
            padding: 34rpx 0 8rpx;
            border-bottom: 1rpx solid #edf0f3;
         }
         &__qrcode-box {
            width: 260rpx;
            height: 260rpx;
            padding: 16rpx;
            border: 2rpx solid #edf0f3;
            border-radius: 12rpx;
            box-sizing: border-box;
         }
         &__qrcode-image {
            width: 100%;
            height: 100%;
         }
         &__qrcode-value {
            margin-top: 18rpx;
            font-size: 44rpx;
            font-weight: 700;
            color: #31363f;
         }
         &__qrcode-label {
            margin-top: 8rpx;
            font-size: 26rpx;
            color: #b0b4bc;
         }
         &__section {
            padding: 28rpx 0;
            border-bottom: 1rpx solid #edf0f3;
            &--photos {
               border-bottom: 0;
            }
         }
         &__section-title {
            display: block;
            font-size: 32rpx;
            font-weight: 700;
            color: #2b3139;
         }
         &__customer {
            margin-top: 28rpx;
            display: flex;
            justify-content: space-between;
            align-items: center;
            gap: 20rpx;
         }
         &__customer-text {
            flex: 1;
            font-size: 32rpx;
            color: #41464f;
            line-height: 1.5;
         }
         &__goods-list {
            margin-top: 22rpx;
         }
         &__goods-item {
            display: flex;
            justify-content: space-between;
            align-items: center;
            padding: 10rpx 0;
         }
         &__goods-name {
            font-size: 32rpx;
            color: #41464f;
         }
         &__goods-count {
            font-size: 30rpx;
            color: #a1a6af;
         }
         &__goods-category {
            display: block;
            margin-top: 24rpx;
            font-size: 32rpx;
            color: #41464f;
         }
         &__photos {
            display: flex;
            gap: 16rpx;
            margin-top: 22rpx;
         }
         &__photo-item {
            width: 120rpx;
            height: 120rpx;
            border-radius: 12rpx;
            overflow: hidden;
            background: #f3f5f8;
         }
         &__photo {
            width: 100%;
            height: 100%;
         }
         &__footer {
            display: flex;
            align-items: center;
            gap: 18rpx;
            padding: 18rpx 24rpx calc(env(safe-area-inset-bottom) + 18rpx);
            background: #ffffff;
            box-shadow: 0 -8rpx 24rpx rgba(16, 27, 49, 0.04);
         }
         &__cancel {
            width: 176rpx;
            height: 100rpx;
            border-radius: 50rpx;
            background: #E5E5E5;
            display: flex;
            justify-content: center;
            align-items: center;
            flex-shrink: 0;
         }
         &__confirm {
            flex: 1;
            display: flex;
            justify-content: center;
            align-items: center;
            gap: 14rpx;
            height: 100rpx;
            line-height: 100rpx;
            border-radius: 50rpx;
            background: #106EFA;
            font-size: 36rpx;
            font-weight: 600;
            color: #ffffff;
            border: 0;
            padding: 0;
            &::after {
               border: 0;
            }
            &--hover {
               opacity: 0.92;
            }
         }
         &__confirm-icon {
            width: 44rpx;
            height: 44rpx;
            flex-shrink: 0;
         }
      }
      &__header {
         position: fixed;
         left: 0;
         top: 0;
         right: 0;
         z-index: 10;
         background: linear-gradient(180deg, #2473f5 0%, #1e6fef 100%);
         box-shadow: 0 12rpx 24rpx rgba(36, 115, 245, 0.08);
      }
      &__user-row {
         display: flex;
         justify-content: space-between;
         align-items: center;
         padding: 20rpx 24rpx 0;
      }
      &__user {
         display: flex;
         align-items: center;
         gap: 14rpx;
      }
      &__avatar {
         width: 42rpx;
         height: 42rpx;
         border-radius: 50%;
         border: 2rpx solid rgba(255, 255, 255, 0.7);
      }
      &__name {
         font-size: 28rpx;
         font-weight: 500;
         color: #ffffff;
      }
      &__status {
         display: flex;
         align-items: center;
         padding: 10rpx 16rpx;
         border-radius: 999rpx;
         background: rgba(255, 255, 255, 0.16);
         backdrop-filter: blur(10rpx);
      }
      &__status-dot {
         width: 14rpx;
         height: 14rpx;
         border-radius: 50%;
         background: #32d74b;
         margin-right: 10rpx;
      }
      &__status-text,
      &__status-arrow {
         font-size: 24rpx;
         color: #ffffff;
      }
      &__status-arrow {
         font-size: 18rpx;
         margin-left: 8rpx;
      }
      &__stats {
         display: flex;
         justify-content: space-between;
         padding: 34rpx 36rpx 28rpx;
      }
      &__stat-item {
         display: flex;
         flex-direction: column;
         align-items: center;
         min-width: 160rpx;
      }
      &__stat-value {
         font-size: 52rpx;
         line-height: 1;
         font-weight: 700;
         color: #ffffff;
      }
      &__stat-label {
         margin-top: 12rpx;
         font-size: 26rpx;
         color: rgba(255, 255, 255, 0.86);
      }
      &__tabs {
         display: flex;
         align-items: center;
         height: 88rpx;
         padding: 0 18rpx;
         background: #ffffff;
      }
      &__tab {
         position: relative;
         display: flex;
         align-items: center;
         justify-content: center;
         height: 100%;
         padding: 0 18rpx;
         font-size: 32rpx;
         color: #8b9099;
      }
      &__tab--active {
         color: #242933;
         font-weight: 700;
      }
      &__tab-text {
         font-size: inherit;
         color: inherit;
      }
      &__tab-count {
         margin-left: 6rpx;
         font-size: 28rpx;
         color: #8b9099;
      }
      &__tab-line {
         position: absolute;
         left: 18rpx;
         right: 18rpx;
         bottom: 0;
         height: 5rpx;
         border-radius: 999rpx;
         background: #2473f5;
      }
      &__filter {
         margin-left: auto;
         display: flex;
         align-items: center;
         gap: 6rpx;
         padding-right: 10rpx;
      }
      &__filter-text {
         font-size: 28rpx;
         color: #9aa1ab;
         &--active {
            color: #106efa;
         }
      }
      &__filter-icon {
         width: 28rpx;
         height: 28rpx;
      }
      &__body {
         box-sizing: border-box;
         background-color: #F6F9FF;
      }
      &__list {
         padding: 22rpx 30rpx calc(22rpx + env(safe-area-inset-bottom)) 30rpx;
      }
      &__empty {
         display: flex;
         justify-content: center;
         align-items: center;
         height: 100%;
         padding-bottom: env(safe-area-inset-bottom);
         box-sizing: border-box;
      }
      &__empty-icon {
         width: 320rpx;
         height: 320rpx;
      }
   }
   .filter-popup {
      position: fixed;
      left: 0;
      right: 0;
      z-index: 20;
      background: rgba(0, 0, 0, 0.24);
      &__panel {
         height: 100%;
         padding: 22rpx 0 26rpx;
         background: #ffffff;
         border-bottom-left-radius: 28rpx;
         border-bottom-right-radius: 28rpx;
         box-sizing: border-box;
         overflow-y: auto;
      }
      &__section {
         padding: 0 18rpx;
         margin-bottom: 28rpx;
      }
      &__title {
         display: block;
         margin-bottom: 20rpx;
         font-size: 28rpx;
         font-weight: 700;
         color: #252b33;
      }
      &__options {
         display: flex;
         flex-wrap: wrap;
         gap: 18rpx 20rpx;
      }
      &__option {
         display: flex;
         justify-content: center;
         align-items: center;
         width: 226rpx;
         height: 74rpx;
         border-radius: 10rpx;
         background: #f5f5f5;
         border: 2rpx solid transparent;
         box-sizing: border-box;
         &--active {
            background: #edf5ff;
            border-color: #3d8cff;
         }
      }
      &__option-text {
         font-size: 28rpx;
         color: #4b515a;
         .filter-popup__option--active & {
            font-weight: 600;
            color: #2678ff;
         }
      }
      &__actions {
         display: flex;
         gap: 24rpx;
         padding: 8rpx 18rpx 0;
      }
      &__button {
         flex: 1;
         height: 92rpx;
         line-height: 92rpx;
         border-radius: 999rpx;
         font-size: 34rpx;
         font-weight: 700;
         border: 0;
         padding: 0;
         &::after {
            border: 0;
         }
         &--reset {
            background: #ebebeb;
            color: #777d86;
         }
         &--confirm {
            background: linear-gradient(180deg, #2d82ff 0%, #206ef6 100%);
            color: #ffffff;
         }
         &--hover {
            opacity: 0.92;
         }
      }
   }
   .order-card {
      margin-bottom: 20rpx;
      padding: 20rpx;
      border-radius: 24rpx;
      background: #ffffff;
      box-shadow: 0 10rpx 24rpx rgba(26, 44, 81, 0.04);
      &__head {
         display: flex;
         justify-content: space-between;
         align-items: flex-start;
      }
      &__time-main {
         font-size: 42rpx;
         font-weight: 700;
         color: #ff8d27;
      }
      &__time-sub {
         margin-left: 8rpx;
         font-size: 28rpx;
         color: #a3a8b2;
      }
      &__price-wrap {
         display: flex;
         flex-direction: column;
         align-items: center;
         gap: 8rpx;
         &--serial-only {
            justify-content: flex-start;
            min-width: 54rpx;
         }
      }
      &__serial {
         font-size: 38rpx;
         font-weight: 700;
         line-height: 1;
         color: #2c3139;
      }
      &__price {
         font-size: 44rpx;
         font-weight: 700;
         color: #ff3b30;
      }
      &__meta {
         display: flex;
         justify-content: space-between;
         align-items: center;
         gap: 16rpx;
         margin-top: 12rpx;
      }
      &__extra {
         flex-shrink: 0;
         font-size: 24rpx;
         color: #a0a5af;
      }
      &__tags {
         display: flex;
         flex: 1;
         flex-wrap: wrap;
         gap: 10rpx;
      }
      &__tag-wrap {
         display: flex;
         align-items: center;
      }
      &__tag-icon {
         width: 108rpx;
         height: 40rpx;
      }
      &__tag {
         padding: 4rpx 10rpx;
         border-radius: 8rpx;
         font-size: 22rpx;
         line-height: 1.2;
         border: 1rpx solid #2473f5;
         color: #2473f5;
         &--orange {
            border-color: #ff9c45;
            background: #ff9c45;
            color: #ffffff;
         }
         &--red {
            border-color: #ff6c57;
            color: #ff6c57;
         }
         &--blue-light {
            border-color: #74a9ff;
            color: #74a9ff;
         }
      }
      &__route {
         display: flex;
         margin-top: 20rpx;
      }
      &__route-side {
         width: 64rpx;
         display: flex;
         flex-direction: column;
         align-items: center;
         flex-shrink: 0;
      }
      &__distance-top,
      &__distance-bottom {
         font-size: 22rpx;
         font-weight: 600;
         color: #555b66;
         text-align: center;
      }
      &__line {
         position: relative;
         width: 4rpx;
         flex: 1;
         min-height: 86rpx;
         margin: 10rpx 0;
         border-radius: 999rpx;
         background: #d8dbe1;
         &::before,
         &::after {
            content: '';
            position: absolute;
            left: 50%;
            transform: translateX(-50%);
            width: 14rpx;
            height: 14rpx;
            border-radius: 50%;
            background: #6a6f79;
         }
         &::before {
            top: -4rpx;
         }
         &::after {
            bottom: -4rpx;
         }
      }
      &__route-main {
         flex: 1;
      }
      &__route-item {
         display: flex;
         justify-content: space-between;
         align-items: flex-start;
         gap: 16rpx;
         &--destination {
            margin-top: 20rpx;
         }
      }
      &__route-texts {
         flex: 1;
         min-width: 0;
      }
      &__route-title {
         display: block;
         font-size: 40rpx;
         font-weight: 700;
         color: #2d3139;
         line-height: 1.3;
      }
      &__route-desc {
         display: block;
         margin-top: 8rpx;
         font-size: 28rpx;
         color: #9ea4ae;
         line-height: 1.4;
      }
      &__nav {
         width: 48rpx;
         height: 48rpx;
         flex-shrink: 0;
      }
      &__goods {
         display: flex;
         justify-content: space-between;
         align-items: center;
         height: 70rpx;
         padding: 0 20rpx;
         margin-top: 20rpx;
         border-radius: 16rpx;
         background: #f4f5f7;
      }
      &__goods-text {
         flex: 1;
         font-size: 28rpx;
         color: #7a818d;
         white-space: nowrap;
         overflow: hidden;
         text-overflow: ellipsis;
      }
      &__goods-arrow {
         margin-left: 12rpx;
         font-size: 24rpx;
         color: #a4a9b1;
      }
      &__button {
         margin-top: 24rpx;
         width: 100%;
         height: 88rpx;
         line-height: 88rpx;
         border-radius: 999rpx;
         background: linear-gradient(180deg, #2b7fff 0%, #1f6ff3 100%);
         font-size: 34rpx;
         font-weight: 700;
         color: #ffffff;
         border: 0;
         padding: 0;
         &::after {
            border: 0;
         }
         &--hover {
            opacity: 0.92;
         }
      }
      &__actions {
         margin-top: 24rpx;
         &--pickup,
         &--delivering {
            display: flex;
            align-items: center;
            gap: 22rpx;
         }
      }
      &__icon-actions {
         display: flex;
         align-items: center;
         gap: 20rpx;
         flex-shrink: 0;
         &--single {
            gap: 0;
         }
      }
      &__icon-action {
         display: flex;
         flex-direction: column;
         align-items: center;
         justify-content: center;
         width: 74rpx;
      }
      &__action-icon {
         width: 40rpx;
         height: 40rpx;
         border-radius: 8rpx;
         background: #f7f8fa;
      }
      &__action-text {
         margin-top: 8rpx;
         font-size: 26rpx;
         line-height: 1;
         color: #5b616b;
      }
      &__button--code {
         flex: 1;
         margin-top: 0;
      }
   }
</style>