| | |
| | | <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-zoom="true" |
| | | :enable-scroll="true" |
| | | ></map> |
| | | <view class="order-detail-page__map-bubble"> |
| | | <text class="order-detail-page__map-bubble-text">剩余3.2km,约4分钟</text> |
| | | </view> |
| | | </view> |
| | | |
| | | <view class="order-detail-page__status-bar"> |
| | |
| | | <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).toFixed(2) : (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> |
| | | <view class="order-detail-page__done-summary-right"> |
| | | <view class="order-detail-page__done-price-row"> |
| | | <text class="order-detail-page__price">¥{{ (orderDetail.driverFee / 100).toFixed(1) }}</text> |
| | | <text class="order-detail-page__price">¥{{ (orderDetail.driverFee / 100).toFixed(2) }}</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).toFixed(2) }}</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> |
| | |
| | | <image class="order-detail-page__qrcode-image" :src="'https://api.qrserver.com/v1/create-qr-code/?size=200x200&data=' + orderDetail.driverVerifyCode" mode="aspectFit"></image> |
| | | </view> |
| | | <text class="order-detail-page__qrcode-value">{{ orderDetail.driverVerifyCode }}</text> |
| | | <text class="order-detail-page__qrcode-label">取货码</text> |
| | | <text class="order-detail-page__qrcode-label">{{ orderDetail.status === 3 ? '取货码' : '存件码' }}</text> |
| | | </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"> |
| | |
| | | <button class="photo-popup__submit" hover-class="photo-popup__submit--hover" @click="submitPhotoPopup">{{ photoPopupSubmitText }}</button> |
| | | </view> |
| | | </u-popup> |
| | | |
| | | <u-popup :show="showTimelinePopup" round="20" mode="bottom" @close="closeTimelinePopup"> |
| | | <view class="track-popup"> |
| | | <view class="track-popup__header"> |
| | | <text class="track-popup__title">订单轨迹</text> |
| | | <view class="track-popup__close" @click="closeTimelinePopup">×</view> |
| | | </view> |
| | | <scroll-view scroll-y class="track-popup__body"> |
| | | <view class="track-empty" v-if="!timelineList.length">暂无轨迹信息</view> |
| | | <view class="track-list" v-else> |
| | | <view class="track-item" v-for="(track, index) in timelineList" :key="track.key || index"> |
| | | <view class="track-item__content"> |
| | | <view class="track-item__rail"> |
| | | <image v-if="index === 0" class="track-item__dot track-item__dot--active" src="/static/image/dian.png" mode="aspectFit"></image> |
| | | <view v-else class="track-item__dot"></view> |
| | | <view class="track-item__line" v-if="index !== timelineList.length - 1"></view> |
| | | </view> |
| | | <view class="track-item__body"> |
| | | <text class="track-item__time">{{ track.time }}</text> |
| | | <text class="track-item__desc">{{ track.title }}</text> |
| | | <view class="track-item__images" v-if="track.images && track.images.length"> |
| | | <image |
| | | v-for="(img, imgIndex) in track.images" |
| | | :key="track.key + '-' + imgIndex" |
| | | :src="img" |
| | | mode="aspectFill" |
| | | @click="previewTimelineImages(track.images, imgIndex)" |
| | | ></image> |
| | | </view> |
| | | </view> |
| | | </view> |
| | | </view> |
| | | </view> |
| | | </scroll-view> |
| | | </view> |
| | | <!-- <view class="timeline-popup"> |
| | | <view class="timeline-popup__header"> |
| | | <view class="timeline-popup__placeholder"></view> |
| | | <text class="timeline-popup__title">订单轨迹</text> |
| | | <u-icon name="close" color="#A7ACB5" size="26" @click="closeTimelinePopup"></u-icon> |
| | | </view> |
| | | |
| | | <scroll-view class="timeline-popup__scroll" scroll-y> |
| | | <view v-if="timelineList.length" class="timeline-popup__list"> |
| | | <view v-for="(item, index) in timelineList" :key="index" class="timeline-popup__item"> |
| | | <view class="timeline-popup__axis"> |
| | | <view class="timeline-popup__dot" :class="index === 0 ? 'timeline-popup__dot--active' : ''"></view> |
| | | <view v-if="index !== timelineList.length - 1" class="timeline-popup__line"></view> |
| | | </view> |
| | | |
| | | <view class="timeline-popup__content"> |
| | | <text class="timeline-popup__time">{{ item.time || '-' }}</text> |
| | | <text class="timeline-popup__desc">{{ item.title || '-' }}</text> |
| | | <view v-if="item.images.length" class="timeline-popup__images"> |
| | | <image |
| | | v-for="(image, imageIndex) in item.images" |
| | | :key="imageIndex" |
| | | class="timeline-popup__image" |
| | | :src="image" |
| | | mode="aspectFill" |
| | | @click="previewTimelineImages(item.images, imageIndex)" |
| | | ></image> |
| | | </view> |
| | | </view> |
| | | </view> |
| | | </view> |
| | | |
| | | <view v-else class="timeline-popup__empty">暂无订单轨迹</view> |
| | | </scroll-view> |
| | | </view> --> |
| | | </u-popup> |
| | | </view> |
| | | </template> |
| | | |
| | | <script> |
| | | import { mapState } from 'vuex' |
| | | import { chooseImageWithNotice, getLocationWithNotice } from '@/utils/utils' |
| | | |
| | | 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, |
| | | showTimelinePopup: false, |
| | | timelineList: [], |
| | | currentLocation: null, |
| | | routePoints: [], |
| | | locationTimer: null, |
| | | distance: 0, |
| | | duration: 0, |
| | | isWithinOperationRadius: true, |
| | | statusTextMap: { |
| | | 2: '待接单', |
| | | 3: '待取货', |
| | | 4: '配送中', |
| | | 5: '已送达', |
| | | 7: '已完成', |
| | | 99: '已取消' |
| | | }, |
| | |
| | | } |
| | | }, |
| | | computed: { |
| | | ...mapState(['userInfo']), |
| | | 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: this.orderDetail.navigateLat, longitude: this.orderDetail.navigateLng } |
| | | const hasEndPoint = this.orderDetail.navigateLat && this.orderDetail.navigateLng |
| | | const endPoint = { latitude: this.orderDetail.navigateLng, longitude: this.orderDetail.navigateLat } |
| | | |
| | | 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: this.orderDetail.navigateLat, longitude: this.orderDetail.navigateLng } |
| | | } |
| | | |
| | | 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 }, |
| | | { latitude: 31.838214, longitude: 117.265358 }, |
| | | { latitude: 31.840126, longitude: 117.272481 }, |
| | | 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 status = this.orderDetail.status |
| | | const takeShopId = this.orderDetail.takeShopId |
| | | const buttons = [{ text: '订单轨迹', primary: false, action: 'timeline' }] |
| | | |
| | | if (status === 2) { |
| | | return [{ text: '立即抢单', primary: true, action: 'grab' }] |
| | | return buttons.concat([{ text: '立即抢单', primary: true, action: 'grab' }]) |
| | | } |
| | | |
| | | if (status === 3) { |
| | | return [ |
| | | return buttons.concat([ |
| | | { text: '取消订单', primary: false, action: 'cancel' }, |
| | | { text: '拍照取货', primary: true, action: 'pickup' } |
| | | ] |
| | | ]) |
| | | } |
| | | |
| | | if (status === 4) { |
| | | if (!takeShopId) { |
| | | return [{ text: '拍照送达', primary: true, action: 'deliver' }] |
| | | return buttons.concat([{ text: '拍照送达', primary: true, action: 'deliver' }]) |
| | | } |
| | | return [] |
| | | return buttons |
| | | } |
| | | |
| | | return [] |
| | | return buttons |
| | | }, |
| | | photoPopupTitle() { |
| | | return this.photoPopupMode === 'deliver' ? '拍照送达' : '拍照取货' |
| | |
| | | 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() |
| | | } |
| | | }, |
| | | |
| | | onUnload() { |
| | | if (this.locationTimer) { |
| | | clearInterval(this.locationTimer) |
| | | this.locationTimer = null |
| | | } |
| | | }, |
| | | |
| | | methods: { |
| | | getOrderDetail() { |
| | | console.log('getOrderDetail', this.orderId) |
| | | this.$u.api.orderDetail({ orderId: this.orderId }).then(res => { |
| | | console.log(res) |
| | | if (res.code === 200) { |
| | | this.orderDetail = res.data |
| | | console.log(this.orderDetail) |
| | | this.goodsList = res.data.items || [] |
| | | this.photos = res.data.photos || [] |
| | | } |
| | | }).catch(err => { |
| | | console.log('err', err) |
| | | handleBack() { |
| | | uni.navigateBack({ delta: 1 }); |
| | | }, |
| | | initOperationRadius() { |
| | | console.log('initOperationRadius') |
| | | return new Promise((resolve) => { |
| | | getLocationWithNotice({ |
| | | type: 'gcj02', |
| | | success: (res) => { |
| | | this.$u.api.checkDriverOperationRadius({ |
| | | lat: res.latitude, |
| | | lng: res.longitude, |
| | | orderId: this.orderId |
| | | }).then(res => { |
| | | if (res.code === 200) { |
| | | this.isWithinOperationRadius = res.data |
| | | console.log(res.data) |
| | | if (!this.isWithinOperationRadius) { |
| | | uni.showToast({ |
| | | title: '您当前位置与收货地址距离超出范围,请在地址附近重新拍照', |
| | | icon: 'none' |
| | | }) |
| | | resolve(false) |
| | | } else { |
| | | resolve(true) |
| | | } |
| | | } else { |
| | | resolve(false) |
| | | } |
| | | }).catch(() => { |
| | | resolve(false) |
| | | }) |
| | | }, |
| | | fail: () => { |
| | | this.isWithinOperationRadius = false |
| | | uni.showToast({ |
| | | title: '无法获取您的位置信息,请前往设置开启定位权限', |
| | | icon: 'none' |
| | | }) |
| | | resolve(false) |
| | | } |
| | | }).catch(() => { |
| | | resolve(false) |
| | | }) |
| | | }) |
| | | }, |
| | | |
| | | makePhoneCall() { |
| | | if (this.orderDetail.contactPhone) { |
| | | getOrderDetail() { |
| | | this.$u.api.orderDetail({ orderId: this.orderId }).then(res => { |
| | | if (res.code === 200) { |
| | | console.log('orderDetail:', res.data) |
| | | this.orderDetail = res.data |
| | | this.goodsList = res.data.items || [] |
| | | 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') |
| | | } |
| | | } |
| | | }) |
| | | }, |
| | | |
| | | getCurrentLocation() { |
| | | this.fetchLocation() |
| | | this.locationTimer = setInterval(() => { |
| | | this.fetchLocation() |
| | | }, 60000) |
| | | }, |
| | | |
| | | fetchLocation() { |
| | | getLocationWithNotice({ |
| | | type: 'gcj02', |
| | | success: (res) => { |
| | | this.currentLocation = { |
| | | latitude: res.latitude, |
| | | longitude: res.longitude |
| | | } |
| | | this.getRoutePlan() |
| | | }, |
| | | fail: (err) => { |
| | | console.log('获取位置失败', err) |
| | | } |
| | | }).catch(() => {}) |
| | | }, |
| | | |
| | | 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}` |
| | | console.log('driverType', this.userInfo.driverType) |
| | | this.$u.api.directionInfo({ |
| | | from, |
| | | to, |
| | | mode: this.userInfo.driverType |
| | | }).then(res => { |
| | | console.log('paths success:', res.data.route.paths[0]) |
| | | if (res.code === 200) { |
| | | const path = res.data.route.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() |
| | | } |
| | | }) |
| | | }, |
| | | |
| | | // 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 |
| | | }) |
| | | }, |
| | | |
| | | normalizeTimelineImages(images) { |
| | | if (Array.isArray(images)) { |
| | | return images.filter(item => !!item) |
| | | } |
| | | |
| | | if (typeof images === 'string') { |
| | | return images.split(',').map(item => item.trim()).filter(item => !!item) |
| | | } |
| | | |
| | | return [] |
| | | }, |
| | | |
| | | openTimelinePopup() { |
| | | if (!this.orderId) { |
| | | uni.showToast({ title: '订单ID不存在', icon: 'none' }) |
| | | return |
| | | } |
| | | |
| | | uni.showLoading({ title: '加载中...' }) |
| | | this.$u.api.timeline(this.orderId).then(res => { |
| | | if (res.code === 200) { |
| | | const list = Array.isArray(res.data) ? res.data : [] |
| | | this.timelineList = list.map(item => ({ |
| | | time: item.time || '', |
| | | title: item.title || '', |
| | | images: this.normalizeTimelineImages(item.images) |
| | | })) |
| | | this.showTimelinePopup = true |
| | | } else { |
| | | uni.showToast({ title: res.msg || '获取订单轨迹失败', icon: 'none' }) |
| | | } |
| | | }).catch(() => { |
| | | uni.showToast({ title: '获取订单轨迹失败', icon: 'none' }) |
| | | }).finally(() => { |
| | | uni.hideLoading() |
| | | }) |
| | | }, |
| | | |
| | | closeTimelinePopup() { |
| | | this.showTimelinePopup = false |
| | | }, |
| | | |
| | | previewTimelineImages(images, currentIndex) { |
| | | if (!images || !images.length) { |
| | | return |
| | | } |
| | | |
| | | uni.previewImage({ |
| | | current: images[currentIndex], |
| | | urls: images |
| | | }) |
| | | }, |
| | | |
| | | handleFooterAction(button) { |
| | |
| | | } |
| | | |
| | | if (action === 'pickup' || action === 'deliver') { |
| | | this.uploadedPhotos = [] |
| | | this.photoRemark = '' |
| | | this.photoPopupMode = action |
| | | this.showPhotoPopup = true |
| | | this.initOperationRadius().then((isValid) => { |
| | | console.log(isValid) |
| | | if (!isValid) return |
| | | this.uploadedPhotos = [] |
| | | this.photoRemark = '' |
| | | this.photoPopupMode = action |
| | | this.showPhotoPopup = true |
| | | }) |
| | | return |
| | | } |
| | | |
| | |
| | | this.handleGrabOrder() |
| | | return |
| | | } |
| | | |
| | | if (action === 'timeline') { |
| | | this.openTimelinePopup() |
| | | } |
| | | }, |
| | | 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() |
| | | uni.$emit('jiedanSuccess') |
| | | setTimeout(() => { |
| | | uni.navigateBack() |
| | | }, 1500) |
| | | } else { |
| | | uni.showToast({ title: res.msg || '接单失败', icon: 'none' }) |
| | | } |
| | | }).catch(() => { |
| | | this.showGrabModal = false |
| | | }) |
| | | }, |
| | | closePhotoPopup() { |
| | |
| | | }, |
| | | chooseImage() { |
| | | const count = 3 - this.uploadedPhotos.length |
| | | uni.chooseImage({ |
| | | chooseImageWithNotice({ |
| | | count: count, |
| | | sourceType: ['camera', 'album'], |
| | | success: (res) => { |
| | | const tempFilePaths = res.tempFilePaths |
| | | this.uploadedPhotos = this.uploadedPhotos.concat(tempFilePaths) |
| | | } |
| | | }) |
| | | }).catch(() => {}) |
| | | }, |
| | | deletePhoto(index) { |
| | | this.uploadedPhotos.splice(index, 1) |
| | | }, |
| | | submitPhotoPopup() { |
| | | submitPhotoPopup() { |
| | | if (this.uploadedPhotos.length === 0) { |
| | | uni.showToast({ title: '请上传照片', icon: 'none' }) |
| | | return |
| | | } |
| | | uni.showLoading({ title: '上传中...' }) |
| | | getLocationWithNotice({ |
| | | type: 'gcj02', |
| | | success: (locationRes) => { |
| | | this.doUploadPhotos(locationRes.latitude, locationRes.longitude) |
| | | }, |
| | | fail: () => { |
| | | this.doUploadPhotos(null, null) |
| | | } |
| | | }).catch(() => { |
| | | this.doUploadPhotos(null, null) |
| | | }) |
| | | }, |
| | | |
| | | doUploadPhotos(latitude, longitude) { |
| | | const uploadTasks = this.uploadedPhotos.map(path => { |
| | | return new Promise((resolve, reject) => { |
| | | const formData = { folder: 'orders' } |
| | | if (latitude && longitude) { |
| | | formData.latitude = latitude |
| | | formData.longitude = longitude |
| | | } |
| | | console.log('formData:', formData) |
| | | uni.uploadFile({ |
| | | url: this.$baseUrl + 'web/public/upload', |
| | | filePath: path, |
| | | name: 'file', |
| | | formData: { |
| | | folder: 'order' |
| | | }, |
| | | formData: formData, |
| | | success: (uploadRes) => { |
| | | const data = JSON.parse(uploadRes.data) |
| | | if (data.code === 200) { |
| | |
| | | }) |
| | | |
| | | Promise.all(uploadTasks).then(images => { |
| | | console.log(images) |
| | | const api = this.photoPopupMode === 'deliver' ? 'confirmDeliver' : 'confirmPickup' |
| | | const params = { |
| | | images: images.map(img => img.imgaddr), |
| | | orderId: this.orderId, |
| | | remark: this.photoRemark |
| | | } |
| | | if (latitude && longitude) { |
| | | params.latitude = latitude |
| | | params.longitude = longitude |
| | | } |
| | | return this.$u.api[api](params) |
| | | }).then(res => { |
| | |
| | | |
| | | <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%; |
| | | } |
| | | |
| | | &__map-bubble { |
| | | position: absolute; |
| | | left: 34rpx; |
| | | bottom: 32rpx; |
| | | padding: 12rpx 18rpx; |
| | | border-radius: 14rpx; |
| | | background: rgba(255, 255, 255, 0.96); |
| | | box-shadow: 0 8rpx 18rpx rgba(23, 74, 163, 0.12); |
| | | } |
| | | |
| | | &__map-bubble-text { |
| | | font-size: 24rpx; |
| | | font-weight: 600; |
| | | color: #2f6ff2; |
| | | width: 750rpx; |
| | | height: 500rpx; |
| | | } |
| | | |
| | | &__status-bar { |
| | |
| | | } |
| | | |
| | | &__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; |
| | |
| | | |
| | | &--hover { |
| | | opacity: 0.92; |
| | | } |
| | | } |
| | | } |
| | | |
| | | .track-popup { |
| | | background: #FFFFFF; |
| | | border-radius: 24rpx 24rpx 0 0; |
| | | padding: 24rpx 24rpx calc(24rpx + env(safe-area-inset-bottom)); |
| | | box-sizing: border-box; |
| | | .track-popup__header { |
| | | height: 88rpx; |
| | | display: flex; |
| | | align-items: center; |
| | | justify-content: center; |
| | | position: relative; |
| | | } |
| | | .track-popup__title { |
| | | font-weight: 600; |
| | | font-size: 32rpx; |
| | | color: #222222; |
| | | } |
| | | .track-popup__close { |
| | | position: absolute; |
| | | right: 0; |
| | | top: 50%; |
| | | transform: translateY(-50%); |
| | | width: 56rpx; |
| | | height: 56rpx; |
| | | display: flex; |
| | | align-items: center; |
| | | justify-content: center; |
| | | font-size: 56rpx; |
| | | line-height: 1; |
| | | color: #A5A7AD; |
| | | } |
| | | .track-popup__body { |
| | | height: 50vh; |
| | | } |
| | | .track-empty { |
| | | line-height: 50vh; |
| | | text-align: center; |
| | | font-size: 28rpx; |
| | | color: #999999; |
| | | } |
| | | .track-list { |
| | | padding: 12rpx 0 0; |
| | | } |
| | | .track-item { |
| | | position: relative; |
| | | } |
| | | .track-item__rail { |
| | | width: 34rpx; |
| | | flex-shrink: 0; |
| | | display: flex; |
| | | flex-direction: column; |
| | | align-items: center; |
| | | align-self: stretch; |
| | | position: relative; |
| | | } |
| | | .track-item__dot { |
| | | width: 16rpx; |
| | | height: 16rpx; |
| | | flex-shrink: 0; |
| | | position: relative; |
| | | z-index: 1; |
| | | border-radius: 50%; |
| | | background: #CCCCCC; |
| | | margin-top: 12rpx; |
| | | } |
| | | .track-item__dot--active { |
| | | width: 32rpx; |
| | | height: 32rpx; |
| | | flex-shrink: 0; |
| | | display: block; |
| | | position: relative; |
| | | z-index: 1; |
| | | margin-top: 4rpx; |
| | | background: transparent; |
| | | } |
| | | .track-item__line { |
| | | width: 2rpx; |
| | | position: absolute; |
| | | left: 50%; |
| | | top: 28rpx; |
| | | bottom: -30rpx; |
| | | transform: translateX(-50%); |
| | | background: #E6EAF0; |
| | | } |
| | | .track-item__content { |
| | | display: flex; |
| | | align-items: flex-start; |
| | | column-gap: 22rpx; |
| | | padding: 0 0 20rpx; |
| | | } |
| | | .track-item__body { |
| | | flex: 1; |
| | | min-width: 0; |
| | | } |
| | | .track-item__time { |
| | | display: block; |
| | | font-weight: 400; |
| | | font-size: 26rpx; |
| | | color: #666666; |
| | | line-height: 1.4; |
| | | } |
| | | .track-item__desc { |
| | | display: block; |
| | | margin-top: 22rpx; |
| | | font-weight: 500; |
| | | font-size: 30rpx; |
| | | color: #666666; |
| | | } |
| | | .track-item__images { |
| | | display: flex; |
| | | flex-wrap: wrap; |
| | | gap: 18rpx; |
| | | margin-top: 24rpx; |
| | | image { |
| | | width: 130rpx; |
| | | height: 130rpx; |
| | | border-radius: 12rpx; |
| | | background: #F3F5F8; |
| | | } |
| | | } |
| | | } |
| | |
| | | &__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; |
| | | } |
| | | |
| | |
| | | } |
| | | } |
| | | } |
| | | |
| | | .timeline-popup { |
| | | padding: 26rpx 28rpx calc(env(safe-area-inset-bottom) + 28rpx); |
| | | background: #ffffff; |
| | | border-top-left-radius: 20rpx; |
| | | border-top-right-radius: 20rpx; |
| | | |
| | | &__header { |
| | | display: flex; |
| | | align-items: center; |
| | | justify-content: space-between; |
| | | } |
| | | |
| | | &__placeholder { |
| | | width: 52rpx; |
| | | height: 52rpx; |
| | | flex-shrink: 0; |
| | | opacity: 0; |
| | | } |
| | | |
| | | &__title { |
| | | font-size: 40rpx; |
| | | font-weight: 700; |
| | | color: #222222; |
| | | } |
| | | |
| | | &__scroll { |
| | | max-height: 50vh; |
| | | margin-top: 30rpx; |
| | | } |
| | | |
| | | &__list { |
| | | padding-bottom: 12rpx; |
| | | } |
| | | |
| | | &__item { |
| | | display: flex; |
| | | align-items: stretch; |
| | | } |
| | | |
| | | &__axis { |
| | | width: 40rpx; |
| | | display: flex; |
| | | flex-direction: column; |
| | | align-items: center; |
| | | flex-shrink: 0; |
| | | } |
| | | |
| | | &__dot { |
| | | width: 18rpx; |
| | | height: 18rpx; |
| | | margin-top: 8rpx; |
| | | border-radius: 50%; |
| | | background: #D5D9E2; |
| | | box-shadow: 0 0 0 8rpx #ffffff; |
| | | z-index: 1; |
| | | |
| | | &--active { |
| | | background: #2F80FF; |
| | | box-shadow: 0 0 0 8rpx rgba(47, 128, 255, 0.14); |
| | | } |
| | | } |
| | | |
| | | &__line { |
| | | flex: 1; |
| | | width: 2rpx; |
| | | margin-top: 8rpx; |
| | | background: #E5EAF2; |
| | | } |
| | | |
| | | &__content { |
| | | flex: 1; |
| | | padding: 0 0 40rpx 16rpx; |
| | | } |
| | | |
| | | &__time { |
| | | display: block; |
| | | font-size: 26rpx; |
| | | font-weight: 400; |
| | | color: #808692; |
| | | line-height: 1.4; |
| | | } |
| | | |
| | | &__desc { |
| | | display: block; |
| | | margin-top: 18rpx; |
| | | font-size: 40rpx; |
| | | font-weight: 600; |
| | | color: #4A4A4A; |
| | | line-height: 1.45; |
| | | } |
| | | |
| | | &__images { |
| | | display: flex; |
| | | flex-wrap: wrap; |
| | | gap: 16rpx; |
| | | margin-top: 22rpx; |
| | | } |
| | | |
| | | &__image { |
| | | width: 124rpx; |
| | | height: 124rpx; |
| | | border-radius: 12rpx; |
| | | background: #F2F4F7; |
| | | } |
| | | |
| | | &__empty { |
| | | line-height: 50vh; |
| | | text-align: center; |
| | | font-size: 28rpx; |
| | | color: #A0A6B0; |
| | | } |
| | | } |
| | | </style> |