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 = {
|
export const cartApi = {
|
||||||
list: () => api.get('/cart'),
|
list: () => api.get('/cart', { silent: true } as any),
|
||||||
add: (data: any) => api.post('/cart', data),
|
add: (data: any) => api.post('/cart', data),
|
||||||
update: (id: number, data: any) => api.put(`/cart/${id}`, data),
|
update: (id: number, data: any) => api.put(`/cart/${id}`, data),
|
||||||
delete: (id: number) => api.delete(`/cart/${id}`),
|
delete: (id: number) => api.delete(`/cart/${id}`),
|
||||||
|
|||||||
+120
-15
@@ -2,51 +2,154 @@ import { ref, watch } from 'vue'
|
|||||||
import { defineStore } from 'pinia'
|
import { defineStore } from 'pinia'
|
||||||
import { cartApi } from '../api'
|
import { cartApi } from '../api'
|
||||||
|
|
||||||
|
const LOCAL_CART_KEY = 'guest_cart'
|
||||||
|
|
||||||
export const useCartStore = defineStore('cart', () => {
|
export const useCartStore = defineStore('cart', () => {
|
||||||
const items = ref<any[]>([])
|
const items = ref<any[]>([])
|
||||||
const loading = ref(false)
|
const loading = ref(false)
|
||||||
const totalAmount = ref(0)
|
const totalAmount = ref(0)
|
||||||
const totalCount = 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() {
|
async function fetchCart() {
|
||||||
loading.value = true
|
loading.value = true
|
||||||
try {
|
try {
|
||||||
const res: any = await cartApi.list()
|
if (isLoggedIn()) {
|
||||||
items.value = res.data || []
|
// 登录用户:从服务器获取
|
||||||
|
const res: any = await cartApi.list()
|
||||||
|
items.value = res.data || []
|
||||||
|
} else {
|
||||||
|
// 游客:从本地存储获取
|
||||||
|
items.value = getGuestCart()
|
||||||
|
}
|
||||||
|
} catch {
|
||||||
|
// 登录用户API失败时,也尝试使用本地数据
|
||||||
|
items.value = getGuestCart()
|
||||||
} finally {
|
} finally {
|
||||||
loading.value = false
|
loading.value = false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function addItem(productId: number, quantity: number) {
|
// 添加商品到购物车
|
||||||
await cartApi.add({ product_id: productId, quantity })
|
async function addItem(productId: number, quantity: number, product?: any) {
|
||||||
await fetchCart()
|
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) {
|
async function updateItem(id: number, quantity: number) {
|
||||||
await cartApi.update(id, { quantity })
|
if (isLoggedIn()) {
|
||||||
await fetchCart()
|
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) {
|
async function removeItem(id: number) {
|
||||||
await cartApi.delete(id)
|
if (isLoggedIn()) {
|
||||||
await fetchCart()
|
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[]) {
|
async function removeItems(ids: number[]) {
|
||||||
await Promise.all(ids.map(id => cartApi.delete(id)))
|
if (isLoggedIn()) {
|
||||||
await fetchCart()
|
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() {
|
function clearCart() {
|
||||||
items.value = []
|
items.value = []
|
||||||
totalAmount.value = 0
|
totalAmount.value = 0
|
||||||
totalCount.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() {
|
function calcTotals() {
|
||||||
totalAmount.value = items.value.reduce((sum, item) => sum + (item.product?.price || 0) * 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, item) => sum + item.quantity, 0)
|
totalCount.value = items.value.reduce((sum: number, item: any) => sum + item.quantity, 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
watch(items, calcTotals, { deep: true })
|
watch(items, calcTotals, { deep: true })
|
||||||
@@ -61,6 +164,8 @@ export const useCartStore = defineStore('cart', () => {
|
|||||||
updateItem,
|
updateItem,
|
||||||
removeItem,
|
removeItem,
|
||||||
removeItems,
|
removeItems,
|
||||||
clearCart
|
clearCart,
|
||||||
|
mergeGuestCart,
|
||||||
|
isLoggedIn
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@@ -54,6 +54,12 @@ async function handleLogin() {
|
|||||||
loading.value = true
|
loading.value = true
|
||||||
try {
|
try {
|
||||||
await userStore.login(form)
|
await userStore.login(form)
|
||||||
|
|
||||||
|
// 登录成功后合并游客购物车
|
||||||
|
const { useCartStore } = await import('../../store/cart')
|
||||||
|
const cartStore = useCartStore()
|
||||||
|
await cartStore.mergeGuestCart()
|
||||||
|
|
||||||
ElMessage.success(t('auth.loginSuccess'))
|
ElMessage.success(t('auth.loginSuccess'))
|
||||||
const user = userStore.user
|
const user = userStore.user
|
||||||
if (user?.role === 'admin') router.push('/admin')
|
if (user?.role === 'admin') router.push('/admin')
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
<div class="cart-page">
|
<div class="cart-page">
|
||||||
<BackNav />
|
<BackNav />
|
||||||
|
|
||||||
|
<!-- 购物车为空 -->
|
||||||
<el-empty v-if="!items.length" description="购物车是空的" :image-size="120">
|
<el-empty v-if="!items.length" description="购物车是空的" :image-size="120">
|
||||||
<template #image>
|
<template #image>
|
||||||
<el-icon :size="80" color="rgba(255,255,255,0.2)"><ShoppingCart /></el-icon>
|
<el-icon :size="80" color="rgba(255,255,255,0.2)"><ShoppingCart /></el-icon>
|
||||||
@@ -573,6 +574,13 @@ async function removeSelected() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function handleCheckout() {
|
async function handleCheckout() {
|
||||||
|
// 检查是否登录
|
||||||
|
if (!cartStore.isLoggedIn()) {
|
||||||
|
ElMessage.warning('请先登录后再下单')
|
||||||
|
router.push('/login')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
if (!canCheckout.value) {
|
if (!canCheckout.value) {
|
||||||
if (!selectedAddressId.value) {
|
if (!selectedAddressId.value) {
|
||||||
ElMessage.warning('请选择收货地址')
|
ElMessage.warning('请选择收货地址')
|
||||||
|
|||||||
Reference in New Issue
Block a user