完成京东秒送外卖模块

This commit is contained in:
tangxinyue 2026-04-15 18:38:50 +08:00
parent bce2fd578f
commit e01f4865e6
13 changed files with 1560 additions and 104 deletions

View File

@ -14,7 +14,9 @@
<view v-if="item.status === '等待付款'" class="status-warning">
<image style="width: 116rpx;height: 30rpx;" src="/static/image/shopping/jingdong/dengdaifukuan.png">
</image>
<text class="status-desc" v-if="item.statusDesc">{{ formatStatusDesc(item.statusDesc) }}</text>
<text class="status-desc" v-if="item.statusDesc">{{
item.shopType != 'waimai' ? formatStatusDesc(item.statusDesc) :
formatWaimaiStatusDesc(item.statusDesc) }}</text>
</view>
<text v-else class="status-text"
:class="{ red: item.statusColor === 'red' || item.status === '正在出库' || item.status === '商家备餐中' || item.status === '骑手到店取餐中' || item.status === '骑手已到店' }">{{
@ -87,7 +89,11 @@
<view class="product-price-box">
<view class="price-wrap wx-font-regular">
<text class="price-symbol"></text>
<text class="price-num">{{ safeFormatPrice(item) }}</text>
<text class="price-num">{{ item.shopType === 'waimai' ?
(item.totalPrice ? Number(item.totalPrice).toFixed(2) : '0.00')
:
safeFormatPrice(item)
}}</text>
</view>
<text class="product-count"
v-if="item.count || (item.products && item.products[0] && item.products[0].count)">{{
@ -119,7 +125,7 @@
</template>
<template v-else-if="item.promoType === 'text'">
<text class="promo-text text-basic">本单可享 <text class="highlight">{{ item.promoHighlight
}}</text>快去支付吧~</text>
}}</text>快去支付吧~</text>
</template>
<template v-else>
<text class="promo-text text-basic">当笔订单返现成功到账<text style="color: #E31700;">点击领取</text></text>
@ -161,6 +167,21 @@ const onLongPress = (e) => {
emit('longpress', { event: e, item: props.item });
};
const formatWaimaiStatusDesc = (desc) => {
if (!desc || !desc.includes(':')) return desc;
const parts = desc.split(':');
if (parts.length === 2) {
const m = parseInt(parts[0]) || 0;
const s = parseInt(parts[1]) || 0;
if (m > 0) {
return `${m}分钟`;
} else {
return `${s}`;
}
}
return desc;
};
const formatStatusDesc = (desc) => {
if (!desc || !desc.includes(' : ')) return desc;
const parts = desc.split(' : ');

View File

@ -515,11 +515,12 @@ const onMenuScroll = (e) => {
}
.nav-text {
font-size: 18px;
font-size: 36rpx;
margin-right: 4px;
color: #FFFFFF;
height: 24px;
line-height: 24px;
font-weight: 500;
}
::v-deep .uni-navbar__header-btns-left {

View File

@ -1,4 +1,4 @@
<template>
<template>
<!-- 水印 -->
<view v-if="$isVip()">
<watermark dark="light" />
@ -64,39 +64,43 @@
<view class="control-buttons">
<!-- 麦克风 -->
<view class="control-item">
<view class="control-btn" :class="{ active: videoData.micOn }" @click="changeInfo('micOn')">
<image class="control-icon"
:src="videoData.micOn ? '/static/image/other/video-call/mic-on.png' : '/static/image/other/video-call/mic-off.png'">
</image>
</view>
<image class="control-btn" :class="{ active: videoData.micOn }" @click="changeInfo('micOn')"
:src="videoData.micOn ? '/static/image/other/video-call/mic-on.png' : '/static/image/other/video-call/mic-off.png'">
</image>
<!-- <view class="" :class="{ active: videoData.micOn }" >
</view> -->
<text class="control-label">{{ videoData.micOn ? '麦克风已开' : '麦克风已关' }}</text>
</view>
<!-- 扬声器 -->
<view class="control-item">
<view class="control-btn" :class="{ active: videoData.speakerOn }" @click="changeInfo('speakerOn')">
<image class="control-icon"
:src="videoData.speakerOn ? '/static/image/other/video-call/speaker-on.png' : '/static/image/other/video-call/speaker-off.png'">
</image>
</view>
<image class="control-btn" :class="{ active: videoData.speakerOn }" @click="changeInfo('speakerOn')"
:src="videoData.speakerOn ? '/static/image/other/video-call/speaker-on.png' : '/static/image/other/video-call/speaker-off.png'">
</image>
<!-- <view class="" :class="{ active: videoData.speakerOn }" @click="changeInfo('speakerOn')">
</view> -->
<text class="control-label">{{ videoData.speakerOn ? '扬声器已开' : '扬声器已关' }}</text>
</view>
<!-- 摄像头 -->
<view class="control-item">
<view class="control-btn" :class="{ active: videoData.cameraOn }" @click="changeInfo('cameraOn')">
<image class="control-icon"
:src="videoData.cameraOn ? '/static/image/other/video-call/camera-on.png' : '/static/image/other/video-call/camera-off.png'">
</image>
</view>
<image class="control-btn" :class="{ active: videoData.cameraOn }" @click="changeInfo('cameraOn')"
:src="videoData.cameraOn ? '/static/image/other/video-call/camera-on.png' : '/static/image/other/video-call/camera-off.png'">
</image>
<!-- <view class="control-btn" :class="{ active: videoData.cameraOn }" @click="changeInfo('cameraOn')">
</view> -->
<text class="control-label">{{ videoData.cameraOn ? '摄像头已开' : '摄像头已关' }}</text>
</view>
</view>
<!-- 挂断按钮 -->
<view class="hangup-btn" @click="hangup">
<image class="hangup-icon" src="/static/image/other/video-call/hangup.png"></image>
</view>
<image class="hangup-btn" @click="hangup" src="/static/image/other/video-call/hangup.png"></image>
<!-- <view class="hangup-btn" @click="hangup">
</view> -->
</view>
</view>

View File

@ -11,8 +11,13 @@
<!-- 未支付 -->
<template v-if="activeTab == 'weizhifu'">
<view class="weizhifu card">
<view class="title">请在{{ order.statusDesc }} <image class="edit-icon"
src="/static/image/common/edit.png"></image>内支付</view>
<view class="title">
<picker mode="multiSelector" :range="countdownRange" :value="countdownValue"
@change="handleCountdownChange">
<text>请在{{ order.statusDesc || '29:59' }} <image class="edit-icon"
src="/static/image/common/edit.png"></image>内支付</text>
</picker>
</view>
<view class="time">现在支付预计 <text class="time-text" @click="onDeliveryTimeClick('deliveryTime')">{{
order.deliveryTime || '11:17-11:32' }}<image class="edit-icon"
src="/static/image/common/edit.png"></image>
@ -43,8 +48,18 @@
</view>
</view>
</template>
<!-- 备餐中/取餐中 -->
<view v-if="activeTab == 'beicanzhong' || activeTab == 'qvcanzhong'" class="time-box"
@click="onClickTrackingTime">
<text style="color: #87868E;">{{ activeTab == 'beicanzhong' ? '备餐时间' : '取餐时间' }}
<text style="color: #1A1A1A;font-weight: 500; margin-left: 10rpx;">{{
order.trackingTime }}</text>
<image class="edit-icon" src="/static/image/common/edit.png"></image>
</text>
</view>
<view v-if="activeTab == 'beicanzhong' || activeTab == 'qvcanzhong'" class="card-box">
<view class="card beicanzhong-card">
<view class="title flex">
<text class="time" @click="onDeliveryTimeClick('trackingTitle')">{{ order.trackingTitle ||
@ -52,7 +67,10 @@
}}</text>
<text>预计送达</text>
</view>
<view class="desc">{{ activeTab == 'qvcanzhong' ? '骑手已到店,取餐中' : '商家备餐中' }}</view>
<view class="desc">
<auto-width-input v-model="order.trackingDesc" fontSize="30rpx" fontWeight="500"
placeholder="请输入描述信息" color="#1A1A1A" show-edit />
</view>
<view class="progress">
<view class="progress-bar">
<view class="progress-bar-fill"
@ -70,8 +88,11 @@
</image>
</view>
</view>
<view class="image-box flex-center">
<image class="add-img" src="/static/image/common/add.png" mode="aspectFill"></image>
<view class="image-box flex-center" @click="chooseDeliveryImage">
<image v-if="!order.deliveryImages" class="add-img" src="/static/image/common/add.png"
mode="aspectFill">
</image>
<image v-else class="w100 h100" :src="order.deliveryImages" mode="aspectFill"></image>
</view>
<view class="btn-box flex-align-center">
<view class="btn">查看发票</view>
@ -124,7 +145,19 @@
<text class="desc">您的订单已取消请查看取消进度详情</text>
</view>
</view>
</view>
<text class="tips" style="margin-top: 24rpx;">*轻点可替换商品图片长按可从相册中选择图片*</text>
<!-- 推荐商品 -->
<view class="shop-recommend flex-center" @click="toggleRecommendImg" @longpress="chooseRecommendImg">
<image v-if="!order.recommendImg" class="w100 h100"
src="/static/image/shopping/jingdong/waimai/shop-recoomend/style-1.png" mode="aspectFit"></image>
<image v-else class="w100 h100" :src="order.recommendImg" mode="aspectFit"></image>
</view>
<!-- 商品卡片 -->
<view class="product-card">
<view class="title-box">
@ -167,7 +200,7 @@
<view class="flex flex-align-center animate-scale wx-font-medium"
style="align-items: baseline;margin-top: 10rpx;">
<text style="font-size: 28rpx;font-weight: 500;"></text>
<auto-width-input class="flex-1 wx-font-medium" v-model="product.price" type="number"
<auto-width-input class="flex-1 wx-font-medium" v-model="product.price" type="digit"
fontSize="36rpx" placeholder="0.00" color="#1A1A1A" fontWeight="500" show-edit />
</view>
</view>
@ -209,6 +242,55 @@
</view>
</view>
<view v-else class="order-info " style="margin-top: 50rpx;">
<view class="item flex-justify-between">
<text class="label shrink-0" style="color: #545454;">商品总价</text>
<view class="flex-1 flex-align-center wx-font-medium"
style="justify-content: flex-end; margin-left: 20rpx;font-weight: 500;">
<auto-width-input class="wx-font-medium" v-model="order.totalPrice" fontSize="32rpx"
placeholder="商品总价" color="#1A1A1A" fontWeight="500" type="digit" show-edit />
</view>
</view>
<view class="item flex-justify-between">
<view class="flex flex-align-center">
<text class="label" style="color: #545454;">运费</text>
<image style="width: 22rpx;height: 22rpx;margin-right: 4rpx;margin-left: 4rpx;"
src="/static/image/shopping/jingdong/detail/help.png">
</image>
</view>
<view class="wx-font-medium flex flex-align-center">
<text style="color: #F10F1A;">已减</text>
<auto-width-input class="wx-font-medium" v-model="order.discountCarriage" fontSize="26rpx"
placeholder="优惠运费" color="#F10F1A" fontWeight="500" type="digit" show-edit />
<auto-width-input style="margin-left: 10rpx;" class="wx-font-medium" v-model="order.carriage"
fontSize="26rpx" placeholder="运费" color="#1A1A1A" fontWeight="500" type="text" show-edit />
</view>
</view>
<view class="item flex-justify-between">
<text class="label shrink-0" style="color: #545454;">打包费</text>
<view class="flex-1 flex-align-center wx-font-medium"
style="justify-content: flex-end; margin-left: 20rpx;font-weight: 500;">
<auto-width-input style="margin-left: 10rpx;" class="wx-font-medium"
v-model="order.packagingFee" fontSize="26rpx" placeholder="打包费" color="#1A1A1A"
fontWeight="500" type="text" show-edit />
</view>
</view>
<view class="item flex-justify-between total-box">
<text class="label shrink-0" style="color: #545454;">合计</text>
<view class="flex-1 flex-align-center"
style="justify-content: flex-end; margin-left: 20rpx;font-weight: 500;">
<text class="red" style="color: #F10F1A;">共减<auto-width-input style="margin-left: 10rpx;"
class="wx-font-medium" v-model="order.discount" fontSize="26rpx" placeholder="优惠"
color="#F10F1A" fontWeight="500" type="text" show-edit /></text>
<text class="wx-font-medium money" style="margin-left: 16rpx;font-size: 28rpx;"><text
style="font-size: 36rpx;"><auto-width-input style="margin-left: 10rpx;"
class="wx-font-medium" v-model="order.amountTo" fontSize="36rpx" placeholder="合计金额"
color="#1A1A1A" fontWeight="500" type="text" show-edit /></text></text>
</view>
</view>
</view>
</view>
@ -218,9 +300,8 @@
<text>商品总价</text>
<view class="wx-font-medium flex flex-align-center"><text
style="font-size: 32rpx;font-weight: 500;"></text>
<!-- <text style="font-size: 32rpx;font-weight: 500;">{{ order.products[0].price }}</text> -->
<auto-width-input class="wx-font-medium" v-model="order.products[0].totalPrice" fontSize="32rpx"
placeholder="商品总价" color="#1A1A1A" fontWeight="500" type="number" show-edit />
<auto-width-input class="wx-font-medium" v-model="order.totalPrice" fontSize="32rpx"
placeholder="商品总价" color="#1A1A1A" fontWeight="500" type="digit" show-edit />
</view>
</view>
<view class="item flex-justify-between">
@ -234,7 +315,7 @@
<view class="wx-font-medium flex flex-align-center">
<text style="color: #F10F1A;">已减</text>
<auto-width-input class="wx-font-medium" v-model="order.discountCarriage" fontSize="26rpx"
placeholder="优惠运费" color="#F10F1A" fontWeight="500" type="number" show-edit />
placeholder="优惠运费" color="#F10F1A" fontWeight="500" type="digit" show-edit />
<auto-width-input style="margin-left: 10rpx;" class="wx-font-medium" v-model="order.carriage"
fontSize="26rpx" placeholder="运费" color="#1A1A1A" fontWeight="500" type="text" show-edit />
</view>
@ -242,12 +323,11 @@
<view class="price">
<view class="label">合计</view>
<view>
共减<text class="number wx-font-medium" style="font-size: 32rpx;">2.7</text>
共减<text class="number wx-font-medium" style="font-size: 32rpx;"><auto-width-input
style="margin-left: 10rpx;" class="wx-font-medium" v-model="order.discount" fontSize="26rpx"
placeholder="优惠运费" color="#F10F1A" fontWeight="500" type="text" show-edit /></text>
需付款 <text class="number wx-font-medium">
{{ (Number(order.products[0].price) + Number(order.carriage)) ?
(Number(order.products[0].price)
+ Number(order.carriage)) :
0 }}</text>
{{ order.amountTo || '0.00' }}</text>
</view>
</view>
@ -298,6 +378,27 @@
</view>
</view>
</view>
<template v-if="screenshotImage">
<view class="w100 flex-center" style="margin-top: 40rpx;margin-bottom: 32rpx;">
<image style="width: 32rpx;height: 20rpx;" src="/static/image/shopping/jingdong/detail/like-left.png">
</image>
<text
style="font-size: 26rpx;color: #1A1A1A;line-height: 26rpx;font-weight: 500;margin: 0 10rpx;">你可能还喜欢</text>
<image style="width: 32rpx;height: 20rpx;" src="/static/image/shopping/jingdong/detail/like-right.png">
</image>
</view>
<!-- 上传图片 -->
<view class="upload-screenshot-box">
<view class="upload-screenshot-content w100">
<!-- <image class="upload-screenshot" src="/static/image/common/upload-screenshot.png" mode="aspectFit">
</image>
<view class="upload-screenshot-text">长按替换截图</view> -->
<image class="w100" :src="screenshotImage" mode="widthFix"></image>
</view>
</view>
</template>
</view>
<uni-popup ref="deliveryTimePopup" type="bottom">
@ -364,14 +465,17 @@ import NavBar from "@/components/nav-bar/nav-bar.vue";
import { waimaiType, waimaiClassfiy } from "../json/order.json";
import AutoWidthInput from "@/components/common/auto-width-input.vue";
import DateTimePicker from '@/components/dengrq-datetime-picker/dateTimePicker/index.vue';
import { stringUtil } from "@/utils/common.js";
const data = reactive({
navTitle: "新增外卖订单",
activeTab: 'weizhifu',
order: {}
order: {},
isEditMode: false,
currentId: ''
})
let { navTitle, activeTab, order } = toRefs(data);
let { activeTab, order, isEditMode, currentId } = toRefs(data);
const navTitle = computed(() => isEditMode.value ? "修改外卖订单" : "新增外卖订单");
/**
* 切换tab
@ -423,17 +527,78 @@ const switchTab = (item) => {
const chooseImage = (index = 0) => {
uni.chooseImage({
count: 1,
sizeType: ['compressed'],
sourceType: ['album', 'camera'],
sizeType: ['original'],
sourceType: ['album'],
success: (res) => {
const tempFile = res.tempFilePaths[0];
//
// #ifdef APP-PLUS || MP-WEIXIN
// App/
uni.saveFile({
tempFilePath: tempFile,
success: (saveRes) => {
order.value.products[index].image = saveRes.savedFilePath;
},
fail: () => {
// 退
order.value.products[index].image = tempFile;
}
});
// #endif
// #ifdef H5
// H5使 (Blob URL)
order.value.products[index].image = tempFile;
// #endif
}
});
};
const chooseDeliveryImage = () => {
//
const oldImagePath = order.value.deliveryImages;
uni.chooseImage({
count: 1,
sizeType: ['original'],
sourceType: ['album'],
success: (res) => {
const tempFilePath = res.tempFilePaths[0];
// #ifdef H5
// H5使
order.value.deliveryImages = tempFilePath;
uni.showToast({ title: '上传预览成功', icon: 'success' });
// #endif
// #ifdef APP-PLUS || MP-WEIXIN
uni.showLoading({ title: '处理图片中...' });
//
uni.saveFile({
tempFilePath: tempFilePath,
success: (saveRes) => {
// 1.
order.value.deliveryImages = saveRes.savedFilePath;
// 2.
if (oldImagePath && (oldImagePath.includes('_doc') || oldImagePath.includes('store_') || oldImagePath.includes('at_'))) {
uni.removeSavedFile({
filePath: oldImagePath,
success: () => console.log('旧配送图已清理:', oldImagePath),
fail: (err) => console.error('旧图清理失败:', err)
});
}
uni.showToast({ title: '上传成功', icon: 'success' });
},
fail: (err) => {
console.error('保存图片失败:', err);
order.value.deliveryImages = tempFilePath;
},
complete: () => {
uni.hideLoading();
}
});
// #endif
}
});
};
@ -464,24 +629,109 @@ const deleteProduct = (index) => {
order.value.products.splice(index, 1);
};
/**
* 通用检测并清理本地持久化文件
*/
const detectAndRemoveLocalFile = (path) => {
if (!path) return;
//
if (path.includes('_doc') || path.includes('store_') || path.includes('at_')) {
uni.removeSavedFile({
filePath: path,
success: () => console.log('本地冗余文件已物理清理:', path),
fail: (err) => console.log('物理清理跳过或失败:', err)
});
}
};
/**
* 切换推荐商品图片 (点击切换内置样式)
*/
const toggleRecommendImg = () => {
const style1 = '/static/image/shopping/jingdong/waimai/shop-recoomend/style-1.png';
const style2 = '/static/image/shopping/jingdong/waimai/shop-recoomend/style-2.png';
//
detectAndRemoveLocalFile(order.value.recommendImg);
if (order.value.recommendImg === style1) {
order.value.recommendImg = style2;
} else {
order.value.recommendImg = style1;
}
};
// ()
const chooseRecommendImg = () => {
const oldImagePath = order.value.recommendImg;
uni.chooseImage({
count: 1,
sizeType: ['original'],
sourceType: ['album'],
success: (res) => {
const tempFilePath = res.tempFilePaths[0];
// #ifdef H5
order.value.recommendImg = tempFilePath;
uni.showToast({ title: '更换预览成功', icon: 'success' });
// #endif
// #ifdef APP-PLUS || MP-WEIXIN
uni.showLoading({ title: '处理图片中...' });
uni.saveFile({
tempFilePath: tempFilePath,
success: (saveRes) => {
// 1.
detectAndRemoveLocalFile(oldImagePath);
// 2.
order.value.recommendImg = saveRes.savedFilePath;
uni.showToast({ title: '自定义图片成功', icon: 'success' });
},
fail: () => {
order.value.recommendImg = tempFilePath;
},
complete: () => {
uni.hideLoading();
}
});
// #endif
}
});
};
const orderRef = ref(data.order);
//
//
const initDefaultTimes = () => {
if (!order.value) return;
const now = new Date();
const timeStr = formatTime(now);
// 1. ()
// 1. ()
if (order.value.orderInfo) {
order.value.orderInfo.forEach(item => {
const label = item.label;
//
if ((label === '下单时间' || label === '支付时间' || label === '交易时间') && (!item.value || item.value.trim() === '' || item.value === '请输入')) {
item.value = timeStr;
}
//
if (label === '订单编号' && (!item.value || item.value.trim() === '' || item.value === '请输入')) {
item.value = generateRandomOrder().toString();
}
});
}
// 3.
if (activeTab.value === 'weizhifu' && (!order.value.statusDesc || order.value.statusDesc.trim() === '')) {
const m = (Math.floor(Math.random() * 30) + 1).toString().padStart(2, '0');
const s = Math.floor(Math.random() * 60).toString().padStart(2, '0');
order.value.statusDesc = `${m}:${s}`;
}
// 2. (+40min +65min)
if (order.value.productsInfo) {
if (!order.value.productsInfo.deliveryTime || order.value.productsInfo.deliveryTime.trim() === '' || order.value.productsInfo.deliveryTime === '请输入') {
@ -492,19 +742,34 @@ const initDefaultTimes = () => {
order.value.productsInfo.deliveryTime = `今天${startText}-${endText}`;
}
}
// Vue
order.value = { ...order.value };
};
onLoad((options) => {
console.log('接收参数:', options);
initDefaultTimes();
if (options.id) {
// ()
// data.isEditMode = true;
console.log('外卖页面接收参数:', options);
const editFlag = String(options.isEdit) === 'true' || options.isEdit === true;
if (editFlag && options.id) {
// 1.
isEditMode.value = true;
currentId.value = String(options.id);
// 2.
const list = uni.getStorageSync('jingdongShopping') || [];
const editData = list.find(item => String(item.id) === String(options.id));
if (editData) {
order.value = JSON.parse(JSON.stringify(editData));
if (editData.activeTab) {
activeTab.value = editData.activeTab;
}
console.log('编辑模式激活,数据回显:', activeTab.value);
}
} else {
// Tab
//
initDefaultTimes();
switchTab({ key: activeTab.value });
}
});
@ -514,6 +779,34 @@ const timepopup = ref(null); // 单点时间选择器引用
const deliveryHours = Array.from({ length: 24 }, (_, i) => i.toString().padStart(2, '0'));
const deliveryMinutes = Array.from({ length: 60 }, (_, i) => i.toString().padStart(2, '0'));
const deliveryPickerValue = ref([11, 17, 0, 11, 32]);
// (0-59, 0-59)
const countdownRange = ref([
Array.from({ length: 60 }, (_, i) => i.toString().padStart(2, '0')),
Array.from({ length: 60 }, (_, i) => i.toString().padStart(2, '0'))
]);
//
const countdownValue = computed(() => {
if (!order.value || !order.value.statusDesc) return [29, 59];
const parts = order.value.statusDesc.split(':');
if (parts.length !== 2) return [29, 59];
return [
parseInt(parts[0]) || 0,
parseInt(parts[1]) || 0
];
});
/**
* 处理倒计时修改
*/
const handleCountdownChange = (e) => {
const val = e.detail.value;
const m = countdownRange.value[0][val[0]];
const s = countdownRange.value[1][val[1]];
order.value.statusDesc = `${m}:${s}`;
};
const editingField = ref('deliveryTime');
//
@ -579,6 +872,24 @@ const onClickItemInfo = (item) => {
}
}
/**
* 点击备餐时间取餐时间
*/
const onClickTrackingTime = () => {
const label = activeTab.value === 'beicanzhong' ? '备餐时间' : '取餐时间';
//
selectItemInfo.value = {
label: label,
value: order.value.trackingTime || formatTime(new Date()),
type: 'time',
key: 'trackingTime'
};
datePickerData.value.selectDate = selectItemInfo.value.value;
datePickerData.value.endDate = "2099-12-31 23:59:59";
if (timepopup.value) timepopup.value.open();
}
/**
* 时间段选择器变化回调
*/
@ -592,6 +903,10 @@ const onTimeRangeChange = (e) => {
const settmes = () => {
if (selectItemInfo.value.type == 'time') {
selectItemInfo.value.value = datePickerData.value.selectDate;
// order.trackingTime
if (selectItemInfo.value.key === 'trackingTime') {
order.value.trackingTime = datePickerData.value.selectDate;
}
} else if (selectItemInfo.value.type == 'timeRange') {
const cols = timeRangePickerData.value.rangeCols;
const sel = timeRangePickerData.value.rangeSelect;
@ -733,12 +1048,83 @@ const generateDefaultExpectedTime = () => {
return `${h}:${min}-${h2}:${min2}`;
};
/**
* 计算商品总价
*/
const totalProductPrice = computed(() => {
if (!order.value || !order.value.products) return '0.00';
const total = order.value.products.reduce((sum, item) => {
const price = parseFloat(item.price) || 0;
const countStr = String(item.count || '1');
// "x"
const count = parseInt(countStr.replace(/[^\d]/g, '')) || 0;
return sum + (price * count);
}, 0);
return total.toFixed(2);
});
//
watch(() => order.value.products, (newProducts) => {
if (newProducts && newProducts.length > 0) {
order.value.totalPrice = totalProductPrice.value;
}
}, { deep: true, immediate: true });
/**
* 安全转换数值
*/
const parseMoney = (val) => {
if (!val) return 0;
if (typeof val === 'number') return val;
//
const num = parseFloat(String(val).replace(/[^\d.]/g, ''));
return isNaN(num) ? 0 : num;
}
//
// = + + -
watch([
() => order.value.totalPrice,
() => order.value.carriage,
() => order.value.packagingFee,
() => order.value.discount
], ([tp, c, pf, d]) => {
const total = parseMoney(tp) + parseMoney(c) + parseMoney(pf) - parseMoney(d);
order.value.amountTo = total > 0 ? total.toFixed(2) : '0.00';
}, { immediate: true });
/**
* 确认订单
*/
const onConfirm = () => {
let list = uni.getStorageSync('jingdongShopping') || [];
if (!Array.isArray(list)) {
list = [];
}
order.value.activeTab = activeTab.value;
if (isEditMode.value) {
// ID ()
const index = list.findIndex(item => String(item.id) === String(currentId.value));
if (index > -1) {
list[index] = JSON.parse(JSON.stringify(order.value));
} else {
//
list.push(JSON.parse(JSON.stringify(order.value)));
}
} else {
// UUID
order.value.id = stringUtil.uuid();
list.push(JSON.parse(JSON.stringify(order.value)));
}
uni.setStorageSync('jingdongShopping', list);
console.log("确认订单", order.value);
uni.showToast({
title: isEditMode.value ? '修改成功' : '保存成功',
icon: 'success'
});
setTimeout(() => {
uni.navigateBack();
}, 1500);
}
</script>
@ -1085,6 +1471,15 @@ const onConfirm = () => {
}
}
.shop-recommend {
height: 208rpx;
background-color: #FFFFFF;
margin: 16rpx;
border-radius: 24rpx;
overflow: hidden;
margin-top: 4rpx;
}
.product-card {
padding: 28rpx 22rpx;
margin: 16rpx;
@ -1372,7 +1767,7 @@ const onConfirm = () => {
display: flex;
justify-content: space-between;
margin-top: 24rpx;
height: 38rpx;
min-height: 38rpx;
.label {
font-size: 26rpx;
@ -1448,4 +1843,21 @@ const onConfirm = () => {
margin-left: 0;
flex-shrink: 0;
}
.tips {
display: inline-block;
margin: 0 24rpx;
font-size: 18rpx;
color: #87868E;
line-height: 24rpx;
margin-top: 16rpx;
}
.time-box {
text-align: left;
margin: 8rpx 20rpx;
padding: 16rpx;
background-color: #ffffff;
border-radius: 24rpx;
}
</style>

View File

@ -403,20 +403,24 @@
"shopType": "waimai",
"shopName": "",
"status": "等待付款",
"statusDesc": "29分钟",
"statusDesc": "",
"deliveryTime": "11:17-11:32",
"address": "",
"phone": "",
"consignee": "",
"discountCarriage": "",
"carriage": "",
"packagingFee": "",
"recommendImg": "/static/image/shopping/jingdong/waimai/shop-recoomend/style-1.png",
"discount": "0.00",
"amountTo": "",
"products": [
{
"image": "",
"title": "",
"desc": "一人份",
"price": "",
"count": "数量x1"
"count": "1"
}
],
"productsInfo": {
@ -456,20 +460,26 @@
"id": 9632554,
"shopType": "waimai",
"type": "beicanzhong",
"shopName": "安野屋 (AARYE) 京...",
"status": "骑手到店取餐中",
"statusColor": "red",
"shopName": "",
"status": "商家备餐中",
"trackingTitle": "10: 22-10: 55",
"trackingDesc": "骑手已到店,大王",
"trackingDesc": "骑手到店取餐中",
"trackingTime": "2026-03-10 15: 14: 30",
"deliveryImages": "",
"discountCarriage": "",
"discount": "0.00",
"carriage": "",
"packagingFee": "",
"recommendImg": "/static/image/shopping/jingdong/waimai/shop-recoomend/style-1.png",
"amountTo": "",
"products": [
{
"image": "/static/image/shopping/jingdong/product1.png",
"title": "超值哈哈哈哈哈哈哈哈哈哈哈哈哈哈好热 少糖",
"desc": "不支持7天无理由退货",
"service": "",
"tags": [],
"price": "69.00",
"count": "1",
"image": "",
"title": "",
"desc": "一人份",
"price": "",
"count": "1"
}
],
"productsInfo": {
@ -520,10 +530,18 @@
"shopType": "waimai",
"type": "qvcanzhong",
"shopName": "安野屋 (AARYE) 京...",
"status": "商家备餐中",
"status": "骑手到店取餐中",
"statusColor": "red",
"trackingTitle": "10: 22-10: 55",
"trackingDesc": "商家已接单,商品备餐中",
"trackingDesc": "骑手已到店,大王",
"trackingTime": "2026-03-10 15: 14: 30",
"recommendImg": "/static/image/shopping/jingdong/waimai/shop-recoomend/style-1.png",
"deliveryImages": "",
"discountCarriage": "",
"carriage": "",
"packagingFee": "",
"discount": "0.00",
"amountTo": "",
"products": [
{
"image": "/static/image/shopping/jingdong/product1.png",
@ -576,7 +594,7 @@
"value": "缺货时与我电话沟通",
"type": "text"
}
],
]
},
"yiwancheng": {
"id": 78456211,
@ -585,6 +603,12 @@
"shopName": "瑞幸咖啡",
"status": "完成",
"statusColor": "gray",
"recommendImg": "/static/image/shopping/jingdong/waimai/shop-recoomend/style-1.png",
"packagingFee": "",
"discountCarriage": "",
"carriage": "",
"discount": "0.00",
"amountTo": "",
"products": [
{
"image": "/static/image/shopping/jingdong/product1.png",
@ -649,6 +673,12 @@
"shopName": "瑞幸咖啡",
"status": "已取消",
"statusColor": "gray",
"recommendImg": "/static/image/shopping/jingdong/waimai/shop-recoomend/style-1.png",
"packagingFee": "",
"discountCarriage": "",
"discount": "0.00",
"carriage": "",
"amountTo": "",
"products": [
{
"image": "/static/image/shopping/jingdong/product1.png",

View File

@ -187,9 +187,15 @@ const editOrder = () => {
const item = actionMenuState.value.item;
if (!item) return;
showActionMenu.value = false;
uni.navigateTo({
url: '/pages/shopping/jingdong/add-order/add-order?id=' + item.id + '&type=' + item.shopType + '&isEdit=true'
});
if (item.shopType == 'waimai') {
uni.navigateTo({
url: '/pages/shopping/jingdong/add-waimai/add-waimai?id=' + item.id + '&type=' + item.shopType + '&isEdit=true'
});
} else {
uni.navigateTo({
url: '/pages/shopping/jingdong/add-order/add-order?id=' + item.id + '&type=' + item.shopType + '&isEdit=true'
});
}
};
/**
@ -215,23 +221,35 @@ const handleCardClick = (item) => {
*/
const handleDeleteOrder = () => {
const itemToDel = actionMenuState.value.item;
if (!itemToDel) return;
// 1. ( + )
const productImage = itemToDel?.products?.[0]?.image;
const courierImage = itemToDel?.courierImg;
// 1.
const pathsToCleanup = [];
//
if (itemToDel.courierImg) pathsToCleanup.push(itemToDel.courierImg);
if (itemToDel.deliveryImages) pathsToCleanup.push(itemToDel.deliveryImages);
if (itemToDel.recommendImg) pathsToCleanup.push(itemToDel.recommendImg);
//
if (itemToDel.products && Array.isArray(itemToDel.products)) {
itemToDel.products.forEach(p => {
if (p.image) pathsToCleanup.push(p.image);
});
}
const cleanupFile = (path) => {
//
if (path && (path.includes('_doc/') || path.includes('_downloads/') || path.includes('store_') || path.includes('at_'))) {
uni.removeSavedFile({
filePath: path,
success: () => console.log('文件删除成功:', path),
fail: (err) => console.error('文件删除失败:', err)
success: () => console.log('物理文件清理成功:', path),
fail: (err) => console.log('物理文件清理跳过或失败:', err)
});
}
};
cleanupFile(productImage);
cleanupFile(courierImage);
pathsToCleanup.forEach(path => cleanupFile(path));
// 2.
const realIndex = mockOrderList.value.findIndex(item => item === itemToDel);

View File

@ -1,7 +1,8 @@
<template>
<view>
<nav-bar title="订单详情" bgColor="#F2F3F7" :buttonGroup="buttonGroup" @button-click="util.clickTitlePopupButton"
tipLayerType="jingdong-shopping-order-detail-tip" isTipLayer tipLayerText="编辑订单信息">
<nav-bar title="订单详情" bgColor="#F2F3F7" isBack :buttonGroup="buttonGroup"
@button-click="util.clickTitlePopupButton" tipLayerType="jingdong-shopping-order-detail-tip" isTipLayer
tipLayerText="编辑订单信息">
<!-- <template v-slot:left>
<image class="back-icon" src="/static/image/common/back.png" mode="aspectFit"></image>
</template> -->
@ -437,7 +438,7 @@
<view class="btn-box">
<view v-if="order.status == '等待付款'" class="big-btn ">立即支付<text class="wx-font-medium money">¥69</text>
</view>
<view v-else class="flex-align-center 00">
<view v-else class="flex-align-center w00">
<view v-for="btn in bottomBtns" :key="btn" class="bottom-btn " :class="{ red: btn == '再次购买' }">
{{ btn }}
</view>

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 144 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 121 KiB