vue实现table拖拽到tree生成数据,树增删编辑数据

转自:https://blog.csdn.net/If_CannewBoyFriend/article/details/108072059

拖拽树主要是绑定 mousedown mouseup 事件 主要参考ztree官网 http://www.treejs.cn/v3/main.php#_zTreeInfo

树结构样式参考 https://github.com/tower1229/Vue-Giant-Tree

下载地址 https://download.csdn.net/download/If_CannewBoyFriend/12720563

存在问题:拖拽时候显示的信息不随鼠标移动,一直在页面底部,
解决方案:在bindMouseDown 方法中显示的信息添加绝对定位,父级相对定位

<span class='dom_tmp' style='position:absolute'>" +
            this.targetNode.name +
 </span>

table拖拽

bindDom () {
  this.$nextTick(() => {
    $('.dragBox').bind('mousedown', this.bindMouseDown)
  })
},
bindMouseDown (e) {
  const target = e.target
  const id = $(target).parent('.dragbody').attr('dataID')
  console.log(id)
  if (target != null && id) {
    const doc = $(document); const target = $(target)
    const docScrollTop = doc.scrollTop()
    const docScrollLeft = doc.scrollLeft()
    const obj = this.tableData.filter(item => item.id === id)
    if (obj && obj.length > 0) {
      this.targetNode = obj[0]
    }
    console.log(this.targetNode)
    const curDom = $("<span class='dom_tmp'>" + this.targetNode.name + '</span>')
    if (this.curTmpTarget) this.curTmpTarget.remove()
    curDom.appendTo('body')
    curDom.css({
      'top': (e.clientY + docScrollTop + 3) + 'px',
      'left': (e.clientX + docScrollLeft + 3) + 'px'
    })
    this.curTarget = target
    this.curTmpTarget = curDom
    doc.bind('mousemove', this.bindMouseMove)
    doc.bind('mouseup', this.bindMouseUp)
  }
  if (e.preventDefault) {
    e.preventDefault()
  }
},
bindMouseMove (e) {
  this.noSel()
  const doc = $(document)
  const docScrollTop = doc.scrollTop()
  const docScrollLeft = doc.scrollLeft()
  const tmpTarget = this.curTmpTarget
  if (tmpTarget) {
    tmpTarget.css({
      'top': (e.clientY + docScrollTop + 3) + 'px',
      'left': (e.clientX + docScrollLeft + 3) + 'px'
    })
  }
  return false
},
bindMouseUp (e) {
  const doc = $(document)
  doc.unbind('mousemove', this.bindMouseMove)
  doc.unbind('mouseup', this.bindMouseUp)
  // const target = this.curTarget
  const tmpTarget = this.curTmpTarget
  if (tmpTarget) tmpTarget.remove()
  /* if ($(e.target).parents('#treeDemo').length === 0) {
    console.log(target)
  } */
},
noSel () {
  /* try {
      window.getSelection ? window.getSelection().removeAllRanges() : document.selection.empty();
  } catch(e){} */
},
// 拖动添加数据 用于捕获 zTree 上鼠标按键松开后的事件回调函数
onMouseUpFun (e, treeId, treeNode) {
  try {
    const target = this.curTarget
    const tmpTarget = this.curTmpTarget
    if (!target) return
    const parentNode = treeNode
    if (tmpTarget) tmpTarget.remove()
    this.curTarget = null
    this.curTmpTarget = null
    if (parentNode) {
      return new Promise((resolve, reject) => {
        addMediaTree(this.targetNode).then(response => {
          if (response && response.errorMsg === '成功') {
            this.$message({
              message: '添加成功!',
              type: 'success'
            })
            // 添加成功之后 更新树
            const dadd = response.data
            // 树展示的属性用的name 我的接口返回的是title  处理数据
            dadd['name'] = dadd.title
            var nodes = this.ztreeObj.addNodes(parentNode, dadd, true)
            console.log(nodes)
          } else {
            this.$message({
              message: '添加失败!',
              type: 'error'
            })
          }
          resolve(response)
        }).catch(error => {
          reject(error)
        })
      })
    } else {
      console.log('数据错误')
    }
  } catch (e) {
    console.log(e)
  }
},

右键方法::


// 右键显示栏目 event, treeId, treeNode
openMenu (e, treeId, object) {
  /* let hasAuthB = false
  if (this.HasAuth('/assettree/add') || this.HasAuth('/assettree/edit') ||
    this.HasAuth('/assettree/delete') || this.HasAuth('/assettree/poster')) {
    hasAuthB = true
  }
  console.log(object.isAuthorized, hasAuthB)
  if (!object.isAuthorized || !hasAuthB) {
    this.$message({
      message: '您暂无权限操作',
      type: 'info'
    })
    return
  } */
  /* if (object.children && object.children.length > 0) {
    this.isHasChildren = true
  } else {
    this.isHasChildren = false
  } */
  this.treeData = object
  const menuMinWidth = 105
  const offsetLeft = this.$el.getBoundingClientRect().left // container margin left
  const offsetWidth = this.$el.offsetWidth // container width
  const maxLeft = offsetWidth - menuMinWidth // left boundary
  const left = e.clientX - offsetLeft + 20// 15: margin right
 
  document.addEventListener('click', (e) => {
    this.visible = false
  })
 
  if (left > maxLeft) {
    this.left = maxLeft
  } else {
    this.left = left
  }
  this.top = e.clientY - 80
 
  this.visible = true
},

实现



<template>
  <div id="app">
    <!--<img src="./assets/logo.png">-->
    <!--<HelloWorld/>-->
    <el-row style="margin-top: 20px;">
      <el-col :span="6" :offset="6">
        <el-row>
          <tree
            :setting="setting"
            :nodes="setTree"
            @onCreated="handleCreated"
          />
        </el-row>
        <ul v-show="visible" :style="{left:left+'px',top:top+'px'}" class="contextmenu">
          <li  @click="NodeAdd()">添加下级栏目</li>
          <li  @click="NodeEdit">编辑栏目</li>
          <li  @click="NodeDel">移除该栏目</li>
        </ul>
      </el-col>
      <el-col :span="12">
        <table class="el-table table table-striped">
          <thead class="thead-dark">
          <tr>
            <th><div class="cell">ID</div></th>
            <th><div class="cell">名称</div></th>
            <th><div class="cell">描述</div></th>
          </tr>
          </thead>
          <tbody class="dragBox">
          <tr v-for="(item,index) in tableData" :key="index" class="dragbody" :dataID="item.id">
            <td class="dragtr" style="text-align: center">{{ item.id }}</td>
            <td style="text-align: center">{{ item.name }}</td>
            <td style="text-align: center">{{ item.desc }}</td>
          </tr>
          </tbody>
        </table>
      </el-col>
    </el-row>
    <div style="margin-top: 100px;">实现功能拖拽右侧表格到左侧树,自动生成数据</div>
    <!--左边节点的增删改查修改 -->
    <el-dialog
      :title="dialog.title"
      :visible.sync="dialog.show"
      :close-on-click-modal="false"
      :close-on-press-escape="false"
      :modal-append-to-body="false"
      :before-close="handleClose"
      custom-class="centerDialog"
    >
      <el-form
        ref="menuData"
        :model="menuData"
        :rules="form_rules"
        label-width="120px"
        style="margin:10px;width:auto;"
      >
        <el-form-item prop="name" label="节点名称">
          <el-input v-model="menuData.name" type="name" placeholder="请输入媒资树节点名称" />
        </el-form-item>
        <el-form-item prop="description" label="节点描述">
          <el-input v-model="menuData.desc" type="textarea" placeholder="请输入节点描述" />
        </el-form-item>
      </el-form>
      <div slot="footer" class="dialog-footer">
        <el-button @click="handleClose()">取 消</el-button>
        <el-button type="primary" @click="onSubmitMenu('menuData')">提 交</el-button>
      </div>
    </el-dialog>
  </div>
</template>
 
<script>
import * as $ from 'jquery'
import tree from '@/components/ztree'
import {addMediaTree, delMediaTree, editMediaTree} from '@/api/index'
if (!window.jQuery) {
  window.jQuery = $
}
export default {
  name: 'App',
  components: {
    tree
  },
  data () {
    return {
      setting: {
        edit: {
          enable: true,
          showRemoveBtn: false,
          showRenameBtn: false,
          drag: {
            prev: true,
            next: true,
            inner: true
          }
        },
        check: {
          enable: false
        },
        data: {
          simpleData: {// 设置数据键值
            enable: true,
            idKey: 'treeID',
            pIdKey: 'parentTreeID'
          }
        },
        view: {
          showIcon: false,
          addDiyDom: this.addDiyDom // 自定义图标
        },
        callback: {
          onMouseUp: this.onMouseUpFun,
          onRightClick: this.openMenu
        }
      },
      setTree: [
        {treeID: '1', name: '少儿剧场', processStatusID: 'input'},
        {treeID: '2', name: '经典怀旧', processStatusID: 'rejected'},
        {treeID: '3', name: '少儿舞蹈', parentTreeID: '1', processStatusID: 'issued'},
        {treeID: '4', name: '川剧', parentTreeID: '2', processStatusID: 'checked'},
        {treeID: '5', name: '韩流剧场', processStatusID: 'input'}
      ],
      ztreeObj: null,
      iconName: {
        'input': 'iconluru inputIcon',
        'rejected': 'iconshibai rejectedIcon',
        'issued': 'icontongguo issuedIcon',
        'checked': 'iconyishen checkedIcon'
      },
      tableData: [
        {id: '11', name: '少儿英语', desc: '少儿英语'},
        {id: '22', name: '周星驰特辑', desc: '周星驰特辑'},
        {id: '33', name: '邪恶之花', desc: '悬疑韩剧'},
        {id: '44', name: '非遗扎染', desc: '非遗扎染'}
      ],
      targetNode: null,
      treeData: {},
      curTmpTarget: null,
      curTarget: null,
      visible: false,
      left: 0,
      top: 0,
      dialog: {
        show: false,
        option: '',
        title: ''
      },
      menuData: {},
      // 自定义验证
      form_rules: {}
    }
  },
  methods: {
    // 自定义图标 使用icon font
    addDiyDom (treeId, treeNode) {
      const aObj = $('#' + treeNode.tId + '_a')
      const className = this.iconName[treeNode.processStatusID]
      const editStr = '<i class="iconfont ' + className + '" ></i>'
      aObj.after(editStr)
    },
    // 拖动添加数据
    onMouseUpFun (e, treeId, treeNode) {
      try {
        const target = this.curTarget
        const tmpTarget = this.curTmpTarget
        if (!target) return
        const parentNode = treeNode
        if (tmpTarget) tmpTarget.remove()
        this.curTarget = null
        this.curTmpTarget = null
        if (parentNode) {
          return new Promise((resolve, reject) => {
            addMediaTree(this.targetNode).then(response => {
              if (response && response.errorMsg === '成功') {
                this.$message({
                  message: '添加成功!',
                  type: 'success'
                })
                // 添加成功之后 更新树
                const dadd = response.data
                // 树展示的属性用的name 我的接口返回的是title  处理数据
                dadd['name'] = dadd.title
                var nodes = this.ztreeObj.addNodes(parentNode, dadd, true)
                console.log(nodes)
              } else {
                this.$message({
                  message: '添加失败!',
                  type: 'error'
                })
              }
              resolve(response)
            }).catch(error => {
              reject(error)
            })
          })
        } else {
          console.log('数据错误')
        }
      } catch (e) {
        console.log(e)
      }
    },
    handleCreated: function (ztreeObj) {
      this.ztreeObj = ztreeObj
      ztreeObj.expandNode(ztreeObj.getNodes()[0], true)
 
      //
      this.bindDom()
    },
    bindDom () {
      this.$nextTick(() => {
        $('.dragBox').bind('mousedown', this.bindMouseDown)
      })
    },
    bindMouseDown (e) {
      const target = e.target
      const id = $(target).parent('.dragbody').attr('dataID')
      console.log(id)
      if (target != null && id) {
        const doc = $(document); const target = $(target)
        const docScrollTop = doc.scrollTop()
        const docScrollLeft = doc.scrollLeft()
        const obj = this.tableData.filter(item => item.id === id)
        if (obj && obj.length > 0) {
          this.targetNode = obj[0]
        }
        console.log(this.targetNode)
        const curDom = $("<span class='dom_tmp'>" + this.targetNode.name + '</span>')
        if (this.curTmpTarget) this.curTmpTarget.remove()
        curDom.appendTo('body')
        curDom.css({
          'top': (e.clientY + docScrollTop + 3) + 'px',
          'left': (e.clientX + docScrollLeft + 3) + 'px'
        })
        this.curTarget = target
        this.curTmpTarget = curDom
        doc.bind('mousemove', this.bindMouseMove)
        doc.bind('mouseup', this.bindMouseUp)
      }
      if (e.preventDefault) {
        e.preventDefault()
      }
    },
    bindMouseMove (e) {
      this.noSel()
      const doc = $(document)
      const docScrollTop = doc.scrollTop()
      const docScrollLeft = doc.scrollLeft()
      const tmpTarget = this.curTmpTarget
      if (tmpTarget) {
        tmpTarget.css({
          'top': (e.clientY + docScrollTop + 3) + 'px',
          'left': (e.clientX + docScrollLeft + 3) + 'px'
        })
      }
      return false
    },
    bindMouseUp (e) {
      const doc = $(document)
      doc.unbind('mousemove', this.bindMouseMove)
      doc.unbind('mouseup', this.bindMouseUp)
      // const target = this.curTarget
      const tmpTarget = this.curTmpTarget
      if (tmpTarget) tmpTarget.remove()
      /* if ($(e.target).parents('#treeDemo').length === 0) {
        console.log(target)
      } */
    },
    noSel () {
      /* try {
          window.getSelection ? window.getSelection().removeAllRanges() : document.selection.empty();
      } catch(e){} */
    },
    // 右键显示栏目 event, treeId, treeNode
    openMenu (e, treeId, object) {
      /* let hasAuthB = false
      if (this.HasAuth('/assettree/add') || this.HasAuth('/assettree/edit') ||
        this.HasAuth('/assettree/delete') || this.HasAuth('/assettree/poster')) {
        hasAuthB = true
      }
      console.log(object.isAuthorized, hasAuthB)
      if (!object.isAuthorized || !hasAuthB) {
        this.$message({
          message: '您暂无权限操作',
          type: 'info'
        })
        return
      } */
      /* if (object.children && object.children.length > 0) {
        this.isHasChildren = true
      } else {
        this.isHasChildren = false
      } */
      this.treeData = object
      const menuMinWidth = 105
      const offsetLeft = this.$el.getBoundingClientRect().left // container margin left
      const offsetWidth = this.$el.offsetWidth // container width
      const maxLeft = offsetWidth - menuMinWidth // left boundary
      const left = e.clientX - offsetLeft + 20// 15: margin right
 
      document.addEventListener('click', (e) => {
        this.visible = false
      })
 
      if (left > maxLeft) {
        this.left = maxLeft
      } else {
        this.left = left
      }
      this.top = e.clientY - 80
 
      this.visible = true
    },
    handleClose () {
      // 关闭弹窗 清除之定义验证提示消息
      this.dialog.show = false
      this.$refs['menuData'].clearValidate()
    },
    // 删除节点
    NodeDel () {
      if (this.treeData.children && this.treeData.children.length !== 0) {
        this.$message.error('此节点有子级,不可删除!')
        return false
      } else {
        // 新增节点可直接删除,已存在的节点要二次确认
        // 删除操作
        const DelFun = () => {
          return new Promise((resolve, reject) => {
            delMediaTree(this.treeData.treeID).then(response => {
              if (response && response.errorMsg === '成功') {
                this.$message({
                  type: 'success',
                  message: '删除成功'
                })
                // 更新树
                this.ztreeObj.removeNode(this.treeData)
                resolve()
              } else {
                this.$message({
                  type: 'error',
                  message: '删除失败!'
                })
                resolve()
              }
            }).catch(error => {
              reject(error)
            })
          })
        }
        // 二次确认
        const ConfirmFun = () => {
          this.$confirm('是否删除此节点?', '提示', {
            confirmButtonText: '确认',
            cancelButtonText: '取消',
            type: 'warning',
            center: true
          }).then(() => {
            DelFun()
          }).catch(() => {
            this.$message({
              type: 'info',
              message: '取消删除!'
            })
          })
        }
        ConfirmFun()
      }
    },
    // 添加节点
    NodeAdd () { // 新增节点,
      this.dialog.show = true
      this.dialog.title = '添加节点信息'
      this.dialog.option = 'add'
      this.dialog.isTop = false
      this.menuData = {
        treeID: 0
      }
    },
    // 编辑节点
    NodeEdit () { // 编辑节点
      this.dialog.show = true
      this.dialog.option = 'edit'
      this.menuData = Object.assign({}, this.treeData)
      this.dialog.title = '编辑该节点信息'
    },
    // 保存节点修改
    onSubmitMenu (form) {
      this.$refs[form].validate(valid => {
        if (valid) {
          // 此时this.menuData 保留了树结构的字段 直接提交后端报错 所以处理数据
          const subData = {
            title: this.menuData.name,
            treeID: this.menuData.treeID,
            parentTreeID: this.menuData.parentTreeID
          }
          if (this.dialog.option === 'edit') {
            return new Promise((resolve, reject) => {
              editMediaTree(subData).then(response => {
                if (response && response.errorMsg === '成功') {
                  this.dialog.show = false
                  this.$message({
                    message: '编辑成功!',
                    type: 'success'
                  })
                  /**
                   * this.ztreeObj.updateNode(this.menuData)
                   * updateNode 更新树只能更新某些特定字段 具体参考 ztree 官网
                   */
                  // 删除
                  this.ztreeObj.removeNode(this.menuData)
                  // 找到父节点
                  const filterData = (node) => {
                    return node.treeID === this.menuData.parentTreeID
                  }
                  const pid = this.ztreeObj.getNodesByFilter(filterData, true)
                  // 添加
                  var nodes = this.ztreeObj.addNodes(pid, this.menuData, true)
                  console.log(nodes)
                  resolve()
                } else {
                  this.$message({
                    message: '编辑失败!',
                    type: 'error'
                  })
                  resolve(response)
                }
              }).catch(error => {
                reject(error)
              })
            })
          } else {
            return new Promise((resolve, reject) => {
              addMediaTree(subData).then(response => {
                this.dialog.show = false
                if (response && response.errorMsg === '成功') {
                  this.$message({
                    message: '添加成功!',
                    type: 'success'
                  })
                  let parentNode = null
                  const addData = response.data
                  // 处理属性不一致
                  addData.name = addData.title
                  if (addData.parentTreeID) {
                    parentNode = this.treeData
                  }
                  var nodes = this.ztreeObj.addNodes(parentNode, addData, true)
                  console.log(nodes)
                } else {
                  this.$message({
                    message: '添加失败!',
                    type: 'error'
                  })
                }
                resolve(response)
              }).catch(error => {
                this.dialog.show = false
                reject(error)
              })
            })
          }
        }
      })
    }
  }
}
</script>
 
<style>
#app {
  font-family: 'Avenir', Helvetica, Arial, sans-serif;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-align: center;
  color: #2c3e50;
  margin-top: 60px;
}
.inputIcon{
  color: #409eff;
  font-size: 14px;
  margin-left: 5px;
}
.rejectedIcon{
  color: #f56c6c;
  font-size: 17px;
  margin-left: 5px;
}
.issuedIcon{
  color: #67c23a;
  font-size: 17px;
  margin-left: 5px;
}
.checkedIcon{ color: #e6a23c;font-size: 17px;margin-left: 5px;}
 
.el-table th{
  white-space: nowrap;
  overflow: hidden;
  user-select: none;
  background-color: #fff;
}
th {
  padding: 12px 0;
  min-width: 0;
  box-sizing: border-box;
  text-overflow: ellipsis;
  vertical-align: middle;
  position: relative;
  text-align: left;
}
.el-table .cell {
  box-sizing: border-box;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: normal;
  word-break: break-all;
  line-height: 23px;
  padding-left: 10px;
  padding-right: 10px;
  text-align: center;
}
.span-ellipsis {
  width: 100%;
  overflow: hidden;
  white-space: nowrap;
  text-overflow: ellipsis;
  display: block;
}
  .dragbody{
    cursor: pointer;
  }
.dom_tmp{
  position: absolute;
  font-size: 16px;
  color: #606266;
}
.contextmenu {
  margin: 0;
  background: #fff;
  z-index: 100;
  position: absolute;
  list-style-type: none;
  padding: 5px 0;
  border-radius: 4px;
  font-size: 14px;
  font-weight: 400;
  color: #333;
  text-align: center;
  box-shadow: 2px 2px 3px 0 rgba(0, 0, 0, .3);
 
}
 
.contextmenu li {
  margin: 0;
  padding: 7px 16px;
  cursor: pointer;
}
 
.contextmenu li:hover {
  background: #eee;
}
</style>
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值