当前位置: 首页 > news >正文

实现 el-table 中键盘方向键导航功能vue2+vue3(类似 Excel)

实现 el-table 中键盘方向键导航功能vue2+vue3(类似 Excel)

功能需求

在 Element UI 的 el-table 表格中实现以下功能:

  • 使用键盘上下左右键在可编辑的 el-input/el-select 之间移动焦点
  • 焦点移动时自动定位到对应单元格
  • 支持光标位置自动调整,提升编辑体验
完整解决方案(vue2)
1. 表格结构修改

在 el-table 中添加键盘事件监听,并为可编辑元素添加定位标识:

<template><el-table :data="tableData" border style="width: 100%" size="small"><el-table-column prop="account_id" label="结算账户" width="180" align="center"><template slot-scope="scope"><el-select v-model="scope.row.account_id" placeholder="请选择账户" clearable filterable @keydown.native.stop="onKeyDown(scope.$index, 'account_id', $event)" :ref="getInputRef(scope.$index, 'account_id')"><el-option label="a" value="1" /><el-option label="b" value="2" /></el-select></template></el-table-column><el-table-column prop="money" label="结算金额" width="180" align="center"><template slot-scope="scope"><el-input v-model="scope.row.money" clearable @keydown.native.stop="onKeyDown(scope.$index, 'money', $event)" :ref="getInputRef(scope.$index, 'money')"/></template></el-table-column><el-table-column prop="remark" label="备注" align="center"><template slot-scope="scope"><el-input v-model="scope.row.remark" @keydown.native.stop="onKeyDown(scope.$index, 'remark', $event)" :ref="getInputRef(scope.$index, 'remark')"></el-input></template></el-table-column></el-table>
</template>
2. 核心 JavaScript 逻辑

在 Vue 组件的 methods 中添加焦点导航控制方法:

<script>
export default {data() {return {tableData: [{},{},{},{}],columns: [{prop: 'account_id'},{prop: 'money'},{prop: 'remark'}],refList: {}}},methods: {getInputRef(rowIndex, columnProp) {return `input-${rowIndex}-${columnProp}`},onKeyDown(rowIndex, columnProp, event) {// 当前列在columns数组中的索引const columnIndex = this.columns.findIndex((c) => c.prop === columnProp)// 计算下一个输入框的位置,如果是当前行的最后一个输入框则移到下一行的第一个输入框let nextColumnIndex = (columnIndex + 1) % this.columns.length;let nextRowIndex;switch(event.keyCode) {case 38:nextRowIndex = rowIndex - 1;nextColumnIndex = columnIndex;break;case 40:nextRowIndex = rowIndex + 1;nextColumnIndex = columnIndex;break;case 37:nextRowIndex = columnIndex === 0 ? rowIndex - 1 : rowIndexnextColumnIndex = columnIndex === 0 ? this.columns.length - 1 : columnIndex - 1break;case 39:nextRowIndex = columnIndex === this.columns.length - 1 ? rowIndex + 1 : rowIndexbreak;}const nextInputRef = `input-${nextRowIndex}-${this.columns[nextColumnIndex].prop}`const currentInputRef = `input-${rowIndex}-${this.columns[columnIndex].prop}`this.$nextTick(() => {if (this.$refs[nextInputRef]) {this.$refs[nextInputRef].focus()if (this.$refs[currentInputRef]) {this.$refs[currentInputRef].blur()}}})}}
}
</script>
完整解决方案(vue3)
 <template><el-table :data="tableData" border style="width: 100%" size="small"><el-table-column prop="account_id" label="结算账户" width="180" align="center"><template #default="{ row, $index,column }"><el-select v-model="row.account_id" placeholder="请选择账户" clearable filterable @keydown="onKeyDown($index,column.property,$event)" :ref="(el)=>setRef(el,`input-${$index}-${column.property}`)"><el-option label="a" value="1" /><el-option label="b" value="2" /></el-select></template></el-table-column><el-table-column prop="money" label="结算金额" width="180" align="center"><template #default="{ row, $index,column }"><el-input v-model="row.money" clearable @keydown="onKeyDown($index,column.property,$event)" :ref="(el)=>setRef(el,`input-${$index}-${column.property}`)"/></template></el-table-column><el-table-column prop="remark" label="备注" align="center"><template #default="{ row,$index,column }"><el-input v-model="row.remark" @keydown="onKeyDown($index,column.property,$event)" :ref="(el)=>setRef(el,`input-${$index}-${column.property}`)"></el-input></template></el-table-column></el-table>
</template>
<script setup>
import { nextTick, ref } from 'vue'const tableData = [{},{},{},{}
]const columns = [{prop:'account_id'},{prop:'money'},{prop:'remark'}
]
const refList = ref({})
const setRef = (el,key)=>{refList.value[key] = el
}
function onKeyDown(rowIndex,columnProp,event){// 当前列在columns数组中的索引const columnIndex = columns.findIndex((c) => c.prop === columnProp)// 计算下一个输入框的位置,如果是当前行的最后一个输入框则移到下一行的第一个输入框let nextColumnIndex = (columnIndex + 1) % columns.length;let nextRowIndex;switch(event.keyCode){case 38:nextRowIndex = rowIndex - 1 ;nextColumnIndex = columnIndex;break;case 40:nextRowIndex = rowIndex + 1 ;nextColumnIndex = columnIndex;break;case 37:nextRowIndex = columnIndex === 0 ? rowIndex - 1 : rowIndexnextColumnIndex = columnIndex === 0 ? columns.length - 1 : columnIndex - 1break;case 39:nextRowIndex = columnIndex === columns.length - 1 ? rowIndex + 1 : rowIndexbreak;}const nextInputRef = `input-${nextRowIndex}-${columns[nextColumnIndex].prop}`const currentInputRef = `input-${rowIndex}-${columns[columnIndex].prop}`nextTick(() => {if (refList.value[nextInputRef]) {refList.value[nextInputRef].focus()refList.value[currentInputRef].blur()}})}
</script>
http://www.lqws.cn/news/494767.html

相关文章:

  • Flux Reconstruction(FR,通量重构)方法
  • GO 语言学习 之 代码风格
  • Java面试复习指南:并发编程、JVM原理与Spring框架
  • RAG-Anything:打破边界的一体化多模态文档处理引擎
  • Recent Advances in Speech Language Models: A Survey
  • 全局配置Axios后的api使用指南
  • 纯血HarmonyOS5 打造小游戏实践:扫雷(附源文件)
  • 从0开始学习R语言--Day30--函数型分析
  • Unity | AmplifyShaderEditor插件基础(第十集:噪声的种类+火焰制作-中)
  • 如何将进度传给前端呢
  • UI设计 | 审美积累 | 极繁风格(Maximalism / Complex UI)
  • 左神算法之给定一个数组arr,返回其中的数值的差值等于k的子数组有多少个
  • leetcode题解77:组合(回溯算法的门面)
  • STM32 串口通信②:蓝牙模块HC-05控制单片机
  • python常用的正则表达式及作用
  • 编程江湖-正则表达式
  • vue3 el-table row-class-name 行字体颜色失效
  • Spring Cloud微服务
  • MM-AttacKG:一种使用大型语言模型构建攻击图的多模态方法
  • GitLab 17.8 备份秘籍:快速获取纯 Git 仓库与核心配置
  • 兆瓦闪充技术革命:解码新能源汽车补能赛道的技术跃迁与从业机会图谱
  • 60天python训练计划----day56
  • 左神算法之二叉树的个数
  • 数据标注师学习内容
  • Domain 层完全指南(面向 iOS 开发者)
  • 【数据结构初阶】--顺序表(一)
  • FPGA基础 -- Verilog 验证平台
  • 《哈希表》K倍区间(解题报告)
  • 论文略读:ASurvey on Intent-aware Recommender Systems
  • 13-MCP4725-带 EEPROM 存储器的 12 位数模转换器