campus-errand/miniapp/pages/food/food-order.vue
2026-03-12 18:12:10 +08:00

345 lines
7.5 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>
<view class="food-order-page">
<!-- 订单商品摘要 -->
<view class="order-summary">
<text class="section-title">订单商品</text>
<view class="summary-item" v-for="item in cartItems" :key="item.dish.id">
<text class="summary-name">{{ item.dish.name }} × {{ item.quantity }}</text>
<text class="summary-price">¥{{ (item.dish.price * item.quantity).toFixed(2) }}</text>
</view>
<view class="summary-item" v-if="packingFee > 0">
<text class="summary-name">打包费</text>
<text class="summary-price">¥{{ packingFee.toFixed(2) }}</text>
</view>
<view class="summary-total">
<text class="total-label">餐品总金额</text>
<text class="total-value">¥{{ goodsAmount }}</text>
</view>
</view>
<!-- 订单表单 -->
<view class="form-section">
<!-- 送达地点 -->
<view class="form-item">
<text class="form-label">送达地点</text>
<input class="form-input" v-model="form.deliveryLocation" placeholder="请输入送达地点" />
</view>
<!-- 备注信息 -->
<view class="form-item">
<text class="form-label">备注信息</text>
<textarea class="form-textarea" v-model="form.remark" placeholder="请输入备注信息(选填)" />
</view>
<!-- 手机号 -->
<view class="form-item">
<text class="form-label">手机号</text>
<input class="form-input" v-model="form.phone" type="number" placeholder="请输入联系手机号" maxlength="11" />
</view>
<!-- 跑腿佣金 -->
<view class="form-item">
<text class="form-label">跑腿佣金</text>
<view class="commission-input">
<text class="commission-unit">¥</text>
<input class="form-input commission-field" v-model="form.commission" type="digit" placeholder="最低1.0元" />
</view>
</view>
</view>
<!-- 提交按钮 -->
<view class="submit-section">
<text class="pay-amount">支付金额:¥{{ payAmount }}</text>
<button class="submit-btn" @click="onSubmit" :loading="submitting" :disabled="submitting">确定</button>
</view>
</view>
</template>
<script>
import { createOrder } from '../../utils/api'
import { useCartStore } from '../../stores/cart'
export default {
data() {
return {
form: {
deliveryLocation: '',
remark: '',
phone: '',
commission: ''
},
submitting: false
}
},
computed: {
cartStore() {
return useCartStore()
},
cartItems() {
return this.cartStore.items
},
packingFee() {
return this.cartStore.packingFee
},
/** 餐品总金额(含打包费) */
goodsAmount() {
return this.cartStore.totalPriceWithFee.toFixed(2)
},
/** 支付金额 = 商品总金额 + 跑腿佣金 */
payAmount() {
const goods = this.cartStore.totalPriceWithFee
const commission = parseFloat(this.form.commission) || 0
return (goods + commission).toFixed(2)
}
},
onLoad() {
// 如果购物车为空,返回上一页
if (this.cartStore.totalCount === 0) {
uni.showToast({ title: '购物车为空', icon: 'none' })
setTimeout(() => { uni.navigateBack() }, 1000)
}
},
methods: {
/** 校验佣金 */
validateCommission() {
const val = this.form.commission
if (!val) {
uni.showToast({ title: '请输入跑腿佣金', icon: 'none' })
return false
}
const num = parseFloat(val)
if (isNaN(num) || num < 1.0) {
uni.showToast({ title: '跑腿佣金不可低于1.0元', icon: 'none' })
return false
}
if (val.includes('.') && val.split('.')[1].length > 1) {
uni.showToast({ title: '佣金最多支持小数点后1位', icon: 'none' })
return false
}
return true
},
/** 校验表单 */
validateForm() {
if (!this.form.deliveryLocation.trim()) {
uni.showToast({ title: '请输入送达地点', icon: 'none' })
return false
}
if (!this.form.phone.trim()) {
uni.showToast({ title: '请输入手机号', icon: 'none' })
return false
}
return this.validateCommission()
},
/** 提交订单 */
async onSubmit() {
if (!this.validateForm()) return
this.submitting = true
try {
const commission = parseFloat(this.form.commission)
const goodsAmount = this.cartStore.totalPriceWithFee
// 构建美食街订单数据
const foodItems = this.cartItems.map(item => ({
shopId: this.cartStore.shopInfo?.id,
dishId: item.dish.id,
quantity: item.quantity,
unitPrice: item.dish.price
}))
const orderData = {
orderType: 'Food',
deliveryLocation: this.form.deliveryLocation.trim(),
remark: this.form.remark.trim(),
phone: this.form.phone.trim(),
goodsAmount: goodsAmount,
commission: commission,
totalAmount: goodsAmount + commission,
foodItems: foodItems
}
const result = await createOrder(orderData)
if (result.paymentParams) {
await this.wxPay(result.paymentParams)
}
// 下单成功,清空购物车
this.cartStore.clearCart()
uni.showToast({ title: '下单成功', icon: 'success' })
setTimeout(() => {
uni.navigateBack({ delta: 2 })
}, 1500)
} catch (e) {
// 错误已在 request 中处理
} finally {
this.submitting = false
}
},
/** 调用微信支付 */
wxPay(params) {
return new Promise((resolve, reject) => {
uni.requestPayment({
...params,
success: resolve,
fail: (err) => {
if (err.errMsg !== 'requestPayment:fail cancel') {
uni.showToast({ title: '支付失败', icon: 'none' })
}
reject(err)
}
})
})
}
}
}
</script>
<style scoped>
.food-order-page {
padding: 24rpx;
padding-bottom: 200rpx;
background-color: #f5f5f5;
min-height: 100vh;
}
.order-summary {
background-color: #ffffff;
border-radius: 16rpx;
padding: 20rpx 30rpx;
margin-bottom: 20rpx;
}
.section-title {
font-size: 30rpx;
font-weight: bold;
color: #333333;
margin-bottom: 16rpx;
display: block;
}
.summary-item {
display: flex;
justify-content: space-between;
align-items: center;
padding: 10rpx 0;
}
.summary-name {
font-size: 26rpx;
color: #666666;
}
.summary-price {
font-size: 26rpx;
color: #333333;
}
.summary-total {
display: flex;
justify-content: space-between;
align-items: center;
padding-top: 16rpx;
margin-top: 12rpx;
border-top: 1rpx solid #f0f0f0;
}
.total-label {
font-size: 28rpx;
color: #333333;
font-weight: bold;
}
.total-value {
font-size: 32rpx;
color: #e64340;
font-weight: bold;
}
.form-section {
background-color: #ffffff;
border-radius: 16rpx;
padding: 20rpx 30rpx;
}
.form-item {
padding: 20rpx 0;
border-bottom: 1rpx solid #f0f0f0;
}
.form-item:last-child {
border-bottom: none;
}
.form-label {
font-size: 28rpx;
color: #333333;
margin-bottom: 12rpx;
display: block;
}
.form-input {
font-size: 28rpx;
color: #333333;
height: 72rpx;
}
.form-textarea {
font-size: 28rpx;
color: #333333;
width: 100%;
height: 160rpx;
}
.commission-input {
display: flex;
align-items: center;
}
.commission-unit {
font-size: 32rpx;
color: #e64340;
margin-right: 8rpx;
font-weight: bold;
}
.commission-field {
flex: 1;
}
.submit-section {
position: fixed;
bottom: 0;
left: 0;
right: 0;
background-color: #ffffff;
padding: 20rpx 30rpx;
padding-bottom: calc(20rpx + env(safe-area-inset-bottom));
display: flex;
align-items: center;
justify-content: space-between;
box-shadow: 0 -2rpx 10rpx rgba(0, 0, 0, 0.05);
}
.pay-amount {
font-size: 32rpx;
color: #e64340;
font-weight: bold;
}
.submit-btn {
width: 240rpx;
height: 80rpx;
line-height: 80rpx;
background-color: #007AFF;
color: #ffffff;
font-size: 30rpx;
border-radius: 40rpx;
border: none;
}
.submit-btn[disabled] {
opacity: 0.6;
}
</style>