| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316 |
- import React, { createContext, useContext, useState, useEffect, ReactNode } from 'react'
- import Taro from '@tarojs/taro'
- export interface CartItem {
- id: number // 商品ID(当前子商品ID,或父商品ID如果无规格)
- parentGoodsId: number // 父商品ID,0表示无父商品(单规格商品)
- name: string // 商品名称(包含规格信息的完整名称)
- price: number // 商品价格
- image: string // 商品图片
- stock: number // 商品库存
- quantity: number // 购买数量
- }
- export interface CartState {
- items: CartItem[]
- totalAmount: number
- totalCount: number
- }
- interface CartContextType {
- cart: CartState
- addToCart: (item: CartItem) => void
- removeFromCart: (id: number) => void
- updateQuantity: (id: number, quantity: number) => void
- switchSpec: (cartItemId: number, newChildGoods: { id: number; name: string; price: number; stock: number; image?: string }, quantity?: number) => void
- clearCart: () => void
- isInCart: (id: number) => boolean
- getItemQuantity: (id: number) => number
- isLoading: boolean
- }
- const CartContext = createContext<CartContextType | undefined>(undefined)
- const CART_STORAGE_KEY = 'mini_cart'
- export const CartProvider: React.FC<{ children: ReactNode }> = ({ children }) => {
- const [cart, setCart] = useState<CartState>({
- items: [],
- totalAmount: 0,
- totalCount: 0
- })
- const [isLoading, setIsLoading] = useState(true)
- // 从本地存储加载购物车
- useEffect(() => {
- const loadCart = () => {
- try {
- const savedCart = Taro.getStorageSync(CART_STORAGE_KEY)
- if (savedCart && Array.isArray(savedCart.items)) {
- // 数据迁移:确保每个购物车项都有parentGoodsId字段
- const migratedItems = savedCart.items.map((item: any) => ({
- ...item,
- parentGoodsId: item.parentGoodsId !== undefined ? item.parentGoodsId : 0 // 旧数据默认为0(单规格商品)
- }))
- const totalAmount = migratedItems.reduce((sum: number, item: CartItem) =>
- sum + (item.price * item.quantity), 0)
- const totalCount = migratedItems.reduce((sum: number, item: CartItem) =>
- sum + item.quantity, 0)
- setCart({
- items: migratedItems,
- totalAmount,
- totalCount
- })
- }
- } catch (error) {
- console.error('加载购物车失败:', error)
- } finally {
- setIsLoading(false)
- }
- }
- loadCart()
- }, [])
- // 保存购物车到本地存储
- const saveCart = (items: CartItem[]) => {
- const totalAmount = items.reduce((sum, item) => sum + (item.price * item.quantity), 0)
- const totalCount = items.reduce((sum, item) => sum + item.quantity, 0)
- const newCart = {
- items,
- totalAmount,
- totalCount
- }
- setCart(newCart)
- try {
- Taro.setStorageSync(CART_STORAGE_KEY, { items })
- } catch (error) {
- console.error('保存购物车失败:', error)
- }
- }
- // 添加商品到购物车,支持父商品和子商品
- // 注意:父子商品的租户一致性验证在API层面进行
- const addToCart = (item: CartItem) => {
- const existingItem = cart.items.find(cartItem => cartItem.id === item.id)
- if (existingItem) {
- // 如果商品已存在,增加数量
- const newQuantity = Math.min(existingItem.quantity + item.quantity, item.stock)
- if (newQuantity === existingItem.quantity) {
- Taro.showToast({
- title: '库存不足',
- icon: 'none'
- })
- return
- }
- const newItems = cart.items.map(cartItem =>
- cartItem.id === item.id
- ? { ...cartItem, quantity: newQuantity }
- : cartItem
- )
- saveCart(newItems)
- Taro.showToast({
- title: '已更新购物车',
- icon: 'success'
- })
- } else {
- // 添加新商品
- if (item.quantity > item.stock) {
- Taro.showToast({
- title: '库存不足',
- icon: 'none'
- })
- return
- }
- saveCart([...cart.items, item])
- }
- }
- // 从购物车移除商品
- const removeFromCart = (id: number) => {
- const newItems = cart.items.filter(item => item.id !== id)
- saveCart(newItems)
- Taro.showToast({
- title: '已移除商品',
- icon: 'success'
- })
- }
- // 更新商品数量
- const updateQuantity = (id: number, quantity: number) => {
- const item = cart.items.find(item => item.id === id)
- if (!item) return
- // 当数量小于等于0时,设为1而不是删除商品
- if (quantity <= 0) {
- quantity = 1
- }
- if (quantity > item.stock) {
- Taro.showToast({
- title: '库存不足',
- icon: 'none'
- })
- return
- }
- const newItems = cart.items.map(item =>
- item.id === id ? { ...item, quantity } : item
- )
- saveCart(newItems)
- }
- // 清空购物车
- const clearCart = () => {
- saveCart([])
- Taro.showToast({
- title: '已清空购物车',
- icon: 'success'
- })
- }
- // 检查商品是否在购物车中
- const isInCart = (id: number) => {
- return cart.items.some(item => item.id === id)
- }
- // 获取购物车中商品数量
- const getItemQuantity = (id: number) => {
- const item = cart.items.find(item => item.id === id)
- return item ? item.quantity : 0
- }
- // 切换购物车项规格
- const switchSpec = (
- cartItemId: number,
- newChildGoods: { id: number; name: string; price: number; stock: number; image?: string },
- quantity?: number
- ) => {
- try {
- const item = cart.items.find(item => item.id === cartItemId)
- if (!item) {
- console.error('切换规格失败:购物车项不存在', cartItemId)
- Taro.showToast({
- title: '商品不存在',
- icon: 'none'
- })
- return
- }
- // 检查是否是父商品(允许切换规格)
- if (item.parentGoodsId === 0) {
- console.error('切换规格失败:单规格商品不支持切换', cartItemId)
- Taro.showToast({
- title: '该商品不支持切换规格',
- icon: 'none'
- })
- return
- }
- // 检查新规格库存是否足够
- if (newChildGoods.stock <= 0) {
- console.error('切换规格失败:规格无库存', { newStock: newChildGoods.stock })
- Taro.showToast({
- title: '该规格已售罄',
- icon: 'none'
- })
- return
- }
- // 确定要使用的数量:如果提供了quantity参数则使用,否则使用原有数量
- const finalQuantity = quantity !== undefined ? quantity : item.quantity
- // 验证数量有效性
- if (finalQuantity <= 0) {
- console.error('切换规格失败:数量无效', { finalQuantity })
- Taro.showToast({
- title: '数量不能小于1',
- icon: 'none'
- })
- return
- }
- if (finalQuantity > newChildGoods.stock) {
- console.error('切换规格失败:库存不足', { finalQuantity, newStock: newChildGoods.stock })
- Taro.showToast({
- title: `规格库存不足,仅剩${newChildGoods.stock}件`,
- icon: 'none'
- })
- return
- }
- // 验证新规格数据完整性
- if (!newChildGoods.id || !newChildGoods.name || newChildGoods.price < 0) {
- console.error('切换规格失败:规格数据不完整', newChildGoods)
- Taro.showToast({
- title: '规格数据错误',
- icon: 'none'
- })
- return
- }
- // 创建更新后的购物车项
- const updatedItem: CartItem = {
- ...item,
- id: newChildGoods.id,
- name: newChildGoods.name,
- price: newChildGoods.price,
- stock: newChildGoods.stock,
- image: newChildGoods.image || item.image,
- quantity: finalQuantity
- }
- // 更新购物车
- const newItems = cart.items.map(cartItem =>
- cartItem.id === cartItemId ? updatedItem : cartItem
- )
- saveCart(newItems)
- Taro.showToast({
- title: '已切换规格',
- icon: 'success'
- })
- } catch (error) {
- console.error('切换规格时发生异常:', error)
- Taro.showToast({
- title: '切换规格失败,请重试',
- icon: 'none'
- })
- }
- }
- const value = {
- cart,
- addToCart,
- removeFromCart,
- updateQuantity,
- switchSpec,
- clearCart,
- isInCart,
- getItemQuantity,
- isLoading
- }
- return (
- <CartContext.Provider value={value}>
- {children}
- </CartContext.Provider>
- )
- }
- export const useCart = () => {
- const context = useContext(CartContext)
- if (context === undefined) {
- throw new Error('useCart must be used within a CartProvider')
- }
- return context
- }
|