element-ui表格组件el-table二次简单封装,基本满足后台管理项目使用

背景

在日常中后台管理系统开发中,表格是使用频率最高的组件之一,当系统或者里面的页面很多时,封装一个表格组件很有必要。本文基于element-ui的table组件进行二次封装,让组件进一步解耦,达到精简代码,提高可读性。如果要在项目中使用,建议直接使用文末测试过的的优化版本。

封装目标

  • api尽量和el-table一致,减少学习使用成本
  • 传入一个表头的配置项,通过该配置项动态生成el-table-columns标签
  • 支持自定义复杂表头

具体步骤

创建名为table的父组件,名为BaseTable的子组件。

在父组件中定义表头的配置项数据columnsattrs是表头属性,slot代表这是个复杂的自定义表头,id是作为循环的唯一key使用,如果直接用下表index,可能存在性能问题。代码如下:

<template>
    <!-- 除了需要传columns,其它api与el-table完全一致 -->
    <BaseTable v-loading="loading" :columns="columns" :data="list"></BaseTable>
</template>

<script>
import BaseTable from '@pages/common/BaseTable'
export default {
    name: 'TabelDemo',
    components: {
        BaseTable
    },
    data() {
        return {
            loading: true,
            list: [],
            columns: Object.freeze([
                {
                    attrs: {
                        prop: 'date',
                        label: '日期',
                        width: '150',
                        align: 'center'
                    },
                    id: 1
                },
                {
                    attrs: {
                        prop: 'author',
                        label: '作者',
                        width: '110',
                        'show-overflow-tooltip': true
                    },
                    id: 2
                },
                {
                    attrs: {
                        prop: 'des',
                        label: '简要描述',
                        'show-overflow-tooltip': true
                    },
                    id: 3
                },
                {
                    slot: 'handle',
                    attrs: {
                        label: '操作',
                        width: '230',
                        'class-name': 'small-padding fixed-width',
                        align: 'center'
                    },
                    id: 4
                }
            ])
        }
    }
}
</script>

子组件中,在el-table组件上使用 a t t r s 和 attrs和 attrslisteners,以$attrs为例,可以接收到父组件除了props已经接收的属性的其它所有属性,不包括class和style。官方说明如下:

包含了父作用域中不作为 prop 被识别 (且获取) 的 attribute 绑定 (classstyle 除外)。当一个组件没有声明任何 prop 时,这里会包含所有父作用域的绑定 (classstyle 除外),并且可以通过 v-bind="$attrs" 传入内部组件——在创建高级别的组件时非常有用。

官方的说明一看会很懵逼,其实简单理解就是你在父组件上绑定的属性(除了classstyle,以及在子组件中用props已经接收的属性),使用 a t t r s 都 可 以 拿 到 。 然 后 在 当 前 子 组 件 上 用 ‘ v − b i n d = " attrs都可以拿到。然后在当前子组件上用 `v-bind=" attrsvbind="attrs"`会把所有属性原样继承过来绑定上。

代码如下:

<template>
    <!-- 通过v-bind="$attrs" v-on="$listeners",把父组件传的属性全部绑定到子组件上,保证了api和el-table一致 -->
    <el-table style="width: 100%" v-bind="$attrs" v-on="$listeners">
        
		<!-- 这里写coluumn-->
        
    </el-table>
</template>

<script>
export default {
    name: 'BaseTable',
    props: {
        columns: {
            type: Array,
            default: () => []
        }
    }
}
</script>

然后处理表头的渲染,columns中有slot属性的为复杂表头,同时为了在父组件中自定义内容,这里使用了具名插槽,同时绑定一个自定义的scope属性,把子组件里拿到的scope值传给父组件。

代码如下:

<el-table style="width: 100%" v-bind="$attrs" v-on="$listeners">
        <template v-for="item in columns">
            <el-table-column v-if="item.slot" :key="item.id" v-bind="item.attrs">
                <template slot-scope="scope">
                    <slot :scope="scope" :name="item.slot"></slot>
                </template>
            </el-table-column>
            <el-table-column v-else :key="item.id" v-bind="item.attrs"></el-table-column>
        </template>
</el-table>

然后完善父组件数据进行测试

<template>
    <!-- 除了需要传columns,其它api与el-table完全一致 -->
    <BaseTable v-loading="loading" :columns="columns" :data="list" empty-text="哈哈哈,我就看看没数据会怎样~">
        <template v-slot:handle="slot">
            <el-button type="primary" size="mini" @click="handleUpdate(slot.scope.row, slot.scope.$index)">
                修改
            </el-button>
            <el-button type="danger" size="mini" @click="handleDelete()">
                清空
            </el-button>
        </template>
    </BaseTable>
</template>

<script>
import BaseTable from '@pages/common/BaseTable'
export default {
    name: 'TabelDemo',
    components: {
        BaseTable
    },
    data() {
        return {
            loading: true,
            list: [],
            columns: Object.freeze([
                {
                    attrs: {
                        prop: 'date',
                        label: '日期',
                        width: '150',
                        align: 'center'
                    },
                    id: 1
                },
                {
                    attrs: {
                        prop: 'author',
                        label: '作者',
                        width: '110',
                        'show-overflow-tooltip': true
                    },
                    id: 2
                },
                {
                    attrs: {
                        prop: 'des',
                        label: '简要描述',
                        'show-overflow-tooltip': true
                    },
                    id: 3
                },
                {
                    slot: 'handle',
                    attrs: {
                        label: '操作',
                        width: '230',
                        'class-name': 'small-padding fixed-width',
                        align: 'center'
                    },
                    id: 4
                }
            ])
        }
    },
    created() {
        setTimeout(() => {
            this.list = [
                {
                    date: '2020-10-13',
                    author: '南巢',
                    des: '我是南方来的燕啊,为何也会迷恋北方的寒。'
                },
                {
                    date: '2019-05-14',
                    author: '测试超出文本显示是否正常测试超出文本显示是否正常测试超出文本显示是否正常测试超出文本显示是否正常',
                    des: '我是南方来的燕啊,为何也会迷恋北方的寒。'
                },
                {
                    date: '2019-02-14',
                    author: '自卑感',
                    des: '低头瞥见自己的影子在前疯狂的跑着躲的离你不远沉默走的路不知几个光年我还原地打转连微笑也腼腆一事无成是最好描述要怎么往前'
                }
            ]
            this.loading = false
        }, 1000)
    },
    methods: {
        handleUpdate(row, index) {
            console.log(row, index)
        },
        handleDelete() {
            this.list = []
        }
    },
}
</script>

以为到这里就结束了吗,作为一个有追求的搬砖者,优化是必不可少的一节,直接贴优化后的代码:

父组件

<template>
    <!-- 除了需要传columns,其它api与el-table完全一致 -->
    <BaseTable v-loading="loading" :columns="columns" :data="list" empty-text="哈哈哈,我就看看没数据会怎样~">
        
        <!-- slot就是接收到的子组件插槽里面的绑定的属性,可以任意命名,里面包含多条属性 -->
        <!-- <template v-slot:handle="slot">
            <el-button type="primary" size="mini" @click="handleUpdate(slot.scope.row, slot.scope.$index)">
                修改
            </el-button>
            <el-button type="danger" size="mini" @click="handleDelete()">
                清空
            </el-button>
        </template> -->

        <!-- 下面是上面的简写,#是v-slot的简写,{scope: {row, $index}}是属性对象slot双重解构,注意这里的scope要与子组件插槽绑定的属性名对应 -->
        <template #handle="{scope: {row, $index}}">
            <el-button type="primary" size="mini" @click="handleUpdate(row, $index)">
                修改
            </el-button>
            <el-button type="danger" size="mini" @click="handleDelete()">
                清空
            </el-button>
        </template>
    </BaseTable>
</template>

<script>
import BaseTable from '@pages/common/BaseTable'  // 根据实际子组件路径引入
export default {
    name: 'TabelDemo',
    components: {
        BaseTable
    },
    data() {
        return {
            loading: true,
            list: [],
            columns: Object.freeze([
                {
                    attrs: {
                        prop: 'date',
                        label: '日期',
                        width: '150',
                        align: 'center'
                    },
                    id: 1
                },
                {
                    attrs: {
                        prop: 'author',
                        label: '作者',
                        width: '110',
                        'show-overflow-tooltip': true
                    },
                    id: 2
                },
                {
                    attrs: {
                        prop: 'des',
                        label: '简要描述',
                        'show-overflow-tooltip': true
                    },
                    id: 3
                },
                {
                    slot: 'handle',
                    attrs: {
                        label: '操作',
                        width: '230',
                        'class-name': 'small-padding fixed-width',
                        align: 'center'
                    },
                    id: 4
                }
            ])
        }
    },
    created() {
        setTimeout(() => {
            this.list = [
                {
                    date: '2020-10-13',
                    author: '南巢',
                    des: '我是南方来的燕啊,为何也会迷恋北方的寒。'
                },
                {
                    date: '2019-05-14',
                    author: '测试超出文本显示是否正常测试超出文本显示是否正常测试超出文本显示是否正常测试超出文本显示是否正常',
                    des: '我是南方来的燕啊,为何也会迷恋北方的寒。'
                },
                {
                    date: '2019-02-14',
                    author: '自卑感',
                    des: '低头瞥见自己的影子在前疯狂的跑着躲的离你不远沉默走的路不知几个光年我还原地打转连微笑也腼腆一事无成是最好描述要怎么往前'
                }
            ]
            this.loading = false
        }, 1000)
    },
    methods: {
        handleUpdate(row, index) {
            console.log(row, index)
        },
        handleDelete() {
            this.list = []
        }
    },
}
</script>

子组件

<template>
    <!-- 通过v-bind="$attrs" v-on="$listeners",把父组件传的属性全部绑定到子组件上,保证了api和el-table一致 -->
    <el-table style="width: 100%" v-bind="$attrs" v-on="$listeners">
        
        <!-- 考虑到v-for和v-if同时使用存在性能问题,这里直接使用computed把需要循环的数据过滤一遍,去除v-if -->
        <!-- <template v-for="item in columns">
            <el-table-column v-if="item.slot" :key="item.id" v-bind="item.attrs">
                <template slot-scope="scope">
                    <slot :scope="scope" :name="item.slot"></slot>
                </template>
            </el-table-column>
            <el-table-column v-else :key="item.id" v-bind="item.attrs"></el-table-column>
        </template> -->

        <el-table-column v-for="item in normalColumns" :key="item.id" v-bind="item.attrs"></el-table-column>
        <el-table-column v-for="item in slotColumns" :key="item.id" v-bind="item.attrs">

            <!-- vue2.6及以上版本,具名插槽和作用域插槽引入了一个新的统一的语法 (即 v-slot 指令)。它取代了 slot 和 slot-scope。
                但考虑到element-ui官方文档依然使用的是slot-scope,这里不做更改。-->            
            <template slot-scope="scope">

                <!-- :scope是绑定的动态属性,可以起任意喜欢的名字,但要注意在父组件中获取数据时key要对应 -->
                <slot :scope="scope" :name="item.slot"></slot>
            </template>
        </el-table-column>
    </el-table>
</template>

<script>
export default {
    name: 'BaseTable',
    props: {
        columns: {
            type: Array,
            default: () => []
        }
    },
    computed: {
        // 获取普通的columns数据
        normalColumns() {
            return this.columns.filter(item => !item.slot) 
        },
        // 获取是插槽的columns数据
        slotColumns() {
            return this.columns.filter(item => item.slot) 
        }
    },
}
</script>

写在最后,按照最后优化的版本会存在一个问题,normalColumns数据肯定会在前面,slotColumns在后面,在项目使用时还是改为注释中的v-for嵌套v-if循环方式。

感谢各位老爷能看到这里,有什么不清楚或者写的不对的,欢迎留言指正~

Vue基于Element UI Table二次封装可以通过创建一个自定义的组件来实现。以下是一个简单的示例,演示了如何封装一个基于Element UI Table组件: ```vue <template> <el-table :data="tableData" :row-key="rowKey" :height="height"> <!-- 渲染表头 --> <el-table-column v-for="column in columns" :key="column.prop" :prop="column.prop" :label="column.label"> <!-- 自定义插槽 --> <template slot-scope="scope"> <slot :column="column" :scope="scope"></slot> </template> </el-table-column> </el-table> </template> <script> export default { name: 'CustomTable', props: { tableData: { type: Array, required: true }, columns: { type: Array, required: true }, rowKey: { type: String, required: true }, height: { type: String, default: 'auto' } } } </script> ``` 在这个示例中,我们创建了一个名为`CustomTable`的组件。它接受`tableData`、`columns`、`rowKey`和`height`作为props,分别表示表格数据、表格列配置、行数据的唯一标识以及表格的高度。 在模板中,我们使用`el-table`和`el-table-column`来渲染Element UI表格。我们使用了`v-for`指令来循环渲染表格列,并通过`slot-scope`来传递数据给插槽。插槽可以在父组件中定义,并在插槽中使用自定义的组件来渲染表格单元格内容。 通过这种方式,我们可以在父组件使用这个封装的自定义表格组件,并通过插槽来定制表格的内容和样式。 希望这个简单的示例能帮助到你进行Vue基于Element UI Table二次封装。如果有任何问题,请随时提问。
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值