结论:京东级sku规格选择器的核心是实时过滤——每次点击后必须用已选规格组合动态筛选skus数组并提取可用值,否则ui再炫也会导致无效组合;需预处理skus建立规格索引、用computed生成禁用状态、合并更新selectedvalues并清空后续规格,同时注意真机兼容性问题。

直接说结论:京东级 SKU 规格选择器在 uni-app 里,核心不是“做多炫的 UI”,而是“每次点击后,用已选规格组合去实时过滤 skus 数组,再提取其他维度的可用值”——漏掉这个逻辑,UI 再像也没用,用户一定点出无效组合。
为什么点完颜色后尺寸全灰了?
常见错误是把 specs 和 skus 当成两个独立列表硬渲染。比如后端返回颜色有 [红, 白, 黑]、尺寸有 [S, M, L],但实际有效 SKU 只有 {spec_str: "红_S", stock: 10} 和 {spec_str: "白_M", stock: 5}。如果没做动态过滤,用户点“红”之后,“M”“L”依然显示可点,但根本不存在“红_M”这个组合。
必须把 skus 预处理成可快速匹配的结构:
- 用
spec_str拆解出键值对,如"红_S"→{ 颜色: "红", 尺寸: "S" } - 按规格名建立索引:
colorMap.set("红", ["S"])、sizeMap.set("S", ["红"]) - 每次点击都调用
getAvailableValues("尺寸"),内部用selectedValues(如{ 颜色: "红" })去查colorMap.get("红"),返回["S"]
如何让 v-for 渲染的规格按钮正确禁用?
不能直接给原始 specs 数组里的对象加 disabled 字段然后 v-for 绑定——uni-app 的响应式系统不会自动追踪这种深层属性变更。
必须用 computed 生成新数组:
- 每个规格项渲染时,调用
isSpecDisabled(specName, specValue) - 该函数遍历
skus,检查是否存在满足当前已选 + 待判断值的完整组合 - 返回布尔值,绑定到
:disabled上 - 示例:
<view v-for="v in colors" :key="v" :class="{ disabled: isSpecDisabled('颜色', v) }">{{v}}</view>
用户改选前面的规格,后面选项为什么不更新?
典型症状:先点“XL”,再点“绿色”,此时“红色”按钮还灰着——其实“红色_XL”是有库存的,只是状态没重算。
问题出在状态更新顺序和依赖关系上:
- 不要分两步更新:
this.selectedValues.color = "绿色"然后this.$nextTick(() => this.updateDisabled())——updateDisabled拿到的是旧快照 - 必须合并更新:
this.$set(this, 'selectedValues', { ...this.selectedValues, color: "绿色" }),再立刻重新计算disabled映射表 - 清空后续规格:点“颜色”后,要删掉所有排在它后面的规格键,比如 specs 顺序是 [颜色, 尺寸, 内存],点完“颜色”就得
this.$delete(this.selectedValues, "尺寸"); this.$delete(this.selectedValues, "内存")
真机上点不动、禁用失效,大概率是这几个坑
不是逻辑问题,是平台兼容性细节:
-
cursor: pointer缺失:安卓部分机型不识别view的点击态,必须显式加style="cursor: pointer" -
Array.find报错:低端安卓或旧版微信不支持,换成for (let i = 0; i -
spec_str拼接顺序错位:后端返回的spec_str: "S_红",但前端 specs 定义顺序是 [颜色, 尺寸],导致解析成{ 颜色: "S", 尺寸: "红" }——必须前后端约定严格顺序 - 没有深拷贝
selectedValues:对象引用被复用,多次点击污染状态,用JSON.parse(JSON.stringify(this.selectedValues))做隔离
最易被忽略的一点:禁用逻辑不能缓存中间结果。哪怕 SKU 只有 20 条,也要每次点击都重跑过滤 —— 因为用户随时可能退回去改第一个选项,整个可用集合就变了。










