refactor(cart): 将购物车逻辑从React迁移到Vue
迁移购物车上下文逻辑,从React的CartContext.jsx迁移到Vue的cart.js,以支持Vue项目的需求。新的实现使用Vue的ref和provide/inject API,并保留了原有的功能,如购物车管理、本地存储同步和结账流程。
Showing
5 changed files
with
122 additions
and
140 deletions
src/contexts/CartContext.jsx
deleted
100644 → 0
| 1 | -import React, { createContext, useContext, useState, useEffect } from 'react'; | ||
| 2 | -import { useNavigate } from 'react-router-dom'; | ||
| 3 | - | ||
| 4 | -// Create a context for the cart | ||
| 5 | -const CartContext = createContext(); | ||
| 6 | - | ||
| 7 | -// Custom hook to use the cart context | ||
| 8 | -export const useCart = () => { | ||
| 9 | - const context = useContext(CartContext); | ||
| 10 | - if (!context) { | ||
| 11 | - throw new Error('useCart must be used within a CartProvider'); | ||
| 12 | - } | ||
| 13 | - return context; | ||
| 14 | -}; | ||
| 15 | - | ||
| 16 | -// Cart provider component | ||
| 17 | -export const CartProvider = ({ children }) => { | ||
| 18 | - const [cartItems, setCartItems] = useState([]); | ||
| 19 | - const navigate = useNavigate(); | ||
| 20 | - | ||
| 21 | - // Load cart from localStorage on component mount | ||
| 22 | - useEffect(() => { | ||
| 23 | - const storedCart = localStorage.getItem('cart'); | ||
| 24 | - if (storedCart) { | ||
| 25 | - try { | ||
| 26 | - setCartItems(JSON.parse(storedCart)); | ||
| 27 | - } catch (error) { | ||
| 28 | - console.error('Failed to parse cart from localStorage:', error); | ||
| 29 | - // Reset cart if there's an error | ||
| 30 | - localStorage.removeItem('cart'); | ||
| 31 | - } | ||
| 32 | - } | ||
| 33 | - }, []); | ||
| 34 | - | ||
| 35 | - // Save cart to localStorage whenever it changes | ||
| 36 | - useEffect(() => { | ||
| 37 | - localStorage.setItem('cart', JSON.stringify(cartItems)); | ||
| 38 | - }, [cartItems]); | ||
| 39 | - | ||
| 40 | - // Add an item to the cart | ||
| 41 | - const addToCart = (item) => { | ||
| 42 | - setCartItems(prevItems => { | ||
| 43 | - // Check if item already exists in cart | ||
| 44 | - const existingItemIndex = prevItems.findIndex(i => | ||
| 45 | - i.id === item.id && i.type === item.type | ||
| 46 | - ); | ||
| 47 | - | ||
| 48 | - if (existingItemIndex >= 0) { | ||
| 49 | - // Item exists, update the quantity | ||
| 50 | - const updatedItems = [...prevItems]; | ||
| 51 | - updatedItems[existingItemIndex] = { | ||
| 52 | - ...updatedItems[existingItemIndex], | ||
| 53 | - quantity: updatedItems[existingItemIndex].quantity + 1 | ||
| 54 | - }; | ||
| 55 | - return updatedItems; | ||
| 56 | - } else { | ||
| 57 | - // Item doesn't exist, add it with quantity 1 | ||
| 58 | - return [...prevItems, { ...item, quantity: 1 }]; | ||
| 59 | - } | ||
| 60 | - }); | ||
| 61 | - }; | ||
| 62 | - | ||
| 63 | - // Remove an item from the cart | ||
| 64 | - const removeFromCart = (itemId, itemType) => { | ||
| 65 | - setCartItems(prevItems => | ||
| 66 | - prevItems.filter(item => !(item.id === itemId && item.type === itemType)) | ||
| 67 | - ); | ||
| 68 | - }; | ||
| 69 | - | ||
| 70 | - // Update quantity of an item in the cart | ||
| 71 | - const updateQuantity = (itemId, itemType, quantity) => { | ||
| 72 | - if (quantity < 1) return; | ||
| 73 | - | ||
| 74 | - setCartItems(prevItems => | ||
| 75 | - prevItems.map(item => | ||
| 76 | - (item.id === itemId && item.type === itemType) | ||
| 77 | - ? { ...item, quantity } | ||
| 78 | - : item | ||
| 79 | - ) | ||
| 80 | - ); | ||
| 81 | - }; | ||
| 82 | - | ||
| 83 | - // Clear the entire cart | ||
| 84 | - const clearCart = () => { | ||
| 85 | - setCartItems([]); | ||
| 86 | - }; | ||
| 87 | - | ||
| 88 | - // Get the number of items in cart | ||
| 89 | - const getItemCount = () => { | ||
| 90 | - return cartItems.reduce((total, item) => total + item.quantity, 0); | ||
| 91 | - }; | ||
| 92 | - | ||
| 93 | - // Calculate the total price of items in the cart | ||
| 94 | - const getTotalPrice = () => { | ||
| 95 | - return cartItems.reduce( | ||
| 96 | - (total, item) => total + (item.price * item.quantity), | ||
| 97 | - 0 | ||
| 98 | - ); | ||
| 99 | - }; | ||
| 100 | - | ||
| 101 | - // Proceed to checkout | ||
| 102 | - const proceedToCheckout = () => { | ||
| 103 | - if (cartItems.length > 0) { | ||
| 104 | - navigate('/checkout'); | ||
| 105 | - } | ||
| 106 | - }; | ||
| 107 | - | ||
| 108 | - // Handle the checkout process | ||
| 109 | - const handleCheckout = (userData) => { | ||
| 110 | - // In a real application, this would send the order to a backend | ||
| 111 | - console.log('Processing order with data:', { items: cartItems, userData }); | ||
| 112 | - | ||
| 113 | - // Simulating successful checkout | ||
| 114 | - return new Promise((resolve) => { | ||
| 115 | - setTimeout(() => { | ||
| 116 | - clearCart(); | ||
| 117 | - resolve({ success: true, orderId: 'ORD-' + Date.now() }); | ||
| 118 | - }, 1500); | ||
| 119 | - }); | ||
| 120 | - }; | ||
| 121 | - | ||
| 122 | - // Values to provide in the context | ||
| 123 | - const value = { | ||
| 124 | - cartItems, | ||
| 125 | - addToCart, | ||
| 126 | - removeFromCart, | ||
| 127 | - updateQuantity, | ||
| 128 | - clearCart, | ||
| 129 | - getItemCount, | ||
| 130 | - getTotalPrice, | ||
| 131 | - proceedToCheckout, | ||
| 132 | - handleCheckout | ||
| 133 | - }; | ||
| 134 | - | ||
| 135 | - return ( | ||
| 136 | - <CartContext.Provider value={value}> | ||
| 137 | - {children} | ||
| 138 | - </CartContext.Provider> | ||
| 139 | - ); | ||
| 140 | -}; | ||
| ... | \ No newline at end of file | ... | \ No newline at end of file |
src/contexts/cart.js
0 → 100644
| 1 | +import { ref, provide, inject, watchEffect } from 'vue' | ||
| 2 | +import { useRouter } from 'vue-router' | ||
| 3 | + | ||
| 4 | +const CartSymbol = Symbol() | ||
| 5 | + | ||
| 6 | +export function provideCart() { | ||
| 7 | + const router = useRouter() | ||
| 8 | + const cartItems = ref([]) | ||
| 9 | + | ||
| 10 | + // 从localStorage加载购物车数据 | ||
| 11 | + try { | ||
| 12 | + const storedCart = localStorage.getItem('cart') | ||
| 13 | + if (storedCart) { | ||
| 14 | + cartItems.value = JSON.parse(storedCart) | ||
| 15 | + } | ||
| 16 | + } catch (error) { | ||
| 17 | + console.error('Failed to parse cart from localStorage:', error) | ||
| 18 | + localStorage.removeItem('cart') | ||
| 19 | + } | ||
| 20 | + | ||
| 21 | + // 监听购物车变化并保存到localStorage | ||
| 22 | + watchEffect(() => { | ||
| 23 | + localStorage.setItem('cart', JSON.stringify(cartItems.value)) | ||
| 24 | + }) | ||
| 25 | + | ||
| 26 | + // 添加商品到购物车 | ||
| 27 | + function addToCart(item) { | ||
| 28 | + const existingItemIndex = cartItems.value.findIndex( | ||
| 29 | + i => i.id === item.id && i.type === item.type | ||
| 30 | + ) | ||
| 31 | + | ||
| 32 | + if (existingItemIndex >= 0) { | ||
| 33 | + const updatedItems = [...cartItems.value] | ||
| 34 | + updatedItems[existingItemIndex] = { | ||
| 35 | + ...updatedItems[existingItemIndex], | ||
| 36 | + quantity: updatedItems[existingItemIndex].quantity + 1 | ||
| 37 | + } | ||
| 38 | + cartItems.value = updatedItems | ||
| 39 | + } else { | ||
| 40 | + cartItems.value = [...cartItems.value, { ...item, quantity: 1 }] | ||
| 41 | + } | ||
| 42 | + } | ||
| 43 | + | ||
| 44 | + // 从购物车移除商品 | ||
| 45 | + function removeFromCart(itemId, itemType) { | ||
| 46 | + cartItems.value = cartItems.value.filter( | ||
| 47 | + item => !(item.id === itemId && item.type === itemType) | ||
| 48 | + ) | ||
| 49 | + } | ||
| 50 | + | ||
| 51 | + // 更新商品数量 | ||
| 52 | + function updateQuantity(itemId, itemType, quantity) { | ||
| 53 | + if (quantity < 1) return | ||
| 54 | + | ||
| 55 | + cartItems.value = cartItems.value.map(item => | ||
| 56 | + item.id === itemId && item.type === itemType | ||
| 57 | + ? { ...item, quantity } | ||
| 58 | + : item | ||
| 59 | + ) | ||
| 60 | + } | ||
| 61 | + | ||
| 62 | + // 清空购物车 | ||
| 63 | + function clearCart() { | ||
| 64 | + cartItems.value = [] | ||
| 65 | + } | ||
| 66 | + | ||
| 67 | + // 获取购物车商品总数 | ||
| 68 | + function getItemCount() { | ||
| 69 | + return cartItems.value.reduce((total, item) => total + item.quantity, 0) | ||
| 70 | + } | ||
| 71 | + | ||
| 72 | + // 计算购物车总价 | ||
| 73 | + function getTotalPrice() { | ||
| 74 | + return cartItems.value.reduce( | ||
| 75 | + (total, item) => total + item.price * item.quantity, | ||
| 76 | + 0 | ||
| 77 | + ) | ||
| 78 | + } | ||
| 79 | + | ||
| 80 | + // 跳转到结账页面 | ||
| 81 | + function proceedToCheckout() { | ||
| 82 | + if (cartItems.value.length > 0) { | ||
| 83 | + router.push('/checkout') | ||
| 84 | + } | ||
| 85 | + } | ||
| 86 | + | ||
| 87 | + // 处理结账流程 | ||
| 88 | + function handleCheckout(userData) { | ||
| 89 | + console.warn('Processing order with data:', { items: cartItems.value, userData }) | ||
| 90 | + | ||
| 91 | + return new Promise((resolve) => { | ||
| 92 | + setTimeout(() => { | ||
| 93 | + clearCart() | ||
| 94 | + resolve({ success: true, orderId: 'ORD-' + Date.now() }) | ||
| 95 | + }, 1500) | ||
| 96 | + }) | ||
| 97 | + } | ||
| 98 | + | ||
| 99 | + const cart = { | ||
| 100 | + items: cartItems, | ||
| 101 | + addToCart, | ||
| 102 | + removeFromCart, | ||
| 103 | + updateQuantity, | ||
| 104 | + clearCart, | ||
| 105 | + getItemCount, | ||
| 106 | + getTotalPrice, | ||
| 107 | + proceedToCheckout, | ||
| 108 | + handleCheckout | ||
| 109 | + } | ||
| 110 | + | ||
| 111 | + provide(CartSymbol, cart) | ||
| 112 | + | ||
| 113 | + return cart | ||
| 114 | +} | ||
| 115 | + | ||
| 116 | +export function useCart() { | ||
| 117 | + const cart = inject(CartSymbol) | ||
| 118 | + if (!cart) { | ||
| 119 | + throw new Error('useCart() must be used within a component that has called provideCart()') | ||
| 120 | + } | ||
| 121 | + return cart | ||
| 122 | +} |
This diff is collapsed. Click to expand it.
src/views/checkout/CheckoutPage.jsx
0 → 100644
This diff is collapsed. Click to expand it.
src/views/checkout/CheckoutPage.vue
0 → 100644
This diff is collapsed. Click to expand it.
-
Please register or login to post a comment