Skip to content
目录
On this page

el-table表格的二次封装

原理:

  1. 通过tableData 传递进表格里面的数据,后台返回的数据
  2. 通过columnConfig 传递进去表格需要展示的效果,是个对象数组,每个对象都是一个列,里面有列名以及对应的tableData.props 属性,比如 typeName
  3. 通过hasOpr 传递看看需不需要操作列,因为有些角色是不是操作权限的,做兼容处理了
  4. 添加作用域插槽,让父组件可以获取子组件的数据,比如row.id,然后就可以在父组件中执行删除操作。

1. 效果图

在这里插入图片描述

2. 父组件

2.1 父组件 template 中的调用

xml
<!-- 表格 -->
<PenkTable :table-data="tableData" :column-config="columnConfig" hasOpr>
  <template #default="{ scope }">
    <el-button
      type="danger"
      :icon="Delete"
      @click="handleForceDeleteItem(scope.$index, scope.row)"
    >
    </el-button>
  </template>
</PenkTable>

可以看到: 如果,只是简单的表格查看的话,只需要传递tableData数据,以及columnConfig配置项; 如果,需要有操作栏,就可以添加hasOpr 属性,并且使用作用域插槽。

2.2. 父组件 script 中的调用

2.2.1 将数据更新到组件上

由于双向绑定,直接修改tableData即可

typescript
// 表格数据
let tableData = reactive([]);
// 查找数据
async function getList() {
  // 清空数据
  tableData.length = 0;
  console.log("tableData1:", tableData);

  // 判断是否有对象,没有的话就自动弄
  let res = await http.getList({
  	//...
  });
  // @ts-ignore
  tableData.push(...res.rows);
  paginationData.total = res.count;

  console.log("tableData:", tableData);
}

tips:这边要注意reactive 创建的tabelData,不能使用直接赋值,因为是个proxy对象~

2.2.2 配置项

这边有的配置是可填可不填的,具体的配置项可以看封装组件里面的参数,这边大概看一下

  • props:每一列对应对象的属性名,方便通过索引的方式获得键值
  • render:渲染函数,比如后端给你返回了0 代表 禁止, 1 代表启用
  • label:在UI组件前面的label
  • width:列的宽度,无设置就是自适应
  • hidden:是否隐藏,有些业务需求是根据不同角色,一些是不展示的
json
 const columnConfig = [
  {
    label: "父级类型名",
    prop: "parentTypeFk.typeName",
    render: (value) => {
      if (value) return "父-"+value;
    },
  },
  {
    label: "类型名",
    prop: "typeName",
  },
  {
    label: "创建时间",
    prop: "createdAt",
  },
  {
    label: "更新时间",
    prop: "updatedAt",
  },
  {
    label: "删除时间",
    prop: "deletedAt",
  },
];

3. 组件的封装

3.1 封装组件template

代码太长了,将显示部分代码...

3.1.1 el-table的配置

这边统一的样式以及配置,就只用了父组件props上的tableData,用于将表格数据传递给elementUI组件 el-table

xml
<el-table
  :data="props.tableData"
  table-layout="fixed"
  border
  highlight-current-row
  size="large"
  empty-text="-"
  stripe
>
  <!-- el-table 没数据插槽 -->
  <template #empty>
    <h1>没数据</h1>
  </template>

  <!-- 选择框,通过props.isSelection 来判断是否需要展示 -->
  <el-table-column v-if="props.isSelection" type="selection" width="55" />
  <!-- 索引,默认展示索引 -->
  <el-table-column type="index" width="50" />
...
...
...

3.1.2 el-table-column 的配置

  1. props.columnConfig:数组,用于每一列的配置。
  2. 通过作用域插槽,可以获取每一条数据row,通过配置中columnConfig.prop 实现字符串索引,可以得到每一项的值。
  3. 配合columnConfig中的render函数,可以渲染出自己想要的数据,比如 0 => 禁用,1=> 启用...
xml
<template v-for="item in props.columnConfig" :key="item.label">
  <el-table-column
    v-if="item.hidden != true"
    :fixed="item.fixed"
    :prop="item.prop"
    :label="item.label"
    :width="item.width || 'auto'"
  >
    <template #default="{ row, column, $index }">
      <span style="white-space: nowrap" v-if="item.render">{{
        item.render(row[item.prop]) || item.emptyStr || "--"
      }}</span>
      <span style="white-space: nowrap" v-else>{{
        row[item.prop] || item.emptyStr || "--"
      }}</span>
    </template>
  </el-table-column>
</template>

3.2 封装组件script

由于使用了TS来编码,咋们可以在组件中interface中写清楚一些配置,方便使用者知道有什么配置 :)

这边用的是typescript写的,不懂得同学可能要先学习一下,或者去我的ts专栏看看~

3.2.1 props属性

typescript
interface props {
  // 表格数据
  tableData: {
    [index: number]: dataItemObj;
  };
  // 列数据
  columnConfig: columnItemObj[];
  // 判断表格有无列操作
  hasOpr?: boolean;
  // 判断是否有checkbox
  isSelection?: boolean;
}

3.2.2 tableData后台返回数据

typescript
// tableData的item对象,是一个不确定的对象
interface dataItemObj {
  [index: string]: any;
}

3.2.3 columnConfig配置文件

typescript
// 渲染函数,比如后端给你返回了0 代表 禁止, 1 代表启用
// 这个时候就需要渲染函数了...
interface render {
  (v: any): string;
}
// 每一列的配置对象
interface columnItemObj {
  // 判断是否隐藏,一些业务需求,比如不同的权限可能就不展示了
  hidden?:boolean
  // 列无数据占位符
  emptyStr?: string;
  // el-table fixed 属性 左右固定布局
  fixed?: string;
  // el-table width 属性 宽度
  width?: number;
  // el-table prop 属性 字段名
  prop: string;
  // el-table label 属性 列名
  label: string;
  // 自定义渲染
  render?: render;
}

总结

这边只是将只是用作展示数据,并没有什么数据交互,所以封装的组件并没有什么事件,唯一需要获取的数据可能就是通过作用域插槽了~

组件源码

typescript
<!--
 * @Author: Penk
 * @LastEditors: Penk
 * @LastEditTime: 2022-11-29 18:23:25
 * @FilePath: \front-master\src\components\public\PenkTable.vue
 * @email: 492934056@qq.com
-->
<template>
<el-table
  :data="props.tableData"
  table-layout="fixed"
  border
  highlight-current-row
  size="large"
  empty-text="-"
  stripe
>
	<!-- el-table 没数据插槽 -->
  <template #empty>
    <h1>没数据</h1>
  </template>

  <!-- 选择框,通过props.isSelection 来判断是否需要展示 -->
  <el-table-column v-if="props.isSelection" type="selection" width="55" />
  <!-- 索引,默认展示索引 -->
  <el-table-column type="index" width="50" />

    <template v-for="item in props.columnConfig" :key="item.label">
      <el-table-column
        v-if="item.hidden != true"
        :fixed="item.fixed"
        :prop="item.prop"
        :label="item.label"
        :width="item.width || 'auto'"
      >
        <template #default="{ row, column, $index }">
          <span style="white-space: nowrap" v-if="item.render">{{
            item.render(row[item.prop]) || item.emptyStr || "--"
          }}</span>
          <span style="white-space: nowrap" v-else>{{
            row[item.prop] || item.emptyStr || "--"
          }}</span>
        </template>
      </el-table-column>
    </template>

    <!-- 操作列 -->
    <el-table-column v-if="hasOpr" fixed="right" label="操作" width="200">
      <!-- 这边是插槽组件,外部无法访问到里面的数据,比如每一列的item -->
      <!-- 使用作用域插槽,将数据从element组件拿下来,
        再通过 自定义插槽属性scope 传递到父组件,
        使父组件可以调用子组件的数据 -->
      <template #default="{ row, column, $index }">
        <slot :scope="{ row, column, $index }"></slot>
      </template>
    </el-table-column>
  </el-table>
</template>

<script setup lang="ts">
// 渲染函数,比如后端给你返回了0 代表 禁止, 1 代表启用
// 这个时候就需要渲染函数了...
interface render {
  (v: any): string;
}

// columnConfig 是一个columnItemObj对象的数组
interface columnConfig {
  [index: string]: columnItemObj;
}

// 每一列的配置对象
interface columnItemObj {
  // 必填
  // el-table prop 属性 字段名
  prop: string;
  // el-table label 属性 列名
  label: string;

  // 选填
  // 判断是否隐藏,一些业务需求,比如不同的权限可能就不展示了
  hidden?: boolean;
  // 列无数据占位符
  emptyStr?: string;
  // el-table fixed 属性 左右固定布局  只能在头尾的配置中存在
  fixed?: string;
  // el-table width 属性 宽度
  width?: number;
  // 自定义渲染
  render?: render;
}

// tableData的item对象,是一个不确定的对象
interface dataItemObj {
  [index: string]: any;
}

interface props {
  // 表格数据
  tableData: {
    [index: number]: dataItemObj;
  };
  // 列数据
  columnConfig: columnConfig;
  // 判断表格有无列操作
  hasOpr?: boolean;
  // 判断是否有checkbox
  isSelection?: boolean;
}

let props = defineProps<props>();
console.log("props:", props.columnConfig);
</script>

<style lang="less" scoped>
.el-table-border {
  border: 1px #eee solid;
}
// :deep(.el-scrollbar__view) {
//   width: 100%;
// }
</style>

Released under the MIT License.