vue3二次封装element-ui中的table组件

news2024/12/23 10:15:40

为什么要做这件事

借助封装table组件的过程来巩固一下vue3相关知识点。

组件有哪些配置项

  1. options:表格的配置项
  2. data: 表格数据源
  3. elementLoadingText:加载文案
  4. elementLoadingSpinner:加载图标
  5. elementLoadingBackground:背景遮罩的颜色
  6. elementLoadingSvg:加载图标(svg)
  7. elementLoadingSvgViewBox:加载图标是svg的话,配置项
  8. editIcon:编辑图标
  9. isEditRow:是否支持编辑
  10. editRowIndex:编辑行的标识符
  11. pagination:是否支持分页
  12. paginationAlign:分页对齐方式
  13. currentPage:当前页数
  14. pageSize:每页显示条目个数
  15. pageSizes:每页显示个数选择器的选项设置
  16. total:总条目数

实现过程

首先,将一个普通的element-plus中的table组件引入进来,表格数据源就是我们通过父组件传递进来的data,所以我们使用defineProps来定义,并且它的数据类型是一个数组;同时我们遵循单向数据流的原则,使用lodash中的深拷贝方法将data拷贝一份出来赋值给变量tableData,将tableData传递给element-plus中的table组件,用来渲染数据。

// 子组件 m-table-copy

<template>
  <el-table :data="tableData">
    <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 setup>
import { ref } from 'vue'
import cloneDeep from 'lodash/cloneDeep'
let props = defineProps({
  data: {
    type: Array,
    required: true
  }
})

// 拷贝一份儿数据
let tableData = ref(cloneDeep(props.data))
</script>

 父组件在使用这个自定义组件的时候应该这么使用:

<!-- 父组件 -->
<template>
  <m-table-copy :data="tableData"></m-table-copy>
</template>

<script setup>
import { ref } from 'vue'

let tableData = ref([])

tableData.value = [
  {
    name: '张三',
    address: '杭州市',
    date: '1998-07-16'
  },
  {
    name: '李四',
    address: '石家庄市',
    date: '2013-09-02'
  }
]
</script>

 这样页面上就能够显示出来我们的数据了:

第一步完成了,我们接着再分析,还有什么是可以封装的呢?仔细看上面的代码,是不是有了想法?是的,label、width、prop这些也是可以放在一个配置项里面的,那我们继续来进行封装:

// 父组件准备好的数据结构
let options = [
  {
    prop: 'date',
    label: '日期',
    align: 'center',
    slot: 'date',
    editable: true,
    width: '230'
  },
  {
    prop: 'name',
    label: '姓名',
    align: 'center',
    slot: 'name'
  },
  {
    prop: 'address',
    label: '地址',
    align: 'center',
    editable: true
  },
  {
    label: '操作',
    align: 'center',
    action: true
  }
]

<template>
  <el-table :data="tableData">
    <template v-for="(item, index) in tableOption" :key="index">
      <el-table-column
        :label="item.label"
        :prop="item.prop"
        :align="item.align"
        :width="item.width" />
    </template>
  </el-table>
</template>

<script setup>
import { ref, computed } from 'vue'
let props = defineProps({
  // 表格配置项
  options: {
    type: Array,
    required: true
  }
})

// 过滤操作项之后的配置
let tableOption = computed(() => props.options.filter((item) => !item.action))
</script>

 一般来说,表格都会配置一下loading状态,所以我们继续封装,将loading相关的配置项也添加进来:

完整代码 

<template>
  <el-table
    :data="tableData"
    v-loading="isLoading"
    :element-loading-text="elementLoadingText"
    :element-loading-spinner="elementLoadingSpinner"
    :element-loading-svg="elementLoadingSvg"
    :element-loading-svg-view-box="elementLoadingSvgViewBox"
    :element-loading-background="elementLoadingBackground"
    @row-click="rowClick"
    v-bind="$attrs">
    <template v-for="(item, index) in tableOption" :key="index">
      <el-table-column
        :label="item.label"
        :prop="item.prop"
        :align="item.align"
        :width="item.width">
        <template #default="scope">
          <!-- 编辑模式 -->
          <template v-if="scope.row.rowEdit">
            <el-input v-model="scope.row[item.prop]"></el-input>
          </template>
          <template v-else>
            <template v-if="scope.$index + scope.column.id === currentEdit">
              <div style="display: flex">
                <el-input v-model="scope.row[item.prop]"></el-input>
                <div>
                  <slot
                    v-if="$slots.cellEdit"
                    name="cellEdit"
                    :scope="scope"></slot>
                  <div class="action-icon" v-else>
                    <el-icon-check
                      class="check"
                      @click.stop="check(scope)"></el-icon-check>
                    <el-icon-close
                      class="close"
                      @click.stop="close(scope)"></el-icon-close>
                  </div>
                </div>
              </div>
            </template>
            <template v-else>
              <!-- slot是一个插槽出口,表示了父元素提供的插槽内容将在哪里被渲染 -->
              <slot v-if="item.slot" :name="item.slot" :scope="scope"></slot>
              <span v-else>{{ scope.row[item.prop] }}</span>
              <component
                v-if="item.editable"
                :is="`el-icon-${toLine(editIcon)}`"
                class="edit"
                @click.stop="clickEditIcon(scope)"></component>
            </template>
          </template>
        </template>
      </el-table-column>
    </template>
    <el-table-column
      :align="actionOption.align"
      :label="actionOption.label"
      :width="actionOption.width">
      <template #default="scope">
        <!-- 编辑模式下显示确认和取消 -->
        <slot name="editRow" :scope="scope" v-if="scope.row.rowEdit"></slot>
        <!-- 正常状态下显示 编辑和删除 -->
        <slot name="action" :scope="scope" v-else></slot>
      </template>
    </el-table-column>
  </el-table>
  <div
    v-if="pagination && !isLoading"
    class="pagination"
    :style="{ justifyContent }">
    <el-pagination
      v-model:currentPage="currentPage"
      :page-size="pageSize"
      :page-sizes="pageSizes"
      @size-change="handleSizeChange"
      @current-change="handleCurrentChange"
      layout="total, sizes, prev, pager, next, jumper"
      :total="total" />
  </div>
</template>
<script setup>
import cloneDeep from 'lodash/cloneDeep'
import { computed, onMounted, ref, watch } from 'vue'
import { toLine } from '../../../utils'
let props = defineProps({
  // 表格配置项
  options: {
    type: Array,
    required: true
  },
  // 表格数据
  data: {
    type: Array,
    required: true
  },
  // 编辑图标
  editIcon: {
    type: String,
    default: 'Edit'
  },
  // 显示在加载图标下方的加载文案
  elementLoadingText: {
    type: String
  },
  // 自定义加载图标
  elementLoadingSpinner: {
    type: String
  },
  // 自定义加载图标(svg)
  elementLoadingSvg: {
    type: String
  },
  // 自定义加载图标(svg)的配置
  elementLoadingSvgViewBox: {
    type: String
  },
  // 背景遮罩的颜色
  elementLoadingBackground: {
    type: String
  },
  // 是否可用编辑行
  isEditRow: {
    type: Boolean,
    default: false
  },
  // 编辑行按钮的标识
  editRowIndex: {
    type: String,
    default: ''
  },
  // 是否显示分页
  pagination: {
    type: Boolean,
    default: false
  },
  // 分页的对齐方式
  paginationAlign: {
    type: String,
    default: 'left'
  },
  // 当前第几页
  currentPage: {
    type: Number,
    default: 1
  },
  // 显示分页数据多少条的选项
  pageSizes: {
    type: Array,
    default: () => [10, 20, 30, 40]
  },
  // 数据总条数
  total: {
    type: Number,
    default: 0
  }
})

// 深拷贝一份表格的数据
let tableData = ref(cloneDeep(props.data))

let cloneEditRowIndex = ref(props.editRowIndex)
// 过滤操作项之后的配置
let tableOption = computed(() => props.options.filter((item) => !item.action))

let actionOption = computed(() => props.options.find((item) => item.action))

// 监听的标识
let watchData = ref<boolean>(false)

// 如果data的数据变了 要重新给tableData赋值
// 只需要监听一次就可以了
let stopWatchData = watch(
  () => props.data,
  (val) => {
    watchData.value = true
    tableData.value = val
    tableData.value.map((item) => {
      item.rowEdit = false
    })
    if (watchData.value) stopWatchData()
  },
  { deep: true }
)

watch(
  () => props.editRowIndex,
  (val) => {
    if (val) cloneEditRowIndex.value = val
  }
)

// 当前被点击的单元格的标志
let currentEdit = ref('')
let currentPage = computed(() => props.currentPage)
let justifyContent = computed(() => {
  if (props.paginationAlign === 'left') return 'flex-start'
  else if (props.paginationAlign === 'right') return 'flex-end'
  else return 'center'
})
let isLoading = computed(() => !props.data || !props.data.length)

let emits = defineEmits([
  'confirm',
  'cancel',
  'update:editRowIndex',
  'size-change',
  'current-change'
])

onMounted(() => {
  tableData.value.map((item) => {
    item.rowEdit = false
  })
})

let clickEditIcon = (scope) => {
  currentEdit.value = scope.$index + scope.column.id
}

let handleSizeChange = (val) => {
  emits('size-change', val)
}

let handleCurrentChange = (val) => {
  emits('current-change', val)
}

let check = (scope) => {
  emits('confirm', scope)
  currentEdit.value = ''
}

let close = (scope) => {
  emits('cancel', scope)
  currentEdit.value = ''
}

let rowClick = (row, column) => {
  if (column.label === actionOption.value.label) {
    if (props.isEditRow && cloneEditRowIndex.value === props.editRowIndex) {
      row.rowEdit = !row.rowEdit
      tableData.value.map((item) => {
        if (item !== row) item.rowEdit = false
      })
      if (!row.rowEdit) emits('update:editRowIndex', '')
    }
  }
}
</script>

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

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

相关文章

解码Java SPI:深入理解与实践【七】

欢迎来到我的博客&#xff0c;代码的世界里&#xff0c;每一行都是一个故事 解码Java SPI&#xff1a;深入理解与实践【七】 前言SPI机制简介&#xff1a;SPI的工作原理java标准SPI示例总结 前言 在编写Java应用程序时&#xff0c;我们经常需要使用不同的库和框架来扩展功能。…

NET Core发布 HTTP Error 500.31 - Failed to load ASP.NET Core runtime

记录一下踩过的坑&#xff1a; 首先&#xff0c;不论是500.31还是500.30 &#xff0c;首先确保安装了三个文件 1.NET Core RunTime 2.NET SDK 3.NET Hosting 其次&#xff0c;确保三个文件的版本一致&#xff0c;如下&#xff1a; 要装就统一装同一个大版本&#xff0c;不要东…

streamlit配合plotly绘制交互式图表

借助st.plotly_chart实现 官方介绍&#xff1a;st.plotly_chart 案例&#xff1a; import pandas as pd import plotly.graph_objs as go import plotly.express as pxst.subheader("课题组成员", dividerred) df pd.read_excel("./data/summary.xlsx"…

护眼灯到底有用吗知乎?护眼台灯的作用

近年来&#xff0c;随着电子产品的普及和使用时间的增加&#xff0c;人们对眼部健康的关注度也日益提高。为解决由此带来的问题&#xff0c;护眼台灯在这一背景下崭露头角。护眼台灯是一种融合先进技术的台灯&#xff0c;旨在缓解眼部疲劳并保护视力&#xff0c;那护眼台灯究竟…

第七站:C++面向对象训练

1:介绍 创建两个类,一个boy类,一个girl类,实现对两个类的数据输入,并通过main函数,对两个类的成员进行比较匹配 涉及: vector容器,面向对象,const关键字, stringstream(sstream.h) stringstream用法 stringstream ret;//可以将写入的数据转换成字符串 ret << &quo…

利用python将Excel文件拆分为多个CSV

目录 一、准备工作 二、拆分Excel文件为多个CSV 1、读取Excel文件&#xff1a; 2、确定要拆分的列&#xff1a; 3、创建空的字典来存储CSV文件&#xff1a; 4、循环遍历数据并根据类别拆分&#xff1a; 5、打印或返回CSV文件名字典&#xff1a; 6、保存CSV到特定目录&a…

python.24.1.16函数

python自带函数 自定义函数 参数的使用

机器学习——支持向量机SVM

1 摘要&#xff1a; 支持向量机&#xff08;SVM&#xff09;是一种二类分类模型&#xff0c;其基本模型是在特征空间上找到最佳的分离超平面使得训练集上正负样本间隔最大&#xff0c;间隔最大使它有别于感知机&#xff0c;支持向量机也可通过核技巧使它成为非线性分类器。支持…

presto 支持regexp_count

一、背景 1、查询regexp_count 函数提示未注册 用户想正则查询特定字符出现次数 function regexp_count not registered 二、调研 1、官网地址&#xff1a; Presto Documentation — Presto 0.284 Documentation 2、regexp_extract_all Regular Expression Functions —…

c++时间复杂度详解

1.基本概念 在计算机科学中&#xff0c;时间复杂性&#xff0c;又称时间复杂度&#xff0c;算法的时间复杂度是一个函数&#xff0c;它定性描述该算法的运行时间。这是一个代表算法输入值的字符串的长度的函数。时间复杂度常用大O符号表述&#xff0c;不包括这个函数的低阶项和…

【Mybatis系列】Mybatis空值关联

&#x1f49d;&#x1f49d;&#x1f49d;欢迎来到我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 推荐:kwan 的首页,持续学…

PDF.js实现按需分片加载pdf文件

pdf.js实现按需、分片加载pdf文件 1.服务端配置 分片加载的实现是基于 HTTP-RANGE&#xff0c;即服务端的文件接口必须实现了HTTP-RANGE。 服务端文件接口实现HTTP-RANGE&#xff0c;需要服务端添加如下响应头 [{key: "Accept-Ranges",value: "bytes"}…

用Growly Draw for Mac,释放您的创意绘画天赋!

在数字化时代&#xff0c;绘画已经不再局限于传统的纸笔之中。如今&#xff0c;我们可以借助强大的绘画应用软件&#xff0c;将创意化为独特的艺术作品。而Growly Draw for Mac就是一款让您能够快速释放创意、创作精美绘画作品的应用软件。 Growly Draw for Mac是一款专为Mac用…

Ezsql

靶场说明 靶机地址解释&#xff1a; 第一行&#xff1a;目标机器 WEB 服务地址 第二行&#xff1a;目标机器 SSH 地址以及端口 第三行&#xff1a;Check 服务访问地址。 http://99bdd2da-7d5e-4b5c-a7ee-79713b8ecabc.node5.buuoj.cn:8199bdd2da-7d5e-4b5c-a7ee-79713b8ecabc…

十、Three场景实现多个物体的合并

Three场景实现多个物体的合并 目的 产品需求是让物体的光柱墙包含一个多边形的区域,二而我的多边形只能使用原型,方向,多边形。那么再研究的时候就需要将这些多边形合并成为一个形状,那么就行实现了。 原先的图形 如上图,是两个mesh组成的。首先寻找mesh合并的方法。 第…

《TrollStore巨魔商店》TrollStore2安装使用教程支持IOS14.0-16.6.1

TrollStore(巨魔商店) 简单的说就相当于一个永久的免费证书&#xff0c;它可以给你的iPhone和iPad安装任何你想要安装的App软件&#xff0c;而且不需要越狱,不用担心证书签名过期的问题&#xff0c;不需要个人签名和企业签名。 支持的版本&#xff1a; TrollStore安装和使用教…

Ubantu 安装vscode配置c/c++环境

文章目录 安装VSCode注意 snap包冲突 安装C/C编译环境注意 进程锁占用 配置C开发环境安装插件配置tasks.json配置c_cpp_properties.json 配置调试环境配置 launch.json 安装VSCode 方式一&#xff1a;ubantu 软件里面直接安装 方式二&#xff1a;官网下载deb安装包https://cod…

面试Java岗老喜欢盯着JVM问,有那么多项目要调优吗?

面试Java岗老喜欢盯着JVM问&#xff0c;有那么多项目要调优吗&#xff1f; 在开始前我有一些资料&#xff0c;是我根据网友给的问题精心整理了一份「Java的资料从专业入门到高级教程」&#xff0c; 点个关注在评论区回复“888”之后私信回复“888”&#xff0c;全部无偿共享给…

C++ 编程需要什么样的开发环境?

C 编程需要什么样的开发环境&#xff1f; 在开始前我有一些资料&#xff0c;是我根据网友给的问题精心整理了一份「C的资料从专业入门到高级教程」&#xff0c; 点个关注在评论区回复“888”之后私信回复“888”&#xff0c;全部无偿共享给大家&#xff01;&#xff01;&#…

资本主义的市场竞争?IBM总监Jerry Chow 谈量子计算的未来

​ 人物介绍&#xff1a;Jerry M.Chow博士在耶鲁大学取得物理博士学位。担任IBM量子系统总监&#xff0c;其研究重点是面向容错量子计算的多量子比特系统。他主要为IBM的量子系统路线图制定战略&#xff0c;与硬件团队领导者一起设定目标研究领域&#xff0c;同时也确保最佳的客…