9 changed files with 478 additions and 50 deletions
@ -0,0 +1,60 @@ |
|||||
|
import React from 'react'; |
||||
|
import { Badge } from "@nutui/nutui-react-taro"; |
||||
|
import { Cart } from "@nutui/icons-react-taro"; |
||||
|
import Taro from '@tarojs/taro'; |
||||
|
import { useCart } from "@/hooks/useCart"; |
||||
|
|
||||
|
interface CartIconProps { |
||||
|
style?: React.CSSProperties; |
||||
|
className?: string; |
||||
|
size?: number; |
||||
|
showBadge?: boolean; |
||||
|
onClick?: () => void; |
||||
|
} |
||||
|
|
||||
|
const CartIcon: React.FC<CartIconProps> = ({ |
||||
|
style, |
||||
|
className = '', |
||||
|
size = 16, |
||||
|
showBadge = true, |
||||
|
onClick |
||||
|
}) => { |
||||
|
const { cartCount } = useCart(); |
||||
|
|
||||
|
const handleClick = () => { |
||||
|
if (onClick) { |
||||
|
onClick(); |
||||
|
} else { |
||||
|
// 默认跳转到购物车页面
|
||||
|
Taro.switchTab({ url: '/pages/cart/cart' }); |
||||
|
} |
||||
|
}; |
||||
|
|
||||
|
if (showBadge) { |
||||
|
return ( |
||||
|
<div |
||||
|
className={className} |
||||
|
style={style} |
||||
|
onClick={handleClick} |
||||
|
> |
||||
|
<Badge value={cartCount} top="-2" right="2"> |
||||
|
<div style={{ display: 'flex', alignItems: 'center' }}> |
||||
|
<Cart size={size} /> |
||||
|
</div> |
||||
|
</Badge> |
||||
|
</div> |
||||
|
); |
||||
|
} |
||||
|
|
||||
|
return ( |
||||
|
<div |
||||
|
className={className} |
||||
|
style={style} |
||||
|
onClick={handleClick} |
||||
|
> |
||||
|
<Cart size={size} /> |
||||
|
</div> |
||||
|
); |
||||
|
}; |
||||
|
|
||||
|
export default CartIcon; |
@ -0,0 +1,151 @@ |
|||||
|
import { useState, useEffect } from 'react'; |
||||
|
import Taro from '@tarojs/taro'; |
||||
|
|
||||
|
// 购物车商品接口
|
||||
|
export interface CartItem { |
||||
|
goodsId: number; |
||||
|
name: string; |
||||
|
price: string; |
||||
|
image: string; |
||||
|
quantity: number; |
||||
|
addTime: number; |
||||
|
} |
||||
|
|
||||
|
// 购物车Hook
|
||||
|
export const useCart = () => { |
||||
|
const [cartItems, setCartItems] = useState<CartItem[]>([]); |
||||
|
const [cartCount, setCartCount] = useState(0); |
||||
|
|
||||
|
// 从本地存储加载购物车数据
|
||||
|
const loadCartFromStorage = () => { |
||||
|
try { |
||||
|
const cartData = Taro.getStorageSync('cart_items'); |
||||
|
if (cartData) { |
||||
|
const items = JSON.parse(cartData) as CartItem[]; |
||||
|
setCartItems(items); |
||||
|
updateCartCount(items); |
||||
|
} |
||||
|
} catch (error) { |
||||
|
console.error('加载购物车数据失败:', error); |
||||
|
} |
||||
|
}; |
||||
|
|
||||
|
// 保存购物车数据到本地存储
|
||||
|
const saveCartToStorage = (items: CartItem[]) => { |
||||
|
try { |
||||
|
Taro.setStorageSync('cart_items', JSON.stringify(items)); |
||||
|
} catch (error) { |
||||
|
console.error('保存购物车数据失败:', error); |
||||
|
} |
||||
|
}; |
||||
|
|
||||
|
// 更新购物车数量
|
||||
|
const updateCartCount = (items: CartItem[]) => { |
||||
|
const count = items.reduce((total, item) => total + item.quantity, 0); |
||||
|
setCartCount(count); |
||||
|
}; |
||||
|
|
||||
|
// 添加商品到购物车
|
||||
|
const addToCart = (goods: { |
||||
|
goodsId: number; |
||||
|
name: string; |
||||
|
price: string; |
||||
|
image: string; |
||||
|
}, quantity: number = 1) => { |
||||
|
const newItems = [...cartItems]; |
||||
|
const existingItemIndex = newItems.findIndex(item => item.goodsId === goods.goodsId); |
||||
|
|
||||
|
if (existingItemIndex >= 0) { |
||||
|
// 如果商品已存在,增加数量
|
||||
|
newItems[existingItemIndex].quantity += quantity; |
||||
|
} else { |
||||
|
// 如果商品不存在,添加新商品
|
||||
|
const newItem: CartItem = { |
||||
|
goodsId: goods.goodsId, |
||||
|
name: goods.name, |
||||
|
price: goods.price, |
||||
|
image: goods.image, |
||||
|
quantity, |
||||
|
addTime: Date.now() |
||||
|
}; |
||||
|
newItems.push(newItem); |
||||
|
} |
||||
|
|
||||
|
setCartItems(newItems); |
||||
|
updateCartCount(newItems); |
||||
|
saveCartToStorage(newItems); |
||||
|
|
||||
|
// 显示成功提示
|
||||
|
Taro.showToast({ |
||||
|
title: '加入购物车成功', |
||||
|
icon: 'success', |
||||
|
duration: 1500 |
||||
|
}); |
||||
|
}; |
||||
|
|
||||
|
// 从购物车移除商品
|
||||
|
const removeFromCart = (goodsId: number) => { |
||||
|
const newItems = cartItems.filter(item => item.goodsId !== goodsId); |
||||
|
setCartItems(newItems); |
||||
|
updateCartCount(newItems); |
||||
|
saveCartToStorage(newItems); |
||||
|
}; |
||||
|
|
||||
|
// 更新商品数量
|
||||
|
const updateQuantity = (goodsId: number, quantity: number) => { |
||||
|
if (quantity <= 0) { |
||||
|
removeFromCart(goodsId); |
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
const newItems = cartItems.map(item => |
||||
|
item.goodsId === goodsId ? { ...item, quantity } : item |
||||
|
); |
||||
|
setCartItems(newItems); |
||||
|
updateCartCount(newItems); |
||||
|
saveCartToStorage(newItems); |
||||
|
}; |
||||
|
|
||||
|
// 清空购物车
|
||||
|
const clearCart = () => { |
||||
|
setCartItems([]); |
||||
|
setCartCount(0); |
||||
|
Taro.removeStorageSync('cart_items'); |
||||
|
}; |
||||
|
|
||||
|
// 获取购物车总价
|
||||
|
const getTotalPrice = () => { |
||||
|
return cartItems.reduce((total, item) => { |
||||
|
return total + (parseFloat(item.price) * item.quantity); |
||||
|
}, 0).toFixed(2); |
||||
|
}; |
||||
|
|
||||
|
// 检查商品是否在购物车中
|
||||
|
const isInCart = (goodsId: number) => { |
||||
|
return cartItems.some(item => item.goodsId === goodsId); |
||||
|
}; |
||||
|
|
||||
|
// 获取商品在购物车中的数量
|
||||
|
const getItemQuantity = (goodsId: number) => { |
||||
|
const item = cartItems.find(item => item.goodsId === goodsId); |
||||
|
return item ? item.quantity : 0; |
||||
|
}; |
||||
|
|
||||
|
// 初始化时加载购物车数据
|
||||
|
useEffect(() => { |
||||
|
loadCartFromStorage(); |
||||
|
}, []); |
||||
|
|
||||
|
return { |
||||
|
cartItems, |
||||
|
cartCount, |
||||
|
addToCart, |
||||
|
removeFromCart, |
||||
|
updateQuantity, |
||||
|
clearCart, |
||||
|
getTotalPrice, |
||||
|
isInCart, |
||||
|
getItemQuantity, |
||||
|
loadCartFromStorage |
||||
|
}; |
||||
|
}; |
@ -1,5 +1,4 @@ |
|||||
export default definePageConfig({ |
export default definePageConfig({ |
||||
navigationBarTitleText: '购物车', |
navigationBarTitleText: '购物车', |
||||
navigationBarTextStyle: 'black', |
|
||||
navigationStyle: 'custom' |
navigationStyle: 'custom' |
||||
}) |
}) |
||||
|
Loading…
Reference in new issue