forked from gxwebsoft/mp-10550
docs: 更新优惠券相关文档- 新增优惠券API集成文档
- 新增优惠券卡片对齐修复文档 - 新增优惠券状态显示调试文档 - 新增优惠券组件警告修复文档- 更新用ShopInfo Hook字段迁移文档 - 更新Arguments关键字修复文档
This commit is contained in:
319
docs/SPEC_SELECTOR_ANALYSIS.md
Normal file
319
docs/SPEC_SELECTOR_ANALYSIS.md
Normal file
@@ -0,0 +1,319 @@
|
||||
# 🔍 SpecSelector规格选择组件深度分析
|
||||
|
||||
## 📋 组件概述
|
||||
|
||||
`SpecSelector`是商品规格选择组件,用于处理多规格商品的选择逻辑。通过分析代码发现了多个关键问题需要改进。
|
||||
|
||||
## 🚨 **发现的主要问题**
|
||||
|
||||
### 1. **核心功能缺失 - 规格选择逻辑未实现**
|
||||
|
||||
#### 问题描述
|
||||
```typescript
|
||||
// ❌ 关键函数被注释掉了
|
||||
// const handleSpecSelect = (specName: string, specValue: string) => {
|
||||
// setSelectedSpecs(prev => ({
|
||||
// ...prev,
|
||||
// [specName]: specValue
|
||||
// }));
|
||||
// };
|
||||
```
|
||||
|
||||
#### 影响
|
||||
- **无法选择规格**:用户无法点击选择具体的规格值
|
||||
- **SKU匹配失效**:无法根据选择的规格找到对应的SKU
|
||||
- **价格库存不更新**:选择规格后价格和库存不会动态更新
|
||||
|
||||
### 2. **规格可用性检查缺失**
|
||||
|
||||
#### 问题描述
|
||||
```typescript
|
||||
// ❌ 规格可用性检查函数被注释
|
||||
// const isSpecValueAvailable = (specName: string, specValue: string) => {
|
||||
// // 检查规格值是否有库存、是否可选
|
||||
// };
|
||||
```
|
||||
|
||||
#### 影响
|
||||
- **无库存规格仍可选**:用户可能选择无库存的规格组合
|
||||
- **用户体验差**:无法提前知道哪些规格组合不可用
|
||||
- **订单失败风险**:可能生成无效订单
|
||||
|
||||
### 3. **UI与数据逻辑分离**
|
||||
|
||||
#### 问题描述
|
||||
```typescript
|
||||
// ❌ 硬编码的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. **数量选择功能缺失**
|
||||
|
||||
#### 问题描述
|
||||
```typescript
|
||||
// ❌ 没有数量选择器
|
||||
const [quantity, setQuantity] = useState(1); // 状态存在但无UI
|
||||
```
|
||||
|
||||
#### 影响
|
||||
- **无法调整数量**:用户只能购买1个商品
|
||||
- **批量购买不支持**:无法满足批量购买需求
|
||||
|
||||
### 5. **错误处理和验证不足**
|
||||
|
||||
#### 问题描述
|
||||
```typescript
|
||||
// ❌ 确认按钮没有充分验证
|
||||
const handleConfirm = () => {
|
||||
if (!selectedSku) {
|
||||
return; // 静默失败,用户不知道为什么无法确认
|
||||
}
|
||||
onConfirm(selectedSku, quantity, action);
|
||||
};
|
||||
```
|
||||
|
||||
#### 影响
|
||||
- **静默失败**:用户不知道为什么无法确认
|
||||
- **缺少提示**:没有错误提示和引导
|
||||
- **体验差**:用户困惑为什么按钮无响应
|
||||
|
||||
## 📊 **数据流分析**
|
||||
|
||||
### 当前数据流(有问题)
|
||||
```
|
||||
specs数据 → specGroups ❌ UI显示硬编码选项
|
||||
selectedSpecs ❌ 永远为空对象
|
||||
selectedSku ❌ 永远为null
|
||||
确认按钮 ❌ 永远被禁用
|
||||
```
|
||||
|
||||
### 期望数据流(正确)
|
||||
```
|
||||
specs数据 → specGroups → 动态生成UI选项
|
||||
用户选择 → selectedSpecs更新 → SKU匹配
|
||||
SKU匹配 → selectedSku更新 → 价格库存更新
|
||||
用户确认 → 验证通过 → 回调执行
|
||||
```
|
||||
|
||||
## 🔧 **需要实现的核心功能**
|
||||
|
||||
### 1. **动态规格选择器**
|
||||
```typescript
|
||||
// 需要实现
|
||||
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. **规格选择处理**
|
||||
```typescript
|
||||
const handleSpecSelect = (specName: string, specValue: string) => {
|
||||
setSelectedSpecs(prev => ({
|
||||
...prev,
|
||||
[specName]: specValue
|
||||
}));
|
||||
};
|
||||
```
|
||||
|
||||
### 3. **规格可用性检查**
|
||||
```typescript
|
||||
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. **数量选择器**
|
||||
```typescript
|
||||
const renderQuantitySelector = () => (
|
||||
<Cell>
|
||||
<Space direction="vertical">
|
||||
<View className="title">数量</View>
|
||||
<InputNumber
|
||||
value={quantity}
|
||||
min={1}
|
||||
max={selectedSku?.stock || 999}
|
||||
onChange={setQuantity}
|
||||
/>
|
||||
</Space>
|
||||
</Cell>
|
||||
);
|
||||
```
|
||||
|
||||
### 5. **完善的确认逻辑**
|
||||
```typescript
|
||||
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匹配逻辑分析**
|
||||
|
||||
### 当前实现问题
|
||||
```typescript
|
||||
// ❌ 问题:规格值排序可能不一致
|
||||
const specValues = sortedSpecNames.map(name => selectedSpecs[name]).join('|');
|
||||
const sku = skus.find(s => s.sku === specValues);
|
||||
```
|
||||
|
||||
### 改进建议
|
||||
```typescript
|
||||
// ✅ 更健壮的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. **规格数据预处理**
|
||||
```typescript
|
||||
// 在组件外部或useMemo中预处理
|
||||
const processedSpecs = useMemo(() => {
|
||||
// 预处理规格数据,建立索引
|
||||
const specIndex = new Map();
|
||||
specs.forEach(spec => {
|
||||
// 建立规格名称到值的映射
|
||||
});
|
||||
return specIndex;
|
||||
}, [specs]);
|
||||
```
|
||||
|
||||
### 2. **SKU查找优化**
|
||||
```typescript
|
||||
// 使用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. **用户体验测试**
|
||||
- [ ] 选择流程顺畅
|
||||
- [ ] 错误提示清晰
|
||||
- [ ] 加载状态友好
|
||||
- [ ] 响应速度快
|
||||
|
||||
## 📈 **改进优先级**
|
||||
|
||||
### 🔥 **高优先级(必须修复)**
|
||||
1. **实现规格选择逻辑** - 核心功能
|
||||
2. **修复UI与数据绑定** - 基础可用性
|
||||
3. **添加数量选择器** - 基本需求
|
||||
4. **完善确认验证** - 防止错误
|
||||
|
||||
### 🔶 **中优先级(重要改进)**
|
||||
1. **规格可用性检查** - 用户体验
|
||||
2. **错误处理优化** - 稳定性
|
||||
3. **性能优化** - 响应速度
|
||||
|
||||
### 🔵 **低优先级(锦上添花)**
|
||||
1. **动画效果** - 视觉体验
|
||||
2. **高级功能** - 额外特性
|
||||
|
||||
## 🎉 **总结**
|
||||
|
||||
SpecSelector组件目前存在**严重的功能缺失**:
|
||||
|
||||
- ❌ **核心功能未实现**:规格选择逻辑被注释
|
||||
- ❌ **UI与数据脱节**:显示硬编码选项
|
||||
- ❌ **用户体验差**:无法正常使用
|
||||
- ❌ **缺少验证**:错误处理不足
|
||||
|
||||
**建议立即进行重构**,实现完整的规格选择功能,确保组件可用性和用户体验。
|
||||
Reference in New Issue
Block a user