JewelryMall/miniprogram/pages/order/submit.vue
2026-03-02 00:20:01 +08:00

457 lines
12 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="navbar" :style="{ paddingTop: statusBarHeight + 'px' }">
<view class="navbar__content" :style="{ height: navBarHeight + 'px' }">
<view class="navbar__back" @click="goBack">
<image src="/static/ic_back.png" class="navbar__back-icon" mode="aspectFit" />
</view>
<text class="navbar__title">订单提交</text>
<view class="navbar__placeholder"></view>
</view>
</view>
<view :style="{ height: (statusBarHeight + navBarHeight) + 'px' }"></view>
<!-- 公司地址 -->
<view class="address-section">
<view class="address-section__header">
<image src="/static/ic_address.png" class="address-section__icon" mode="aspectFit" />
<text class="address-section__title">公司地址</text>
</view>
<view class="address-section__content">
<text class="address-section__name">叶生珠宝-空托之城</text>
<text class="address-section__detail">广东省深圳市罗湖区水贝2路贝丽花园21栋108#生珠宝</text>
<text class="address-section__phone">15920028399</text>
<text class="address-section__contact">联系电话:空托之城 叶生15920028399</text>
</view>
</view>
<!-- 您的信息 -->
<view class="info-section">
<view class="info-section__header">
<image src="/static/ic_tip.png" class="info-section__icon" mode="aspectFit" />
<text class="info-section__title">您的信息</text>
<text class="info-section__subtitle">请留下您的联系方式</text>
</view>
<view class="form-item">
<text class="form-item__label required">姓名</text>
<input class="form-item__input" v-model="receiverName" placeholder="请输入真实姓名" />
</view>
<view class="form-item">
<text class="form-item__label required">电话</text>
<input class="form-item__input" v-model="receiverPhone" type="number" placeholder="请输入电话" />
</view>
<view class="form-item">
<text class="form-item__label required">地址</text>
<input class="form-item__input" v-model="receiverAddress" placeholder="请输入收货地址" />
</view>
<view class="form-item">
<text class="form-item__label">备注</text>
<input class="form-item__input" v-model="remark" placeholder="请输入备注信息" />
</view>
</view>
<!-- 发货时间 -->
<view class="delivery-section">
<view class="delivery-section__header">
<image src="/static/ic_time.png" class="delivery-section__icon" mode="aspectFit" />
<text class="delivery-section__title">发货时间</text>
</view>
<text class="delivery-section__content">上班时间9:30-20:30分下单15分钟即可出货。镶嵌主石每天18:00前正常情况当天出货。</text>
</view>
<!-- 商品列表 -->
<view class="product-section">
<view class="product-item" v-for="item in orderItems" :key="item.id">
<image class="product-item__img" :src="fullUrl(item.product.thumb || item.product.bannerImages?.[0] || '')" mode="aspectFill" />
<view class="product-item__info">
<text class="product-item__name">{{ item.product.name }}</text>
<view class="product-item__specs">
<text class="product-item__spec">款号:{{ item.specData.modelName || 'B2022' }}</text>
<text class="product-item__spec">商品型号:{{ item.specData.fineness || '2606' }}</text>
<text class="product-item__spec">成色:{{ item.specData.fineness || '30' }}</text>
<text class="product-item__spec">主石:{{ item.specData.mainStone || '13.00#' }}</text>
<text class="product-item__spec">手寸:{{ item.specData.ringSize || '13.00#' }}</text>
<text class="product-item__spec">金料总重:{{ item.specData.goldTotalWeight || '236' }}</text>
</view>
<text class="product-item__price">¥{{ item.specData.totalPrice }}元</text>
</view>
</view>
</view>
<!-- 贵重物品不退换提醒 -->
<view class="agreement" @click="agreed = !agreed">
<image :src="agreed ? '/static/ic_check_s.png' : '/static/ic_check.png'" class="agreement__checkbox" mode="aspectFit" />
<text class="agreement__text">因珠宝产品属于贵重物品,一旦出货,产品无质量问题不支持退换!</text>
</view>
<!-- 底部提交栏 -->
<view class="submit-bar">
<view class="submit-bar__left">
<text class="submit-bar__label">合计:</text>
<text class="submit-bar__price">¥{{ totalPrice }}</text>
</view>
<view
class="submit-bar__btn"
:class="{ 'submit-bar__btn--disabled': !canSubmit }"
@click="handleSubmit"
>
开始下单
</view>
</view>
</view>
</template>
<script setup lang="ts">
import { ref, computed, onMounted } from 'vue'
import { useCartStore } from '../../store/cart'
import { createOrder } from '../../api/order'
import { BASE_URL } from '../../utils/request'
import type { CartItem } from '../../types'
const cartStore = useCartStore()
const statusBarHeight = ref(0)
const navBarHeight = ref(44)
const receiverName = ref('')
const receiverPhone = ref('')
const receiverAddress = ref('')
const remark = ref('')
const agreed = ref(false)
const submitting = ref(false)
function fullUrl(path: string): string {
if (!path) return ''
if (path.startsWith('http')) return path
return BASE_URL + path
}
function goBack() {
uni.navigateBack()
}
/** 从购物车获取已勾选的商品作为订单项 */
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
}
}
onMounted(() => {
const sysInfo = uni.getSystemInfoSync()
statusBarHeight.value = sysInfo.statusBarHeight || 0
const menuBtn = uni.getMenuButtonBoundingClientRect()
navBarHeight.value = menuBtn.height + (menuBtn.top - (sysInfo.statusBarHeight || 0)) * 2
})
</script>
<style scoped>
.order-submit {
min-height: 100vh;
background: #f5f5f5;
padding-bottom: 140rpx;
}
/* 导航栏 */
.navbar {
position: fixed;
top: 0;
left: 0;
right: 0;
z-index: 100;
background: linear-gradient(135deg, #FFCFDE 0%, #FFA6C4 100%);
}
.navbar__content {
display: flex;
align-items: center;
justify-content: space-between;
padding: 0 24rpx;
}
.navbar__back {
padding: 10rpx;
}
.navbar__back-icon {
width: 40rpx;
height: 40rpx;
}
.navbar__title {
font-size: 34rpx;
font-weight: 600;
color: #000;
}
.navbar__placeholder {
width: 60rpx;
}
/* 公司地址 */
.address-section {
background: #fff;
margin: 16rpx 24rpx;
padding: 24rpx;
border-radius: 16rpx;
}
.address-section__header {
display: flex;
align-items: center;
margin-bottom: 16rpx;
}
.address-section__icon {
width: 32rpx;
height: 32rpx;
margin-right: 8rpx;
}
.address-section__title {
font-size: 28rpx;
color: #e91e63;
font-weight: 600;
}
.address-section__content {
display: flex;
flex-direction: column;
gap: 8rpx;
}
.address-section__name {
font-size: 28rpx;
color: #333;
font-weight: 600;
}
.address-section__detail,
.address-section__phone,
.address-section__contact {
font-size: 24rpx;
color: #666;
line-height: 36rpx;
}
/* 您的信息 */
.info-section {
background: #fff;
margin: 0 24rpx 16rpx;
padding: 24rpx;
border-radius: 16rpx;
}
.info-section__header {
display: flex;
align-items: center;
margin-bottom: 20rpx;
}
.info-section__icon {
width: 32rpx;
height: 32rpx;
margin-right: 8rpx;
}
.info-section__title {
font-size: 28rpx;
color: #e91e63;
font-weight: 600;
margin-right: 12rpx;
}
.info-section__subtitle {
font-size: 24rpx;
color: #999;
}
.form-item {
display: flex;
align-items: center;
padding: 20rpx 0;
border-bottom: 1rpx solid #f0f0f0;
}
.form-item:last-child {
border-bottom: none;
}
.form-item__label {
font-size: 26rpx;
color: #333;
width: 100rpx;
flex-shrink: 0;
}
.form-item__label.required::before {
content: '*';
color: #e91e63;
margin-right: 4rpx;
}
.form-item__input {
flex: 1;
font-size: 26rpx;
color: #333;
}
/* 发货时间 */
.delivery-section {
background: #fff;
margin: 0 24rpx 16rpx;
padding: 24rpx;
border-radius: 16rpx;
}
.delivery-section__header {
display: flex;
align-items: center;
margin-bottom: 12rpx;
}
.delivery-section__icon {
width: 32rpx;
height: 32rpx;
margin-right: 8rpx;
}
.delivery-section__title {
font-size: 28rpx;
color: #e91e63;
font-weight: 600;
}
.delivery-section__content {
font-size: 24rpx;
color: #e91e63;
line-height: 36rpx;
}
/* 商品列表 */
.product-section {
background: #fff;
margin: 0 24rpx 16rpx;
padding: 24rpx;
border-radius: 16rpx;
}
.product-item {
display: flex;
gap: 20rpx;
}
.product-item__img {
width: 160rpx;
height: 160rpx;
border-radius: 12rpx;
flex-shrink: 0;
background: #f5f5f5;
}
.product-item__info {
flex: 1;
display: flex;
flex-direction: column;
}
.product-item__name {
font-size: 28rpx;
color: #333;
font-weight: 600;
margin-bottom: 8rpx;
}
.product-item__specs {
display: grid;
grid-template-columns: repeat(2, 1fr);
gap: 8rpx;
margin-bottom: 12rpx;
}
.product-item__spec {
font-size: 22rpx;
color: #666;
}
.product-item__price {
font-size: 30rpx;
color: #e91e63;
font-weight: 700;
}
/* 协议 */
.agreement {
background: #fffbe6;
margin: 0 24rpx 16rpx;
padding: 20rpx 24rpx;
border-radius: 16rpx;
display: flex;
align-items: center;
}
.agreement__checkbox {
width: 32rpx;
height: 32rpx;
margin-right: 12rpx;
flex-shrink: 0;
}
.agreement__text {
font-size: 24rpx;
color: #333;
line-height: 36rpx;
}
/* 底部提交栏 */
.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);
display: flex;
align-items: center;
justify-content: space-between;
}
.submit-bar__left {
display: flex;
align-items: baseline;
}
.submit-bar__label {
font-size: 26rpx;
color: #333;
}
.submit-bar__price {
font-size: 36rpx;
color: #e91e63;
font-weight: 700;
}
.submit-bar__btn {
background: linear-gradient(135deg, #FF6D9B, #FF4081);
color: #fff;
padding: 20rpx 60rpx;
border-radius: 44rpx;
font-size: 28rpx;
font-weight: 600;
}
.submit-bar__btn--disabled {
background: #ccc;
pointer-events: none;
}
</style>