深入理解element-plus table二次封装:从理论到实践的全面指南

前言

在许多中后台管理系统中,表格占据着半壁江山,如果使用element plus组件库,那么少不了要用到table组件,可是table组件的功能过于基础,因此,我在table组件的实现基础之上进一步封装,从而实现功能更强大的table组件。

在实现table组件的功能之前,我们首先需要先来看看用法,由于使用示例比较多,可能将会有几篇文章主要介绍所有示例用法,介绍完示例之后将会有专门的文章基于使用方式去详细讲解封装思路。

毕竟我们要实现一个东西,就应该需要先理解这个东西怎么用的,从如何使用再去推导如何实现就会很有思路了。

所以在实现之前,我们需要先来具体了解一下每个示例的用法和效果。

首先这个组件被我封装成以插件的形式来使用,我们可以看到在main.ts下的使用代码如下所示:

import { createApp } from 'vue'
import App from './App.vue'
//...
import ElementTable from './components/index'


const app = createApp(App);
//....
app.use(ElementTable);
app.mount('#app')

如此一来,我们就可以全局使用该组件了。

示例1-基础使用

组件支持传入column和data属性,即表格列数据和表体数据,前面我们已经说过了,我们是在全局当中注册好了,因此在vue文件当中可以直接使用。示例代码如下:

 <element-table :column="column" :data="tableData" />

然后就是ts代码,如下所示:

import { ElTableColumnProps } from '../components/tableProps';
/* 引入ElTableColumnProps定义column可获得类型提示 */
const column: ElTableColumnProps[] = [
  {
    type: "index",
    width: "60px",
    label: "序号",
  },
  {
    prop: "name",
    label: "名字",
  },
  {
    prop: "date",
    label: "日期",
  },
  {
    prop: "address",
    label: "地址",
  },
];
// 后续如无特殊说明,表体数据都是这个
const tableData = [
  {
    date: "2016-05-02",
    name: "佘太君",
    address: "上海市普陀区金沙江路 1518 弄",
  },
  {
    date: "2016-05-04",
    name: "王小虎",
    address: "上海市普陀区金沙江路 1517 弄",
  },
  {
    date: "2016-05-01",
    name: "王小帅",
    address: "上海市普陀区金沙江路 1519 弄",
  },
  {
    date: "2016-05-03",
    name: "王小呆",
    address: "上海市普陀区金沙江路 1516 弄",
  },
];

从这个示例,我们可以看到,我们的column传入一个对象数组,对象的属性包含prop,label,type,width等属性,其中type值为index,代表制定一个索引列,然后label和prop分别指定列名以及对应绑定的表体数据字段名,width则指定列宽。

上述代码展示效果如下所示:

示例2--基础的render table

render渲染函数html部分和前面示例1保持一致,唯一不同的是ts代码,我们只需要绑定column和data属性数据即可,一个示例代码如下所示:

import { h } from 'vue';
// 这里是导入的类型
import { ElTableColumnProps } from '../components/tableProps';
const column: ElTableColumnProps[] = [
  {
    type: "index",
    width: "60px",
    label: "序号",
  },
  {
    label: "名字",
    prop: "name",
    render: (data, scope) => {
      const { row, column } = scope;
      const propKey = column.property;
      return h('div', null, { default: () => `render 结果 -- ${row[propKey]}== ${data}` })
    },
  },
  {
    prop: "date",
    label: "日期",
  },
  {
    prop: "address",
    label: "地址",
  },
];

上述代码展示效果如下所示:

从这个示例,我们可以看到,render是一个渲染函数,代表自定义渲染列。

这里尤其需要注意render函数的参数,第一个参数指的是数据,第二个参数则指的是当前表格上下文,我们可以从中获取一些属性,例如列数据column,行数据row等等,从而实现自定义渲染表体。

示例3--插槽用法

关于使用插槽,我们通常使用一个template标签,并制定#加属性值来表示,如示例:

 <element-table :column="column" :data="tableData">
    <template #name="{ row }">
      <div>
        <span>
          <code>name</code>
          插槽--{{ row.name }}
        </span>
      </div>
    </template>
    <template #default>
      <div>
        <span>
          默认插槽名字为
          <strong>default</strong>
        </span>
      </div>
    </template>
  </element-table>

这里分成了两种插槽,第一种是具名插槽,通过#[属性名(这里的属性名通过column里的slotName属性指定)]指定,第二种则是默认插槽,通过#default(与前面具名插槽原理类似)指定,然后在配置的column列数据中,我们通过slotName来指定插槽名,代码如下所示:

const column = [
  {
    type: "index",
    width: "60px",
    label: "序号",
  },
  {
    label: "具名插槽",
    slotName: "name",
  },
  {
    label: "默认插槽",
    slotName: "default",
  },
  {
    prop: "date",
    label: "日期",
  },
  {
    prop: "address",
    label: "地址",
  },
];

上述代码展示效果如下所示:

示例4--边框

边框table,只是在组件当中传入一个border属性即可,其它跟前面示例没什么两样。代码如下所示:

<element-table border :column="column" :data="tableData" />

ts代码忽略,以上代码展示效果如下图所示:

示例5--自定义列渲染

在前面的示例3当中,我们已经初窥自定义列渲染的实现了,接下来这个示例,我们将基于示例3,实现更为复杂的自定义渲染列示例。我们将会引入element-plus的icon(图标)组件和popover(悬浮对话框)组件。代码如下所示:

  <element-table :column="column" :data="tableData" style="width: 100%">
    <template #date="scope">
      <div style="display: flex; align-items: center">
        <el-icon>
          <timer />
        </el-icon>
        <span style="margin-left: 10px">{{ scope.row.date }}</span>
      </div>
    </template>
    <template #name="scope">
      <el-popover effect="light" trigger="hover" placement="top" width="auto">
        <template #default>
          <div>name: {{ scope.row.name }}</div>
          <div>address: {{ scope.row.address }}</div>
        </template>
        <template #reference>
          <el-tag>{{ scope.row.name }}</el-tag>
        </template>
      </el-popover>
    </template>
    <template #default="scope">
      <el-button size="small" @click="handleEdit(scope.$index, scope.row)"
        >Edit</el-button
      >
      <el-button size="small" type="danger" @click="handleDelete(scope.$index, scope.row)"
        >Delete</el-button
      >
    </template>
  </element-table>

接下来,来看我们的ts代码,如下所示:

import { Timer } from "@element-plus/icons-vue";

interface User {
  date: string;
  name: string;
  address: string;
}

const handleEdit = (index: number, row: User) => {
  console.log(index, row);
};
const handleDelete = (index: number, row: User) => {
  console.log(index, row);
};

const column = [
  {
    type: "index",
    width: "60px",
    label: "序号",
  },
  {
    prop: "date",
    label: "日期",
    width: 180,
    slotName: "date",
  },
  {
    prop: "name",
    width: 180,
    label: "名字",
    slotName: "name",
  },
  {
    prop: "address",
    label: "Operations",
    slotName: "default",
  },
];

const tableData: User[] = [
  //...
];

这个示例其实也还是插槽的用法,只不过多了绑定按钮事件。上述代码展示效果如下所示:

示例6-自定义表头

自定义表头与自定义列渲染是同一个原理,也是插槽用法,只不过自定义列的插槽名属性叫做slotName,而自定义表头的插槽名则叫做headerSlot,然后就可以像自定义列那样自定义表头即可。如以下一个示例:

 <element-table :column="column" :data="filterTableData" style="width: 100%">
    <template #header>
      <el-input v-model="search" size="small" placeholder="Type to search" />
    </template>
    <template #default="scope">
      <el-button size="small" @click="handleEdit(scope.$index, scope.row)"
        >Edit</el-button
      >
      <el-button size="small" type="danger" @click="handleDelete(scope.$index, scope.row)"
        >Delete</el-button
      >
    </template>
  </element-table>

然后我们的ts代码就可以基于搜索框,实现数据的过滤,利用ref函数创建一个响应式数据,也就是search。代码如下所示:

import { ref, computed } from "vue";

interface User {
  date: string;
  name: string;
  address: string;
}

const search = ref("");
//...注意这里
const filterTableData = computed(() =>
  tableData.filter(
    (data) =>
      !search.value || data.name.toLowerCase().includes(search.value.toLowerCase())
  )
);

const handleEdit = (index: number, row: User) => {
  console.log(index, row);
};
const handleDelete = (index: number, row: User) => {
  console.log(index, row);
};

const column = [
  {
    prop: "date",
    label: "日期",
    width: 180,
    slotName: "date",
  },
  {
    prop: "name",
    width: 180,
    label: "名字",
    slotName: "name",
  },
  {
    align: "right",
    slotName: "default",
    //这里就是自定义表头的自定义插槽名
    headerSlot: "header",
  },
];

const tableData: User[] = [
  //...
];

以上代码展示效果如下图所示:

输入搜索值之后,展示效果如下图所示:

示例7--自定义索引值

自定义索引值示例很简单,只需要配置列的index属性值为一个函数即可,代码如下:

const indexMethod = (index: number) => index * 2;
const column = [
  {
    type: "index",
    //主要是这里
    index: indexMethod,
  },
 //...
];
const tableData = [
    // ...
]

其余的没什么变化,html代码也很简单,如下所示:

<element-table :column="column" :data="tableData" style="width: 100%"></element-table>

以上代码展示效果如下图所示:

项目附件:点此下载

相关推荐
还是大剑师兰特17 分钟前
什么是尾调用,使用尾调用有什么好处?
javascript·大剑师·尾调用
m0_7482361126 分钟前
Calcite Web 项目常见问题解决方案
开发语言·前端·rust
Watermelo61738 分钟前
详解js柯里化原理及用法,探究柯里化在Redux Selector 的场景模拟、构建复杂的数据流管道、优化深度嵌套函数中的精妙应用
开发语言·前端·javascript·算法·数据挖掘·数据分析·ecmascript
m0_7482489440 分钟前
HTML5系列(11)-- Web 无障碍开发指南
前端·html·html5
m0_748235611 小时前
从零开始学前端之HTML(三)
前端·html
一个处女座的程序猿O(∩_∩)O3 小时前
小型 Vue 项目,该不该用 Pinia 、Vuex呢?
前端·javascript·vue.js
hackeroink6 小时前
【2024版】最新推荐好用的XSS漏洞扫描利用工具_xss扫描工具
前端·xss
迷雾漫步者7 小时前
Flutter组件————FloatingActionButton
前端·flutter·dart
向前看-8 小时前
验证码机制
前端·后端
燃先生._.9 小时前
Day-03 Vue(生命周期、生命周期钩子八个函数、工程化开发和脚手架、组件化开发、根组件、局部注册和全局注册的步骤)
前端·javascript·vue.js