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

news2025/1/10 16:34:28

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、效果图,如下:

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

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

相关文章

神经网络与深度学习——第3章 线性模型

本文讨论的内容参考自《神经网络与深度学习》https://nndl.github.io/ 第3章 线性模型 线性模型 线性模型&#xff08;Linear Model&#xff09;是机器学习中应用最广泛的模型&#xff0c;指通过样本特征的线性组合来进行预测的模型&#xff0c;给定一个 D D D维样本 x [ x …

定时器与PWM的LED控制

目录 一、基础概念定时器定时器类型定时器特性 PWM定义占空比原理 二、实验1.LED周期性亮灭定时器TIM2配置GPIO引脚设置工程相关参数配置Keil编写程序 2.LED呼吸灯(PWM)呼吸灯原理Keil编写程序Keil虚拟示波器&#xff0c;观察 PWM输出波形设置点击setup&#xff0c;并设置观察引…

贪心算法拓展(反悔贪心)

相信大家对贪心算法已经见怪不怪了&#xff0c;但是一旦我们的决策条件会随着我们的步骤变化&#xff0c;我们该怎么办呢&#xff1f;有没有什么方法可以反悔呢&#xff1f; 今天就来讲可以后悔的贪心算法&#xff0c;反悔贪心。 https://www.luogu.com.cn/problem/CF865Dhttp…

[图的搜索]5.图解狄克斯特拉算法及其代码演示

狄克斯特拉算法 与前面提到的贝尔曼-福特算法类似&#xff0c;狄克斯特拉&#xff08;Dijkstra&#xff09;算法也是求解最短路径问题的算法&#xff0c;使用它可以求得从起点到终点的路径中权重总和最小的那条路径路径。 图解 01 这里我们设A为起点、G为终点&#xff0c;来讲…

“揭秘乐园通行证:Spring JWT的魔法之旅

嗨&#xff0c;我将带你深入了解如何利用JWT打造一个既安全又高效的网络乐园。从基础概念到实战技巧&#xff0c;再到安全策略&#xff0c;每一步都充满惊喜。你将学会如何为乐园设置无状态的门票系统&#xff0c;如何通过RBAC和ABAC确保游客安全&#xff0c;以及如何在微服务架…

统计信号处理-匹配滤波器实现与验证(matlab仿真)

什么是匹配滤波器 匹配滤波器是一种信号处理技术&#xff0c;它用于从噪声中提取信号&#xff0c;特别是在信号与噪声比率较低的情况下。匹配滤波器之所以存在&#xff0c;是因为它在信号检测和估计方面具有几个关键的优势&#xff1a; 最大化信噪比&#xff1a;匹配滤波器设计…

数字化校园建设让学习更加广阔

校园构建数字化校园的亮点是什么&#xff1f;校园以智能服务、才智办理、数字讲堂为中心内容的智慧校园建造&#xff0c;不只使师生作业和日子更高效&#xff0c;并且使他们有更多的时刻投入到智能教育和智能学习中去&#xff0c;进步教育质量&#xff0c;使学生走出校门时紧跟…

项目管理主要文档介绍

1、商业论证&#xff1a;一般由项目发起人创建&#xff0c;用于论证项目是否对组织有财务方面的收益。商业论证创建于项日开始之前&#xff0c;用于判断项目是否需要被开展。 2、项目章程&#xff1a;一般由项日经理创建,并由发起入和关键相关力提供输人&#xff0c;最后经项目…

MATLAB函数模块光显示zeros/poles怎么办?

出现下面这种图了怎么办&#xff1f;是做错了吗&#xff1f; 这种图就是它显示不完整了&#xff0c;把它拉大点就可以完全显示了。

【机器学习】深入探索机器学习:利用机器学习探索股票价格预测的新路径

❀机器学习 &#x1f4d2;1. 引言&#x1f4d2;2. 多种机器学习算法的应用&#x1f4d2;3. 机器学习在股票价格预测中的应用现状&#x1f389;数据收集与预处理&#x1f389;模型构建与训练&#x1f308;模型评估与预测&#x1f31e;模型评估&#x1f319;模型预测⭐注意事项 &…

检定记录内容解析:非红外二氧化硫气体检测仪的维护与验证

在工业生产与环境保护中&#xff0c;二氧化硫作为一种常见的有害气体&#xff0c;其浓度的监测和控制显得尤为重要。 非红外二氧化硫气体检测仪以其独特的检测原理和高灵敏度&#xff0c;在二氧化硫监测领域发挥着不可或缺的作用。 在这篇文章中&#xff0c;佰德将详细介绍非…

【原创】springboot+mysql校园通讯录管理系统设计与实现

个人主页&#xff1a;程序猿小小杨 个人简介&#xff1a;从事开发多年&#xff0c;Java、Php、Python、前端开发均有涉猎 博客内容&#xff1a;Java项目实战、项目演示、技术分享 文末有作者名片&#xff0c;希望和大家一起共同进步&#xff0c;你只管努力&#xff0c;剩下的交…

MongoDB CRUD操作:插入文档

MongoDB CRUD操作&#xff1a;插入文档 文章目录 MongoDB CRUD操作&#xff1a;插入文档使用MongoDB Atlas UI插入文档插入单个文档插入多个文档插入行为自动创建集合_id字段原子性写确认 在MongoDB中插入文档的集中方式&#xff1a; 使用编程语言提供的驱动程序&#xff0c;在…

【第七节】C++的STL基本使用

目录 前言 一、STL简介 1.1 STL基本概念 1.2 STL六大组件 1.3 STL优点 二、STL三大组件 2.1 容器 2.2 算法 2.3 迭代器 三、STL常见的容器 3.1 string容器 3.1.1 string容器基本概念 3.1.2 string容器的常用操作 3.1.2.1 string 构造函数 3.1.2.2 string 基本赋…

U盘格式化怎么操作?快来学这4种法

U盘格式化怎么操作&#xff1f;在计算机领域中&#xff0c;格式化通常指对存储设备&#xff08;如硬盘、U盘&#xff09;进行格式化操作&#xff0c;清空其中的数据并重新建立文件系统&#xff0c;以便进行数据存储和管理。 U盘格式化一共有哪些方法&#xff1f;在格式化U盘之…

SJ701-II安全帽耐冲击穿刺测试仪

一、主要用途 主要用于安全帽耐冲击性能和耐穿刺性能试验。 二、仪器特征 整机创新全新结构&#xff0c;并获得国家专利&#xff08;专利号201420182139.8&#xff09; 1、整机结构&#xff1a;首创采用欧标型材组装成型&#xff0c;内藏式线路折叠式结构。结构美观耐用&…

07.爬虫---使用session发送请求

07.使用session发送请求 1.目标网站2.代码实现 1.目标网站 我们以这个网站作为目标网站 http://www.360doc.com/ 注册用户 注册后从登录界面获取到这些信息 2.代码实现 import requestssession requests.Session() url http://www.360doc.com/ajax/login/login.ashx u…

基于SSM框架的手机商城项目

后端: 订单管理 客户管理&#xff1a; 商品管理 类目管理 前端&#xff1a; 首页&#xff1a;

windows 在cmd 使用cd命令无法进入指定目录解决方法

目录预览 一、问题描述二、原因分析三、解决方案四、参考链接 一、问题描述 使用cmd命令想要快速进入某个目录&#xff0c;发现没有跳转&#xff0c;如下&#xff1a; 二、原因分析 cmd 切换目录跨磁盘的话&#xff0c;需要先进行磁盘的转换&#xff0c;也就是要进入到另外一…

码蹄集部分题目(2024OJ赛17期;二分+差分+ST表+单调队列+单调栈)

1&#x1f40b;&#x1f40b;小码哥处理订单&#xff08;钻石&#xff1b;二分差分&#xff09; 时间限制&#xff1a;1秒 占用内存&#xff1a;128M &#x1f41f;题目描述 &#x1f41f;题目思路 【码蹄集进阶塔全题解07】算法基础&#xff1a;二分 MT2070 – MT2079_哔哩…