基于el-table的动态表格列设置组件

需求背景

用户想对表格列进行宽度、位置、是否显示等进行操作,方便查看自己想看到的数据,并且希望这些设置能够记录下来,即使关闭浏览器,或换其他电脑再次访问系统时,表格显示的列依然是之前配置的结果。

解决思路

由于用户希望换了电脑访问系统还是能显示之前表格操作效果,这里使用localstorage显然是不行的,需要后台提供接口将操作后的表头数据存到数据库中。
由于系统表格表头涉及到多个层级,以及需自定义列内容,这里使用了jsx渲染表头数据,在开发业务功能时,只需维护好表头数据就行,不需要书写表格dom结构。

思维导图

image.png

image.png

实现

功能点:

  1. 调整列宽
  2. 恢复默认表头
  3. 设置 全部/单个 列 显示/隐藏
  4. 设置固定列
  5. 列文本对齐方式
  6. 调整列顺序

定义表头数据

表头数据必须定义u_id字段,并且要保证u_id的唯一性,因为从数据库提取出来表头数据根据u_id与本地表头数据比对合并

data(){
    return {
        cols: [{
            u_id: '0',
            label: '日期',
            prop: 'date',
            render(h){
                return h('el-table-column', {
                    props: {
                        ...this
                    },
                    scopedSlots: {
                        default: (props) => {
                            const { row } = props
                            return <span><i class="el-icon-date"/>{row.date}</span>
                        }
                    }
                })
            }
        }, {
            u_id: '1',
            label: '配送信息',
            children: [{
                u_id: '10',
                label: '姓名',
                prop: 'name',
                width: '120'
            }, {
                u_id: '11',
                label: '地址',
                children: [{
                    u_id: '110',
                    label: '省份',
                    prop: 'province',
                    width: '120'
                }, {
                    u_id: '111',
                    label: '市区',
                    prop: 'city',
                    width: '120'
                }, {
                    u_id: '112',
                    label: '地址',
                    prop: 'address',
                    width: '300'
                }, {
                    u_id: '113',
                    label: '邮编',
                    prop: 'zip',
                    width: '120'
                }]
            }]
        }]
    }
}
复制代码

定义公共方法

  1. 深拷贝方法

由于表头数据里面可能会有方法属性,不要使用JSON.parse(JSON.stringify(obj))的方式去拷贝数据,这样会使方法属性丢失

export function deepClone(obj) {
    let newObj = null
    if (typeof obj === 'object' && obj !== null) {
        newObj = obj instanceof Array ? [] : {}
        for (let i in obj) {
            newObj[i] = typeof obj[i] === 'object' ? deepClone(obj[i]) : obj[i]
        }
    } else {
        newObj = obj
    }
    return newObj
}
复制代码
  1. 创建id/删除id方法
export function createId(arr, defaultId = '_'){
    arr.forEach((item, index) => {
        item.id = defaultId + index
        if(defaultId != '_') item.p_id = defaultId
        if(item.children && item.children.length) createId(item.children, item.id)
    })
    return arr
}

export function removeId(arr){
    arr.forEach(item => {
        item.id && delete item.id
        item.p_id && delete item.p_id
        if(item.children && item.children.length) removeId(item.children)
    })
    return arr
}
复制代码
  1. 一维转多维/多维转一维方法
export function treeToArray(arr){
    return arr.reduce((acc, cur) => {
        if(Array.isArray(cur.children)){
            const copyCur = { ...cur }
            delete copyCur.children
            return acc.concat(copyCur, treeToArray(cur.children))
        }else{
            return acc.concat(cur)
        }
    }, [])
}

export function arrayToTree(arr) {
    let temp = {}
    let tree = {}
    arr.forEach(item => {
        temp[item.id] = item
    })

    let tempKeys = Object.keys(temp)
    tempKeys.forEach(key => {
        let item = temp[key]
        let pid = item.p_id
        let pItem = temp[pid]
        if(pItem){
            if(!pItem.children){
                pItem.children = []
            }
            pItem.children.push(item)
        }else{
            tree[item.id] = item
        }
    })
    return Object.keys(tree).map(key => tree[key])
}
复制代码
  1. 查找多维数组里某个元素的所有子元素方法
export function findChildLen(arr, id, dLen){
    handler(id)
    function handler(id){
        let temp = []
        arr.forEach(item => {
            if(item.p_id == id){
                dLen++
                temp.push(item)
            }
        })
        temp.forEach(item => {
            handler(item.id)
        })
    }
    return dLen
}
复制代码

定义DynamicTable组件

作用:该组件主要实现渲染table,调用接口获取/保存表头数据,调整列宽等功能 组件支持接收属性:

  1. el-table属性
  2. cols:表头数据
  3. tableflag:模块名,用于将数据存储时,区分是哪一个页面的数据

多层级表头
表格通过vue的render函数以jsx方式渲染,并定义renderCol方法递归多层级表头。 列自定义内容 有时列的内容需要特殊处理,例如在日期前加个图标

image.png
我们可以在表头数据cols中定义render属性方法,接收h函数(vm.$createElement),去渲染虚拟dom

render() {
    return (
      <div class="dt-container">
        <div class="dt-container-box">
          <el-table
            ref="table"
            props={this.$attrs}
            on-header-dragend={this.headDragend}
          >
            {this.renderCol(this.columns)}
          </el-table>
        </div>
        <ColumnSetting
          ref="csRef"
          defaultTableHeadData={this.cols}
          tableHeadData={this.tableHeadData}
          columns={this.columnInfo}
          onChangeColumn={this.handleColumnChange}
        />
      </div>
    );
  },
  methods: {
      // 渲染列
    renderCol(arr) {
      return arr.map((col) => {
        const cols = this.getShowColumn(col.children || []);  //过滤掉不显示的列
        if (col.render) return col.render(this.$createElement);
        if (cols.length == 0) {
          return <el-table-column props={col}/>;
        }
        return (
          <el-table-column props={col}>{this.renderCol(cols)}</el-table-column>
        );
      });
    },
  }
复制代码

表头数据cols

{
    u_id: '0',
    label: '日期',
    prop: 'date',
    render(h){
        return h('el-table-column', {
            props: {
                ...this
            },
            scopedSlots: {
                default: (props) => {
                    const { row } = props
                    return <span><i class="el-icon-date"/>{row.date}</span>
                }
            }
        })
    }
}
复制代码

在页面挂载时调用接口获取之前配置的表头数据,与初始的数据进行合并

// 从服务器获取表头数据
getHeadDataByData() {
  let para = {
    pageCode: `${moduleKey}_${this.tabelflag}`,
  };
  fetchFieldsByUser(para).then((res) => {
    const data = res.data || {};
    const columns = data.columns || "[]";
    this.mergeHeadData(JSON.parse(columns));
  });
},
// 本地表头数据和服务器存储数据合并
mergeHeadData(uiData) {
  let tableHeadData = createId(deepClone(this.tableHeadData));
  // 添加索引,用于排序
  tableHeadData = createIndex(tableHeadData);
  uiData = createIndex(uiData);
  // 多维转一维
  let uiData_temp = treeToArray(uiData);
  let tableHeadData_temp = treeToArray(tableHeadData);

  // 以本地数据为准,合并数据
  uiData_temp.forEach((item) => {
    tableHeadData_temp.forEach((i) => {
      if (item.u_id == i.u_id) {
        Object.assign(i, item);
        return;
      }
    });
  });

  // 根据索引排序
  tableHeadData_temp = tableHeadData_temp.sort((a, b) => {
    let idx1 = a.idx;
    let idx2 = b.idx;
    if (idx1 < idx2) {
      return -1;
    } else if (idx1 > idx2) {
      return 1;
    } else {
      return 0;
    }
  });

  this.$set(this, "tableHeadData", []);
  this.$nextTick(() => {
    this.$set(
      this,
      "tableHeadData",
      arrayToTree(deepClone(tableHeadData_temp))
    );
    // 解决设置fixed样式错乱问题
    this.$refs.table.doLayout()
  });

  function createIndex(arr) {
    arr.forEach((item, index) => {
      item.idx = index;
      if (item.children && item.children.length) {
        createIndex(item.children);
      }
    });
    return arr;
  }
},
复制代码

在el-table上绑定headerDragend方法,更新表头数据列宽

image.png

// 改变列宽
headDragend(newWidth, oldWidth, column) {
  let findCol = findItem(column.property, this.tableHeadData);
  if (findCol) findCol.width = newWidth + "";
  this.submitTableHeadData(this.tableHeadData);

  function findItem(prop, arr) {
    for (let item of arr) {
      if (item.prop == prop) return item;
      if (item.children && item.children.length) {
        let _item = findItem(prop, item.children);
        if (_item) return _item;
      }
    }
  }
}
复制代码

定义列设置弹窗组件(ColumnSetting)

组件支持接收属性

  1. tableHeadData:表头数据
  2. defaultTableHeadData:初始表头数据,没配置过的,用于恢复默认时试用
  3. columns:el-table的组件的表头数据,通过this. r e f s . t a b l e . refs.table. children[0].columns获得,主要是为了获取列的真实宽度

功能

  1. 恢复默认,将表头数据还原成初始数据
  2. 全部显示/隐藏
  3. 固定列,只能在没有父层级的列设置固定列,否则会出现样式错乱,要注意的是,在设置固定列时,表头最外层没有设置宽度的话,表格样式会出现错位的情况,这时就需要通过props的columns获取el-table真实的列宽,去计算出最外层的宽度并设置
  4. 对齐方式,只能在没有子节点的列才能设置,否则是不生效的
  5. 列显示,设置当前列是否显示
  6. 调整列顺序,通过使用sortablejs插件实现拖拽排列。Sortablejs的用法可参考文档,这里主要用了onStart(开始拖拽)、onEnd(结束拖拽)两个属性方法

在onStart中,需先备份列数据,主要用于拖拽的列越出当前层级时,恢复到拖拽前的数据;以及备份当前所有展开行的数据,当拖拽结束,列数据更新时,表格的数据会被刷新,展开行会收缩,需设置回拖拽前的状态。当前展开行的数据可通过监听el-table的expand-change事件设置。

image.png

image.png

// 保存展开行,解决数据刷新时,行全部收缩问题
handleExpandChange(row, expanded) {
  if (expanded) {
    this.rowKeys.push(row.id);
  } else {
    let index = this.rowKeys.indexOf(row.id);
    this.rowKeys.splice(index, 1);
  }
}
复制代码

在onEnd中,可以获取到oldIndex(拖拽前的索引)、newIndex(拖拽后的索引),通过这两个参数去得到拖拽的列数据,以及插入的列数据,判断它们的p_id(父级id)是否相同,不同时,则表示拖拽越层级,恢复回拖拽前的数据;相同时,会去获取拖拽的列数据的所有子数据(包括当前拖拽的数据),插入到拖拽后的位置。

onEnd: ({ newIndex, oldIndex }) => {
  let table = Object.assign([], this.tableData);
  let newTable = treeToArray(table); //多维转一维

  const sourceItem = newTable[oldIndex];
  const targetItem = newTable[newIndex];
  //判断拖拽是否在同级中操作
  if (sourceItem.p_id !== targetItem.p_id) {
    console.log("只能在同级之间进行拖拽排序");
    // 更新为拖拽前的数据
    this.$set(this, "tableData", []);
    this.$nextTick(() => {
      this.$set(this, "tableData", copyTableHeadData);
      this.$set(this, "rowKeys", copyRowKeys);
      this.rowDrop();
    });
  } else {
    let length = findChildLen(newTable, sourceItem.id, 0); // 获取拖拽行的子集
    const sourceList = newTable.splice(oldIndex, length + 1); //提取拖拽行以及其子集
    newTable.splice(newIndex, 0, ...sourceList); //插入到指定位置
    let tree = arrayToTree(newTable); // 一维转多维
    // 更新数据
    this.$set(this, "tableData", []);
    this.$nextTick(() => {
      this.$set(this, "tableData", tree);
      this.$set(this, "rowKeys", copyRowKeys);
      this.rowDrop();
    });
  }
}
复制代码

使用

定义好表头数据,将表头数据和表格数据传给dynamicTable组件,若要打开列设置弹窗,只需调用dynamicTable组件的handleSettingColumn方法

image.png

完整项目

目录结构

image.png

代码

主组件index.vue

<script>
import ColumnSetting from "./ColumnSetting";
// import { fetchFieldsByUser, saveUserFields } from "./api";
import {
  treeToArray,
  arrayToTree,
  createId,
  deepClone,
  removeId,
} from "./utils";
// const { moduleKey } = require("@/settings");

export default {
  name: "DynamicTable",
  props: {
    cols: {
      type: Array,
      default: () => [],
    },
    tabelflag: {
      type: String,
      required: true,
    },
  },
  data() {
    return {
      tableHeadData: [],
      columnInfo: []
    };
  },
  components: { ColumnSetting },
  created() {
    this.tableHeadData = deepClone(this.cols);
  },
  mounted() {
    this.getHeadDataByData();
  },
  computed: {
    columns() {
      return this.getShowColumn(this.tableHeadData);
    },
  },
  methods: {
    // 过滤不显示列
    getShowColumn(arr) {
      return arr.filter((col) => !col.hide);
    },
    // 渲染列
    renderCol(arr) {
      return arr.map((col) => {
        const cols = this.getShowColumn(col.children || []);  //过滤掉不显示的列
        if (col.render) return col.render(this.$createElement);
        if (cols.length == 0) {
          return <el-table-column props={col}/>;
        }
        return (
          <el-table-column props={col}>{this.renderCol(cols)}</el-table-column>
        );
      });
    },
    // 打开列设置弹窗
    handleSettingColumn() {
      this.columnInfo = this.$refs.table.$children[0].columns
      console.log(this.columnInfo)
      this.$refs.csRef.dialogVisible = true;
    },
    // 从服务器获取表头数据
    getHeadDataByData() {
      let para = {
        pageCode: `${moduleKey}_${this.tabelflag}`,
      };
      fetchFieldsByUser(para).then((res) => {
          const data = res.data || {};
          const columns = data.columns || "[]";
          this.mergeHeadData(JSON.parse(columns));
      });
    },
    // 本地表头数据和服务器存储数据合并
    mergeHeadData(uiData) {
      let tableHeadData = createId(deepClone(this.tableHeadData));
      // 添加索引,用于排序
      tableHeadData = createIndex(tableHeadData);
      uiData = createIndex(uiData);
      // 多维转一维
      let uiData_temp = treeToArray(uiData);
      let tableHeadData_temp = treeToArray(tableHeadData);

      // 以本地数据为准,合并数据
      uiData_temp.forEach((item) => {
        tableHeadData_temp.forEach((i) => {
          if (item.u_id == i.u_id) {
            Object.assign(i, item);
            return;
          }
        });
      });
      
      // 根据索引排序
      tableHeadData_temp = tableHeadData_temp.sort((a, b) => {
        let idx1 = a.idx;
        let idx2 = b.idx;
        if (idx1 < idx2) {
          return -1;
        } else if (idx1 > idx2) {
          return 1;
        } else {
          return 0;
        }
      });

      this.$set(this, "tableHeadData", []);
      this.$nextTick(() => {
        this.$set(
          this,
          "tableHeadData",
          arrayToTree(deepClone(tableHeadData_temp))
        );
        // 解决设置fixed样式错乱问题
        this.$refs.table.doLayout()
      });

      function createIndex(arr) {
        arr.forEach((item, index) => {
          item.idx = index;
          if (item.children && item.children.length) {
            createIndex(item.children);
          }
        });
        return arr;
      }
    },
    // 改变列宽
    headDragend(newWidth, oldWidth, column) {
      let findCol = findItem(column.property, this.tableHeadData);
      if (findCol) findCol.width = newWidth + "";
      this.submitTableHeadData(this.tableHeadData);

      function findItem(prop, arr) {
        for (let item of arr) {
          if (item.prop == prop) return item;
          if (item.children && item.children.length) {
            let _item = findItem(prop, item.children);
            if (_item) return _item;
          }
        }
      }
    },
    // 提交表头数据
    submitTableHeadData(data) {
      let tempData = removeId(data);
      let para = {
        pageCode: `${moduleKey}_${this.tabelflag}`,
        columns: JSON.stringify(tempData),
      };
      saveUserFields(para).then((res) => {
        console.log("保存表头信息成功");
      });
    },
    handleColumnChange(data) {
      this.$set(this, "tableHeadData", []);
      this.$nextTick(() => {
        this.$set(this, "tableHeadData", data);
        this.submitTableHeadData(data);
      });
    },
  },
  render() {
    return (
      <div class="dt-container">
        <div class="dt-container-box">
          <el-table
            ref="table"
            props={this.$attrs}
            on-header-dragend={this.headDragend}
          >
            {this.renderCol(this.columns)}
          </el-table>
        </div>
        <ColumnSetting
          ref="csRef"
          defaultTableHeadData={this.cols}
          tableHeadData={this.tableHeadData}
          columns={this.columnInfo}
          onChangeColumn={this.handleColumnChange}
        />
      </div>
    );
  },
};
</script>

<style scoped>
.dt-container {
  width: 100%;
  flex: 1;
  position: relative;
}
.dt-container-box {
  height: 100%;
  width: 100%;
  display: flex;
  position: absolute;
}
</style>
复制代码

列设置弹窗组件columnSetting

<template>
  <el-dialog top="5vh" title="列设置" :visible.sync="dialogVisible">
    <div class="column-box">
      <div class="columnBtn">
        <el-button @click="initTableData(defaultTableHeadData)"
          >恢复默认</el-button
        >
        <el-button @click="setColumnShow(tableData, !columnShow)">{{
          columnShow ? "全部隐藏" : "全部显示"
        }}</el-button>
      </div>
      <el-table
        ref="table"
        height="100%"
        class="columnTable"
        row-key="id"
        :data="tableData"
        :expand-row-keys="rowKeys"
        :tree-props="{ children: 'children' }"
        border
        stripe
        @expand-change="handleExpandChange"
      >
        <el-table-column type="index" align="center" />
        <el-table-column label="列名" prop="label" />
        <el-table-column 
          label="固定列"
          prop="fixed"
          align="center"
          width="160"
        >
          <template v-if="!row.p_id" slot-scope="{ row }">
            <el-select 
              v-model="row.fixed"
              @change="changeFixed(row)"
            >
              <el-option 
                v-for="option in fixedOptions"
                :key="option.value"
                :value="option.value"
                :label="option.label"
              />
            </el-select>
          </template>
        </el-table-column>
        <el-table-column
          label="对齐方式"
          prop="align"
          align="center"
          width="160"
        >
          <template v-if="!row.children" slot-scope="{ row }">
            <el-select v-model="row.align">
              <el-option
                v-for="option in alignOptions"
                :key="option.value"
                :value="option.value"
                :label="option.label"
              />
            </el-select>
          </template>
        </el-table-column>
        <el-table-column label="显示列" prop="show" align="center" width="100">
          <template slot-scope="{ row }">
            <el-switch v-model="row.show" @change="changeShow(row)"/>
          </template>
        </el-table-column>
      </el-table>
    </div>
    <template #footer>
      <el-button @click="dialogVisible = false">取消</el-button>
      <el-button type="primary" @click="changeColumn">确定</el-button>
    </template>
  </el-dialog>
</template>

<script>
import Sortable from "sortablejs";
import {
  treeToArray,
  arrayToTree,
  deepClone,
  createId,
  findChildLen,
} from "../utils";

export default {
  name: "ColumnSetting",
  props: {
    tableHeadData: {
      type: Array,
      default: [],
    },
    defaultTableHeadData: {
      type: Array,
      default: [],
    },
    columns: {
      type: Array,
      default: []
    }
  },
  data() {
    return {
      dialogVisible: false,
      tableData: [],
      fixedOptions: [
        {
          value: "left",
          label: "固定左侧",
        },
        {
          value: "right",
          label: "固定右侧",
        },
        {
          value: false,
          label: "不固定",
        },
      ],
      alignOptions: [
        {
          value: "left",
          label: "左对齐",
        },
        {
          value: "right",
          label: "右对齐",
        },
        {
          value: "center",
          label: "居中",
        },
      ],
      rowKeys: [],
    };
  },
  computed: {
    columnShow() {
      let tempArr = treeToArray(deepClone(this.tableData));
      let show = true;
      for (let temp of tempArr) {
        if (!temp.show) {
          show = false;
          break;
        }
      }
      return show;
    },
  },
  methods: {
    getEL() {
      return this.$refs.table.$el;
    },
    // 保存展开行,解决数据刷新时,行全部收缩问题
    handleExpandChange(row, expanded) {
      if (expanded) {
        this.rowKeys.push(row.id);
      } else {
        let index = this.rowKeys.indexOf(row.id);
        this.rowKeys.splice(index, 1);
      }
    },
    // 设置全部显示/隐藏
    setColumnShow(arr, show) {
      arr.forEach((item) => {
        item.show = show;
        item.hide = !show
        if (item.children && item.children) {
          this.setColumnShow(item.children, show);
        }
      });
    },
    changeFixed(row){
      setFixed(row)

      let tempArr = treeToArray(deepClone([row]))
      let childrens = []
      for(let temp of tempArr){
        for(let c of this.columns){
          if(c.property && c.property == temp.prop){
            childrens.push(c)
            break
          }
        }
      }
      if(childrens.length){
        row.width = childrens.reduce((sum, child) => sum + Number(child.realWidth || child.width), 0)
      }

      function setFixed(obj){
        if(!obj.children || !obj.children.length) return
        obj.children.forEach(item => {
          item.fixed = obj.fixed
          setFixed(item)
        })
      }
    },
    changeShow(row){
      row.hide = !row.hide
    },
    changeColumn() {
      this.$emit("changeColumn", this.tableData);
      this.dialogVisible = false;
    },
    // 初始化数据
    initTableData(source) {
      let tempArr = createShow(source)
      this.$set(this, "tableData", []);
      this.$nextTick(() => {
        this.$set(this, "tableData", createId(deepClone(tempArr)));
        this.rowDrop();
      });

      function createShow(arr){
        arr.forEach(item => {
          item.hide ? item.show = false : item.show = true
          if(item.children && item.children.length){
            createShow(item.children)
          }
        })
        return arr
      }
    },
    // 行拖拽操作
    rowDrop() {
      const tbody = document.querySelector(
        ".columnTable .el-table__body-wrapper tbody"
      );
      let copyTableHeadData = null;
      let copyRowKeys = null;
      this.sortable = Sortable.create(tbody, {
        onStart: () => {
          copyTableHeadData = deepClone(this.tableData);
          copyRowKeys = deepClone(this.rowKeys);
        },
        onEnd: ({ newIndex, oldIndex }) => {
          let table = Object.assign([], this.tableData);
          let newTable = treeToArray(table); //多维转一维

          const sourceItem = newTable[oldIndex];
          const targetItem = newTable[newIndex];
          //判断拖拽是否在同级中操作
          if (sourceItem.p_id !== targetItem.p_id) {
            console.log("只能在同级之间进行拖拽排序");
            // 更新为拖拽前的数据
            this.$set(this, "tableData", []);
            this.$nextTick(() => {
              this.$set(this, "tableData", copyTableHeadData);
              this.$set(this, "rowKeys", copyRowKeys);
              this.rowDrop();
            });
          } else {
            let length = findChildLen(newTable, sourceItem.id, 0); // 获取拖拽行的子集
            const sourceList = newTable.splice(oldIndex, length + 1); //提取拖拽行以及其子集
            newTable.splice(newIndex, 0, ...sourceList); //插入到指定位置
            let tree = arrayToTree(newTable); // 一维转多维
            // 更新数据
            this.$set(this, "tableData", []);
            this.$nextTick(() => {
              this.$set(this, "tableData", tree);
              this.$set(this, "rowKeys", copyRowKeys);
              this.rowDrop();
            });
          }
        },
      });
    },
  },
  watch: {
    dialogVisible(value) {
      if (value) {
        this.initTableData(this.tableHeadData);
      }
    },
  },
};
</script>

<style scoped>
.columnBtn {
  text-align: right;
  margin-bottom: 10px;
}
::v-deep.columnTable .el-table__placeholder {
  display: inline !important;
}
.column-box {
  height: 70vh;
  overflow: auto;
  display: flex;
  flex-direction: column;
}
</style>
复制代码

公共方法utils

export function treeToArray(arr){
    return arr.reduce((acc, cur) => {
        if(Array.isArray(cur.children)){
            const copyCur = { ...cur }
            delete copyCur.children
            return acc.concat(copyCur, treeToArray(cur.children))
        }else{
            return acc.concat(cur)
        }
    }, [])
}

export function arrayToTree(arr) {
    let temp = {}
    let tree = {}
    arr.forEach(item => {
        temp[item.id] = item
    })

    let tempKeys = Object.keys(temp)
    tempKeys.forEach(key => {
        let item = temp[key]
        let pid = item.p_id
        let pItem = temp[pid]
        if(pItem){
            if(!pItem.children){
                pItem.children = []
            }
            pItem.children.push(item)
        }else{
            tree[item.id] = item
        }
    })
    return Object.keys(tree).map(key => tree[key])
}

export function deepClone(obj) {
    let newObj = null
    if (typeof obj === 'object' && obj !== null) {
        newObj = obj instanceof Array ? [] : {}
        for (let i in obj) {
            newObj[i] = typeof obj[i] === 'object' ? deepClone(obj[i]) : obj[i]
        }
    } else {
        newObj = obj
    }
    return newObj
}
    
export function createId(arr, defaultId = '_'){
    arr.forEach((item, index) => {
        item.id = defaultId + index
        if(defaultId != '_') item.p_id = defaultId
        if(item.children && item.children.length) createId(item.children, item.id)
    })
    return arr
}

export function removeId(arr){
    arr.forEach(item => {
        item.id && delete item.id
        item.p_id && delete item.p_id
        if(item.children && item.children.length) removeId(item.children)
    })
    return arr
}

export function findChildLen(arr, id, dLen){
    handler(id)
    function handler(id){
        let temp = []
        arr.forEach(item => {
            if(item.p_id == id){
                dLen++
                temp.push(item)
            }
        })
        temp.forEach(item => {
            handler(item.id)
        })
    }
    return dLen
}
复制代码

请求方法api

import axios from 'axios'
export const fetchFieldsByUser = (data) => axios.post('获取表头数据接口', data)

export const saveUserFields = (data) => axios.post('保存表头数据接口', data)
复制代码

调用示例

<template>
    <div id="app">
        <el-button @click="handleSettingColumn">列设置</el-button>
        <div style="height: 500px;display: flex;">
            <DynamicTable 
                ref="DTable" :cols="cols" 
                :tabelflag="$options.name" :data="rows"
                border
            />
        </div>
    </div>
</template>

<script>
import DynamicTable from '@/components/DynamicTable'
export default {
    name: 'App',
    components: { DynamicTable },
    data(){
        return {
            cols: [{
                u_id: '0',
                label: '日期',
                prop: 'date',
                render(h){
                    return h('el-table-column', {
                        props: {
                            ...this
                        },
                        scopedSlots: {
                            default: (props) => {
                                const { row } = props
                                return <span><i class="el-icon-date"/>{row.date}</span>
                            }
                        }
                    })
                }
            }, {
                u_id: '1',
                label: '配送信息',
                children: [{
                    u_id: '10',
                    label: '姓名',
                    prop: 'name',
                    width: '120'
                }, {
                    u_id: '11',
                    label: '地址',
                    children: [{
                        u_id: '110',
                        label: '省份',
                        prop: 'province',
                        width: '120'
                    }, {
                        u_id: '111',
                        label: '市区',
                        prop: 'city',
                        width: '120'
                    }, {
                        u_id: '112',
                        label: '地址',
                        prop: 'address',
                        width: '300'
                    }, {
                        u_id: '113',
                        label: '邮编',
                        prop: 'zip',
                        width: '120'
                    }]
                }]
            }],
            rows: [{
            date: '2016-05-03',
            name: '王小虎',
            province: '上海',
            city: '普陀区',
            address: '上海市普陀区金沙江路 1518 弄',
            zip: 200333
            }, {
            date: '2016-05-02',
            name: '王小虎',
            province: '上海',
            city: '普陀区',
            address: '上海市普陀区金沙江路 1518 弄',
            zip: 200333
            }, {
            date: '2016-05-04',
            name: '王小虎',
            province: '上海',
            city: '普陀区',
            address: '上海市普陀区金沙江路 1518 弄',
            zip: 200333
            }, {
            date: '2016-05-01',
            name: '王小虎',
            province: '上海',
            city: '普陀区',
            address: '上海市普陀区金沙江路 1518 弄',
            zip: 200333
            }, {
            date: '2016-05-08',
            name: '王小虎',
            province: '上海',
            city: '普陀区',
            address: '上海市普陀区金沙江路 1518 弄',
            zip: 200333
            }, {
            date: '2016-05-06',
            name: '王小虎',
            province: '上海',
            city: '普陀区',
            address: '上海市普陀区金沙江路 1518 弄',
            zip: 200333
            }, {
            date: '2016-05-07',
            name: '王小虎',
            province: '上海',
            city: '普陀区',
            address: '上海市普陀区金沙江路 1518 弄',
            zip: 200333
            }]
        }
    },
    methods: {
        handleSettingColumn(){
            this.$refs.DTable.handleSettingColumn()
        }
    }
}
</script>
复制代码

猜你喜欢

转载自juejin.im/post/7036173436648947748
今日推荐