forked from gxwebsoft/mp-10550
完成购物车功能
This commit is contained in:
@@ -1,5 +1,4 @@
|
||||
export default definePageConfig({
|
||||
navigationBarTitleText: '购物车',
|
||||
navigationBarTextStyle: 'black',
|
||||
navigationStyle: 'custom'
|
||||
})
|
||||
|
||||
@@ -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<number>()
|
||||
const [statusBarHeight, setStatusBarHeight] = useState<number>(0);
|
||||
const [selectedItems, setSelectedItems] = useState<number[]>([]);
|
||||
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)
|
||||
});
|
||||
}, []); // 新增: 添加滚动事件监听
|
||||
|
||||
// 设置导航栏背景色
|
||||
Taro.setNavigationBarColor({
|
||||
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 (
|
||||
<>
|
||||
|
||||
<View style={{backgroundColor: '#f6f6f6', height: `${statusBarHeight}px`}}
|
||||
className="fixed z-10 top-0 w-full"></View>
|
||||
<NavBar
|
||||
fixed={true}
|
||||
style={{marginTop: `${statusBarHeight}px`}}
|
||||
onBackClick={() => {
|
||||
}}
|
||||
left={
|
||||
<>
|
||||
<div className={'flex justify-between items-center w-full'}>
|
||||
<Space>
|
||||
<Search size={18} className={'mx-1'}/>
|
||||
<Received size={18} className={'mx-1'}/>
|
||||
<Scan size={18} className={'mx-1'}/>
|
||||
</Space>
|
||||
</div>
|
||||
</>
|
||||
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>商品</span>
|
||||
<span className="text-lg">购物车({cartCount})</span>
|
||||
</NavBar>
|
||||
<GoodsList/>
|
||||
|
||||
{/* 购物车内容 */}
|
||||
<View className="pt-24">
|
||||
{cartItems.length === 0 ? (
|
||||
// 空购物车
|
||||
<View className="flex flex-col items-center justify-center h-96">
|
||||
<Empty
|
||||
image={<Shopping size={80}/>}
|
||||
description="购物车空空如也"
|
||||
>
|
||||
<Button
|
||||
type="primary"
|
||||
size="small"
|
||||
onClick={() => Taro.switchTab({url: '/pages/index/index'})}
|
||||
>
|
||||
去逛逛
|
||||
</Button>
|
||||
</Empty>
|
||||
</View>
|
||||
) : (
|
||||
<>
|
||||
{/* 商品列表 */}
|
||||
<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"
|
||||
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>
|
||||
<View className="flex items-center gap-2">
|
||||
<InputNumber
|
||||
value={item.quantity}
|
||||
min={1}
|
||||
max={99}
|
||||
onChange={(value) => handleQuantityChange(item.goodsId, Number(value))}
|
||||
className="w-24"
|
||||
/>
|
||||
<Del className={'text-red-500'} size={16} onClick={() => handleRemoveItem(item.goodsId)}/>
|
||||
</View>
|
||||
</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>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -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')
|
||||
})
|
||||
}
|
||||
|
||||
/* 获取用户手机号 */
|
||||
|
||||
@@ -29,7 +29,7 @@ function MySearch(props) {
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
background: '#ffffff',
|
||||
padding: '0 7px',
|
||||
padding: '0 5px',
|
||||
borderRadius: '20px',
|
||||
marginTop: '100px',
|
||||
}}
|
||||
|
||||
Reference in New Issue
Block a user