Vue3+ts+element-plus 组件的二次封装– el-table的封装,通过json进行配置表单,可用插槽进行拓展

导读:本篇文章讲解 Vue3+ts+element-plus 组件的二次封装– el-table的封装,通过json进行配置表单,可用插槽进行拓展,希望对大家有帮助,欢迎收藏,转发!站点地址:www.bmabk.com

Vue 笔记

本人是一个web前端开发工程师,主要是vue框架,整理了一些Vue常用的技术,一方面是分享,一方面是做总结,今后也会一直更新,有好建议的同学欢迎评论区分享 ;-)

Vue专栏:点击此处
Vue组件库专栏:点击此处
Vue2 vs Vue3 专栏:点击此处
Typescript专栏:点击此处

在这里插入图片描述


组件库开发流程

Vue组件库专栏会按顺序执行一下流程,不断完善组件库开发流程

  1. Vue3+element-plus+vite 组件的二次封装,封装了头部的搜索条件栏,tabel栏,分页栏,form表单,都设置成了通过json可配置项,方便复用;
  2. 封装好了就开始打包,并且进行本地测试;
  3. 组件库发布到npm;
  4. 添加vitest单元测试框架;
  5. 添加vuepress文档。


前言

环境状态
vue版本:vue3
是否使用 ts:是

后台管理系统的网站,一个页面无非就是4个常用业务块

  1. 头部的搜索栏
  2. table表格
  3. 页脚
  4. 新增编辑弹框

那咋们是不是可以将其进行封装成组件呢?
只需要传入一个配置文件就可以了~

项目解构如下:
在这里插入图片描述

封装后的展示图如下:
在这里插入图片描述

效果图如下:

在这里插入图片描述

如果能做成这样,是不是页面就会整洁很多?


el-table表格的二次封装

原理:

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

1. 效果图

在这里插入图片描述


2. 父组件

2.1 父组件 template 中的调用

<!-- 表格 -->
<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即可

// 表格数据
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:是否隐藏,有些业务需求是根据不同角色,一些是不展示的
 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

<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=> 启用…
<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属性

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

3.2.2 tableData后台返回数据

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

3.2.3 columnConfig配置文件

// 渲染函数,比如后端给你返回了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;
}

总结

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


组件源码

<!--
 * @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>

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/66357.html

(0)
小半的头像小半

相关推荐

极客之音——专业性很强的中文编程技术网站,欢迎收藏到浏览器,订阅我们!