Initial commit of akmon project

This commit is contained in:
2026-01-20 08:04:15 +08:00
commit 77a2bab985
1309 changed files with 343305 additions and 0 deletions

View File

@@ -0,0 +1,774 @@
<!-- 配送端 - 订单详情页 -->
<template>
<view class="delivery-order-detail">
<!-- 订单状态 -->
<view class="order-status">
<view class="status-progress">
<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_phone }}</text>
<text class="route-detail">{{ pickupAddress }}</text>
</view>
<button v-if="order.status === 3" 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().phone }}</text>
<text class="route-detail">{{ getDeliveryAddress().detail }}</text>
</view>
<button v-if="order.status === 4" 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 class="note-item">
<text class="note-label">配送备注:</text>
<input v-model="deliveryNote" class="note-input" placeholder="请输入配送备注" />
</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">{{ getDeliveryAddress().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 class="bottom-actions">
<button v-if="order.status === 3" class="action-btn accept" @click="acceptOrder">接受订单</button>
<button v-if="order.status === 3" class="action-btn reject" @click="rejectOrder">拒绝订单</button>
<button v-if="order.status === 4" class="action-btn navigate" @click="startNavigation">开始导航</button>
<button v-if="order.status === 4" class="action-btn complete" @click="completeDelivery">完成配送</button>
<button class="action-btn contact" @click="contactService">联系客服</button>
</view>
</view>
</template>
<script>
import { OrderType, OrderItemType, MerchantType } from '@/types/mall-types.uts'
type DeliveryInfoType = {
distance: number
estimated_time: number
courier_id: string
pickup_time: string
delivery_time: string
}
export default {
data() {
return {
order: {
id: '',
order_no: '',
user_id: '',
merchant_id: '',
status: 0,
total_amount: 0,
discount_amount: 0,
delivery_fee: 0,
actual_amount: 0,
payment_method: 0,
payment_status: 0,
delivery_address: {},
created_at: ''
} as OrderType,
orderItems: [] as Array<OrderItemType & { product_image: string }>,
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: ''
} as MerchantType,
deliveryInfo: {
distance: 0,
estimated_time: 0,
courier_id: '',
pickup_time: '',
delivery_time: ''
} as DeliveryInfoType,
pickupAddress: '',
customerNote: '',
merchantNote: '',
deliveryNote: ''
}
},
onLoad(options: any) {
const orderId = options.orderId as string
if (orderId) {
this.loadOrderDetail(orderId)
}
},
methods: {
loadOrderDetail(orderId: string) {
// 模拟加载订单详情数据
this.order = {
id: orderId,
order_no: 'ORD202401150001',
user_id: 'user_001',
merchant_id: 'merchant_001',
status: 3, // 3:待接单 4:配送中 5:已送达
total_amount: 299.98,
discount_amount: 30.00,
delivery_fee: 8.00,
actual_amount: 277.98,
payment_method: 1,
payment_status: 1,
delivery_address: {
name: '张三',
phone: '13800138000',
detail: '北京市朝阳区某某街道某某小区1号楼101室'
},
created_at: '2024-01-15 14:30:00'
}
this.orderItems = [
{
id: 'item_001',
order_id: orderId,
product_id: 'product_001',
sku_id: 'sku_001',
product_name: '精选好物商品',
sku_specifications: { color: '红色', size: 'M' },
price: 199.99,
quantity: 1,
total_amount: 199.99,
product_image: '/static/product1.jpg'
}
]
this.merchant = {
id: 'merchant_001',
user_id: 'user_001',
shop_name: '优质好店',
shop_logo: '/static/shop-logo.png',
shop_banner: '/static/shop-banner.png',
shop_description: '专注品质生活',
contact_name: '店主小王',
contact_phone: '13800138000',
shop_status: 1,
rating: 4.8,
total_sales: 15680,
created_at: '2023-06-01'
}
this.pickupAddress = '北京市朝阳区商家街道123号'
this.customerNote = '请送到门口,谢谢'
this.merchantNote = '商品易碎,小心搬运'
this.deliveryInfo = {
distance: 3.2,
estimated_time: 25,
courier_id: 'courier_001',
pickup_time: '',
delivery_time: ''
}
},
getStatusDesc(): string {
const statusDescs = [
'',
'',
'',
'等待配送员接单',
'配送员正在前往取货',
'订单已送达完成'
]
return statusDescs[this.order.status] || ''
},
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() {
uni.showModal({
title: '确认取货',
content: '确认已从商家处取到商品?',
success: (res) => {
if (res.confirm) {
this.deliveryInfo.pickup_time = new Date().toISOString()
uni.showToast({
title: '取货确认成功',
icon: 'success'
})
}
}
})
},
confirmDelivery() {
uni.showModal({
title: '确认送达',
content: '确认商品已送达到顾客手中?',
success: (res) => {
if (res.confirm) {
this.order.status = 5
this.deliveryInfo.delivery_time = new Date().toISOString()
uni.showToast({
title: '送达确认成功',
icon: 'success'
})
}
}
})
},
acceptOrder() {
uni.showModal({
title: '接受订单',
content: '确定接受这个配送订单吗?',
success: (res) => {
if (res.confirm) {
this.order.status = 4
uni.showToast({
title: '订单接受成功',
icon: 'success'
})
}
}
})
},
rejectOrder() {
uni.showModal({
title: '拒绝订单',
content: '确定拒绝这个配送订单吗?',
success: (res) => {
if (res.confirm) {
uni.showToast({
title: '订单已拒绝',
icon: 'success'
})
uni.navigateBack()
}
}
})
},
startNavigation() {
// 开启导航功能
uni.showToast({
title: '正在启动导航',
icon: 'loading'
})
// 模拟调用地图导航
setTimeout(() => {
uni.showToast({
title: '导航已启动',
icon: 'success'
})
}, 1000)
},
completeDelivery() {
if (!this.deliveryNote.trim()) {
uni.showToast({
title: '请填写配送备注',
icon: 'none'
})
return
}
this.confirmDelivery()
},
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;
padding-bottom: 160rpx;
}
.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: #666;
}
.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;
align-items: center;
padding: 25rpx;
background-color: #f8f9fa;
border-radius: 10rpx;
}
.contact-icon {
font-size: 32rpx;
margin-right: 15rpx;
}
.contact-info {
flex: 1;
}
.contact-name {
font-size: 26rpx;
color: #333;
margin-bottom: 5rpx;
}
.contact-phone {
font-size: 24rpx;
color: #007aff;
}
.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;
}
.action-btn {
flex: 1;
height: 70rpx;
border-radius: 35rpx;
font-size: 26rpx;
border: none;
}
.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>