vue+element多级表头表格的封装(插槽+递归的嵌套)

vue+element多级表头表格的封装(插槽+递归的嵌套)平时开发过程中,一般都会对element这些组件库中的表格进行二次封装以方便使用

平时开发过程中,一般都会对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

(0)
编程小号编程小号

相关推荐

发表回复

您的电子邮箱地址不会被公开。 必填项已用*标注