el-table 递归表头slot透传封装

文章详细描述了如何在Vue中使用Table组件和Column组件进行列头的深度slot封装,包括递归结构和性能考量,以及如何通过`deepSlotNames`传递数据。作者还提示了关于性能影响的讨论点。
摘要由CSDN通过智能技术生成

 最终效果

实现过程

组件封装

Table.vue组件

<template>
  <el-table :data="list">
    <Column v-for="column in columns" :key="column.prop" :column="column" :sortParams="sortParams" @sortChange="handleSortChange">
      <template v-if="column.slotName" #[column.slotName]>
        <slot :name="column.slotName"></slot>
      </template>
      <template v-for="item in column.deepSlotNames" #[item]="scope">
        <slot  :name="item" v-bind="scope"></slot>
      </template>
      
    </Column>
  </el-table>
</template>

<script lang="ts">
import Column from './Column.vue';

export default {
  props: {
    columns: {
      type: Array,
      default: () => [
        { prop: 'name', label: '姓名', sortable: true },
        { prop: 'age', label: '年龄', sortable: true },
        { prop: 'info', label: '个人信息', deepSlotNames: ['father', 'mother', 'brother', 'xiaox'], children: [
          { prop: 'parent', label: '父母', deepSlotNames: ['father', 'mother'], children: [
            { prop: 'father', label: '父亲', slotName: 'father'  },
            { prop: 'mother',label: '母亲', slotName: 'mother', children: [
            ]  }
          ]},
          { prop: 'brother', label: '兄弟', slotName: 'brother' },
          { prop: 'school', label: '学校', deepSlotNames: ['xiaox'], children: [
            { prop: 'yiwu', label: '义务教育阶段', deepSlotNames: ['xiaox'], children: [
              { prop: 'xiaox', label: '小学', slotName: 'xiaox' }
            ] }
          ] }
          ] 
        },
        { prop: 'operation', label: '操作', slotName: 'operation' }
      ]
    }
  },
  components: {
    Column,
  },
  data() {
    return {
    
      list: [
        { id: 1, name: '小明', age: 12, father: '小明爸爸1', mother: '小明妈妈' },
        { id: 2, name: '小红', age: 22, father: '小红爸爸2', mother: '小红妈妈' },
        { id: 3, name: '小白', age: 32, father: '小白爸爸3', mother: '小白妈妈' },
      ],
      sortParams: {}
    }
  },

  methods: {
    handleSortChange() {
      console.log('sss22', this.sortParams)
    }
    
  }
}
</script>

Column 表头组件

<template>
  <el-table-column :label="column.label" :prop="column.prop">
    <Column v-for="columnItem in column.children" :column="columnItem" :key="columnItem.prop">
      <template v-for="item in column.deepSlotNames" #[item]="scope">
        <slot :name="item" v-bind="scope"></slot>
      </template>

      <template #[column.slotName]="scope">
        <slot :name="column.slotName" v-bind="scope"></slot>
      </template>

    </Column>
    <template v-if="column.sortable" #header>
      <div class="cust-header">
        <span>{{ column.label }}</span>
        <SortItem :sort.sync="sortParams[column.prop]" @sortChange="handleSortChange"></SortItem>
      </div>
    </template>
    <!-- 递归出口 -->
    <template v-if="column.slotName" #default="scope">
      <slot :name="column.slotName" v-bind="scope"></slot>
    </template>

  </el-table-column>
</template>

<script lang="ts">
import { PropType } from 'vue'
import SortItem from './SortItem.vue'

type TColumn = {
  label: string
  prop: string
  deepSlotNames?: string[]
  children?: TColumn
  [key: string]: any
}
export default {
  name: 'Column',
  components: {
    SortItem
  },
  props: {
    column: {
      type: Object as PropType<TColumn>,
      default: () => ({})
    },
    sortParams: {
      type: Object,
      default: () => ({})
    }
  },
  methods: {
    handleSortChange() {
      this.$emit('sortChange')
    }
  }
}
</script>

<style lang="css">
.cust-header {
  display: flex;
  justify-content: space-between;
}
</style>

SortItem组件

<template>
  <div class="table-sort">
    <div class="table-sort-up" @click="handleSortMode('up')" :class="{
      active: sort === 'asc'
    }">^</div>
    <div class="table-sort-down"  @click="handleSortMode('down')" :class="{
      active: sort === 'desc'
    }">v</div>
  </div>
</template>

<script lang="ts">
import { PropType } from 'vue'

export default {
  props: {
    sort: {
      type: String as PropType<'asc' | 'desc'>
    }
  },

  methods: {
    handleSortMode(type: string) {
      if (type === 'up') {
        const newValue = this.sort === 'asc' ? '' : 'asc'
        this.$emit('update:sort', newValue)
      } else {
        const newValue = this.sort === 'desc' ? '' : 'desc'
        this.$emit('update:sort', newValue)
      }
      this.$emit('sortChange')
    }
  }
}
</script>

<style lang="css">
.table-sort {
  display: flex;
  flex-direction: column;
}
.table-sort-up, .table-sort-down {
  width: 16px;
  height: 16px;
  text-align: center;
  line-height: 16px;
  cursor: pointer;
}
.table-sort-up.active, .table-sort-down.active {
  color: red;
}
.table-sort-up  {
  font-size: 16px;
}
.table-sort-down {
  font-size: 14px;
}
</style>

封装表格调用slot透传

<Table>
      <template #operation>操作-slot</template>
      <template #brother>brother</template>
      <template #father="{ row }">
        <div @click="handleGetRow(row)">{{ row.id }}: father-slot</div>
      </template>
      <template #mother="{ row }">{{ row.id }}: mother-slot</template>
      <template #xiaox>xiaox-slot</template>
    </Table>

特殊说明,由于递归层级不确定,slot透传主要靠 deepSlotNames 这个字段接收slot并传递下去,还不确定性能影响,如果有更好的方式欢迎留言讨论

表头的层数比较多时,可以使用递归的方式来生成多级表头。以下是一个示例代码: ```html <el-table :data="tableData" style="width: 100%"> <template v-for="(column, index) in columns"> <template v-if="!column.children"> <el-table-column :key="column.prop" :prop="column.prop" :label="column.label"></el-table-column> </template> <template v-else> <el-table-column :key="column.prop" :label="column.label" :align="column.align"> <template slot="header"> <el-row> <el-table-column v-for="(subColumn, subIndex) in column.children" :key="subColumn.prop" :prop="subColumn.prop" :label="subColumn.label" :width="subColumn.width" :align="subColumn.align"></el-table-column> </el-row> </template> <template v-if="column.children"> <template v-for="(subColumn, subIndex) in column.children"> <el-table-column v-if="!subColumn.children" :key="subColumn.prop" :prop="subColumn.prop" :label="subColumn.label"></el-table-column> <el-table-column v-else :key="subColumn.prop" :label="subColumn.label"> <template slot="header"> <el-row> <el-table-column v-for="(subSubColumn, subSubIndex) in subColumn.children" :key="subSubColumn.prop" :prop="subSubColumn.prop" :label="subSubColumn.label" :width="subSubColumn.width" :align="subSubColumn.align"></el-table-column> </el-row> </template> </el-table-column> </template> </template> </el-table-column> </template> </template> </el-table> ``` 在上述代码中,`columns` 是一个数组,用于存储表头列的属性。如果一个表头列具有子列,则在该列对象中添加 `children` 属性,该属性的值也是一个数组,存储子列的属性。在渲染表头时,首先使用 `v-for` 循环遍历 `columns` 数组,判断当前列是否有子列,如果没有子列,则直接生成表头列;如果有子列,则使用递归的方式生成多级表头。 在生成多级表头时,需要注意以下几点: - 在父级表头中,使用 `slot="header"` 来指定子级表头的渲染位置。 - 在子级表头中,使用 `v-if` 判断当前列是否有子级表头,如果没有子级表头,则直接生成表头列;如果有子级表头,则继续递归生成多级表头。 通过以上方式,就可以递归生成多级表头
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值