Vue.js 多组v-model复选框:跨组已选值自动禁用的实现方案
跨组复选框选中互斥实现方案
核心思路
全程保留Vuex中公共数据源的原始独立性,所有跨组的禁用/过滤逻辑都在组件层计算完成,不会修改原始共享数据,保证三组选择的独立性。
代码修正步骤
1. 逻辑层调整
先移除原有冗余、存在逻辑缺陷的computed代码,补全状态判断逻辑:
import { ref, computed } from 'vue' import { useStore } from 'vuex' const store = useStore() // 从Vuex读取公共DSCP数据源 const dscpData = computed(() => store.state.dscpData) // 三组独立的选中值存储,直接作为v-model绑定目标 const dscpChoiceQ1 = ref([]) const dscpChoiceQ2 = ref([]) const dscpChoiceQ3 = ref([]) // 合并所有组的已选值,生成Set实现O(1)快速查找 const allSelectedValues = computed(() => { return new Set([ ...dscpChoiceQ1.value, ...dscpChoiceQ2.value, ...dscpChoiceQ3.value ]) }) // 通用禁用判断:传入选项值、当前组自己的选中数组 // 规则:值被其他组选中就禁用,当前组自己选的值保留可取消的能力 const isOptionDisabled = (optionValue, currentGroupSelected) => { return allSelectedValues.value.has(optionValue) && !currentGroupSelected.includes(optionValue) } // 正确实现空格分隔的字符串拼接,只读,不需要冗余set方法 const dscpStrQ1 = computed(() => dscpChoiceQ1.value.join(' ')) const dscpStrQ2 = computed(() => dscpChoiceQ2.value.join(' ')) const dscpStrQ3 = computed(() => dscpChoiceQ3.value.join(' '))
原有代码的computed存在明显问题:所有v-model直接绑定原始ref,那三个带set的computed永远不会被触发,属于无效代码;就算硬把v-model绑到computed上,get返回的是拼接后的字符串,而多选checkbox的v-model要求绑定数组类型,会直接导致选中状态判断完全失效。
2. 模板层修正
修复原模板中的id重复、label关联错误问题,补全禁用逻辑:
<div> <h6>DSCP Values in Q1</h6> <div> <ul v-for="option in dscpData" :key="option.id"> <li> <input type="checkbox" <!-- 给每个组的id加后缀,解决全局id重复问题 --> :id="option.id + '-q1'" :value="option.value" v-model="dscpChoiceQ1" <!-- 绑定禁用判断逻辑 --> :disabled="isOptionDisabled(option.value, dscpChoiceQ1)" /> <!-- 修复for属性关联,绑定对应input的id --> <label :for="option.id + '-q1'">{{ option.value }}</label> </li> </ul> </div> <h6>DSCP Values in Q2</h6> <div> <ul v-for="option in dscpData" :key="option.id"> <li> <input type="checkbox" :id="option.id + '-q2'" :value="option.value" v-model="dscpChoiceQ2" :disabled="isOptionDisabled(option.value, dscpChoiceQ2)" /> <label :for="option.id + '-q2'">{{ option.value }}</label> </li> </ul> </div> <h6>DSCP Values in Q3</h6> <div> <ul v-for="option in dscpData" :key="option.id"> <li> <input type="checkbox" :id="option.id + '-q3'" :value="option.value" v-model="dscpChoiceQ3" :disabled="isOptionDisabled(option.value, dscpChoiceQ3)" /> <label :for="option.id + '-q3'">{{ option.value }}</label> </li> </ul> </div> </div>
可选:实现直接移除选项的效果
如果不需要保留禁用态的选项展示,要直接从列表中移除被其他组选中的值,只要给每个组单独做过滤后的列表即可,不需要修改原始公共数据源:
// Q1组过滤列表:排除Q2、Q3已经选中的值 const q1FilteredOptions = computed(() => { const selectedFromOtherGroups = new Set([...dscpChoiceQ2.value, ...dscpChoiceQ3.value]) return dscpData.value.filter(option => !selectedFromOtherGroups.has(option.value)) }) // Q2、Q3的过滤列表逻辑同理,分别排除另外两个组的已选值
之后把模板里v-for的遍历源从dscpData换成对应过滤后的列表就行。
容易卡调试的坑
- 原代码所有checkbox直接拿
option.id当全局id,三个组放同一个id的元素,违反HTML全局id唯一的规范,最后会出现点Q3的label选中Q1的checkbox、选中状态错乱的问题,必须给每个组的input id加专属后缀 - 原代码label的for属性是写死的字符串
"id",根本没绑定实际的input id,点label根本触发不了选中,别浪费时间在样式调整上 - 别直接改Vuex里的公共数据源做过滤,一改就是三个组同步变,完全失去三组独立选择的意义,所有过滤都在组件层计算就行
内容的提问来源于stack exchange,提问作者fencepencil
相关产品推荐
相关产品推荐

