feat: 支持游客购物车,下单时才需要登录
- cart.ts: 使用 localStorage 存储游客购物车,登录后合并 - Cart.vue: 下单时检查登录状态 - Login.vue: 登录成功后自动合并游客购物车 - api/index.ts: cart list 使用静默模式避免 401 跳转 游客可以浏览商品、添加购物车,只有下单时才需要登录
This commit is contained in:
@@ -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}`),
|
||||
|
||||
+109
-4
@@ -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<any[]>([])
|
||||
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 {
|
||||
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) {
|
||||
// 添加商品到购物车
|
||||
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) {
|
||||
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) {
|
||||
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[]) {
|
||||
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
|
||||
}
|
||||
})
|
||||
@@ -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')
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
<div class="cart-page">
|
||||
<BackNav />
|
||||
|
||||
<!-- 购物车为空 -->
|
||||
<el-empty v-if="!items.length" description="购物车是空的" :image-size="120">
|
||||
<template #image>
|
||||
<el-icon :size="80" color="rgba(255,255,255,0.2)"><ShoppingCart /></el-icon>
|
||||
@@ -573,6 +574,13 @@ async function removeSelected() {
|
||||
}
|
||||
|
||||
async function handleCheckout() {
|
||||
// 检查是否登录
|
||||
if (!cartStore.isLoggedIn()) {
|
||||
ElMessage.warning('请先登录后再下单')
|
||||
router.push('/login')
|
||||
return
|
||||
}
|
||||
|
||||
if (!canCheckout.value) {
|
||||
if (!selectedAddressId.value) {
|
||||
ElMessage.warning('请选择收货地址')
|
||||
|
||||
Reference in New Issue
Block a user