原生 js 和 vue+ element-ui 分别实现树形表格 ? (JSONP 和 Axios 请求本地 JSON 文件)

使用原生 JS 实现树形表格 ?


【 如需查看完整示例请点击:tree-table_JS: 使用原生 JS 实现,树形表格案例。支持多级数据渲染,新增,删除,编辑等操作。 


  • 请求本地 JSON 文件获取数据 。
  1. 问题:cors ,跨域问题 。
  2. 解决:使用 jsonp 方式,请求相关数据 。
  3. 注意:在 json 文件中,json 数据使用 callback( json数据 ) 包裹,才能保持浏览器控制台打印出数据无报错。我这里回调函数是 getData( json数据 )。
  4. 代码:
    <script type="text/javascript">
     /*
      getData 函数: JSONP 的函调函数,解决跨域问题。请求本地 JSON 文件
      参数: data---》拿到的 JSON 数据
    */
     function getData(data) {
         // initData---》存放当前操作完的全局树形数据
         var initData = [] 
         initData = JSON.parse(JSON.stringify(formatData(data.menuTree,'')))
         initTable(initData)
     }
    </script>
    <script type="text/javascript" src="./data.json?callback=getData"></script>
  • 一维数组数据转换为树形数据 (递归)。
  1. 思路:使用递归的方式将一维数组转换成树形数据,根据 menuParentid 和 menuId 的对比,对数据进行分类,添加到合适的位置上。
  2. 代码:
    /* 
     formatData 函数:数据转换,将一维数组数据转换为树形数据
     参数:data---》原始一维数组数据
     返回值:resultArr---》格式化好的树形数据
    */
    function formatData(data, pid) {
        var resultArr = [], temp;
        for (var i = 0; i < data.length; i++) {
            if (data[i].menuParentid == pid) {
                var obj = JSON.parse(JSON.stringify(data[i]));
                temp = formatData(data, data[i].menuId);
                if (temp.length > 0) {
                    obj.children = temp;
                }
                resultArr.push(obj);
            }
        }
        return resultArr;
    }
  • JS 中初始化表格 。
  1. 思路:在 initTR_DG 函数中递归的根据数据,动态新增 tr 到 table 中。根据 当前数据项的 menuType 是否为菜单,决定是否有前缀的展开和收起按钮。
  2. 代码:
    /* 
      initTable 函数: 初始化表格
      参数: data---》转换的树形数据
    */
    function initTable(data) {
        globalData = JSON.parse(JSON.stringify(data))
        var tableEL = document.createElement('table')
        var containerEL = document.getElementById('container')
        containerEL.appendChild(tableEL)
        tableEL.appendChild(createTH())
        initTR_DG(data,tableEL,'')
    }
    function initTR_DG(data,tableEL,index){
        if(data && data.length != 0){
            for(var i=0; i<data.length; i++){
                if(data[i].menuType == '菜单') {
                    var trEL = createTR(data[i], 1, index+'.'+(i+1))
                } else {
                    var trEL = createTR(data[i], 0, index+'.'+(i+1))
                }
                tableEL.appendChild(trEL)
                if(data[i].children && data[i].children.length != 0){
                    initTR_DG(data[i].children,tableEL,index+'.'+(i+1))
                }
            }
        }
    }
    /*
      createTH 函数: 创建表头
      返回值: trEL---》表头元素
    */
    function createTH() {
        var trEL = document.createElement('tr')
        var thEL0 = document.createElement('th')
        thEL0.innerHTML = '序号'
        trEL.appendChild(thEL0)
        var thEL1 = document.createElement('th')
        thEL1.innerHTML = '功能名称'
        trEL.appendChild(thEL1)
        var thEL2 = document.createElement('th')
        thEL2.innerHTML = '是否启用'
        trEL.appendChild(thEL2)
        var thEL3 = document.createElement('th')
        thEL3.innerHTML = '功能类型'
        trEL.appendChild(thEL3)
        var thEL4 = document.createElement('th')
        thEL4.innerHTML = '创建时间'
        trEL.appendChild(thEL4)
        var thEL5 = document.createElement('th')
        thEL5.innerHTML = '功能备注'
        trEL.appendChild(thEL5)
        var thEL6 = document.createElement('th')
        thEL6.innerHTML = '相关操作'
        trEL.appendChild(thEL6)
        return trEL
    }
    /* 
      createTR 函数: 创建每一列
      参数1: currentData---》当前列的数据
      参数2: type---》类型,决定有没有展开折叠按钮,1 有,其他无
      参数3 index---》计算的当前列的标号
      参数4: tier---》第几层
      返回值: trEL---》创建的当前 tr
    */
    function createTR(currentData, type, index) {
        var trEL = document.createElement('tr')
        if (type == 1) {
            var tdEL0 = document.createElement('td')
            tdEL0.innerHTML = index
            tdEL0.style.fontWeight = 'bold'
            tdEL0.style.fontSize = '8px'
            trEL.appendChild(tdEL0)
            var spanEL = document.createElement('span')
            spanEL.classList.add('span')
            spanEL.title = '展开'
            spanEL.onclick = clickZK
            var spanEL1 = document.createElement('span')
            spanEL1.classList.add('span1')
            spanEL1.title = '收起'
            spanEL1.onclick = clickSQ
            var tdEL1 = document.createElement('td')
            tdEL1.innerHTML = currentData.menuText
            tdEL0.style.textAlign = 'left'
            tdEL0.appendChild(spanEL)
            tdEL0.appendChild(spanEL1)
            trEL.appendChild(tdEL1)
            var tdEL2 = document.createElement('td')
            tdEL2.innerHTML = currentData.menuIsmodify
            trEL.appendChild(tdEL2)
            var tdEL3 = document.createElement('td')
            tdEL3.innerHTML = currentData.menuType
            currentData.menuType == '菜单' ? tdEL3.style.color = '#409EFF' : tdEL3.style.color = 'green'
            trEL.appendChild(tdEL3)
            var tdEL4 = document.createElement('td')
            tdEL4.innerHTML = datefomate(currentData.menuCreatetime)
            trEL.appendChild(tdEL4)
            var tdEL5 = document.createElement('td')
            tdEL5.innerHTML = currentData.menuNote
            trEL.appendChild(tdEL5)
            var tdEL6 = document.createElement('td')
            var editBtn = document.createElement('button')
            editBtn.innerText = '编辑'
            editBtn.classList.add('button1')
            editBtn.onclick = editTableData
            tdEL6.appendChild(editBtn)
            var delBtn = document.createElement('button')
            delBtn.innerText = '删除'
            delBtn.classList.add('button2')
            delBtn.onclick = delTableData
            tdEL6.appendChild(delBtn)
            var addBtn = document.createElement('button')
            addBtn.innerText = '新增'
            addBtn.onclick = addTableData
            tdEL6.appendChild(addBtn)
            addBtn.classList.add('button0')
            trEL.appendChild(tdEL6)
        } else {
            var tdEL0 = document.createElement('td')
            var strongEL = document.createElement('strong')
            strongEL.innerHTML = index
            tdEL0.appendChild(strongEL)
            tdEL0.style.fontSize = '8px'
            trEL.appendChild(tdEL0)
            var tdEL1 = document.createElement('td')
            tdEL1.innerHTML = currentData.menuText
            trEL.appendChild(tdEL1)
            var tdEL2 = document.createElement('td')
            tdEL2.innerHTML = currentData.menuIsmodify
            trEL.appendChild(tdEL2)
            var tdEL3 = document.createElement('td')
            tdEL3.innerHTML = currentData.menuType
            currentData.menuType == '菜单' ? tdEL3.style.color = '#409EFF' : tdEL3.style.color = 'green'
            trEL.appendChild(tdEL3)
            var tdEL4 = document.createElement('td')
            tdEL4.innerHTML = datefomate(currentData.menuCreatetime)
            trEL.appendChild(tdEL4)
            var tdEL5 = document.createElement('td')
            tdEL5.innerHTML = currentData.menuNote
            trEL.appendChild(tdEL5)
            var tdEL6 = document.createElement('td')
            var editBtn = document.createElement('button')
            editBtn.innerText = '编辑'
            editBtn.classList.add('button1')
            editBtn.onclick = editTableData
            tdEL6.appendChild(editBtn)
            var delBtn = document.createElement('button')
            delBtn.innerText = '删除'
            delBtn.classList.add('button2')
            delBtn.onclick = delTableData
            tdEL6.appendChild(delBtn)
            trEL.appendChild(tdEL6)
        }
        return trEL
    }
  • 列收起和列展开。
  1. 思路:根据 clickSQ 的点击事件,确定点击的数据项,在根据 clickSQ_DG 函数,递归的找出当前数据项,让它的 children = [ ],重新初始化表格。根据 clickZK 的点击事件,清空原始 table 表格结构,使用缓存的 initData 数据去重新初始化表格。
  2. 代码:
    // clickSQ 函数: 列收起
    function clickSQ() {
        initData = JSON.parse(JSON.stringify(globalData))
        var currentText = this.parentNode.parentNode.children[1].innerText
        clickSQ_DG(globalData,currentText)
        document.getElementById('container').innerHTML = ''
        initTable(globalData)
        SQstatus = false
    }
    function clickSQ_DG(globalData,currentText){
        for (var i = 0; i < globalData.length; i++) {
            if (globalData[i].menuText == currentText) {
                globalData[i].children = []
                SQstatus = true
                break
            }
            if(SQstatus == false){
                if(globalData[i].children && globalData[i].children.length != 0){
                    clickSQ_DG(globalData[i].children,currentText)
                }
            }
        }
    }
    // clickZK 函数: 列展开
    function clickZK() {
        document.getElementById('container').innerHTML = ''
        initTable(initData)
    }
  • 删除操作 。
  1. 思路:根据 delTableData 确定当前操作的数据列,使用 delTableData_DG 函数递归来找到当前的数据,进行删除 ,重新初始化表格 
  2. 表格:
    // delTableData函数: 删除操作
    function delTableData() {
        var currentText = this.parentNode.parentNode.children[1].innerText
        delTableData_DG(globalData,currentText)
        document.getElementById('container').innerHTML = ''
        initTable(globalData)
        SQstatus = false
    }
    function delTableData_DG(globalData,currentText){
        for (var i = 0; i < globalData.length; i++) {
            if (globalData[i].menuText == currentText) {
                globalData.splice(i, 1)
                SQstatus = true
                break
            }
            if(SQstatus == false){
                if(globalData[i].children && globalData[i].children.length != 0){
                    delTableData_DG(globalData[i].children,currentText)
                }
            }
        }
    }
  • 编辑操作 。
  1. 思路:根据 editTableData 确定当前操作的数据列,使用 editTableData_DG 函数递归来找到当前的数据,使用 setEditData 函数进行数据回显,使用 getCurrentEdit 函数进行数据缓存,当点击编辑保存事件时进行当前缓存数据的更改,最后重新初始化表格 。
  2. 代码:
    // editTableData 函数: 编辑操作
    function editTableData() {
        document.getElementById('edit').style.display = 'block'
        var currentText = this.parentNode.parentNode.children[1].innerText
        editTableData_DG(globalData,currentText)
        SQstatus = false
    }
    function editTableData_DG(globalData,currentText){
        for (var i = 0; i < globalData.length; i++) {
            if (globalData[i].menuText == currentText) {
                setEditData(globalData[i])
                getCurrentEdit(globalData[i])
                SQstatus = true
                return
            }
            if(SQstatus == false){
                if(globalData[i].children && globalData[i].children.length != 0){
                    editTableData_DG(globalData[i].children,currentText,currentGlobalData)
                }
            }
        }
    }
    function getCurrentEdit(data){
        return currentGlobalData = data
    }
    /* 
      etEditData 函数: 编辑数据回显
      参数: data---》编辑回显的当前数据
    */
    function setEditData(data) {
        document.getElementById('menuText').value = data.menuText
        document.getElementById('menuIsmodify').value = data.menuIsmodify == '启用' ? 1 : 0
        document.getElementById('menuType').value = data.menuType == '菜单' ? 1 : 0
        document.getElementById('menuCreatetime').value = datefomate(data.menuCreatetime)
        document.getElementById('menuNote').value = data.menuNote
    }
    // 编辑关闭
    document.getElementById('editClose').onclick = function() {
        document.getElementById('edit').style.display = 'none'
    }
    // 编辑保存
    document.getElementById('editSave').onclick = function() {
        currentGlobalData.menuText = document.getElementById('menuText').value
        currentGlobalData.menuIsmodify = document.getElementById('menuIsmodify').value == 1 ? '启用' : '禁用'
        currentGlobalData.menuType = document.getElementById('menuType').value == 1 ? '菜单' : '链接'
        currentGlobalData.menuCreatetime = document.getElementById('menuCreatetime').value
        currentGlobalData.menuNote = document.getElementById('menuNote').value
        document.getElementById('container').innerHTML = ''
        initTable(globalData)
        document.getElementById('edit').style.display = 'none'
    }
    // 编辑取消
    document.getElementById('editCancle').onclick = function() {
       document.getElementById('edit').style.display = 'none'
    }
  • 新增操作 。
  1. 思路:根据 addTableData 确定当前操作的数据列,使用 addTableData_DG 函数递归来找到当前的数据,使用 getCurrentAdd 函数进行数据缓存,当点击新增保存事件时进将数据添加到缓存的当前数据项的 children 中,最后重新初始化表格 。
  2. 代码:
    // addTableData 函数:新增操作
    function addTableData() {
        document.getElementById('add').style.display = 'block'
        document.getElementById('menuCreatetime1').value = datefomate(Date.now())
        var currentText = this.parentNode.parentNode.children[1].innerText
        addTableData_DG(globalData,currentText)
        SQstatus = false
    }
    function addTableData_DG(globalData,currentText){
        for (var i = 0; i < globalData.length; i++) {
            if (globalData[i].menuText == currentText) {
                getCurrentAdd(globalData[i])
                SQstatus = true
                return
            }
            if(SQstatus == false){
                if(globalData[i].children && globalData[i].children.length != 0){
                    addTableData_DG(globalData[i].children,currentText)
                }
            }
        }
    }
    function getCurrentAdd(data){
        return currentGlobalData1 = data
    }
    // 新增关闭
    document.getElementById('addClose').onclick = function() {
        document.getElementById('menuText1').value = ''
        document.getElementById('menuNote1').value = ''
        document.getElementById('add').style.display = 'none'
    }
    //新增保存
    document.getElementById('addSave').onclick = function() {
        var obj = {}
        obj.children = []
        obj.menuText = document.getElementById('menuText1').value
        obj.menuIsmodify = document.getElementById('menuIsmodify1').value == 1 ? '启用' : '禁用'
        obj.menuType = document.getElementById('menuType1').value == 1 ? '菜单' : '链接'
        obj.menuCreatetime = document.getElementById('menuCreatetime1').value
        obj.menuNote = document.getElementById('menuNote1').value
        if(!currentGlobalData1.children){
            currentGlobalData1.children = []
            currentGlobalData1.children[0] = obj
        }else{
            currentGlobalData1.children.push(obj)
        }
        document.getElementById('container').innerHTML = ''
        initTable(globalData)
        document.getElementById('menuText1').value = ''
        document.getElementById('menuNote1').value = ''
        document.getElementById('add').style.display = 'none'
    }
    // 新增取消
    document.getElementById('addCancle').onclick = function() {
        document.getElementById('menuText1').value = ''
        document.getElementById('menuNote1').value = ''
        document.getElementById('add').style.display = 'none'
    }
  • 格式化日期 。
  1. 思路:使用 new Date(value) 方法返回值提供的一系列方法进行详细日期时间子项的提取,然后在根据合适的格式进行返回。
  2. 代码:
    // datefomate函数: 格式化日期
    function datefomate(value) {
        if (value == null || value == undefined) {
            return "";
        }
        var date = new Date(value);
        Y = date.getFullYear(),
        m = date.getMonth() + 1,
        d = date.getDate(),
        H = date.getHours(),
        i = date.getMinutes(),
        s = date.getSeconds();
        return Y + '-' + m + '-' + d + '    ' + H + ':' + i + ':' + s;
    };

使用 VUE 和 Element-UI  实现树形表格 ?


【 如需查看完整示例请点击:tree_table_vue_element: 使用 vue 和 element-ui 实现,树形表格案例,支持多级数据渲染,新增,数辑,删除等操作。 


  •  请求本地 JSON 文件获取数据 。
  1. 思路:使用 axios.get( ) 的方法,请求本地 JSON 文件,获取数据 。
  2. 注意:cli2 需要把  json 放在 static 目录下,cli3 中静态资源文件目录由 static 变为 public 不能把 .json 文件直接放到 public 下,必须放到 public/js 下(自己创建 js 目录),否则会报 404 错误 。
  3. 代码:
    axios.get('/js/data.json').then((res) => {
      this.defaultData = res.data.menuTree
      this.tableData = this.formatData(this.defaultData)
    })
  • 一维数组数据转换为树形数据(三级)。
  1. 思路:数据格式转换,根据自己需求场景的不同来决定。我这里只涉及三级数据,选择使用 for 循环嵌套去实现(第一层判断 menuParentid 是否为空,第二三层根据比对 menuParentid 和 menuId 的值来确定存放的合适 children 位置)。更多级推荐使用递归方式实现,具体使用参考上面 。
  2. 代码:
    formatData(data) {
      let resultArr = []
      for (let i = 0; i < data.length; i++) {
        if (data[i].menuParentid == '') {
          resultArr.push(data[i])
        }
      }
      for (let i = 0; i < resultArr.length; i++) {
        let newArr = []
        for (let j = 0; j < data.length; j++) {
          if (data[j].menuParentid == resultArr[i].menuId) {
            newArr.push(data[j])
          }
        }
        resultArr[i].children = newArr
      }
      for (let i = 0; i < resultArr.length; i++) {
        for (let k = 0; k < resultArr[i].children.length; k++) {
          let newArr1 = []
          for (let j = 0; j < data.length; j++) {
            if (data[j].menuParentid == resultArr[i].children[k].menuId) {
              newArr1.push(data[j])
            }
          }
          resultArr[i].children[k].children = newArr1
        }
      }
      return resultArr
    }
  • 数据渲染,使用 Element-UI 中提供的表格组件 。
  1. 思路:这里使用 Element-UI 中的表格组件去实现,也可以使用 layui 或者其他 UI 库中的组件去实现 。
  2. 代码:
    <el-table :data="tableData" height="500" style="width: 100%;margin-bottom: 20px;" row-key="id" border
          :expand-row-keys="['1','2','16','17','18']" :tree-props="{children: 'children', hasChildren: 'hasChildren'}">
          <el-table-column type="index" width="60" label="序号" align="center">
          </el-table-column>
          <el-table-column prop="menuText" label="功能名称" width="180" align="center">
          </el-table-column>
          <el-table-column prop="menuIsmodify" label="是否启用" width="180" align="center">
          </el-table-column>
          <el-table-column prop="menuType" label="功能类型" align="center">
            <template slot-scope="scope">
              <el-tag :type="scope.row.menuType === '菜单' ? 'primary' : 'success'" disable-transitions>{{scope.row.menuType}}
              </el-tag>
            </template>
          </el-table-column>
          <el-table-column prop="menuCreatetime" label="创建时间" align="center">
          </el-table-column>
          <el-table-column prop="menuNote" label="备注" width="200" align="center">
          </el-table-column>
          <el-table-column label="操作" align="center">
            <template slot-scope="scope">
              <el-button style="color:#67C23A;" @click="addHandle(scope.row)" type="text" size="small"
                 v-if="scope.row.menuType === '菜单'" icon="el-icon-circle-plus-outline">新增</el-button>
              <el-button type="text" size="small" @click="editHandle(scope.row)" icon="el-icon-edit">编辑</el-button>
              <el-button style="color:red;" @click="delHandle(scope.row)" type="text" size="small" icon="el-icon-delete">删除
          </el-button>
        </template>
      </el-table-column>
    </el-table>
  • 删除操作 。
  1. 思路:遍历 defaultData 中的数据,找到删除的那一项进行删除,在使用 formatData 函数对数据进行格式化树形数据,重新赋值给 tableData ,进行表格的重新渲染。
  2. 代码:
     delHandle(row) {
      this.$confirm('此操作将永久删除此项, 是否继续?', '提示', {
        confirmButtonText: '确定',
        cancelButtonText: '取消',
        type: 'warning'
      }).then(() => {
        for (let i = 0; i < this.defaultData.length; i++) {
          if (this.defaultData[i].id == row.id) {
            this.defaultData.splice(i, 1)
          }
        }
        this.tableData = this.formatData(this.defaultData)
        this.$message({
          type: 'success',
          message: '删除成功!'
        });
      }).catch(() => {
        this.$message({
          type: 'info',
          message: '已取消删除'
        });
      });
    }
  • 编辑操作 。
  1. 思路:遍历 defaultData 中的数据,找到要编辑的那一项进行修改,在使用 formatData 函数对数据进行格式化树形数据,重新赋值给 tableData ,进行表格的重新渲染。
  2. 代码:
    editHandle(row) {
      this.isExit = true
      this.timer = new Date().getTime()
      for (let i = 0; i < this.defaultData.length; i++) {
        if (this.defaultData[i].id == row.id) {
          this.currentEdit = this.defaultData[i]
        }
      }
    },
    eventHandle($event) {
      for (let i = 0; i < this.defaultData.length; i++) {
         if (this.defaultData[i].id == $event.id) {
          this.defaultData[i] = $event
        }
      }
      this.tableData = this.formatData(this.defaultData)
    }
  • 新增操作 。
  1. 思路:遍历 defaultData 中的数据,找到要新增项的父级,使得新增项的 menuParentid 等于新增项的父级的 menuId ,同时将新增项添加到 defaultData  中 。在使用 formatData 函数对数据进行格式化树形数据,重新赋值给 tableData ,进行表格的重新渲染。
  2. 代码:
    addHandle(row) {
      this.isExit1 = true
      this.timer1 = new Date().getTime()+'1'
      for (let i = 0; i < this.defaultData.length; i++) {
        if (this.defaultData[i].id == row.id) {
          this.currentAdd = this.defaultData[i]
        }
      }
    },
    eventHandle1($event) {
      this.defaultData.push($event)
      $event.id = this.defaultData.length
      $event.menuParentid = this.currentAdd.menuId
      this.tableData = this.formatData(this.defaultData)
     }
    }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值