全选、半选、未选状态由购物车商品选中数量与总数关系自动推导:全选为全部select为true;半选为至少一个选中且非全选;未选为无商品选中;Pinia getters实现响应式派生逻辑。

全选与半选状态的联动计算,本质是基于购物车商品列表的 选中数量 与 总商品数 的关系判断,Pinia 的 Getters 正适合做这种响应式、缓存化的派生逻辑——不修改 state,只声明“当前应显示什么状态”。
明确三种状态的判定逻辑
在购物车场景中,“全选”“半选”“未选”不是互斥开关,而是由数据自动推导出的 UI 状态:
-
全选:所有商品的
select === true - 半选(即 indeterminate):至少有一个选中,且并非全部选中
- 未选:没有任何商品被选中
注意:HTML 原生 checkbox 的 indeterminate 属性需通过 JS 手动设置,但它的触发条件(是否显示横线)完全由上述逻辑决定。
用 Getters 实现状态派生
在 Pinia Store 中定义如下 getters,它们会随 goods 数组或其 select 字段变化而自动更新:
selectedCount: (state) => state.goods.filter(g => g.select).lengthtotalGoods: (state) => state.goods.lengthisAllSelected: (state) => state.goods.length > 0 && state.selectedCount === state.totalGoodsisIndeterminate: (state) => state.selectedCount > 0 && state.selectedCount
其中 isIndeterminate 就是“半选”状态的布尔值,组件可直接用于控制 checkbox 的 indeterminate 属性。
在组件中正确使用 indeterminate
Vue 模板中不能直接绑定 :indeterminate 到响应式 ref,必须用 ref + watch 或生命周期钩子手动设置 DOM 属性:
- 给全选 checkbox 加
ref="allSelectRef" - 用
storeToRefs解构isIndeterminate和isAllSelected - 监听这两个值变化,执行:
allSelectRef.value.indeterminate = isIndeterminate.value - 同时设置
checked绑定为isAllSelected(注意:indeterminate 为 true 时,checked 值会被忽略)
避免常见陷阱
这类联动容易出错的地方在于响应式依赖未被正确捕获:
- 不要在 getter 内部调用非响应式方法(如
JSON.parse()处理原始数组),否则缓存失效或无法追踪变化 - 确保
goods数组本身是响应式的(用ref([])或reactive([])初始化),且每个商品对象的select是响应式属性(例如用ref(false)或直接定义在 reactive 对象里) - 不要把判断逻辑写在模板里(如
v-if="goods.filter(...).length === goods.length"),既重复计算又无法复用


















