混凝土运输管理微信小程序、替班
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

812 lines
18 KiB

<template>
<view class="page">
<navbar title="订单详情" leftClick @leftClick="$utils.navigateBack" />
<view class="content">
<!-- 订单状态卡片 -->
<view class="status-card">
<view class="status-header">
<view class="status-icon" :class="orderInfo.status">
<uv-icon :name="getStatusIcon(orderInfo.status)" size="30" color="#fff"></uv-icon>
</view>
<view class="status-info">
<view class="status-text">{{ getStatusText(orderInfo.status) }}</view>
<view class="status-desc">{{ getStatusDesc(orderInfo.status) }}</view>
</view>
</view>
<!-- 进度条 -->
<view v-if="orderInfo.status === 'processing'" class="progress-container">
<view class="progress-bar">
<view class="progress-fill" :style="{width: orderInfo.progress + '%'}"></view>
</view>
<view class="progress-text">{{ orderInfo.progress }}%</view>
</view>
</view>
<!-- 订单基本信息 -->
<view class="order-info-card">
<view class="section-title">
<view class="title-bar"></view>
<text>订单信息</text>
</view>
<view class="info-item">
<view class="label">订单编号</view>
<view class="value order-no-value">
<text>{{ orderInfo.orderNo }}</text>
<uv-icon name="copy" size="16" color="#007AFF" @click="copyOrderNo"></uv-icon>
</view>
</view>
<view class="info-item">
<view class="label">项目名称</view>
<view class="value">{{ orderInfo.projectName }}</view>
</view>
<view class="info-item">
<view class="label">工作地址</view>
<view class="value address-value">
<text>{{ orderInfo.workAddress }}</text>
<uv-icon name="map-pin" size="16" color="#007AFF" @click="openMap"></uv-icon>
</view>
</view>
<view class="info-item">
<view class="label">服务类型</view>
<view class="value">{{ orderInfo.serviceType }}</view>
</view>
<view class="info-item">
<view class="label">工作时间</view>
<view class="value">{{ orderInfo.workTime }}</view>
</view>
<view class="info-item">
<view class="label">混凝土方量</view>
<view class="value">{{ orderInfo.volume }}m³</view>
</view>
<view class="info-item">
<view class="label">预计趟数</view>
<view class="value">{{ orderInfo.tripCount }}趟</view>
</view>
</view>
<!-- 联系人信息 -->
<view class="contact-card">
<view class="section-title">
<view class="title-bar"></view>
<text>联系人信息</text>
</view>
<view class="contact-info">
<view class="contact-item">
<view class="contact-label">联系人</view>
<view class="contact-value">{{ orderInfo.contactName }}</view>
</view>
<view class="contact-item">
<view class="contact-label">联系电话</view>
<view class="contact-value">
<text>{{ orderInfo.contactPhone }}</text>
<uv-icon name="phone" size="20" color="#007AFF" @click="makeCall"></uv-icon>
</view>
</view>
</view>
</view>
<!-- 司机信息 -->
<view v-if="orderInfo.driverInfo" class="driver-card">
<view class="section-title">
<view class="title-bar"></view>
<text>司机信息</text>
</view>
<view class="driver-info">
<view class="driver-avatar">
<image :src="orderInfo.driverInfo.avatar" mode="aspectFill"></image>
</view>
<view class="driver-details">
<view class="driver-name">{{ orderInfo.driverInfo.name }}</view>
<view class="driver-phone">{{ orderInfo.driverInfo.phone }}</view>
<view class="driver-vehicle">车牌号:{{ orderInfo.driverInfo.vehicleNo }}</view>
<view class="driver-rating">
<text>评分:{{ orderInfo.driverInfo.rating }}分</text>
<text>完成订单:{{ orderInfo.driverInfo.completedOrders }}单</text>
</view>
</view>
<view class="driver-actions">
<view class="action-btn" @click="callDriver">
<uv-icon name="phone" size="20" color="#fff"></uv-icon>
</view>
</view>
</view>
</view>
<!-- 订单步骤 -->
<view class="steps-container">
<view class="section-title">
<view class="title-bar"></view>
<text>订单进度</text>
<view v-if="orderInfo.status === 'processing' || orderInfo.status === 'completed'" class="track-btn" @click="viewTrack">
<text>查看轨迹</text>
<uv-icon name="arrow-right" size="14" color="#007AFF"></uv-icon>
</view>
</view>
<uv-steps
activeColor="#007AFF"
:current="stepsCurrent"
direction="column">
<uv-steps-item
v-for="(item, index) in steps"
:key="index"
:title="item.title"
:desc="item.desc">
</uv-steps-item>
</uv-steps>
</view>
<!-- 费用明细 -->
<view class="cost-card">
<view class="section-title">
<view class="title-bar"></view>
<text>费用明细</text>
</view>
<view class="cost-item">
<view class="cost-label">单价</view>
<view class="cost-value">{{ orderInfo.unitPrice }}元/趟</view>
</view>
<view class="cost-item">
<view class="cost-label">预计趟数</view>
<view class="cost-value">{{ orderInfo.tripCount }}趟</view>
</view>
<view class="cost-item">
<view class="cost-label">小计</view>
<view class="cost-value">{{ orderInfo.subtotal }}元</view>
</view>
<view class="cost-item total">
<view class="cost-label">总金额</view>
<view class="cost-value total-amount">¥{{ orderInfo.totalAmount }}</view>
</view>
</view>
<!-- 快捷操作 -->
<view class="quick-actions">
<view class="action-item" @click="shareOrder">
<uv-icon name="share" size="24" color="#007AFF"></uv-icon>
<text>分享</text>
</view>
<view class="action-item" @click="copyOrderNo">
<uv-icon name="copy" size="24" color="#007AFF"></uv-icon>
<text>复制</text>
</view>
<view v-if="orderInfo.status === 'processing' || orderInfo.status === 'completed'" class="action-item" @click="viewTrack">
<uv-icon name="map" size="24" color="#007AFF"></uv-icon>
<text>轨迹</text>
</view>
<view class="action-item" @click="contactCustomer">
<uv-icon name="phone" size="24" color="#007AFF"></uv-icon>
<text>联系</text>
</view>
</view>
<!-- 操作按钮 -->
<view class="action-buttons">
<view v-if="orderInfo.status === 'pending'" class="btn-group">
<view class="btn secondary" @click="cancelOrder">取消订单</view>
<view class="btn primary" @click="confirmOrder">确认接单</view>
</view>
<view v-if="orderInfo.status === 'processing'" class="btn-group">
<view class="btn secondary" @click="contactCustomer">联系客户</view>
<view class="btn primary" @click="completeOrder">完成订单</view>
</view>
<view v-if="orderInfo.status === 'completed'" class="btn-group">
<view class="btn secondary" @click="rateOrder">评价订单</view>
<view class="btn primary" @click="reorder">再次下单</view>
</view>
<view v-if="orderInfo.status === 'cancelled'" class="btn-group">
<view class="btn primary full-width" @click="reorder">重新下单</view>
</view>
</view>
</view>
</view>
</template>
<script>
import navbar from '@/components/base/navbar.vue'
export default {
name: 'OrderDetail',
components: {
navbar
},
data() {
return {
orderId: '',
stepsCurrent: 2,
orderInfo: {
orderNo: 'ORD202401150001',
projectName: '长沙市雨花区建设项目混凝土运输',
workAddress: '长沙市雨花区某某路123号建设工地',
serviceType: '泵车运输',
workTime: '2024-01-16 08:00 - 18:00',
volume: '200',
tripCount: '8',
unitPrice: '350',
subtotal: '2800',
totalAmount: '2800',
contactName: '张工',
contactPhone: '13800138000',
status: 'processing', // pending, processing, completed, cancelled
progress: 65,
driverInfo: {
name: '李师傅',
phone: '13900139000',
avatar: '/static/re/logo.png',
vehicleNo: '湘A12345',
rating: 4.8,
completedOrders: 156
}
},
steps: [
{
title: '订单确认',
desc: '2024-01-15 14:30'
},
{
title: '司机接单',
desc: '2024-01-15 15:00'
},
{
title: '前往工地',
desc: '2024-01-16 07:30'
},
{
title: '开始运输',
desc: '预计 2024-01-16 08:00'
},
{
title: '运输完成',
desc: '预计 2024-01-16 18:00'
}
]
}
},
onLoad(options) {
this.orderId = options.id || '1';
uni.setNavigationBarTitle({
title: '订单详情'
});
// 根据订单ID加载订单详情
this.loadOrderDetail();
},
methods: {
loadOrderDetail() {
// 模拟加载订单详情
console.log('加载订单详情:', this.orderId);
// 根据订单ID设置不同的订单状态和信息
if (this.orderId === '1') {
this.orderInfo.status = 'pending';
this.stepsCurrent = 0;
} else if (this.orderId === '2') {
this.orderInfo.status = 'processing';
this.stepsCurrent = 2;
} else if (this.orderId === '3') {
this.orderInfo.status = 'completed';
this.stepsCurrent = 4;
}
},
getStatusIcon(status) {
switch(status) {
case 'pending': return 'clock';
case 'processing': return 'play-circle';
case 'completed': return 'checkmark-circle';
case 'cancelled': return 'close-circle';
default: return 'help-circle';
}
},
getStatusText(status) {
switch(status) {
case 'pending': return '待接单';
case 'processing': return '运输中';
case 'completed': return '已完成';
case 'cancelled': return '已取消';
default: return '未知状态';
}
},
getStatusDesc(status) {
switch(status) {
case 'pending': return '等待司机接单';
case 'processing': return '司机正在运输中';
case 'completed': return '订单已完成';
case 'cancelled': return '订单已取消';
default: return '';
}
},
openMap() {
uni.openLocation({
latitude: 28.2282,
longitude: 112.9388,
name: this.orderInfo.projectName,
address: this.orderInfo.workAddress
});
},
makeCall() {
uni.makePhoneCall({
phoneNumber: this.orderInfo.contactPhone
});
},
callDriver() {
uni.makePhoneCall({
phoneNumber: this.orderInfo.driverInfo.phone
});
},
cancelOrder() {
uni.showModal({
title: '确认取消',
content: '确定要取消这个订单吗?',
success: (res) => {
if (res.confirm) {
uni.showToast({
title: '订单已取消',
icon: 'success'
});
this.orderInfo.status = 'cancelled';
}
}
});
},
confirmOrder() {
uni.showModal({
title: '确认接单',
content: '确定要接取这个订单吗?',
success: (res) => {
if (res.confirm) {
uni.showToast({
title: '接单成功',
icon: 'success'
});
this.orderInfo.status = 'processing';
this.stepsCurrent = 2;
}
}
});
},
contactCustomer() {
this.makeCall();
},
completeOrder() {
uni.showModal({
title: '确认完成',
content: '确定已完成这个订单吗?',
success: (res) => {
if (res.confirm) {
uni.showToast({
title: '订单已完成',
icon: 'success'
});
this.orderInfo.status = 'completed';
this.stepsCurrent = 4;
}
}
});
},
rateOrder() {
uni.showToast({
title: '评价功能开发中',
icon: 'none'
});
},
reorder() {
uni.navigateTo({
url: '/pages_order/staff/create'
});
},
// 分享订单
shareOrder() {
uni.share({
provider: 'weixin',
scene: 'WXSceneSession',
type: 0,
title: '订单详情',
summary: `订单号:${this.orderInfo.orderNo}`,
targetUrl: `pages_order/order/orderDetail?id=${this.orderId}`,
success: function (res) {
uni.showToast({
title: '分享成功',
icon: 'success'
});
},
fail: function (err) {
console.log('分享失败:', err);
}
});
},
// 复制订单号
copyOrderNo() {
uni.setClipboardData({
data: this.orderInfo.orderNo,
success: function () {
uni.showToast({
title: '订单号已复制',
icon: 'success'
});
}
});
},
// 查看运输轨迹
viewTrack() {
if (this.orderInfo.status === 'processing' || this.orderInfo.status === 'completed') {
uni.navigateTo({
url: `/pages_order/order/track?id=${this.orderId}`
});
} else {
uni.showToast({
title: '订单未开始运输',
icon: 'none'
});
}
}
}
}
</script>
<style scoped lang="scss">
.page {
display: flex;
flex-direction: column;
height: 100vh;
background-color: #f5f5f5;
}
.content {
padding: 20rpx;
min-height: 100vh;
background-color: #f5f5f5;
padding-bottom: 120rpx;
}
.status-card {
background-color: #fff;
border-radius: 15rpx;
padding: 30rpx;
margin-bottom: 20rpx;
box-shadow: 0 4rpx 20rpx rgba(0, 0, 0, 0.1);
.status-header {
display: flex;
align-items: center;
gap: 20rpx;
.status-icon {
width: 80rpx;
height: 80rpx;
border-radius: 40rpx;
display: flex;
align-items: center;
justify-content: center;
&.pending {
background-color: #ff9500;
}
&.processing {
background-color: #007AFF;
}
&.completed {
background-color: #34c759;
}
&.cancelled {
background-color: #ff3b30;
}
}
.status-info {
flex: 1;
.status-text {
font-size: 36rpx;
font-weight: bold;
color: #333;
margin-bottom: 8rpx;
}
.status-desc {
font-size: 28rpx;
color: #666;
}
}
}
.progress-container {
margin-top: 30rpx;
.progress-bar {
height: 8rpx;
background-color: #f0f0f0;
border-radius: 4rpx;
overflow: hidden;
margin-bottom: 10rpx;
.progress-fill {
height: 100%;
background-color: #007AFF;
transition: width 0.3s;
}
}
.progress-text {
font-size: 24rpx;
color: #007AFF;
text-align: right;
}
}
}
.section-title {
display: flex;
align-items: center;
gap: 15rpx;
margin-bottom: 30rpx;
font-size: 32rpx;
font-weight: bold;
color: #333;
justify-content: space-between;
.title-bar {
width: 8rpx;
height: 30rpx;
background-color: #007AFF;
border-radius: 4rpx;
}
.track-btn {
display: flex;
align-items: center;
gap: 5rpx;
font-size: 26rpx;
color: #007AFF;
font-weight: normal;
}
}
.order-info-card,
.contact-card,
.driver-card,
.steps-container,
.cost-card {
background-color: #fff;
border-radius: 15rpx;
padding: 30rpx;
margin-bottom: 20rpx;
box-shadow: 0 2rpx 10rpx rgba(0, 0, 0, 0.05);
}
.info-item {
display: flex;
justify-content: space-between;
align-items: center;
padding: 20rpx 0;
border-bottom: 1rpx solid #f0f0f0;
&:last-child {
border-bottom: none;
}
.label {
font-size: 28rpx;
color: #666;
width: 200rpx;
}
.value {
flex: 1;
font-size: 28rpx;
color: #333;
text-align: right;
&.address-value {
display: flex;
align-items: center;
justify-content: flex-end;
gap: 10rpx;
}
&.order-no-value {
display: flex;
align-items: center;
justify-content: flex-end;
gap: 10rpx;
}
}
}
.contact-info {
.contact-item {
display: flex;
justify-content: space-between;
align-items: center;
padding: 15rpx 0;
.contact-label {
font-size: 28rpx;
color: #666;
}
.contact-value {
display: flex;
align-items: center;
gap: 15rpx;
font-size: 28rpx;
color: #333;
}
}
}
.driver-info {
display: flex;
align-items: center;
gap: 20rpx;
.driver-avatar {
width: 120rpx;
height: 120rpx;
border-radius: 60rpx;
overflow: hidden;
image {
width: 100%;
height: 100%;
}
}
.driver-details {
flex: 1;
.driver-name {
font-size: 32rpx;
font-weight: bold;
color: #333;
margin-bottom: 8rpx;
}
.driver-phone,
.driver-vehicle {
font-size: 26rpx;
color: #666;
margin-bottom: 5rpx;
}
.driver-rating {
display: flex;
gap: 20rpx;
font-size: 24rpx;
color: #999;
}
}
.driver-actions {
.action-btn {
width: 80rpx;
height: 80rpx;
background-color: #007AFF;
border-radius: 40rpx;
display: flex;
align-items: center;
justify-content: center;
}
}
}
.cost-item {
display: flex;
justify-content: space-between;
align-items: center;
padding: 20rpx 0;
border-bottom: 1rpx solid #f0f0f0;
&:last-child {
border-bottom: none;
}
&.total {
padding-top: 30rpx;
border-top: 2rpx solid #f0f0f0;
margin-top: 20rpx;
}
.cost-label {
font-size: 28rpx;
color: #666;
}
.cost-value {
font-size: 28rpx;
color: #333;
&.total-amount {
font-size: 36rpx;
font-weight: bold;
color: #ff3b30;
}
}
}
.quick-actions {
background-color: #fff;
border-radius: 15rpx;
padding: 30rpx;
margin-bottom: 20rpx;
box-shadow: 0 2rpx 10rpx rgba(0, 0, 0, 0.05);
display: flex;
justify-content: space-around;
.action-item {
display: flex;
flex-direction: column;
align-items: center;
gap: 10rpx;
padding: 20rpx;
border-radius: 10rpx;
transition: background-color 0.3s;
&:active {
background-color: #f0f0f0;
}
text {
font-size: 24rpx;
color: #333;
}
}
}
.action-buttons {
position: fixed;
bottom: 0;
left: 0;
right: 0;
background-color: #fff;
padding: 20rpx;
box-shadow: 0 -2rpx 10rpx rgba(0, 0, 0, 0.1);
.btn-group {
display: flex;
gap: 20rpx;
.btn {
flex: 1;
height: 80rpx;
border-radius: 40rpx;
display: flex;
align-items: center;
justify-content: center;
font-size: 28rpx;
font-weight: bold;
&.primary {
background-color: #007AFF;
color: #fff;
}
&.secondary {
background-color: #f8f8f8;
color: #333;
border: 1rpx solid #e0e0e0;
}
&.full-width {
width: 100%;
}
}
}
}
</style>