feat: 商品详情交互开发

This commit is contained in:
duanshuwen
2025-08-03 18:06:06 +08:00
parent 42c5354978
commit 5e0d53fc20
22 changed files with 1906 additions and 582 deletions

View File

@@ -1,83 +1,107 @@
<template>
<uni-popup ref="popup" type="bottom">
<view class="good-confirm-container">
<!-- 头部标题栏 -->
<uni-popup
ref="popup"
type="bottom"
background-color="#E9F3F7"
border-radius="12px 12px 0 0"
mask-background-color="rgba(0,0,0,0.5)"
:safe-area="false"
>
<view class="good-container">
<!-- 头部区域 -->
<view class="header">
<view class="header-title">确认订单</view>
<view class="header-title">填写信息</view>
<view class="close-btn" @click="closePopup">
<uni-icons type="closeempty" size="20" color="#666"></uni-icons>
<uni-icons type="closeempty" size="24" color="#333" />
</view>
</view>
<!-- 商品信息区域 -->
<view class="goods-info">
<view class="goods-image">
<image
:src="
goodsData.commodityPhotoList?.[0]?.photoUrl ||
'/static/test/mk_img_1.png'
"
mode="aspectFill"
/>
</view>
<view class="goods-details">
<view class="goods-title">{{
goodsData.commodityName || "商品名称"
}}</view>
<view class="goods-price">
<text class="currency">¥</text>
<text class="price">{{ goodsData.price || 399 }}</text>
</view>
<view class="goods-tag" v-if="goodsData.timeTag">
{{ goodsData.timeTag }}
</view>
</view>
</view>
<!-- 数量选择区域 -->
<view class="quantity-section">
<view class="quantity-label">购买数量</view>
<view class="quantity-control">
<view
class="quantity-btn"
:class="{ disabled: quantity <= 1 }"
@click="decreaseQuantity"
>
<uni-icons type="minus" size="16" color="#666"></uni-icons>
</view>
<view class="quantity-input">
<input
type="number"
v-model="quantity"
@input="handleQuantityInput"
:disabled="false"
<scroll-view
class="good-content"
:scroll-y="true"
:show-scrollbar="false"
>
<view class="wrapper">
<view class="good-info-wrapper">
<!-- 轮播图区域 -->
<ImageSwiper
:images="goodsData.commodityPhotoList"
:height="130"
:border-radius="0"
:showThumbnails="false"
/>
</view>
<view class="quantity-btn" @click="increaseQuantity">
<uni-icons type="plus" size="16" color="#666"></uni-icons>
</view>
</view>
</view>
<!-- 总价区域 -->
<view class="total-section">
<view class="total-label">合计</view>
<view class="total-price">
<text class="currency">¥</text>
<text class="price">{{ totalPrice }}</text>
<!-- 商品信息区域 -->
<view class="goods-info">
<view class="goods-details">
<view class="goods-title">{{
goodsData.commodityName || "商品名称"
}}</view>
<view class="goods-price">
<text class="currency">¥</text>
<text class="price">{{ goodsData.price || 399 }}</text>
</view>
<view class="goods-service-list">
<view class="service-title">包含服务</view>
<view class="goods-service-item">
<text class="service-label">随时可退</text>
<text class="service-value">1</text>
</view>
</view>
</view>
</view>
</view>
<!-- 数量选择区域 -->
<view class="quantity-section">
<ModuleTitle title="游客信息" />
<Stepper v-model="quantity" />
</view>
<!-- 游客信息区域 -->
<scroll-view
class="user-form-list"
:scroll-x="true"
:show-scrollbar="false"
>
<FormCard
v-for="(item, index) in userFormList"
:title="`游客${index + 1}`"
:form="item"
:showDeleteIcon="userFormList.length > 1"
:key="index"
@update:name="(value) => updateUserForm(index, 'name', value)"
@update:phone="(value) => updateUserForm(index, 'phone', value)"
@delete="() => deleteUserForm(index)"
/>
</scroll-view>
<!-- 总价区域 -->
<SumCard />
</view>
</view>
</scroll-view>
<!-- 底部按钮区域 -->
<view class="footer">
<button class="confirm-btn" @click="confirmOrder">确认购买</button>
<view class="left">
<text class="total-count">{{ quantity }}合计</text>
<text class="total-price">{{ totalPrice }}</text>
</view>
<view class="confirm-btn" @click="confirmOrder">立即支付</view>
</view>
</view>
</uni-popup>
</template>
<script setup>
import { ref, computed, defineProps, defineEmits } from "vue";
import { ref, computed, watch, defineProps, defineEmits } from "vue";
import ImageSwiper from "@/components/ImageSwiper/index.vue";
import ModuleTitle from "@/components/ModuleTitle/index.vue";
import Stepper from "@/components/Stepper/index.vue";
import FormCard from "@/components/FormCard/index.vue";
import SumCard from "@/components/SumCard/index.vue";
// Props定义
const props = defineProps({
@@ -93,6 +117,8 @@ const emits = defineEmits(["confirm", "close"]);
// 响应式数据
const popup = ref(null);
const quantity = ref(1);
const userFormList = ref([{ name: "", phone: "" }]); // 初始化一个表单项
const isDeleting = ref(false); // 标志位防止删除时watch冲突
// 计算属性
const totalPrice = computed(() => {
@@ -100,6 +126,31 @@ const totalPrice = computed(() => {
return (price * quantity.value).toFixed(0);
});
// 监听 quantity 变化,动态调整 userFormList
watch(
quantity,
(newQuantity, oldQuantity) => {
// 如果正在执行删除操作跳过watch逻辑
if (isDeleting.value) {
isDeleting.value = false;
return;
}
const currentLength = userFormList.value.length;
if (newQuantity > currentLength) {
// 数量增加,添加新的表单项
for (let i = currentLength; i < newQuantity; i++) {
userFormList.value.push({ name: "", phone: "" });
}
} else if (newQuantity < currentLength) {
// 数量减少,删除多余的表单项
userFormList.value.splice(newQuantity);
}
},
{ immediate: false }
);
// 方法定义
const showPopup = () => {
popup.value?.open();
@@ -110,23 +161,31 @@ const closePopup = () => {
emits("close");
};
const increaseQuantity = () => {
quantity.value++;
};
const decreaseQuantity = () => {
if (quantity.value > 1) {
quantity.value--;
const updateUserForm = (index, field, value) => {
if (userFormList.value[index]) {
userFormList.value[index][field] = value;
}
};
const handleQuantityInput = (e) => {
const value = parseInt(e.detail.value);
if (value && value > 0) {
quantity.value = value;
} else {
quantity.value = 1;
const deleteUserForm = (index) => {
// 确保至少保留一个表单项
if (userFormList.value.length <= 1) {
uni.showToast({
title: "至少需要一位游客信息",
icon: "none",
duration: 2000,
});
return;
}
// 设置删除标志位防止watch监听器干扰
isDeleting.value = true;
// 删除指定索引的表单项
userFormList.value.splice(index, 1);
// 同步更新quantity
quantity.value = userFormList.value.length;
};
const confirmOrder = () => {
@@ -134,6 +193,7 @@ const confirmOrder = () => {
goodsData: props.goodsData,
quantity: quantity.value,
totalPrice: totalPrice.value,
userFormList: userFormList.value,
};
emits("confirm", orderData);
closePopup();