Vue2 + Element UI 封装 Table 递归多层级列表头动态

1、在 components 中创建 HeaderTable 文件夹,在创建 ColumnItem.vue 和 index.vue。

如下:

2、index.vue 代码内容,如下:

<template>
  <div>
    <el-table
      :data="dataTableData"
      style="width: 100%"
      max-height="700"
      :cell-style="{
        'border-right': '1px solid #C4C6CB',
        'border-bottom': '1px solid #C4C6CB',
      }"
      :header-cell-style="{
        'background-color': '#f8f8f9',
        'border-right': '1px solid #C4C6CB',
        'border-bottom': '1px solid #C4C6CB',
      }"
    >
      <el-table-column
        prop="date"
        label="时间"
        width="150"
        align="center"
        v-if="flag"
        >
      </el-table-column>
      <!-- 递归组件 -->
      <template v-if="flag">
        <ColumnItem
          v-for="(item,index) in dataHeaders"
          :key="item.label"
          :col="item"
          :maxLength="maxLength"
          >
        </ColumnItem>
      </template>
      <el-table-column
        prop=""
        width="150"
        align="center"
        v-if="showAddBut && flag"
        >
        <template slot="header" slot-scope="scope">
          <i class="el-icon-plus" @click="getAdd({})"/>
        </template>
      </el-table-column>
    </el-table>
    
    <el-drawer
      :title="title"
      :visible.sync="drawer"
      :size="350"
      direction="ltr"
      :before-close="handleClose">
      <el-form
        :inline="true"
        :model="column"
        label-position="right"
        label-width="auto"
        ref="ruleFormRef"
        :rules="rules"
        class="demo-form-inline form-padding"
      >
        <el-form-item label="名称" prop="label">
          <el-input v-model="column.label" placeholder="请输入名称" clearable />
        </el-form-item>
        <el-form-item label="宽度" prop="width">
          <el-input
            v-model="column.width"
            type="number"
            placeholder="请输入列宽度(不填为自适应)"
            clearable
          />
        </el-form-item>
        <el-form-item label="对齐方式" prop="align">
          <el-select
            v-model="column.align"
            placeholder="请选择对齐方式"
            clearable
          >
            <el-option label="左对齐" value="left" />
            <el-option label="居中对齐" value="center" />
            <el-option label="右对齐" value="right" />
          </el-select>
        </el-form-item>
        <el-form-item label="映射变量" prop="prop">
          <el-cascader
            v-model="column.prop"
            :options="options"
            :props="optionProps"
            @change="handleChange">
          </el-cascader>
        </el-form-item>
      </el-form>
      <div class="drawer-footer">
        <el-button @click="handleClose">取消</el-button>
        <el-button type="primary" :loading="loading" @click="handleSubmit">
          {{ submitText }}
        </el-button>
      </div>
    </el-drawer>
  </div>
</template>

<script>
import { EventBus } from "../../utils/event-bus.js";
import ColumnItem from './ColumnItem.vue';
import { apiEditHeaderTable, apiGetList, apiGetTreeselect } from "@/api/headerTable/index.js";
export default {
  name: 'CustomElTable',
  components: {
    ColumnItem
  },
  props: {
    // 表头数据
    headers: {
      type: Array,
      required: true
    },
    // 表格列表数据
    tableData: {
      type: Array,
      required: true
    },
    // 区分哪个页面下的列表
    tablekey: {
      type: String,
      required: true
    },
    // 限制新增最大列
    maxLength: {
      type: Number,
      required: false
    },
  },
  data() {
    return {
      dataHeaders: this.headers,
      dataTableData: this.tableData,
      dataNum: this.num,
      flag: true, // 为了更新子组件的状态
      drawer: false,
      loading: false,
      column: {
        label: "",
        width: "",
        align: "center",
        prop: "",
      },
      optionProps: { 
        expandTrigger: 'click',
        value: "id",
        label: "label",
        children: "children",
      },
      copyCol: {},
      rules: {
        label: [{ required: true, message: "请输入名称", trigger: "blur" }],
        align: [{ required: true, message: "请选择对齐方式", trigger: "blur" }],
      },
      title: '新增列',
      submitText: '新增',
      options: [],
    }
  },
  computed: {
    showAddBut() {
      return this.$store.state.common.headerEditBut
    }
  },
  watch: {
    headers: {
      handler(newValue,oldValue) {
        this.dataHeaders = newValue;
      }
    },
  },
  methods: {
    // 删除
    getDelete(col) {
      this.$confirm('是否删除?', '提示', {
        confirmButtonText: '确定',
        cancelButtonText: '取消',
        type: 'warning'
      }).then(_ => {
          let newHeaders = this.getRecursion(this.dataHeaders,col);
          console.log("处理完数据的", newHeaders);
          this.dataHeaders = newHeaders;
          // 数据更新,更新子组件状态
          this.flag = false;
          this.$nextTick(() => {
            this.flag = true;
          })
          EventBus.$emit("getHeaders",this.headers)
        }).catch(() => {});
    },
    // 递归删除
    getRecursion(arr,col) {
      arr.map((item,index) => {
        if(item.label === col.label && item.deep === col.deep) {
          arr.splice(index,1);
          return;
        }
        if(item.children && item.children.length > 0) {
          this.getRecursion(item.children,col)
        }
      })
      return arr;
    },
    // 新增
    getAdd(col) {
      this.drawer = true;
      this.title = '新增列';
      this.submitText = '新增';
      this.column.label = '新增列';
      this.copyCol = col;
    },
    // 编辑
    getEdit(col) {
      this.drawer = true;
      this.title = '编辑列',
      this.submitText = '保存',
      this.column.label = col.label;
      this.copyCol = col;
    },
    // 编辑,新增里的取消
    handleClose() {
      this.$confirm('内容未保存,确认关闭?','提示',{
        confirmButtonText: '确定',
        cancelButtonText: '取消',
        type: 'warning'
      })
        .then(_ => {
          this.drawer = false;
        })
        .catch(_ => {});
    },
    // 编辑,新增里的保存
    handleSubmit() {
      let col = this.copyCol
      this.loading = true;
      let item = {
        label: this.column.label,
        prop: "",
        width: this.column.width,
        align: this.column.align,
        deep: 1,
        selected: false,
        children: []
      }
      if(this.title === '新增列') {
        if(!Object.keys(col).length == 0){
          item.deep = col.deep + 1;
          col.children.push(item);
        }else {
          this.dataHeaders.push(item);
        }
      }else {
        this.flag = false;
        this.$nextTick(() => {
          this.flag = true;
          col.label = this.column.label;
          col.width = this.column.width;
          col.align = this.column.align;
          col.prop = this.column.prop;
        })
      }
      this.loading = false;
      this.drawer = false;
      console.log("dataHeaders", this.dataHeaders);
      // this.handleApi();
    },
    // 请求接口,保存表头数据
    handleApi() {
      let data = {
        tableKey: this.tablekey,
        tableJson: JSON.stringify(this.dataHeaders)
      }
      apiEditHeaderTable(data).then(() => {
        this.loading = false;
        this.drawer = false;
        this.$message({
          message: '编辑成功',
          type: 'success'
        });
      }).catch(() => {
        this.loading = false;
        this.drawer = false;
        this.$message({
          message: '编辑失败',
          type: 'error'
        });
      })
    },
    handlePreserve() {
      console.log("保存");
      this.handleApi();
    },
    //
    handleChange(val) {
      let aa = val[val.length-1]
      apiGetList({
        deptId: aa
      }).then(res => {
        console.log(">>>>",res);
      })
    },
    handleList() {
      let data = {}
      apiGetTreeselect(data).then((res) => {
        this.options = res.data;
      })
    }
  },
  mounted() {
    this.handleList();
    console.log("tablekey",this.tablekey);
    EventBus.$on("getDelete",(col) => {
      this.getDelete(col)
    });
    EventBus.$on("getAdd",(col) => {
      this.getAdd(col)
    });
    EventBus.$on("getEdit",(col) => {
      this.getEdit(col)
    });
    EventBus.$on("handlePreserve",() => {
      this.handlePreserve()
    });
  }
}
</script>

<style lang="scss" scoped>
.scope_p {
  margin: 0;
}
.i_margin {
  margin: 0 10px;
}

.content-main {
  .table-title {
    display: flex;
    justify-content: space-around;
    margin-bottom: 10px;
    > p {
      flex: 1;
      font-weight: bold;
      font-size: 18px;
    }
  }
  .el-input {
    width: 250px;
  }
  .el-select {
    width: 280px;
  }
}
.form-padding {
  padding: 0 20px;
}
.drawer-footer {
  display: flex;
  align-items: center;
  justify-content: center;
}

.header-btn {
  display: flex;
  align-items: center;
  justify-content: center;
  .el-icon-plus {
    margin-left: 10px;
    cursor: pointer;
  }
  .el-icon-edit {
    margin-left: 10px;
    cursor: pointer;
  }
  .el-icon-delete {
    margin-left: 10px;
    cursor: pointer;
  }
}

.selected {
  color: #409eff;
}

.header-scroll::v-deep {
  .el-table__header-wrapper {
    overflow-x: auto;
  }

  /* 修改滚动条样式 */
  ::-webkit-scrollbar {
    height: 8px; /* 设置滚动条宽度 */
  }

  ::-webkit-scrollbar-track {
    background-color: #ffffff; /* 设置滚动条轨道背景色 */
  }

  ::-webkit-scrollbar-thumb {
    background-color: #ececec; /* 设置滚动条滑块颜色 */
    border-radius: 4px; /* 设置滚动条滑块圆角 */
  }

  ::-webkit-scrollbar-thumb:hover {
    background-color: #e2e2e2; /* 设置滚动条滑块鼠标悬停时颜色 */
  }
}
.error_label {
  color: #f56c6c !important;
}
</style>

3、ColumnItem.vue 代码内容,如下:

<template>
  <el-table-column
    :prop="col.prop"
    :width="col.width"
    :align="col.align"
    min-width="150"
    >
    <template slot="header" slot-scope="scope">
        <p class="scope_p">{{col.label}}</p>
        <div v-if="showAddBut">
          <i class="el-icon-plus" v-if="col.deep < maxLength" @click="Add(col)"/>
          <i class="el-icon-edit-outline i_margin" @click="Edit(col)"/>
          <i class="el-icon-delete" @click="Delete(col)"/>
        </div>
    </template>
    <!-- 遍历递归 -->
    <template v-for="(item,index) in col.children">
      <ColumnItem v-if="item.children" :col="item" :key="item.label" :maxLength="maxLength"/>
    </template>
  </el-table-column>
</template>

<script>
import { EventBus } from "../../utils/event-bus.js";
export default {
  name: "ColumnItem",
  props: {
    col: {
      type: Object,
      required: true
    },
    maxLength: {
      type: Number,
      required: false
    },
  },
  methods: {
    Add(col) {
      EventBus.$emit("getAdd",col)
    },
    Edit(col) {
      EventBus.$emit("getEdit",col)
    },
    Delete(col) {
      console.log(col);
      EventBus.$emit("getDelete",col)
    }
  },
  computed: {
    showAddBut() {
      return this.$store.state.common.headerEditBut
    }
  },
  mounted() {
  },
}
</script>

<style lang="scss" scoped>
.scope_p {
  margin: 0;
}
i {
  cursor: pointer;
}
.i_margin {
  margin: 0 10px;
}
</style>

4、在 .vue 文件中使用和数据,如下: 

<HeaderTable :headers="headers" :tableData="tableData" :tablekey="tablekey" :maxLength="4"/>
data() {
  return {
     headers: [
        {
          label: "1",
          prop: "name",
          width: "150",
          align: "center",
          deep: 1,
          selected: false,
          children: [
            {
              label: "1-1",
              prop: "",
              width: "",
              align: "center",
              deep: 2,
              selected: false,
              children: [
                {
                  label: "1-1-1",
                  prop: "",
                  width: "",
                  align: "center",
                  deep: 3,
                  selected: false,
                  children: []
                }
              ]
            },
            {
              label: "1-2",
              prop: "",
              width: "",
              align: "center",
              deep: 2,
              selected: false,
              children: []
            }
          ]
        },
        {
          label: "2",
          prop: "name",
          width: "150",
          align: "center",
          deep: 1,
          selected: false,
          children: [
            {
              label: "2-1",
              prop: "",
              width: "",
              align: "center",
              deep: 2,
              selected: false,
              children: [
                {
                  label: "2-1-1",
                  prop: "",
                  width: "",
                  align: "center",
                  deep: 3,
                  selected: false,
                  children: []
                },
                {
                  label: "2-1-2",
                  prop: "",
                  width: "",
                  align: "center",
                  deep: 3,
                  selected: false,
                  children: []
                }
              ]
            },
            {
              label: "2-2",
              prop: "",
              width: "",
              align: "center",
              deep: 2,
              selected: false,
              children: []
            }
          ]
        }
      ],
      tableData: [
      {
        date: '2016-05-03',
      }, {
        date: '2016-05-02',
      }, {
        date: '2016-05-04',
      }, {
        date: '2016-05-01',
      }, {
        date: '2016-05-08',
      }, {
        date: '2016-05-06',
      }, {
        date: '2016-05-07', 
      }
      ],
  }
}

5、效果图,如下:

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

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

相关文章

TXT文档拆分、合并、添加内容,修改内容、删除内容——首助编辑高手软件一招解决

下面这个TXT文档里面是一篇长篇小说&#xff0c;大家都知道一般小说文字内容是比较大的一个文件呢&#xff0c;想要拆分&#xff0c;拆分肯定是有方法呢&#xff0c;比如比较重统的方法手动一章一章复制出来&#xff0c;粘贴到另一个文档里面去粘贴&#xff0c;手动操作是不是很…

从人工向智能化转变,企业级指标管理平台建设实战

随着大数据技术和人工智能的发展&#xff0c;企业逐渐意识到构建一个集中化的指标管理平台的必要性。这样的平台旨在解决几个核心问题&#xff1a;首先&#xff0c;确保所有部门都能通过统一的入口提交指标需求&#xff0c;实现需求的透明化管理&#xff1b;其次&#xff0c;建…

计算机视觉与模式识别实验1-1 图像的直方图平衡

文章目录 &#x1f9e1;&#x1f9e1;实验流程&#x1f9e1;&#x1f9e1;1.读入图像‘rice.png’&#xff0c;在一个窗口中显示灰度级n64&#xff0c;128和256的图像直方图。2.调解图像灰度范围&#xff0c;观察变换后的图像及其直方图的变化。3.分别对图像‘pout.tif’和‘ti…

神奇的现象——body背景

当我们设置 body 背景时&#xff0c;有一个神奇的现象。 当 HTML 元素有背景时 html {background-color: lightblue }body {width: 100px;height: 100px;border: 3px dashed #f66e6e;background-color: thistle; } 显示如下&#xff1a; 注意&#xff1a; 由于 body 设置了…

CraftCMS ConditionsController.php 代码执行漏洞(CVE-2023-41892)

0x01 产品简介 Craft CMS是一个开源的内容管理系统,它专注于用户友好的内容创建过程,逻辑清晰明了,是一个高度自由,高度自定义设计的平台吗,可以用来创建个人或企业网站也可以搭建企业级电子商务系统。 0x02 漏洞概述 Craft CMS在4.4.15版本之前存在远程代码执行漏洞,…

安防综合管理系统EasyCVR平台GA/T1400视图库:基于XML的消息体格式

GA/T 1400标准的应用范围广泛&#xff0c;涵盖了公安系统的视频图像信息应用系统&#xff0c;如警务综合平台、治安防控系统、交通管理系统等。在视频监控系统中&#xff0c;GA/T 1400公安视图库的对接是实现视频图像信息传输、处理和管理的重要环节。 以视频汇聚EasyCVR视频监…

Linux|虚拟机|Windows 11 家庭版的Hyper虚拟机服务开启

前言&#xff1a; Windows11的版本是比较多的&#xff0c;但有的时候笔记本预装的可能是家庭版&#xff0c;而家庭版的Windows通常是不支持虚拟机的&#xff0c;也就是说Hyper服务根本就看不到 Windows的程序和功能大体如下&#xff1a; &#x1f197;&#xff0c;那么如何开…

运维怎么转行网络安全?

经常有人问我&#xff1a;干网工、干运维多年遇瓶颈&#xff0c;想学点新技术给自己涨涨“身价”&#xff0c;应该怎么选择&#xff1f; 聪明人早已经用脚投票&#xff1a;近年来&#xff0c;越来越多运维的朋友寻找新的职业发展机会&#xff0c;将目光聚焦到了网络安全产业。…

反激电源压敏电阻设计

压敏电阻的作用&#xff1a;浪涌防护。在电源出现浪涌冲击时&#xff0c;保护核心器件不受到损坏。其实类似于稳压二极管 瞬间的瞬态波 1 压敏电压 单位是&#xff0c;虽然压敏电阻可以吸收很大的浪涌能量&#xff0c;但是不能承受mA以上的持续电流。压敏电压计算公式 2 通流容…

免费的VMware ?就是它了!【送源码】

在 Docker 没有出来之前&#xff0c;很多项目的的部署方案是使用虚拟机&#xff0c;在一台服务器上创建好几个虚机出来&#xff0c;配置一下网络&#xff0c;就可以把一台服务器当做多个服务器用了。 而作为开发者来说&#xff0c;我们经常碰到需要使用不同操作系统的需求&…

HTML+CSS 响应式侧边栏菜单

效果演示 实现了一个响应式的侧边栏菜单,当用户点击菜单按钮时,菜单会从左侧滑出,同时页面内容会向右移动,展示菜单选项。菜单选项包括一个头像和用户名,以及其他的菜单项,当用户将鼠标悬停在菜单项上时,菜单项会高亮显示。这段代码使用了CSS的flex布局和过渡效果,以及…

【Linux】线程ID

大致草稿—————————— 思维导图 学习目标 一、线程ID的理解 1.1 引出对tid的理解 我们先来创建一个线程复习一下线程的函数&#xff1a; pthread_t tid; // 创建一个线程 pthread_create(&tid, nullptr, threadrun, (void*)"thread-1"); // 打印出…

Nacos-SpringBoot-配置中心

Nacos配置中心 前情回顾 上一章呢 了解并且学习了Nacos服务注册与发现 在一系列破防中走了出来Nacos服务注册完成https://blog.csdn.net/m0_68711597/article/details/139265244?spm1001.2014.3001.5502 本以为接下来会一帆风顺 一马平川 没想刚出坑 又入坑 Nacos的配置…

副业赚钱的路子有哪些?推荐15个靠谱副业和赚钱软件给你做

在这个工资永远追不上物价的时代&#xff0c;你是否也感到焦虑&#xff1f;是否想过在工作之余&#xff0c;开辟一条赚钱的副业之路&#xff1f;别急&#xff0c;今天就为你揭开副业赚钱的神秘面纱&#xff0c;带你一探究竟&#xff01; 一、宅富社任务&#xff1a;每日小任务&…

华为官网的自助申诉

代码&#xff1a;如下 <!DOCTYPE html> <html lang"en"> <head> <meta charset"UTF-8"> <meta name"viewport" content"widthdevice-width, initial-scale1.0"> <title>Document</title> …

Python自动化办公2.0 即将发布

第一节课&#xff1a;数据整理与清洗 第二节课&#xff1a;数据筛选、过滤与排序 第三节课&#xff1a;高级数据处理技巧 第四节课&#xff1a;数据可视化与实践案例 第五节课&#xff1a;统计分析与报表 第六节&#xff1a;常见的Excel报表 与下方的课程形成知识体系&…

【教学类-60-01】彩色消划掉01(四个数字,X*Y宫格)

背景需求&#xff1a; &#x1f9e0;思维启蒙 - 小红书注意力训练小分享-彩色划消 训练孩子的视觉辨别能力、视觉稳定性、注意力分配额能力&#x1f440; 一起来试试吧&#xff5e; #分享学习方法 #注意力训练 #专注力训练#天津 #亲子时光 #孩子成长 #思维启蒙 #数学思维启蒙 …

天工大模型:中国AI搜索巨头如何一骑绝尘?

5月&#xff0c;全球大模型领域的竞争&#xff0c;或可用“你方唱罢我方登场”来形容。 谷歌、腾讯、阿里、字节跳动等业界巨擘纷纷亮出大招。有的凭借技术底蕴实现全面升级&#xff0c;有的慷慨将大模型开源免费&#xff0c;更有甚者通过价格战掀起狂风巨浪&#xff0c;整个市…

4月啤酒品类线上销售数据分析

近期&#xff0c;中国啤酒行业正处于一个重要的转型期。首先&#xff0c;消费者对高品质啤酒的需求不断增加&#xff0c;这推动了行业向高端化、场景化和社交化的方向发展。精酿啤酒作为这一趋势的代表&#xff0c;其发展势头强劲&#xff0c;不仅满足了消费者对品质化、个性化…

解决get请求入参@NotNull验证不生效问题

get请求NotNull验证不生效 在这里插入图片描述 解决办法 在该方法的controller类上加Validated 解决get请求入参NotNull验证不生效问题