| | |
| | | <template> |
| | | <view class="order-detail-page"> |
| | | <view class="order-detail-page" v-if="orderDetail"> |
| | | <view v-if="!showMapStatus" class="order-detail-page__simple-nav" :style="{ paddingTop: statusBarHeight + 'px' }"> |
| | | <view class="order-detail-page__simple-nav-inner"> |
| | | <text class="order-detail-page__simple-nav-title">{{ statusTextMap[orderDetail.status] || '订单详情' }}</text> |
| | | <u-icon name="arrow-left" color="#ffffff" size="20" @click="handleBack"></u-icon> |
| | | <text class="order-detail-page__simple-nav-title">订单详情</text> |
| | | <u-icon name="arrow-left" color="#106EFA" size="20"></u-icon> |
| | | </view> |
| | | </view> |
| | | |
| | | <view v-if="showMapStatus" class="order-detail-page__fixed-top"> |
| | | <view class="order-detail-page__map-wrap"> |
| | | <map |
| | | id="orderDetailMap" |
| | | class="order-detail-page__map" |
| | | :latitude="mapData.center.latitude" |
| | | :longitude="mapData.center.longitude" |
| | |
| | | :enable-scroll="true" |
| | | ></map> |
| | | <view class="order-detail-page__map-bubble"> |
| | | <text class="order-detail-page__map-bubble-text">剩余3.2km,约4分钟</text> |
| | | <text class="order-detail-page__map-bubble-text">{{ distance > 0 ? `剩余${(distance / 1000).toFixed(1)}km,约${Math.ceil(duration / 60)}分钟` : '正在获取位置信息...' }}</text> |
| | | </view> |
| | | </view> |
| | | |
| | |
| | | <text class="order-detail-page__status-title">{{ statusTextMap[orderDetail.status] || '待取货' }}</text> |
| | | </view> |
| | | <view class="order-detail-page__status-right"> |
| | | <text v-if="orderDetail.status === 3" class="order-detail-page__status-cancel">取消订单</text> |
| | | <text class="order-detail-page__status-no">#{{ orderIndex }}</text> |
| | | <text v-if="orderDetail.status === 3" class="order-detail-page__status-cancel" @click="handleCancelOrder">取消订单</text> |
| | | <text class="order-detail-page__status-no" v-if="orderIndex">#{{ orderIndex }}</text> |
| | | </view> |
| | | </view> |
| | | </view> |
| | |
| | | <view v-if="showMapStatus" class="order-detail-page__summary"> |
| | | <view class="order-detail-page__summary-left"> |
| | | <view class="order-detail-page__head-left"> |
| | | <text class="order-detail-page__time">{{ orderDetail.remainMinutes }}</text> |
| | | <text class="order-detail-page__time-sub">分钟</text> |
| | | <template v-if="formattedRemainTime"> |
| | | <text class="order-detail-page__time">{{ formattedRemainTime }}内</text> |
| | | <text class="order-detail-page__time-sub">送达</text> |
| | | </template> |
| | | <text class="order-detail-page__time" v-else>配送已超时,请尽快送达</text> |
| | | </view> |
| | | |
| | | <view class="order-detail-page__tags"> |
| | |
| | | </view> |
| | | |
| | | <view class="order-detail-page__summary-right"> |
| | | <text class="order-detail-page__price">¥{{ (orderDetail.driverFee / 100).toFixed(1) }}</text> |
| | | <text v-if="orderDetail.urgentAmount" class="order-detail-page__extra">含加急¥{{ orderDetail.urgentAmount / 100 }}</text> |
| | | <text class="order-detail-page__price">¥{{ orderDetail.platformRewardAmount ? (orderDetail.driverFee + orderDetail.platformRewardAmount) / 100 : (orderDetail.driverFee / 100).toFixed(2) }}</text> |
| | | <text v-if="orderDetail.platformRewardAmount" class="order-detail-page__extra">含加急¥{{ (orderDetail.platformRewardAmount / 100).toFixed(2) }}</text> |
| | | </view> |
| | | </view> |
| | | |
| | |
| | | <view class="order-detail-page__done-price-row"> |
| | | <text class="order-detail-page__price">¥{{ (orderDetail.driverFee / 100).toFixed(1) }}</text> |
| | | </view> |
| | | <text v-if="orderDetail.urgentAmount" class="order-detail-page__extra">含加急¥{{ orderDetail.urgentAmount / 100 }}</text> |
| | | <text v-if="orderDetail.isUrgent === 1" class="order-detail-page__extra">含加急¥{{ orderDetail.urgentAmount / 100 }}</text> |
| | | </view> |
| | | </view> |
| | | |
| | | <view class="order-detail-page__route-list"> |
| | | <view class="order-detail-page__route-item"> |
| | | <view class="order-detail-page__route-left"> |
| | | <text class="order-detail-page__distance-top">{{ orderDetail.takeDistance }}</text> |
| | | <text class="order-detail-page__distance-unit">m</text> |
| | | <view class="order-detail-page__route-badge order-detail-page__route-badge--take">取</view> |
| | | <view class="order-detail-page__route-divider"></view> |
| | | </view> |
| | | <view class="order-detail-page__route-main"> |
| | | <view class="order-detail-page__route-texts"> |
| | | <text class="order-detail-page__route-title">{{ orderDetail.takeName }}</text> |
| | | <text class="order-detail-page__route-desc">{{ orderDetail.depositShopAddress }}</text> |
| | | </view> |
| | | <view class="order-detail-page__route-actions"> |
| | | <image class="order-detail-page__route-icon" src="/static/image/ic_c1all@2x.png" mode="aspectFit"></image> |
| | | <image class="order-detail-page__route-icon" src="/static/image/ic_daohang@2x.png" mode="aspectFit"></image> |
| | | </view> |
| | | </view> |
| | | </view> |
| | | |
| | | <view class="order-detail-page__route-item order-detail-page__route-item--end"> |
| | | <view class="order-detail-page__route-left"> |
| | | <view class="order-detail-page__route-pin"></view> |
| | | <view class="order-detail-page__route-divider order-detail-page__route-divider--light"></view> |
| | | <text class="order-detail-page__distance-top">{{ orderDetail.depositDistance }}</text> |
| | | <text class="order-detail-page__distance-unit"></text> |
| | | </view> |
| | | <view class="order-detail-page__route-main"> |
| | | <view class="order-detail-page__route-texts"> |
| | | <text class="order-detail-page__route-title">{{ orderDetail.depositShopName }}</text> |
| | | <text class="order-detail-page__route-desc">{{ orderDetail.depositShopAddress }}</text> |
| | | </view> |
| | | <view class="order-detail-page__route-actions"> |
| | | <image class="order-detail-page__route-icon" src="/static/image/ic_c1all@2x.png" mode="aspectFit"></image> |
| | | <image class="order-detail-page__route-icon" src="/static/image/ic_daohang@2x.png" mode="aspectFit"></image> |
| | | <view class="order-detail-page__route-actions" v-if="![7,99].includes(orderDetail.status)"> |
| | | <image class="order-detail-page__route-icon" src="/static/image/ic_c1all@2x.png" mode="aspectFit" v-if="[3,4,5].includes(orderDetail.status) && orderDetail.depositShopPhone" @click="makeShopCall('deposit')"></image> |
| | | <image class="order-detail-page__route-icon" src="/static/image/ic_daohang@2x.png" mode="aspectFit" @click="navigateToAddress('deposit')"></image> |
| | | </view> |
| | | </view> |
| | | </view> |
| | | |
| | | <view class="order-detail-page__route-item order-detail-page__route-item--end" style="padding-bottom: 30rpx; box-sizing: border-box; border-bottom: 1px solid #E5E5E5;"> |
| | | <view class="order-detail-page__route-left"> |
| | | <view class="order-detail-page__route-badge order-detail-page__route-badge--send">送</view> |
| | | </view> |
| | | <view class="order-detail-page__route-main"> |
| | | <view class="order-detail-page__route-texts"> |
| | | <text class="order-detail-page__route-title">{{ orderDetail.takeName }}</text> |
| | | <text class="order-detail-page__route-desc">{{ orderDetail.takeAddress }}</text> |
| | | </view> |
| | | <view class="order-detail-page__route-actions" v-if="![7,99].includes(orderDetail.status)"> |
| | | <image class="order-detail-page__route-icon" src="/static/image/ic_c1all@2x.png" mode="aspectFit" v-if="[4,5].includes(orderDetail.status) && orderDetail.takeContactPhone" @click="makeShopCall('take')"></image> |
| | | <image class="order-detail-page__route-icon" src="/static/image/ic_daohang@2x.png" mode="aspectFit" @click="navigateToAddress('take')"></image> |
| | | </view> |
| | | </view> |
| | | </view> |
| | |
| | | </view> |
| | | </view> |
| | | |
| | | <view class="order-detail-page__section"> |
| | | <text class="order-detail-page__section-title">客户信息</text> |
| | | <view class="order-detail-page__row-info"> |
| | | <text class="order-detail-page__row-text">{{ orderDetail.contactPhone }}</text> |
| | | <image class="order-detail-page__row-icon" src="/static/image/ic_call@2x.png" mode="aspectFit" @click="makePhoneCall"></image> |
| | | </view> |
| | | <view v-if="orderDetail.status === 7" class="order-detail-page__comment-card"> |
| | | <text class="order-detail-page__comment-title">客户已评价:</text> |
| | | <view class="order-detail-page__comment-score"> |
| | | <text class="order-detail-page__comment-star">★</text> |
| | | <text class="order-detail-page__comment-score-text">4.5</text> |
| | | <view class="order-detail-page__section" style="margin-top: 30rpx; padding: 0 30rpx; box-sizing: border-box;"> |
| | | <view style="width: 100%; padding-bottom: 30rpx; box-sizing: border-box; border-bottom: 1px solid #E5E5E5;"> |
| | | <text class="order-detail-page__section-title">客户信息</text> |
| | | <view class="order-detail-page__row-info"> |
| | | <text class="order-detail-page__row-text">{{ orderDetail.customerInfo || '' }}</text> |
| | | <!-- <image class="order-detail-page__row-icon" v-if="[3,4].includes(orderDetail.status)" src="/static/image/ic_call@2x.png" mode="aspectFit" @click="makePhoneCall"></image> --> |
| | | </view> |
| | | <text class="order-detail-page__comment-content">送的很快,东西完好无损</text> |
| | | <image class="order-detail-page__comment-image" src="/static/logo.png" mode="aspectFill"></image> |
| | | <view v-if="orderDetail.commentStatus === 1" class="order-detail-page__comment-card"> |
| | | <text class="order-detail-page__comment-title">客户已评价:</text> |
| | | <view class="order-detail-page__comment-score"> |
| | | <text class="order-detail-page__comment-star">★</text> |
| | | <text class="order-detail-page__comment-score-text">4.5</text> |
| | | </view> |
| | | <text class="order-detail-page__comment-content">{{ orderDetail.commentContent || '' }}</text> |
| | | <image class="order-detail-page__comment-image" src="/static/logo.png" mode="aspectFill"></image> |
| | | </view> |
| | | </view> |
| | | </view> |
| | | |
| | | <view class="order-detail-page__section"> |
| | | <view class="order-detail-page__section" style="margin-top: 30rpx; padding: 0 30rpx; box-sizing: border-box;"> |
| | | <text class="order-detail-page__section-title">物品清单(共{{ goodsList.length }}件)</text> |
| | | <view class="order-detail-page__goods-list"> |
| | | <view class="order-detail-page__goods-list" style="width: 100%; padding-bottom: 30rpx; box-sizing: border-box; border-bottom: 1px solid #E5E5E5;"> |
| | | <view v-for="item in goodsList" :key="item.name" class="order-detail-page__goods-item"> |
| | | <text class="order-detail-page__goods-name" :style="item.isOversized === 1 ? 'color: #FF0020;' : ''">{{ item.name }}</text> |
| | | <text class="order-detail-page__goods-count">x{{ item.quantity }}</text> |
| | |
| | | </view> |
| | | </view> |
| | | |
| | | <view class="order-detail-page__section"> |
| | | <view class="order-detail-page__section" style="margin-top: 30rpx; padding: 0 30rpx; box-sizing: border-box;"> |
| | | <text class="order-detail-page__section-title">物品信息</text> |
| | | <text class="order-detail-page__goods-category">文件</text> |
| | | <text class="order-detail-page__goods-category">{{ orderDetail.goodTypeName || '' }}</text> |
| | | <view class="order-detail-page__photos"> |
| | | <image v-for="(item, index) in photos" :key="index" class="order-detail-page__photo" :src="item" mode="aspectFill"></image> |
| | | <view class="order-detail-page__photo" v-for="(item, index) in photos" :key="index"> |
| | | <image :src="item" mode="heightFix" @click="previewImage(item)"></image> |
| | | </view> |
| | | </view> |
| | | </view> |
| | | |
| | | <view class="order-detail-page__section order-detail-page__section--last"> |
| | | <view style="width: 100%; height: 30rpx; background-color: #f9f9f9;"></view> |
| | | |
| | | <view class="order-detail-page__section order-detail-page__section--last" style="margin-top: 30rpx; padding: 0 30rpx; box-sizing: border-box;"> |
| | | <text class="order-detail-page__section-title">订单信息</text> |
| | | <view class="order-detail-page__detail-list"> |
| | | <view class="order-detail-page__detail-item"> |
| | | <text class="order-detail-page__detail-label">订单编号:</text> |
| | | <text class="order-detail-page__detail-value">{{ orderDetail.code }}</text> |
| | | <text class="order-detail-page__detail-value">{{ orderDetail.code || '-' }}</text> |
| | | </view> |
| | | <view v-if="orderDetail.createTime" class="order-detail-page__detail-item"> |
| | | <text class="order-detail-page__detail-label">下单时间:</text> |
| | | <text class="order-detail-page__detail-value">{{ orderDetail.createTime }}</text> |
| | | <text class="order-detail-page__detail-value">{{ orderDetail.createTime || '-' }}</text> |
| | | </view> |
| | | <view v-if="orderDetail.acceptTime" class="order-detail-page__detail-item"> |
| | | <view v-if="[3,4,5,6,7,99].includes(orderDetail.status)" class="order-detail-page__detail-item"> |
| | | <text class="order-detail-page__detail-label">接单时间:</text> |
| | | <text class="order-detail-page__detail-value">{{ orderDetail.acceptTime }}</text> |
| | | <text class="order-detail-page__detail-value">{{ orderDetail.acceptTime || '-' }}</text> |
| | | </view> |
| | | <view class="order-detail-page__detail-item"> |
| | | <view v-if="[3,4,5,6,7,99].includes(orderDetail.status)" class="order-detail-page__detail-item"> |
| | | <text class="order-detail-page__detail-label">订单备注:</text> |
| | | <text class="order-detail-page__detail-value">{{ orderDetail.remark || '-' }}</text> |
| | | </view> |
| | | <view v-if="[4,5,6,7,99].includes(orderDetail.status)" class="order-detail-page__detail-item"> |
| | | <text class="order-detail-page__detail-label">取货时间:</text> |
| | | <text class="order-detail-page__detail-value">{{ orderDetail.driverTakeTime || '-' }}</text> |
| | | </view> |
| | | <view v-if="[5,6,7,99].includes(orderDetail.status)" class="order-detail-page__detail-item"> |
| | | <text class="order-detail-page__detail-label">完成时间:</text> |
| | | <text class="order-detail-page__detail-value">{{ orderDetail.finishTime || '-' }}</text> |
| | | </view> |
| | | <view v-if="orderDetail.isEvaluated === 1" class="order-detail-page__detail-item"> |
| | | <text class="order-detail-page__detail-label">评价时间:</text> |
| | | <text class="order-detail-page__detail-value">{{ orderDetail.commentTime || '-' }}</text> |
| | | </view> |
| | | </view> |
| | | </view> |
| | |
| | | </scroll-view> |
| | | |
| | | <view v-if="footerButtons.length" class="order-detail-page__footer"> |
| | | <button |
| | | v-for="button in footerButtons" |
| | | :key="button.text" |
| | | class="order-detail-page__footer-btn" |
| | | :class="button.primary ? 'order-detail-page__footer-btn--primary' : 'order-detail-page__footer-btn--ghost'" |
| | | hover-class="order-detail-page__footer-btn--hover" |
| | | @click="handleFooterAction(button)" |
| | | > |
| | | {{ button.text }} |
| | | </button> |
| | | <view></view> |
| | | <view style="display: flex; align-items: center; gap: 20rpx;"> |
| | | <button |
| | | v-for="button in footerButtons" |
| | | :key="button.text" |
| | | class="order-detail-page__footer-btn" |
| | | :class="button.primary ? 'order-detail-page__footer-btn--primary' : 'order-detail-page__footer-btn--ghost'" |
| | | hover-class="order-detail-page__footer-btn--hover" |
| | | @click="handleFooterAction(button)" |
| | | > |
| | | {{ button.text }} |
| | | </button> |
| | | </view> |
| | | |
| | | </view> |
| | | |
| | | <u-modal |
| | | :show="showCancelModal" |
| | | showCancelButton |
| | | @cancel="showCancelModal = false" |
| | | cancelColor="#666666" |
| | | confirmColor="#0055FF" |
| | | title="取消订单确认" |
| | | @confirm="confirmCancelOrder"> |
| | | <view style="text-align: center;color: #333333;font-size: 28rpx;font-weight: 400;"> |
| | | 您今日还可取消 {{ cancelRemain }} 次订单,次数用尽后今日将无法接单,是否确认取消? |
| | | </view> |
| | | </u-modal> |
| | | |
| | | <u-modal |
| | | :show="showGrabModal" |
| | | showCancelButton |
| | | @cancel="showGrabModal = false" |
| | | cancelColor="#666666" |
| | | confirmColor="#0055FF" |
| | | title="温馨提示" |
| | | @confirm="confirmGrabOrder"> |
| | | <view style="text-align: center;color: #333333;font-size: 28rpx;font-weight: 400;"> |
| | | {{ orderDetail && orderDetail.hasOversized === 1 ? '本订单有特大件尺寸行李,请确认是否继续抢单?' : '是否确认接单?' }} |
| | | </view> |
| | | </u-modal> |
| | | |
| | | <u-popup :show="showPhotoPopup" round="20" mode="bottom" @close="closePhotoPopup"> |
| | | <view class="photo-popup"> |
| | |
| | | </template> |
| | | |
| | | <script> |
| | | import image from 'uview-ui/libs/config/props/image'; |
| | | |
| | | export default { |
| | | data() { |
| | | return { |
| | | orderId: null, |
| | | orderIndex: null, |
| | | orderDetail: {}, |
| | | orderDetail: null, |
| | | statusBarHeight: 0, |
| | | topFixedHeight: 0, |
| | | showPhotoPopup: false, |
| | | photoPopupMode: '', |
| | | photoRemark: '', |
| | | uploadedPhotos: [], |
| | | showCancelModal: false, |
| | | cancelRemain: 0, |
| | | showGrabModal: false, |
| | | currentLocation: null, |
| | | routePoints: [], |
| | | distance: 0, |
| | | duration: 0, |
| | | statusTextMap: { |
| | | 2: '待接单', |
| | | 3: '待取货', |
| | | 4: '配送中', |
| | | 5: '已送达', |
| | | 7: '已完成', |
| | | 99: '已取消' |
| | | }, |
| | |
| | | } |
| | | }, |
| | | computed: { |
| | | formattedRemainTime() { |
| | | const minutes = this.orderDetail.remainMinutes |
| | | if (!minutes) return null |
| | | if (minutes >= 60) { |
| | | const hours = Math.floor(minutes / 60) |
| | | const mins = minutes % 60 |
| | | return mins > 0 ? `${hours}小时${mins}分钟` : `${hours}小时` |
| | | } |
| | | return `${minutes}分钟` |
| | | }, |
| | | showMapStatus() { |
| | | return this.orderDetail.status === 2 || this.orderDetail.status === 3 || this.orderDetail.status === 4 |
| | | return this.orderDetail.status === 3 || this.orderDetail.status === 4 |
| | | }, |
| | | mapData() { |
| | | const startPoint = { latitude: 31.829512, longitude: 117.239211 } |
| | | const endPoint = { latitude: 31.841268, longitude: 117.278695 } |
| | | const routePoints = [ |
| | | const startPoint = this.currentLocation || { latitude: 31.829512, longitude: 117.239211 } |
| | | const hasEndPoint = this.orderDetail.navigateLat && this.orderDetail.navigateLng |
| | | const endPoint = hasEndPoint |
| | | ? { latitude: this.orderDetail.navigateLng, longitude: this.orderDetail.navigateLat } |
| | | : { latitude: 31.841268, longitude: 117.278695 } |
| | | |
| | | let center |
| | | let scale = 12 |
| | | if (this.currentLocation && hasEndPoint) { |
| | | const latSpan = Math.abs(this.currentLocation.latitude - endPoint.latitude) |
| | | const lngSpan = Math.abs(this.currentLocation.longitude - endPoint.longitude) |
| | | const maxSpan = Math.max(latSpan, lngSpan) |
| | | center = { |
| | | latitude: (this.currentLocation.latitude + endPoint.latitude) / 2, |
| | | longitude: (this.currentLocation.longitude + endPoint.longitude) / 2 |
| | | } |
| | | if (maxSpan > 0.3) { |
| | | scale = 9 |
| | | } else if (maxSpan > 0.15) { |
| | | scale = 10 |
| | | } else if (maxSpan > 0.08) { |
| | | scale = 11 |
| | | } else if (maxSpan > 0.04) { |
| | | scale = 12 |
| | | } else if (maxSpan > 0.02) { |
| | | scale = 13 |
| | | } else if (maxSpan > 0.01) { |
| | | scale = 14 |
| | | } else if (maxSpan > 0.005) { |
| | | scale = 15 |
| | | } else if (maxSpan > 0.002) { |
| | | scale = 16 |
| | | } else { |
| | | scale = 17 |
| | | } |
| | | } else if (this.currentLocation) { |
| | | center = this.currentLocation |
| | | } else { |
| | | center = { latitude: 31.83539, longitude: 117.258953 } |
| | | } |
| | | |
| | | const markers = [ |
| | | { id: 1, latitude: startPoint.latitude, longitude: startPoint.longitude, iconPath: '/static/image/start.png', width: 32, height: 38, anchor: { x: 0.5, y: 1 } }, |
| | | { id: 2, latitude: endPoint.latitude, longitude: endPoint.longitude, iconPath: '/static/image/end.png', width: 32, height: 38, anchor: { x: 0.5, y: 1 } }, |
| | | { id: 3, latitude: startPoint.latitude, longitude: startPoint.longitude, iconPath: '/static/image/dizhi.png', width: 12, height: 12, anchor: { x: 0.5, y: 0.5 } } |
| | | ] |
| | | |
| | | const routePoints = this.routePoints.length > 0 ? this.routePoints : [ |
| | | startPoint, |
| | | { latitude: 31.831624, longitude: 117.247836 }, |
| | | { latitude: 31.834918, longitude: 117.255467 }, |
| | |
| | | endPoint |
| | | ] |
| | | |
| | | return { |
| | | center: { latitude: 31.83539, longitude: 117.258953 }, |
| | | 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 } } |
| | | const result = { |
| | | center, |
| | | markers, |
| | | polyline: this.routePoints.length > 0 ? [ |
| | | { points: routePoints, color: '#05be76', width: 25, arrowLine: true, dottedLine: false } |
| | | ] : [ |
| | | { points: routePoints, color: '#05be76', width: 25, arrowLine: true, dottedLine: true } |
| | | ], |
| | | polyline: [ |
| | | { points: routePoints, color: '#00c67a', width: 20, arrowLine: true, dottedLine: true, borderColor: '#469972', borderWidth: 10 } |
| | | ], |
| | | includePoints: routePoints, |
| | | scale: 12 |
| | | includePoints: [startPoint, endPoint], |
| | | scale |
| | | } |
| | | return result |
| | | }, |
| | | bodyStyle() { |
| | | const footerHeight = uni.upx2px(116) |
| | | const simpleNavHeight = this.statusBarHeight + uni.upx2px(88) |
| | | return { |
| | | paddingTop: (this.showMapStatus ? this.topFixedHeight : simpleNavHeight) + 'px', |
| | | height: `calc(100vh - ${this.footerButtons.length ? footerHeight : 0}px)` |
| | | height: `calc(100vh - ${this.footerButtons.length ? footerHeight + 20 : 20}px)` |
| | | } |
| | | }, |
| | | footerButtons() { |
| | |
| | | const systemInfo = uni.getSystemInfoSync() |
| | | this.statusBarHeight = systemInfo.statusBarHeight || 0 |
| | | this.orderId = options.id || pageOptions.id |
| | | this.orderIndex = options.index || pageOptions.index |
| | | this.orderIndex = options.index |
| | | this.topFixedHeight = uni.upx2px(500 + 92) |
| | | if (this.orderId) { |
| | | this.getOrderDetail() |
| | | } |
| | | }, |
| | | methods: { |
| | | handleBack() { |
| | | uni.navigateBack({ delta: 1 }); |
| | | }, |
| | | getOrderDetail() { |
| | | console.log('getOrderDetail', this.orderId) |
| | | this.$u.api.orderDetail({ orderId: this.orderId }).then(res => { |
| | | console.log(res) |
| | | if (res.code === 200) { |
| | | console.log('orderDetail:', res.data) |
| | | this.orderDetail = res.data |
| | | console.log(this.orderDetail) |
| | | this.goodsList = res.data.items || [] |
| | | this.photos = res.data.photos || [] |
| | | this.photos = res.data.orderImages || [] |
| | | if ((this.orderDetail.status === 3 || this.orderDetail.status === 4) && this.orderDetail.navigateLat && this.orderDetail.navigateLng) { |
| | | this.getCurrentLocation() |
| | | } else { |
| | | console.log('Skipping getCurrentLocation - status or coordinates not available') |
| | | } |
| | | } |
| | | }).catch(err => { |
| | | console.log('err', err) |
| | | }) |
| | | }, |
| | | |
| | | makePhoneCall() { |
| | | if (this.orderDetail.contactPhone) { |
| | | getCurrentLocation() { |
| | | uni.getLocation({ |
| | | type: 'gcj02', |
| | | success: (res) => { |
| | | this.currentLocation = { |
| | | latitude: res.latitude, |
| | | longitude: res.longitude |
| | | } |
| | | this.getRoutePlan() |
| | | }, |
| | | fail: (err) => { |
| | | console.log('获取位置失败', err) |
| | | } |
| | | }) |
| | | }, |
| | | |
| | | getRoutePlan() { |
| | | if (!this.currentLocation || !this.orderDetail.navigateLat || !this.orderDetail.navigateLng) { |
| | | console.log('Skipping route plan - missing data') |
| | | return |
| | | } |
| | | const from = `${this.currentLocation.latitude},${this.currentLocation.longitude}` |
| | | const to = `${this.orderDetail.navigateLng},${this.orderDetail.navigateLat}` |
| | | this.$u.api.directionInfo({ |
| | | from, |
| | | to, |
| | | mode: 'driving' |
| | | }).then(res => { |
| | | console.log('directionInfo success:', res) |
| | | if (res && res.paths && res.paths.length > 0) { |
| | | const path = res.paths[0] |
| | | this.distance = path.distance |
| | | this.duration = path.duration |
| | | const points = [] |
| | | path.steps.forEach(step => { |
| | | const polylineStr = step.polyline |
| | | const coordinates = polylineStr.split(';') |
| | | coordinates.forEach(coord => { |
| | | const [lng, lat] = coord.split(',') |
| | | points.push({ |
| | | latitude: parseFloat(lat), |
| | | longitude: parseFloat(lng) |
| | | }) |
| | | }) |
| | | }) |
| | | this.routePoints = points |
| | | this.$forceUpdate() |
| | | } else { |
| | | console.log('No route data returned:', res) |
| | | } |
| | | }).catch(err => { |
| | | console.log('路径规划失败', err) |
| | | }) |
| | | }, |
| | | |
| | | // makePhoneCall() { |
| | | // if (this.orderDetail.contactPhone) { |
| | | // uni.makePhoneCall({ |
| | | // phoneNumber: this.orderDetail.contactPhone |
| | | // }) |
| | | // } |
| | | // }, |
| | | |
| | | makeShopCall(type) { |
| | | const phone = type === 'take' ? this.orderDetail.takeContactPhone : this.orderDetail.depositShopPhone |
| | | if (phone) { |
| | | uni.makePhoneCall({ |
| | | phoneNumber: this.orderDetail.contactPhone |
| | | phoneNumber: phone |
| | | }) |
| | | } |
| | | }, |
| | | |
| | | navigateToAddress(type) { |
| | | let latitude, longitude, name, address |
| | | if (type === 'deposit') { |
| | | latitude = this.orderDetail.depositShopLat |
| | | longitude = this.orderDetail.depositShopLng |
| | | name = this.orderDetail.depositShopName |
| | | address = this.orderDetail.depositShopAddress |
| | | } else { |
| | | latitude = this.orderDetail.takeLat |
| | | longitude = this.orderDetail.takeLng |
| | | name = this.orderDetail.takeName |
| | | address = this.orderDetail.takeAddress |
| | | } |
| | | if (!latitude || !longitude) { |
| | | uni.showToast({ title: '地址坐标缺失', icon: 'none' }) |
| | | return |
| | | } |
| | | uni.openLocation({ |
| | | latitude, |
| | | longitude, |
| | | name, |
| | | address, |
| | | success: () => {}, |
| | | fail: (err) => { |
| | | uni.showToast({ title: '打开地图失败', icon: 'none' }) |
| | | console.error('openLocation fail:', err) |
| | | } |
| | | }) |
| | | }, |
| | | |
| | | previewImage(current) { |
| | | uni.previewImage({ |
| | | current, |
| | | urls: this.photos |
| | | }) |
| | | }, |
| | | |
| | | handleFooterAction(button) { |
| | |
| | | } |
| | | }, |
| | | handleCancelOrder() { |
| | | uni.showModal({ |
| | | title: '温馨提示', |
| | | content: '确定要取消订单吗?', |
| | | confirmColor: '#0055FF', |
| | | cancelColor: '#666666', |
| | | success: (res) => { |
| | | if (res.confirm) { |
| | | this.$u.api.cancelOrder({ orderId: this.orderId }).then(res => { |
| | | if (res.code === 200) { |
| | | uni.showToast({ title: '取消成功', icon: 'success' }) |
| | | this.getOrderDetail() |
| | | } else { |
| | | uni.showToast({ title: res.msg || '取消失败', icon: 'none' }) |
| | | } |
| | | }) |
| | | } |
| | | this.$u.api.cancelLimit().then(res => { |
| | | if (res.code === 200) { |
| | | this.cancelRemain = res.data.remain |
| | | } |
| | | }).finally(() => { |
| | | this.showCancelModal = true |
| | | }) |
| | | }, |
| | | confirmCancelOrder() { |
| | | this.$u.api.cancelOrder({ orderId: this.orderId }).then(res => { |
| | | this.showCancelModal = false |
| | | if (res.code === 200) { |
| | | uni.showToast({ title: '取消成功', icon: 'success' }) |
| | | this.getOrderDetail() |
| | | } |
| | | }).finally(() => { |
| | | this.showCancelModal = false |
| | | }) |
| | | }, |
| | | handleGrabOrder() { |
| | | uni.showModal({ |
| | | title: '温馨提示', |
| | | content: '是否确认接单?', |
| | | confirmColor: '#0055FF', |
| | | cancelColor: '#666666', |
| | | success: (res) => { |
| | | if (res.confirm) { |
| | | this.$u.api.grabOrder({ orderId: this.orderId }).then(res => { |
| | | if (res.code === 200) { |
| | | uni.showToast({ title: '接单成功', icon: 'success' }) |
| | | uni.navigateBack() |
| | | } else { |
| | | uni.showToast({ title: res.msg || '接单失败', icon: 'none' }) |
| | | } |
| | | }) |
| | | } |
| | | this.showGrabModal = true |
| | | }, |
| | | confirmGrabOrder() { |
| | | this.$u.api.grabOrder({ orderId: this.orderId }).then(res => { |
| | | this.showGrabModal = false |
| | | if (res.code === 200) { |
| | | uni.showToast({ title: '接单成功', icon: 'success' }) |
| | | this.getOrderDetail() |
| | | setTimeout(() => { |
| | | uni.navigateBack() |
| | | }, 1500) |
| | | } else { |
| | | uni.showToast({ title: res.msg || '接单失败', icon: 'none' }) |
| | | } |
| | | }).catch(() => { |
| | | this.showGrabModal = false |
| | | }) |
| | | }, |
| | | closePhotoPopup() { |
| | |
| | | }) |
| | | |
| | | Promise.all(uploadTasks).then(images => { |
| | | console.log(images) |
| | | const api = this.photoPopupMode === 'deliver' ? 'confirmDeliver' : 'confirmPickup' |
| | | const params = { |
| | | images: images.map(img => img.imgaddr), |
| | |
| | | |
| | | <style lang="scss" scoped> |
| | | .order-detail-page { |
| | | height: 100vh; |
| | | background: #f6f8fc; |
| | | overflow: hidden; |
| | | background: #ffffff; |
| | | |
| | | &__simple-nav { |
| | | position: fixed; |
| | |
| | | height: 88rpx; |
| | | display: flex; |
| | | align-items: center; |
| | | padding: 0 24rpx; |
| | | justify-content: space-between; |
| | | padding: 0 30rpx; |
| | | box-sizing: border-box; |
| | | } |
| | | |
| | | &__simple-nav-title { |
| | |
| | | position: relative; |
| | | margin: 0; |
| | | height: 500rpx; |
| | | width: 750rpx; |
| | | border-radius: 0; |
| | | overflow: hidden; |
| | | background: #dbe8ff; |
| | | } |
| | | |
| | | &__map { |
| | | width: 100%; |
| | | height: 100%; |
| | | width: 750rpx; |
| | | height: 500rpx; |
| | | } |
| | | |
| | | &__map-bubble { |
| | |
| | | } |
| | | |
| | | &__content { |
| | | padding: 16rpx 0 calc(env(safe-area-inset-bottom) + 26rpx); |
| | | // padding: 16rpx 0 calc(env(safe-area-inset-bottom) + 26rpx); |
| | | } |
| | | |
| | | &__section { |
| | | margin: 16rpx 20rpx 0; |
| | | padding: 26rpx 22rpx; |
| | | border-radius: 20rpx; |
| | | // margin: 16rpx 20rpx 0; |
| | | background: #ffffff; |
| | | |
| | | &--main { |
| | |
| | | display: flex; |
| | | justify-content: space-between; |
| | | align-items: flex-start; |
| | | padding: 30rpx; |
| | | box-sizing: border-box; |
| | | } |
| | | |
| | | &__done-summary { |
| | | padding: 30rpx; |
| | | box-sizing: border-box; |
| | | display: flex; |
| | | justify-content: space-between; |
| | | align-items: flex-start; |
| | | gap: 20rpx; |
| | | background: #F6F9FF; |
| | | } |
| | | |
| | | &__done-summary-left { |
| | |
| | | } |
| | | |
| | | &__time { |
| | | font-size: 42rpx; |
| | | font-weight: 700; |
| | | color: #ff972c; |
| | | font-size: 34rpx; |
| | | font-weight: 600; |
| | | color: #FA8010; |
| | | } |
| | | |
| | | &__time-sub, |
| | |
| | | } |
| | | |
| | | &__price { |
| | | font-size: 44rpx; |
| | | font-size: 26rpx; |
| | | font-weight: 700; |
| | | color: #ff4132; |
| | | } |
| | |
| | | &__tag-text { |
| | | padding: 5rpx 12rpx; |
| | | border-radius: 8rpx; |
| | | background: #ff9c45; |
| | | background: linear-gradient(319deg, #EE9D0E 0%, #FF4E4E 100%); |
| | | font-size: 22rpx; |
| | | font-weight: 600; |
| | | color: #ffffff; |
| | | } |
| | | |
| | | &__route-list { |
| | | margin-top: 22rpx; |
| | | margin-top: 36rpx; |
| | | padding: 0 30rpx; |
| | | box-sizing: border-box; |
| | | } |
| | | |
| | | &__route-item { |
| | | display: flex; |
| | | align-items: flex-start; |
| | | align-items: stretch; |
| | | position: relative; |
| | | |
| | | &--end { |
| | | margin-top: 24rpx; |
| | | margin-top: 20rpx; |
| | | } |
| | | } |
| | | |
| | |
| | | flex-direction: column; |
| | | align-items: center; |
| | | flex-shrink: 0; |
| | | position: relative; |
| | | } |
| | | |
| | | &__distance-top { |
| | | font-size: 28rpx; |
| | | &__route-badge { |
| | | width: 44rpx; |
| | | height: 44rpx; |
| | | border-radius: 50%; |
| | | display: flex; |
| | | align-items: center; |
| | | justify-content: center; |
| | | font-size: 24rpx; |
| | | font-weight: 600; |
| | | color: #515866; |
| | | } |
| | | color: #ffffff; |
| | | position: relative; |
| | | z-index: 1; |
| | | |
| | | &__distance-unit { |
| | | font-size: 22rpx; |
| | | color: #939aa6; |
| | | } |
| | | &--take { |
| | | background: #10B2FA; |
| | | } |
| | | |
| | | &__route-divider { |
| | | width: 4rpx; |
| | | height: 42rpx; |
| | | margin-top: 8rpx; |
| | | border-radius: 999rpx; |
| | | background: #d7dbe2; |
| | | |
| | | &--light { |
| | | height: 18rpx; |
| | | margin: 8rpx 0; |
| | | &--send { |
| | | background: #FA8010; |
| | | } |
| | | } |
| | | |
| | | &__route-pin { |
| | | width: 18rpx; |
| | | height: 18rpx; |
| | | margin-top: 4rpx; |
| | | border-radius: 50%; |
| | | background: #888f9b; |
| | | &__route-divider { |
| | | position: absolute; |
| | | top: 64rpx; |
| | | bottom: 0; |
| | | width: 0; |
| | | border-left: 2rpx dashed #d7dbe2; |
| | | } |
| | | |
| | | &__route-main { |
| | |
| | | display: flex; |
| | | justify-content: space-between; |
| | | gap: 18rpx; |
| | | margin-left: 10rpx; |
| | | } |
| | | |
| | | &__route-texts { |
| | |
| | | |
| | | &__route-title { |
| | | display: block; |
| | | font-size: 38rpx; |
| | | font-weight: 700; |
| | | font-weight: 600; |
| | | font-size: 34rpx; |
| | | color: #222222; |
| | | line-height: 1.3; |
| | | color: #2b3139; |
| | | } |
| | | |
| | | &__route-desc { |
| | | display: block; |
| | | margin-top: 8rpx; |
| | | line-height: 1.5; |
| | | font-weight: 400; |
| | | font-size: 26rpx; |
| | | color: #999999; |
| | | } |
| | | |
| | | &__route-actions { |
| | |
| | | display: flex; |
| | | flex-direction: column; |
| | | align-items: center; |
| | | padding-top: 30rpx; |
| | | margin-top: 30rpx; |
| | | } |
| | | |
| | | &__qrcode-box { |
| | |
| | | } |
| | | |
| | | &__qrcode-value { |
| | | margin-top: 16rpx; |
| | | font-size: 40rpx; |
| | | font-weight: 700; |
| | | color: #303640; |
| | | margin-top: 32rpx; |
| | | font-weight: 600; |
| | | font-size: 36rpx; |
| | | color: #222222; |
| | | } |
| | | |
| | | &__qrcode-label { |
| | | margin-top: 6rpx; |
| | | margin-top: 12rpx; |
| | | font-weight: 400; |
| | | font-size: 26rpx; |
| | | color: #999999; |
| | | } |
| | | |
| | | &__section-title { |
| | |
| | | |
| | | &__photos { |
| | | display: flex; |
| | | gap: 16rpx; |
| | | margin-top: 18rpx; |
| | | flex-wrap: wrap; |
| | | gap: 20rpx; |
| | | margin-top: 30rpx; |
| | | padding-bottom: 30rpx; |
| | | box-sizing: border-box; |
| | | } |
| | | |
| | | &__photo { |
| | | width: 92rpx; |
| | | height: 92rpx; |
| | | border-radius: 10rpx; |
| | | width: 120rpx; |
| | | height: 120rpx; |
| | | border-radius: 8rpx; |
| | | overflow: hidden; |
| | | |
| | | image { |
| | | width: 100%; |
| | | height: 100%; |
| | | } |
| | | } |
| | | |
| | | &__detail-label { |
| | |
| | | right: 0; |
| | | bottom: 0; |
| | | display: flex; |
| | | justify-content: flex-end; |
| | | justify-content: space-between; |
| | | gap: 20rpx; |
| | | padding: 14rpx 20rpx calc(env(safe-area-inset-bottom) + 14rpx); |
| | | background: #ffffff; |
| | |
| | | &__upload-card, |
| | | &__preview-card { |
| | | position: relative; |
| | | width: 160rpx; |
| | | height: 160rpx; |
| | | border-radius: 8rpx; |
| | | width: 144rpx; |
| | | height: 144rpx; |
| | | overflow: hidden; |
| | | } |
| | | |
| | |
| | | flex-direction: column; |
| | | align-items: center; |
| | | justify-content: center; |
| | | border: 2rpx solid #ebedf2; |
| | | background: #f8f9fb; |
| | | } |
| | | |