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

- <div>
- <div class="searchBar">
- <el-form inline>
- <el-form-item label="时间选择">
- <el-date-picker
- v-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-table
- ref="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-column
- prop="dataTime"
- 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"
- :showAddBut="showAddBut"
- >
- 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="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-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="映射变量1" prop="prop">
- <el-cascader
- v-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-option
- v-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>
-
- <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;
- }
- },
- // 递归遍历出 propId
- getForRecursion(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.copyCol
- this.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 = "";
- },
- // 映射变量更改获取数据 ID
- handleChange(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>
- <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>
-
- <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>
<HeaderTable tablekey="year" :maxLength="4" />



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