Element Plus Tree 树形控件 嵌套 Dropdown 下拉菜单 无感刷新

element plus 的 tree 组件虽然是比较好用的,但是并不能满足传统OA系统的对 树 的操作,浏览了整个element plus,Tree 树形控件 嵌套 Dropdown 下拉菜单。当然,如果简单的嵌套,似乎没什么难度,所以我给自己上了点难度,不仅要完美实现效果,还要做到无感刷新。

老规矩,先把 element tree 组件的示例代码搬过来,运行,可以有以下效果

在这里插入图片描述

接下来就要实现嵌套,同样需要把 Dropdown 的代码拿过来嵌套

先看效果

在这里插入图片描述

上代码,讲解放代码注释里

<template>
	<el-input
      v-model="filterText"
      placeholder="请输入关键字"
      class="mb-24"
    />
    <el-tree
      ref="treeRef" 
      class="filter-tree"
      :data="data"
      :props="defaultProps"
      :expand-on-click-node="false"
      default-expand-all
      :filter-node-method="filterNode"
      @node-click="handleNodeClick"
    >
      <template #default="{ node }">
        <span>{
    
    {
    
     node.label }}</span>
        <el-dropdown
          class="tree-dropdown"
          :style="[activeNode === node.data.$treeNodeId && 'visibility:visible']"  // 保证点击的时候能够让操作图标显示
          :hide-on-click="false"
          placement="right-start"
          trigger="click"
        >
          <span class="inline-block rotate-90">
            <el-icon
              :size="16"
            ><Histogram /></el-icon>
          </span>
          <template #dropdown>
            <el-dropdown-menu>
              <el-dropdown-item @click="handleEdit">
                重命名
              </el-dropdown-item>
              <el-dropdown-item>
                <el-dropdown
                  placement="right-start"
                >
                  <span class="flex justify-between">
                    添加<el-icon
                      :size="16"
                    ><ArrowRight /></el-icon>
                  </span>
                  <template #dropdown>
                    <el-dropdown-menu>
                      <el-dropdown-item @click="handleInsertBefore">
                        上方添加目录
                      </el-dropdown-item>
                      <el-dropdown-item @click="handleInsertAfter">
                        下方添加目录
                      </el-dropdown-item>
                      <el-dropdown-item
                        :disabled="node.level===3"  // 只有第一第二级才能添加子目录
                        @click="handleInsertChild"
                      >
                        添加子目录
                      </el-dropdown-item>
                    </el-dropdown-menu>
                  </template>
                </el-dropdown>
              </el-dropdown-item>
              <el-dropdown-item>
                <el-dropdown
                  placement="right-start"
                >
                  <span class="flex justify-between">
                    移动<el-icon size="16"><ArrowRight /></el-icon>
                  </span>
                  <template #dropdown>
                    <el-dropdown-menu>
                      <el-dropdown-item
                        :disabled="!node.previousSibling"  // 第一个不能上移
                        @click="handleMoveUp(node)"
                      >
                        上移
                      </el-dropdown-item>
                      <el-dropdown-item
                        :disabled="!node.nextSibling"  // 最后一个不能下移
                        @click="handleMoveDown(node)"
                      >
                        下移
                      </el-dropdown-item>
                      <!-- <el-dropdown-item          // 升降级,可以做,但是没必要,暂时屏蔽,有需要的要自己实现
                        :disabled="node.level===3"    // 第三级不能降级
                        @click="handleTierDown(node)"
                      >
                        降级
                      </el-dropdown-item>
                      <el-dropdown-item
                        :disabled="node.level===1"    // 第一级不能升级
                        @click="handleUpgrades(node)"
                      >
                        升级
                      </el-dropdown-item> -->
                    </el-dropdown-menu>
                  </template>
                </el-dropdown>
              </el-dropdown-item>
              <el-dropdown-item @click="handleClone(node)">
                复制
              </el-dropdown-item>
              <el-dropdown-item @click="handleDelete">
                删除
              </el-dropdown-item>
            </el-dropdown-menu>
          </template>
        </el-dropdown>
      </template>
    </el-tree>
    
		//  弹窗自己写,我的是组件,就不放代码了
		
 </template>

<script lang="ts" setup>
import {
    
     toRefs, onMounted, ref, Ref, watch } from 'vue'
import {
    
     ElTree, ElMessage, ElMessageBox } from 'element-plus'
import {
    
     Histogram, ArrowRight } from '@element-plus/icons'
import {
    
     v4 as uuidV4 } from 'uuid'

interface Tree {
    
    
  [key: string]: any
}

const emits = defineEmits(['changeScale'])

const filterText = ref('')
const treeRef = ref<InstanceType<typeof ElTree>>()

const defaultProps = {
    
    
  children: 'children',
  label: 'label',
}

const name = ref('')              // 重命名
const activeNode = ref()          // 选中的虚拟树节点id,不是自己定义的
const activeNodeName = ref('')    // 选中树的名字,方便重命名/删除
const level = ref()               // 选中层级
const action = ref('insertBefore') as Ref<'insertBefore'|'insertAfter'|'append'>   // 当前选中的操作,因为下拉菜单是嵌套的,只能够记录当前操作。

const handleInsertBefore = () => {
    
    
  action.value = 'insertBefore'
  // 这里让你的弹窗显示
  // 弹窗.title = "添加目录"
}
const handleInsertAfter = () => {
    
    
  action.value = 'insertAfter'
  // 这里让你的弹窗显示
  // 弹窗.title = "添加目录"
}
const handleInsertChild = () => {
    
    
  action.value = 'append'
  // 这里让你的弹窗显示
  // 弹窗.title = "添加子目录"
}
const handleEdit = () => {
    
    
  action.value = 'insertAfter'
  name.value = activeNodeName.value  // 把原来的名字带到弹窗去
  // 这里让你的弹窗显示
  // 弹窗.title = "修改目录"
}
const handleClone = async (node) => {
    
    
  try {
    
    
    await ElMessageBox.confirm(`确定复制该模板吗?`, '提示', {
    
    
      confirmButtonText: '确定',
      cancelButtonText: '取消',
      type: 'warning',
      center: true
    })
    const {
    
     $treeNodeId, ...newData } = node.data
    if (treeRef.value) treeRef.value.insertAfter(newData, $treeNodeId)  // 复制的原理就是,在改节点下方复制一个一模一样的,原则来说,包含的id是要替换掉新的,这个就得涉及到结构赋值了。
    activeNode.value = ''  // 清空选中的节点id,让图标隐藏
  } catch (err) {
    
    
    ElMessage({
    
     message: '已取消', showClose: true, type: 'info' })
    return
  }
}
const handleDelete = async () => {
    
    
  try {
    
    
    await ElMessageBox.confirm(`是否删除 ${
      
      activeNodeName.value}`, '提示', {
    
    
      confirmButtonText: '确定',
      cancelButtonText: '取消',
      type: 'warning',
      center: true
    })
    if (treeRef.value) treeRef.value.remove(activeNode.value)
    activeNode.value = ''  // 清空选中的节点id,让图标隐藏
  } catch (err) {
    
    
    ElMessage({
    
     message: '已取消', showClose: true, type: 'info' })
    return
  }
}
const handleMoveUp = (node) => {
    
      // 上移的原理就是现在选中节点上方复制一个一模一样的节点,然后删掉原来那个
  const {
    
     $treeNodeId, ...newData } = node.data
  if (treeRef.value) treeRef.value.insertBefore(newData, node.previousSibling.data.$treeNodeId)
  if (treeRef.value) treeRef.value.remove($treeNodeId)
}
const handleMoveDown = (node) => {
    
      // 下移的原理就是现在选中节点下方复制一个一模一样的节点,然后删掉原来那个
  const {
    
     $treeNodeId, ...newData } = node.data
  if (treeRef.value) treeRef.value.insertAfter(newData, node.nextSibling.data.$treeNodeId)
  if (treeRef.value) treeRef.value.remove($treeNodeId)
}
// const handleTierDown = (node) => {
    
    
//   console.log(node)
// }
// const handleUpgrades = (node) => {
    
    
//   console.log(node)
// }

watch(filterText, (val) => {
    
    
  treeRef.value!.filter(val)
})

const filterNode = (value: string, data: Tree) => {
    
    
  if (!value) return true
  return data.label.includes(value)
}

const data: Tree[] = [
  {
    
    
    label: 'Level one 1',
    children: [
      {
    
    
        label: 'Level two 1-1',
        children: [
          {
    
    
            label: 'Level three 1-1-1',
          },
        ],
      },
    ],
  },
  {
    
    
    label: 'Level one 2',
    children: [
      {
    
    
        label: 'Level two 2-1',
        children: [
          {
    
    
            label: 'Level three 2-1-1',
          },
        ],
      },
      {
    
    
        label: 'Level two 2-2',
        children: [
          {
    
    
            label: 'Level three 2-2-1',
          },
        ],
      },
    ],
  },
  {
    
    
    label: 'Level one 3',
    children: [
      {
    
    
        label: 'Level two 3-1',
        children: [
          {
    
    
            label: 'Level three 3-1-1',
          },
        ],
      },
      {
    
    
        label: 'Level two 3-2',
        children: [
          {
    
    
            label: 'Level three 3-2-1',
          },
        ],
      },
    ],
  },
]

const handleNodeClick = (event) => {
    
    
  console.log(event)
  activeNode.value = event.$treeNodeId  // 选中的节点树id
  activeNodeName.value = event.label    // 选中的名字
  level.value = event.level             // 选中的层级
}

const handleSubmit = () => {
    
      //  这里是你的弹窗做出正确选择的时候触发
  submitBoxProps.value.visible = false
  const data: object = {
    
    
    id: uuidV4(),        // 定义新的id,这里用的是uuid,你可以先跑跟后端约定好的接口,让他把对应的 data 返回给你
    label: name.value,   // 你弹窗输入的内容(节点名字)
  }
  level.value === 3 ? Object.assign(data, {
    
     // 你的第三级自己的内容 }) : Object.assign(data, {children: []})  // 根据层级,重新重构data
  if (action.value === 'insertBefore' && treeRef.value) treeRef.value.insertBefore(data, activeNode.value)  // 上方插入
  if (action.value === 'insertAfter' && treeRef.value) treeRef.value.insertAfter(data, activeNode.value)   // 下方插入
  if (action.value === 'append' && treeRef.value) treeRef.value.append(data, activeNode.value)        // 子插入
  activeNode.value = ''
}
</script>

<style lang="scss" scoped>
.template-tree {
    
    
  width: 300px;
  height: max-content;
  background: #ffffff;

  :deep(.el-tree-node__content) {
    
    
    .tree-dropdown {
    
    
      visibility: hidden;  // 默认让菜单时隐藏的
    }
  }
  :deep(.el-tree-node__content:active),
  :deep(.el-tree-node__content:hover) {
    
    
      .tree-dropdown {
    
    
        visibility: visible !important;  // 菜单显示
      }
    }
}
</style>

注意:每一个方法都应该配合后端来实现的,无感刷新的原理是,前端手动更改了树,而不是每掉一次后端接口就刷新获取数据。

希望能帮到您。

猜你喜欢

转载自blog.csdn.net/weixin_44872023/article/details/131582055
今日推荐