Vue3和ElementPlus封装table组件

news2024/11/14 20:22:14

最近学习vue3.2并自己在写一个项目,然后发现好几个页面都是列表页,重复写table和column也是觉得累,学习的项目列表页不算多,要是公司项目就不一样了,所以就想着自己封装一个table组件,免去大量重复工作和copy改改改。

本文也是仅仅封装了一个相对简单的table组件,主要是table + 分页,要是不想要分页,也是可以通过使用table组件穿参数控制是否展示分页。基于查询表单,后续再安排~

封装一个table组件并不难,主要是搞懂插槽、作用域插槽的写法和用法,下面先复习一下插槽,再进行封装。

一、slot插槽

定义

Vue 实现了一套内容分发的 API,将 元素作为承载分发内容的出口。

简单理解:就是对子组件的扩展,通过 插槽向子组件内部指定位置传递内容。

插槽是组件的一块HTML模板,这块模板显示不显示,怎样显示是由父组件来控制的,而插槽在哪里显示就由子组件来进行控制。

分类

  • 匿名插槽
  • 具名插槽
  • 作用域插槽

匿名插槽

它不用设置名称属性,可以放置在组件的任意位置;可以理解为父传入样式及内容,子负责展示

<!--index.vue-->
<Child >
     <ul>
         <li>1</li>
         <li>2</li>
     </ul>
     <ul slot>
         <li v-for="(v,i) in arr" :key="i">{{v}}</li>
     </ul>
</Child>

<!--child.vue-->
<!--匿名插槽-->
<template>
 <div>
 <slot></slot>
 </div>
</template>

具名插槽

插槽加了名称属性,就变成了具名插槽。 在 v-slot 之后添加冒号 (:) ,然后写出要传递内容的 slot 的名称(v-slot:slotname);简写:#slotname

<!--index.vue-->
<Child>
    <template #default> 
    <!--指定了 default 的名称,表示不需要为默认插槽指定名称;-->
        <p>我是main的内容</p> 
     </template>
     
     <template #header>
        <h2>艾梯哎教育</h2>
     </template>

     <template #list>
        <h2>列表展示</h2>
        <ul>
          <li v-for="(v,i) in arr" :key="i">{{v}}</li>
        </ul>
     </template>
</Child>

<!--child.vue-->
<!--具名插槽-->
<slot></slot>
<slot name="header"></slot>
<slot name="list"></slot>

作用域插槽

好理解的叫法:带数据的插槽。

作用域插槽跟单个插槽和具名插槽的区别,因为单个插槽和具名插槽不绑定数据,所以父组件提供的模板一般要既包括样式又包括内容,而作用域插槽,相当于父组件提供一套样式,数据都是子组件的。

<!--index.vue-->
<Child >
  <template #user="obj">
      <h2>老师:{{ obj.user.username }}</h2> 
      <h2>所授课程:{{ obj.user.course }}</h2>
  </template>
  
  <!--解构写法-->
  <template #user="{user}">
      <h2>老师:{{ user.username }}</h2> 
      <h2>所授课程:{{ user.course }}</h2> 
  </template>
  
  <!--解构别名-->
  <template #user="{user:person}">
     <h2>老师:{{ person.username }}</h2> 
     <h2>所授课程:{{ person.course }}</h2> 
  </template>
  
</Child>


<!--child.vue-->
<template>
 <div>
 <slot name="user" :user="user"></slot> 
  </div>
</template>

<script setup>
import {ref,reactive} from 'vue'
const user = ref({id:1,username:'张老师',course:'vue'});
</script>

二、封装table

理解了具名作用域插槽 之后,相信大家脑海里已经有思路如何封装了。我这里的思路大概如下:

  • 接收tableData数据和columns列以及其他杂七杂八的配置项;
  • 然后在el-table-column上循环columns列;
  • 然后定义以列名为名称的插槽,并将数据row传递出去;
  • 并提供默认插槽内容,当没特殊数据展示,可不传父的内容,直接使用 定义的默认内容;
  • 如果 columns传入fommater,则会根据传入的function进行转换数据。

具体实现如下:

<!--MyTable-->
<template>
    <div>
        <el-table 
            :style="{ width: '100%' }"
            ref="elTableRef"
            :data="tableData" 
            :height="height" 
            :max-height="maxHeight"
            :stripe="stripe"
            :row-key="rowKey"
            :highlight-current-row="highlightCurrentRow"
            @row-click="handleRowClick"
	         @selection-change="handleSelectionChange">
            <el-table-column 
                v-for="item in columns" 
                :key="item.prop"
                :prop="item.prop" 
                :label="item.label"
                :show-overflow-tooltip="item.showOverflowTooltip"
                :width="item.width"
                :fixed="item.fixed"
                :type="item.type"
                :sortable="item.sortable"
                :selectable="item.selectableFn">
                <!-- type 对应列的类型。 如果设置了selection则显示多选框; -->
                <!-- 如果设置了 index 则显示该行的索引(从 1 开始计算); -->
                <!-- 如果设置了 expand 则显示为一个可展开的按钮-->
                <!-- selection / index / expand -->
                 <template #default="{row, column, $index}" v-if="item.type==='index'">
                    {{getIndex($index)}}
                </template>
                <template #default="{row, column, $index}" v-if="!item.type">
                    <!-- 具名作用域插槽 -->
                    <slot 
                     :name="item.prop"
                     :slotProps="row"
                     :index="$index">
                        <!-- 默认内容,当父组件不传内容时默认显示该内容 -->
                        <span v-if="item.formatter">
                            {{ item.formatter(row[item.prop]) }}
                        </span>
                        <span v-else>
                            {{ row[item.prop] }}
                        </span>
                    </slot>
                </template>
            </el-table-column>
        </el-table>
        <div class="pagination-wrap">
            <el-pagination
                    v-if="hasPagination"
                    v-model:current-page="currentPage"
                    v-model:page-size="pageSize"
                    :page-sizes="pageSizes"
                    :small="small"
                    :background="true"
                    :layout="layout"
                    :total="total"
                    @size-change="handleSizeChange"
                    @current-change="handleCurrentChange"
            />
        </div>
    </div>
</template>

<script setup>
import {toRefs} from 'vue'
const props = defineProps({
    // 表格相关
    tableData: {
        type: Array,
        default: []
    },
    columns:{
        type: Array,
        default: []
    },
    height: {
        type: String,
        default: '500px'
    },
    maxHeight: {
        type: String,
        default: '500px'
    },
    stripe: {
        type: Boolean,
        default: true
    },
    rowKey: {
        type: String,
		default: 'id'
    },
    highlightCurrentRow: {
        type: Boolean,
        default: true
    },
    //  分页相关
    hasPagination: {
        type:Boolean,
        default: true
    },
    total: {
        type: Number,
        default: 0
    },
    currentPage: {
        type: Number,
        default: 1
    },
    pageSize: {
        type:Number,
        default: 10
    },
    pageSizes: {
        type: Array,
        default: [10, 20, 50, 100, 200]
    },
    layout: {
        type: String,
        default: 'total, sizes, prev, pager, next, jumper'
    },
    small: {
        type: String,
        default: 'small'
    }
})

let {
    tableData,
    columns,
    height,
    maxHeight,
    stripe,
    rowKey,
    highlightCurrentRow,
    hasPagination,
    total,
    currentPage,
    pageSize,
    pageSizes,
    layout,
    small
} = toRefs(props)

const emit = defineEmits(['rowClick','selectChange','changeTableData','update:currentPage','update:pageSize'])
// 当某一行被点击时会触发该事件
const handleRowClick = (row, column, event) => {
    emit('rowClick', { row, column, event })
}
// 当选择项发生变化时会触发该事件
const handleSelectionChange = (selection) => {
    emit('selectChange', selection)
}

// 每页条数变化的事件
const handleSizeChange = (val) => {
    emit('update:pageSize',val)
    emit('changeTableData', currentPage.value, val)
}
// 当前页码变化的事件
const handleCurrentChange = (val) => {
    emit('update:currentPage',val)
    emit('changeTableData', val, pageSize.value)
}

const getIndex = (index)=> {
    return index + 1 + (currentPage.value - 1) * pageSize.value
}

</script>

<style lang="scss" scoped>

</style>

三、使用MyTable组件

因为check需要格式化内容并且用el-tag来进行展示内容,所以此处向table组件传入了需要展示的内容,此时,向table的name=check的插槽传入内容,那么table组件的默认展示内容则失效。

同理,由于每个taable组件的操作项也不一样,所以此处向name=operator的slot插入内容。

<MyTable 
    :tableData="tableData"
    :columns="columns"
    :total="total"
    :currentPage="listQuery.pageNo"
    :pageSize="listQuery.pageSize"
    @changeTableData="changeTableData">
    
    <template #check="{ slotProps }">
        <el-tag class="ml-2" :type="slotProps.check?'success':'danger'">
            {{ checkFilter(slotProps.check) }}
        </el-tag>
    </template>
    
    <template #operator="{slotProps}">
        <el-button type="primary" @click="setData('edit',slotProps)">编辑</el-button>
        <el-button type="danger" @click="handleDel(slotProps)">删除</el-button>
    </template>
</MyTable>

<script setup>
import { ref,onMounted } from 'vue'
import MyTable from '@/components/table/index.vue'
import useHooks from '@/hooks'

const { checkFilter, dateFormate } = useHooks()

let listQuery = ref({
    pageNo:1,
    pageSize: 10
})
const tableData = ref([])
let total = ref(0)


/**
 * prop:数据项列名
 * label:列名展示名
 * fixed:固定列 true/right/left
 * width:列宽
 * show-overflow-tooltip
 * type:对应列的类型 selection / index / expand
 * sortable:true/false
 * selectable:Function
 * formatter:格式化内容 function(row, column, cellValue, index)
**/
let columns = ref([
    {prop: 'number',label: '车牌自编号'},
    {prop: 'numberplate',label: '车牌号'},
    {prop: 'date',label: '出厂日期',formatter: dateFormate},
    {prop: 'check',label: '车辆状态'},
    {prop: 'operator',label: '操作',fixed: "right"},
])

onMounted(() => {
    getCarList()
})

const changeTableData = (pageNum,pageSize) => {
    listQuery.value.pageNo = pageNum
    listQuery.value.pageSize = pageSize
    getCarList()
}

// 列表查询
async function getCarList() {
    const {data: {data}} = await carList(listQuery.value)
    tableData.value = data.list
    total.value = data.rows
}

</script>

效果如下:

 

cef83f1ca276458da478452ec7772e20.png如果需要加上索引或者复选框,需要在columns上添加上

 

    {type: 'selection',label:'',width: '50px'},
    {type: 'index',label:'序号',width: '60px'},

若是列项超长不需要tooltip,则配置showOverflowTooltip为false(默认是true)

    {prop: 'number',label: '车牌自编号',showOverflowTooltip: true, width: '100px'},

 

113407b2c8dd4584a369f61df475b6f0.png到此一个talbe组件就封装完成,如有不当的地方还请大家多多包含和赐教!如大家有不一样的封装思想也多多留言交流,互相学习互相进步。

 

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1487392.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

综合实验nginx+nfs+kpa

综合实验 实验目的&#xff1a; 静态资源和动态资源分别存放在远端存储NFS上&#xff0c;NFS上数据实现实时备份&#xff0c;用户通过负载访问后端的web服务。实现ngixn负载高可用&#xff0c;当keepalived master宕机&#xff0c;vip能自动跳转到备用节点 实验环境&#xff…

游戏引擎分层简介

游戏引擎分层架构&#xff08;自上而下&#xff09; 工具层&#xff08;Tool Layer&#xff09; 在一个现代游戏引擎中&#xff0c;我们最先看到的可能不是复杂的代码&#xff0c;而是各种各样的编辑器&#xff0c;利用这些编辑器&#xff0c;我们可以制作设计关卡、角色、动画…

多功能声学综合馆:塑造未来城市空间的先锋

现代城市需要多功能声学综合馆&#xff0c;这不仅是一座建筑&#xff0c;更是空间的变革者&#xff0c;为城市注入活力&#xff0c;展现着未来的发展方向。让我们一同探讨多功能声学综合馆的种种优势&#xff0c;它是如何为城市带来独特的价值。 1. 灵活性与多功能性的典范&am…

【RT-DETR有效改进】结合SOTA思想利用双主干网络改进RT-DETR(全网独家创新,重磅更新)

一、本文介绍 本文给大家带来的改进机制是结合目前SOTAYOLOv9的思想利用双主干网络来改进RT-DETR&#xff08;本专栏目前发布以来改进最大的内容&#xff0c;同时本文内容为我个人一手整理全网独家首发 | 就连V9官方不支持的模型宽度和深度修改我都均已提供&#xff0c;本文内…

RK3568平台开发系列讲解(基础篇)中断线程化

🚀返回专栏总目录 文章目录 一、什么是中断线程化二、中断线程化接口三、中断线程化案例沉淀、分享、成长,让自己和他人都能有所收获!😄 一、什么是中断线程化 中断线程化是一种优化技术, 用于提高多线程程序的性能。 中断线程化的核心思想是将中断处理和主线程的工作分…

paimon取消hive转filesystem

目录 概述实践关键配置spark sql 结束 概述 公司上一版本保留了 hive &#xff0c;此版优化升级后&#xff0c;取消 hive。 实践 关键配置 同步数据时&#xff0c;配置如下&#xff0c;将形成两个库 # ods库 CREATE CATALOG paimon WITH (type paimon,warehouse hdfs:///d…

ospf协议以及案例

OSPF协议网络类型 OSPF协议支持四种网络类型&#xff0c;分别是点到点网络&#xff0c;广播型网络&#xff0c;NBMA网络和点到多点网络。 1、点到点网络是指只把两台路由器直接相连的网络。一个运行PPP的64K串行线路就是一个点到点网络的例子。 2、广播型网络是指支持两台以上…

第二篇【传奇开心果系列】Python的自动化办公库技术点案例示例:深度解读Pandas金融数据分析

传奇开心果博文系列 系列博文目录Python的自动化办公库技术点案例示例系列 博文目录前言一、Pandas 在金融数据分析中的常见用途和功能介绍二、金融数据清洗和准备示例代码三、金融数据索引和选择示例代码四、金融数据时间序列分析示例代码五、金融数据可视化示例代码六、金融数…

第四届信息通信与软件工程国际会议(ICICSE 2024)即将召开!

2024年第四届信息通信与软件工程国际会议&#xff08;ICICSE 2024&#xff09;将于2024年5月10-12日在中国北京举办。本次会议由北京工业大学、IEEE以及Comsoc 联合主办。随着当今社会信息化的高速发展&#xff0c;电子信息技术的应用更是随处可见。其中&#xff0c;信息通信与…

虚拟化之CPU

一 cpu 1 如何查看内核版本&#xff1a;uname -r 2 如何查看操作系统的发行版本&#xff1a;cat /etc/redhat-release 3 计算机系统子的系统 cpu处理器memory内存storage存储network 网络Display显示 4 进程模式 用户模式&#xff08;user mode&#xff09;主要处理I/O的模…

TypeScript11:类型兼容性

类型的兼容性 B -> A&#xff0c;将 B 赋值给 A &#xff0c;如果能完成赋值&#xff0c;则 B 和 A 类型兼容。 鸭子辨型法&#xff08;子结构辨型法&#xff09;&#xff1a; 目标类型需要某一些特征&#xff0c;赋值的类型只要能满足该特征即可。 TS如何进行类型兼容…

SINAMICS V90 PN 指导手册 第7章 位置跟踪

位置跟踪 位置跟踪的主要作用有以下几点&#xff1a; 为单圈绝对值编码器设置一个虚拟多圈扩展位置区域使用齿轮箱时编码器可以重复负载位置 当位置跟踪功能使能后&#xff0c;那么实际位置值在驱动断电时会保存在驱动的掉电存储区中。当驱动器重新上电后&#xff0c;驱动可…

【计算机考研】408学到什么程度才能考130?

408考130要比考研数学考130难的多 我想大部分考过408的考生都是这么认为的。408的难点在于他涉及的范围太广了&#xff0c;首先如果你要备考408&#xff0c;你要准备四门课程&#xff0c;分别是数据结构&#xff0c;计算机组成原理&#xff0c;操作系统和计算机网络。 这四门…

RabbitMQ队列

RabbitMQ队列 1、死信的概念 ​ 先从概念解释上搞清楚这个定义&#xff0c;死信&#xff0c;顾名思义就是无法被消费的消息&#xff0c;字面意思可以这样理解&#xff0c;一般来说,producer将消息投递到broker或者直接到queue里了&#xff0c;consumer 从 queue取出消息进行消…

模型部署 - BevFusion - (1) - 思路总结

模型部署实践 - BevFusion 思路总结一、网络结构 - 总结1.1、代码1.2、网络流程图1.3、模块大致梳理 二、Onnx 的导出 -总体思路分析三、优化思路总结 学习 BevFusion 的部署&#xff0c;看了很多的资料&#xff0c;这篇博客进行总结和记录自己的实践 思路总结 对于一个模型我…

如何限制一个账号只在一处登陆

大家好&#xff0c;我是广漂程序员DevinRock&#xff01; 1. 需求分析 前阵子&#xff0c;和问答群里一个前端朋友&#xff0c;随便唠了唠。期间他问了我一个问题&#xff0c;让我印象深刻。 他问的是&#xff0c;限制同一账号只能在一处设备上登录&#xff0c;是如何实现的…

【Java项目介绍和界面搭建】拼图小游戏——美化界面

&#x1f36c; 博主介绍&#x1f468;‍&#x1f393; 博主介绍&#xff1a;大家好&#xff0c;我是 hacker-routing &#xff0c;很高兴认识大家~ ✨主攻领域&#xff1a;【渗透领域】【应急响应】 【Java】 【VulnHub靶场复现】【面试分析】 &#x1f389;点赞➕评论➕收藏 …

AI企业发力智慧物流 HEGERLS四向车开启新一代托盘柔性物流解决方案

不论自动仓储、智能仓储&#xff0c;解决方案都需要更加平民化&#xff0c;普惠更多企业。柔性灵活、易于部署和扩展、初期投入成本低的方案一定是其中的重点。要实现这些特点&#xff0c;最重要的是硬件要做到标准化、软件要模块化&#xff0c;让仓储设备可以即插即用。凭借柔…

三维可视化技术在设备管理系统中的应用

随着科技的进步&#xff0c;传统的设备管理方法已经不能满足现代企业的需求。为了更高效地管理资产&#xff0c;设备管理系统开始采用三维可视化动态技术。这种技术不仅能够帮助用户快速找到相应的设备&#xff0c;还能够展示设备的现场位置、所处环境、关联设备以及设备参数等…

密钥加密机的工作原理

密钥加密机是信息安全领域中不可或缺的核心设备&#xff0c;它承担着保护通信内容、确保数据完整性以及验证信息发送方身份等重要任务。随着信息技术的迅猛发展&#xff0c;密钥加密机的作用愈发凸显&#xff0c;其安全性和可靠性直接关系到国家安全、商业机密和个人隐私等多个…