JewelryMall/miniprogram/pages/order/submit.vue
2026-02-14 19:29:15 +08:00

300 lines
7.2 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="order-submit">
<!-- 公司地址 -->
<view class="section">
<view class="section__title">公司地址</view>
<view class="section__content">
<text class="company-address">广东省深圳市罗湖区水贝珠宝交易中心</text>
</view>
</view>
<!-- 用户发货信息 -->
<view class="section">
<view class="section__title">收货信息</view>
<view class="form-item">
<text class="form-item__label">收货人</text>
<input class="form-item__input" v-model="receiverName" placeholder="请输入收货人姓名" />
</view>
<view class="form-item">
<text class="form-item__label">联系电话</text>
<input class="form-item__input" v-model="receiverPhone" type="number" placeholder="请输入联系电话" />
</view>
<view class="form-item">
<text class="form-item__label">收货地址</text>
<input class="form-item__input" v-model="receiverAddress" placeholder="请输入详细收货地址" />
</view>
</view>
<!-- 发货时间提醒 -->
<view class="section notice-section">
<text class="notice-text">温馨提示订单确认后预计3-7个工作日内发货具体以客服通知为准。</text>
</view>
<!-- 商品列表 -->
<view class="section">
<view class="section__title">商品列表</view>
<view class="order-item" v-for="item in orderItems" :key="item.id">
<image class="order-item__img" :src="item.product.bannerImages[0] || ''" mode="aspectFill" />
<view class="order-item__info">
<text class="order-item__name">{{ item.product.name }}</text>
<text class="order-item__spec">{{ item.specData.modelName }} {{ item.specData.fineness }}</text>
<view class="order-item__bottom">
<text class="order-item__price">¥{{ item.specData.totalPrice }}</text>
<text class="order-item__qty">x{{ item.quantity }}</text>
</view>
</view>
</view>
</view>
<!-- 总价 -->
<view class="total-bar">
<text>合计:</text>
<text class="total-bar__price">¥{{ totalPrice }}</text>
</view>
<!-- 贵重物品不退换提醒 -->
<view class="agreement" @click="agreed = !agreed">
<view class="agreement__checkbox" :class="{ 'agreement__checkbox--checked': agreed }">
<text v-if="agreed">✓</text>
</view>
<text class="agreement__text">我已知晓贵重物品售出后不支持退换</text>
</view>
<!-- 提交按钮 -->
<view class="submit-bar">
<view
class="submit-btn"
:class="{ 'submit-btn--disabled': !canSubmit }"
@click="handleSubmit"
>
提交订单
</view>
</view>
</view>
</template>
<script setup lang="ts">
import { ref, computed } from 'vue'
import { useCartStore } from '../../store/cart'
import { createOrder } from '../../api/order'
import type { CartItem } from '../../types'
const cartStore = useCartStore()
const receiverName = ref('')
const receiverPhone = ref('')
const receiverAddress = ref('')
const agreed = ref(false)
const submitting = ref(false)
/** 从购物车获取已勾选的商品作为订单项 */
const orderItems = computed<CartItem[]>(() => cartStore.checkedItems)
const totalPrice = computed(() =>
orderItems.value.reduce((sum, item) => sum + item.specData.totalPrice * item.quantity, 0),
)
const canSubmit = computed(() => agreed.value && !submitting.value)
async function handleSubmit() {
if (!canSubmit.value) return
if (!receiverName.value.trim()) {
uni.showToast({ title: '请输入收货人姓名', icon: 'none' })
return
}
if (!receiverPhone.value.trim()) {
uni.showToast({ title: '请输入联系电话', icon: 'none' })
return
}
if (!receiverAddress.value.trim()) {
uni.showToast({ title: '请输入收货地址', icon: 'none' })
return
}
submitting.value = true
try {
const order = await createOrder({
items: orderItems.value.map((item) => ({
productId: item.productId,
specDataId: item.specDataId,
quantity: item.quantity,
unitPrice: item.specData.totalPrice,
})),
receiverName: receiverName.value.trim(),
receiverPhone: receiverPhone.value.trim(),
receiverAddress: receiverAddress.value.trim(),
})
// 清除已下单的购物车项
for (const item of orderItems.value) {
cartStore.removeFromCart(item.id)
}
uni.redirectTo({ url: `/pages/order/detail?id=${order.id}` })
} catch {
uni.showToast({ title: '提交订单失败,请重试', icon: 'none' })
} finally {
submitting.value = false
}
}
</script>
<style scoped>
.order-submit {
min-height: 100vh;
background: #f5f5f5;
padding-bottom: 140rpx;
}
.section {
background: #fff;
margin-bottom: 16rpx;
padding: 24rpx;
}
.section__title {
font-size: 30rpx;
font-weight: bold;
color: #333;
margin-bottom: 16rpx;
}
.section__content {
font-size: 26rpx;
color: #666;
}
.company-address {
font-size: 26rpx;
color: #666;
}
.form-item {
display: flex;
align-items: center;
padding: 16rpx 0;
border-bottom: 1rpx solid #f0f0f0;
}
.form-item__label {
font-size: 28rpx;
color: #333;
width: 160rpx;
flex-shrink: 0;
}
.form-item__input {
flex: 1;
font-size: 28rpx;
color: #333;
}
.notice-section {
background: #fffbe6;
}
.notice-text {
font-size: 24rpx;
color: #faad14;
}
.order-item {
display: flex;
padding: 16rpx 0;
border-bottom: 1rpx solid #f0f0f0;
}
.order-item__img {
width: 160rpx;
height: 160rpx;
border-radius: 8rpx;
flex-shrink: 0;
background: #f5f5f5;
}
.order-item__info {
flex: 1;
margin-left: 20rpx;
display: flex;
flex-direction: column;
justify-content: space-between;
}
.order-item__name {
font-size: 28rpx;
color: #333;
}
.order-item__spec {
font-size: 24rpx;
color: #999;
margin-top: 8rpx;
}
.order-item__bottom {
display: flex;
justify-content: space-between;
align-items: center;
margin-top: 8rpx;
}
.order-item__price {
font-size: 28rpx;
color: #e4393c;
font-weight: bold;
}
.order-item__qty {
font-size: 24rpx;
color: #999;
}
.total-bar {
background: #fff;
padding: 24rpx;
display: flex;
justify-content: flex-end;
align-items: center;
font-size: 28rpx;
color: #333;
margin-bottom: 16rpx;
}
.total-bar__price {
font-size: 36rpx;
color: #e4393c;
font-weight: bold;
margin-left: 8rpx;
}
.agreement {
background: #fff;
padding: 24rpx;
display: flex;
align-items: center;
margin-bottom: 16rpx;
}
.agreement__checkbox {
width: 40rpx;
height: 40rpx;
border: 2rpx solid #ddd;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
margin-right: 16rpx;
font-size: 24rpx;
color: #fff;
}
.agreement__checkbox--checked {
background: #e4393c;
border-color: #e4393c;
}
.agreement__text {
font-size: 26rpx;
color: #666;
}
.submit-bar {
position: fixed;
bottom: 0;
left: 0;
right: 0;
background: #fff;
padding: 16rpx 24rpx;
box-shadow: 0 -2rpx 10rpx rgba(0, 0, 0, 0.05);
}
.submit-btn {
background: #e4393c;
color: #fff;
text-align: center;
padding: 20rpx 0;
border-radius: 44rpx;
font-size: 30rpx;
}
.submit-btn--disabled {
background: #ccc;
pointer-events: none;
}
</style>