Element-plus 虚拟表格 绑定单元行的双击事件、单击事件 (Vue3、Nuxt3)

更新:

其实是有提供的,只是官网文档给的描述太隐蔽了,我没看出来是啥意思。。。

element-plus 虚拟化表格如何自定义事件_会做饭的二哈的博客-CSDN博客

 传入一个对象,里面的属性是原生DOM事件,比如onClick等

下面的可以不用看了,是一些DOM操作,因为我不知道官方提供了这些。。。

———————————————————————————————————————————

一、引言

        由于做的内容需要渲染大量数据,用原本的表格渲染会十分卡顿,所以选择使用虚拟化表格,官方文档:Virtualized Table 虚拟化表格 | Element Plus (element-plus.org)

        但是由于这个表格还是beta版,所以提供的事件也非常少,如下:

         我做的项目需要实现表格的单击、双击单元行,来进行操作这一行的数据,但是翻阅文档并没有找到这个事件,那么只能自己实现了。

二、代码

        话不多说,直接上代码 (基于Nuxt3,比普通的Vue3多嵌套了一层client-only组件,删掉就行。除此之外,Nuxt3会自动帮忙引入vue中的函数,所以vue3的小伙伴需要你自己去引入一些组件和函数)

        下面的代码实现了,双击获取当前单元行在数组中的索引,并根据索引获取该单元行的数据

        所用技术:nuxt3 / vue3 、 Element-plus 、tsx / jsx、原生js事件

<template>
    <div class="test">
        <client-only>
            <el-table-v2 :columns="columns" :data="list" :width="1300" :row-class="rowClassName" :height="300"
                @dblclick="dblclick" />
        </client-only>
    </div>
</template>
<script setup lang='tsx'>
import { ElDropdown, ElDropdownMenu, ElDropdownItem, } from 'element-plus'
import type { Column } from 'element-plus'
/**双击事件 */
const dblclick = (e: any) => {
    /**当前点击的元素 */
    const target = e.target
    /**当前点击的元素的父元素,需要不断向上寻找,找到class为 el-table-v2__row 的元素,说明找到真正的"行"了 */
    let row = target.parentNode
    // console.log(row, row.className);
    //不断循环向上查找父元素,直到找到 “行” : el-table-v2__row
    while (1) {
        /**把类名切割,防止多类名影响。 */
        const nowClassArr = row.className?.split(' ') || []
        //如果这个row的类名包含el-table-v2__row,就是我们要找的行了
        if (nowClassArr.indexOf('el-table-v2__row') > -1) break
        //如果发现了其它的的类名,说明点错位置了  (TODO:这里用到了多次indexof,感觉很浪费,可能有优化方法)
        if (nowClassArr.indexOf('el-table-v2__header') > -1 || nowClassArr.indexOf('el-virtual-scrollbar') > -1 || nowClassArr.indexOf('el-table-v2__empty') > -1) {
            console.log('点击的可能是表头、滚动条、空列表,不做操作');
            return
        }
        //下面是没有找到正确元素,会不断向上查找父元素
        const parentNode = row.parentNode //获取父元素
        if (!parentNode) {//如果为空了就返回,避免死循环 
            console.log('没有找到');
            return
        }
        row = parentNode
    }
    // console.log(row); //最终这里拿到的就是行元素了 

    /**父元素的class为_index的子元素 */
    const indexSpan = row.querySelector('._index')
    if (!indexSpan) {//这个为空 说明失败了
        console.log('获取失败,拿到的行为', row);
        return
    }
    /**当前单元行在数组中的序号 */
    const index = parseInt(indexSpan!.innerHTML)
    if (isNaN(index)) {
        console.log('获取失败,根据dom查看原因:', row, indexSpan);
        return
    }
    console.log('拿到当前单元行在列表中的索引值', index);
    console.log('根据索引,拿到当前单元行的数据', list[index]);

}
/**随便设置一个点击事件 */
const handle = (id: number) => {
    console.log('点击了这个按钮,id为', id);
}
/**给某个单元行特殊类名  这里主要的用处是给每个数据加索引值,方便我们后面获取 */
const rowClassName = ({ rowData, rowIndex }: { rowData: any, rowIndex: number }) => {
    // if (rowData?.id == 'xxxx') {//如果你想让某个单元行有特殊样式,就可以这么做。参考: http://element-plus.org/zh-CN/component/table-v2.html#带状态的表格
    //     return `xxxxx`
    // }
    rowData._index = rowIndex //给每一个数据加一个序号,方便我们到时候获取
    return ``
}
/**表头数据 */
const columns: Column<any>[] = reactive([
    {
        key: 'name',
        title: '姓名',
        dataKey: 'name',
        width: 500,
    },
    {
        key: 'age',
        title: '年龄',
        dataKey: 'age',
        width: 500,
    },
    {
        key: 'id',
        title: '',
        dataKey: 'id',
        width: 500,
        cellRenderer: () => <ElDropdown v-slots={
   
   {
            dropdown: ({ cellData: id }: { cellData: number }) => <ElDropdownMenu>
                <ElDropdownItem onClick={() => handle(id)}>
                    操作1
                </ElDropdownItem>
                <ElDropdownItem disabled>待添加</ElDropdownItem>
            </ElDropdownMenu>
        }}>
            <span style={
   
   { cursor: "pointer" }}>...</span>
        </ElDropdown>
    },
    {
        key: '_index',//这里的_index不是list数据中自带的,而是在 rowClassName 函数中顺便加上的
        title: '',
        dataKey: '_index',
        width: 0, //这里选择让索引值不显示,你可以根据你的需要设置
        align: 'center',
        cellRenderer: ({ cellData: _index }) => <span style={
   
   { display: 'none' }} class='_index'>{_index}</span>//这里选择让索引值不显示,你可以根据你的需要设置
    },
])
/**测试数据 */
const list: any[] = [
    {
        name: '测试1',
        age: 18,
        id: 1,
    },
    {
        name: '测试2',
        age: 20,
        id: 2,
    },
    {
        name: '测试3',
        age: 15,
        id: 3,
    },
]
</script>
<style lang='less' scoped>
.test {
    color: black;
    background-color: white;
}
</style>

三、原理

1. 关于 jsx / tsx 可以去了解相关文档,这里用的挺多,也可以看上一篇文章 在Vue3中使用 tsx / jsx + 插槽 (element-plus中的tsx写法与插槽)_l煎饼果子的博客-CSDN博客

2. 想获取点击/双击的单元行的数据,那么我们可以通过获取这个单元行的索引,进而获取数据。那么怎么获得索引呢?我这边是通过 给列表中每一项都添加一个_index 数据,用于存放索引,并且在页面中用dom渲染出索引。然后根据双击/点击事件的事件对象e,获取点击的元素,然后不断向上寻找父元素,直到找到当前的单元行的dom元素。然后获取当前单元行的存放索引的元素,根据innerHTML提取出索引。 (这里为了页面美观,把索引display: none;了,你可以根据需要显示)

四、结语

        本代码有很多缺点,但是也基本实现了需求。如果大家有更好的方法请告诉我,我也很需要

        下面讲一下存在的缺点,首先是多次使用了 indexOf 函数,可能存在性能问题 + 不美观,同时我做的错误判断没有包括所有情况,如果大家在使用过程中发现了bug,需要进行排除

        这种获取dom对象的做法,肯定是不如组件直接对外暴露一个方法的,但是我也没办法了,希望element官方在未来能暴露出表格行的双击/单击事件吧

        tip: 除此之外,还可以在每一个columns单元格的渲染函数中,给元素直接绑定事件。但是缺点就是,有几个表头,就要绑定几次事件,可能还会影响其它元素的事件。

猜你喜欢

转载自blog.csdn.net/m0_64130892/article/details/131643756