You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
350 lines
10 KiB
350 lines
10 KiB
import {useEffect, useState} from "react";
|
|
import Taro, {useShareAppMessage, useDidShow} from '@tarojs/taro';
|
|
import {
|
|
NavBar,
|
|
Checkbox,
|
|
Image,
|
|
InputNumber,
|
|
Button,
|
|
Empty,
|
|
Divider,
|
|
ConfigProvider
|
|
} from '@nutui/nutui-react-taro';
|
|
import {ArrowLeft, Del} from '@nutui/icons-react-taro';
|
|
import {View} from '@tarojs/components';
|
|
import {CartItem, useCart} from "@/hooks/useCart";
|
|
import './cart.scss';
|
|
|
|
function Cart() {
|
|
const [statusBarHeight, setStatusBarHeight] = useState<number>(0);
|
|
const [selectedItems, setSelectedItems] = useState<number[]>([]);
|
|
const [isAllSelected, setIsAllSelected] = useState(false);
|
|
|
|
const {
|
|
cartItems,
|
|
cartCount,
|
|
updateQuantity,
|
|
removeFromCart,
|
|
clearCart,
|
|
loadCartFromStorage
|
|
} = useCart();
|
|
|
|
// InputNumber 主题配置
|
|
const customTheme = {
|
|
nutuiInputnumberButtonWidth: '28px',
|
|
nutuiInputnumberButtonHeight: '28px',
|
|
nutuiInputnumberInputWidth: '40px',
|
|
nutuiInputnumberInputHeight: '28px',
|
|
nutuiInputnumberInputBorderRadius: '4px',
|
|
nutuiInputnumberButtonBorderRadius: '4px',
|
|
}
|
|
|
|
useShareAppMessage(() => {
|
|
return {
|
|
title: '购物车 - 网宿小店',
|
|
success: function () {
|
|
console.log('分享成功');
|
|
},
|
|
fail: function () {
|
|
console.log('分享失败');
|
|
}
|
|
};
|
|
});
|
|
|
|
// 页面显示时刷新购物车数据
|
|
useDidShow(() => {
|
|
loadCartFromStorage();
|
|
});
|
|
|
|
useEffect(() => {
|
|
Taro.getSystemInfo({
|
|
success: (res) => {
|
|
setStatusBarHeight(res.statusBarHeight || 0);
|
|
},
|
|
});
|
|
|
|
// 设置导航栏背景色
|
|
Taro.setNavigationBarColor({
|
|
backgroundColor: '#ffffff',
|
|
frontColor: 'black',
|
|
});
|
|
}, []);
|
|
|
|
// 根据购物车状态动态设置页面背景色
|
|
useEffect(() => {
|
|
if (cartItems.length === 0) {
|
|
// 购物车为空时设置透明背景
|
|
Taro.setBackgroundColor({
|
|
backgroundColor: 'transparent'
|
|
});
|
|
} else {
|
|
// 有商品时恢复默认背景
|
|
Taro.setBackgroundColor({
|
|
backgroundColor: '#f5f5f5'
|
|
});
|
|
}
|
|
}, [cartItems.length]);
|
|
|
|
// 处理单个商品选择
|
|
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;
|
|
}
|
|
|
|
// 获取选中的商品
|
|
const selectedCartItems = cartItems.filter((item: CartItem) =>
|
|
selectedItems.includes(item.goodsId)
|
|
);
|
|
|
|
// 将选中的商品信息存储到本地,供结算页面使用
|
|
Taro.setStorageSync('checkout_items', JSON.stringify(selectedCartItems));
|
|
|
|
// 跳转到购物车结算页面
|
|
Taro.navigateTo({
|
|
url: '/shop/orderConfirmCart/index'
|
|
});
|
|
};
|
|
|
|
// 检查是否全选
|
|
useEffect(() => {
|
|
if (cartItems.length > 0 && selectedItems.length === cartItems.length) {
|
|
setIsAllSelected(true);
|
|
} else {
|
|
setIsAllSelected(false);
|
|
}
|
|
}, [selectedItems, cartItems]);
|
|
|
|
if (cartItems.length === 0) {
|
|
return (
|
|
<>
|
|
<NavBar
|
|
fixed={true}
|
|
style={{marginTop: `${statusBarHeight}px`}}
|
|
left={<ArrowLeft onClick={() => Taro.navigateBack()}/>}
|
|
right={
|
|
cartItems.length > 0 && (
|
|
<Button
|
|
size="small"
|
|
type="primary"
|
|
fill="none"
|
|
onClick={() => {
|
|
Taro.showModal({
|
|
title: '确认清空',
|
|
content: '确定要清空购物车吗?',
|
|
success: (res) => {
|
|
if (res.confirm) {
|
|
clearCart();
|
|
setSelectedItems([]);
|
|
}
|
|
}
|
|
});
|
|
}}
|
|
>
|
|
</Button>
|
|
)
|
|
}
|
|
>
|
|
<span className="text-lg">购物车({cartCount})</span>
|
|
</NavBar>
|
|
|
|
{/* 垂直居中的空状态容器 */}
|
|
<View
|
|
className="flex items-center justify-center"
|
|
style={{
|
|
height: `calc(100vh - ${statusBarHeight + 150}px)`,
|
|
paddingTop: `${statusBarHeight + 50}px`,
|
|
backgroundColor: 'transparent'
|
|
}}
|
|
>
|
|
<Empty
|
|
description="购物车空空如也"
|
|
actions={[{ text: '去逛逛' }]}
|
|
style={{
|
|
backgroundColor: 'transparent'
|
|
}}
|
|
onClick={() => Taro.switchTab({ url: '/pages/index/index' })}
|
|
/>
|
|
</View>
|
|
</>
|
|
)
|
|
}
|
|
|
|
return (
|
|
<>
|
|
<View style={{backgroundColor: '#f6f6f6', height: `${statusBarHeight}px`}}
|
|
className="fixed z-10 top-0 w-full"></View>
|
|
<NavBar
|
|
fixed={true}
|
|
style={{marginTop: `${statusBarHeight}px`}}
|
|
left={<ArrowLeft onClick={() => Taro.navigateBack()}/>}
|
|
right={
|
|
cartItems.length > 0 && (
|
|
<Button
|
|
size="small"
|
|
type="primary"
|
|
fill="none"
|
|
onClick={() => {
|
|
Taro.showModal({
|
|
title: '确认清空',
|
|
content: '确定要清空购物车吗?',
|
|
success: (res) => {
|
|
if (res.confirm) {
|
|
clearCart();
|
|
setSelectedItems([]);
|
|
}
|
|
}
|
|
});
|
|
}}
|
|
>
|
|
</Button>
|
|
)
|
|
}
|
|
>
|
|
<span className="text-lg">购物车({cartCount})</span>
|
|
</NavBar>
|
|
|
|
{/* 购物车内容 */}
|
|
<View className="pt-24">
|
|
{/* 商品列表 */}
|
|
<View className="bg-white">
|
|
{cartItems.map((item: CartItem, index: number) => (
|
|
<View key={item.goodsId}>
|
|
<View className="bg-white px-4 py-3 flex items-center gap-3">
|
|
{/* 选择框 */}
|
|
<Checkbox
|
|
checked={selectedItems.includes(item.goodsId)}
|
|
onChange={(checked) => handleItemSelect(item.goodsId, checked)}
|
|
/>
|
|
|
|
{/* 商品图片 */}
|
|
<Image
|
|
src={item.image}
|
|
width="80"
|
|
height="80"
|
|
lazyLoad={false}
|
|
radius="8"
|
|
className="flex-shrink-0"
|
|
/>
|
|
|
|
{/* 商品信息 */}
|
|
<View className="flex-1 min-w-0">
|
|
<View className="text-lg font-bold text-gray-900 truncate mb-1">
|
|
{item.name}
|
|
</View>
|
|
<View className="flex items-center justify-between">
|
|
<View className={'flex text-red-500 text-xl items-baseline'}>
|
|
<span className={'text-xs'}>¥</span>
|
|
<span className={'font-bold text-lg'}>{item.price}</span>
|
|
</View>
|
|
<ConfigProvider theme={customTheme}>
|
|
<InputNumber
|
|
value={item.quantity}
|
|
min={1}
|
|
onChange={(value) => handleQuantityChange(item.goodsId, Number(value))}
|
|
/>
|
|
</ConfigProvider>
|
|
<Del className={'text-red-500'} size={14} onClick={() => handleRemoveItem(item.goodsId)}/>
|
|
</View>
|
|
</View>
|
|
</View>
|
|
{index < cartItems.length - 1 && <Divider/>}
|
|
</View>
|
|
))}
|
|
</View>
|
|
|
|
{/* 底部结算栏 */}
|
|
<View
|
|
className="fixed z-50 bottom-0 left-0 right-0 bg-white border-t border-gray-200 px-4 py-3 safe-area-bottom">
|
|
<View className="flex items-center justify-between">
|
|
<View className="flex items-center gap-3">
|
|
<Checkbox
|
|
checked={isAllSelected}
|
|
onChange={handleSelectAll}
|
|
>
|
|
全选
|
|
</Checkbox>
|
|
<View className="text-sm text-gray-600">
|
|
已选 {selectedItems.length} 件
|
|
</View>
|
|
</View>
|
|
|
|
<View className="flex items-center gap-4">
|
|
<View className="text-right">
|
|
<View className="text-xs text-gray-500">合计:</View>
|
|
<div className={'flex text-red-500 text-xl items-baseline'}>
|
|
<span className={'text-xs'}>¥</span>
|
|
<span className={'font-bold text-lg'}>{getSelectedTotalPrice()}</span>
|
|
</div>
|
|
</View>
|
|
<Button
|
|
type="primary"
|
|
size="large"
|
|
disabled={selectedItems.length === 0}
|
|
onClick={handleCheckout}
|
|
className="px-6"
|
|
>
|
|
结算({selectedItems.length})
|
|
</Button>
|
|
</View>
|
|
</View>
|
|
</View>
|
|
|
|
{/* 底部安全区域占位 */}
|
|
<View className="h-20"></View>
|
|
</View>
|
|
</>
|
|
);
|
|
}
|
|
|
|
export default Cart;
|