el-table 树级表格(二)

该代码段展示了一个Vue.js组件,用于处理带有父子关系的数据表格。表格支持父子级的选择,当父级被选中时会自动选择所有子级,反之则仅选择父级。同时,表格可以对数据进行排序,包括自定义排序和子节点排序。用户还可以编辑排序索引。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

 

  1. 父子数据表格
  2. 父级选中携带全部子级
  3. 子级非全选,父级脱离选中
  4. 父子可单独进行排序
<template>
  <div>
    <el-table ref="SortTableTree" :default-expand-all="isEdit"
      :header-cell-style="{ background: '#E8EFFC', color: '#333333', height: '56px' }" v-loading="tableLoading"
      element-loading-background="rgba(255, 255, 255, 0.7)" :data="sortTableData" border style="width: 100%"
      :row-class-name="tableRowClassName" :row-key="getRowKeys" :span-method="arraySpanMethod"
      :tree-props="{ children: 'children' }" @expand-change="handleExpandChange" @select="handletableSelect"
      @select-all="handletableSelectAll" @selection-change="handletableSelectionChange">
      <template slot="empty">
        <el-empty :image-size="100" description="暂无数据"></el-empty>
      </template>
      <el-table-column type="selection" width="55"> </el-table-column>
      <el-table-column align="left" prop="label" label="项目分类" width="230">
        <template slot-scope="{ row,$index }">
          <span>{{ row.label }}</span>
          <div class="sortIcon" v-if="!row.parentType && row.testType">
            <el-input :disabled="isEdit" style="width: 50px; padding: none !important;" v-model.number="row.itemIndex"
              @blur="toSortItem(row)"></el-input>
          </div>
        </template>
      </el-table-column>
      <el-table-column align="left" prop="testTypeIndex" label="排序" width="50">
        <template slot-scope="{ row,$index }">
          <div v-if="row.parentType && row.label !== '其它'">
            <el-input style="width: 50px;" :disabled="row.isDisable || isEdit"
              v-model.number="row.parentIndex"></el-input>
          </div>
          <span v-if="row.label === '其它'">{{ row.parentIndex }}</span>
        </template>
      </el-table-column>
      <el-table-column v-for="(item, index) in tableList" :key="item.label" :min-width="item.width"
        :show-overflow-tooltip="false" :fixed="item.fixed" align="center">
        <template slot="header">{{ item.label }}</template>
        <template slot-scope="{ row,$index }">
          <div v-if="item.type == '操作' || item.type == 'slot'">
            <slot :row="row" :label="item" :$index="$index" />
          </div>
          <!-- 显示类型字典 -->
          <span v-else-if="item.dicts">
            {{ dict.dict[item.dicts][row[item.prop]]?.label }}
          </span>
          <span v-else>{{ row[item.prop] }}</span>
        </template>
      </el-table-column>
    </el-table>
  </div>
</template>

<script>
import { v4 as uuidv4 } from 'uuid';
export default {
  props: ['detailData', 'editState', 'tableList', 'tableLoading', 'isEdit'],
  dicts: ['item_export_show_range'],  //显示类型字典
  data() {
    return {
      num: 10000,
      expands: [],
      sortTableData: [
        {
          children: [
            {
              id: 123,
              name:'kkk'
            }
          ],
          label:"一级1.1",
          parentIndex:6,
          parentType:true,
          pid:"cb175c38-0c04-4db0-a678-5261289f2c54"
        },
          {
          children: [
            {
              id: 1234,
              name: 'kkk'
            }
          ],
          label: "一级1.2",
          parentIndex: 61,
          parentType: true,
          pid: "cb175c38-0c04-4db0-a678-ewda8847123"
        }
      ],
    };
  },
  mounted() {
  },

  methods: {
    //获取当前选中
    getSelection() {
      return this.$refs["SortTableTree"].selection;
    },
    // 获取row的key值
    getRowKeys(row) {
      return row.id || row.pid;
    },
    // 合并
    arraySpanMethod({ row, column, rowIndex, columnIndex }) {
      // 合并岗位
      if (row.parentType) {
        if (columnIndex === 2) {
          return {
            rowspan: 1,
            colspan: 20
          }
        }
      }
    },
    // 设置失效行的class
    tableRowClassName({ row, rowIndex }) {
      if (!row.type && row.takeEffect === false) {
        return 'warning-row';
      }
      return '';
    },
    // 子排序
    toSortItem(row) {
      this.$request({
        url: this.$baseApi.MAINTASKLIST.itemIndexOrder,
        method: 'PUT',
        data: [{
          "id": row.id,
          "itemIndex": parseFloat(row.itemIndex) || 0
        }]
      }).then(res => {
        this.sortTableData.forEach(o => {
          o.children.sort((a, b) => a.itemIndex - b.itemIndex)
        })
      }, err => {
        this.$message.error("失败!")
      })
    },
    //handleExpandChange 控制父级禁用
    handleExpandChange(row, isExpend) {
      this.$set(row, 'isDisable', isExpend)
    },
    // 当用户手动勾选数据行的 Checkbox 时触发的事件
    handletableSelect(selection, row) {
      if (row.children) { //只对有子节点的行响应
        // if (row.isChecked === undefined) row.isChecked = true
        if (!row.isChecked) {   //由行数据中的元素isChecked判断当前是否被选中
          this.traverseChildNodes(row.children, this.$refs.SortTableTree, true)
          row.isChecked = true; //当前行isChecked标志元素切换为false
        } else {
          this.traverseChildNodes(row.children, this.$refs.SortTableTree, false)
          row.isChecked = false;
        }
      }
      // 处理父级选择逻辑
      // tableSelect 字段是 handletableSelectionChange 回调回来的
      // 如果不用 this.$nextTick 无法获取到正确数据
      this.$nextTick(() => {
        // 递归寻找当前子节点的父级节点
        let getParentNode = function (data, id) {
          for (let i in data) {
            if (data.hasOwnProperty(i)) {
              if (data[i].id === id) {
                return [data[i]]
              }
              if (data[i].children) {
                let node = getParentNode(data[i].children, id);
                if (node !== undefined) {
                  return node.concat(data[i])
                }
              }
            }
          }
        }
        // 设置checkbox半选择状态
        // 此处的传入的id就是匹配表格id字段的
        let updateCheckboxIndeterminate = function (id, isIndeterminate) {
          setTimeout(() => {
            // el-table不支持checkbox半选 目前方案是遍历dom获取行节点设置样式
            let elementsRow = document.getElementsByClassName("el-table__row");
            for (let i = 0; i < elementsRow.length; i++) {
              let element = elementsRow.item(i);
              // tips: 通过其他手段设置行内row-id的 以下代码需要修改
              let childNode = element.childNodes.item(1); // row-key 字段在dom中的索引   第二列=1
              if (childNode.innerText === `${id}`) {
                let td = element.childNodes.item(0) // 获取要设置半选中状态的checkbox
                let div = td.firstChild; // <div class="cell">
                let label = div.firstChild; // <label class="el-checkbox">
                let span = label.firstChild; // <span class="el-checkbox__input">
                if (isIndeterminate)
                  span.classList.add("is-indeterminate")  // 设置半选中状态
                else
                  span.classList.remove("is-indeterminate")  // 取消半选中状态
              }
            }
          }, 1)

        }
        let parentNode = getParentNode(this.$refs.SortTableTree.data, row.id)
        // > 1 说明节点有子节点
        if (parentNode.length > 1) {
          // 提取兄弟节点ids
          let siblingNodeIds = parentNode[1].children.map(item => item.id)
          // 提取全选所选择项的ids
          let checkedIds = this.tableSelect.map(item => item.id)
          // 获取兄弟行选择数
          let siblingNodeCheckedIds = siblingNodeIds.filter(id => checkedIds.indexOf(id) !== -1)
          if (siblingNodeCheckedIds.length === siblingNodeIds.length) {
            // 兄弟节点选择数===兄弟节点数   全选
            updateCheckboxIndeterminate(parentNode[1].id, false)
            parentNode[1].isChecked = true
            this.$refs.SortTableTree.toggleRowSelection(parentNode[1], true)
          } else if (siblingNodeCheckedIds.length === 0) {
            // 兄弟节点选择数===0   全不选
            parentNode[1].isChecked = false
            this.$refs.SortTableTree.toggleRowSelection(parentNode[1], false)
            updateCheckboxIndeterminate(parentNode[1].id, false)
          } else if (siblingNodeCheckedIds.length < siblingNodeIds.length) {
            parentNode[1].isChecked = true
            this.$refs.SortTableTree.toggleRowSelection(parentNode[1], true)
            for (let i = 0; i < parentNode.length; i++) {
              if (i > 0) updateCheckboxIndeterminate(parentNode[i].id, true)
            }
          }
        }
      })
    },
    // 当用户手动勾选全选 Checkbox 时触发的事件
    handletableSelectAll(selection) {
      this.$refs.SortTableTree.data.map(items => {
        if (items.children && items.children.length > 0) {
          if (!items.isChecked) {
            this.$refs.SortTableTree.toggleRowSelection(items, true);
            items.isChecked = true
            this.traverseChildNodes(items.children, this.$refs.SortTableTree, true)
          } else {
            this.$refs.SortTableTree.toggleRowSelection(items, false);
            items.isChecked = false;
            this.traverseChildNodes(items.children, this.$refs.SortTableTree, false)
          }
        } else {
          items.isChecked = !items.isChecked;
        }
      })
    },
    // 当选择项发生变化时会触发该事件
    handletableSelectionChange(selection) {
      this.tableSelect = selection
    },
    // 递归设置子节点
    traverseChildNodes(children, ref, selected) {
      children.map(item => {
        ref.toggleRowSelection(item, selected);
        item.isChecked = selected;
        if (item.children) {
          this.traverseChildNodes(item.children, ref, selected)
        }
      })
    },
  }
};
</script>

<style lang='scss' scoped>
.hand {
  cursor: pointer;
  font-size: 18px;
}

.sortIcon {
  text-align: center;
  display: flex;
  flex-direction: column;
  align-items: center;
}

::v-deep {
  .el-table__row--level-0 {
    background-color: #f7f9fc;
  }

  .el-input--small .el-input__inner {
    padding: 0 5px !important;
    text-align: center;
  }

  .el-table__row--level-1 {
    background-color: #fff;

    .el-checkbox__input {
      margin-left: 15px;
    }
  }

  .el-dialog {
    margin: 5vh auto !important;
  }

  .el-table::before {
    height: 0px;
  }

  .el-table__row>td {
    border: none;
  }
}
</style>

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值