alipay-emulator/pages/shopping/jingdong/add-waimai/add-waimai.vue

1451 lines
37 KiB
Vue

<template>
<view>
<NavBar :title="navTitle" bgColor="#F2F3F7" isRightButton @right-click="onConfirm"></NavBar>
<view class="page-container">
<view class="tab-box" v-if="!isEditMode">
<view class="tab" :class="{ active: item.key == activeTab }" v-for="item in waimaiType" :key="item.key"
@click="switchTab(item)">
<text>{{ item.label }}</text>
</view>
</view>
<!-- 未支付 -->
<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="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>
</text> 送达</view>
<!-- <view class="flex flex-justify-between flex-align-center" style="margin-top: 32rpx;">
<text>更多</text>
<view class="flex flex-align-center">
<view class="btn cancel-btn">取消订单</view>
<view class="btn pay-btn">立即支付 ¥14.5</view>
</view>
</view> -->
</view>
<view class="card location-box flex flex-justify-between flex-align-center">
<view class="w100">
<view class="card-title">
<image class="location-icon" src="/static/image/shopping/jingdong/waimai/location.png">
</image>
<auto-width-input class="name" placeholder="请输入姓名" v-model="order.consignee"
fontSize="30rpx" type="text" color="#1A1A1A" fontWeight="500" show-edit />
<auto-width-input class="phone" placeholder="请输入号码" v-model="order.phone" fontSize="26rpx"
type="number" color="#87868E" show-edit />
<!-- <text class="desc">号码保护中</text> -->
</view>
<view class="loction-text">
<auto-width-input class="value" placeholder="请输入收货地址" v-model="order.address"
fontSize="26rpx" type="textarea" color="#87868E" show-edit />
</view>
</view>
</view>
</template>
<!-- 备餐中/取餐中 -->
<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 ||
'10:43-10:58'
}}</text>
<text>预计送达</text>
</view>
<view class="desc">{{ activeTab == 'qvcanzhong' ? '骑手已到店,取餐中' : '商家备餐中' }}</view>
<view class="progress">
<view class="progress-bar">
<view class="progress-bar-fill"
:style="{ width: activeTab == 'qvcanzhong' ? '70%' : '50%' }">
</view>
<image class="progress-icon fukuan" src="/static/image/shopping/jingdong/waimai/fukuan.png">
</image>
<image class="progress-icon beican" src="/static/image/shopping/jingdong/waimai/beican.png">
</image>
<image class="progress-icon songcan"
:src="`/static/image/shopping/jingdong/waimai/${activeTab == 'qvcanzhong' ? 'qvcan' : 'songcan'}.png`">
</image>
<image class="progress-icon qianshou"
src="/static/image/shopping/jingdong/waimai/qianshou.png">
</image>
</view>
</view>
<view class="image-box flex-center">
<image class="add-img" src="/static/image/common/add.png" mode="aspectFill"></image>
</view>
<view class="btn-box flex-align-center">
<view class="btn">查看发票</view>
<view class="btn">订单跟踪</view>
<view class="btn">申请退款</view>
<view class="btn">联系商家</view>
</view>
</view>
<view class="notice-box flex-align-center">
<image class="icon" src="/static/image/shopping/jingdong/waimai/notice.png" mode="aspectFill">
</image>
<text class="flex-1 notice-text">开启系统通知,第一时间获取订单聊天信息</text>
<view class="open-btn">去开启</view>
</view>
</view>
<!-- 已完成/已取消 -->
<view v-if="order.type == 'yiwancheng' || order.type == 'yiquxiao'" class="weizhifu card yiwancheng">
<view class="title flex-align-center">{{ order.type == 'yiwancheng' ? '订单已完成' : '订单已取消' }} <uni-icons
style="margin-left: 6rpx;" type="right" size="12" color="#1A1A1A"></uni-icons>
</view>
<view v-if="order.type == 'yiwancheng'" class="desc">订单已送达,请厉行节约,拒绝浪费,期待能再次光临</view>
<view v-if="order.type == 'yiquxiao'" class="desc quxiao">抱歉本次未能让您满意,我们会努力改进</view>
<view class="flex flex-justify-between flex-align-center" style="margin-top: 32rpx;">
<text style="font-size: 24rpx;color: #87868E;line-height: 24rpx;">{{ order.type == 'yiwancheng' ?
'更多' :
'' }}</text>
<view class="flex flex-align-center">
<view v-if="order.type == 'yiquxiao'" class="btn cancel-btn">删除订单</view>
<view class="btn cancel-btn">联系商家</view>
<view v-if="order.type == 'yiwancheng'" class="btn cancel-btn">联系骑手</view>
<view v-if="order.type == 'yiwancheng'" class="btn cancel-btn primary">评价晒单</view>
<view class="btn cancel-btn primary">再次购买</view>
</view>
</view>
</view>
<!-- 已取消 -->
<view v-if="order.type == 'yiquxiao'" class="cancel-progress-box">
<view class="cancel-content">
<view class="title-row">
<view class="left">
<image class="icon" src="/static/image/shopping/jingdong/detail/tuikuan.png"
mode="aspectFit">
</image>
<text class="title">取消/退款进度</text>
</view>
<uni-icons type="right" size="10" color="#1A1A1A"></uni-icons>
</view>
<text class="desc">您的订单已取消,请查看取消进度详情。</text>
</view>
</view>
</view>
<!-- 商品卡片 -->
<view class="product-card">
<view class="title-box">
<view class="left-box">
<image class="img" src="/static/image/shopping/jingdong/waimai/waimai.png">
</image>
<view class="title flex animate-scale">
<auto-width-input v-model="order.shopName" fontSize="26rpx" fontWeight="500"
placeholder="请输入店铺名称" color="#1A1A1A" show-edit />
</view>
</view>
<uni-icons class="right-icon" size="14" color="#1A1A1A" type="right"></uni-icons>
</view>
<!-- 商品列表 -->
<view class="product-info-wrapper" v-for="(product, pIndex) in order.products" :key="pIndex">
<view class="product-info">
<view class="image-box" @click="chooseImage(pIndex)">
<view v-if="!product.image" class="w100 h100 flex flex-center"
style="background-color: #eeeeee;">
<image style="width: 60rpx;height: 60rpx;" src="/static/image/common/add.png"></image>
</view>
<image v-else class="w100 h100" :src="product.image"></image>
</view>
<view class="info-box">
<view class="name-row flex flex-justify-between">
<auto-width-input class="name flex-1" v-model="product.title" fontSize="26rpx"
fontWeight="500" placeholder="商品标题名称" color="#1A1A1A" show-edit type="textarea" />
<uni-icons v-if="order.products.length > 1" type="trash" size="18" color="#FE1528"
@click="deleteProduct(pIndex)" style="margin-left: 20rpx;"></uni-icons>
</view>
<view class="flex flex-align-center animate-scale">
<auto-width-input class="desc" v-model="product.desc" type="textarea" fontSize="22rpx"
placeholder="商品描述" color="#87868E" show-edit />
</view>
<view class="flex flex-align-center animate-scale">
<auto-width-input class="desc" v-model="product.count" type="textarea" fontSize="22rpx"
placeholder="请输入数量" color="#8D8D8D" show-edit />
</view>
<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"
fontSize="36rpx" placeholder="0.00" color="#1A1A1A" fontWeight="500" show-edit />
</view>
</view>
</view>
<view v-if="pIndex < order.products.length - 1" class="product-divider"></view>
</view>
<!-- 添加商品按钮 -->
<view class="add-product-btn flex flex-center" @click="addProduct">
<image class="icon" src="/static/image/common/add.png" style="width: 24rpx; height: 24rpx;"></image>
<text>添加商品</text>
</view>
<view v-if="activeTab == 'weizhifu' || activeTab == 'yiquxiao'" class="order-info"
style="margin-top: 24rpx;">
<view class="item flex-justify-between">
<text class="label shrink-0">餐具数量</text>
<view class="flex-1 flex-align-center"
style="justify-content: flex-end;width: 300rpx;overflow-x: auto;margin-left: 20rpx;">
<auto-width-input class="value" v-model="order.productsInfo.cutleryCount" fontSize="26rpx"
type="textarea" color="#1A1A1A" show-edit />
</view>
</view>
<view class="item flex-justify-between">
<text class="label shrink-0">配送偏好</text>
<view class="flex-1 flex-align-center"
style="justify-content: flex-end;width: 300rpx;overflow-x: auto;margin-left: 20rpx;">
<auto-width-input class="value" v-model="order.productsInfo.deliveryPreference" type="textarea"
fontSize="26rpx" color="#1A1A1A" show-edit />
</view>
</view>
<view class="item flex-justify-between">
<text class="label shrink-0">如遇缺货</text>
<view class="flex-1 flex-align-center"
style="justify-content: flex-end;width: 300rpx;overflow-x: auto;margin-left: 20rpx;">
<auto-width-input class="value" v-model="order.productsInfo.outOfStock" type="textarea"
fontSize="26rpx" color="#1A1A1A" show-edit />
</view>
</view>
</view>
</view>
<!-- 商品总价 -->
<view v-if="activeTab == 'weizhifu' || activeTab == 'yiquxiao'" class="product-price">
<view class="title flex-justify-between">
<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 />
</view>
</view>
<view class="item flex-justify-between">
<view class="flex flex-align-center">
<text class="label">运费</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="number" 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="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">
¥{{ (Number(order.products[0].price) + Number(order.carriage)) ?
(Number(order.products[0].price)
+ Number(order.carriage)) :
0 }}</text>
</view>
</view>
</view>
<!-- 订单信息 -->
<view class="order-info-box">
<view class="title flex-justify-between">
<text>订单信息</text>
</view>
<!-- 用户信息 -->
<template v-if="activeTab != 'weizhifu' && activeTab != 'yiquxiao'">
<view class="item flex-justify-between"
@click="onClickItemInfo({ label: '期望时间', type: 'timeRange', key: 'deliveryTime' })">
<text class="label shrink-0">期望时间</text>
<view class="flex-1 flex-align-center"
style="justify-content: flex-end;width: 300rpx;overflow-x: auto;margin-left: 20rpx;">
<auto-width-input class="value" v-model="order.productsInfo.deliveryTime" fontSize="26rpx"
type="text" color="#1A1A1A" show-edit />
</view>
</view>
<view class="item flex-justify-between">
<text class="label shrink-0">配送地址</text>
<view class="flex-1 flex-align-center"
style="justify-content: flex-end;width: 300rpx;overflow-x: auto;margin-left: 20rpx;">
<auto-width-input class="value" v-model="order.address" fontSize="26rpx" type="textarea"
color="#1A1A1A" show-edit />
</view>
</view>
<view class="item flex-justify-between">
<text class="label shrink-0">餐具数量</text>
<view class="flex-1 flex-align-center"
style="justify-content: flex-end;width: 300rpx;overflow-x: auto;margin-left: 20rpx;">
<auto-width-input class="value" v-model="order.productsInfo.cutleryCount" fontSize="26rpx"
type="text" color="#1A1A1A" show-edit />
</view>
</view>
</template>
<view class="order-info">
<view class="item flex-justify-between" v-for="item in order.orderInfo" @click="onClickItemInfo(item)">
<text class="label shrink-0">{{ item.label }}</text>
<view class="flex-1 flex-align-center"
style="justify-content: flex-end;width: 300rpx;overflow-x: auto;margin-left: 20rpx;">
<auto-width-input class="value" v-model="item.value" fontSize="26rpx" color="#1A1A1A"
:type="item.type ? item.type : 'text'" show-edit />
</view>
</view>
</view>
</view>
</view>
<uni-popup ref="deliveryTimePopup" type="bottom">
<view class="timeBox">
<view class="titleBox">
<view class="title" @click="closeDeliveryPopup">取消</view>
<view class="btn" @click="confirmDeliveryTime">确定</view>
</view>
<view class="picker-container">
<picker-view class="range-picker-view" :value="deliveryPickerValue" @change="onDeliveryPickerChange">
<picker-view-column>
<view class="picker-item" v-for="item in deliveryHours" :key="item">{{ item }}</view>
</picker-view-column>
<picker-view-column>
<view class="picker-item" v-for="item in deliveryMinutes" :key="item">{{ item }}</view>
</picker-view-column>
<picker-view-column>
<view class="picker-item flex-center">-</view>
</picker-view-column>
<picker-view-column>
<view class="picker-item" v-for="item in deliveryHours" :key="item">{{ item }}</view>
</picker-view-column>
<picker-view-column>
<view class="picker-item" v-for="item in deliveryMinutes" :key="item">{{ item }}</view>
</picker-view-column>
</picker-view>
</view>
</view>
</uni-popup>
<uni-popup ref="timepopup" type="bottom">
<!-- 单点时间选择 -->
<view v-if="selectItemInfo.type == 'time'" class="timeBox">
<view class="titleBox">
<view class="title" @click="closeTimePicker">取消</view>
<view class="btn" @click="settmes">确定</view>
</view>
<DateTimePicker :defaultDate="datePickerData.selectDate" :minDate="datePickerData.startDate"
:maxDate="datePickerData.endDate" :mode="4" @onChange="onChangeStartDate" />
</view>
<!-- 时间段选择 -->
<view v-if="selectItemInfo.type == 'timeRange'" class="timeBox">
<view class="titleBox">
<view class="title" @click="closeTimePicker">取消</view>
<view class="btn" @click="settmes">确定</view>
</view>
<view class="picker-container">
<picker-view class="range-picker-view" :value="timeRangePickerData.rangeSelect"
@change="onTimeRangeChange">
<picker-view-column v-for="(col, colIdx) in timeRangePickerData.rangeCols" :key="colIdx">
<view class="picker-item" v-for="(item, idx) in col" :key="idx">{{ item }}</view>
</picker-view-column>
</picker-view>
</view>
</view>
</uni-popup>
</template>
<script setup>
import { onLoad } from "@dcloudio/uni-app";
import { ref, computed, watch, onMounted, nextTick, reactive, toRefs } from "vue";
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';
const data = reactive({
navTitle: "新增外卖订单",
activeTab: 'weizhifu',
order: {}
})
let { navTitle, activeTab, order } = toRefs(data);
/**
* 切换tab
* @param index
*/
/**
* @param item
*/
const switchTab = (item) => {
// 备份当前数据用于跨页签恢复
const oldData = JSON.parse(JSON.stringify(order.value));
data.activeTab = item.key;
order.value = JSON.parse(JSON.stringify(waimaiClassfiy[data.activeTab]));
// 恢复基础通用字段
if (oldData) {
order.value.id = oldData.id || order.value.id;
order.value.consignee = oldData.consignee || order.value.consignee;
order.value.phone = oldData.phone || order.value.phone;
order.value.fullAddress = oldData.fullAddress || order.value.fullAddress;
order.value.shopName = oldData.shopName || order.value.shopName;
order.value.shopType = oldData.shopType || order.value.shopType;
// 恢复商品列表 (如果旧数据有商品,尝试继承,防止切 Tab 丢失已添加内容)
if (oldData.products && oldData.products.length > 0) {
order.value.products = JSON.parse(JSON.stringify(oldData.products));
}
// 恢复 orderInfo 中的手动输入值
if (order.value.orderInfo && oldData.orderInfo) {
order.value.orderInfo.forEach(newInfo => {
const target = oldData.orderInfo.find(o => o.label === newInfo.label);
if (target && target.value) {
newInfo.value = target.value;
}
});
}
// 统一调用初始化逻辑补全缺失时间(下单、支付、交易、期望时间)
initDefaultTimes();
}
}
/**
* 选择图片
* @param index 商品索引
*/
const chooseImage = (index = 0) => {
uni.chooseImage({
count: 1,
sizeType: ['compressed'],
sourceType: ['album', 'camera'],
success: (res) => {
const tempFile = res.tempFilePaths[0];
// 将临时路径持久化
uni.saveFile({
tempFilePath: tempFile,
success: (saveRes) => {
order.value.products[index].image = saveRes.savedFilePath;
}
});
}
});
};
/**
* 添加商品
*/
const addProduct = () => {
if (!order.value.products) {
order.value.products = [];
}
order.value.products.push({
image: "",
title: "",
desc: "",
service: "不支持7天无理由退货",
price: "0.00",
count: 1
});
};
/**
* 删除商品
* @param index
*/
const deleteProduct = (index) => {
if (!order.value.products || order.value.products.length <= 1) return;
order.value.products.splice(index, 1);
};
const orderRef = ref(data.order);
// 初始化默认时间值
const initDefaultTimes = () => {
if (!order.value) return;
const now = new Date();
const timeStr = formatTime(now);
// 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;
}
});
}
// 2. 初始化期望时间 (当前+40min 至 当前+65min)
if (order.value.productsInfo) {
if (!order.value.productsInfo.deliveryTime || order.value.productsInfo.deliveryTime.trim() === '' || order.value.productsInfo.deliveryTime === '请输入') {
const start = new Date(now.getTime() + 40 * 60000);
const end = new Date(now.getTime() + 65 * 60000);
const startText = `${start.getHours().toString().padStart(2, '0')}:${start.getMinutes().toString().padStart(2, '0')}`;
const endText = `${end.getHours().toString().padStart(2, '0')}:${end.getMinutes().toString().padStart(2, '0')}`;
order.value.productsInfo.deliveryTime = `今天${startText}-${endText}`;
}
}
// 强制通知 Vue 更新
order.value = { ...order.value };
};
onLoad((options) => {
console.log('接收参数:', options);
initDefaultTimes();
if (options.id) {
// 编辑模式 (假设从缓存或全局存储获取,此处仅作结构演示)
// data.isEditMode = true;
} else {
// 新增模式:初始化第一个 Tab 的默认数据
switchTab({ key: activeTab.value });
}
});
const deliveryTimePopup = ref(null);
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]);
const editingField = ref('deliveryTime');
// 单点时间选择状态
const selectItemInfo = ref({
type: '',
value: '',
label: '',
id: ''
});
const datePickerData = ref({
selectDate: "",
startDate: "",
endDate: "",
});
// 时间段选择器数据 (参考 add-order)
const timeRangePickerData = ref({
rangeCols: [[], [], [], [], []], // 年, 月, 日, 开始时间, 结束时间
rangeSelect: [0, 5, 14, 9, 22]
});
/**
* 初始化时间段选择器列
*/
const initTimeRangeColumns = () => {
const years = [];
const now = new Date();
const currentYear = now.getFullYear();
for (let i = currentYear - 5; i <= currentYear + 5; i++) {
years.push(i + '年');
}
const months = Array.from({ length: 12 }, (_, i) => (i + 1).toString().padStart(2, '0') + '月');
const days = Array.from({ length: 31 }, (_, i) => (i + 1).toString().padStart(2, '0') + '日');
const hours = Array.from({ length: 24 }, (_, i) => i.toString().padStart(2, '0') + ':00');
timeRangePickerData.value.rangeCols = [years, months, days, hours, hours];
};
onMounted(() => {
initTimeRangeColumns();
});
/**
* 点击信息项
*/
const onClickItemInfo = (item) => {
if (item.label === '收货信息') return;
if (item.type == 'time') {
datePickerData.value.selectDate = item.value || formatTime(new Date());
datePickerData.value.endDate = "2099-12-31 23:59:59";
selectItemInfo.value = item;
if (timepopup.value) timepopup.value.open();
} else if (item.type == 'timeRange') {
selectItemInfo.value = item;
// 预设选择器索引 (默认设为今天)
const now = new Date();
const yIdx = timeRangePickerData.value.rangeCols[0].indexOf(now.getFullYear() + '年');
const mIdx = now.getMonth();
const dIdx = now.getDate() - 1;
timeRangePickerData.value.rangeSelect = [yIdx > -1 ? yIdx : 5, mIdx, dIdx, 9, 22];
if (timepopup.value) timepopup.value.open();
}
}
/**
* 时间段选择器变化回调
*/
const onTimeRangeChange = (e) => {
timeRangePickerData.value.rangeSelect = e.detail.value;
}
/**
* 设置时间
*/
const settmes = () => {
if (selectItemInfo.value.type == 'time') {
selectItemInfo.value.value = datePickerData.value.selectDate;
} else if (selectItemInfo.value.type == 'timeRange') {
const cols = timeRangePickerData.value.rangeCols;
const sel = timeRangePickerData.value.rangeSelect;
const yStr = cols[0][sel[0]].replace('年', '');
const mStr = cols[1][sel[1]].replace('月', '');
const dStr = cols[2][sel[2]].replace('日', '');
const startStr = cols[3][sel[3]];
const endStr = cols[4][sel[4]];
// 判断是否为今天
const now = new Date();
const isToday = parseInt(yStr) === now.getFullYear() &&
parseInt(mStr) === (now.getMonth() + 1) &&
parseInt(dStr) === now.getDate();
const dateLabel = isToday ? '今天' : `${yStr}-${mStr}-${dStr} `;
const resultValue = `${dateLabel}${startStr}-${endStr}`;
if (selectItemInfo.value.key === 'deliveryTime') {
order.value.productsInfo.deliveryTime = resultValue;
} else {
selectItemInfo.value.value = resultValue;
}
}
// 强制触发响应式
order.value = { ...order.value };
closeTimePicker();
}
/**
* 单点时间选择器变化回调
*/
const onChangeStartDate = (e) => {
datePickerData.value.selectDate = e;
}
/**
* 关闭单点时间选择器弹窗
*/
const closeTimePicker = () => {
if (timepopup.value) timepopup.value.close();
}
/**
* 配送时间点击事件
*/
const onDeliveryTimeClick = (field = 'deliveryTime') => {
editingField.value = field;
// 解析当前时间字符串设置索引
const currentOrder = data.order;
const timeStr = currentOrder[field];
if (timeStr) {
const parts = timeStr.split('-');
if (parts.length === 2) {
const start = parts[0].split(':');
const end = parts[1].split(':');
deliveryPickerValue.value = [
parseInt(start[0]) || 0,
parseInt(start[1]) || 0,
0,
parseInt(end[0]) || 0,
parseInt(end[1]) || 0
];
}
}
nextTick(() => {
if (deliveryTimePopup.value) deliveryTimePopup.value.open();
})
};
/**
* 关闭配送时间选择器弹窗
*/
const closeDeliveryPopup = () => {
if (deliveryTimePopup.value) deliveryTimePopup.value.close();
}
/**
* 配送时间选择器变化回调
*/
const onDeliveryPickerChange = (e) => {
deliveryPickerValue.value = e.detail.value;
};
/**
* 确认配送时间
*/
const confirmDeliveryTime = () => {
const val = deliveryPickerValue.value;
const startH = deliveryHours[val[0]];
const startM = deliveryMinutes[val[1]];
const endH = deliveryHours[val[3]];
const endM = deliveryMinutes[val[4]];
const finalTime = `${startH}:${startM}-${endH}:${endM}`;
// 直接操作 order.value (由于 toRefs 解构,它直接指向 data.order)
if (order.value) {
console.log(editingField.value, finalTime);
order.value[editingField.value] = finalTime;
// 显式触发全量替换以确保 UI 刷新
order.value = { ...order.value };
}
closeDeliveryPopup();
};
/**
* 格式化时间
*/
const formatTime = (date) => {
const y = date.getFullYear();
const m = (date.getMonth() + 1).toString().padStart(2, '0');
const d = date.getDate().toString().padStart(2, '0');
const h = date.getHours().toString().padStart(2, '0');
const min = date.getMinutes().toString().padStart(2, '0');
const s = date.getSeconds().toString().padStart(2, '0');
return `${y}-${m}-${d} ${h}:${min}:${s}`;
};
/**
* 生成随机订单号
*/
const generateRandomOrder = () => {
return Math.floor(Math.random() * 9000000000) + 1000000000;
};
/**
* 生成默认期望时间
*/
const generateDefaultExpectedTime = () => {
const date = new Date();
date.setMinutes(date.getMinutes() + 30);
const h = date.getHours().toString().padStart(2, '0');
const min = date.getMinutes().toString().padStart(2, '0');
const date2 = new Date(date.getTime() + 15 * 60000);
const h2 = date2.getHours().toString().padStart(2, '0');
const min2 = date2.getMinutes().toString().padStart(2, '0');
return `${h}:${min}-${h2}:${min2}`;
};
/**
* 确认订单
*/
const onConfirm = () => {
console.log("确认订单", order.value);
}
</script>
<style lang="less" scoped>
.tab-box {
background-color: #F2F3F7;
padding: 22rpx 24rpx;
display: flex;
justify-content: space-between;
align-items: center;
overflow: hidden;
overflow-x: scroll;
&::-webkit-scrollbar {
display: none;
/* 隐藏滚动条 */
}
.tab {
white-space: nowrap;
padding: 10rpx 20rpx;
border-radius: 28rpx;
margin-right: 16rpx;
background-color: #fff;
font-size: 26rpx;
color: #1a1a1a;
flex-shrink: 0;
font-weight: 500;
&.active {
color: #1B71F8;
}
}
}
.card {
background-color: #fff;
border-radius: 16rpx;
padding: 20rpx 22rpx;
margin: 0 24rpx;
}
.weizhifu {
.title {
font-size: 52rpx;
color: #1A1A1A;
line-height: 52rpx;
font-weight: 500;
}
.time {
font-size: 30rpx;
color: #1A1A1A;
line-height: 30rpx;
margin-top: 26rpx;
font-weight: 500;
.time-text {
color: #F10F1A;
}
}
.btn {
padding: 0 20rpx;
border-radius: 8rpx;
margin-left: 16rpx;
background-color: #fff;
font-size: 26rpx;
line-height: 60rpx;
color: #1a1a1a;
flex-shrink: 0;
font-weight: 500;
border: 0.5px solid #C7C7C7;
&.pay-btn {
width: 380rpx;
border: none;
color: #fff;
text-align: center;
background: linear-gradient(87deg, #FF435B 0%, #FE1528 100%);
}
}
}
.location-box {
margin-top: 16rpx;
padding: 26rpx 22rpx;
.location-icon {
width: 26rpx;
height: 26rpx;
}
.name {
font-size: 30rpx;
color: #1A1A1A;
line-height: 30rpx;
font-weight: 500;
margin: 0 16rpx;
}
.phone {
font-size: 26rpx;
color: #87868E;
line-height: 26rpx;
}
.desc {
background-color: #F3F3F5;
border-radius: 4rpx 4rpx 4rpx 4rpx;
font-size: 22rpx;
line-height: 22rpx;
color: #1A1A1A;
padding: 6rpx 8rpx;
margin-left: 16rpx;
}
.loction-text {
margin: 10rpx 0 0 42rpx;
font-size: 26rpx;
color: #87868E;
line-height: 40rpx;
}
}
.card-box {
margin: 0 16rpx;
background-color: #F6F7FB;
border: 2rpx solid #FFFFFF;
border-radius: 24rpx;
}
.beicanzhong-card {
padding: 24rpx 20rpx;
margin: 0;
.title {
font-size: 26rpx;
color: #1A1A1A;
font-weight: 500;
align-items: flex-end;
.time {
font-size: 60rpx;
color: #1A1A1A;
line-height: 52rpx;
font-weight: 500;
margin-right: 12rpx;
}
}
.desc {
margin-top: 32rpx;
font-size: 30rpx;
color: #1A1A1A;
line-height: 30rpx;
font-weight: 500;
}
.progress {
margin-top: 34rpx;
margin: 34rpx 27rpx 0;
.progress-bar {
position: relative;
width: 100%;
height: 24rpx;
background-color: #F2F2F4;
border-radius: 0rpx;
.progress-bar-fill {
width: 50%;
height: 100%;
background-color: #FE0F22;
}
.progress-icon {
position: absolute;
width: 54rpx;
height: 54rpx;
}
.fukuan {
left: 0;
top: 50%;
transform: translate(-50%, -50%);
}
.beican {
left: calc(30.3% - 27rpx/2);
top: 50%;
transform: translateY(-50%);
}
.songcan {
left: calc(60.6% + 27rpx/2);
top: 50%;
transform: translateY(-50%);
}
.qianshou {
right: 0;
top: 50%;
transform: translate(50%, -50%);
}
}
}
.image-box {
width: 100%;
height: 432rpx;
background-color: #F2F2F2;
margin-top: 40rpx;
border-radius: 28rpx;
overflow: hidden;
.add-img {
width: 112rpx;
height: 112rpx;
}
}
.btn-box {
justify-content: flex-end;
margin-top: 28rpx;
.btn {
font-size: 22rpx;
color: #1A1A1A;
line-height: 62rpx;
border: 0.5px solid #C7C7C7;
padding: 0 16rpx;
margin-left: 16rpx;
border-radius: 8rpx;
}
}
}
.notice-box {
padding: 20rpx 26rpx;
.icon {
width: 26rpx;
height: 26rpx;
}
.notice-text {
font-size: 22rpx;
color: #4D4D4D;
line-height: 22rpx;
margin: 0 10rpx;
}
.open-btn {
font-size: 22rpx;
color: #4D4D4D;
line-height: 22rpx;
border-radius: 8rpx 8rpx 8rpx 8rpx;
border: 1rpx solid #ACACAC;
padding: 10rpx 6rpx 8rpx;
}
}
.yiwancheng {
.title {
font-weight: 700;
}
.desc {
margin-top: 26rpx;
font-size: 26rpx;
color: #1A1A1A;
line-height: 30rpx;
&.quxiao {
font-size: 26rpx;
color: #87868E;
}
}
.btn {
font-size: 22rpx;
line-height: 62rpx;
}
.primary {
color: #ED1C04;
border-color: #ED1C04;
}
}
.cancel-progress-box {
display: flex;
align-items: center;
background-color: #ffffff;
border-radius: 24rpx;
margin: 10rpx 16rpx;
padding: 26rpx 22rpx 30rpx;
.cancel-icon {
width: 60rpx;
height: 60rpx;
margin-right: 16rpx;
}
.cancel-content {
flex: 1;
.title-row {
display: flex;
justify-content: space-between;
align-items: center;
.left {
display: flex;
align-items: center;
}
.icon {
width: 46rpx;
height: 46rpx;
margin-right: 16rpx;
}
.title {
font-size: 30rpx;
line-height: 30rpx;
font-weight: 500;
color: #1A1A1A;
}
}
.desc {
margin-top: 4rpx;
font-size: 26rpx;
color: #8C8C8C;
line-height: 36rpx;
}
}
}
.product-card {
padding: 28rpx 22rpx;
margin: 16rpx;
background-color: #FFFFFF;
border-radius: 24rpx;
.title-box {
display: flex;
align-items: center;
justify-content: space-between;
.left-box {
display: flex;
align-items: center;
flex: 1;
width: 200px;
.img {
width: 50rpx;
height: 26rpx;
margin-right: 8rpx;
flex-shrink: 0;
}
.title {
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
font-size: 26rpx;
color: #1A1A1A;
line-height: 28rpx;
font-weight: 700;
}
}
.right-icon {
margin-left: 12rpx;
}
}
.product-info-wrapper {
margin-top: 14rpx;
.product-divider {
height: 1rpx;
background-color: #f2f2f2;
margin: 20rpx 0;
}
}
.product-info {
display: flex;
position: relative;
.image-box {
width: 152rpx;
height: 152rpx;
border-radius: 24rpx;
overflow: hidden;
margin-right: 24rpx;
flex-shrink: 0;
}
.info-box {
flex: 1;
overflow: hidden;
.name-row {
align-items: flex-start;
}
.name {
width: 100%;
font-size: 26rpx;
color: #1A1A1A;
line-height: 28rpx;
font-weight: 700;
}
.desc {
margin-top: 16rpx;
font-size: 22rpx;
color: #87868E;
display: -webkit-box;
-webkit-box-orient: vertical;
-webkit-line-clamp: 2;
overflow: hidden;
}
.tag {
font-size: 22rpx;
color: #A96F24;
margin-top: 18rpx;
}
}
.price-box {
font-weight: 500;
font-size: 28rpx;
color: #1A1A1A;
.price {
font-weight: 500;
}
}
}
.add-product-btn {
margin-top: 30rpx;
height: 70rpx;
border: 1rpx dashed #C7C7C7;
padding: 0 20rpx;
display: flex;
align-items: center;
justify-content: center;
border-radius: 12rpx;
font-size: 24rpx;
color: #87868E;
.icon {
margin-right: 10rpx;
}
&:active {
background-color: #f9f9f9;
}
}
.btn-box {
display: flex;
justify-content: flex-end;
margin-top: 26rpx;
.btn {
display: flex;
align-items: center;
justify-content: center;
width: 144rpx;
white-space: nowrap;
font-size: 22rpx;
line-height: 22rpx;
height: 60rpx;
line-height: 58rpx;
border-radius: 8rpx;
border: 0.5px solid #C7C7C7;
color: #1A1A1A;
margin-left: 22rpx;
.icon {
width: 28rpx;
height: 28rpx;
margin-right: 6rpx;
}
}
.red {
color: #F10F1A;
border: 0.5px solid #F10F1A;
}
.img {
width: 32rpx;
height: 32rpx;
flex-shrink: 0;
margin-right: 14rpx;
}
}
.name {
font-size: 26rpx;
color: #1A1A1A;
}
.text {
font-size: 22rpx;
color: #87868E;
line-height: 24rpx;
margin-top: 8rpx;
}
.item {
display: flex;
justify-content: space-between;
margin-top: 26rpx;
.label {
font-size: 26rpx;
color: #87868E;
line-height: 26rpx;
}
.value {
font-size: 26rpx;
color: #1A1A1A;
line-height: 26rpx;
text-align: right;
}
}
}
.product-price {
background-color: #FFFFFF;
padding: 38rpx 22rpx;
border-radius: 24rpx;
margin: 16rpx;
.title {
font-size: 30rpx;
color: #1A1A1A;
line-height: 30rpx;
font-weight: 700;
}
.item {
margin-top: 36rpx;
.label {
color: #87868E;
font-size: 26rpx;
line-height: 26rpx;
}
.value {
font-size: 32rpx;
line-height: 32rpx;
font-weight: 500;
}
}
.price {
display: flex;
justify-content: space-between;
margin-top: 32rpx;
font-size: 26rpx;
color: #ED1C04;
line-height: 26rpx;
font-weight: 500;
align-items: baseline;
.label {
font-size: 30rpx;
color: #1A1A1A;
line-height: 30rpx;
}
.number {
font-size: 36rpx;
font-weight: 500;
margin-left: 6rpx;
}
}
}
.order-info-box {
margin: 16rpx;
background-color: #FFFFFF;
border-radius: 24rpx;
padding: 28rpx 22rpx;
&.product-info-box {
padding: 0;
margin: 0;
}
.title {
font-size: 30rpx;
color: #1A1A1A;
line-height: 30rpx;
font-weight: 700;
}
.order-info {
padding-bottom: 34rpx;
}
.item {
display: flex;
justify-content: space-between;
margin-top: 24rpx;
height: 38rpx;
.label {
font-size: 26rpx;
color: #4D4D4D;
line-height: 26rpx;
}
.value {
font-size: 26rpx;
color: #1A1A1A;
line-height: 26rpx;
text-align: right;
}
}
}
.timeBox {
background-color: #fff;
border-radius: 20rpx 20rpx 0 0;
padding-bottom: env(safe-area-inset-bottom);
.titleBox {
display: flex;
justify-content: space-between;
padding: 30rpx 40rpx;
font-size: 32rpx;
border-bottom: 2rpx solid #f5f5f5;
.title {
color: #999;
}
.btn {
color: #333;
font-weight: 500;
}
}
.picker-container {
height: 500rpx;
width: 100%;
.range-picker-view {
height: 100%;
width: 100%;
.picker-item {
line-height: 50px;
text-align: center;
font-size: 32rpx;
display: flex;
align-items: center;
justify-content: center;
}
}
}
}
.flex-center {
display: flex;
align-items: center;
justify-content: center;
}
</style>
<style>
/* 直接在页面导入公共样式 */
@import '/common/main.css';
@import "@/common/specify-style.less";
.edit-icon {
width: 28rpx;
height: 28rpx;
margin-left: 0;
flex-shrink: 0;
}
</style>