feat: 新增确认送达弹框组件、添加安卓打包测试证书、骑手配送流程接口对接

This commit is contained in:
admin
2026-02-24 11:58:30 +08:00
parent b85833690f
commit bc1833ec89
25 changed files with 2982 additions and 1205 deletions

View File

@@ -1,16 +1,14 @@
<template>
<s-layout title="订单详情" class="set-userinfo-wrap">
<view class="order-detail-page">
<!-- 地图占位可替换为原生 map 组件或第三方地图组件 -->
<!-- 地图区域 -->
<view class="map-area">
<!-- 真实项目建议使用 <map> 并渲染 polyline/markers -->
<image class="map-image" src="/static/img/map-placeholder.png" mode="widthFix" v-if="!mapAvailable" />
<map v-else class="map-native" :latitude="order?.pickupLat" :longitude="order?.pickupLng" show-location
<map class="map-native" :latitude="order?.pickupLat" :longitude="order?.pickupLng" show-location
enable-3D enable-zoom :scale="16"></map>
<view class="map-overlay">
<view class="eta">距离商家{{ distanceText }}预计{{ etaText }}到达</view>
<view class="nav-btn" @click="navigateToShop">导航到商家</view>
</view>
<cover-view class="map-overlay">
<cover-view class="eta">距离商家{{ distanceText }}预计{{ etaText }}到达</cover-view>
<cover-view class="nav-btn" @click="navigateToShop">导航到商家</cover-view>
</cover-view>
</view>
<!-- 地址块 -->
@@ -67,7 +65,7 @@
</view>
<!-- 占位底部高度避免内容被底部按钮遮挡 -->
<view style="height:140rpx;"></view>
<view style="height:200rpx;"></view>
</scroll-view>
<!-- 底部操作 -->
@@ -76,10 +74,13 @@
<view class="icon-phone" @click="callPhone(order?.receiverPhone)"></view>
</view>
<view class="right-actions">
<view class="btn remind">催单</view>
<view class="btn remind"
@click="sheep.$helper.toast('功能开发中ing')">催单</view>
<view class="btn remind" @click="openRemindPopup">电话联系</view>
<!-- <view class="btn confirm">转单</view> -->
<view class="btn confirm" @click="confirmArrive">确认到店</view>
<view class="btn confirm" v-if="order?.deliveryStatus == 2" @click="confirmArrive">确认到店</view>
<view class="btn confirm" v-if="order?.deliveryStatus == 3" @click="confirmPickup">确认取餐</view>
<view class="btn handover" v-if="order?.deliveryStatus == 4" @click="deliveryHandover">送达交接点</view>
<view class="btn handover" v-if="order?.deliveryStatus == 5" @click="openDeliveryPopup">确认送达顾客</view>
</view>
</view>
<!-- 催单弹框 -->
@@ -98,6 +99,15 @@
<view class="remind-cancel" @click="showRemind = false">取消</view>
</template>
</up-popup>
<!-- 确认送达顾客弹框 -->
<DeliveryPopup
:show="showDeliveryPopup"
:receiverPhone="order?.receiverPhone || ''"
@update:show="showDeliveryPopup = $event"
@submit="handleDeliveryConfirm"
@close="showDeliveryPopup = false"
/>
</view>
</s-layout>
</template>
@@ -111,11 +121,13 @@
onLoad
} from '@dcloudio/uni-app';
import sheep from '@/sheep';
import DeliveryOrderApi from '@/sheep/api/member/deliveryOrder';
import DeliveryPopup from '@/pages/index/components/delivery-popup.vue';
const orderId = ref(null);
const order = ref(null);
const loading = ref(false);
const mapAvailable = ref(false); // 如果需要使用 map 组件,置为 true
const mapAvailable = ref(true); // 如果需要使用 map 组件,置为 true
// 入口:从页面参数取 orderId然后加载数据
onLoad((options = {}) => {
@@ -126,51 +138,36 @@
async function fetchOrder() {
loading.value = true;
try {
// 优先尝试平台统一 request项目内可能封装在 sheep.request 或 sheep.api
if (sheep && typeof sheep.request === 'function') {
const res = await sheep.request({
url: '/order/detail',
method: 'GET',
data: {
id: orderId.value
}
});
// 根据封装不同,这里兼容 res.data 或 res
order.value = (res && res.data) ? res.data : res;
} else if (sheep && sheep.$api && sheep.$api.trade && typeof sheep.$api.trade.detail === 'function') {
const res = await sheep.$api.trade.detail({
id: orderId.value
});
order.value = res?.data || res;
} else {
// 回退 mock 数据,避免界面空白,开发时替换为真实接口
// 使用 DeliveryOrderApi 获取配送单详情
const res = await DeliveryOrderApi.getDetail(orderId.value);
// 根据封装不同,这里兼容 res.data 或 res
const resData = (res && res.data) ? res.data : res;
console.log("res数据", res);
if (resData) {
// 字段映射:将接口返回字段映射到页面使用字段
order.value = {
id: orderId.value || 1001,
shopName: '店铺名(示例)',
pickupAddress: '广东省广州市天河区学院站荷光路118-121号',
pickupLat: 23.1005,
pickupLng: 113.3301,
deliveryAddress: '广东省广州市天河区华景新城软件园区B栋西梯501',
deliveryLat: 23.105,
deliveryLng: 113.335,
receiverName: '张先生',
receiverPhone: '13900001234',
note: '依据餐量提供餐具',
items: [{
name: '商品名称A',
spec: '规格1',
quantity: 2,
price: 23.89
},
{
name: '商品名称B',
spec: '规格2',
quantity: 1,
price: 45.00
}
]
...resData,
// 店铺坐标 (取货点)
pickupLat: resData.shopLatitude,
pickupLng: resData.shopLongitude,
pickupAddress: resData.shopAddress,
// 收货地址
deliveryLat: resData.receiverLatitude,
deliveryLng: resData.receiverLongitude,
deliveryAddress: resData.receiverAddress,
// 商品列表
items: resData.orderItemDataList?.map(item => ({
name: item.productName,
quantity: item.quantity,
price: parseFloat(item.unitPrice) || 0
})) || [],
// 备注
note: resData.orderRemark,
deliveryStatus: resData.deliveryStatus
};
}
console.log("order.value数据", order.value);
} catch (e) {
console.error('fetchOrder error', e);
// 友好提示
@@ -240,6 +237,42 @@
showRemind.value = false;
}
// 确认送达顾客弹框控制
const showDeliveryPopup = ref(false);
function openDeliveryPopup() {
showDeliveryPopup.value = true;
}
// 确认送达顾客
async function handleDeliveryConfirm(imageUrl) {
console.log('送达照片URL:', imageUrl);
if (!orderId.value) {
sheep.$helper.toast('订单信息异常');
return;
}
try {
const res = await DeliveryOrderApi.riderConfirmDelivery({
deliveryOrderId: orderId.value,
imageUrl: imageUrl
});
if (res.code === 0 && res.data === true) {
sheep.$helper.toast('已提交送达照片');
showDeliveryPopup.value = false;
// 刷新订单状态
fetchOrder();
} else {
sheep.$helper.toast(res.msg || '提交失败');
}
} catch (error) {
console.error('确认送达异常:', error);
sheep.$helper.toast('提交失败,请重试');
}
}
function callShopPhone() {
// 商家电话优先使用 order.shopPhone否则尝试 fallback
const phone = order.value?.shopPhone || order.value?.shopPhoneNumber || order.value?.receiverPhone || '';
@@ -261,29 +294,68 @@
closeRemindPopup();
}
function confirmArrive() {
// 确认到店:调用接口或本地改变状态
if (!order.value) return;
// 示例:调用后端接口(兼容性判断)
(async () => {
try {
if (sheep && typeof sheep.request === 'function') {
await sheep.request({
url: '/order/confirmArrive',
method: 'POST',
data: {
id: order.value.id
}
});
}
sheep.$helper && sheep.$helper.toast && sheep.$helper.toast('已确认到店');
// 可在此刷新订单状态
// 确认到店
async function confirmArrive() {
if (!order.value || !orderId.value) return;
try {
uni.showLoading({ title: '提交中...' });
const res = await DeliveryOrderApi.riderConfirmArrival(orderId.value);
uni.hideLoading();
if (res.code === 0 && res.data === true) {
sheep.$helper.toast('已确认到店');
// 刷新订单状态
fetchOrder();
} catch (e) {
console.error(e);
sheep.$helper && sheep.$helper.toast && sheep.$helper.toast('确认失败,请重试');
} else {
sheep.$helper.toast(res.msg || '确认到店失败');
}
})();
} catch (e) {
uni.hideLoading();
console.error('confirmArrive error', e);
sheep.$helper.toast('确认失败,请重试');
}
}
// 确认取餐
async function confirmPickup() {
if (!order.value || !orderId.value) return;
try {
uni.showLoading({ title: '提交中...' });
const res = await DeliveryOrderApi.riderConfirmPickup(orderId.value);
uni.hideLoading();
if (res.code === 0 && res.data === true) {
sheep.$helper.toast('已确认取餐');
// 刷新订单状态
fetchOrder();
} else {
sheep.$helper.toast(res.msg || '确认取餐失败');
}
} catch (e) {
uni.hideLoading();
console.error('confirmPickup error', e);
sheep.$helper.toast('确认失败,请重试');
}
}
// 送达交接点
async function deliveryHandover() {
if (!order.value || !orderId.value) return;
try {
uni.showLoading({ title: '提交中...' });
await DeliveryOrderApi.riderDeliveryHandover(orderId.value);
uni.hideLoading();
sheep.$helper && sheep.$helper.toast && sheep.$helper.toast('已送达交接点');
// 刷新订单状态
fetchOrder();
} catch (e) {
console.error('deliveryHandover error', e);
uni.hideLoading();
sheep.$helper && sheep.$helper.toast && sheep.$helper.toast('提交失败,请重试');
}
}
//跳转交接记录
@@ -329,7 +401,7 @@
}
.map-area {
height: 360rpx;
height: 560rpx;
background: #f3f3f3;
position: relative;
overflow: hidden;
@@ -351,26 +423,32 @@
position: absolute;
left: 20rpx;
right: 20rpx;
bottom: 20rpx;
bottom: 45rpx;
display: flex;
justify-content: space-between;
align-items: center;
flex-direction: row;
}
.eta {
background: rgba(255, 255, 255, 0.95);
padding: 10rpx 14rpx;
padding: 0rpx 14rpx;
border-radius: 20rpx;
font-size: 24rpx;
color: #333;
text-align: center;
// line-height: 32rpx;
}
.nav-btn {
background: #fff;
padding: 10rpx 14rpx;
padding: 0rpx 20rpx;
border-radius: 20rpx;
color: #1e9fff;
border: 1rpx solid #dbeeff;
font-size: 24rpx;
text-align: center;
// line-height: 32rpx;
}
.content {
@@ -578,6 +656,10 @@
background: #1e9fff;
}
.btn.handover {
background: #2ecc71;
}
.remind-popup {
padding: 20rpx 0;
}