Element - el-tree 树形结构拖拽以及增删改查

1、SystemTreeItem.vue

<el-tree
      :data="treeData"
      node-key="id"
      default-expand-all
      :expand-on-click-node="false"
      @node-drop="handleDrop"
      @node-drag-enter="nodeDragEnter"
      draggable
      :allow-drop="allowDrop"
      :allow-drag="allowDrag"
    >
      <span class="custom-tree-node" :class="{isDisabled: node.data.enabled === false}" slot-scope="{ node, data }" :key="data.id">
        <template v-if="!node.data.isEdit">
          <el-tooltip class="item" effect="light" :content="node.data.knowledgeName" :open-delay="1000" placement="bottom">
            <span class="label" v-if="node.data.enabled" @dblclick="nodeClick(data,node,true)">{{ node.data.knowledgeName }}</span>
            <span class="label isDisabled" v-else-if="node.data.id && !node.data.enabled" @dblclick="nodeClick(data,node,true)">{{ "(已禁用)" + node.data.knowledgeName }}</span>
          </el-tooltip>
        </template>
        <span class="knowledgeCode" v-else-if="node.data.isEdit && hasAuth('knowledge_points_system_edit')">
          <el-input v-model.trim="knowledgeCode" placeholder="请输入知识点code"  v-focus />
          <el-button size="mini" type="primary" :disabled="!knowledgeCode" @click="addKnoeledgeCode(node,data)">确 定</el-button>
          <el-button size="mini" @click="nodeClick(data,node,false)">取 消</el-button>
        </span>
        <span class="operation" v-show="!node.data.isEdit">
            <el-button type="text" class="btn" :disabled="!canStatusChange || !node.data.id" @click="insertAfter(data, node)" style="color: #333">
              添加同级
            </el-button>
            <span>|</span>
            <el-button type="text" class="btn" :disabled="!canStatusChange || !node.data.id" @click="append(data, node)" style="color: #333">
              添加下级
            </el-button>
            <span>|</span>
            <el-button type="text" class="btn" :disabled="!node.data.id" @click="toDetail(data)" style="color: #333">
              查看详情
            </el-button>
            <span>|</span>
            <el-button type="text" class="btn" :disabled="!node.data.id" @click="remove(node, data)" style="color: #333">
              删除
            </el-button>
        </span>
      </span>
    </el-tree>
   
<script>
import { debounce } from '@/utils/comUtil'
export default {
  name: 'systemTreeItem',
  data() {
    return {
      knowledgeCode: '',
    }
  },
  props: {
    treeData: Array,
    canStatusChange: Boolean
  },
  directives: {
    focus: {
      inserted: function(el) {
        el.querySelector("input").focus();
      }
    }
  },
  methods: {
    addKnoeledgeCode(node,data){
      this.$emit('addKnoeledgeCode',node,data,this.knowledgeCode)
      this.knowledgeCode = ""
    },
    // 拖拽成功完成时触发的事件
    handleDrop(draggingNode, dropNode, dropType, e) {
      this.$emit('handleDrop', draggingNode, dropNode, dropType, e)
    },
    // 获取总层级数
    getTotalLevel(node, arr) {
      if (node.childNodes && node.childNodes.length) {
        node.childNodes.forEach(item => {
          arr.push(item.level)
          if(item.childNodes && node.childNodes.length) {
            this.getTotalLevel(item, arr)
          }
        })
      }else{
        arr.push(node.level)
      }
    },
    // 拖拽进入其他节点时触发的事件
    nodeDragEnter(draggingNode, dropNode){
      // console.log("拖拽进入其他节点时触发的事件",draggingNode, dropNode)
      let arr = []
      this.getTotalLevel(draggingNode, arr)
      const totalLevel = Math.max(...arr) - draggingNode.level + 1 + dropNode.level
      if(totalLevel <= 8) return
      this.banMessag();
      arr = []
    },
    banMessag: debounce(function(){
      this.$message({
        type: 'warning',
        message: '节点层级已达最大值!',
      })
    },500),
    // 拖拽时判定目标节点能否被放置
    allowDrop(draggingNode, dropNode, type) {
      //draggingNode 被拖拽的节点
      //dropNode 目标节点
      //type 参数有三种情况:'prev'、'inner' 和 'next',分别表示放置在目标节点前、插入至目标节点和放置在目标节点后
      // console.log(draggingNode.level, dropNode.level, type)
      // 获取被拖拽的节点层级
      let arr = []
      this.getTotalLevel(draggingNode, arr)
      const totalLevel = Math.max(...arr) - draggingNode.level + 1 + dropNode.level
      // 插入至目标节点内部 节点层级大于8级  不能被放置
      if(totalLevel > 8){
        this.banMessag();
        arr = []
        return false
      }else{
        return true;
      }
    },
    // 判断节点能否被拖拽
    allowDrag(draggingNode) {
      // console.log("allowDrag", draggingNode)
      // 节点处于编辑状态 或者 节点为空节点 不能拖拽
      if(draggingNode.data.isEdit || !draggingNode.data.id){
        return false
      }else{
        return true
      }
    },
    // 节点双击事件 节点变为可编辑状态
    nodeClick(data, node, isEdit) {
      if(isEdit === false){
        this.knowledgeCode = ""
      }
      this.$emit('nodeClick', data, node, isEdit)
    },
    // 添加同级
    insertAfter(data, refNode) {
      this.$emit('insertAfter', data, refNode)
    },
    // 添加下级
    append(data, parentNode) {
      this.$emit('append', data, parentNode)
    },
    toDetail(data) {
      this.$emit('toDetail', data)
    },
    remove(node, data) {
      this.$emit('remove', node, data)
    },
  },
}
</script>

2、SystemTree.vue

拖拽涉及代码是:handleDrop

<system-tree-item
  :treeData="systemTreeList"
  :canStatusChange="canStatusChange"
  @handleDrop="handleDrop"
  @nodeClick="nodeClick"
  @insertAfter="insertAfter"
  @append="append"
  @toDetail="toDetail"
  @remove="remove"
  @addKnoeledgeCode="addKnoeledgeCode"
/>
<script>
import systemTreeService from '@/api/systemTreeService'
import SystemTreeItem from './components/SystemTreeItem.vue'
export default {
  name: 'SystemTree',
  components: { SystemTreeItem },
  data() {
    return {
      systemTreeList: [], // 体系树列表
      list: [], // 备份体系树列表数据
      canStatusChange: false //禁用/启用是否可切换  体系树无数据,节点处于编辑状态,节点无id 为false
    }
  },
  created() {
    this.getSystemTreeList()
  },
  methods: {
    // 体系树数据排序
    sortTreeData(arr){
      if(!arr.length) return
      arr.sort((a,b) => a.sort - b.sort)
      arr.forEach(item => {
        if(item.children){
          this.sortTreeData(item.children)
        }
      })
    },
    // 获取体系树列表
    async getSystemTreeList(type) {
      if(!this.systemCode) return
      if (type === 'add') {
        this.systemTreeList = [
          {
            id: '',
            knowledgeCode: '',
            knowledgeName: '',
            parentId: 'ROOT',
            level: 1,
            systemCode: this.systemCode,
            isEdit: true,
            enabled: true,
            children: []
          },
        ]
      } else {
        let res = await systemTreeService.getSystemTreeList({ systemCode: this.systemCode })
        if (res && res.errorCode === 0) {
          this.systemTreeList = res.result // 后端返回的数据为树状结构
          this.sortTreeData(this.systemTreeList)
          // 备份原数据 拖拽失败后还原
          this.list = JSON.parse(JSON.stringify(this.systemTreeList))
          if(this.systemTreeList.length){
            this.canStatusChange = true
          }else{
            this.systemTreeList = [
              {
                id: '',
                knowledgeCode: '',
                knowledgeName: '',
                parentId: 'ROOT',
                level: 1,
                systemCode: this.systemCode,
                isEdit: true,
                enabled: true,
                children: []
              },
            ]
            this.canStatusChange = false
          }
        } else {
          this.systemTreeList = []
          this.canStatusChange = false
        }
      }
    },
    // 添加知识点
    async addKnoeledgeCode(node, data, knowledgeCode) {
      // console.log("添加知识点", node, data)
      node.data.isEdit = false
      data.isEdit = false
      if (knowledgeCode) {
        if(data.id){
          // 替换
          // 如果knowledgeCode等于oldKnowledgeCode
          let oldKnowledgeCode = data.knowledgeCode
          if(knowledgeCode === oldKnowledgeCode){
            this.$message({
              type: 'warning',
              message: '当前knowledgeCode与旧knowledgeCode相同,不能替换!'
            })
            return
          }
          let params = {
            systemCode: this.systemCode,
            nodeId: data.id,
            oldKnowledgeCode,
            newKnowledgeCode: knowledgeCode,
          }
          let res = await systemTreeService.updateKnowledgePoint(params)
          if (res && res.errorCode === 0) {
            this.$message({
              type: 'success',
              message: res.errorInfo,
            })
          }
        }else{
          // 新增  添加同级 添加下级
          // 添加同级 若为一级节点 nodeParentId 为 "", 否则为node.parent.data.id
          let nodeParentId = node.level === 1 ? "" : node.parent.data.id
          let params = {
            systemCode: this.systemCode,
            knowledgeCode,
            nodeParentId,
          }
          let res = await systemTreeService.addKnowledgePoint(params)
          if (res && res.errorCode === 0) {
            this.$message({
              type: 'success',
              message: res.errorInfo,
            })
          }
        }
        this.getSystemTreeList()
      }
    },
    // 拖拽事件
    async handleDrop(draggingNode, dropNode, dropType,e) {
      //draggingNode 被拖拽的节点
      //dropNode 目标节点
      //dropType 类型 被拖拽的节点相对于目标节点的位置 inner before after
      // console.log('tree drop', draggingNode, dropNode, dropType)
      // 难点在于获取受影响的节点,然后遍历,后端根据节点ID修改父节点ID以及sort
      let paramData = [];
      // 当拖拽类型不为inner,同级排序,寻找目标节点的父ID,获取其对象以及所有的子节点
      // 当拖拽类型为inner,说明拖拽节点成为了目标节点的子节点,只需获取目标节点对象
      let data = dropType != "inner" ? dropNode.parent.data : dropNode.data;
      //目标节点为一级节点,并且拖拽类型不为inner即当前节点将成为与目标节点同一级的节点,也是一级节点
      //nodeData=dropNode.parent.data,但是因为目标节点已经是一级节点了,因此nodeData还是目标节点
      //目标节点为一级节点,并且拖拽类型为inner即当前节点将成为目标节点的子节点即二级节点
      //nodeDate=dropNode.parent.data.children,即为目标节点的父节点的子节点,即目标节点同一层级的节点
      //目标节点不是一级节点,并且拖拽类型不为inner即当前节点将成为与目标节点同一层级的节点
      //nodeDate=dropNode.parent.data.children,即为目标节点的父节点的子节点,即目标节点同一层级的节点
      //目标节点不是一级节点,并且拖拽类型为inner即当前节点将成为目标节的子节点
      //nodeDate=dropNode.data.children,即目标节点的子节点
      let nodeData = dropNode.level == 1 && dropType != "inner" ? data : data.children;
      let nodeParentId = ""
      nodeData.forEach((item, i) => {
        if(dropType != "inner"){
          if(draggingNode.data.parentId === dropNode.data.parentId){
            nodeParentId = item.parentId
          }else{
            nodeParentId = dropNode.data.parentId
          }
        }else{
          nodeParentId = data.id
        }
        let collection = {
          nodeId: item.id,
          nodeParentId,
          sort: i + 1
        };
        paramData.push(collection);
      });
      // console.log(paramData)
      let params = {
        systemCode: this.systemCode,
        nodeSortList: paramData
      }
      let res = await systemTreeService.dragTreeNode(params)
      if(res && res.errorCode === 0){
        this.$message({
          type: 'success',
          message: res.errorInfo,
        })
        this.getSystemTreeList()
      }else{
        // 接口报错之后,将数据恢复
        this.systemTreeList = this.list
      }
    },
    // 节点双击事件
    nodeClick(data, node, isEdit) {
      // console.log("nodeClick",data,node,isEdit)
      // 当节点处于编辑状态 或者 节点id不存在
      if(isEdit || !data.id){
        this.canStatusChange = false
      }else{
        this.canStatusChange = true
      }
      this.$set(data,'isEdit',isEdit)
      this.$set(node.data,'isEdit',isEdit)
    },
    // 添加同级
    insertAfter(data, refNode) {
      // console.log("添加同级", data, refNode)
      const newNode = {
        parentId: data.parentId,
        id: '',
        knowledgeCode: '',
        knowledgeName: '',
        level: data.level,
        systemCode: this.systemCode,
        isEdit: true,
        enabled: true,
        children: [],
      }
      this.canStatusChange = false
      if(data.parentId === 'ROOT'){
        refNode.parent.data.push(newNode)
      }else{
        refNode.parent.data.children.push(newNode)
      }
    },
    //添加下级
    append(data, parentNode) {
      // console.log('添加下级', data)
      if (data.level >= 8) {
        this.$message({
          type: 'warning',
          message: '节点层级已达最大值!',
        })
        return
      } else {
        const newChild = {
          parentId: data.id,
          id: '',
          knowledgeCode: '',
          knowledgeName: '',
          level: data.level + 1,
          systemCode: this.systemCode,
          isEdit: true,
          enabled: true,
          children: [],
        }
        this.canStatusChange = false
        if (!data.children) {
          this.$set(data, 'children', [])
        }
        data.children.push(newChild)
      }
    }
  }
}
</script>
  • 0
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
如果您想要实现Element Table树形表格的动功能,可以考虑使用Vue.js框架并结合element-ui组件库,再使用一些自定义指令和事件来实现。 以下是一种实现方式: 1. 在Vue组件中引入element-ui组件库,并在模板中使用el-table组件,设置其tree-props属性为{children: 'children', hasChildren: 'hasChildren'},以支持树形结构。 2. 使用自定义指令v-draggable,在表格行上绑定该指令,使得行可以被动。 3. 监听el-table组件的row-drop事件,在该事件中更新数据源,以实现行的排序。 下面是示例代码: ```html <template> <el-table :data="tableData" :tree-props="{ children: 'children', hasChildren: 'hasChildren' }" @row-drop="handleRowDrop" > <el-table-column prop="name" label="名称"></el-table-column> <el-table-column prop="age" label="年龄"></el-table-column> <el-table-column prop="address" label="地址"></el-table-column> <el-table-column label="操作"> <template slot-scope="scope"> <button v-if="scope.row.hasChildren" @click="toggleNode(scope.row)"> {{ scope.row.expanded ? '收起' : '展开' }} </button> </template> </el-table-column> <el-table-column label="动" width="80"> <template slot-scope="scope"> <div v-draggable class="drag-handle"> <i class="el-icon-s-grid"></i> </div> </template> </el-table-column> </el-table> </template> <script> import { directive } from 'vuedraggable' export default { directives: { draggable: directive }, data() { return { tableData: [ { name: '张三', age: 18, address: '北京市朝阳区', children: [ { name: '李四', age: 22, address: '北京市海淀区', children: [], hasChildren: false } ], hasChildren: true }, { name: '王五', age: 30, address: '上海市浦东区', children: [], hasChildren: false } ] } }, methods: { toggleNode(node) { this.$refs.table.toggleRowExpansion(node) }, handleRowDrop(event) { const { treeData, newIndex, oldIndex } = event // 更新数据源 treeData.splice(newIndex, 0, treeData.splice(oldIndex, 1)[0]) } } } </script> <style> .drag-handle { cursor: move; display: inline-block; padding: 5px; border: 1px solid #ccc; border-radius: 4px; text-align: center; } </style> ``` 在上面的代码中,我们首先引入了vuedraggable库中的directive指令,并将其作为自定义指令draggable注册到Vue实例中。 在模板中,我们将el-table组件的tree-props属性设置为指定的树形结构字段,以支持树形表格的展示。然后在动列中使用v-draggable指令,使得行可以被。 最后,我们监听了el-table组件的row-drop事件,在事件中更新数据源,以实现行的排序。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值