300 lines
7.2 KiB
Vue
300 lines
7.2 KiB
Vue
<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>
|