Vue3和ElementPlus封装table组件

最近学习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.mfbz.cn/a/428329.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Acwing---1497. 树的遍历

树的遍历 1.题目2.基本思想3.代码实现 1.题目 一个二叉树&#xff0c;树中每个节点的权值互不相同。 现在给出它的后序遍历和中序遍历&#xff0c;请你输出它的层序遍历。 输入格式 第一行包含整数 N&#xff0c;表示二叉树的节点数。 第二行包含 N个整数&#xff0c;表示二…

Javase-类与对象

文章目录 一 . 面向过程的初步认知二 . 如何创建一个类三 . 如何创建一个对象四 . this引用五 . 构造方法六 . 初始化 一 . 面向过程的初步认知 Java是一门纯面向对象的语言(Object Oriented Program&#xff0c;简称OOP)&#xff0c;在面向对象的世界里&#xff0c;一切皆为对…

使用Android Native Hook技术解决VLC播放器闪退的问题

文章目录 1.概述2.问题描述3.问题分析4.问题解决5.总结 1.概述 在做公司的一个TOB的需求时&#xff0c;发现调起Unity提供的3D播放器播放网络在线视频时闪退了&#xff0c;然后就拉着相关部门的人一起分析问题&#xff0c;最后定位到是VLC里面用到的系统日志打印函数在部分的系…

Flask入门二(Flask的CBV、模版语法、请求和响应、session执行流程分析、Flask闪现、请求拓展、g对象)

文章目录 一、Flask的CBV1.CBV的写法2.CBV的执行流程3.endpoint 的使用4.CBV中得methods作用5.CBV加装饰器 二、模版语法1.渲染变量2.变量的循环3.逻辑判断 三、请求和响应四、session执行流程分析1.基本使用2.执行流程3.Django中session的执行流程 五、Flask闪现1.作用2.案例3…

【Unity】Node.js安装与配置环境

引言 我们在使用unity开发的时候&#xff0c;有时候会使用一些辅助工具。 Node.js就是开发中&#xff0c;经常会遇到的一款软件。 1.下载Node.js 下载地址&#xff1a;https://nodejs.org/en 2.安装Node.js ①点击直接点击Next下一步 ②把协议勾上&#xff0c;继续点击…

【论文精读】I-JEPA

摘要 计算机视觉中&#xff0c;常采用基于不变性和基于生成的方法进行自监督学习。对比学习&#xff08;CL&#xff09;是典型的基于不变性的方法&#xff0c;通过预训练方法优化编码器&#xff0c;使其能生成同一图像的两个或多个视图的相似嵌入&#xff0c;其中图像视图通常由…

格两例12345

osu/Lucky Roll gaming 周末osu有道题&#xff1a;lcg已知低位 def lcg(s, a, b, p):return (a * s b) % pp getPrime(floor(72.7)) a randrange(0, p) b randrange(0, p) seed randrange(0, p) print(f"{p }") print(f"{a }") print(f"{b …

关于python函数参数传递

参数传递 在 python 中&#xff0c;类型属于对象&#xff0c;对象有不同类型的区分&#xff0c;变量是没有类型的&#xff1a; 在下面的代码示例重&#xff0c;[1,2,3] 是 List 类型&#xff0c;“qayrup” 是 String 类型&#xff0c;而变量 a 是没有类型&#xff0c;它仅仅…

java找工作之Mybatis(入门及xml配置相关)

Mybatis 学习Mybatis就要学会查看官网&#xff0c;官网地址如下&#xff1a;<MyBatis中文网 > 1、简介 1.1什么是Mybatis MyBatis 是一款优秀的持久层框架&#xff0c;它支持自定义 SQL、存储过程以及高级映射。MyBatis 免除了几乎所有的 JDBC 代码以及设置参数和获取…

图形系统开发实战课程:进阶篇(上)——9.空间算法(一)

图形开发学院&#xff5c;GraphAnyWhere 课程名称&#xff1a;图形系统开发实战课程&#xff1a;进阶篇(上)课程章节&#xff1a;“图形样式”原文地址&#xff1a;https://www.graphanywhere.com/graph/advanced/2-9.html 第九章 空间算法&#xff08;一&#xff09; \quad 在…

计算机专业必看的十部电影

计算机专业必看的十部电影 1. 人工智能2. 黑客帝国3. 盗梦空间4. 社交网络5. Her6. 模仿游戏7. 斯诺登8. 头号玩家9. 暗网10. 网络迷踪 计算机专业必看的十部电影&#xff0c;就像一场精彩盛宴&#xff01; 《黑客帝国》让你穿越虚拟世界&#xff0c;感受高科技的魅力《模仿游戏…

小红关鸡(双指针)

链接&#xff1a;登录—专业IT笔试面试备考平台_牛客网 来源&#xff1a;牛客网 时间限制&#xff1a;C/C 1秒&#xff0c;其他语言2秒 空间限制&#xff1a;C/C 262144K&#xff0c;其他语言524288K Special Judge, 64bit IO Format: %lld 题目描述 有nnn个鸡窝排成一排&a…

#WEB前端(CCS常用属性,补充span、div)

1.实验&#xff1a; 复合元素、行内元素、块内元素、行内块元素 2.IDE&#xff1a;VSCODE 3.记录&#xff1a; span为行内元素&#xff1a;不可设置宽高&#xff0c;实际占用控件决定分布空间。 div为块内元素&#xff1a;占满整行&#xff0c;可以设置宽高 img为行内块元…

Windows 2012 设置 nginx 开机自启动(适用于windows2012/10)

Windows 2012 设置 nginx 开机自启动&#xff08;适用于windows2012/10&#xff09;https://www.cnblogs.com/xuegqcto/articles/7521483.html 在windows server 2012上安装nginx&#xff0c;同时配置开机自启动服务&#xff08;推荐使用“Windows Service Wrapper”工具&…

前后端分离项目服务器部署

文章目录 前言准备工作安装jdk1.8安装nginx安装库解压、编译nginx并安装nginx 命令测试nginx 安装mysql卸载mariadb用root用户登录系统&#xff0c;增加mysql用户和组准备数据目录初始化MySQL将mysql加入到服务中编辑配置文件&#xff0c;保存退出启动mysql配置环境变量设置开机…

20 个不同的 Python 函数实例

Python 是一种广泛使用的高级编程语言&#xff0c;其函数是 Python 编程中至关重要的概念之一。函数是一段可以重复使用的代码块&#xff0c;可以接收输入参数并返回输出结果。使用函数能够提高代码的可读性、可维护性和重用性。 基础知识 在 Python 中&#xff0c;函数使用关…

C语言初阶—数组

数组是一组相同类型元素的集合。 在C99标准之前&#xff0c;数组的大小必须是常量或常量表达式。 在C99标准之后&#xff0c;数组的大小可以是变量&#xff0c;可以支持变长数组&#xff0c;但变长数组不能初始化。 不完全初始化&#xff0c;剩余的元素默认初始化为0 。 数组访…

c++_leetcode_寻找峰值

目录 一、寻找峰值的示例 二、官方实现代码及解释 1、官方测试结果&#xff1a; 2、代码解释&#xff1a; 3、解题思路&#xff1a; 三、我的暴力解决 1、测试一&#xff1a; 2、测试二&#xff1a; 3、最终“暴力求解”代码&#xff1a; 4、官网提交测试通过&#xf…

终极排序(快排,归并,库函数)

一、快速排序 1、确定分界点&#xff1a;q [ l ] , q [ ( l r ) / 2 ] , q [ r ] ,或者其它区间之中的随机数。&#xff08;左 l 右 r &#xff09; 2、调整区间&#xff1a;&#xff08;较难理解的部分&#xff09; &#xff08;1&#xff09;、暴力做法 …

基于Siamese网络的zero-shot意图分类

原文地址&#xff1a;Zero-Shot Intent Classification with Siamese Networks 通过零样本意图分类有效定位域外意图 2021 年 9 月 24 日 意图识别是面向目标对话系统的一项重要任务。意图识别(有时也称为意图检测)是使用标签对每个用户话语进行分类的任务&#xff0c;该标签…
最新文章