Vue.js实现列表每行3个展示、点击选中编辑功能的问题排查
问题根因
你将原始一维items数组切割为按行分组的二维rowArray后,内层循环使用的i是单一行内的局部索引,取值范围固定为0、1、2,所有行的索引都会重复:
- 判断激活状态时
activeIndex === i会匹配所有行相同位置的元素 - 用这个局部索引直接取原始一维
items数组,永远只能取到前3个第一行的元素,自然只能编辑第一行内容
修复方案
计算每个条目对应的原始数组全局索引,用全局索引做激活判断和取值:
- 点击条目的时候传递
行索引*每行条目数 + 行内索引作为全局索引 - 激活状态判断、编辑取值都用这个全局索引即可
修改后的核心代码
1. 模板部分(item-list-template)
<script type="text/x-template" id="item-list-template"> <div> <input type="text" placeholder="Edit selected items" :value="activeItemValue" @input="setActiveItemValue" /> <div class="items-col"> <ul class="items-list" v-for="(row, rowIndex) in rowArray" :key="rowIndex"> <!-- 计算全局索引:rowIndex * 3 + i,传递给点击事件、用于激活判断 --> <item v-for="(item, i) in row" :key="`${rowIndex}-${i}`" :title="item.title" :isActive="activeIndex === (rowIndex * 3 + i)" @click.native="onItemClick(rowIndex * 3 + i)" /> </ul> </div> </div> </script>
2. 逻辑部分无需修改,原有的onItemClick、setActiveItemValue都可以正常复用,因为现在activeIndex存储的已经是原始items数组的全局索引。
如果后续需要调整每行展示的条目数,可以把固定的3抽为公共变量,比如在data里定义itemsPerRow: 3,模板里替换为rowIndex * itemsPerRow + i即可,不用修改多处代码。
完整可运行代码
Vue.component('item-list', { template: '#item-list-template', data() { return { items: [ {title: 'item 1'}, {title: 'item 2'}, {title: 'item 3'}, {title: 'item 4'}, {title: 'item 5'}, {title: 'item 6'} ], activeIndex: -1, rowArray: [], itemsPerRow: 3 // 抽成变量方便后续修改 } }, mounted(){ this.fakeFetchData(); }, methods: { fakeFetchData(){ var cloned = this.items.slice(); while (cloned.length > 0) { let chunk = cloned.splice(0,this.itemsPerRow); this.rowArray.push(chunk); } }, onItemClick(index) { this.activeIndex = this.activeIndex === index ? -1 : index; }, setActiveItemValue(event) { const foundItem = this.items[this.activeIndex]; if (!foundItem) return; return this.items[this.activeIndex].title = event.currentTarget.value; } }, computed: { activeItemValue() { return this.items[this.activeIndex]?.title ?? ''; } } }); Vue.component('item', { template: '#item-template', props: { isActive: Boolean, title: String } }); new Vue({ el: '#app' });
li.active { background-color: yellow; } .items-list { display: flex; }
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script> <div id="app"> <item-list></item-list> </div> <script type="text/x-template" id="item-list-template"> <div> <input type="text" placeholder="Edit selected items" :value="activeItemValue" @input="setActiveItemValue" /> <div class="items-col"> <ul class="items-list" v-for="(row, rowIndex) in rowArray" :key="rowIndex"> <item v-for="(item, i) in row" :key="`${rowIndex}-${i}`" :title="item.title" :isActive="activeIndex === (rowIndex * itemsPerRow + i)" @click.native="onItemClick(rowIndex * itemsPerRow + i)" /> </ul> </div> </div> </script> <script type="text/x-template" id="item-template"> <li class="item" :class="{ active: isActive }">{{ title }}</li> </script>
内容的提问来源于stack exchange,提问作者Ioann Sulyma
相关产品推荐
相关产品推荐

