AvueAvue
首页
  • 开发指南
  • Skill开发
  • 在线测试工具
  • Form组件
  • Crud组件
  • Default组件
  • Data组件
  • Component组件
产品
工作台
授权
联系
2.x文档
个人支付接口
首页
  • 开发指南
  • Skill开发
  • 在线测试工具
  • Form组件
  • Crud组件
  • Default组件
  • Data组件
  • Component组件
产品
工作台
授权
联系
2.x文档
个人支付接口
  • Article 文章
  • Group 分组容器
  • Title 标题文本
  • CountUp 数字动画
  • Comment 评论
  • Chat 客服聊天
  • Captcha 验证码
  • Contextmenu 右键菜单
  • Clipboard 复制文本
  • DialogForm 弹窗表单
  • Draggable 拖拽
  • Export Excel 导入与导出
  • Flow 流程
  • FilterBuilder 高级筛选
  • FileManager 文件管理
  • QRCode 二维码
  • Iframe 嵌入页面
  • ImagePreview 图片预览
  • Login 登录组件
  • License 授权书
  • Print 打印
  • Screenshot 页面截图
  • Search 标签搜索
  • Sign 电子签名
  • TextEllipsis 超出文本省略
  • Tabs 选项卡
  • Tree 树型
  • Video 摄像头
  • Calendar 日历组合示例

Tree 树型

常用的树型组件

普通用法

效果预览可直接操作下方示例

option配置属性,data为数据,node-click为点击事件,node-contextmenu右键事件

正在加载示例…
<template>
  <avue-tree ref="tree"
             :option="option"
             :data="data"
             v-model="form"
             @node-expand="nodeExpand"
             @node-contextmenu="nodeContextmenu"
             @node-click="nodeClick">
  </avue-tree>
</template>

<script setup>
import { ref, onMounted } from 'vue'
import { ElMessage } from 'element-plus'

const form = ref({})
const data = ref([
  {
    value: 0,
    label: '一级部门',
    children: [
      { value: 1, label: '一级部门1' },
      { value: 2, label: '一级部门2' }
    ]
  },
  {
    value: 3,
    label: '二级部门',
    children: [
      { value: 4, label: '二级部门1' },
      { value: 5, label: '二级部门2' }
    ]
  }
])

const option = ref({
  menu: false,
  addBtn: false,
  defaultExpandAll: true,
  formOption: {
    labelWidth: 100,
    column: [
      { label: '自定义项', prop: 'label' },
      { label: '测试', prop: 'test' }
    ]
  }
})

const tree = ref(null)

onMounted(() => {
  tree.value.setCurrentKey(0)
})

const nodeContextmenu = (data, node, comp) => {
  ElMessage.success(JSON.stringify(data))
}
const nodeExpand = (data, node, comp) => {
  ElMessage.success(JSON.stringify(data))
}

const nodeClick = (data) => {
  ElMessage.success(JSON.stringify(data))
}
</script>

多选

效果预览可直接操作下方示例

multiple配置为true即可开启多选,check-change方法为回调函数

正在加载示例…
<template>
  <avue-tree ref="tree"
             :option="option"
             :data="data"
             v-model="form"
             @check-change="checkChange">
  </avue-tree>
</template>

<script setup>
import { ref, onMounted } from 'vue'
import { ElMessage } from 'element-plus'

const tree = ref(null)
const form = ref({})
const data = ref([
  {
    value: 0,
    label: '一级部门',
    children: [
      { value: 1, label: '一级部门1' },
      { value: 2, label: '一级部门2' }
    ]
  },
  {
    value: 3,
    label: '二级部门',
    children: [
      { value: 4, label: '二级部门1' },
      { value: 5, label: '二级部门2' }
    ]
  }
])
const option = ref({
  menu: false,
  addBtn: false,
  defaultExpandAll: true,
  multiple: true,
  formOption: {
    labelWidth: 100,
    column: [
      { label: '自定义项', prop: 'label' },
      { label: '测试', prop: 'test' }
    ],
  }
})

const checkChange = (data, checked, indeterminate) => {
  ElMessage.success(JSON.stringify(data))
}

onMounted(() => {
  tree.value.setCheckedKeys([0])
})
</script>

配置属性

效果预览可直接操作下方示例
正在加载示例…
<template>
  <avue-tree @save="save" @update="update" @del="remove" :option="option"
             :data="data"
             v-model="form">
  </avue-tree>
</template>

<script setup>
import { ref } from 'vue'
import { ElMessage } from 'element-plus'

const form = ref({})
const data = ref([
  {
    value: 0,
    label: '一级部门',
    children: [
      { value: 1, label: '一级部门1' },
      { value: 2, label: '一级部门2' }
    ]
  },
  {
    value: 3,
    label: '二级部门',
    children: [
      { value: 4, label: '二级部门1' },
      { value: 5, label: '二级部门2' }
    ]
  }
])
const option = ref({
  title: '我是标题',
  filterText: "搜索关键字自定义",
  defaultExpandAll: true,
  addBtnText: '新增自定义文案',
  editBtnText: '修改自定义文案',
  delBtnText: '删除自定义文案',
  defaultExpandedKeys: [1],
  formOption: {
    labelWidth: 100,
    column: [
      { label: '自定义项', prop: 'label' }
    ],
  },
  props: {
    labelText: '标题',
    label: 'label',
    value: 'value',
    children: 'children'
  }
})

// 本地增删改示例;真实接口成功后再调用 done(savedData)。
let nextValue = 6
const save = (_node, formData, done) => done({ ...formData, value: nextValue++, children: [] })
const update = (_node, formData, done) => done(formData)
const remove = (_node, done) => done()
</script>

打开前和关闭前

效果预览可直接操作下方示例

before-open打开前的方法,before-close关闭前的方法

正在加载示例…
<template>
  <avue-tree @save="save" @update="update" @del="remove" :before-open="beforeOpen"
             :before-close="beforeClose"
             :option="option"
             :data="data"
             v-model="form">
  </avue-tree>
</template>

<script setup>
import { ref } from 'vue'
import { ElMessage, ElMessageBox } from 'element-plus'

const form = ref({})
const data = [
  {
    value: 0,
    label: '一级部门',
    children: [
      {
        value: 1,
        label: '一级部门1',
      }, {
        value: 2,
        label: '一级部门2',
      }
    ]
  }, {
    value: 3,
    label: '二级部门',
    children: [
      {
        value: 4,
        label: '二级部门1',
      }, {
        value: 5,
        label: '二级部门2',
      }
    ]
  }
]
const option = {
  defaultExpandAll: true,
  formOption: {
    labelWidth: 100,
    column: [{
      label: '自定义项',
      prop: 'label'
    }, {
      label: '测试',
      prop: 'test'
    }],
  }
}

const beforeOpen = (done, type) => {
  ElMessageBox.alert(`我是${type}`, {
    confirmButtonText: '确定'
  }).then(() => {
    if (['view', 'edit'].includes(type)) {
      // 查看和编辑逻辑
    } else {
      //新增逻辑
      form.value.test = '初始化赋值'
    }
    done()
  })
}

const beforeClose = (done) => {
  ElMessageBox.confirm('确认关闭?')
    .then(() => {
      done()
    })
    .catch(() => { })
}

// 本地增删改示例;真实接口成功后再调用 done(savedData)。
let nextValue = 6
const save = (_node, formData, done) => done({ ...formData, value: nextValue++, children: [] })
const update = (_node, formData, done) => done(formData)
const remove = (_node, done) => done()
</script>

事件和自定义

效果预览可直接操作下方示例

option配置属性即可,data为可配置项目,具体参考下表文档

正在加载示例…
<template>
  <avue-tree :option="option"
             :data="data"
             :filter-node-method="filterNodeMethod"
             @update="update"
             @save="save"
             @del="del"
             v-model="form">
    <template #="{ node, data }">
      <span class="el-tree-node__label">
        <i class="el-icon-user-solid"></i>
        {{ (node || {}).label }}
      </span>
    </template>
    <template #menu="{node}">
      <div class="avue-tree__item"
           @click="tip(node)">自定义按钮</div>
    </template>
  </avue-tree>
</template>

<script setup>
import { ref } from 'vue'
import { ElMessage, ElMessageBox } from 'element-plus'

const form = ref({})
const data = ref([
  {
    value: 0,
    label: '一级部门',
    children: [
      { value: 1, label: '一级部门1' },
      { value: 2, label: '一级部门2' }
    ]
  },
  {
    value: 3,
    label: '二级部门',
    children: [
      { value: 4, label: '二级部门1' },
      { value: 5, label: '二级部门2' }
    ]
  }
])

const option = ref({
  defaultExpandAll: true,
  formOption: {
    labelWidth: 100,
    column: [{ label: '自定义项', prop: 'label' }]
  }
})

const filterNodeMethod = (value, data) => {
  return !value || data.label.indexOf(value) !== -1
}

const tip = (node) => {
  ElMessage.success(JSON.stringify(node.data))
}

const del = (data, done) => {
  ElMessageBox.confirm('确认删除这个本地演示节点?', '提示', {
    confirmButtonText: '确定',
    cancelButtonText: '取消',
    type: 'warning'
  }).then(() => {
    ElMessage.success('删除回调')
    done()
  }).catch(() => {
    ElMessage.info('已取消删除')
  })
}

const update = (node, data, done, loading) => {
  ElMessage.success('更新回调')
  done()
}

let nextValue = 6
const save = (node, data, done, loading) => {
  ElMessage.success('新增回调')
  done({ ...data, value: nextValue++, children: [] })
}
</script>

拖拽

效果预览可直接操作下方示例
正在加载示例…
<template>
  <avue-tree @node-drag-start="handleDragStart"
             @node-drag-enter="handleDragEnter"
             @node-drag-leave="handleDragLeave"
             @node-drag-over="handleDragOver"
             @node-drag-end="handleDragEnd"
             @node-drop="handleDrop"
             :option="option"
             :data="data">
  </avue-tree>
</template>

<script setup>
import { ref } from 'vue'
import { ElMessage } from 'element-plus'

const data = ref([
  {
    value: 0,
    label: '一级部门',
    children: [
      { value: 1, label: '一级部门1' },
      { value: 2, label: '一级部门2' }
    ]
  },
  {
    value: 3,
    label: '二级部门',
    children: [
      { value: 4, label: '二级部门1' },
      { value: 5, label: '二级部门2' }
    ]
  }
])

const option = ref({
  menu: false,
  addBtn: false,
  draggable: true,
  defaultExpandAll: true,
  allowDrop: (draggingNode, dropNode, type) => {
    if (dropNode.data.label === '一级部门2') {
      return type !== 'inner'
    } else {
      return true
    }
  },
  allowDrag: (draggingNode) => {
    return draggingNode.data.label.indexOf('一级部门2') === -1
  },
  formOption: {
    labelWidth: 100,
    column: [{ label: '自定义项', prop: 'label' }]
  }
})

const handleDragStart = (node, ev) => {
  console.log('drag start', node)
}

const handleDragEnter = (draggingNode, dropNode, ev) => {
  console.log('tree drag enter: ', dropNode.label)
}

const handleDragLeave = (draggingNode, dropNode, ev) => {
  console.log('tree drag leave: ', dropNode.label)
}

const handleDragOver = (draggingNode, dropNode, ev) => {
  console.log('tree drag over: ', dropNode.label)
}

const handleDragEnd = (draggingNode, dropNode, dropType, ev) => {
  console.log('tree drag end: ', dropNode && dropNode.label, dropType)
}

const handleDrop = (draggingNode, dropNode, dropType, ev) => {
  console.log('tree drop: ', dropNode.label, dropType)
}
</script>

权限

效果预览可直接操作下方示例
正在加载示例…
<template>
  <avue-tree @save="save" @update="update" @del="remove" :permission="getPermission"
             :option="option"
             :data="data">
  </avue-tree>
</template>

<script setup>
import { ref } from 'vue'

const data = ref([
  {
    value: 0,
    label: '一级部门',
    children: [
      { value: 1, label: '一级部门1' },
      { value: 2, label: '一级部门2' }
    ]
  },
  {
    value: 3,
    label: '二级部门',
    children: [
      { value: 4, label: '二级部门1' },
      { value: 5, label: '二级部门2' }
    ]
  }
])

const option = ref({
  defaultExpandAll: true,
  formOption: {
    labelWidth: 100,
    column: [
      { label: '自定义项', prop: 'label' }
    ],
  }
})

function getPermission (key, data) {
  if (key === "addBtn" && data.value === 0) { return false }
  return true
}

// 本地增删改示例;真实接口成功后再调用 done(savedData)。
let nextValue = 6
const save = (_node, formData, done) => done({ ...formData, value: nextValue++, children: [] })
const update = (_node, formData, done) => done(formData)
const remove = (_node, done) => done()
</script>

懒加载

效果预览可直接操作下方示例

lazy设置为true即可,完后在treeLoad函数里面处理逻辑,具体看下面例子

正在加载示例…
<template>
  <avue-tree :option="option" @node-click="row => selected = row.label" />
  <el-alert :title="selected ? '当前节点:' + selected : '展开节点模拟异步加载,叶子节点不会再发起加载'" :closable="false" />
</template>

<script setup>
import { onBeforeUnmount, ref } from 'vue'
const selected = ref('')
const timers = new Set()
let seed = 0
const option = {
  lazy: true, menu: false, addBtn: false,
  treeLoad(node, resolve) {
    if (node.level === 0) { resolve([{ label: '组织架构', value: 'root' }]); return }
    if (node.level > 2) { resolve([]); return }
    const timer = setTimeout(() => {
      timers.delete(timer)
      resolve([
        { label: `成员 ${++seed}`, value: `member-${seed}`, leaf: true },
        { label: `分组 ${++seed}`, value: `group-${seed}`, leaf: node.level >= 2 }
      ])
    }, 400)
    timers.add(timer)
  }
}
onBeforeUnmount(() => { timers.forEach(clearTimeout); timers.clear() })
</script>

虚拟

虚拟模式使用 Element Plus TreeV2,适合较多节点;请设置可见高度,并根据数据规模验证交互。

效果预览可直接操作下方示例

virtualize 设置为 true 开启虚拟列表,height 设置可见区域高度。

正在加载示例…
<template>
  <avue-tree :option="option" :data="data" @node-click="row => selected = row.label" />
  <el-alert :title="selected ? '当前节点:' + selected : '共 600 个节点,固定 320px 可视区域'" :closable="false" />
</template>

<script setup>
import { ref } from 'vue'
const selected = ref('')
const option = {
  virtualize: true, height: 320, menu: false, addBtn: false,
  props: { value: 'id', label: 'label', children: 'children' }
}
const data = Array.from({ length: 100 }, (_, index) => ({
  id: `department-${index}`, label: `部门 ${index + 1}`,
  children: Array.from({ length: 5 }, (_, child) => ({
    id: `department-${index}-team-${child}`, label: `部门 ${index + 1} / 小组 ${child + 1}`
  }))
}))
</script>

Props

参数说明类型默认值
option树和编辑表单的配置object{}
data树节点数组array[]
modelValue / v-model新增、编辑弹窗中的表单对象,不是选中节点object{}
loading树区域加载状态booleanfalse
permission权限对象或 (key, data) => booleanobject / function{}
beforeOpen / beforeClose弹窗打开 / 关闭前钩子 (done, type),调用 done() 才继续function—
filterNodeMethod自定义筛选函数 (value, data) => booleanfunction—
expandOnClickNode点击节点时是否展开booleanfalse
checkOnClickNode / checkOnClickLeaf点击节点 / 叶子节点是否切换勾选booleanfalse
indent / iconClass缩进像素 / 自定义展开图标number / string—

beforeOpen / beforeClose 是函数属性,请用 :before-open="beforeOpen" 传入,不能写成事件监听。type 为 add(子节点)、parentAdd(顶层节点)或 edit。

Option

参数说明默认值
props节点字段映射,见下表{}
multiple显示复选框进行多选未开启
checkStrictly / accordion勾选父子不关联 / 手风琴展开未开启
defaultExpandAll / defaultExpandedKeys初始展开全部 / 指定键数组—
virtualize / height使用 TreeV2 / 可见区域高度未开启 / —
lazy / treeLoad懒加载 / (node, resolve) => void 加载函数未开启 / —
draggable / allowDrag / allowDrop拖拽开关与判断函数,透传给底层树—
menu显示节点右键菜单true
filter / filterText显示筛选输入框 / 输入提示true / 语言包文案
addBtn / editBtn / delBtn新增、编辑、删除菜单开关true
addBtnText / editBtnText / delBtnText / updateBtnText按钮文字语言包文案
formOption编辑弹窗内的完整 avue-form 配置,需要自行提供 column{}
title / dialogWidth编辑窗口标题 / 宽度新增标题文案 / 50%
size控件尺寸—

虚拟树和普通树的底层能力不同,不应同时假定 TreeV2 支持普通 Tree 的所有懒加载、拖拽或实例方法。

Option.props 与节点数据

映射项含义默认字段
label节点标题,默认筛选依赖该字段为字符串label
value节点唯一键,同时作为底层 node-keyvalue
children子节点数组children
leaf懒加载叶子节点标记leaf
disabled禁用节点disabled

如果业务用 id 作为主键,应设置 option.props.value = 'id',保持各节点唯一且类型一致。option.nodeKey 不是当前模板实际绑定的节点键,不应依赖它替代 props.value。

Events

事件名参数说明
update:modelValue / changeform弹窗表单对象同步
savenode, data, done, loading新增提交;成功后调用 done(savedData?) 更新树并关闭,失败时调用 loading() 结束提交状态
updatenode, data, done, loading编辑提交;成功后调用 done(savedData?) 替换节点并保留子节点
delnode, done删除请求;调用 done() 后从树中移除
node-click / node-expanddata, node, nodeComponent点击 / 展开节点
node-contextmenudata, node, nodeComponent节点右键菜单事件,不包含原始鼠标事件
check-changedata, checked, indeterminate勾选状态变化
node-drag-startnode, event开始拖拽
node-drag-enter / node-drag-leave / node-drag-overdraggingNode, dropNode, event拖拽经过节点
node-drag-end / node-dropdraggingNode, dropNode, dropType, event拖拽结束 / 放置

组件不负责接口持久化。在保存回调中取得后端生成的唯一键后再调用 done(savedData),避免新增节点没有有效 props.value。删除回调的第一个参数是底层 Node 包装对象,业务数据位于 node.data。

Methods

在挂载后通过组件 ref 调用。下列方法代理到底层树,具体参数与返回值遵循已安装 Element Plus 对应版本;虚拟模式只可调用 TreeV2 实际提供的方法。

方法用途
filter(value)过滤节点
getCheckedNodes(leafOnly?, includeHalfChecked?) / getCheckedKeys(leafOnly?)获取勾选数据 / 键
setCheckedNodes(nodes) / setCheckedKeys(keys, leafOnly?) / setChecked(keyOrData, checked, deep?)设置勾选状态
getHalfCheckedNodes() / getHalfCheckedKeys()获取半选节点 / 键
getCurrentKey() / getCurrentNode()获取当前高亮节点的键 / 数据
setCurrentKey(key) / setCurrentNode(node)设置当前高亮节点
getNode(keyOrData)获取 Node 包装对象
updateKeyChildren(key, data)更新指定节点的子节点
append(data, parent?) / insertBefore(data, reference) / insertAfter(data, reference) / remove(dataOrNode)增删节点
parentAdd()打开顶层节点新增窗口
hide()按关闭前钩子流程关闭编辑窗口

Slots

名称说明参数
default节点内容{ node, data }
menu节点右键菜单附加内容{ node }
add-btn替换顶部新增按钮;需开启 filter 和 addBtn无
最后更新:
贡献者: smallwei
Prev
Tabs 选项卡
Next
Video 摄像头