Files
medical-mall/pages/mall/delivery/order-detail.uvue
not-like-juvenile c803a77c8f 修改页面逻辑
2026-02-03 12:01:10 +08:00

1049 lines
32 KiB
Plaintext
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<template>
<view class="delivery-order-detail">
<view class="page-content">
<!-- 返回按钮 -->
<view class="back-header">
<view class="back-box" @click="goBack">
<text class="back-icon"></text>
<text class="back-text">返回</text>
</view>
</view>
<!-- 订单状态 -->
<view class="order-status">
<view class="status-progress">
<view class="progress-item" :class="{ active: order.status >= 1 }">
<view class="progress-dot"></view>
<text class="progress-text">待接单</text>
</view>
<view class="progress-line" :class="{ active: order.status >= 2 }"></view>
<view class="progress-item" :class="{ active: order.status >= 2 }">
<view class="progress-dot"></view>
<text class="progress-text">已接单</text>
</view>
<view class="progress-line" :class="{ active: order.status >= 3 }"></view>
<view class="progress-item" :class="{ active: order.status >= 3 }">
<view class="progress-dot"></view>
<text class="progress-text">取货中</text>
</view>
<view class="progress-line" :class="{ active: order.status >= 4 }"></view>
<view class="progress-item" :class="{ active: order.status >= 4 }">
<view class="progress-dot"></view>
<text class="progress-text">已取货</text>
</view>
<view class="progress-line" :class="{ active: order.status >= 5 }"></view>
<view class="progress-item" :class="{ active: order.status >= 5 }">
<view class="progress-dot"></view>
<text class="progress-text">已送达</text>
</view>
</view>
<text class="status-desc">{{ getStatusDesc() }}</text>
</view>
<!-- 配送信息 -->
<view class="delivery-info">
<view class="section-title">配送信息</view>
<view class="delivery-route">
<view class="route-item pickup">
<view class="route-icon">📦</view>
<view class="route-content">
<text class="route-title">取货地址</text>
<text class="route-address">{{ merchant.contact_name ? (merchant.contact_name + ' · ') : '' }}{{ merchant.contact_phone }}</text>
<text class="route-detail">{{ pickupAddress }}</text>
</view>
<!-- 只在取货中状态且订单未完成时显示按钮 -->
<button v-if="order.status === 3 && order.status < 5" class="route-action" @click="confirmPickup">确认取货</button>
</view>
<view class="route-line"></view>
<view class="route-item delivery">
<view class="route-icon">🏠</view>
<view class="route-content">
<text class="route-title">送货地址</text>
<text class="route-address">{{ getDeliveryAddress().name ? (getDeliveryAddress().name + ' · ') : '' }}{{ (order.delivery_address as UTSJSONObject)['phone'] || '' }}</text>
<text class="route-detail">{{ getDeliveryAddress().detail }}</text>
</view>
<!-- 只在已取货状态且订单未完成时显示按钮 -->
<button v-if="order.status === 4 && order.status < 5" class="route-action" @click="confirmDelivery">确认送达</button>
</view>
</view>
<view class="delivery-distance">
<text class="distance-label">配送距离:</text>
<text class="distance-value">{{ deliveryInfo.distance }}km</text>
<text class="time-label">预计时长:</text>
<text class="time-value">{{ deliveryInfo.estimated_time }}分钟</text>
</view>
</view>
<!-- 订单商品 -->
<view class="order-products">
<view class="section-title">订单商品</view>
<view class="shop-info">
<text class="shop-name">{{ merchant.shop_name }}</text>
<text class="order-no">订单号:{{ order.order_no }}</text>
</view>
<view v-for="item in orderItems" :key="item.id" class="product-item">
<image :src="item.product_image || '/static/default-product.png'" class="product-image" />
<view class="product-info">
<text class="product-name">{{ item.product_name }}</text>
<text v-if="item.sku_specifications" class="product-spec">{{ getSpecText(item.sku_specifications) }}</text>
<view class="price-quantity">
<text class="product-price">¥{{ item.price }}</text>
<text class="product-quantity">×{{ item.quantity }}</text>
</view>
</view>
</view>
<view class="order-summary">
<view class="summary-item">
<text class="summary-label">商品总额</text>
<text class="summary-value">¥{{ order.total_amount }}</text>
</view>
<view class="summary-item">
<text class="summary-label">配送费</text>
<text class="summary-value delivery-fee">¥{{ order.delivery_fee }}</text>
</view>
<view class="summary-item total">
<text class="summary-label">实付金额</text>
<text class="summary-value">¥{{ order.actual_amount }}</text>
</view>
</view>
</view>
<!-- 配送备注 -->
<view class="delivery-notes">
<view class="section-title">配送备注</view>
<view class="note-item">
<text class="note-label">顾客备注:</text>
<text class="note-content">{{ customerNote || '无备注' }}</text>
</view>
<view class="note-item">
<text class="note-label">商家备注:</text>
<text class="note-content">{{ merchantNote || '无备注' }}</text>
</view>
<!-- 只在订单未完成时显示输入框 -->
<view v-if="order.status < 5" class="note-item">
<text class="note-label">配送备注:</text>
<input v-model="deliveryNote" class="note-input" placeholder="请输入配送备注" />
</view>
<!-- 如果订单已完成,显示存储的备注 -->
<view v-else-if="deliveryNote" class="note-item">
<text class="note-label">配送备注:</text>
<text class="note-content">{{ deliveryNote }}</text>
</view>
</view>
<!-- 联系方式 -->
<view class="contact-section">
<view class="section-title">联系方式</view>
<view class="contact-list">
<view class="contact-item" @click="callCustomer">
<view class="contact-icon">📞</view>
<view class="contact-info">
<text class="contact-name">联系顾客</text>
<text class="contact-phone">{{ (order.delivery_address as UTSJSONObject)['phone'] || '' }}</text>
</view>
</view>
<view class="contact-item" @click="callMerchant">
<view class="contact-icon">🏪</view>
<view class="contact-info">
<text class="contact-name">联系商家</text>
<text class="contact-phone">{{ merchant.contact_phone }}</text>
</view>
</view>
</view>
</view>
</view>
<!-- 底部操作 -->
<view class="bottom-actions">
<!-- 只在待接单状态且订单未完成时显示接受/拒绝订单按钮 -->
<button v-if="order.status === 1" class="action-btn accept" @click="acceptOrder">接受订单</button>
<button v-if="order.status === 1" class="action-btn reject" @click="rejectOrder">拒绝订单</button>
<!-- 只在已接单状态且订单未完成时显示“前往取货”和“正在取货”按钮 -->
<button v-if="order.status === 2" class="action-btn navigate" @click="startNavigation">前往取货</button>
<button v-if="order.status === 2" class="action-btn complete" @click="confirmArrivedAtPickup">正在取货</button>
<!-- 只在取货中状态且订单未完成时显示“确认取货”按钮 -->
<button v-if="order.status === 3" class="action-btn complete" @click="confirmPickup">确认取货</button>
<!-- 只在已取货状态且订单未完成时显示“确认送达”按钮 -->
<button v-if="order.status === 4" class="action-btn complete" @click="confirmDelivery">确认送达</button>
<!-- 始终显示联系客服按钮 -->
<button class="action-btn contact" @click="contactService">联系客服</button>
</view>
</view>
</template>
<script lang="uts">
import supa, { supaReady } from '@/components/supadb/aksupainstance.uts'
import { getCurrentUserId } from '@/utils/store.uts'
export default {
data() {
return {
order: {
id: '',
order_no: '',
user_id: '',
merchant_id: '',
status: 0, // 👈 从 URL 参数获取
total_amount: 0,
discount_amount: 0,
delivery_fee: 0,
actual_amount: 0,
payment_method: 0,
payment_status: 0,
delivery_address: {},
created_at: ''
},
orderItems: [],
merchant: {
id: '',
user_id: '',
shop_name: '',
shop_logo: '',
shop_banner: '',
shop_description: '',
contact_name: '',
contact_phone: '',
shop_status: 0,
rating: 0,
total_sales: 0,
created_at: ''
},
deliveryInfo: {
distance: 0,
estimated_time: 0,
courier_id: '',
pickup_time: '',
delivery_time: ''
},
pickupAddress: '',
customerNote: '',
merchantNote: '',
deliveryNote: '',
}
},
onLoad(options: any) {
const orderId = options.id as string
// ✅ 从 URL 参数获取 status
const status = parseInt(options.status as string) || 0
if (orderId) {
this.order.id = orderId
this.order.status = status // ✅ 设置传入的状态
this.loadOrderDetail(orderId)
}
},
methods: {
goBack() {
uni.navigateBack()
},
async loadOrderDetail(orderId: string) {
const originalStatus = this.order.status
try {
// 使用 1.5s 超时策略包装 supaReady防止 session 刷新卡死页面
const readyPromise = supaReady
const timeoutPromise = new Promise((_, reject) => setTimeout(() => reject(new Error('supaReady timeout')), 1500))
try {
await Promise.race([readyPromise, timeoutPromise])
} catch (e) {
console.warn('loadOrderDetail: supaReady timeout, proceeding with current session', e)
}
console.log('loadOrderDetail: start loading', { orderId, originalStatus })
uni.showLoading({ title: '加载中...' })
// 配送端详情页逻辑:
// 1. 先尝试直接查 ml_orders (传入的是真实订单ID时)
// 2. 如果没查到,尝试从 ml_delivery_tasks 查找 (传入的是任务ID时)
// 3. 如果从任务表查到了,再拿其关联的 order_id 回查 ml_orders
let targetOrderId = orderId
let orderRes: any = null
let taskData: any = null
const isUuid = /^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$/.test(orderId)
const isNumber = /^\d+$/.test(orderId)
console.log('loadOrderDetail: detect id format', { isUuid, isNumber })
// 步骤 A: 查 ml_orders
if (isUuid) {
orderRes = await supa.from('ml_orders').select('*').eq('id', orderId).limit(1).execute()
} else if (isNumber) {
orderRes = await supa.from('ml_orders').select('*').eq('cid', Number(orderId)).limit(1).execute()
} else {
orderRes = await supa.from('ml_orders').select('*').eq('order_no', orderId).limit(1).execute()
}
// 步骤 B: 如果订单没查到,尝试从任务表查
if (!(orderRes && Array.isArray(orderRes.data) && orderRes.data.length > 0)) {
console.log('loadOrderDetail: order not found directly, checking ml_delivery_tasks')
const taskRes: any = await supa.from('ml_delivery_tasks').select('*').eq('id', orderId).limit(1).execute()
if (taskRes && Array.isArray(taskRes.data) && taskRes.data.length > 0) {
taskData = taskRes.data[0]
console.log('loadOrderDetail: found task record', taskData)
if (taskData.order_id) {
targetOrderId = taskData.order_id
console.log('loadOrderDetail: found linked order_id in task', targetOrderId)
orderRes = await supa.from('ml_orders').select('*').eq('id', targetOrderId).limit(1).execute()
}
}
}
console.log('loadOrderDetail: final orderRes data length=', orderRes?.data?.length)
if (orderRes && Array.isArray(orderRes.data) && orderRes.data.length > 0) {
const row = orderRes.data[0]
console.log('loadOrderDetail: discovered order details', row)
// 如果没有预先获取 taskData在这里查一次
if (!taskData) {
const dtQuery: any = await supa.from('ml_delivery_tasks').select('*').eq('order_id', row.id).limit(1).execute()
if (dtQuery && Array.isArray(dtQuery.data) && dtQuery.data.length > 0) taskData = dtQuery.data[0]
}
let shipping: UTSJSONObject = {} as UTSJSONObject
try {
const rawShipping = row.shipping_address
if (typeof rawShipping == 'string' && (rawShipping as string).startsWith('{')) {
shipping = JSON.parse(rawShipping as string) as UTSJSONObject
} else if (rawShipping != null && typeof rawShipping == 'object') {
shipping = rawShipping as UTSJSONObject
}
} catch (e) {
console.warn('loadOrderDetail: parse shipping_address failed', e)
}
this.order = Object.assign(this.order, {
id: row.id,
order_no: row.order_no || '',
user_id: row.user_id || '',
merchant_id: row.merchant_id || '',
status: Number(row.order_status) || originalStatus,
total_amount: Number(row.total_amount || row.product_amount || 0),
discount_amount: Number(row.discount_amount || 0),
delivery_fee: Number(row.shipping_fee || row.shipping_fee || 0),
actual_amount: Number(row.paid_amount || row.actual_amount || 0),
payment_method: row.payment_method || 0,
payment_status: row.payment_status || 0,
delivery_address: {
name: shipping['name'] || shipping['recipient'] || '',
phone: shipping['phone'] || shipping['mobile'] || '',
detail: shipping['detail'] || shipping['address'] || JSON.stringify(shipping)
},
created_at: row.created_at || ''
})
this.customerNote = row.remark || ''
this.merchantNote = row.merchant_memo || ''
// 读取订单商品 - 使用从主表获得的真实 order id
const realOrderId = row.id
const itemsRes: any = await supa.from('ml_order_items').select('*').eq('order_id', realOrderId).execute()
console.log('loadOrderDetail: itemsRes=', itemsRes)
if (itemsRes && Array.isArray(itemsRes.data)) {
this.orderItems = itemsRes.data.map((it:any) => ({
id: it.id,
order_id: it.order_id,
product_id: it.product_id,
sku_id: it.sku_id,
product_name: it.product_name,
sku_specifications: it.specifications || it.sku_name || null,
price: Number(it.price) || 0,
quantity: Number(it.quantity) || 0,
total_amount: Number(it.total_amount) || 0,
product_image: it.image_url || it.product_image || ''
}))
}
// 读取商家店铺信息ml_shops
if (row.merchant_id) {
const shopRes: any = await supa.from('ml_shops').select('*').eq('merchant_id', row.merchant_id).limit(1).execute()
console.log('loadOrderDetail: shopRes=', shopRes)
if (shopRes && Array.isArray(shopRes.data) && shopRes.data.length > 0) {
const s = shopRes.data[0]
this.merchant = Object.assign(this.merchant, {
id: s.id || '',
user_id: s.merchant_id || '',
shop_name: s.shop_name || s.name || '',
shop_logo: s.shop_logo || '',
shop_banner: s.shop_banner || '',
shop_description: s.description || s.shop_description || '',
contact_name: s.contact_name || '',
contact_phone: s.contact_phone || '',
shop_status: s.status || 0,
rating: s.rating || 0,
total_sales: s.total_sales || 0,
created_at: s.created_at || ''
})
this.pickupAddress = s.address || ''
}
}
// deliveryInfo 从 ml_delivery_tasks 中读取(如果存在)
if (taskData) {
this.deliveryInfo.distance = Number(taskData.distance) || this.deliveryInfo.distance
this.deliveryInfo.estimated_time = Number(taskData.estimated_time) || this.deliveryInfo.estimated_time
this.deliveryInfo.courier_id = taskData.driver_id || ''
this.deliveryInfo.pickup_time = taskData.pickup_time || ''
this.deliveryInfo.delivery_time = taskData.delivered_time || taskData.delivered_at || ''
}
} else if (taskData) {
// ⚠️ 数据不一致兜底:订单主表查不到,但任务表查到了
console.warn('loadOrderDetail: order missing but task found. Using fallback.')
const parseF = (v: any) => {
if (!v) return { detail: '', name: '', phone: '' }
let o: any = v
if (typeof v === 'string') {
try { o = JSON.parse(v) } catch (e) { o = { detail: v } }
}
return {
detail: o.detail || o.address || o.full_address || '',
name: o.name || o.contact_name || o.recipient_name || '',
phone: o.phone || o.mobile || o.contact_phone || ''
}
}
const pickup = parseF(taskData.pickup_address)
const delivery = parseF(taskData.delivery_address)
const pContact = parseF(taskData.pickup_contact)
const dContact = parseF(taskData.delivery_contact)
this.order = Object.assign(this.order, {
id: taskData.order_id || taskData.id,
order_no: taskData.order_no || taskData.id.substring(0, 8),
status: Number(taskData.status) || originalStatus,
delivery_address: {
name: dContact.name || delivery.name,
phone: dContact.phone || delivery.phone,
detail: delivery.detail
}
})
this.merchant.contact_name = pContact.name || pickup.name
this.merchant.contact_phone = pContact.phone || pickup.phone
this.pickupAddress = pickup.detail
this.deliveryInfo.distance = Number(taskData.distance) || 0
this.deliveryInfo.estimated_time = Number(taskData.estimated_time) || 0
uni.showToast({ title: '已回退从任务记录显示', icon: 'none' })
} else {
console.warn('loadOrderDetail: no order found for id', orderId)
uni.showToast({ title: '未找到订单或任务信息', icon: 'none' })
}
} catch (e) {
console.error('loadOrderDetail db error', e)
uni.showToast({ title: '加载订单失败', icon: 'none' })
} finally {
uni.hideLoading()
}
},
getStatusDesc(): string {
if (this.order.status >= 5) {
return '订单已送达完成'
} else if (this.order.status === 4) {
return '已取货,正在前往送达'
} else if (this.order.status === 3) {
return '配送员正在前往取货'
} else if (this.order.status === 2) {
return '订单已接取'
} else if (this.order.status === 1) {
return '等待配送员接单'
} else {
return '订单状态未知'
}
},
getDeliveryAddress(): any {
return this.order.delivery_address as any
},
getSpecText(specifications: any): string {
if (!specifications) return ''
return Object.keys(specifications).map(key => `${key}: ${specifications[key]}`).join(', ')
},
confirmPickup() {
if (this.order.status < 5) {
uni.showModal({
title: '确认取货',
content: '确认已从商家处取到商品?',
success: async (res) => {
if (res.confirm) {
try {
await supaReady
// 更新 delivery task
const dtRes: any = await supa.from('ml_delivery_tasks').update({ status: 4, pickup_time: new Date().toISOString() }).eq('order_id', this.order.id).execute()
// 同步订单状态
await supa.from('ml_orders').update({ order_status: 4 }).eq('id', this.order.id).execute()
this.order.status = 4
uni.showToast({ title: '取货确认成功', icon: 'success' })
} catch (e) {
console.error('confirmPickup db error', e)
uni.showToast({ title: '取货确认失败', icon: 'none' })
}
}
}
})
}
},
confirmDelivery() {
if (this.order.status < 5) {
uni.showModal({
title: '确认送达',
content: '确认商品已送达到顾客手中?',
success: async (res) => {
if (res.confirm) {
try {
await supaReady
// 更新 delivery task
await supa.from('ml_delivery_tasks').update({ status: 6, delivered_time: new Date().toISOString() }).eq('order_id', this.order.id).execute()
// 更新订单状态为已送达
await supa.from('ml_orders').update({ order_status: 5 }).eq('id', this.order.id).execute()
this.order.status = 5
uni.showToast({ title: '送达确认成功', icon: 'success' })
} catch (e) {
console.error('confirmDelivery db error', e)
uni.showToast({ title: '送达确认失败', icon: 'none' })
}
}
}
})
}
},
acceptOrder() {
if (this.order.status < 5) {
uni.showModal({
title: '接受订单',
content: '确定接受这个配送订单吗?',
success: async (res) => {
if (res.confirm) {
try {
await supaReady
const userId = getCurrentUserId()
let driverId = null
if (userId) {
const dRes: any = await supa.from('ml_delivery_drivers').select('id').eq('user_id', userId).limit(1).execute()
if (dRes && Array.isArray(dRes.data) && dRes.data.length > 0) driverId = dRes.data[0].id
}
// 尝试更新已有 delivery task
const updateRes: any = await supa.from('ml_delivery_tasks').update({ driver_id: driverId, status: 2 }).eq('order_id', this.order.id).execute()
if (!(updateRes && Array.isArray(updateRes.data) && updateRes.data.length > 0)) {
// 若不存在,则插入新的 task
await supa.from('ml_delivery_tasks').insert({ order_id: this.order.id, driver_id: driverId, status: 2, created_at: new Date().toISOString() }).execute()
}
// 更新订单状态
await supa.from('ml_orders').update({ order_status: 2 }).eq('id', this.order.id).execute()
this.order.status = 2
uni.showToast({ title: '订单接受成功', icon: 'success' })
} catch (e) {
console.error('acceptOrder db error', e)
uni.showToast({ title: '接受订单失败', icon: 'none' })
}
}
}
})
}
},
rejectOrder() {
if (this.order.status < 5) {
uni.showModal({
title: '拒绝订单',
content: '确定拒绝这个配送订单吗?',
success: async (res) => {
if (res.confirm) {
try {
await supaReady
// 标记订单为已拒绝order_status = 7
await supa.from('ml_orders').update({ order_status: 7 }).eq('id', this.order.id).execute()
uni.showToast({ title: '订单已拒绝', icon: 'success' })
uni.navigateBack()
} catch (e) {
console.error('rejectOrder db error', e)
uni.showToast({ title: '拒绝订单失败', icon: 'none' })
}
}
}
})
}
},
startNavigation() {
if (this.order.status < 5) {
uni.showToast({
title: '正在启动导航',
icon: 'loading'
})
setTimeout(() => {
uni.showToast({
title: '导航已启动',
icon: 'success'
})
}, 1000)
}
},
callCustomer() {
const phone = this.getDeliveryAddress().phone
uni.makePhoneCall({ phoneNumber: phone })
},
callMerchant() {
uni.makePhoneCall({ phoneNumber: this.merchant.contact_phone })
},
contactService() {
uni.navigateTo({
url: `/pages/mall/service/chat?orderId=${this.order.id}&type=delivery`
})
}
}
}
</script>
<style>
/* 保持原有样式不变 */
.delivery-order-detail {
background-color: #f5f5f5;
min-height: 100vh;
display: flex;
flex-direction: column;
padding-bottom: 0; /* padding moved to .page-content to avoid double spacing */
}
.page-content {
flex: 1;
overflow: auto;
padding-bottom: 300rpx; /* 留出底部操作区域高度,确保联系商家等内容可滚动到最下 */
}
/* ... 其余样式保持原样 ... */
/* 返回按钮头部 */
.back-header {
background-color: #fff;
padding: 20rpx 30rpx;
position: relative;
height: 80rpx;
display: flex;
align-items: center;
}
.back-box {
position: absolute;
left: 30rpx;
top: 50%;
transform: translateY(-50%);
display: flex;
align-items: center;
cursor: pointer;
padding: 10rpx;
border-radius: 8rpx;
transition: background-color 0.2s ease;
}
.back-box:active {
background-color: #f0f0f0;
}
.back-icon {
font-size: 36rpx;
color: #333;
margin-right: 5rpx;
}
.back-text {
font-size: 28rpx;
color: #333;
font-weight: 500;
}
.order-status {
background-color: #fff;
padding: 40rpx 30rpx;
margin-bottom: 20rpx;
}
.status-progress {
display: flex;
align-items: center;
margin-bottom: 20rpx;
}
.progress-item {
display: flex;
flex-direction: column;
align-items: center;
flex: 1;
}
.progress-dot {
width: 24rpx;
height: 24rpx;
border-radius: 50%;
background-color: #ddd;
margin-bottom: 10rpx;
}
.progress-item.active .progress-dot {
background-color: #4caf50;
}
.progress-text {
font-size: 22rpx;
color: #666;
}
.progress-item.active .progress-text {
color: #4caf50;
font-weight: bold;
}
.progress-line {
height: 2rpx;
background-color: #ddd;
flex: 1;
margin: 0 20rpx;
margin-bottom: 32rpx;
}
.progress-line.active {
background-color: #4caf50;
}
.status-desc {
font-size: 26rpx;
color: #666;
text-align: center;
}
.delivery-info, .order-products, .delivery-notes, .contact-section {
background-color: #fff;
padding: 30rpx;
margin-bottom: 20rpx;
}
.section-title {
font-size: 30rpx;
font-weight: bold;
color: #333;
margin-bottom: 25rpx;
}
.delivery-route {
margin-bottom: 30rpx;
}
.route-item {
display: flex;
align-items: flex-start;
padding: 25rpx 0;
}
.route-icon {
font-size: 36rpx;
margin-right: 20rpx;
margin-top: 10rpx;
}
.route-content {
flex: 1;
}
.route-title {
font-size: 28rpx;
font-weight: bold;
color: #333;
margin-bottom: 10rpx;
}
.route-address {
font-size: 26rpx;
color: #666;
margin-bottom: 8rpx;
}
.route-detail {
font-size: 24rpx;
color: #999;
line-height: 1.4;
}
.route-action {
padding: 15rpx 30rpx;
background-color: #4caf50;
color: #fff;
border-radius: 25rpx;
font-size: 24rpx;
border: none;
}
.route-line {
width: 2rpx;
height: 40rpx;
background-color: #ddd;
margin-left: 38rpx;
}
.delivery-distance {
display: flex;
align-items: center;
padding: 20rpx;
background-color: #f8f9fa;
border-radius: 10rpx;
}
.distance-label, .time-label {
font-size: 24rpx;
color: #666;
margin-right: 10rpx;
}
.distance-value, .time-value {
font-size: 24rpx;
color: #333;
font-weight: bold;
margin-right: 30rpx;
}
.shop-info {
display: flex;
justify-content: space-between;
align-items: center;
padding: 20rpx 0;
border-bottom: 1rpx solid #f5f5f5;
margin-bottom: 25rpx;
}
.shop-name {
font-size: 28rpx;
color: #333;
font-weight: bold;
}
.order-no {
font-size: 24rpx;
color: #333;
font-weight: bold;
}
.product-item {
display: flex;
padding: 25rpx 0;
border-bottom: 1rpx solid #f5f5f5;
}
.product-item:last-child {
border-bottom: none;
}
.product-image {
width: 100rpx;
height: 100rpx;
border-radius: 8rpx;
margin-right: 20rpx;
}
.product-info {
flex: 1;
}
.product-name {
font-size: 26rpx;
color: #333;
margin-bottom: 8rpx;
line-height: 1.3;
}
.product-spec {
font-size: 22rpx;
color: #999;
margin-bottom: 12rpx;
}
.price-quantity {
display: flex;
justify-content: space-between;
align-items: center;
}
.product-price {
font-size: 26rpx;
color: #ff4444;
font-weight: bold;
}
.product-quantity {
font-size: 24rpx;
color: #666;
}
.order-summary {
margin-top: 25rpx;
padding-top: 20rpx;
border-top: 1rpx solid #f5f5f5;
}
.summary-item {
display: flex;
justify-content: space-between;
align-items: center;
padding: 8rpx 0;
}
.summary-label {
font-size: 26rpx;
color: #666;
}
.summary-value {
font-size: 26rpx;
color: #333;
}
.summary-value.delivery-fee {
color: #4caf50;
}
.summary-item.total {
border-top: 1rpx solid #f5f5f5;
margin-top: 10rpx;
padding-top: 15rpx;
}
.summary-item.total .summary-label,
.summary-item.total .summary-value {
font-weight: bold;
font-size: 28rpx;
color: #ff4444;
}
.note-item {
margin-bottom: 20rpx;
}
.note-label {
font-size: 26rpx;
color: #666;
margin-bottom: 8rpx;
}
.note-content {
font-size: 26rpx;
color: #333;
line-height: 1.4;
}
.note-input {
width: 100%;
padding: 15rpx;
border: 1rpx solid #ddd;
border-radius: 8rpx;
font-size: 26rpx;
background-color: #f8f9fa;
}
.contact-list {
display: flex;
gap: 30rpx;
}
.contact-item {
flex: 1;
display: flex;
flex-direction: column; /* 改为垂直排列避免空间不足 */
align-items: center;
justify-content: center;
padding: 20rpx;
background-color: #f8f9fa;
border-radius: 10rpx;
min-height: 120rpx;
}
.contact-icon {
font-size: 36rpx;
margin-bottom: 10rpx;
}
.contact-info {
flex: 1;
display: flex;
flex-direction: column;
align-items: center;
text-align: center;
}
.contact-name {
font-size: 24rpx;
color: #666;
margin-bottom: 5rpx;
}
.contact-phone {
font-size: 26rpx;
color: #007aff;
font-weight: bold;
}
.bottom-actions {
position: fixed;
bottom: 0;
left: 0;
right: 0;
background-color: #fff;
padding: 25rpx 30rpx;
box-shadow: 0 -2rpx 10rpx rgba(0, 0, 0, 0.1);
display: flex;
gap: 15rpx;
flex-wrap: wrap;
}
.action-btn {
flex: 1;
min-width: 120rpx;
height: 70rpx;
border-radius: 35rpx;
font-size: 26rpx;
border: none;
margin: 5rpx;
box-sizing: border-box;
}
.action-btn.accept, .action-btn.complete {
background-color: #4caf50;
color: #fff;
}
.action-btn.reject {
background-color: #ff4444;
color: #fff;
}
.action-btn.navigate {
background-color: #2196f3;
color: #fff;
}
.action-btn.contact {
background-color: #ffa726;
color: #fff;
}
</style>