购物车悬浮加减
This commit is contained in:
@@ -5,13 +5,26 @@ import {
|
||||
clearCartApi,
|
||||
deleteCartItemApi,
|
||||
getCartApi,
|
||||
getCartSummaryApi,
|
||||
updateCartItemApi,
|
||||
} from '@/services/cart'
|
||||
import type { CartItem } from '@/types/cart'
|
||||
import type { CartMutationResult } from '@/services/cart'
|
||||
import { getToken } from '@/utils/request'
|
||||
import type { CartItem, CartSummary } from '@/types/cart'
|
||||
|
||||
/** 存储 key */
|
||||
const STORAGE_KEY = 'cart_data'
|
||||
|
||||
/** 汇总请求序号(并发时仅采用最后一次响应) */
|
||||
let summarySeq = 0
|
||||
/** 汇总防抖校准定时器(列表加减停止 800ms 后整体拉取一次,以服务端为准) */
|
||||
let summaryTimer: ReturnType<typeof setTimeout> | null = null
|
||||
|
||||
/** 数值 → 2 位小数字符串(与服务端金额/数量口径一致) */
|
||||
function to2(n: number): string {
|
||||
return (Math.round(n * 100) / 100).toFixed(2)
|
||||
}
|
||||
|
||||
/** 持久化的购物车快照(服务端为准,此处仅作展示缓存) */
|
||||
interface StoredCart {
|
||||
items: CartItem[]
|
||||
@@ -46,8 +59,8 @@ interface CartState {
|
||||
loading: boolean
|
||||
/** 拉取购物车(以服务端为准,金额一律服务端重算) */
|
||||
fetchCart: () => Promise<void>
|
||||
/** 加购 */
|
||||
addItem: (productId: number, quantity: number) => Promise<void>
|
||||
/** 加购(返回合并后的购物车行,供列表页回写 cart_id/cart_quantity) */
|
||||
addItem: (productId: number, quantity: number) => Promise<CartMutationResult>
|
||||
/** 修改数量 */
|
||||
updateQuantity: (id: number, quantity: number) => Promise<void>
|
||||
/** 删除单项 */
|
||||
@@ -56,6 +69,12 @@ interface CartState {
|
||||
clearCart: () => Promise<void>
|
||||
/** 下单成功后本地清空(不请求接口) */
|
||||
clearLocal: () => void
|
||||
/** 用接口附带的汇总块(首页/商品列表响应的 cart 字段)直接更新悬浮球 */
|
||||
setSummary: (summary: CartSummary) => void
|
||||
/** 拉取轻量汇总(需登录;未登录跳过,避免 401 跳转) */
|
||||
fetchSummary: () => Promise<void>
|
||||
/** 列表行内加减后本地增减悬浮球(乐观展示),并防抖调 fetchSummary 校准 */
|
||||
applyDelta: (delta: { quantity: number; amount: number; count?: number }) => void
|
||||
}
|
||||
|
||||
/** 空的购物车快照 */
|
||||
@@ -80,6 +99,18 @@ const useCartStore = create<CartState>((set, get) => {
|
||||
}
|
||||
}
|
||||
|
||||
/** 写入悬浮球汇总并持久化(列表项快照保持不变) */
|
||||
const applySummary = (summary: CartSummary) => {
|
||||
const next = {
|
||||
items: get().items,
|
||||
totalCount: summary.total_count,
|
||||
totalQuantity: summary.total_quantity,
|
||||
totalAmount: summary.total_amount,
|
||||
}
|
||||
set(next)
|
||||
persist(next)
|
||||
}
|
||||
|
||||
return {
|
||||
...EMPTY_SNAPSHOT,
|
||||
items: cached?.items ?? [],
|
||||
@@ -111,8 +142,9 @@ const useCartStore = create<CartState>((set, get) => {
|
||||
|
||||
/** 加购:服务端校验上架与等级价,成功后重新同步 */
|
||||
addItem: async (productId, quantity) => {
|
||||
await addCartApi({ product_id: productId, quantity })
|
||||
const res = await addCartApi({ product_id: productId, quantity })
|
||||
await get().fetchCart()
|
||||
return res.data
|
||||
},
|
||||
|
||||
/** 修改数量 */
|
||||
@@ -139,6 +171,36 @@ const useCartStore = create<CartState>((set, get) => {
|
||||
set(EMPTY_SNAPSHOT)
|
||||
persist(EMPTY_SNAPSHOT)
|
||||
},
|
||||
|
||||
/** 写入接口附带的汇总块(首页/商品列表) */
|
||||
setSummary: (summary) => {
|
||||
applySummary(summary)
|
||||
},
|
||||
|
||||
/** 拉取轻量汇总(并发时仅采用最后一次响应) */
|
||||
fetchSummary: async () => {
|
||||
// 未登录无汇总(接口固定 401,会触发清理登录态),直接跳过
|
||||
if (!getToken()) return
|
||||
const seq = ++summarySeq
|
||||
const res = await getCartSummaryApi()
|
||||
if (seq !== summarySeq) return // 已有更新的请求,丢弃本次响应
|
||||
applySummary(res.data)
|
||||
},
|
||||
|
||||
/** 列表加减后的本地增减:即时反馈,防抖后以服务端汇总校准 */
|
||||
applyDelta: ({ quantity, amount, count = 0 }) => {
|
||||
const s = get()
|
||||
applySummary({
|
||||
total_count: Math.max(0, s.totalCount + count),
|
||||
total_quantity: to2(Math.max(0, Number(s.totalQuantity) + quantity)),
|
||||
total_amount: to2(Math.max(0, Number(s.totalAmount) + amount)),
|
||||
})
|
||||
if (summaryTimer) clearTimeout(summaryTimer)
|
||||
summaryTimer = setTimeout(() => {
|
||||
summaryTimer = null
|
||||
get().fetchSummary().catch(() => {})
|
||||
}, 800)
|
||||
},
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
Reference in New Issue
Block a user