From bdc9285845d2e84266d6f9ad0a30f000c7eaca02 Mon Sep 17 00:00:00 2001 From: Admin Date: Mon, 13 Jul 2026 01:51:56 +0000 Subject: [PATCH] =?UTF-8?q?feat:=20=E6=94=AF=E6=8C=81=E6=B8=B8=E5=AE=A2?= =?UTF-8?q?=E8=B4=AD=E7=89=A9=E8=BD=A6=EF=BC=8C=E4=B8=8B=E5=8D=95=E6=97=B6?= =?UTF-8?q?=E6=89=8D=E9=9C=80=E8=A6=81=E7=99=BB=E5=BD=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - cart.ts: 使用 localStorage 存储游客购物车,登录后合并 - Cart.vue: 下单时检查登录状态 - Login.vue: 登录成功后自动合并游客购物车 - api/index.ts: cart list 使用静默模式避免 401 跳转 游客可以浏览商品、添加购物车,只有下单时才需要登录 --- frontend/src/api/index.ts | 2 +- frontend/src/store/cart.ts | 135 ++++++++++++++++++++++++++---- frontend/src/views/auth/Login.vue | 6 ++ frontend/src/views/user/Cart.vue | 8 ++ 4 files changed, 135 insertions(+), 16 deletions(-) diff --git a/frontend/src/api/index.ts b/frontend/src/api/index.ts index eee4d10..51177f0 100644 --- a/frontend/src/api/index.ts +++ b/frontend/src/api/index.ts @@ -65,7 +65,7 @@ export const productApi = { } export const cartApi = { - list: () => api.get('/cart'), + list: () => api.get('/cart', { silent: true } as any), add: (data: any) => api.post('/cart', data), update: (id: number, data: any) => api.put(`/cart/${id}`, data), delete: (id: number) => api.delete(`/cart/${id}`), diff --git a/frontend/src/store/cart.ts b/frontend/src/store/cart.ts index 537e32d..172102c 100644 --- a/frontend/src/store/cart.ts +++ b/frontend/src/store/cart.ts @@ -2,51 +2,154 @@ import { ref, watch } from 'vue' import { defineStore } from 'pinia' import { cartApi } from '../api' +const LOCAL_CART_KEY = 'guest_cart' + export const useCartStore = defineStore('cart', () => { const items = ref([]) const loading = ref(false) const totalAmount = ref(0) const totalCount = ref(0) + // 检查是否登录 + function isLoggedIn(): boolean { + return !!localStorage.getItem('token') + } + + // 获取游客购物车 + function getGuestCart(): any[] { + const cartStr = localStorage.getItem(LOCAL_CART_KEY) + return cartStr ? JSON.parse(cartStr) : [] + } + + // 保存游客购物车 + function saveGuestCart(cart: any[]) { + localStorage.setItem(LOCAL_CART_KEY, JSON.stringify(cart)) + } + + // 获取购物车(登录用户从服务器,游客从本地) async function fetchCart() { loading.value = true try { - const res: any = await cartApi.list() - items.value = res.data || [] + if (isLoggedIn()) { + // 登录用户:从服务器获取 + const res: any = await cartApi.list() + items.value = res.data || [] + } else { + // 游客:从本地存储获取 + items.value = getGuestCart() + } + } catch { + // 登录用户API失败时,也尝试使用本地数据 + items.value = getGuestCart() } finally { loading.value = false } } - async function addItem(productId: number, quantity: number) { - await cartApi.add({ product_id: productId, quantity }) - await fetchCart() + // 添加商品到购物车 + async function addItem(productId: number, quantity: number, product?: any) { + if (isLoggedIn()) { + // 登录用户:添加到服务器 + await cartApi.add({ product_id: productId, quantity }) + await fetchCart() + } else { + // 游客:添加到本地 + const guestCart = getGuestCart() + const existingItem = guestCart.find(item => item.product_id === productId) + + if (existingItem) { + existingItem.quantity += quantity + } else { + guestCart.push({ + id: Date.now(), // 临时ID + product_id: productId, + quantity, + product: product || null + }) + } + + saveGuestCart(guestCart) + items.value = guestCart + } } + // 更新购物车项数量 async function updateItem(id: number, quantity: number) { - await cartApi.update(id, { quantity }) - await fetchCart() + if (isLoggedIn()) { + await cartApi.update(id, { quantity }) + await fetchCart() + } else { + // 游客:更新本地 + const guestCart = getGuestCart() + const item = guestCart.find(item => item.id === id) + if (item) { + item.quantity = quantity + saveGuestCart(guestCart) + items.value = guestCart + } + } } + // 删除购物车项 async function removeItem(id: number) { - await cartApi.delete(id) - await fetchCart() + if (isLoggedIn()) { + await cartApi.delete(id) + await fetchCart() + } else { + // 游客:从本地删除 + const guestCart = getGuestCart() + const filtered = guestCart.filter(item => item.id !== id) + saveGuestCart(filtered) + items.value = filtered + } } + // 删除多个购物车项 async function removeItems(ids: number[]) { - await Promise.all(ids.map(id => cartApi.delete(id))) - await fetchCart() + if (isLoggedIn()) { + await Promise.all(ids.map(id => cartApi.delete(id))) + await fetchCart() + } else { + // 游客:批量删除本地项 + const guestCart = getGuestCart() + const filtered = guestCart.filter(item => !ids.includes(item.id)) + saveGuestCart(filtered) + items.value = filtered + } } + // 清空购物车 function clearCart() { items.value = [] totalAmount.value = 0 totalCount.value = 0 + localStorage.removeItem(LOCAL_CART_KEY) + } + + // 登录后合并游客购物车到服务器 + async function mergeGuestCart() { + const guestCart = getGuestCart() + if (guestCart.length === 0) return + + // 将游客购物车中的商品添加到服务器购物车 + for (const item of guestCart) { + try { + await cartApi.add({ product_id: item.product_id, quantity: item.quantity }) + } catch { + // 单个商品添加失败不影响其他商品 + } + } + + // 清空游客购物车 + localStorage.removeItem(LOCAL_CART_KEY) + + // 刷新购物车 + await fetchCart() } function calcTotals() { - totalAmount.value = items.value.reduce((sum, item) => sum + (item.product?.price || 0) * item.quantity, 0) - totalCount.value = items.value.reduce((sum, item) => sum + item.quantity, 0) + totalAmount.value = items.value.reduce((sum: number, item: any) => sum + (item.product?.price || 0) * item.quantity, 0) + totalCount.value = items.value.reduce((sum: number, item: any) => sum + item.quantity, 0) } watch(items, calcTotals, { deep: true }) @@ -61,6 +164,8 @@ export const useCartStore = defineStore('cart', () => { updateItem, removeItem, removeItems, - clearCart + clearCart, + mergeGuestCart, + isLoggedIn } -}) +}) \ No newline at end of file diff --git a/frontend/src/views/auth/Login.vue b/frontend/src/views/auth/Login.vue index 27d6520..fccaf27 100644 --- a/frontend/src/views/auth/Login.vue +++ b/frontend/src/views/auth/Login.vue @@ -54,6 +54,12 @@ async function handleLogin() { loading.value = true try { await userStore.login(form) + + // 登录成功后合并游客购物车 + const { useCartStore } = await import('../../store/cart') + const cartStore = useCartStore() + await cartStore.mergeGuestCart() + ElMessage.success(t('auth.loginSuccess')) const user = userStore.user if (user?.role === 'admin') router.push('/admin') diff --git a/frontend/src/views/user/Cart.vue b/frontend/src/views/user/Cart.vue index 7c2e9e0..d2f1f6b 100644 --- a/frontend/src/views/user/Cart.vue +++ b/frontend/src/views/user/Cart.vue @@ -2,6 +2,7 @@
+