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

ops/2024/11/17 23:45:23/

 注:此功能改为独立模块直接拿代码就可以使用。

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

如下:

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

<template><div><div class="searchBar"><el-form inline><el-form-item label="时间选择"><el-date-pickerv-model="queryParams.dataTime"type="date"value-format="yyyy-MM-dd"placeholder="选择年份"></el-date-picker></el-form-item><el-form-item><el-button type="primary" @click="getList">搜索</el-button></el-form-item><el-form-item><el-button @click="resetSearchForm">重置</el-button></el-form-item><el-form-item><el-button @click="exportToExcel" type="warning">导出表格</el-button></el-form-item><el-form-item><el-button @click="handleEditBut(1)" type="primary" v-if="!showAddBut">编辑表头</el-button><el-button @click="handleEditBut('preserve')" type="primary" v-if="showAddBut">保存修改</el-button><el-button @click="handleEditBut('quit')" type="danger" v-if="showAddBut">退出修改</el-button></el-form-item></el-form></div><el-tableref="table":data="dataTableData"v-loading="vLoading"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-columnprop="dataTime"label="时间"width="150"align="center"v-if="flag"></el-table-column><!-- 递归组件 --><template v-if="flag"><ColumnItemv-for="(item,index) in dataHeaders":key="item.label":col="item":maxLength="maxLength":showAddBut="showAddBut"></ColumnItem></template><el-table-columnprop=""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="380"direction="ltr":before-close="handleClose"><el-form:inline="true":model="column"label-position="right"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-inputv-model="column.width"type="number"placeholder="请输入列宽度(不填为自适应)"clearable/></el-form-item><el-form-item label="对齐方式" prop="align"><el-selectv-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="映射变量1" prop="prop"><el-cascaderv-model="column.prop":options="options":props="optionProps"@change="handleChange"></el-cascader></el-form-item><el-form-item label="映射变量2" prop="propId" v-show="column.prop"><el-select v-model="column.propId" placeholder="请选择"><el-optionv-for="item in optionsId":key="item.id":label="item.equipName":value="item.id"></el-option></el-select></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 * as XLSX from "xlsx";
import ColumnItem from './ColumnItem.vue';
import { apiEditHeaderTable, apiGetList, apiGetTreeselect, listTableReport, listYearReport, listMonthReport, listDayReport } from "@/api/headerTable/index.js";
export default {name: 'CustomElTable',components: {ColumnItem},props: {// 区分哪个页面下的列表tablekey: {type: String,required: true},// 限制新增最大列maxLength: {type: Number,required: false},},data() {let today = new Date();let todayString = `${today.getFullYear()}-${today.getMonth() + 1}-${today.getDate()}`;return {dataHeaders: [], // 表头数据dataTableData: [], // 表格列表数据// 查询参数queryParams: {dataTime: todayString,ids: [],},vLoading: false,showAddBut: false, // 控制按钮状态flag: true, // 为了更新子组件的状态drawer: false,loading: false,column: {label: "",width: "",align: "center",prop: "",propId: "",},optionProps: { expandTrigger: 'click',value: "id",label: "label",children: "children",},copyCol: {},rules: {label: [{ required: true, message: "请输入名称", trigger: "blur" }],align: [{ required: true, message: "请选择对齐方式", trigger: "blur" }],prop: [{ required: true, message: "请选择映射变量1", trigger: ["blur","change"] }],propId: [{ required: true, message: "请选择映射变量2", trigger: ["blur","change"] }],},title: '新增列',submitText: '新增',options: [],optionsId: [],}},methods: {// 立即执行获取数据 搜索getTableList() {this.vLoading = true;this.showTable = false;listTableReport(this.tablekey).then(res => {if(res.code == 200) {let tableJson = res.data.tableJson ? JSON.parse(res.data.tableJson) : [];console.log("tableJson",tableJson);this.getForRecursion(tableJson);this.dataHeaders = tableJson;this.getList();}})},// 获取列表数据async getList() {console.log("this.queryParams",this.queryParams);if(this.queryParams.ids) {this.vLoading = true;let data = {dataTime: this.queryParams.dataTime,ids: this.queryParams.ids.length == 0 ? [] : this.queryParams.ids}let res = null;if(this.tablekey === 'year') {res = await listYearReport(data);}else if(this.tablekey === 'month') {res = await listMonthReport(data);}else {res = await listDayReport(data);}this.dataTableData = res.data;this.showTable = true;this.vLoading = false;}},// 递归遍历出 propIdgetForRecursion(list) {console.log("list",list);if(list?.length > 0) {list.map(item => {if (item.children && item.children.length > 0) {this.getForRecursion(item.children);}else {this.queryParams.ids.push(item.propId);}})}else {this.queryParams.ids = [];}},// 请求接口,保存表头数据handleSave() {console.log("保存");let data = {tableKey: this.tablekey,tableJson: JSON.stringify(this.dataHeaders)}apiEditHeaderTable(data).then(() => {this.loading = false;this.drawer = false;this.$message({message: '编辑成功',type: 'success'});this.getTableList();}).catch(() => {this.loading = false;this.drawer = false;this.$message({message: '编辑失败',type: 'error'});})},// 重置resetSearchForm() {let today = new Date();let todayString = `${today.getFullYear()}-${today.getMonth() + 1}-${today.getDate()}`;this.queryParams.dataTime = todayString;this.getList();},// 操作列表头handleEditBut(val) {this.showAddBut = !this.showAddBut;if(val === 'preserve') {this.handleSave();}else if(val === 'quit') {this.getTableList();}},// 删除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;this.emptyColumn();},// 编辑getEdit(col) {console.log("col",col);this.drawer = true;this.title = '编辑列',this.submitText = '保存',this.column.label = col.label;this.column.width = col.width;this.column.align = col.align;this.column.prop = col.prop.split(",").map(item => Number(item));if(col?.prop) {this.handleChange(this.column.prop);}console.log("this.column.prop",this.column.prop);this.column.propId = col.propId;this.copyCol = col;},// 编辑,新增里的取消handleClose() {this.$confirm('内容未保存,确认关闭?','提示',{confirmButtonText: '确定',cancelButtonText: '取消',type: 'warning'}).then(( )=> {this.emptyColumn();this.drawer = false;}).catch(() => {});},// 编辑,新增里的保存handleSubmit() {this.$refs['ruleFormRef'].validate((valid) => {if(!valid) {return false;}console.log("this.column",this.column);let col = this.copyColthis.loading = true;let item = {label: this.column.label,prop:  this.column.prop.toString(),propId: this.column.propId,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;if(!col.children) {col.children = [];}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.toString();col.propId = this.column.propId;})}this.loading = false;this.drawer = false;})},// 清空 column 的数据emptyColumn() {this.column.width = "";this.column.align = "center";this.column.prop = "";this.column.propId = "";},// 映射变量更改获取数据 IDhandleChange(val) {let aa = val[val.length-1]apiGetList({deptId: aa}).then(res => {if(res.code == 200) {this.optionsId = res.rows;}})},// 映射变量下拉数据handleList() {let data = {}apiGetTreeselect(data).then((res) => {this.options = res.data;})},// 导出表格exportToExcel() {const table = this.$refs.table.$el;const workbook = XLSX.utils.table_to_book(table, { raw: true });const wbout = XLSX.write(workbook, { bookType: "xlsx", type: "array" });let tableName = '';if(this.tablekey === 'year') {tableName = '年报表';}else if(this.tablekey === 'month') {tableName = '月报表';}else {tableName = '日报表';}tableName ? tableName + ".xlsx" : "表格.xlsx";this.downloadExcel(wbout, tableName);},downloadExcel(buffer, filename) {const blob = new Blob([buffer], { type: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" });const link = document.createElement("a");const url = URL.createObjectURL(blob);link.href = url;link.setAttribute("download", filename);document.body.appendChild(link);link.click();document.body.removeChild(link);URL.revokeObjectURL(url);},},mounted() {console.log("this.tablekey",this.tablekey);this.getTableList();this.handleList();// 接收子组件传递过来的事件EventBus.$on("getDelete",(col) => {this.getDelete(col) // 删除});EventBus.$on("getAdd",(col) => {this.getAdd(col) // 新增});EventBus.$on("getEdit",(col) => {this.getEdit(col) // 编辑});}
}
</script><style lang="scss" scoped>
.searchBar {padding: 15px;padding-bottom: 0;
}
.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;::v-deep .el-form-item__label {width: 88px;}
}
.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="`column_${col.propId}`":width="col.width":min-width="col.width ? '' : '100'":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}-${item.propId}`" :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},showAddBut: {type: Boolean,},},methods: {Add(col) {EventBus.$emit("getAdd",col)},Edit(col) {EventBus.$emit("getEdit",col)},Delete(col) {console.log(col);EventBus.$emit("getDelete",col)}},mounted() {},
}
</script><style lang="scss" scoped>
.scope_p {margin: 0;
}
i {cursor: pointer;
}
.i_margin {margin: 0 10px;
}
</style>

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

<HeaderTable tablekey="year" :maxLength="4" />

5、效果图,如下:

 

6、引入事件总线 EventBus 

// event-bus.js
import Vue from 'vue'
export const EventBus = new Vue()

http://www.ppmy.cn/ops/47360.html

相关文章

关于django的一些基础问答

1. Django 和 Tornado 的关系 Django 是一个功能强大、全面且高度规范化的 Web 框架&#xff0c;它提供了丰富的内置功能和工具&#xff0c;适合构建复杂的 Web 应用。注重快速开发、代码组织和安全性。 Tornado 则是一个注重高性能和异步处理的 Web 框架。它特别适合处理高并发…

在HTML中写入JavaScript

在网站中添加JavaScript代码是为了增加页面的交互性。这可以通过直接在HTML文件中写入JavaScript代码&#xff0c;或者通过引入外部JavaScript文件来实现。下面我将详细说明这两种方法。 直接在HTML中写入JavaScript 这种方法简单直接&#xff0c;适合用于快速测试或添加少量的…

Windows下使用Airsim+QGC进行PX4硬件在环HITL(一)

Windows下使用AirsimQGC进行PX4硬件在环HITL This tutorial will guide you through the installation of Airsim and QGC on Windows, so that the hardware-in-the-loop experiment can be conducted. Hardware-in-the-Loop (HITL or HIL) is a simulation mode in which nor…

AndroidStudio电脑连接手机后但AndroidStudio识别不到

我用的是mac androidstudio&#xff0c;通过USB数据线连接了小米手机&#xff0c;手机可以通过USB充电&#xff0c;但androidstudio没有显示对应的设备&#xff0c;adb命令检查不到设备&#xff1a; 后来发现是因为手机的开发者选项没有打开&#xff0c;其实以前是打开了的&…

Python面试宝典:Python中与设计模式相关的面试笔试题(1000加面试笔试题助你轻松捕获大厂Offer)

Python面试宝典:1000加python面试题助你轻松捕获大厂Offer【第二部分:Python高级特性:第二十二章:代码设计和设计模式:第二节:设计模式】 第二十二章:代码设计和设计模式第二节:设计模式创建型模式结构型模式行为型模式python中与设计模式相关的面试笔试题面试题1面试题…

论文降重:AI辅助工具的高效策略

现在大部分学校已经进入到论文查重降重的阶段了。如果查重率居高不下&#xff0c;延毕的威胁可能就在眼前。对于即将告别校园的学子们&#xff0c;这无疑是个噩梦。四年磨一剑&#xff0c;谁也不想在最后关头功亏一篑。 查重率过高&#xff0c;无非以下两种原因。要么是作为“…

C#WPF数字大屏项目实战02--主窗体布局

1、主窗体起始属性 设置有关属性如下&#xff1a; WindowStyle"None"-》无边框 AllowsTransparency"True" -》允许透明 WindowStartupLocation"CenterScreen"-》启动时位于屏幕中间 FontFamily"Microsoft YaHei"-》字体微软雅黑 …

落地式台灯怎么选比较好?五款618可入护眼大路灯分享

落地式台灯怎么选比较好&#xff1f;落地式台灯&#xff0c;简而言之&#xff0c;即一款提供健康舒适照明的立式灯具。它凭借卓越的LED光源和先进的科技技术&#xff0c;实现了传统台灯难以企及的健康照明效果&#xff0c;满足了从日常阅读、工作加班到小说阅读、艺术创作等多元…