Vue动态Select重复选值异常:选中项重置为默认值问题
问题分析与解决方案
问题现象
- 动态新增Select组件后,选择已存在于
selects数组中的选项时,选中项会被重置为默认选项{ id: '0', name: 'Please Select an action', value: '0' } - 无法在
selects数组中添加多个相同的选项
核心原因
- select项标识冲突:直接用选项的
id作为select项的id,当多个select选同一个选项时,所有这些select项的id完全相同,导致查找索引时定位错误,更新操作会影响到第一个匹配的项。 - v-for使用index作为key:数组新增/删除时Vue会复用DOM节点,导致组件状态与数据不匹配,出现选中状态混乱。
- option被错误设置为disabled:代码中所有
<option>都添加了disabled属性,这会阻止用户正常选择选项(属于代码笔误)。 - changeOption逻辑错误:依赖旧的选项id查找select项,存在重复选项id时无法定位到正确的目标项。
解决方案
1. 给每个select项添加唯一标识
修改selects数组结构,每个项包含独立的唯一标识(自增id或uuid),与选项id分离:
uuid:select项的唯一身份标识optionId:当前选中的选项idname:选项名称value:选项值
2. 替换v-for的key为唯一标识
将:key="index"改为:key="select.uuid",避免DOM复用导致的状态混乱。
3. 修正v-model与changeOption逻辑
- v-model绑定到
select.optionId,而非覆盖select项的唯一标识 - changeOption方法通过select项的
uuid定位目标,确保更新准确
4. 移除option的disabled属性
恢复选项的可选状态。
修改后的完整代码
模板部分
<div class="col-span-6 grid md:grid-cols-1 md:grid-cols-6 gap-2 mt-5 px-5 py-2 bg-gray-50 shadow rounded"> <div class="mt-2 col-span-6 md:col-span-3" v-for="(select, index) in selects" :key="select.uuid"> <label for="actions" class="block text-sm font-medium text-gray-700 mb-2"> Actions: <span v-if="index > 0">(Optional)</span> </label> <div class="flex justify-center mt-1 gap-2"> <select v-model="select.optionId" @change="changeOption(select.uuid, select.optionId)" name="actions[]" class="appearance-none rounded-md relative block w-full px-3 py-2 border border-gray-300 placeholder-gray-500 text-gray-900 focus:outline-none focus:ring-indigo-500 focus:border-indigo-500 focus:z-10 sm:text-sm" placeholder="Task Action" > <option v-for="option in options" :key="option.id" :value="option.id">{{ option.name }}</option> </select> <button @click.prevent="remove(index)" v-if="index > 0" class="bg-red-50 text-red-600 px-2 py-2 rounded"> <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-5 h-5"> <path stroke-linecap="round" stroke-linejoin="round" d="M6 18 18 6M6 6l12 12" /> </svg> </button> </div> </div> <div class="col-span-6"> <button @click.prevent="addselect" class="bg-green-600 text-white px-2 py-2 mt-3 rounded">Add Actions</button> </div> </div>
脚本部分
import axios from 'axios'; export default { data() { return { selectedActions: [], options: [ { id: '0', name: 'Please Select an action', value: '0' }, { id: '1', name: 'Contact Info > Provide Email Address', value: '0.05' }, { id: '2', name: 'Contact Info > Provide Phone Number', value: '0.05' }, // 其余选项保持不变 ], selects: [{ uuid: 1, optionId: '0', name: 'Please Select an action', value: '0' }], nextUuid: 2 } }, created() { if (this.$route.params.id) { axios.get(`/api/advertiser/task/${this.$route.params.id}`).then(res => { const actionsData = JSON.parse(res.data.actions); this.selects = actionsData.map((action, index) => ({ uuid: index + 1, optionId: action.id, name: action.name, value: action.value })); this.nextUuid = this.selects.length + 1; }) } }, methods: { addselect() { const defaultOption = this.options.find(option => option.id === '0'); if (defaultOption) { this.selects.push({ uuid: this.nextUuid++, optionId: '0', name: defaultOption.name, value: defaultOption.value }); } }, changeOption(uuid, newOptionId) { const selectIndex = this.selects.findIndex(select => select.uuid === uuid); if (selectIndex === -1) return; const selectedOption = this.options.find(option => option.id === newOptionId); if (!selectedOption) return; this.selects[selectIndex] = { ...this.selects[selectIndex], name: selectedOption.name, value: selectedOption.value, optionId: newOptionId }; const actionIndex = this.selectedActions.findIndex(action => action.uuid === uuid); if (actionIndex !== -1) { this.selectedActions[actionIndex] = this.selects[selectIndex]; } else { this.selectedActions.push(this.selects[selectIndex]); } }, remove(index) { const removedUuid = this.selects[index].uuid; this.selects.splice(index, 1); const actionIndex = this.selectedActions.findIndex(action => action.uuid === removedUuid); if (actionIndex !== -1) { this.selectedActions.splice(actionIndex, 1); } }, edittask() { const formData = new FormData(); const formattedSelects = this.selects.map(select => ({ id: select.optionId, name: select.name, value: select.value })); formData.append('actions', JSON.stringify(formattedSelects)); const config = { headers: { 'content-type': 'multipart/form-data' } }; axios.post(`/api/advertiser/update/task/${this.$route.params.id}`, formData, config) .then(res => console.log(res.data)) .catch(error => console.log(error)); }, onFileChange(e) { const file = e.target.files[0]; if (file) { const reader = new FileReader(); reader.onloadend = () => { this.file = file; this.preview = reader.result; }; reader.readAsDataURL(file); } } }, computed: { totalprice() { return this.selects.reduce((sum, select) => { const selectedOption = this.options.find(option => option.id === select.optionId); return sum + (selectedOption ? parseFloat(selectedOption.value) : 0); }, 0).toFixed(2); } } }
关键修改说明
- 唯一标识uuid:确保每个select项独立,避免重复选项id导致的定位错误
- 分离选项id与select标识:v-model绑定
optionId,保留select项的唯一身份 - 移除disabled属性:恢复选项的可选状态
- 精准定位更新:通过uuid查找目标select项,更新操作不会影响其他项
- 同步数据一致性:删除select项时同步移除
selectedActions中的对应项
内容的提问来源于stack exchange,提问作者DJ Emil
相关产品推荐
相关产品推荐

