平时开发过程中,一般都会对element这些组件库中的表格进行二次封装以方便使用。为了可以通过传入配置项之间展示多级表头的表格才有了这篇文章。
开发环境:vue3+element-plus(vue2也适用)
问题:表头和表格内容区域都可以进行自定义,也就是会使用插槽。多层级的插槽还会涉及到透传,而且表格列组件需要进行递归,所以这里主要解决了递归+插槽嵌套的问题
2021.9.10更改
问题:之前获取slots是通过getInstance中的ctx获取的,但是ctx开发与生产环境获取的值不一致,详情可查看https://github.com/vuejs/vue-next/issues/2743
解决方法:通过getInstance的proxy获取即可,和之前的ctx使用类似const { proxy } = getInstance()
简单介绍
表格具有的几个主要功能:表头自定义、表格内容区域自定义、多级表头(效果图如下)
为了使代码看起来简洁,关于分页、单双选、合计等功能及居中、排序、格式化等属性都不会在这里面展示。
表格组件接收的配置项为一整个对象compTableOpts,这个对象中包含一些与上述几个主要功能无关的项我就不累述了,主要有用的就是columns属性,这个属性传入的是所有列的有关配置,具体的属性和说明如下。
属性 | 类型 | 说明 |
---|---|---|
label | string | 表头显示的文字 |
prop | string | 数据对应的字段名(如果为嵌套表头则不需要传此属性) |
headerSlot | string | 当前表头如果为自定义内容,此属性值为当前自定义内容的插槽名 |
slotFlag | boolean | 当前内容区域是否为自定义内容,为true时则prop作为当前自定义内容的插槽名 |
children | array | 如当前表头为嵌套表头,则被嵌套的区域作为当前项的children传入 |
上面的描述可能说的不清晰,所以下面直接上示例代码
实际业务页面business.vue
<x-table :comp-table-opts="compTableOpts">
<!-- 这里的header1与下面定义中的headerSlot的值对应,也就是当前插槽的内容会渲染在这一项的表头上 -->
<template #header1>
<span style="color: red">成年</span>
</template>
<template #header2>
<span style="color: yellow">未成年</span>
</template>
<template #header3>
<span style="color: blue;">爱好</span>
</template>
<!-- 这里的minor与下面定义中的prop的值对应,同时这一项的slotFlag为true,所以当前插槽的内容会渲染在这一表格内容中 -->
<template #minor="row">
<span>{
{ row.rowData.minor === '是' ? '🌼' : '🥬' }}</span>
</template>
</x-table>
<script> setup() {
// 这里为了展示多层嵌套的插槽,所以多加几个children const compTableOpts = reactive({
columns: [ {
label: 'ID', prop: 'id' }, {
label: '姓名', prop: 'name' }, {
label: '爱好', prop: 'hobby', headerSlot: 'header3' }, {
label: '基本情况', children: [ {
label: '性别', prop: 'sex' }, {
label: '年龄', prop: 'age' }, {
label: '是否成年', children: [ {
headerSlot: 'header1', prop: 'adult' }, {
headerSlot: 'header2', prop: 'minor', slotFlag: true } ] } ] } ] }) } </script>
表格组件x-table
这里表格组件需要注意的是关于slot+递归嵌套的问题。
假设具体页面为父页面,表格组件为子页面,表格列组件TableColumn为孙子组件,在父页面中使用了插槽,并且插槽放置了内容,在孙子组件中是获取不到的。
在官网查找中并没有找到这方面的描述,不过github上vue的issue里有提到关于嵌套插槽的问题
https://github.com/vuejs/vue/issues/5965
这里尤老师回答说建议使用渲染函数,不过在找资料的过程中发现了
通过模板解决的方法
两者相同的解决思路是把父页面中的所有插槽获取到,在子组件中用之前插槽的名称把这些插槽传递给孙子组件。我采用的是模板解决
<el-table ref="compoundTableRef" v-loading="tableLoading" element-loading-text="拼命加载中" :data="tableData" style="width: 100%;" >
<TableColumn v-for="item in compTableOpts.columns" key="id" :col="item" >
<!-- 注意:如果是vue2中的话customSlots可以替换为$scopedSlots,而且下面setup中的取值也不需要了 -->
<template v-for="slot in Object.keys(customSlots)" #[slot]="scope">
<!-- 以之前的名字命名插槽,同时把数据原样绑定 -->
<slot :name="slot" v-bind="scope" />
</template>
</TableColumn>
</el-table>
<script> import {
getCurrentInstance } from 'vue' setup() {
const {
proxy } = getCurrentInstance() const customSlots = reactive({
...proxy.$slots }) return {
customSlots } } </script>
表格列组件TableColumn
此组件是被递归的组件,所以有关的插槽也需要同时进行传递
<template>
<el-table-column v-if="!col.children" :label="col.label" :prop="col.prop || ''" :show-overflow-tooltip="!col.els" >
<template #header>
<slot v-if="col.headerSlot" :name="col.headerSlot" />
<span v-else>{
{ col.label }}</span>
</template>
<template #default="scope">
<slot v-if="col.slotFlag" :name="col.prop" :rowData="scope.row" />
<span v-else>{
{ typeof scope.row[col.prop] !== 'number' && !scope.row[col.prop] ? '--' : scope.row[col.prop] }}</span>
</template>
</el-table-column>
<el-table-column v-else :label="col.label" >
<TableColumn v-for="t in col.children" :key="t.prop" :col="t" >
<!-- 注意:如果是vue2中的话customSlots可以替换为$scopedSlots,而且下面setup中的取值也不需要了 -->
<template v-for="slot in Object.keys(customSlots)" #[slot]="scope">
<!-- 以之前的名字命名插槽,同时把数据原样绑定 -->
<slot :name="slot" v-bind="scope" />
</template>
</TableColumn>
</el-table-column>
</template>
<script> import {
getCurrentInstance, reactive } from 'vue' export default {
// 这里的name为必须的 name: 'TableColumn', props: {
col: {
type: Object, default: () => {
} } }, setup() {
const {
proxy } = getCurrentInstance() const customSlots = reactive({
...proxy.$slots }) return {
customSlots } } } </script>
结束,撒花
今天的文章vue+element多级表头表格的封装(插槽+递归的嵌套)分享到此就结束了,感谢您的阅读。
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
如需转载请保留出处:https://bianchenghao.cn/68240.html