小程序订单详情页补齐全状态操作:分配任务、司机、状态流转、申请取消、结算

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
wsb1224 2026-06-23 00:32:29 +08:00
parent 45b82f3f82
commit dd6be0d41a
3 changed files with 457 additions and 10 deletions

View File

@ -45,6 +45,12 @@ Page({
driverList: [],
selectedDriverIndex: -1,
selectedDriverId: null,
taskForm: {
pickup_address: "",
delivery_address: "",
pickup_content: "",
quantity: "1",
},
},
async onLoad(options) {
@ -128,6 +134,18 @@ Page({
if (logistics && logistics.tracking_number) {
this.loadTrace();
}
// 待工厂状态:自动填充任务表单
if (data.order_status === "pending_factory") {
this.setData({
taskForm: {
pickup_address: data.factory_name || "",
delivery_address: data.customer_address || "",
pickup_content: data.customer_name ? data.customer_name + " 订单货物" : "",
quantity: "1",
},
});
}
} catch (error) {
this.setData({ message: error.message || "加载失败" });
} finally {
@ -265,6 +283,308 @@ Page({
});
},
// 任务表单输入
onTaskFormInput: function (e) {
var field = e.currentTarget.dataset.field;
var obj = {};
obj["taskForm." + field] = e.detail.value;
this.setData(obj);
},
// 提交审核(草稿/已驳回 → 待审批)
handleSubmitReview: function () {
var that = this;
var app = getApp();
wx.showModal({
title: "确认提交",
content: "确认提交此订单进入审核?",
confirmColor: "#2563eb",
success: function (res) {
if (!res.confirm) return;
that.setData({ actionLoading: true, message: "" });
app.request({
url: "/api/orders/" + that.data.orderInfo.order_id + "/submit",
method: "POST",
}).then(function () {
wx.showToast({ title: "已提交审核", icon: "success" });
return that.loadDetail();
}).catch(function (error) {
that.setData({ message: error.message || "提交失败" });
}).finally(function () {
that.setData({ actionLoading: false });
});
},
});
},
// 推进待工厂(已审批 → 待工厂处理)
handlePushToFactory: function () {
var that = this;
var app = getApp();
var orderInfo = this.data.orderInfo;
if (!orderInfo) return;
// 自发订单:直接完成
if (orderInfo.tracking_number) {
wx.showModal({
title: "确认",
content: "马甸工厂自发,审批通过后将直接完成订单",
confirmColor: "#2563eb",
success: function (res) {
if (!res.confirm) return;
that._changeStatus("completed", "订单已直接完成");
},
});
return;
}
if (!that.data.selectedFactoryId) {
wx.showToast({ title: "请选择工厂", icon: "none" });
return;
}
if (!that.data.selectedDriverId) {
wx.showToast({ title: "请选择司机", icon: "none" });
return;
}
that.setData({ actionLoading: true, message: "" });
app.request({
url: "/api/orders/" + orderInfo.order_id + "/status",
method: "POST",
data: { target_status: "pending_factory", factory_id: that.data.selectedFactoryId },
}).then(function () {
return app.request({
url: "/api/logistics/tasks",
method: "POST",
data: {
order_id: orderInfo.order_id,
driver_id: Number(that.data.selectedDriverId),
pickup_address: orderInfo.factory_name || "",
delivery_address: orderInfo.customer_address || "",
pickup_content: orderInfo.customer_name ? orderInfo.customer_name + " 订单货物" : "",
quantity: 1,
factory_id: that.data.selectedFactoryId,
},
});
}).then(function () {
wx.showToast({ title: "已推进待工厂并分配司机", icon: "success" });
return that.loadDetail();
}).catch(function (error) {
that.setData({ message: error.message || "操作失败" });
}).finally(function () {
that.setData({ actionLoading: false });
});
},
// 分配任务(待工厂处理 → 待司机接单)
handleAssignTask: function () {
var that = this;
var app = getApp();
var orderInfo = this.data.orderInfo;
if (!orderInfo) return;
if (!that.data.selectedDriverId) {
wx.showToast({ title: "请选择司机", icon: "none" });
return;
}
var form = that.data.taskForm;
if (!form.pickup_address.trim()) {
wx.showToast({ title: "请填写取货地址", icon: "none" });
return;
}
if (!form.delivery_address.trim()) {
wx.showToast({ title: "请填写送货地址", icon: "none" });
return;
}
if (!form.pickup_content.trim()) {
wx.showToast({ title: "请填写取货内容", icon: "none" });
return;
}
that.setData({ actionLoading: true, message: "" });
app.request({
url: "/api/logistics/tasks",
method: "POST",
data: {
order_id: orderInfo.order_id,
driver_id: Number(that.data.selectedDriverId),
pickup_address: form.pickup_address.trim(),
delivery_address: form.delivery_address.trim(),
pickup_content: form.pickup_content.trim(),
quantity: Number(form.quantity) || 1,
factory_id: orderInfo.factory_id || undefined,
},
}).then(function () {
wx.showToast({ title: "任务分配成功", icon: "success" });
return that.loadDetail();
}).catch(function (error) {
that.setData({ message: error.message || "分配失败" });
}).finally(function () {
that.setData({ actionLoading: false });
});
},
// 分配/重新分配司机(待司机接单)
handleAssignDriver: function () {
var that = this;
var app = getApp();
var orderInfo = this.data.orderInfo;
if (!orderInfo) return;
if (!that.data.selectedDriverId) {
wx.showToast({ title: "请选择司机", icon: "none" });
return;
}
var doAssign = function () {
that.setData({ actionLoading: true, message: "" });
app.request({
url: "/api/logistics/tasks",
method: "POST",
data: {
order_id: orderInfo.order_id,
driver_id: Number(that.data.selectedDriverId),
pickup_address: orderInfo.factory_name || "",
delivery_address: orderInfo.customer_address || "",
pickup_content: orderInfo.customer_name ? orderInfo.customer_name + " 订单货物" : "",
quantity: 1,
factory_id: orderInfo.factory_id || undefined,
},
}).then(function () {
wx.showToast({ title: "司机分配成功", icon: "success" });
return that.loadDetail();
}).catch(function (error) {
that.setData({ message: error.message || "分配失败" });
}).finally(function () {
that.setData({ actionLoading: false });
});
};
// 如果已有物流任务,先取消
if (that.data.logistics && that.data.logistics.task_id) {
wx.showModal({
title: "重新分配",
content: "确定要重新分配司机吗?当前物流任务将被取消。",
confirmColor: "#2563eb",
success: function (res) {
if (!res.confirm) return;
that.setData({ actionLoading: true });
app.request({
url: "/api/logistics/tasks/" + that.data.logistics.task_id + "/cancel",
method: "POST",
data: { cancel_reason: "管理员重新分配司机" },
}).then(function () {
return doAssign();
}).catch(function (error) {
that.setData({ message: error.message || "取消物流任务失败", actionLoading: false });
});
},
});
} else {
doAssign();
}
},
// 简单状态变更
handleSimpleTransition: function (e) {
var that = this;
var target = e.currentTarget.dataset.target;
var labelMap = {
picked_up: "确认揽货",
delivered: "确认送达",
production: "标记生产",
shipped: "标记发货",
completed: "标记完成",
pending_settle: "待结算",
};
wx.showModal({
title: "确认操作",
content: "确认" + (labelMap[target] || "变更状态") + "",
confirmColor: "#2563eb",
success: function (res) {
if (!res.confirm) return;
that._changeStatus(target, labelMap[target] || "操作成功");
},
});
},
_changeStatus: function (targetStatus, successMsg) {
var that = this;
var app = getApp();
that.setData({ actionLoading: true, message: "" });
app.request({
url: "/api/orders/" + that.data.orderInfo.order_id + "/status",
method: "POST",
data: { target_status: targetStatus },
}).then(function () {
wx.showToast({ title: successMsg || "操作成功", icon: "success" });
return that.loadDetail();
}).catch(function (error) {
that.setData({ message: error.message || "操作失败" });
}).finally(function () {
that.setData({ actionLoading: false });
});
},
// 申请取消
handleApplyCancel: function () {
var that = this;
wx.showModal({
title: "申请取消",
editable: true,
placeholderText: "请输入取消原因(必填)",
confirmColor: "#be123c",
success: function (res) {
if (!res.confirm) return;
var reason = (res.content || "").trim();
if (!reason) {
wx.showToast({ title: "请输入取消原因", icon: "none" });
return;
}
var app = getApp();
that.setData({ actionLoading: true, message: "" });
app.request({
url: "/api/orders/" + that.data.orderInfo.order_id + "/cancel",
method: "POST",
data: { cancel_reason: reason },
}).then(function () {
wx.showToast({ title: "已发起取消", icon: "success" });
return that.loadDetail();
}).catch(function (error) {
that.setData({ message: error.message || "取消失败" });
}).finally(function () {
that.setData({ actionLoading: false });
});
},
});
},
// 结算提成
handleSettle: function () {
var that = this;
var app = getApp();
wx.showModal({
title: "确认结算",
content: "确认结算此订单提成?",
confirmColor: "#2563eb",
success: function (res) {
if (!res.confirm) return;
that.setData({ actionLoading: true, message: "" });
app.request({
url: "/api/orders/" + that.data.orderInfo.order_id + "/settle",
method: "POST",
}).then(function () {
wx.showToast({ title: "提成已结算", icon: "success" });
return that.loadDetail();
}).catch(function (error) {
that.setData({ message: error.message || "结算失败" });
}).finally(function () {
that.setData({ actionLoading: false });
});
},
});
},
handleBack: function () { wx.navigateBack({ delta: 1 }); },
// 分享给好友

View File

@ -168,11 +168,14 @@
<view wx:else-if="{{logistics.tracking_number}}" class="empty-hint">暂无物流轨迹</view>
</view>
<!-- 下发工厂选择(仅待审批状态) -->
<!-- 状态操作区 -->
<!-- 自发订单提示(待审批+有快递单号) -->
<view wx:if="{{orderInfo.order_status === 'pending_approve' && orderInfo.tracking_number}}" class="card action-card">
<view class="approval-hint">马甸工厂自发 — 已填写快递单号({{orderInfo.tracking_number}}),审批通过后跳过工厂下发</view>
</view>
<view wx:if="{{orderInfo.order_status === 'pending_approve' && !orderInfo.tracking_number}}" class="card action-card">
<!-- 工厂+司机选择(待审批/已审批,非自发) -->
<view wx:if="{{(orderInfo.order_status === 'pending_approve' || orderInfo.order_status === 'approved') && !orderInfo.tracking_number}}" class="card action-card">
<view class="section-title">下发工厂</view>
<picker mode="selector" range="{{factoryList}}" range-key="supplierName" value="{{selectedFactoryIndex}}" bindchange="onFactoryChange">
<view class="picker-value">
@ -181,9 +184,48 @@
</view>
</picker>
</view>
<view wx:if="{{(orderInfo.order_status === 'pending_approve' || orderInfo.order_status === 'approved') && !orderInfo.tracking_number}}" class="card action-card">
<view class="section-title">分配司机</view>
<picker mode="selector" range="{{driverList}}" range-key="real_name" value="{{selectedDriverIndex}}" bindchange="onDriverChange">
<view class="picker-value">
{{selectedDriverIndex >= 0 ? driverList[selectedDriverIndex].real_name : '请选择司机'}}
<text class="picker-arrow">▶</text>
</view>
</picker>
</view>
<!-- 分配司机选择(仅待审批状态) -->
<view wx:if="{{orderInfo.order_status === 'pending_approve' && !orderInfo.tracking_number}}" class="card action-card">
<!-- 待工厂处理:司机+任务信息 -->
<view wx:if="{{orderInfo.order_status === 'pending_factory'}}" class="card action-card">
<view class="section-title">分配任务</view>
<view class="field-group">
<view class="field-label">选择司机</view>
<picker mode="selector" range="{{driverList}}" range-key="real_name" value="{{selectedDriverIndex}}" bindchange="onDriverChange">
<view class="picker-value">
{{selectedDriverIndex >= 0 ? driverList[selectedDriverIndex].real_name : '请选择司机'}}
<text class="picker-arrow">▶</text>
</view>
</picker>
</view>
<view class="field-group">
<view class="field-label">取货地址</view>
<input class="field-input" value="{{taskForm.pickup_address}}" bindinput="onTaskFormInput" data-field="pickup_address" placeholder="请输入取货地址" />
</view>
<view class="field-group">
<view class="field-label">送货地址</view>
<input class="field-input" value="{{taskForm.delivery_address}}" bindinput="onTaskFormInput" data-field="delivery_address" placeholder="请输入送货地址" />
</view>
<view class="field-group">
<view class="field-label">取货内容</view>
<input class="field-input" value="{{taskForm.pickup_content}}" bindinput="onTaskFormInput" data-field="pickup_content" placeholder="请输入取货内容" />
</view>
<view class="field-group">
<view class="field-label">数量</view>
<input class="field-input" type="number" value="{{taskForm.quantity}}" bindinput="onTaskFormInput" data-field="quantity" placeholder="请输入数量" />
</view>
</view>
<!-- 待司机接单:司机选择 -->
<view wx:if="{{orderInfo.order_status === 'pending_driver'}}" class="card action-card">
<view class="section-title">分配司机</view>
<picker mode="selector" range="{{driverList}}" range-key="real_name" value="{{selectedDriverIndex}}" bindchange="onDriverChange">
<view class="picker-value">
@ -196,11 +238,79 @@
<!-- 固定底部操作栏 -->
<view wx:if="{{orderInfo}}" class="action-bar">
<button class="action-btn reject-btn" loading="{{actionLoading}}" data-result="reject" bindtap="handleApprove">
{{ (orderInfo.order_status === 'cancel_pending' || orderInfo.order_status === 'cancel_fulfillment_pending') ? '驳回取消' : '退回' }}
</button>
<button class="action-btn pass-btn" type="primary" loading="{{actionLoading}}" data-result="pass" bindtap="handleApprove">
{{ (orderInfo.order_status === 'cancel_pending' || orderInfo.order_status === 'cancel_fulfillment_pending') ? '同意取消' : '通过' }}
</button>
<!-- 草稿/已驳回:提交审核 -->
<block wx:if="{{orderInfo.order_status === 'draft' || orderInfo.order_status === 'rejected'}}">
<button class="action-btn pass-btn" type="primary" loading="{{actionLoading}}" bindtap="handleSubmitReview">提交审核</button>
</block>
<!-- 待审批:通过+退回 -->
<block wx:elif="{{orderInfo.order_status === 'pending_approve'}}">
<button class="action-btn reject-btn" loading="{{actionLoading}}" bindtap="handleApprove" data-result="reject">退回</button>
<button class="action-btn pass-btn" type="primary" loading="{{actionLoading}}" bindtap="handleApprove" data-result="pass">通过</button>
</block>
<!-- 取消审批:同意+驳回 -->
<block wx:elif="{{orderInfo.order_status === 'cancel_pending' || orderInfo.order_status === 'cancel_fulfillment_pending'}}">
<button class="action-btn reject-btn" loading="{{actionLoading}}" bindtap="handleApprove" data-result="reject">驳回取消</button>
<button class="action-btn pass-btn" type="primary" loading="{{actionLoading}}" bindtap="handleApprove" data-result="pass">同意取消</button>
</block>
<!-- 已审批:推进待工厂 -->
<block wx:elif="{{orderInfo.order_status === 'approved'}}">
<button class="action-btn cancel-btn" loading="{{actionLoading}}" bindtap="handleApplyCancel">申请取消</button>
<button class="action-btn pass-btn" type="primary" loading="{{actionLoading}}" bindtap="handlePushToFactory">推进待工厂</button>
</block>
<!-- 待工厂处理:分配任务 -->
<block wx:elif="{{orderInfo.order_status === 'pending_factory'}}">
<button class="action-btn cancel-btn" loading="{{actionLoading}}" bindtap="handleApplyCancel">申请取消</button>
<button class="action-btn pass-btn" type="primary" loading="{{actionLoading}}" bindtap="handleAssignTask">分配任务</button>
</block>
<!-- 待司机接单 -->
<block wx:elif="{{orderInfo.order_status === 'pending_driver'}}">
<button class="action-btn cancel-btn" loading="{{actionLoading}}" bindtap="handleApplyCancel">申请取消</button>
<button class="action-btn pass-btn" type="primary" loading="{{actionLoading}}" bindtap="handleAssignDriver">{{logistics ? '重新分配司机' : '分配司机'}}</button>
</block>
<!-- 已接单:确认揽货 -->
<block wx:elif="{{orderInfo.order_status === 'accepted'}}">
<button class="action-btn cancel-btn" loading="{{actionLoading}}" bindtap="handleApplyCancel">申请取消</button>
<button class="action-btn pass-btn" type="primary" loading="{{actionLoading}}" bindtap="handleSimpleTransition" data-target="picked_up">确认揽货</button>
</block>
<!-- 已揽货:确认送达 -->
<block wx:elif="{{orderInfo.order_status === 'picked_up'}}">
<button class="action-btn cancel-btn" loading="{{actionLoading}}" bindtap="handleApplyCancel">申请取消</button>
<button class="action-btn pass-btn" type="primary" loading="{{actionLoading}}" bindtap="handleSimpleTransition" data-target="delivered">确认送达</button>
</block>
<!-- 已送达:标记生产 -->
<block wx:elif="{{orderInfo.order_status === 'delivered'}}">
<button class="action-btn cancel-btn" loading="{{actionLoading}}" bindtap="handleApplyCancel">申请取消</button>
<button class="action-btn pass-btn" type="primary" loading="{{actionLoading}}" bindtap="handleSimpleTransition" data-target="production">标记生产</button>
</block>
<!-- 生产中:标记发货 -->
<block wx:elif="{{orderInfo.order_status === 'production'}}">
<button class="action-btn cancel-btn" loading="{{actionLoading}}" bindtap="handleApplyCancel">申请取消</button>
<button class="action-btn pass-btn" type="primary" loading="{{actionLoading}}" bindtap="handleSimpleTransition" data-target="shipped">标记发货</button>
</block>
<!-- 已发货:标记完成 -->
<block wx:elif="{{orderInfo.order_status === 'shipped'}}">
<button class="action-btn cancel-btn" loading="{{actionLoading}}" bindtap="handleApplyCancel">申请取消</button>
<button class="action-btn pass-btn" type="primary" loading="{{actionLoading}}" bindtap="handleSimpleTransition" data-target="completed">标记完成</button>
</block>
<!-- 已完成(有佣金):待结算 -->
<block wx:elif="{{orderInfo.order_status === 'completed' && orderInfo.commission_amount > 0}}">
<button class="action-btn pass-btn" type="primary" loading="{{actionLoading}}" bindtap="handleSimpleTransition" data-target="pending_settle">待结算</button>
</block>
<!-- 已完成(无佣金)/ 待结算:结算 -->
<block wx:elif="{{orderInfo.order_status === 'completed' || orderInfo.order_status === 'pending_settle'}}">
<button class="action-btn pass-btn" type="primary" loading="{{actionLoading}}" bindtap="handleSettle">结算提成</button>
</block>
</view>
</view>

View File

@ -170,6 +170,23 @@
font-weight: 600;
}
/* 表单字段 */
.field-group { margin-bottom: 20rpx; }
.field-group:last-child { margin-bottom: 0; }
.field-label { font-size: 24rpx; color: #64748b; font-weight: 600; margin-bottom: 8rpx; }
.field-input {
width: 100%; padding: 16rpx 20rpx; background: #f8fafc;
border: 1rpx solid #d1d5db; border-radius: 12rpx;
font-size: 28rpx; color: #0f172a; box-sizing: border-box;
}
/* 取消按钮 */
.cancel-btn {
background: #fff1f2;
color: #be123c;
border: 2rpx solid #fecdd3;
}
/* 物流轨迹 */
.trace-timeline { position: relative; padding-left: 28rpx; margin-top: 16rpx; }
.trace-item { position: relative; padding-bottom: 20rpx; }