【Vue+ElementUI】Table表格实现自定义表头展示+表头拖拽排序(附源码)

效果图

在这里插入图片描述
在这里插入图片描述

因项目采用的是Vue2,所以这个功能目前采用的是Vue2的写法。
Vue3请自行修改扩展代码;或收藏关注帖子,后续Vue3项目如有用到会在本帖子更新修改。

安装vuedraggable(拖拽插件)

cnpm i vuedraggable

先说用法,下方附全源码

引入自定义表头组件

import indicatorTable from "@/components/indicatorTable/index.vue";

使用:(传参说明已在下方标识)

<indicatorTableref="rois":defaultArr="columns":cardDataProp="cardDataProp"cacheKeyProp="keyROI"@propData="propsTableHander"currenKey="ROT"
/>

props参数说明:(均为必传字段)

// ref:用于调用子组件方法。
// columns:表头数据,例如:
[{prop: "cost_platform",label: "广告金",
}]// cardDataProp:可选表头复选框,列如:
cardDataProp: [{title: "指标", // 每一项的分类title标题,详见第一张效果图checkboxes: [...columns], // columns这个就是上面的一样},
],// cacheKeyProp:储存的key名,名字自定义来,避免缓存的key一样就行,列如:
cacheKeyProp="keyROI"// propData:回调方法,用于更新表头,接受函数,直接表头columns数据 = 参数即可// currenKey:保存的指标key,避免缓存的key一样就行。

页面table使用方法,需用循环:

<el-tablev-loading="loading":data="tableList"border@sort-change="tableSort":height="tableHeight"ref="tableRef"
><el-table-columnv-for="item in columns":prop="item.prop":label="item.label":width="item.width"align="center"sortable="custom":show-overflow-tooltip="true"></el-table-column>
</el-table>

上面表格的参数不用多说了吧,除非你不会前端!

附源码(拿来直接用!只要参数没问题!)

如遇到报错、不显示等问题,一定是参数不对!已自测 无任何报错或警告信息!
如需要Vue3版本,自行开发或私信,有空定会帮助!
新建组件直接复制:

<template><div class="indicator-all-box"><el-popover placement="bottom" width="300" trigger="click"><div class="add-custom-indicator-container"><el-button type="primary" @click="addUserDefinedIndicators">新增自定义指标</el-button><div class="indicator-list"><ul><liv-for="(item, index) in pointerArr":key="index":class="currenPointIndex == index ? 'active-li' : ''"@click="pointClick(item, index)"><div class="flex-indicator-item"><span>{{ item.title }}</span><div class="right-indicator"><iclass="el-icon-edit"@click.stop.prevent="pointIndexHander(item, 'edit')"></i><iclass="el-icon-delete"@click.stop.prevent="pointIndexHander(item, 'delete')"></i></div></div></li></ul></div></div><el-button slot="reference" type="success">自定义指标</el-button></el-popover><!-- 弹窗自定义 --><el-dialog :title="openTitle" :visible.sync="dialogVisible" width="70%"><div class="customize-indicator-data-container"><div class="card-checkbox-content-left"><el-cardv-for="(item, index) in cardData":key="index"class="box-card"><div slot="header" class="clearfix"><span>{{ item.title }}</span><el-checkboxv-model="item.selectedAll"@change="handleSelectAll(item)":indeterminate="isIndeterminate(item)"style="float: right">全选</el-checkbox></div><div class="check-card-item"><el-checkbox-groupref="checkboxGroup"v-model="selectedCheckboxes"><el-checkboxv-for="(checkbox, idx) in item.checkboxes":key="idx":label="checkbox.label">{{ checkbox.label }}</el-checkbox></el-checkbox-group></div></el-card></div><div class="sort-view-dx"><el-divider>排序</el-divider><div class="sort-row"><draggablev-if="selectedCheckboxes.length > 0"v-model="selectedCheckboxes"animation="300"><p v-for="(item, index) in selectedCheckboxes" :key="index">{{ item }}</p></draggable><el-empty v-else></el-empty></div></div></div><span slot="footer" class="dialog-footer"><el-button @click="dialogVisible = false">取 消</el-button><el-button type="primary" @click="addPointerSubmit">确 定</el-button></span></el-dialog></div>
</template><script>
import draggable from "vuedraggable";export default {name: "indicatorTable",components: {draggable,},props: {// 默认指标defaultArr: {type: Array,required: true,},// 可选指标cardDataProp: {type: Array,required: true,},// 存储指标keycacheKeyProp: {type: String,required: true,},// 存储的索引key名currenKey: {type: String,required: true,},},data() {return {// 弹窗showdialogVisible: false,// 全部指标数组cardData: this.cardDataProp,// 勾选指标selectedCheckboxes: [],// 弹框titleopenTitle: "添加",// 下拉指标列表pointerArr: [],// 获取当前编辑itemeditItem: null,// 传出去的prop数组emitArr: null,// 当前选择的指标currenPointIndex: null,};},computed: {Local() {return {get(key) {const value = localStorage.getItem(key);if (value == "[]") {return null;} else {return value !== null ? JSON.parse(value) : null;}},set(key, value) {localStorage.setItem(key, JSON.stringify(value));},remove(key) {localStorage.removeItem(key);},};},// 指标指定label排序sortColumns() {return function (data, sort) {if (data) {return data.sort((a, b) => sort.indexOf(a.label) - sort.indexOf(b.label));}};},// 获取选指标labelfilterCheckbox() {return function (data, isSort = false, sortData) {if (data) {let filteredCheckboxes = [];this.cardData.forEach((item) => {item.checkboxes.forEach((checkbox) => {if (data.arrayCheck.includes(checkbox.label)) {filteredCheckboxes.push(checkbox);}});});// 获取后是否排序if (isSort) {return this.sortColumns(filteredCheckboxes, sortData);} else {return filteredCheckboxes;}}};},},created() {// this.Local.remove("displayType");this.getPointData("init");},methods: {// 存储key索引storeSetCurrentIndex(type = "set") {if (type === "set") {const getIndexObj = this.Local.get("pointIndex") || {};getIndexObj[this.currenKey] = this.currenPointIndex;this.Local.set("pointIndex", getIndexObj);} else {return this.Local.get("pointIndex") || {};}},// 选择当前指标pointClick(row, index) {if (this.currenPointIndex != index) {this.currenPointIndex = index;// 存储当前点击指标indexthis.storeSetCurrentIndex("set");const checkData = this.filterCheckbox(row,true,this.pointerArr[this.currenPointIndex].arrayCheck);this.$emit("propData", checkData);}},// 扩展方法-倍数ROI处理// roiDisposeFn() {//   const getPonit = this.Local.get(this.cacheKeyProp);//   const displayType = this.Local.get("displayType");//   const prointArrItem = this.pointerArr[this.currenPointIndex];//   const updatedArray = prointArrItem.arrayCheck.map((item) => {//     if (//       displayType == 2 &&//       item.startsWith("ROI") &&//       !item.includes("倍数")//     ) {//       return item + "倍数";//     } else if (displayType != 2 && item.includes("倍数")) {//       return item.replace("倍数", "");//     }//     return item;//   });//   const labelCheckBoxAll = this.filterCheckbox({//     arrayCheck: updatedArray,//   }).map((item) => item.label);//   if (prointArrItem.arrayCheck !== labelCheckBoxAll) {//     getPonit[this.currenPointIndex].arrayCheck = labelCheckBoxAll;//     this.Local.set(this.cacheKeyProp, getPonit);//     this.pointerArr[this.currenPointIndex].arrayCheck = labelCheckBoxAll;//   }// },// 获取-更新指标getPointData(type) {const getPonit = this.Local.get(this.cacheKeyProp);if (getPonit) {this.pointerArr = getPonit;this.currenIndexNob();const prointArrItem = this.pointerArr[this.currenPointIndex];this.roiDisposeFn();const checkData = this.filterCheckbox(this.pointerArr[this.currenPointIndex],true,prointArrItem.arrayCheck);if (checkData) {this.$emit("propData", checkData);}} else if (!getPonit && type !== "init") {// 如果是空this.Local.remove(this.cacheKeyProp);this.$emit("propData", []);} else {// 如果默认的if (this.defaultArr && type === "init" && this.pointerArr.length <= 0) {const arrs = JSON.parse(JSON.stringify(this.defaultArr));const labelsArray = arrs.map((item) => item.label);this.currenIndexNob();this.pointerArr.push({title: "默认指标",arrayCheck: labelsArray,});const prointArrItem = this.pointerArr[this.currenPointIndex];const checkData = this.filterCheckbox(prointArrItem,true,labelsArray);this.$emit("propData", checkData);}}},// 编辑-删除指标pointIndexHander(item, type) {if (type === "edit") {this.openTitle = "编辑";this.selectedCheckboxes = item.arrayCheck;this.editItem = item;this.dialogVisible = true;} else {const itemToDelete = this.pointerArr.find((ls) => ls.title === item.title);if (itemToDelete) {const indexToDelete = this.pointerArr.indexOf(itemToDelete);if (indexToDelete > -1) {this.pointerArr.splice(indexToDelete, 1);this.Local.set(this.cacheKeyProp, this.pointerArr);// 删除当前行更新,否则不更新if (indexToDelete === this.currenPointIndex) {this.getPointData();} else {this.currenIndexNob();}}}}},// 全选当前指标handleSelectAll(item) {item.checkboxes.forEach((checkbox) => {const checkboxIndex = this.selectedCheckboxes.indexOf(checkbox.label);if (item.selectedAll && checkboxIndex === -1) {this.selectedCheckboxes.push(checkbox.label);} else if (!item.selectedAll && checkboxIndex !== -1) {this.selectedCheckboxes.splice(checkboxIndex, 1);}});},// 全选状态判断isIndeterminate(item) {const selectedLabels = this.selectedCheckboxes;const allLabels = item.checkboxes.map((checkbox) => checkbox.label);const selectedCount = selectedLabels.filter((label) =>allLabels.includes(label)).length;item.selectedAll = selectedCount === allLabels.length;return selectedCount > 0 && selectedCount < allLabels.length;},// 指定索引currenIndexNob() {const getIndexObj = this.storeSetCurrentIndex("get");this.currenPointIndex = getIndexObj[this.currenKey];if (!this.currenPointIndex) {this.currenPointIndex = 0;} else {if (this.pointerArr.length <= 1) {this.currenPointIndex = 0;} else {this.currenPointIndex = getIndexObj[this.currenKey] || 0;}}this.storeSetCurrentIndex("set");},// 添加指标addPointerSubmit() {this.dialogVisible = false;this.emitArr = this.filterCheckbox({arrayCheck: this.selectedCheckboxes,},true,this.selectedCheckboxes);const dataItem = {title: "",arrayCheck: this.selectedCheckboxes,};if (this.openTitle === "添加") {this.$prompt("请输入指标名", "提示", {confirmButtonText: "确定",cancelButtonText: "取消",closeOnClickModal: false,inputValidator: (value) => {if (!value) {return "不能为空!";}},beforeClose: (action, instance, done) => {if (action === "confirm") {const isDuplicate = this.pointerArr.some((item) => item.title === instance.inputValue);if (isDuplicate) {this.$message.error("已存在相同指标名");return false;} else {done();}} else {done();}},}).then(({ value }) => {dataItem.title = value;if (this.pointerArr && Array.isArray(this.pointerArr)) {const updatedData = [...this.pointerArr, dataItem];this.Local.set(this.cacheKeyProp, updatedData);} else {const newData = [dataItem];this.Local.set(this.cacheKeyProp, newData);}this.$emit("propData", this.emitArr);this.getPointData();});} else {const editIndex = this.pointerArr.findIndex((item) => item.title === this.editItem.title);if (editIndex !== -1) {(dataItem.title = this.editItem.title),(this.pointerArr[editIndex] = dataItem);this.Local.set(this.cacheKeyProp, this.pointerArr);}this.$emit("propData", this.emitArr);}},// 新增自定义指标addUserDefinedIndicators() {this.openTitle = "添加";this.selectedCheckboxes = [];this.dialogVisible = true;},},
};
</script><style lang="scss" scoped>
.indicator-all-box {float: right;margin-right: 5px;
}
.indicator-list {ul {padding: 0;li {padding: 10px 0;border-bottom: 1px solid #e1e1e1;}}.flex-indicator-item {display: flex;justify-content: space-between;padding: 0 15px;.right-indicator {i {padding-left: 10px;display: inline-block;font-size: 16px;cursor: pointer;}}}
}.box-card {margin-bottom: 10px;
}.el-divider {margin: 10px 0;
}.rihgt-all-check {float: right;padding: 3px 0;
}.el-checkbox {margin-bottom: 10px;
}.customize-indicator-data-container {display: flex;min-height: 60vh;.card-checkbox-content-left {max-height: 600px;overflow-y: scroll;flex: 1;}.sort-view-dx {width: 300px;margin-left: 15px;.sort-row {height: 60vh;overflow-y: scroll;p {background-color: #fdfdfd;height: 32px;line-height: 32px;border: 1px solid #ebebeb;padding: 0 10px;margin: 5px 0;&:hover {cursor: move;}}}}
}.active-li {background-color: #efefef;
}
</style>

上方注释扩展方法说明:
比如你上方有筛选条件需要关联切换的,拿我自己的例子,见顶部ROI区域
他筛选条件有一个ROI、ROI倍数的筛选。然后字段展示是ROI123456…等,是循环的数量。切换ROI倍数的时候 表头原有的ROI需要变成ROI倍数 以及prop也一样要变化。
列如顶部ROI附加复选框的方法:
在这里插入图片描述

this.cardDataProp[1] = {title: "ROI指标",checkboxes: Array.from({ length: this.queryParams.displayNum },(_, i) => ({prop: `roi${i + 1}${this.queryParams.displayType == 1 ? "_rate" : ""}`,label: `ROI${i + 1}${this.queryParams.displayType == 2 ? "倍数" : ""}`,})),
};筛选条件选择切换displayType类型后调用 this.$refs.rois.getPointData("init"); 刷新表头

以上根据了选项displayType变化label和prop 但又是属于同一个label表头 只是字段不一样的 或者要用循环的,可采用这种方式,扩展方法自己研究…估计没有其他人需要用这个扩展的,就注释了,不用的可以删掉!

在这里插入图片描述
感谢你的阅读,如对你有帮助请收藏+关注!
只分享干货实战精品从不啰嗦!!!
如某处不对请留言评论,欢迎指正~
博主可收徒、常玩QQ飞车,可一起来玩玩鸭~

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

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

相关文章

kafka查看消息两种方式(命令行和软件)+另附发送消息方式

1、命令行方式 ①找到kafka安装文件夹 ②执行命令 #指定offset为指定时间作为消息起始位置 kafka-consumer-groups.sh \ --bootstrap-server 20.2.246.116:9092 \ --group group_1 \ --topic lanxin_qiao \ --reset-offsets \ --to-datetime 2023-07-19T01:00:00.000 \ -exe…

MySQL-Linux安装

JDK安装&#xff08;linux版&#xff09; CentOS7环境&#xff1a; jdk下载地址huaweicloud.com 创建目录&#xff1a; mkdir /opt/jdk通过 ftp 客户端 上传 jdk压缩包&#xff08;linux版本&#xff09;到 1中目录进入目录&#xff1a;cd /opt/jdk解压&#xff1a;tar -zxv…

一篇了解电感的使用

一、电感理论基础 1.电感的定义 当电流通过线圈后&#xff0c;会产生磁场&#xff0c;磁感线穿过线圈&#xff0c;产生的磁通量与电流 i有如下关系&#xff1a; 将漆包线、纱包线或塑皮线等在绝缘骨架或磁心、铁心上绕制而成的器件&#xff0c;当线圈通过电流后&#xff0c;在…

linux应用程序需要编写的脚本

每一个程序都按照下面的要求进行脚本编写 多个应用之间联合安装采用编写外围脚本&#xff0c;依次调用多个应用的脚本的方式实现

MyBatis Oracle 批量插入数据

MyBatis Oracle 批量插入数据 1.需求描述2.实现方案2.1 循环 insert 插入2.2 insert all 插入2.3 insert union all 插入 3.分析总结 系统&#xff1a;Win10 JDK&#xff1a;1.8.0_351 IDEA&#xff1a;2022.3.3 1.需求描述 在一次项目中实施过程中&#xff0c;后台需要将地区…

给一篇word注音可不可以只要拼音不要汉字 word中如何只保留拼音不要汉字

word中如何只保留拼音不要汉字&#xff0c;如果你想要只保留拼音而去除汉字&#xff0c;可以通过一系列步骤来实现。以下是一个详细的教程&#xff0c;帮助你完成这个任务。 首先&#xff0c;确保你的电脑已经安装了“汇帮注音大师”软件。如果没有&#xff0c;你需要安装一下…

云计算 3月8号 (wordpress的搭建)

项目wordpress 实验目的&#xff1a; 熟悉yum和编译安装操作 锻炼关联性思维&#xff0c;便于以后做项目 nginx 编译安装 1、安装源码包 [rootlinux-server ~]# yum -y install gcc make zlib-devel pcre pcre-devel openssl-devel [rootlinux-server ~]# wget http://nginx.…

安卓7原生相机切到视频崩溃

目录 1、查看日志 2、分析日志、提取重点 3、寻找解决方法 author daisy.skye的博客_CSDN博客-嵌入式,Qt,Linux领域博主 daisy.skye_嵌入式,Linux,Qt-CSDN博客daisy.skye擅长嵌入式,Linux,Qt,等方面的知识https://blog.csdn.net/qq_40715266?typeblog 1、查看日志 由于安…

vscode setting.json 全局设置 工作区设置 位置 优先级

vscode中setting.json有两种配置权限 一、全局配置&#xff1a;setting.json文件位于C:\Users\Administrator\AppData\Roaming\Code\User\settings.json 二、工作区配置&#xff1a;setting.json文件位于工作区的.vscode\settings.json 当两种配置同时存在时&#xff0c;工作区…

生成对抗网络 (GAN)

生成对抗网络&#xff08;Generative Adversarial Networks&#xff0c;GAN&#xff09;是由Ian Goodfellow等人在2014年提出的一种深度学习模型。GAN由两部分组成&#xff1a;一个生成器&#xff08;Generator&#xff09;和一个判别器&#xff08;Discriminator&#xff09;&…

一文学会搭建 cli 脚手架工具

文章目录 设置工具命令package.json bin 字段注释&#xff1a;#!/usr/bin/env node设置环境变量 接收命令选项参数process 实现commander 命令行交互&#xff1a;inquirer下载项目模板&#xff1a;download-git-repo执行额外命令&#xff1a;自动安装依赖child_processexeca 体…

基于卷积神经网络的野外可食用植物分类系统

温馨提示&#xff1a;文末有 CSDN 平台官方提供的学长 QQ 名片 :) 1. 项目简介 本文详细探讨了一基于深度学习的可食用植物图像识别系统。采用TensorFlow和Keras框架&#xff0c;利用卷积神经网络&#xff08;CNN&#xff09;进行模型训练和预测&#xff0c;并引入迁移学习模型…

Mac测试环境搭建

1 下载pycharm 下载地址&#xff1a;PyCharm&#xff1a;JetBrains 出品的用于数据科学和 Web 开发的 Python IDE 2 安装python3.6.8 下载地址&#xff1a;Index of /ftp/python/3.6.8/ 安装后提示错误 换一种方式&#xff1a;用conda 下载地址&#xff1a;Free Download | …

ipad协议847最新版

ipad协议其实就是模拟ipad端微信的人工操作&#xff0c;跟微信服务器通信。协议的关键点主要是PB协议、mmtls、07加密算法、rqt算法、aes加密、rsa加密等&#xff0c;只要把这些点拿下&#xff0c;就可以模拟官方微信的所有功能了&#xff0c;还可以模拟android、pc、mac端的登…

数字音频工作站(DAW)fl studio 21 for mac 21.2.3.3586中文版图文安装教程

随着音乐制作行业的不断发展&#xff0c;越来越多的音乐人和制作人开始使用数字音频工作站&#xff08;DAW&#xff09;来创作和制作音乐。其中FL Studio 21是一个备受欢迎的选择&#xff0c;因为它提供了强大的音乐制作工具和易于使用的界面。 然而&#xff0c;一直以来&…

2024 RubyMine 激活,分享几个RubyMine 激活的方案

文章目录 RubyMine 公司简介我这边使用RubyMine 的理由RubyMine 2023.3 最新变化AI Assistant 正式版对 AI 生成名称建议的支持改进了 Ruby 上下文单元测试生成 RailsRails 应用程序和引擎的自定义路径Rails 路径的自动导入对存储在默认位置之外的模型、控制器和邮件器的代码洞…

Python数据分析实验一:Python数据采集与存储

目录 一、实验目的与要求二、实验过程三、主要程序清单和运行结果1、爬取 “中国南海网” 站点上的相关信息2、爬取天气网站上的北京的历史天气信息 四、程序运行结果五、实验体会 一、实验目的与要求 1、目的&#xff1a; 理解抓取网页数据的一般处理过程&#xff1b;熟悉应用…

基于Redis自增实现全局ID生成器(详解)

本博客为个人学习笔记&#xff0c;学习网站与详细见&#xff1a;黑马程序员Redis入门到实战 P48 - P49 目录 全局ID生成器介绍 基于Redis自增实现全局ID 实现代码 全局ID生成器介绍 背景介绍 当用户在抢购商品时&#xff0c;就会生成订单并保存到数据库的某一张表中&#…

day12_SpringCloud(Gateway,Nacos配置中心,Sentinel组件)

文章目录 1 Gateway组件1.1 Gateway简介1.2 Gateway入门1.3 网关路由流程图1.4 路由工厂1.5 过滤器1.5.1 过滤器简介1.5.2 内置过滤器1.5.3 路由过滤器1.5.4 默认过滤器1.5.5 全局过滤器1.5.6 过滤器执行顺序 2 Nacos配置中心2.1 统一配置管理2.2 Nacos入门2.2.1 Nacos中添加配…

多线程多进程

秋招面试的java八股文知识点补充以及iot 这里有一点阅读补充 线程和进程区别 什么是进程? 进程 (Process) 是计算机中的一个独立执行单元&#xff0c;是操作系统资源分配的基本单位每个进程有各自独立的内存空间和资源&#xff0c;它们之间相互独立&#xff0c;相互之间不能…