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-width: 100%;
     display:inline-block !important;
 }

.rightClickMenu {
    z-index: 999
}

</style>

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

原文地址:https://www.cnblogs.com/lanhuo666/p/11563824.html

时间: 2024-07-30 20:25:07

vue中使用element来创建目录列表的相关文章

vue中对element的弹框messagebox的二次封装

在vue中对确认框的二次封装 使用场景:在页面中做某些需要警告的操作时的弹框提示 1.在utils文件夹下新建一个confirm.js文件来对messageBox的封装,内容如下: /** confirm.js */ import { MessageBox } from 'element-ui' export function handleCofirm(text = '确定执行此操作吗?', type = 'warning') { return MessageBox.confirm(text,

vue中使用Element主题自定义肤色

一.搭建好项目的环境. 二.根据ElementUI官网的自定义主题(http://element.eleme.io/#/zh-CN/component/custom-theme)来安装[主题生成工具]. 三.在 element-variables.scss 文件里修改 $–color-primary:#409EFF,即你想要的主题颜色.然后,执行主题编译命令生成主题(et),根目录会生成一个theme文件夹. 四.封装动态换肤色ThemePicker.vue组件. <template> <

在Vue框架中引入Element

文章会讲到如何在Vue框架中引入Element 那我们先来说一下Vue框架:Vue是渐进式,JavaScript框架.很多人不理解什么是渐进式,简单点讲就是易用.灵活.高效(没有太多限制) 这里介绍npm安装方式: 打开cmd,找到你Vue项目的路径 运行 npm i element-ui -S 然后在main.js里写入以下内容: import Vue from 'vue'; import ElementUI from 'element-ui'; import 'element-ui/lib/

如何在vue项目中引入element ui组件

(1)安装element ui,即: npm i element-ui -S 然后在项目中的node_modules目录下查看是否有element-ui文件夹,如果有说明安装成功了: (2)引入element ui 在main.js中引入element ui,即: 1 import ElementUI from 'element-ui' 2 import 'element-ui/lib/theme-chalk/index.css' 3 Vue.use(ElementUI) [注意红色部分,以前是

vue中的列表渲染

<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Vue中列表渲染</title> <script src="./vue.js"></script> </head> <body> <div id="app">

3-7 Vue中的列表渲染

 举个案例:循环data中的list的值在div中,并显示相应的index值. 关于数组的循环: //显示效果如下图: //一般的列表渲染最好带一个key值,要把key值设置为唯一值的话,可以选择index.但在频繁操作DOM元素相对应的数据的时候,它还是有点浪费性能,可能让Vue没法充分复用DOM节点,所以不太建议用index来做key值 //所以一般的项目中的后端会传递过来一些数据,这些数据可以把它作为key值来使用(一般会携带一个后端或数据库相关的一个唯一的数据条目标识符,例如:id) /

理解Vue中的Render渲染函数

VUE一般使用template来创建HTML,然后在有的时候,我们需要使用javascript来创建html,这时候我们需要使用render函数.比如如下我想要实现如下html: <div id="container"> <h1> <a href="#"> Hello world! </a> </h1> </div> 我们会如下使用: <!DOCTYPE html> <html

better-scroll在vue中的应用

在我们日常的移动端项目开发中,处理滚动列表是再常见不过的需求了,以滴滴为例,可以是这样竖向滚动的列表,如图所示: 微信 -> 钱包->滴滴出行"体验效果. 什么是 better-scroll better-scroll 是一个移动端滚动的解决方案,它是基于 iscroll 的重写,它和 iscroll 的主要区别在 这里 .better-scroll 也很强大,不仅可以做普通的滚动列表,还可以做轮播图.picker 等等. 不少同学可能用过 better-scroll,我收到反馈最多

better-scroll在vue中的坑

在我们日常的移动端项目开发中,处理滚动列表是再常见不过的需求了,以滴滴为例,可以是这样竖向滚动的列表,如图所示: 也可以是横向滚动的导航栏,如图所示: 可以打开"微信 -> 钱包->滴滴出行"体验效果. 我们在实现这类滚动功能的时候,会用到我写的第三方库,better-scroll. 什么是 better-scroll better-scroll 是一个移动端滚动的解决方案,它是基于 iscroll 的重写,它和 iscroll 的主要区别在这里.better-scroll