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.
8.1 KiB
8.1 KiB
🔍 SpecSelector规格选择组件深度分析
📋 组件概述
SpecSelector
是商品规格选择组件,用于处理多规格商品的选择逻辑。通过分析代码发现了多个关键问题需要改进。
🚨 发现的主要问题
1. 核心功能缺失 - 规格选择逻辑未实现
问题描述
// ❌ 关键函数被注释掉了
// const handleSpecSelect = (specName: string, specValue: string) => {
// setSelectedSpecs(prev => ({
// ...prev,
// [specName]: specValue
// }));
// };
影响
- 无法选择规格:用户无法点击选择具体的规格值
- SKU匹配失效:无法根据选择的规格找到对应的SKU
- 价格库存不更新:选择规格后价格和库存不会动态更新
2. 规格可用性检查缺失
问题描述
// ❌ 规格可用性检查函数被注释
// const isSpecValueAvailable = (specName: string, specValue: string) => {
// // 检查规格值是否有库存、是否可选
// };
影响
- 无库存规格仍可选:用户可能选择无库存的规格组合
- 用户体验差:无法提前知道哪些规格组合不可用
- 订单失败风险:可能生成无效订单
3. UI与数据逻辑分离
问题描述
// ❌ 硬编码的UI,与动态数据不匹配
<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>
影响
- 静态选项:显示固定的"选项1、选项2、选项3"
- 数据不匹配:与实际的
specs
数据完全脱节 - 功能失效:选择操作不会影响实际状态
4. 数量选择功能缺失
问题描述
// ❌ 没有数量选择器
const [quantity, setQuantity] = useState(1); // 状态存在但无UI
影响
- 无法调整数量:用户只能购买1个商品
- 批量购买不支持:无法满足批量购买需求
5. 错误处理和验证不足
问题描述
// ❌ 确认按钮没有充分验证
const handleConfirm = () => {
if (!selectedSku) {
return; // 静默失败,用户不知道为什么无法确认
}
onConfirm(selectedSku, quantity, action);
};
影响
- 静默失败:用户不知道为什么无法确认
- 缺少提示:没有错误提示和引导
- 体验差:用户困惑为什么按钮无响应
📊 数据流分析
当前数据流(有问题)
specs数据 → specGroups ❌ UI显示硬编码选项
selectedSpecs ❌ 永远为空对象
selectedSku ❌ 永远为null
确认按钮 ❌ 永远被禁用
期望数据流(正确)
specs数据 → specGroups → 动态生成UI选项
用户选择 → selectedSpecs更新 → SKU匹配
SKU匹配 → selectedSku更新 → 价格库存更新
用户确认 → 验证通过 → 回调执行
🔧 需要实现的核心功能
1. 动态规格选择器
// 需要实现
const renderSpecOptions = () => {
return specGroups.map(group => (
<Cell key={group.specName}>
<Space direction="vertical">
<View className="title">{group.specName}</View>
<Radio.Group
value={selectedSpecs[group.specName]}
onChange={(value) => handleSpecSelect(group.specName, value)}
>
{group.values.map(value => (
<Radio
key={value}
shape="button"
value={value}
disabled={!isSpecValueAvailable(group.specName, value)}
>
{value}
</Radio>
))}
</Radio.Group>
</Space>
</Cell>
));
};
2. 规格选择处理
const handleSpecSelect = (specName: string, specValue: string) => {
setSelectedSpecs(prev => ({
...prev,
[specName]: specValue
}));
};
3. 规格可用性检查
const isSpecValueAvailable = (specName: string, specValue: string) => {
const testSpecs = { ...selectedSpecs, [specName]: specValue };
// 如果还有其他规格未选择,则认为可选
if (Object.keys(testSpecs).length < specGroups.length) {
return true;
}
// 构建规格值字符串并查找SKU
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;
};
4. 数量选择器
const renderQuantitySelector = () => (
<Cell>
<Space direction="vertical">
<View className="title">数量</View>
<InputNumber
value={quantity}
min={1}
max={selectedSku?.stock || 999}
onChange={setQuantity}
/>
</Space>
</Cell>
);
5. 完善的确认逻辑
const handleConfirm = () => {
// 验证规格选择
if (Object.keys(selectedSpecs).length < specGroups.length) {
Taro.showToast({
title: '请选择完整规格',
icon: 'none'
});
return;
}
// 验证SKU
if (!selectedSku) {
Taro.showToast({
title: '所选规格暂无库存',
icon: 'none'
});
return;
}
// 验证库存
if (!selectedSku.stock || selectedSku.stock < quantity) {
Taro.showToast({
title: '库存不足',
icon: 'none'
});
return;
}
onConfirm(selectedSku, quantity, action);
};
🎯 SKU匹配逻辑分析
当前实现问题
// ❌ 问题:规格值排序可能不一致
const specValues = sortedSpecNames.map(name => selectedSpecs[name]).join('|');
const sku = skus.find(s => s.sku === specValues);
改进建议
// ✅ 更健壮的SKU匹配
const findMatchingSku = (specs: Record<string, string>) => {
return skus.find(sku => {
if (!sku.sku) return false;
const skuSpecs = sku.sku.split('|');
const selectedValues = Object.values(specs);
// 检查是否所有选中的规格值都在SKU中
return selectedValues.every(value => skuSpecs.includes(value)) &&
selectedValues.length === skuSpecs.length;
});
};
🚀 性能优化建议
1. 规格数据预处理
// 在组件外部或useMemo中预处理
const processedSpecs = useMemo(() => {
// 预处理规格数据,建立索引
const specIndex = new Map();
specs.forEach(spec => {
// 建立规格名称到值的映射
});
return specIndex;
}, [specs]);
2. SKU查找优化
// 使用Map提高查找效率
const skuMap = useMemo(() => {
const map = new Map();
skus.forEach(sku => {
if (sku.sku) {
map.set(sku.sku, sku);
}
});
return map;
}, [skus]);
🧪 测试场景
1. 基础功能测试
- 规格选项正确显示
- 规格选择状态更新
- SKU匹配正确
- 价格库存动态更新
2. 边界情况测试
- 无库存规格处理
- 单规格商品处理
- 数据为空的处理
- 网络错误处理
3. 用户体验测试
- 选择流程顺畅
- 错误提示清晰
- 加载状态友好
- 响应速度快
📈 改进优先级
🔥 高优先级(必须修复)
- 实现规格选择逻辑 - 核心功能
- 修复UI与数据绑定 - 基础可用性
- 添加数量选择器 - 基本需求
- 完善确认验证 - 防止错误
🔶 中优先级(重要改进)
- 规格可用性检查 - 用户体验
- 错误处理优化 - 稳定性
- 性能优化 - 响应速度
🔵 低优先级(锦上添花)
- 动画效果 - 视觉体验
- 高级功能 - 额外特性
🎉 总结
SpecSelector组件目前存在严重的功能缺失:
- ❌ 核心功能未实现:规格选择逻辑被注释
- ❌ UI与数据脱节:显示硬编码选项
- ❌ 用户体验差:无法正常使用
- ❌ 缺少验证:错误处理不足
建议立即进行重构,实现完整的规格选择功能,确保组件可用性和用户体验。