diff --git a/src/api/system/userVerify/index.ts b/src/api/system/userVerify/index.ts index 3d81d15..21416e7 100644 --- a/src/api/system/userVerify/index.ts +++ b/src/api/system/userVerify/index.ts @@ -128,15 +128,3 @@ export async function submit(data: UserVerify) { } return Promise.reject(new Error(res.message)); } - -export async function myTenantList(params: any) { - const res = await request.get>( - 'http://127.0.0.1:8080/api/v1/tenants', - params - ); - if (res.code === 0) { - return res.data; - } - return Promise.reject(new Error(res.message)); -} - diff --git a/src/components/CartIcon.tsx b/src/components/CartIcon.tsx new file mode 100644 index 0000000..f3bd7eb --- /dev/null +++ b/src/components/CartIcon.tsx @@ -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 = ({ + 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 ( +
+ +
+ +
+
+
+ ); + } + + return ( +
+ +
+ ); +}; + +export default CartIcon; diff --git a/src/hooks/useCart.ts b/src/hooks/useCart.ts new file mode 100644 index 0000000..66f9c34 --- /dev/null +++ b/src/hooks/useCart.ts @@ -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([]); + 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 + }; +}; diff --git a/src/pages/cart/cart.config.ts b/src/pages/cart/cart.config.ts index 0ce9ae5..c22f898 100644 --- a/src/pages/cart/cart.config.ts +++ b/src/pages/cart/cart.config.ts @@ -1,5 +1,4 @@ export default definePageConfig({ navigationBarTitleText: '购物车', - navigationBarTextStyle: 'black', navigationStyle: 'custom' }) diff --git a/src/pages/cart/cart.tsx b/src/pages/cart/cart.tsx index db4107e..52c96a2 100644 --- a/src/pages/cart/cart.tsx +++ b/src/pages/cart/cart.tsx @@ -1,21 +1,40 @@ -import {useEffect, useState} from "react"; // 添加 useCallback 引入 +import {useEffect, useState} from "react"; import Taro, {useShareAppMessage, useShareTimeline} from '@tarojs/taro'; -import {Space, NavBar} from '@nutui/nutui-react-taro' -import {Search, Received, Scan} from '@nutui/icons-react-taro' -import GoodsList from "@/components/GoodsList"; +import { + NavBar, + Checkbox, + Image, + InputNumber, + Button, + Empty, + Divider +} from '@nutui/nutui-react-taro'; +import {ArrowLeft, Del, Shopping} from '@nutui/icons-react-taro'; +import {View} from '@tarojs/components'; +import {CartItem, useCart} from "@/hooks/useCart"; function Cart() { - const [statusBarHeight, setStatusBarHeight] = useState() + const [statusBarHeight, setStatusBarHeight] = useState(0); + const [selectedItems, setSelectedItems] = useState([]); + const [isAllSelected, setIsAllSelected] = useState(false); + + const { + cartItems, + cartCount, + updateQuantity, + removeFromCart, + clearCart + } = useCart(); useShareTimeline(() => { return { - title: '注册即可开通 - webSoft云应用' + title: '购物车 - 云上商店' }; }); useShareAppMessage(() => { return { - title: '注册即可开通 - webSoft云应用', + title: '购物车 - 云上商店', success: function (res) { console.log('分享成功', res); }, @@ -28,38 +47,235 @@ function Cart() { useEffect(() => { Taro.getSystemInfo({ success: (res) => { - setStatusBarHeight(res.statusBarHeight) + setStatusBarHeight(res.statusBarHeight || 0); }, - }) - // 设置导航栏背景色(含状态栏) + }); + + // 设置导航栏背景色 Taro.setNavigationBarColor({ - backgroundColor: '#ffffff', // 状态栏+导航栏背景色 - frontColor: 'black', // 状态栏文字颜色(仅支持 black/white) + backgroundColor: '#ffffff', + frontColor: 'black', + }); + }, []); + + // 处理单个商品选择 + const handleItemSelect = (goodsId: number, checked: boolean) => { + if (checked) { + setSelectedItems([...selectedItems, goodsId]); + } else { + setSelectedItems(selectedItems.filter(id => id !== goodsId)); + setIsAllSelected(false); + } + }; + + // 处理全选 + const handleSelectAll = (checked: boolean) => { + setIsAllSelected(checked); + if (checked) { + setSelectedItems(cartItems.map((item: CartItem) => item.goodsId)); + } else { + setSelectedItems([]); + } + }; + + // 更新商品数量 + const handleQuantityChange = (goodsId: number, value: number) => { + updateQuantity(goodsId, value); + }; + + // 删除商品 + const handleRemoveItem = (goodsId: number) => { + Taro.showModal({ + title: '确认删除', + content: '确定要删除这个商品吗?', + success: (res) => { + if (res.confirm) { + removeFromCart(goodsId); + setSelectedItems(selectedItems.filter(id => id !== goodsId)); + } + } + }); + }; + + // 计算选中商品的总价 + const getSelectedTotalPrice = () => { + return cartItems + .filter((item: CartItem) => selectedItems.includes(item.goodsId)) + .reduce((total: number, item: CartItem) => total + (parseFloat(item.price) * item.quantity), 0) + .toFixed(2); + }; + + // 去结算 + const handleCheckout = () => { + if (selectedItems.length === 0) { + Taro.showToast({ + title: '请选择要结算的商品', + icon: 'none' + }); + return; + } + + // 这里可以跳转到结算页面 + Taro.showToast({ + title: '跳转到结算页面', + icon: 'success' }); - }, []); // 新增: 添加滚动事件监听 + }; + + // 检查是否全选 + useEffect(() => { + if (cartItems.length > 0 && selectedItems.length === cartItems.length) { + setIsAllSelected(true); + } else { + setIsAllSelected(false); + } + }, [selectedItems, cartItems]); return ( <> + + { - }} - left={ - <> -
- - - - - -
- + left={ Taro.navigateBack()}/>} + right={ + cartItems.length > 0 && ( + + ) } > - 商品 + 购物车({cartCount})
- + + {/* 购物车内容 */} + + {cartItems.length === 0 ? ( + // 空购物车 + + } + description="购物车空空如也" + > + + + + ) : ( + <> + {/* 商品列表 */} + + {cartItems.map((item: CartItem, index: number) => ( + + + {/* 选择框 */} + handleItemSelect(item.goodsId, checked)} + /> + + {/* 商品图片 */} + + + {/* 商品信息 */} + + + {item.name} + + + + + {item.price} + + + handleQuantityChange(item.goodsId, Number(value))} + className="w-24" + /> + handleRemoveItem(item.goodsId)}/> + + + + + {index < cartItems.length - 1 && } + + ))} + + + {/* 底部结算栏 */} + + + + + 全选 + + + 已选 {selectedItems.length} 件 + + + + + + 合计: +
+ + {getSelectedTotalPrice()} +
+
+ +
+
+
+ + {/* 底部安全区域占位 */} + + + )} +
); } diff --git a/src/pages/index/Header.tsx b/src/pages/index/Header.tsx index 053f20a..2756790 100644 --- a/src/pages/index/Header.tsx +++ b/src/pages/index/Header.tsx @@ -6,7 +6,7 @@ import {Popup, Avatar, NavBar} from '@nutui/nutui-react-taro' import {getSiteInfo, getUserInfo, getWxOpenId} from "@/api/layout"; import {TenantId} from "@/utils/config"; import {getOrganization} from "@/api/system/organization"; -import {myTenantList, myUserVerify} from "@/api/system/userVerify"; +import {myUserVerify} from "@/api/system/userVerify"; import {CmsWebsite} from "@/api/cms/cmsWebsite/model"; import {User} from "@/api/system/user/model"; import './Header.scss'; @@ -84,10 +84,6 @@ const Header = () => { Taro.setStorageSync('RealName', data.realName) } }) - // - myTenantList({page: 2, page_size: 50}).then(res => { - console.log(res, '...res...lei') - }) } /* 获取用户手机号 */ diff --git a/src/pages/index/MySearch.tsx b/src/pages/index/MySearch.tsx index efe4174..867982a 100644 --- a/src/pages/index/MySearch.tsx +++ b/src/pages/index/MySearch.tsx @@ -29,7 +29,7 @@ function MySearch(props) { display: 'flex', alignItems: 'center', background: '#ffffff', - padding: '0 7px', + padding: '0 5px', borderRadius: '20px', marginTop: '100px', }} diff --git a/src/shop/goodsDetail/index.tsx b/src/shop/goodsDetail/index.tsx index 51e25ba..711c667 100644 --- a/src/shop/goodsDetail/index.tsx +++ b/src/shop/goodsDetail/index.tsx @@ -8,6 +8,7 @@ import {getShopGoods} from "@/api/shop/shopGoods"; import {Swiper} from '@nutui/nutui-react-taro' import {wxParse} from "@/utils/common"; import "./index.scss"; +import {useCart} from "@/hooks/useCart"; const GoodsDetail = () => { const [goods, setGoods] = useState(null); @@ -15,6 +16,21 @@ const GoodsDetail = () => { const router = Taro.getCurrentInstance().router; const goodsId = router?.params?.id; + // 使用购物车Hook + const { cartCount, addToCart } = useCart(); + + // 处理加入购物车 + const handleAddToCart = () => { + if (!goods) return; + + addToCart({ + goodsId: goods.goodsId!, + name: goods.name || '', + price: goods.price || '0', + image: goods.image || '' + }); + }; + useEffect(() => { if (goodsId) { getShopGoods(Number(goodsId)) @@ -100,8 +116,9 @@ const GoodsDetail = () => { height: "32px", top: "50px", right: "110px", - }}> - + }} + onClick={() => Taro.navigateTo({url: '/pages/cart/cart'})}> +
@@ -181,7 +198,7 @@ const GoodsDetail = () => {
Taro.showToast({title: '加入购物车成功', icon: 'success'})}>加入购物车 + onClick={() => handleAddToCart()}>加入购物车
Taro.navigateTo({url: '/shop/orderConfirm/index'})}>立即购买 diff --git a/tsconfig.json b/tsconfig.json index 18a1b6a..a71efcd 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -25,7 +25,8 @@ "@/components/*": ["./src/components/*"], "@/utils/*": ["./src/utils/*"], "@/assets/*": ["./src/assets/*"], - "@/api/*": ["./src/api/*"] + "@/api/*": ["./src/api/*"], + "@/hooks/*": ["./src/hooks/*"] } }, "include": ["./src", "./types"],