sku选择必须基于已选规格组合实时过滤,不能仅凭单值查库存;每次点击需用selectedvalues生成完整key匹配skus中spec_value_ids且stock>0的项,禁用状态须动态重算、不可缓存,v-for中disabled字段须由computed返回新数组以触发视图更新。

SKU选择必须基于已选规格组合实时过滤,不是单值查库存
点完“红色”后,“XL”还能点,但提交报“无货”,说明你只判断了“XL”是否在任意 SKU 里出现过,没验证“红色+XL”这个组合是否存在且有库存。真正有效的判断逻辑是:把当前已选的所有规格(比如 {颜色: "红色", 尺寸: "XL"})当作一个完整 key,去 skus 数组里找完全匹配的项,且 stock > 0。
- 后端返回的
skus必须含明确的规格映射字段,如spec_value_ids: ["color:red", "size:xl"]或spec_str: "red_xl",不能只给个模糊的sku_code - 前端不要自己拼接字符串去比对,容易因顺序错位导致匹配失败;优先用
spec_value_ids这种结构化数组做.every()全字段校验 - 每次点击都要重算,不能缓存“已禁用列表”,因为用户可能回退修改前面选项,整个可用集就变了
disabled状态必须用 computed 动态生成,不能直接改原始数据
你在 v-for 里渲染规格按钮时,如果直接给原始 specs 数组里的对象加 disabled: true,uni-app 的响应式系统不会触发视图更新——它只监听数组引用或对象属性的新增/删除,不监听已有属性值变更。
- 正确做法是写一个
computed,例如availableSpecs,它返回一个新数组,每个项都带disabled字段 - 计算逻辑:先提取当前已选规格
selectedValues,再遍历所有skus,筛选出满足全部已选条件的子集,最后从该子集中提取当前规格维度的所有合法值,用new Set()去重 - 示例片段:
isSpecDisabled(specName, specValue)内部应调用skus.filter(sku => Object.keys(selectedValues).every(k => sku.spec_value_ids.includes(`${k}:${selectedValues[k]}`)))
清空后续规格要按顺序截断,不能只删 key
用户先选“XL”,再选“绿色”,此时“红色”按钮应该恢复可点——但如果不清掉“XL”之后的已选状态,selectedValues 还带着旧值,后续过滤永远带着干扰项。
- 点击某个规格时,先获取它在
specs中的索引位置,再遍历selectedValues的 key,把索引更大的规格全删掉 - 别用
this.$delete(this.selectedValues, key)逐个删,容易漏;推荐用Object.keys(this.selectedValues).forEach(key => { if (allSpecNames.indexOf(key) > currentIndex) this.$delete(this.selectedValues, key) }) - 注意:
allSpecNames必须和后端返回的specs顺序严格一致,否则索引错位会导致清空失效
真机点击失效大概率是缺 cursor:pointer 或事件穿透
开发时看着能点,真机上点了没反应,90% 是样式或事件问题,和算法无关。
- 给所有规格按钮加
style="cursor: pointer",尤其 H5 端,否则 iOS Safari 默认不触发click - 弹窗层如果用了
@touchmove.stop.prevent,要确认它没拦住下层按钮的tap事件;更稳妥的是在按钮上加@tap.stop - 微信小程序里,如果按钮被
scroll-view或swiper包裹,检查是否设置了enhanced属性,未开启时部分事件会丢失
selectedValues 必须精确反映当前用户路径,skus 必须结构清晰可匹配,computed 必须不缓存中间结果。任何一环带脏数据,整个联动就会断链。










