优化下单流程

This commit is contained in:
2025-07-30 15:34:27 +08:00
parent a8a84f8b39
commit b626e615c6
5 changed files with 700 additions and 0 deletions

View File

View File

@@ -0,0 +1,176 @@
import React, { useState, useEffect } from 'react';
import { View } from '@tarojs/components';
import { Popup, Button, Radio, Image, Space, Cell, CellGroup } from '@nutui/nutui-react-taro';
import { ShopGoodsSku } from '@/api/shop/shopGoodsSku/model';
import { ShopGoodsSpec } from '@/api/shop/shopGoodsSpec/model';
import { ShopGoods } from '@/api/shop/shopGoods/model';
import './index.scss';
interface SpecSelectorProps {
visible?: boolean;
onClose: () => void;
goods: ShopGoods;
specs: ShopGoodsSpec[];
skus: ShopGoodsSku[];
onConfirm: (selectedSku: ShopGoodsSku, quantity: number, action?: 'cart' | 'buy') => void;
action?: 'cart' | 'buy';
}
interface SpecGroup {
specName: string;
values: string[];
}
const SpecSelector: React.FC<SpecSelectorProps> = ({
visible = true,
onClose,
goods,
specs,
skus,
onConfirm,
action = 'cart'
}) => {
const [selectedSpecs, setSelectedSpecs] = useState<Record<string, string>>({});
const [selectedSku, setSelectedSku] = useState<ShopGoodsSku | null>(null);
const [quantity, setQuantity] = useState(1);
const [specGroups, setSpecGroups] = useState<SpecGroup[]>([]);
// 组织规格数据
useEffect(() => {
if (specs.length > 0) {
const groups: Record<string, Set<string>> = {};
specs.forEach(spec => {
if (spec.specName && spec.specValue) {
if (!groups[spec.specName]) {
groups[spec.specName] = new Set();
}
groups[spec.specName].add(spec.specValue);
}
});
const groupsArray = Object.entries(groups).map(([specName, values]) => ({
specName,
values: Array.from(values)
}));
setSpecGroups(groupsArray);
}
}, [specs]);
// 根据选中规格找到对应SKU
useEffect(() => {
if (Object.keys(selectedSpecs).length === specGroups.length && skus.length > 0) {
// 构建规格值字符串,按照规格名称排序确保一致性
const sortedSpecNames = specGroups.map(g => g.specName).sort();
const specValues = sortedSpecNames.map(name => selectedSpecs[name]).join('|');
const sku = skus.find(s => s.sku === specValues);
setSelectedSku(sku || null);
} else {
setSelectedSku(null);
}
}, [selectedSpecs, skus, specGroups]);
// 选择规格值
const handleSpecSelect = (specName: string, specValue: string) => {
setSelectedSpecs(prev => ({
...prev,
[specName]: specValue
}));
};
// 确认选择
const handleConfirm = () => {
if (!selectedSku) {
return;
}
onConfirm(selectedSku, quantity, action);
};
// 检查规格值是否可选是否有对应的SKU且有库存
const isSpecValueAvailable = (specName: string, specValue: string) => {
const testSpecs = { ...selectedSpecs, [specName]: specValue };
// 如果还有其他规格未选择,则认为可选
if (Object.keys(testSpecs).length < specGroups.length) {
return true;
}
// 构建规格值字符串
const sortedSpecNames = specGroups.map(g => g.specName).sort();
const specValues = sortedSpecNames.map(name => testSpecs[name]).join('|');
const sku = skus.find(s => s.sku === specValues);
return sku && sku.stock && sku.stock > 0 && sku.status === 0;
};
return (
<Popup
visible={visible}
position="bottom"
onClose={onClose}
style={{ height: '60vh' }}
>
<View className="spec-selector">
{/* 商品信息 */}
<View className="spec-selector__header p-4">
<Space className="flex">
<Image
src={selectedSku?.image || goods.image || ''}
width="80"
height="80"
radius="8"
/>
<View className="goods-detail">
<View className="goods-name font-medium text-lg">{goods.name}</View>
<View className="text-red-500">
¥{selectedSku?.price || goods.price}
</View>
<View className="goods-stock text-gray-500">
{selectedSku?.stock || goods.stock}
</View>
</View>
</Space>
</View>
{/* 规格选择 */}
<CellGroup className="spec-selector__content">
<Cell>
<Space direction="vertical">
<View className={'title'}></View>
<Radio.Group defaultValue="1" direction="horizontal">
<Radio shape="button" value="1">
1
</Radio>
<Radio shape="button" value="2">
2
</Radio>
<Radio shape="button" value="3">
3
</Radio>
</Radio.Group>
</Space>
</Cell>
</CellGroup>
{/* 底部按钮 */}
<View className="fixed bottom-7 w-full">
<View className={'px-4'}>
<Button
type="success"
size="large"
className={'w-full'}
block
// disabled={!selectedSku || !selectedSku.stock || selectedSku.stock <= 0}
onClick={handleConfirm}
>
</Button>
</View>
</View>
</View>
</Popup>
);
};
export default SpecSelector;