🤔️❓❓如何用Vue3和Element-Plus二次封装一个可定制化的的Table组件

前言

为什么需要二次封装

开发后台管理系统,会接触到很多表格和表单,一但表格表单多起来,仅仅只需要一小部分改变,都需要在中重写一大堆代码,许多重复逻辑,我们可以把重复逻辑抽离出来二次封装一个组件 使用,减少在开发中需要编写的代码。

为什么需要定制化

每个项目或业务都有自己的特点和需求,可能需要特定的样式、交互行为或功能。通过定制化组件,可以根据具体需求进行定制提高复用性,使得组件更符合业务场景,而且定制化可以使组件更加贴近实际业务需求。

需要了解的前置知识

vue3中插槽slot与template

v-bind的使用

v-bind="$attrs"

el-table的基本属性

不多废话,创建文件Hytable,开始代码

这是一个el-plus基础表格

vue 复制代码
//Hytable.vue
<template>
  <el-table :data="tableData" style="width: 100%">
    <el-table-column prop="date" label="Date" width="180" />
    <el-table-column prop="name" label="Name" width="180" />
    <el-table-column prop="address" label="Address" />
  </el-table>
</template>

<script lang="ts" setup>
const tableData = [
  {
    date: '2016-05-03',
    name: 'Tom',
    address: 'No. 189, Grove St, Los Angeles',
  },
  {
    date: '2016-05-02',
    name: 'Tom',
    address: 'No. 189, Grove St, Los Angeles',
  },
  {
    date: '2016-05-04',
    name: 'Tom',
    address: 'No. 189, Grove St, Los Angeles',
  },
  {
    date: '2016-05-01',
    name: 'Tom',
    address: 'No. 189, Grove St, Los Angeles',
  },
]
</script>

一般的表格数据(TableData)是在父组件中请求返回的

vue 复制代码
//HyTable.vue
<template>
 <el-table :data="tableData" style="width: 100%">
    <el-table-column prop="date" label="Date" width="180" />
    <el-table-column prop="name" label="Name" width="180" />
    <el-table-column prop="address" label="Address" /> 
   </el-table>
</template>
<script lang="ts" setup>
import requestTabledata from '../api/index.ts'
interface TableProps{
requestApi?: (params: any) => Promise<any>; 
}
// 接受父组件参数,配置默认值
const props = withDefaults(defineProps<TableProps>(), {
	requestApi: requestTableData
});
let tableData= ref([])

const getTableList = async (api:any) => {
let { data } = await api();
tableData.value = data
};
onMounted(() => {
getTableList(props.requestApi)
})	
</script>

我们可以看到el-table-column有很多重复的代码,如何让他根据父组件的传递值来动态的生成列的配置呢

我们先来看下table-column的属性 这里我们可以用\<template>来包裹el-table-column组件再用v-for来遍历

为什么要使用<template>来包裹,因为等下需要根据列配置用v-if判断el-table-column是否存在列配置type或prop属性,父组件需要传递列配置。

vue 复制代码
//HyTable.vue

<template>
 <el-table :data="tableData" style="width: 100%" row-key="id">
<template v-for="item in columnData" :key="columnData.item">
<el-table-column

v-if="item.type && ['selection','index','expand'].includes(item.type)"

v-bind="item"

:align="'center'"

:reserve-selection="item.type == 'selection'">

<template #default="scope">

<template v-if="item.type === 'expand'">

<slot :name="item.type" v-bind="scope" />

</template>

</template>

</el-table-column>

<el-table-column v-if="!item.type && item.prop" v-bind="item" :align="'center'" >

<template #default="scope">

<slot :name="item.prop" v-bind="scope" :row="scope.row">

{{ scope.row[item.prop] }}

</slot>

</template>

</el-table-column>
</template> 
  </el-table>
</template>
<script setup lang="ts" name="Hytable">
import { onMounted, ref } from "vue";
import {requestTableData} from "../../api/index.ts"

interface TableProps{
columns: any,
requestApi?: (params?: any) => Promise<any>;
}

const props = withDefaults(defineProps<TableProps>(), {
columns: [],
requestApi: requestTableData
});
// 将列设置响应化
const columnData = ref(props.columns);

let tableData= ref([])

const getTableList = async (api:any) => {
let { data } = await api();
tableData.value = data
};
onMounted(() => {
getTableList(props.requestApi)
})	
</script>

增加无数据和表格最后一行的插槽

vue 复制代码
<template>
 <el-table :data="tableData" style="width: 100%">
<template v-for="item in columnData" :key="columnData.item">
<el-table-column
v-if="item.type && ['selection','index','expand'].includes(item.type)"
v-bind="item"
:align="'center'"
:reserve-selection="item.type == 'selection'">
<template #default="scope">

<template v-if="item.type === 'expand'">

<slot :name="item.type" v-bind="scope" />

</template>

</template>

</el-table-column>

<el-table-column v-if="!item.type && item.prop" v-bind="item" :align="'center'" >

<template #default="scope">

<slot :name="item.prop" v-bind="scope" :row="scope.row">

{{ scope.row[item.prop] }}

</slot>
</template>
</el-table-column>
</template> 
<!-- 这是el-table组件自带的插槽 -->
<template #append>
<slot name="append" />
</template>
<!-- 无数据 -->
<template #empty>
<div class="table-empty">
<slot name="empty">
<div>暂无数据</div>
</slot>
</div>
</template>
  </el-table>
</template> 

基本封装已经完成了 实现根据父组件传入数据来实现表格的行与列

让我们来使用一波

vue 复制代码
//父组件
<template>

<Hytable :columns="columnData" >

<template #expand="scope">

{{scope.row}}

</template>

<template #gender="scope">

<el-button type="primary"  plain>

{{ scope.row.gender}}

</el-button>

</template>

<template #operation>

<el-button

type="primary"

link

:icon="View"

>查看</el-button

>

<el-button

type="primary"

link

:icon="EditPen"

>编辑</el-button

>

<el-button

type="primary"

link

:icon="Delete"

>删除</el-button

>

</template>

</HyTable>

</template>

  

<script setup lang="ts">

import Hytable from '../components/testTable/index.vue';

import {Delete, EditPen, View} from "@element-plus/icons-vue";

import { ElMessage} from "element-plus";

const columnData =

[

{ type: 'selection', fixed: 'left', width: 70 },

{ type: 'expand', label: 'Expand', width: 80 },

{ prop: 'name', label: '姓名', search: { el: 'input' } },

{ prop: 'email', label: '邮箱' },

{ prop: 'address', label: '居住地址' },

{ prop:'gender', label: '性别'},

{prop: 'state', label:'用户状态'},

{ prop: 'operation', label: '操作', fixed: 'right', width: 300 },

]

</script>

来看实现效果

基本封装已经可以了,接下来可以增加些功能 如滚动条和内容溢出处理

js 复制代码
// Hytable.vue
<template>
...
<el-table-column v-if="!item.type && item.prop" v-bind="item" :align="'center'" show-overflow-tooltip="showOverflowTooltip">

<template #default="scope">

<slot :name="item.prop" v-bind="scope" :row="scope.row">

{{ scope.row[item.prop] }}

</slot>
</template>
</el-table-column>
...
<slot name="pagination" v-if="pagination">
<el-pagination

:background="true"

:current-page="pageable.pageNum"

:page-size="pageable.pageSize"

:page-sizes="[10, 25, 50, 100]"

:total="pageable.total"

layout="total, sizes, prev, pager, next, jumper"

@size-change="handleSizeChange"

@current-change="handleCurrentChange"

>
</el-pagination>
</slot>
</template>
<script setup lang="ts">
import { onMounted, ref } from "vue";

import {requestTableData} from "../../api/index.ts"

import { reactive } from "vue";

import { toRefs } from "vue";

import { computed } from "vue";

interface TableProps{

columns: any,

requestApi?: (params?: any) => Promise<any>;

pagination?: boolean,

showOverflowTooltip?: boolean;

}

const props = withDefaults(defineProps<TableProps>(), {

columns: [],

requestApi: requestTableData,

pagination: true,

showOverflowTooltip:true,

});

const columnData = ref(props.columns);

// let tableData= ref([])

onMounted(() => {

getTableList(props.requestApi)

})

const handlePage:()=>any = ()=>{

const state = reactive({

tableData: [],

pageable: {

pageNum:1,

pageSize:10,

total: 0,

}

})

const getTableList = async (api:any) => {

let { data } = await api();

state.tableData = data

state.pageable.total = data.length

};

  

const handleSizeChange = (val:any) => {

state.pageable.pageNum = 1;

state.pageable.pageSize = val;

getTableList(props.requestApi)

}

const handleCurrentChange = (val: any) => {

state.pageable.pageNum = val;

getTableList(props.requestApi)

}

// pageable.total = data

return{

...toRefs(state),

getTableList,

handleSizeChange,

handleCurrentChange,

}
}
const {tableData,pageable,handleSizeChange,handleCurrentChange,getTableList} = handlePage()

const processTableData = computed(() => {

if (!props.pagination) return tableData.value
return tableData.value.slice(
pageable.value.pageNum - 1) * pageable.value.pageSize,
pageable.value.pageSize * pageable.value.pageNum
)
})
</script>

看看效果

额。。。还有很多可以加的比如增删改查、查询表单、虚拟滚动之类 慢慢更新吧

相关推荐
uzong37 分钟前
面试官:Redis中的 16 库同时发送命令,服务端是串行执行还是并行执行
后端·面试·架构
关键帧-Keyframe1 小时前
音视频面试题集锦第 26 期
面试·音视频
百万蹄蹄向前冲1 小时前
Trae分析Phaser.js游戏《洋葱头捡星星》
前端·游戏开发·trae
朝阳5812 小时前
在浏览器端使用 xml2js 遇到的报错及解决方法
前端
GIS之路2 小时前
GeoTools 读取影像元数据
前端
ssshooter3 小时前
VSCode 自带的 TS 版本可能跟项目TS 版本不一样
前端·面试·typescript
Jerry3 小时前
Jetpack Compose 中的状态
前端
dae bal4 小时前
关于RSA和AES加密
前端·vue.js
柳杉4 小时前
使用three.js搭建3d隧道监测-2
前端·javascript·数据可视化
倔强青铜三4 小时前
苦练Python第39天:海象操作符 := 的入门、实战与避坑指南
人工智能·python·面试