Files
delivery-uniapp/pages/index/components/order-code-popup.vue

532 lines
11 KiB
Vue
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>
<up-popup :show="show" mode="center" :round="5" safeAreaInsetBottom @close="onClose" :closeable="true">
<view class="popup-wrap" style="width:680rpx;">
<!-- 标题 -->
<view class="popup-header">
<text class="title">催单</text>
<text class="order-no">#{{ orderIndex }}</text>
</view>
<!-- 查询输入 -->
<view class="search-row">
<view style="width:620rpx;">
<input class="search-input" v-model="orderCode" placeholder="输入收货人手机尾号4位+取单号,如8927#11" />
</view>
<view style="margin-left:15rpx;">
<up-button type="primary" size="small" text="查询" @click="queryOrder"></up-button>
</view>
</view>
<!-- 取货点 -->
<view class="section">
<view class="section-title">
<text class="icon">🏬</text>
<text class="text">取货点店铺名称</text>
</view>
<text class="muted">取货店铺详细地址{{ pickupName }}</text>
</view>
<!-- 送餐地址 -->
<view class="section">
<view class="section-title">
<text class="icon">🚩</text>
<text class="text">送餐详细地址</text>
</view>
<text class="muted">{{ address }}</text>
<view class="recipient">收货人名称先生 尾号{{ recipientTail }}</view>
</view>
<!-- 客户备注 -->
<view class="customer-note">
<text>顾客{{ customerNote }}</text>
</view>
<!-- 交接备注 下拉 -->
<view class="form-row">
<text class="label">交接备注</text>
<picker :range="remarks" @change="onRemarkChange">
<view class="picker-display">{{ selectedRemarkText || '请选择交接备注' }}</view>
</picker>
</view>
<!-- 凭证 上传 -->
<view class="form-row proof-row">
<text class="label required">凭证</text>
<up-upload
:fileList="uploadFileList"
@afterRead="afterRead"
@delete="deleteUpload"
:maxCount="1"
:maxSize="5 * 1024 * 1024"
uploadText="上传凭证"
:previewFullImage="true"
></up-upload>
</view>
<!-- 底部操作 -->
<view class="footer-row">
<up-button type="success" text="联系上一个骑手" icon="phone" @click="callPhone('13131008612')"></up-button>
<view style="margin-left:15rpx;width:600rpx;">
<up-button type="primary" text="确认交接" @click="onConfirm"></up-button>
</view>
</view>
</view>
</up-popup>
</template>
<script setup>
import DeliveryOrderApi from '@/sheep/api/member/deliveryOrder';
import sheep from '@/sheep';
import { baseUrl, apiPath } from '@/sheep/config';
import {
ref,
reactive,
computed
} from 'vue'
const props = defineProps({
show: {
type: Boolean,
default: false
}
})
const emit = defineEmits(['close', 'confirm'])
// 响应式数据
const orderIndex = ref('')
const orderCode = ref('')
const pickupName = ref('')
const address = ref('')
const recipientTail = ref('')
const customerNote = ref('依据餐量提供餐具')
const remarks = ['餐品破损', '餐品撒翻', '其他']
const selectedRemark = ref('')
const selectedRemarkText = computed(() => remarks[selectedRemark.value] || '')
// 上传文件列表 (用于 up-upload 组件)
const uploadFileList = ref([])
// 已上传的凭证URL列表
const proofUrls = ref([])
// 方法
const onClose = () => {
emit('close')
}
const queryOrder = async () => {
if (!orderCode.value) {
uni.showToast({
title: '请输入查询条件',
icon: 'none'
})
return
}
try {
// 解析输入支持输入订单ID或手机尾号#取单号格式
let orderId = orderCode.value;
// 如果包含 # 符号尝试提取订单ID这里假设后缀是订单ID
if (orderCode.value.includes('#')) {
const parts = orderCode.value.split('#');
orderId = parts[parts.length - 1];
}
uni.showLoading({ title: '查询中...' });
const res = await DeliveryOrderApi.getDetail(orderId);
const data = res.data || res;
uni.hideLoading();
if (data && data.id) {
// 更新订单信息
orderIndex.value = data.id;
pickupName.value = data.shopName || '';
address.value = data.receiverAddress || '';
customerNote.value = data.orderRemark || '';
// 处理收货人信息
if (data.receiverPhone) {
recipientTail.value = (data.receiverPhone + '').slice(-4);
} else {
recipientTail.value = '';
}
uni.showToast({
title: '查询成功',
icon: 'success'
});
} else {
uni.showToast({
title: '未找到订单',
icon: 'none'
});
}
} catch (e) {
uni.hideLoading();
console.error('queryOrder error', e);
uni.showToast({
title: '查询失败',
icon: 'none'
});
}
}
// 拨打电话
const callPhone = (phone) => {
if (!phone) {
sheep.$helper && sheep.$helper.toast && sheep.$helper.toast('未找到联系电话');
return;
}
uni.makePhoneCall({
phoneNumber: phone
});
}
const onRemarkChange = (e) => {
selectedRemark.value = e.detail.value
}
// 删除上传的图片
const deleteUpload = (event) => {
uploadFileList.value.splice(event.index, 1)
proofUrls.value.splice(event.index, 1)
}
// 上传单个文件到 OSS
async function uploadFile(filePath) {
const token = uni.getStorageSync('token');
return new Promise((resolve, reject) => {
uni.uploadFile({
url: baseUrl + apiPath + '/app/file/uploadOss', // 上传接口地址
header: {
'Authorization': token
},
filePath: filePath,
name: 'file',
success: (res) => {
if (res.statusCode === 200) {
try {
const data = JSON.parse(res.data);
if (data.code === 0 && data.data) {
resolve(data.data);
} else {
console.error('上传失败:', data.msg);
resolve(null);
}
} catch (e) {
console.error('解析上传响应失败:', e);
resolve(null);
}
} else {
console.error('上传失败:', res.statusCode);
resolve(null);
}
},
fail: (err) => {
console.error('上传请求失败:', err);
reject(err);
}
});
});
}
// 上传图片后的回调
async function afterRead(event) {
// 当设置 mutiple 为 true 时, event.file.list 为数组
const { file } = event;
// 如果是单选,转换为数组处理
const fileList = Array.isArray(file) ? file : [file];
for (const item of fileList) {
// 标记为上传中
uploadFileList.value.push({
...item,
status: 'uploading',
message: '上传中'
});
}
// 依次上传文件
for (let i = 0; i < fileList.length; i++) {
const item = fileList[i];
const index = uploadFileList.value.findIndex(f => f.url === item.url);
try {
const url = await uploadFile(item.url);
if (url) {
// 上传成功,更新状态
uploadFileList.value[index] = {
...uploadFileList.value[index],
status: 'success',
message: ''
};
proofUrls.value.push(url);
} else {
// 上传失败
uploadFileList.value[index] = {
...uploadFileList.value[index],
status: 'failed',
message: '上传失败'
};
}
} catch (error) {
console.error('上传图片异常:', error);
uploadFileList.value[index] = {
...uploadFileList.value[index],
status: 'failed',
message: '上传失败'
};
}
}
}
const onConfirm = async () => {
// 校验配送单信息
if (!orderIndex.value) {
uni.showToast({
title: '没有配送单信息',
icon: 'none'
})
return
}
// 校验凭证
if (proofUrls.value.length === 0) {
uni.showToast({
title: '请上传凭证',
icon: 'none'
})
return
}
try {
uni.showLoading({ title: '提交中...' });
// 调用确认交接接口
const res = await DeliveryOrderApi.riderConfirmHandover({
deliveryOrderId: Number(orderIndex.value),
// 注意:这里假设 remarks 的 index 就是 handoverRemarkId如果后端需要实际ID可能需要调整
handoverRemarkId: selectedRemark.value === 0 ? undefined : selectedRemark.value,
imageUrlList: proofUrls.value
});
uni.hideLoading();
if (res.code === 0 && res.data === true) {
uni.showToast({
title: '交接成功',
icon: 'success'
});
// 关闭弹框并通知父组件
emit('confirm', {
orderIndex: orderIndex.value,
orderCode: orderCode.value,
remark: selectedRemarkText.value,
proofImages: proofUrls.value
});
} else {
uni.showToast({
title: res.msg || '交接失败',
icon: 'none'
});
}
} catch (error) {
uni.hideLoading();
console.error('确认交接异常:', error);
uni.showToast({
title: '交接失败,请重试',
icon: 'none'
});
}
}
</script>
<style scoped>
.popup-wrap {
padding: 24rpx;
background: #fff;
border-radius: 12rpx;
}
.popup-header {
display: flex;
justify-content: center;
align-items: center;
margin-bottom: 18rpx;
}
.title {
color: #00a0df;
font-size: 30rpx;
font-weight: 700;
margin-right: 8rpx;
}
.order-no {
color: #666;
font-size: 24rpx;
}
.search-row {
display: flex;
align-items: center;
margin-bottom: 16rpx;
}
.search-input {
flex: 1;
height: 64rpx;
border: 1rpx solid #ddd;
padding: 0 20rpx;
border-radius: 6rpx;
font-size: 22rpx;
}
.search-btn {
width: 120rpx;
height: 64rpx;
font-size: 22rpx;
background: #fff;
border-left: 1rpx solid #ddd;
}
.section {
padding: 12rpx 0;
border-top: 1rpx solid #f0f0f0;
}
.section-title {
display: flex;
align-items: center;
margin-bottom: 8rpx;
}
.section-title .icon {
margin-right: 12rpx;
font-size: 26rpx;
}
.section-title .text {
font-weight: 700;
font-size: 26rpx;
}
.muted {
color: #666;
font-size: 22rpx;
}
.recipient {
color: #999;
font-size: 20rpx;
margin-top: 6rpx;
}
.customer-note {
background: #f7f7f7;
padding: 12rpx;
border-radius: 6rpx;
margin: 12rpx 0;
color: #666;
}
.form-row {
display: flex;
align-items: center;
margin: 12rpx 0;
}
.label {
width: 180rpx;
color: #333;
}
.required {
color: #e53935;
}
.picker-display {
flex: 1;
height: 64rpx;
line-height: 64rpx;
border: 1rpx solid #e6e6e6;
padding: 0 12rpx;
border-radius: 6rpx;
}
.proof-row .proof-list {
display: flex;
align-items: center;
}
.proof-item {
position: relative;
width: 140rpx;
height: 140rpx;
margin-right: 14rpx;
}
.proof-img {
width: 100%;
height: 100%;
border-radius: 6rpx;
}
.remove {
position: absolute;
right: 4rpx;
top: 4rpx;
background: rgba(0, 0, 0, 0.5);
color: #fff;
padding: 2rpx 6rpx;
border-radius: 10rpx;
font-size: 20rpx;
}
.proof-add {
width: 140rpx;
height: 140rpx;
border: 1rpx dashed #ddd;
display: flex;
justify-content: center;
align-items: center;
font-size: 48rpx;
color: #999;
border-radius: 6rpx;
}
.footer-row {
display: flex;
justify-content: space-between;
align-items: center;
margin-top: 18rpx;
}
.phone-area {
display: flex;
align-items: center;
color: #333;
}
.phone-icon {
font-size: 30rpx;
margin-right: 8rpx;
}
.confirm-btn {
background: #0a99e6;
color: #fff;
padding: 14rpx 32rpx;
border-radius: 8rpx;
}
</style>