<template>
|
<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>
|
|
<script>
|
export default {
|
data() {
|
return {
|
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'
|
}
|
}
|
},
|
|
onReady() {
|
this.initTTS() // 页面渲染完成初始化语音
|
},
|
|
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('仅支持安卓')
|
return
|
}
|
|
try {
|
// 导入安卓原生类
|
const TextToSpeech = plus.android.importClass('android.speech.tts.TextToSpeech')
|
const Locale = plus.android.importClass('java.util.Locale')
|
|
// 创建TTS
|
this.tts = new TextToSpeech(plus.android.runtimeMainActivity(), {
|
onInit: (status) => {
|
if (status == 0) {
|
// 设置中文
|
this.tts.setLanguage(Locale.CHINA)
|
console.log('语音初始化成功')
|
}
|
}
|
})
|
} catch (e) {
|
console.log('初始化失败', e)
|
}
|
},
|
|
speak(text) {
|
if (!this.tts) {
|
uni.showToast({
|
title: '语音未准备好',
|
icon: 'none'
|
})
|
return
|
}
|
|
try {
|
// 安卓原生播报(QUEUE_FLUSH = 立即播报,打断上一条)
|
this.tts.speak(text, 0, null)
|
} catch (err) {
|
console.log('播报失败', err)
|
}
|
},
|
|
stopSpeak() {
|
if (this.tts) this.tts.stop()
|
}
|
},
|
|
onUnload() {
|
if (this.tts) {
|
this.tts.stop()
|
this.tts.shutdown()
|
}
|
}
|
}
|
</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>
|