zoukankan      html  css  js  c++  java
  • vue中使用element来创建目录列表

    之前按照项目需求使用element中的tree来创建目录列表,如今记录一下。

    一、项目需求

    1.完整展示目录列表

    2.右击节点选择重命名,删除,创建文件夹三个选项

    3.拖拽文件夹,其中拖拽文件夹有以下要求:

      a. 如果该文件夹内已存在上传文件,则其他文件夹不能拖拽进入该文件夹内

      b.整个目录中有且仅有一个根目录,拖拽文件夹的范围只能在该根目录里面

    4.重命名文件夹要求:

      a.重命名完成后按enter键完成

    5.删除文件夹要求:

      a.如果该文件夹内已含有上传文件,则删除失败

    6.创建文件夹要求:

      a.如果该文件夹内已含有上传文件,则创建失败

    二、思路整理

    1.在mounted中向后台请求项目列表,然后存储treeData

    2.鼠标右键弹出的是menu菜单选项框,对于获取点击节点的信息可以使用el-tree中的 @node-contextmenu="rightClick"

    3.选择删除文件夹的时候要注意在发送请求的同时也要完成前端的“删除”动作,使用map,indexOf, slice

    4.重命名中使用@keyup.enter.native来触发请求,使用focus()聚焦输入框

    5.鼠标右击的时候menu的位置是随之改变的,且当鼠标位置加上menu的高度或宽度超出视频范围的时候要合适调节menu的位置。

    /* 菜单定位基于鼠标点击位置 */
              let height = document.documentElement.clientHeight || document.body.clientHeight
              if (event.clientY + 168 > height) {
                menu.style.left = event.clientX - 5 + 'px'
                menu.style.top = event.clientY - 10 - 168 + 'px'
              } else {
                menu.style.left = event.clientX + 10 + 'px'
                menu.style.top = event.clientY + 5 + 'px'
              }

    6.menu的显示与隐藏,当鼠标点击其他位置的时候,menu隐藏

    document.addEventListener('click', this.hide, true)
    document.removeEventListener('click', this.hide)

    7.当文件夹命名过长的时候,出现横向滚动条

    .comp-tree {
        margin-top: 1em;
        overflow-y: hidden;
      overflow-x: scroll;
    }
    
     .el-tree {
         min-width: 100%;
         display:inline-block !important;
     }

    三、源码分析

    参考:

    点击

    参考

    在此基础上做的修改

    <template>
        <div v-loading="isLoading" class="comp-tree" @click="hiddenMenu">
            <el-tree ref="SlotTree"
                :data="treeData"
                :props="defaultProps"
                :expand-on-click-node="true"
                highlight-current
                :node-key="NODE_KEY"
                @node-click="handleNodeClick"
                @node-contextmenu="rightClick"
                @node-drag-start = "handleDragStart"
          @node-drop="handleDrop"
                :draggable = draggable
                default-expand-all
            >
                <div class="comp-tr-node" slot-scope="{ node, data }">
                    <!-- 编辑状态 -->
                    <template v-if="node.isEdit">
                        <el-input v-model="data.menuName"
                            autofocus
                            size="mini"
                            :ref="'slotTreeInput'+data.recordId"
                            @keyup.enter.native="handleInput(node, data)">
                        </el-input>
                    </template>
    
                    <!-- 非编辑状态 -->
                    <template v-else>
                        <!-- 名称: 新增节点增加class(is-new) -->
                        <span>
                            <span>
                                {{ node.label }}
                            </span>
                        </span>
                    </template>
                </div>
            </el-tree>
    
             <!-- 鼠标右键产生的选项 -->
          <div v-show="menuVisible" id="menu">
            <el-menu
              class="el-menu-vertical rightClickMenu"
              @select="handleRightSelect"
                        text-color="#303133"
                        active-text-color = "#303133"
                        >
              <el-menu-item index="1" class="menuItem">
                <i class="el-icon-edit"></i>
                <span slot="title">重命名</span>
              </el-menu-item>
              <el-menu-item index="2" class="menuItem">
                <i class="el-icon-folder-add"></i>
                <span slot="title">新建文件夹</span>
              </el-menu-item>
              <el-menu-item index="3" class="menuItem">
                <i class="el-icon-delete"></i>
                <span slot="title">删除</span>
              </el-menu-item>
            </el-menu>
          </div>
                <detail-win v-if="isShow" @close="closeWin" :node = NODE v-bind="$attrs" v-on="$listeners"></detail-win>
        </div>
    </template>
    
    <script>
      import detailWin from './detail-win'
      import { baseWarn } from 'src/common/fn'
     
      export default {
        components: {
          detailWin
        },
        data () {
          return {
            isLoading: false, // 是否加载
            NODE_KEY: 'recordId', // id对应字段
            defaultProps: {// 默认设置
              children: 'children',
              label: 'menuName'
            },
            menuVisible: false,
            isShow: false,
            clickNode: '',
            DATA: null,
            NODE: null,
            objectID: null,
            dragPid: null,
            dragIndex: null,
            newParentId: null,
            oldMenuName: null
          }
        },
        computed: {
          treeData () {
            return this.$sysStore.fileTreeData
          },
          addNode () {
            return this.$sysStore.addNode
          },
          draggable () {
            if (!this.$store.state.user.isOuterUser) {
              return true
            } else {
              return false
            }
          }
        },
        watch: {
        },
        created () {
        },
        mounted () {
        },
        methods: {
          // 修改节点
          handleInput (node, data) {
            // 退出编辑状态
            if (node.isEdit) {
              this.$set(node, 'isEdit', false)
            }
            if (this.oldMenuName === data.menuName) {
    
            } else {
              this.$axios({
                url: '........',
                method: 'post',
                data: {
                  ...
                }
              })
                .then(res => {
                  this.$message({ type: 'success', message: '重命名成功' })
                })
                .catch(function (error) {
                  this.$message({ type: 'error', message: '重命名失败' })
                  console.log(error)
                })
            }
          },
    
          // 重命名
          handleEdit (node, data) {
            // 模板文件夹名称不能重命名
            if (data.isTemplate === 1) {
              this.$message.error('该文件夹无法重命名')
            } else {
              // 设置编辑状态
              if (!node.isEdit) {
                this.$set(node, 'isEdit', true)
              }
    
              // 输入框聚焦
              this.$nextTick(() => {
                if (this.$refs['slotTreeInput' + data.recordId]) {
                  this.$refs['slotTreeInput' + data.recordId].$refs.input.focus()
                }
              })
            }
          },
          // 新增节点
          handleAdd (node, data) {
            // console.log(node,data)
            if (data.fileSum !== 0) {
              this.$message.error('此文件夹已存在上传文件,不能新增文件夹!')
              return
            }
            this.isShow = true
          },
          // 删除节点
          handleDelete (node, data) {
            if (data.children && data.children.length !== 0) {
              this.$message.error('此文件夹内含有其他文件夹,不可删除!')
              return
            }
            if (data.fileSum !== 0) {
              this.$message.error('此文件夹内含有上传文件,不可删除!')
            } else {
              this.$confirm(`是否删除${node.label}?`, '提示', {
                confirmButtonText: '确认',
                cancelButtonText: '取消',
                type: 'warning'
              }).then(() => {
                this.deleteProj(node, data)
              }).catch(() => {})
            }
          },
          deleteProj (node, data) {
            this.$axios({
              url: '...',
              method: 'post',
              data: {
                ...
              }
            })
              .then(res => {
                if (res.success) {
                  let _list = node.parent.data.children || node.parent.data// 节点同级数据
                  let _index = _list.map((c) => c.recordId).indexOf(data.recordId)
                  _list.splice(_index, 1)
                  this.$message.success('删除成功')
                  // 使右侧列表消失
                  this.$parent.$parent.disabled = true
                  // 直接改变vuex中treeData的数值
                  let fileTreeData = this.filter(this.treeData, node.data.kid)
                  this.$store.dispatch(`${this.wpstore}/setFileTreeData`, fileTreeData)
                } else {
                  this.$message.error('删除失败,请重试')
                }
              })
          },
    
          /**
           * 删除或者更改treeData中指定kid的子节点
           *
           * @returns arr
           */
          filter (arr, kid) {
            for (var i = 0; i < arr.length; i++) {
              var el = arr[i]
              if (el.kid === kid) {
                arr.splice(i, 1)
              } else {
                if (el.children && el.children.length) {
                  this.filter(el.children, kid)
                }
              }
            }
            return arr
          },
    
          // 点击节点
          handleNodeClick (node) {
            this.menuVisible = false
            this.$store.dispatch(`${this.wpstore}/selectNode`, node)
          },
    
          // 拖拽开始时记录该节点的pid及其在原来数组中的原始数据
          handleDragStart (node) {
            this.dragPid = node.data.recordPid
            let p = this.$refs.SlotTree.getNode(this.dragPid)
            let _list = p.data.children // 节点同级数据
            let _index = _list.map((c) => c.recordId).indexOf(node.data.recordId)
            this.dragIndex = _index
          },
    
          // 拖拽成功时触发请求
          handleDrop (draggingNode, dropNode, dropType, e) {
            // debugger
            if (dropNode.data.fileSum !== 0 && dropType === 'inner') {
              baseWarn('该文件夹内含有上传文件,拖拽失败', null, null, null)
              let _list = dropNode.data.children // 节点同级数据
              let _index = _list.map((c) => c.recordId).indexOf(draggingNode.data.recordId)
              _list.splice(_index, 1)
              // 将节点返回到原来的位置上
              let p = this.$refs.SlotTree.getNode(this.dragPid)
              // console.log(p,'p')
              p.data.children.splice(this.dragIndex, 0, draggingNode.data)
            } else if (dropNode.data.recordPid === 'root') {
              baseWarn('无法拖拽为根文件夹', null, null, null)
              this.$refs.SlotTree.remove(draggingNode.data.recordId)
              let p = this.$refs.SlotTree.getNode(this.dragPid)
              p.data.children.splice(this.dragIndex, 0, draggingNode.data)
            } else {
              // 算出子节点对于父节点的相对位置
              let _list = dropNode.parent.data.children // 拖拽节点同级数据
              let _index = _list.map((c) => c.recordId).indexOf(draggingNode.data.recordId)
              if (_index === -1) {
                _list = dropNode.childNodes
                _index = _list.map((c) => c.data.recordId).indexOf(draggingNode.data.recordId)
              }
              // 得出目标节点的recordId
              if (dropNode.parent.data.recordId === this.dragPid) {
                // 相同的父节点
                this.newParentId = this.dragPid
              } else {
                this.newParentId = dropNode.parent.data.recordId
              }
              // 发送请求
              this.$axios({
                url: '...',
                method: 'post',
                data: {
                  ...
                }
              })
                .then(res => {
                  this.$message({ type: 'success', message: '拖拽成功' })
                })
                .catch(function (error) {
                  console.log(error)
                })
            }
          },
    
          // 鼠标右击
          rightClick (event, object, value, element) {
            this.oldMenuName = object.menuName
            if (this.$store.state.user.isOuterUser) {
              this.menuVisible = false
            } else {
              if (this.objectID !== object.recordId) {
                this.objectID = object.recordId
                this.menuVisible = true
                this.DATA = object
                this.NODE = value
              } else {
                this.menuVisible = !this.menuVisible
              }
              // document.addEventListener('click', (e) => {
              //   this.menuVisible = false
              // })
              this.hiddenMenu()
              let menu = document.querySelector('.rightClickMenu')
              /* 菜单定位基于鼠标点击位置 */
              let height = document.documentElement.clientHeight || document.body.clientHeight
              if (event.clientY + 168 > height) {
                menu.style.left = event.clientX - 5 + 'px'
                menu.style.top = event.clientY - 10 - 168 + 'px'
              } else {
                menu.style.left = event.clientX + 10 + 'px'
                menu.style.top = event.clientY + 5 + 'px'
              }
              menu.style.position = 'fixed'
            }
          },
          handleRightSelect (key) {
            this.menuVisible = false
            if (key == 1) {
              this.handleEdit(this.NODE, this.DATA)
            } else if (key == 2) {
              this.handleAdd(this.NODE, this.DATA)
            } else if (key == 3) {
              this.handleDelete(this.NODE, this.DATA)
            }
          },
          hiddenMenu () {
            document.addEventListener('click', this.hide, true)
            document.removeEventListener('click', this.hide)
          },
    
          hide() {
            this.menuVisible = false
          },
          closeWin (val) {
            this.isShow = val
          }
        }
      }
    </script>
    
    <style lang="scss" scoped>
    .el-menu {
        border: solid 1px #e6e6e6
    }
    
    .comp-tree {
        margin-top: 1em;
        overflow-y: hidden;
      overflow-x: scroll;
    }
    
     .el-tree {
         min- 100%;
         display:inline-block !important;
     }
    
    .rightClickMenu {
        z-index: 999
    }
    
    </style>
    View Code

    这里仅供参考,具体的编写代码还需根据各位实际项目需求来修改

  • 相关阅读:
    in_array()和explode()的使用笔记
    写sql语句连接的时候注意的一个小细节
    在thinkphp框架模板中引用session
    查询数据库所有(某个)表中字段名,数据类型,说明等,导出数据字典
    (委托事件处理)关于多线程执行显示进度条的实例(转)&&线程间操作无效: 从不是创建控件“rtxtEntryNO”的线程访问它。
    判断当前线程所处的状态 (转)以及终止当前线程
    string 字符串的分隔处理与list的相互转换
    C# 动态调用webservice
    C#中的List<string>泛型类示例
    命名空间"system.web"中不存在类型或命名空间名称security"
  • 原文地址:https://www.cnblogs.com/lanhuo666/p/11563824.html
Copyright © 2011-2022 走看看