From ff5b70345e3ad79e67c06c9d870d722572b205cc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=A4=A7=E8=84=8F=E7=8B=BC?= <786316265@qq.com> Date: Wed, 27 Aug 2025 10:16:48 +0800 Subject: [PATCH] =?UTF-8?q?=E6=9B=B4=E6=94=B9=E4=BA=86=E8=B4=AD=E7=89=A9?= =?UTF-8?q?=E8=BD=A6=E9=80=BB=E8=BE=91=EF=BC=8C=E5=A2=9E=E5=8A=A0=E4=BA=86?= =?UTF-8?q?=E5=9C=B0=E5=9D=80=E7=AE=A1=E7=90=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/router/index.js | 14 +- src/views/Address.vue | 594 ++++++++++++++++++++++++++++++++++++ src/views/BuyDetails.vue | 205 +++++++++++-- src/views/Cart.vue | 559 +++++++++++++++++++++++++++++++++ src/views/MyProfile.vue | 4 +- src/views/Pay.vue | 123 +++++++- src/views/ProductDetail.vue | 217 ++++++++++--- 7 files changed, 1626 insertions(+), 90 deletions(-) create mode 100644 src/views/Address.vue create mode 100644 src/views/Cart.vue diff --git a/src/router/index.js b/src/router/index.js index 1fd9595..b44e552 100644 --- a/src/router/index.js +++ b/src/router/index.js @@ -247,9 +247,21 @@ const routes = [ { path: '/pay', name: 'Pay', - component: () => import('../views/Pay.vue'), + component: () => import('@/views/Pay.vue'), meta: { title: '确认支付' } }, + { + path: '/cart', + name: 'Cart', + component: () => import('@/views/Cart.vue'), + meta: { title: '购物车' } + }, + { + path: '/address', + name: 'Address', + component: () => import('@/views/Address.vue'), + meta: { title: '地址管理', requiresAuth: true } + }, { path: '/payfailed', name: 'PayFailed', diff --git a/src/views/Address.vue b/src/views/Address.vue new file mode 100644 index 0000000..66f6fae --- /dev/null +++ b/src/views/Address.vue @@ -0,0 +1,594 @@ + + + + + \ No newline at end of file diff --git a/src/views/BuyDetails.vue b/src/views/BuyDetails.vue index fb36e08..da1c7db 100644 --- a/src/views/BuyDetails.vue +++ b/src/views/BuyDetails.vue @@ -23,20 +23,43 @@
- 收货 地址 - + 收货地址 + + 管理地址 +
-
{{ shippingAddress }}
- + + +
+
+ {{ address.recipientName }} {{ address.recipientPhone }} + 默认 +
+
{{ address.province }}{{ address.city }}{{ address.district }}{{ address.detailAddress }}
+
+
+
+
+ 暂无收货地址 + + 立即添加 + +
@@ -115,7 +138,6 @@ class="note-input" autofocus /> - @@ -127,7 +149,7 @@ 加入购物车 @@ -170,9 +192,10 @@ const categories = ref([]) const sizes = ref([]) const selectedCategory = ref(null) const selectedSize = ref(null) -const shippingAddress = ref('请输入收货地址') +const addresses = ref([]) +const selectedAddressId = ref('') +const selectedAddress = ref(null) const orderNote = ref('') -const showAddressEdit = ref(false) const showNoteEdit = ref(false) // 计算属性 @@ -272,15 +295,21 @@ const addToCart = async () => { } } +// 立即购买功能 const handlePurchase = async () => { if (!canPurchase.value) { ElMessage.error('请选择完整的商品信息') return } + if (!selectedAddress.value) { + ElMessage.error('请选择收货地址') + return + } + try { - // 先将商品添加到购物车 - const cartItem = { + // 创建单独的购买订单 + const orderData = { productId: product.value.id, quantity: quantity.value, categoryId: selectedCategory.value.id, @@ -289,11 +318,11 @@ const handlePurchase = async () => { name: product.value.name, image: product.value.image, stock: product.value.stock, - shippingAddress: shippingAddress.value, + addressId: selectedAddress.value.id, orderNote: orderNote.value } - const response = await api.post('/cart/add', cartItem) + const response = await api.post('/cart/buy-now', orderData) if (response.data.success) { const cartId = response.data.data.cartId @@ -306,13 +335,73 @@ const handlePurchase = async () => { } }) } else { - throw new Error(response.data.message || '添加到购物车失败') + throw new Error(response.data.message || '创建订单失败') } } catch (error) { ElMessage.error(error.message || '操作失败,请重试') } } +// 添加到购物车功能(新增) +const handleAddToCart = async () => { + if (!canPurchase.value) { + ElMessage.error('请选择完整的商品信息') + return + } + + try { + const cartItem = { + productId: product.value.id, + quantity: quantity.value, + categoryId: selectedCategory.value.id, + sizeId: selectedSize.value.id, + points: product.value.points, + name: product.value.name, + image: product.value.image, + stock: product.value.stock + } + + const response = await api.post('/cart/add', cartItem) + + if (response.data.success) { + ElMessage.success('商品已加入购物车!') + // 可以选择返回上一页或跳转到购物车页面 + router.go(-1) + } else { + throw new Error(response.data.message || '添加到购物车失败') + } + } catch (error) { + ElMessage.error(error.message || '添加到购物车失败,请重试') + } +} + +// 获取用户地址列表 +const getAddressList = async () => { + try { + const response = await api.get('/address/list') + addresses.value = response.data.data.addresses || [] + // 如果有默认地址,自动选中 + const defaultAddress = addresses.value.find(addr => addr.isDefault) + if (defaultAddress) { + selectedAddressId.value = defaultAddress.id + selectedAddress.value = defaultAddress + } + } catch (error) { + console.error('获取地址列表失败:', error) + ElMessage.error('获取地址列表失败') + } +} + +// 处理地址选择变化 +const handleAddressChange = (addressId) => { + selectedAddress.value = addresses.value.find(addr => addr.id === addressId) +} + +// 跳转到地址管理页面 +const goToAddressManage = () => { + router.push('/address') +} + // 生命周期 onMounted(() => { // 从URL参数获取初始数量 @@ -324,6 +413,7 @@ onMounted(() => { getProductInfo() getCategories() getSizes() + getAddressList() }) @@ -382,10 +472,7 @@ onMounted(() => { color: #666; } -.address-text { - color: #666; - font-size: 14px; -} + .product-section { background: white; @@ -557,12 +644,67 @@ onMounted(() => { color: #999; } -.arrow-icon { - color: #ccc; +.address-select { + width: 100%; + margin-top: 10px; } -.address-input { - margin-top: 8px; +.manage-address-btn { + color: #409eff; + font-size: 14px; + margin-left: auto; +} + +.manage-address-btn:hover { + color: #66b1ff; +} + +.address-option { + padding: 8px 0; +} + +.address-info { + display: flex; + align-items: center; + justify-content: space-between; + margin-bottom: 4px; +} + +.recipient-info { + font-weight: 500; + color: #303133; +} + +.default-tag { + margin-left: 8px; +} + +.address-detail { + color: #606266; + font-size: 12px; + line-height: 1.4; +} + +.no-address { + display: flex; + align-items: center; + justify-content: center; + padding: 20px; + color: #909399; + font-size: 14px; +} + +.no-address-text { + margin-right: 8px; +} + +.add-address-btn { + color: #409eff; + font-size: 14px; +} + +.add-address-btn:hover { + color: #66b1ff; } .note-input { @@ -575,10 +717,7 @@ onMounted(() => { flex: 1; } -.address-text { - cursor: pointer; - padding: 4px 0; -} + .note-content { cursor: pointer; diff --git a/src/views/Cart.vue b/src/views/Cart.vue new file mode 100644 index 0000000..712037c --- /dev/null +++ b/src/views/Cart.vue @@ -0,0 +1,559 @@ + + + + + \ No newline at end of file diff --git a/src/views/MyProfile.vue b/src/views/MyProfile.vue index 6d75754..b2b7fbb 100644 --- a/src/views/MyProfile.vue +++ b/src/views/MyProfile.vue @@ -146,8 +146,8 @@ export default { {text:'隐私协议'}, ]); const functionItems = ref([ - { image: "/imgs/mainpage/交易记录.png", text: "购物车", path: "" }, - { image: "/imgs/mainpage/订单查询.png", text: "地址", path: "" }, + { image: "/imgs/mainpage/交易记录.png", text: "购物车", path: "/cart" }, + { image: "/imgs/mainpage/订单查询.png", text: "地址", path: "/address" }, { image: "/imgs/mainpage/客服中心.png", text: "收藏", path: "" } ]); diff --git a/src/views/Pay.vue b/src/views/Pay.vue index 91d8f80..e442156 100644 --- a/src/views/Pay.vue +++ b/src/views/Pay.vue @@ -64,6 +64,36 @@ + +
+

商品清单 ({{ paymentData.items.length }})

+
+
+
+ +
+
+
{{ item.name }}
+
+ {{ item.category }} + {{ item.size }} +
+
+ + {{ item.points || item.price }} +
+
+
+ x{{ item.quantity }} +
+
+
+
+

支付方式

@@ -162,7 +192,8 @@ const paymentData = ref({ totalAmount: 0, pointsAmount: 0, beansAmount: 0, - cartId: null + cartId: null, + items: [] // 添加商品列表 }) // 计算属性 @@ -201,7 +232,8 @@ const fetchPaymentData = async () => { totalAmount: 0, pointsAmount: 0, beansAmount: 0, - cartId: null + cartId: null, + items: [] } timeLeft.value = 900 // 默认15分钟 startCountdown() @@ -218,7 +250,8 @@ const fetchPaymentData = async () => { totalAmount: data.totalAmount || 0, pointsAmount: data.pointsAmount || 0, beansAmount: data.beansAmount || 0, - cartId: cartId + cartId: cartId, + items: data.items || [] // 获取商品列表 } // 设置倒计时时间(从后端获取,单位:秒) @@ -245,8 +278,8 @@ const handleGoBack = async () => { '确认要放弃付款吗?', '提示', { - confirmButtonText: '狠心离开', - cancelButtonText: '继续付款', + confirmButtonText: '确认', + cancelButtonText: '取消', type: 'warning' } ) @@ -447,6 +480,7 @@ onUnmounted(() => { } .amount-section, +.items-section, .payment-method-section { background: white; padding: 16px; @@ -460,6 +494,85 @@ onUnmounted(() => { color: #333; } +.items-list { + display: flex; + flex-direction: column; + gap: 12px; +} + +.item-card { + display: flex; + align-items: center; + gap: 12px; + padding: 12px; + border: 1px solid #eee; + border-radius: 8px; + background: #fafafa; +} + +.item-image { + width: 60px; + height: 60px; + border-radius: 6px; + overflow: hidden; + flex-shrink: 0; +} + +.item-image img { + width: 100%; + height: 100%; + object-fit: cover; +} + +.item-info { + flex: 1; + min-width: 0; +} + +.item-name { + font-size: 14px; + font-weight: 500; + color: #333; + margin-bottom: 4px; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} + +.item-details { + display: flex; + gap: 8px; + margin-bottom: 6px; +} + +.item-category, +.item-size { + font-size: 12px; + color: #666; + background: #f0f0f0; + padding: 2px 6px; + border-radius: 4px; +} + +.item-price { + display: flex; + align-items: center; + gap: 4px; + font-size: 14px; + color: #ffae00; + font-weight: 500; +} + +.item-quantity { + flex-shrink: 0; +} + +.quantity-label { + font-size: 14px; + color: #666; + font-weight: 500; +} + .amount-display { text-align: center; padding: 20px 0; diff --git a/src/views/ProductDetail.vue b/src/views/ProductDetail.vue index 9e02d97..2e7b7a9 100644 --- a/src/views/ProductDetail.vue +++ b/src/views/ProductDetail.vue @@ -125,15 +125,29 @@
-

商品描述

-

{{ product.description }}

+

+ 商品描述 +

+
+

{{ product.description }}

+
+
+ 详情 +
-

商品详情

-
-

{{ product.description || '暂无详细描述' }}

+

+ 商品详情 +

+
+
+

{{ product.description || '暂无详细描述' }}

+
+
+
+ 详情
@@ -261,9 +275,14 @@
共 {{ cartTotalItems }} 件商品 - - 清空购物车 - +
+ + 管理 + + + 清空购物车 + +
@@ -378,6 +397,10 @@ const cartLoading = ref(false) const userPoints = ref(0) const cartItems = ref([]) const cartCount = ref(0) +const showDescription = ref(false) +const showDetails = ref(false) +const selectedCategory = ref(null) +const selectedSize = ref(null) // 计算属性 const totalPoints = computed(() => { @@ -432,7 +455,7 @@ const getProductDetail = async () => { } } -const addToCart = () => { +const addToCart = async () => { if (!product.value) { ElMessage.error('商品信息加载中,请稍后再试') return @@ -443,14 +466,51 @@ const addToCart = () => { return } - // 跳转到BuyDetails页面进行确认订单 - router.push({ - path: '/buydetail', - query: { - productId: product.value.id, - quantity: quantity.value + try { + // 检查是否已选择必要的商品属性 + if (!selectedCategory.value || !selectedSize.value) { + // 如果没有选择属性,跳转到BuyDetails页面进行详细配置 + router.push({ + path: '/buydetail', + query: { + productId: product.value.id, + quantity: quantity.value + } + }) + return } - }) + + // 构建购物车商品数据 + const cartItem = { + productId: product.value.id, + quantity: quantity.value, + categoryId: selectedCategory.value.id, + sizeId: selectedSize.value.id, + points: product.value.points, + name: product.value.name, + image: product.value.images?.[0] || product.value.image, + stock: product.value.stock + } + + // 添加到购物车 + const response = await api.post('/cart/add', cartItem) + + if (response.data.success) { + ElMessage.success('商品已加入购物车!') + + // 更新本地购物车数据 + await loadCartFromBackend() + + // 重置选择状态 + quantity.value = 1 + selectedCategory.value = null + selectedSize.value = null + } else { + throw new Error(response.data.message || '添加到购物车失败') + } + } catch (error) { + ElMessage.error(error.message || '添加到购物车失败,请重试') + } } // 购物车商品管理方法 @@ -540,6 +600,12 @@ const loadCartFromBackend = async () => { } } +// 跳转到购物车管理页面 +const goToCartPage = () => { + showCart.value = false + router.push('/cart') +} + // 购物车结算功能 const checkoutCart = async () => { if (cartItems.value.length === 0) { @@ -547,43 +613,40 @@ const checkoutCart = async () => { return } - if (cartTotalPoints.value > userPoints.value) { - ElMessage.error('积分不足,无法结算') - return - } - try { - await ElMessageBox.confirm( - `确定要花费 ${cartTotalPoints.value} 积分购买这些商品吗?`, - '确认结算', - { - confirmButtonText: '确定', - cancelButtonText: '取消', - type: 'warning' - } - ) - - const orderData = { + // 创建购物车结算请求 + const cartData = { items: cartItems.value.map(item => ({ - productId: item.id, + productId: item.id || item.productId, quantity: item.quantity, - points: item.points - })), - totalPoints: cartTotalPoints.value + points: item.points, + name: item.name, + image: item.image, + categoryId: item.categoryId, + sizeId: item.sizeId + })) } - await api.post('/orders', orderData) + const response = await api.post('/cart/checkout', cartData) - // 清空购物车 - cartItems.value = [] - showCart.value = false - - ElMessage.success('结算成功!') - router.push('/orders') + if (response.data.success) { + const cartId = response.data.data.cartId + + // 跳转到支付页面 + router.push({ + path: '/pay', + query: { + cartId: cartId + } + }) + + // 关闭购物车弹窗 + showCart.value = false + } else { + throw new Error(response.data.message || '创建订单失败') + } } catch (error) { - if (error !== 'cancel') { - ElMessage.error('结算失败,请重试') - } + ElMessage.error(error.message || '结算失败,请重试') } } @@ -652,6 +715,14 @@ const getUserPoints = async () => { } } +const toggleDescription = () => { + showDescription.value = !showDescription.value +} + +const toggleDetails = () => { + showDetails.value = !showDetails.value +} + // 生命周期 onMounted(() => { //getProductDetail() @@ -881,19 +952,52 @@ watch( margin-bottom: 20px; } -.product-description h3, -.product-details h3 { +.section-title { margin: 0 0 12px 0; font-size: 16px; color: #333; + padding: 8px 0; + border-bottom: 1px solid #eee; } -.product-description p { +.section-content { + padding: 12px 0; + animation: slideDown 0.3s ease; +} + +.section-content p { margin: 0; line-height: 1.6; color: #666; } +.section-placeholder { + padding: 12px 0; +} + +.placeholder-text { + color: #999; + font-size: 14px; + font-style: italic; + cursor: pointer; + transition: color 0.3s ease; +} + +.placeholder-text:hover { + color: #409eff; +} + +@keyframes slideDown { + from { + opacity: 0; + max-height: 0; + } + to { + opacity: 1; + max-height: 200px; + } +} + .detail-item { display: flex; padding: 8px 0; @@ -1172,6 +1276,21 @@ watch( color: #666; } +.cart-actions { + display: flex; + align-items: center; + gap: 10px; +} + +.manage-btn { + color: #409eff; + font-size: 14px; +} + +.manage-btn:hover { + color: #66b1ff; +} + .clear-btn { color: #ff4757; font-size: 12px;