npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@wsh704/vue3-tree-org-department

v1.0.2

Published

基于 `vue3-tree-org` 扩展的 Vue 3 组织架构图组件包。当前包保留原有 `<vue3-tree-org>` 的基础组织树能力,并提供 `<department-tree>` 增强用法:在原组织树能力之上多传一个 `bottomNode`,自动生成最后一行底部汇总和匹配连线。

Readme

@wsh704/vue3-tree-org-department 使用方法

基于 vue3-tree-org 扩展的 Vue 3 组织架构图组件包。当前包保留原有 <vue3-tree-org> 的基础组织树能力,并提供 <department-tree> 增强用法:在原组织树能力之上多传一个 bottomNode,自动生成最后一行底部汇总和匹配连线。

当前源码版本:

@wsh704/[email protected]

当前公网 npm 可查版本:

@wsh704/[email protected]

说明:1.0.2 已在本地源码和 tarball 中完成构建验证,并已安装到 vue3-demo 验证通过;该版本修复了部门 tree 底部汇总框在多层节点高度不一致时可能和节点重叠的问题。公网 npm 当前仍是 1.0.1,发布 1.0.2 需要 npm 发布 OTP,或带 Bypass 2FA 权限的 granular access token。

功能概览

  • 支持普通组织树展示。
  • 支持组织树拖拽、缩放、居中、横向布局。
  • 支持节点展开/收起、默认展开层级、指定展开节点。
  • 支持默认右键菜单:复制文本、新增节点、编辑节点、删除节点。
  • 支持自定义右键菜单。
  • 支持自定义节点插槽、自定义展开按钮插槽。
  • 支持搜索过滤。
  • 支持懒加载子节点。
  • 支持组织树注释连线,注释可以连接一个或多个节点。
  • 支持右键新增、选择、编辑、删除注释。
  • 新增 DepartmentTree / <department-tree> 部门树增强能力:
    • 在原组织树所有基础能力之上增加 bottomNode 汇总。
    • 支持原 <vue3-tree-org> 的属性、事件、方法和插槽继续使用。
    • 推荐接收 { root, bottomNode } 业务数据,也支持在根节点数据上直接多放一个 bottomNode
    • root 是原多级组织树数据。
    • bottomNode 是组织树之外的底部汇总节点。
    • 只在组织树最后一级叶子节点按姓名匹配汇总,不跨级匹配。
    • titleposition 固定展示,只有姓名可以右键编辑。
    • 右键某个组织节点或底部分组后,该节点/分组内所有姓名都可以单独编辑。

安装

npm install @wsh704/vue3-tree-org-department --save

也可以使用 yarn 或 pnpm:

yarn add @wsh704/vue3-tree-org-department
pnpm add @wsh704/vue3-tree-org-department

如果需要明确从 npm 公网安装:

npm install @wsh704/vue3-tree-org-department --registry=https://registry.npmjs.org/

全局引入

main.jsmain.ts 中引入:

import { createApp } from 'vue'
import App from './App.vue'
import treeOrg from '@wsh704/vue3-tree-org-department'
import '@wsh704/vue3-tree-org-department/lib/vue3-tree-org.css'

const app = createApp(App)

app.use(treeOrg)
app.mount('#app')

全局引入后可以直接在模板中使用:

<template>
  <vue3-tree-org :data="treeData" />
  <department-tree :data="departmentData" />
</template>

局部引入

如果不想全局注册,也可以局部引入组件。

<template>
  <vue3-tree-org :data="treeData" />
  <department-tree :data="departmentData" />
</template>

<script setup>
import { Vue3TreeOrg, DepartmentTree } from '@wsh704/vue3-tree-org-department'
import '@wsh704/vue3-tree-org-department/lib/vue3-tree-org.css'

const treeData = {
  id: 'root',
  pid: '',
  label: '安全生产领导小组',
  children: []
}

const departmentData = {
  root: {
    title: '安全生产领导小组',
    type: 1,
    members: [],
    children: []
  },
  bottomNode: {
    title: '驾驶员、押运员、装卸管理人员',
    members: [],
    children: []
  }
}
</script>

<script setup> 中,导入的 Vue3TreeOrgDepartmentTree 会自动可用于模板,模板中可以写 <Vue3TreeOrg> / <DepartmentTree>,也可以写 kebab-case 的 <vue3-tree-org> / <department-tree>

普通组织树基础用法

普通组织树使用 <vue3-tree-org>

<template>
  <div class="tree-page">
    <vue3-tree-org
      :data="data"
      :props="treeProps"
      :center="true"
      :collapsable="true"
      :default-expand-level="3"
      :tool-bar="treeTools"
      :draggable="true"
      :scalable="true"
      :node-draggable="true"
      @on-node-click="handleNodeClick"
      @on-contextmenu="handleContextmenu"
    />
  </div>
</template>

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

const treeProps = {
  id: 'id',
  pid: 'pid',
  label: 'label',
  children: 'children',
  expand: 'expand'
}

const treeTools = {
  scale: true,
  restore: true,
  expand: true,
  zoom: true,
  fullscreen: true
}

const data = ref({
  id: 'root',
  pid: '',
  label: '安全生产领导小组',
  children: [
    {
      id: 'dispatch',
      pid: 'root',
      label: '综合调度部',
      children: [
        { id: 'waybill', pid: 'dispatch', label: '运单管理员' },
        { id: 'finance', pid: 'dispatch', label: '财务人员' }
      ]
    },
    {
      id: 'safety',
      pid: 'root',
      label: '安全部',
      children: [
        { id: 'inspector', pid: 'safety', label: '专职安全员' },
        { id: 'monitor', pid: 'safety', label: '专职监控人员' }
      ]
    }
  ]
})

function handleNodeClick(event, data, node) {
  console.log('点击节点:', data, node)
}

function handleContextmenu(payload) {
  console.log('右键命令:', payload.command, payload.data)
}
</script>

<style scoped>
.tree-page {
  height: 720px;
  background: #fff;
}
</style>

普通组织树数据默认字段:

| 字段 | 说明 | | --- | --- | | id | 节点唯一标识。需要新增、删除、编辑、拖拽时必须有。 | | pid | 父级节点标识。需要新增、删除、编辑、拖拽时必须有。 | | label | 节点显示文本。 | | children | 子节点数组。 | | expand | 是否展开。 | | disabled | 当前节点是否禁止编辑。 | | className | 节点自定义 class。 | | style | 节点自定义样式。 | | noDragging | 当前节点是否禁止拖拽画布。 |

如果你的业务字段不是这些名称,可以通过 props 映射:

<vue3-tree-org
  :data="data"
  :props="{
    id: 'deptId',
    pid: 'parentId',
    label: 'deptName',
    children: 'childList',
    expand: 'opened'
  }"
/>

普通组织树自定义节点

使用默认插槽自定义节点内容:

<vue3-tree-org :data="data">
  <template #default="{ node }">
    <div class="custom-node">
      <strong>{{ node.label }}</strong>
      <span>{{ node.$$data.duty }}</span>
    </div>
  </template>
</vue3-tree-org>

node 是组件内部节点对象,常用字段:

| 字段 | 说明 | | --- | --- | | node.id | 节点 id。 | | node.label | 节点显示文本。 | | node.pid | 父节点 id。 | | node.expand | 是否展开。 | | node.$$data | 原始业务节点数据。 | | node.$$level | 当前节点层级。 |

普通组织树自定义展开按钮

<vue3-tree-org :data="data" :collapsable="true">
  <template #expand="{ node }">
    <span>{{ node.expand ? '-' : '+' }}</span>
  </template>
</vue3-tree-org>

默认右键菜单

默认右键菜单包含:

  • 复制文本
  • 新增节点
  • 编辑节点
  • 删除节点
<vue3-tree-org
  :data="data"
  :disabled="false"
  @on-contextmenu="handleContextmenu"
  @on-node-focus="handleNodeFocus"
  @on-node-blur="handleNodeBlur"
  @on-node-delete="handleNodeDelete"
/>
function handleContextmenu(payload) {
  // payload.command: copy / add / edit / delete
  // payload.node: 内部节点
  // payload.data: 原始业务节点
  console.log(payload.command, payload.data)
}

function handleNodeFocus(event, data, node) {
  console.log('进入编辑:', data)
}

function handleNodeBlur(event, data, node) {
  console.log('结束编辑:', data)
}

function handleNodeDelete(data) {
  console.log('删除节点:', data)
}

注意:

  • 默认新增、编辑、删除依赖节点的 idpid
  • 根节点不允许默认删除。
  • 如果设置 disabled = true,默认新增、编辑、删除不可用。
  • 单个节点也可以通过节点数据上的 disabled: true 禁止编辑。

自定义右键菜单

<vue3-tree-org
  :data="data"
  :define-menus="menus"
  @on-contextmenu="handleMenu"
/>
const menus = [
  { name: '标记已检查', command: 'mark-checked' },
  { name: '记录节点名称', command: 'record-label' }
]

function handleMenu(payload) {
  if (payload.command === 'mark-checked') {
    payload.data.checked = true
  }

  if (payload.command === 'record-label') {
    console.log(payload.data.label)
  }
}

define-menus 也可以是函数:

function menus(event, node) {
  if (node.$$root) {
    return [{ name: '根节点操作', command: 'root-action' }]
  }
  return [{ name: '普通节点操作', command: 'node-action' }]
}

搜索过滤

先给组件设置 filter-node-method,再通过组件实例调用 filter(value)

<template>
  <input v-model="keyword" @input="runFilter" />
  <vue3-tree-org
    ref="treeRef"
    :data="data"
    :filter-node-method="filterNodeMethod"
  />
</template>

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

const treeRef = ref()
const keyword = ref('')

function filterNodeMethod(value, node) {
  if (!value) return true
  return String(node.label || '').includes(value)
}

function runFilter() {
  treeRef.value.filter(keyword.value)
}
</script>

懒加载

开启懒加载需要同时设置 lazyload。如果能提前知道某个节点是不是叶子节点,建议通过 props.isLeaf 映射叶子字段。

<vue3-tree-org
  ref="treeRef"
  :data="data"
  :props="treeProps"
  :lazy="true"
  :load="loadChildren"
  :collapsable="true"
  :default-expand-level="0"
/>
const treeProps = {
  id: 'id',
  pid: 'pid',
  label: 'label',
  children: 'children',
  expand: 'expand',
  isLeaf: 'isLeaf'
}

const data = {
  id: 'root',
  pid: '',
  label: '懒加载总部',
  isLeaf: false
}

function loadChildren(node, resolve) {
  const children = [
    {
      id: `${node.id}-child-1`,
      pid: node.id,
      label: '懒加载岗位1',
      isLeaf: true
    }
  ]

  // 组件接口
  resolve(children, true)

  // 如果业务侧需要同步当前数据,也可以同时写回原始节点
  node.$$data.children = children
  node.$$data.expand = true
}

懒加载注意事项:

  • 开启 lazy 后,组件默认无法预知没有加载的节点是否还有子级,因此非叶子节点会显示展开按钮。
  • 可以用 isLeaf 告诉组件当前节点是否叶子节点。
  • 开启懒加载后,default-expand-leveldefault-expand-keys 和工具栏全部展开在复杂场景下可能不适合一起使用。

注释连线

注释通过 annotations 配置,不会写入 children,不会改变组织树结构。

<vue3-tree-org
  v-model:annotations="annotations"
  :data="data"
  :annotation-editable="true"
  @on-annotations-change="handleAnnotationsChange"
>
  <template #annotation="{ annotation, nodes }">
    <div class="custom-annotation">
      <strong>{{ Array.isArray(annotation.content) ? annotation.content[0] : annotation.content }}</strong>
      <span>{{ nodes.map(item => item.label).join('、') }}</span>
    </div>
  </template>
</vue3-tree-org>
import { ref } from 'vue'

const annotations = ref([
  {
    id: 'risk-control',
    targetKeys: ['inspector', 'monitor'],
    content: ['风险管控', '安全部岗位联动'],
    color: '#16a34a',
    lineStyle: 'solid',
    placement: 'bottom',
    boxWidth: 220,
    boxHeight: 76
  },
  {
    id: 'document-flow',
    targetKeys: ['waybill', 'finance'],
    content: '单据流转',
    color: '#f97316',
    lineStyle: 'dashed',
    placement: 'bottom',
    boxWidth: 180,
    boxHeight: 56
  }
])

function handleAnnotationsChange(payload) {
  // payload.action: add / update / delete
  // payload.annotation: 当前变化的注释
  // payload.annotations: 最新注释数组
  annotations.value = payload.annotations
}

annotation-editable = true 后:

  • 右键组织节点会追加 新增注释选择注释
  • 新增或选择注释后会打开内置注释编辑面板。
  • 面板中可以编辑注释内容、颜色、线型、位置。
  • 面板打开时点击组织节点可以增减注释目标。
  • 右键已有注释框可以编辑或删除注释。

注释配置字段:

| 字段 | 类型 | 说明 | | --- | --- | --- | | id | string \| number | 注释唯一标识。 | | targetKey | string \| number | 连接一个节点。 | | targetKeys | Array<string \| number> | 连接多个节点。 | | content | string \| string[] | 注释内容。 | | color | string | 注释线和边框颜色。 | | className | string | 注释框自定义 class。 | | style | object | 注释框自定义行内样式。 | | lineStyle | 'solid' \| 'dashed' | 连线样式。 | | lineWidth | number | 连线粗细。 | | placement | 'auto' \| 'bottom' \| 'right' | 注释位置。 | | boxAlign | 'target' \| 'tree-bottom' | 注释框定位基准。target 按目标节点定位,tree-bottom 按整棵可见树的最底部定位。 | | boxGap | number | 注释框和定位基准之间的间距。 | | boxWidth | number | 注释框布局宽度,会影响连线锚点计算。 | | boxHeight | number | 注释框布局高度,会影响连线锚点计算。 |

部门 tree 基础用法

部门 tree 使用 <department-tree>。它可以理解为 <vue3-tree-org> 的增强版:原组织树属性、事件、方法、插槽都可以继续传,额外通过 bottomNode 生成底部汇总。

推荐数据格式为:

{
  root: {
    title: '组织树根节点标题',
    type: 1,
    members: [],
    children: []
  },
  bottomNode: {
    title: '底部汇总标题',
    members: [],
    children: []
  }
}

也可以把 bottomNode 直接挂在根节点数据上:

{
  title: '组织树根节点标题',
  type: 1,
  members: [],
  children: [],
  bottomNode: {
    title: '底部汇总标题',
    members: [],
    children: []
  }
}

示例:

<template>
  <div class="department-page">
    <department-tree
      :data="data"
      :summary-width="560"
      :summary-height="150"
      :tool-bar="treeTools"
      :default-expand-level="8"
      :disabled="disabled"
      @on-member-name-change="handleMemberNameChange"
    />
  </div>
</template>

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

const disabled = ref(false)

const treeTools = {
  scale: true,
  restore: true,
  expand: true,
  zoom: true,
  fullscreen: true
}

const data = ref({
  root: {
    title: '安全生产领导小组',
    type: 1,
    members: [
      { position: '组长', name: '陈荣兴' },
      { position: '副组长', name: '黄臻超' }
    ],
    children: [
      {
        title: '综合部',
        type: 2,
        members: [{ position: null, name: '朱湘君' }],
        children: [
          {
            title: '运单管理员',
            type: 3,
            members: [{ position: null, name: '蒋华琴' }]
          },
          {
            title: '财务人员',
            type: 3,
            members: [{ position: null, name: '郭荣阁' }]
          }
        ]
      },
      {
        title: '安全部',
        type: 2,
        members: [{ position: null, name: '黄臻超' }],
        children: [
          {
            title: '专职安全员',
            type: 3,
            members: [
              { position: null, name: '单筱岑' },
              { position: null, name: '徐俊' },
              { position: null, name: '黄臻超' }
            ]
          },
          {
            title: '专职监控人员',
            type: 3,
            members: [
              { position: null, name: '蒋文玉' },
              { position: null, name: '程燕娜' },
              { position: null, name: '李萍' }
            ]
          }
        ]
      },
      {
        title: '车辆技术管理部',
        type: 2,
        members: [{ position: null, name: '杨广成' }],
        children: [
          {
            title: '车辆技术管理员',
            type: 3,
            members: [{ position: null, name: '杨广成' }]
          }
        ]
      }
    ]
  },
  bottomNode: {
    title: '驾驶员、押运员、装卸管理人员',
    members: ['蒋华琴', '郭荣阁', '单筱岑', '徐俊', '黄臻超', '蒋文玉', '程燕娜', '李萍', '杨广成'],
    children: [
      {
        title: '驾驶员',
        type: 3,
        members: [
          { position: null, name: '蒋华琴' },
          { position: null, name: '郭荣阁' },
          { position: null, name: '单筱岑' }
        ]
      },
      {
        title: '押运员',
        type: 3,
        members: [
          { position: null, name: '徐俊' },
          { position: null, name: '黄臻超' },
          { position: null, name: '蒋文玉' }
        ]
      },
      {
        title: '装卸管理人员',
        type: 3,
        members: [
          { position: null, name: '程燕娜' },
          { position: null, name: '李萍' },
          { position: null, name: '杨广成' }
        ]
      }
    ]
  }
})

function handleMemberNameChange(payload) {
  console.log('姓名变更:', payload)
}
</script>

<style scoped>
.department-page {
  height: 760px;
  background: #fff;
}
</style>

部门 tree 数据规则

root

root 是真实组织树,也就是原来传给 <vue3-tree-org> 的树数据主体。<department-tree> 默认仍兼容原组件的 label 显示字段;如果数据只有 title 没有 label,组件会自动用 title 作为显示文本。也可以通过 props.label 映射成其它业务字段。

| 字段 | 类型 | 说明 | | --- | --- | --- | | title | string | 节点标题,固定展示,不参与编辑。 | | type | number | 节点类型。1 根节点,2 部门,3 岗位。 | | members | Array<{ position, name }> | 节点成员,有几条就竖向显示几条。 | | children | Array | 子节点。 | | id | string \| number | 可选。不传时组件内部会自动生成。 | | pid | string \| number | 可选。不传时组件内部会自动生成。 | | label | string | 可选。默认显示字段仍是原组件的 label;如果没有 label 但有 title,会自动用 title 兜底。 |

成员字段:

| 字段 | 类型 | 说明 | | --- | --- | --- | | position | string \| null | 职位。存在时显示为 职位:姓名,为空时只显示姓名。固定展示,不参与编辑。 | | name | string | 姓名。右键编辑时只编辑这个字段。 |

bottomNode

bottomNode 是组织树之外的底部汇总。

| 字段 | 类型 | 说明 | | --- | --- | --- | | title | string | 底部汇总标题,固定展示,不参与编辑。 | | members | string[] | 用于匹配组织树最后一级叶子节点成员姓名。 | | children | Array | 底部汇总框内的分组展示数据。 |

底部汇总匹配规则:

  • 只使用 bottomNode.members 中的姓名去匹配。
  • 只匹配 root 组织树的最后一级叶子节点。
  • 不跨级匹配。
  • 如果同一个姓名既出现在部门节点,又出现在岗位叶子节点,只会连接最后一级岗位叶子节点。
  • bottomNode.children 只负责底部汇总框内的分组展示,不会被插入 root.children
  • 底部汇总通过注释层渲染,不改变组织树层级结构。
  • 自动生成的底部汇总注释不会回写到业务 annotations,也不会进入普通注释编辑列表。

部门 tree 姓名编辑规则

部门 tree 中:

  • title 固定,不可编辑。
  • position 固定,不可编辑。
  • 只有 members[].name 可以编辑。

编辑方式:

  1. 右键某个组织节点,点击 编辑名称
  2. 当前组织节点内所有姓名变为独立输入框。
  3. 每个姓名可以单独点击、单独输入。
  4. 点击同一节点内其他姓名输入框时,不会退出编辑态。
  5. 按 Enter 或输入框失去焦点后保存。
  6. 保存后触发 on-member-name-change

底部汇总分组同理:

  1. 右键底部某个分组,例如 驾驶员
  2. 点击 编辑名称
  3. 该分组内所有姓名变为独立输入框。
  4. 每个姓名单独编辑并回写到该分组的 members[].name

禁用编辑:

<department-tree
  :data="data"
  :disabled="true"
/>

disabled = true 后,组织节点和底部汇总分组都不会显示 编辑名称 菜单。

部门 tree 姓名变更事件

<department-tree
  :data="data"
  @on-member-name-change="handleMemberNameChange"
/>
function handleMemberNameChange(payload) {
  console.log(payload)
}

事件参数:

{
  name: '新姓名',
  oldName: '旧姓名',
  member: {
    position: null,
    name: '新姓名'
  },
  scope: {
    key: 'summary:0',
    type: 'summary',
    title: '驾驶员'
  },
  index: 0
}

字段说明:

| 字段 | 说明 | | --- | --- | | name | 修改后的姓名。 | | oldName | 修改前的姓名。 | | member | 当前成员对象。 | | scope.key | 当前编辑范围 key。 | | scope.type | 编辑来源。tree 表示组织树节点,summary 表示底部汇总分组。 | | scope.title | 当前组织节点标题或底部分组标题。 | | index | 当前姓名在成员数组中的下标。 |

部门 tree 自定义组织节点

使用 node 插槽:

<department-tree :data="data">
  <template #node="{ node }">
    <div class="my-department-node">
      <strong>{{ node.$$data.title }}</strong>
      <div v-for="member in node.$$data.members" :key="member.name">
        <span v-if="member.position">{{ member.position }}:</span>
        <span>{{ member.name }}</span>
      </div>
    </div>
  </template>
</department-tree>

注意:如果完全自定义 node 插槽,默认的右键姓名编辑 UI 不会自动出现在你的自定义内容里。需要姓名编辑能力时,建议优先使用默认节点样式,或在自定义节点中自行处理编辑交互。

部门 tree 自定义底部汇总

使用 summary 插槽:

<department-tree :data="data">
  <template #summary="{ bottomNode, groups }">
    <div class="my-summary">
      <h3>{{ bottomNode.title }}</h3>
      <div class="my-summary-groups">
        <section v-for="group in groups" :key="group.key">
          <strong>{{ group.title }}</strong>
          <span v-for="member in group.members" :key="member.name">
            {{ member.name }}
          </span>
        </section>
      </div>
    </div>
  </template>
</department-tree>

注意:如果完全自定义 summary 插槽,默认底部分组右键姓名编辑 UI 不会自动出现在你的自定义内容里。需要姓名编辑能力时,建议优先使用默认汇总样式,或在自定义汇总中自行处理编辑交互。

DepartmentTree Props

| 参数 | 类型 | 默认值 | 说明 | | --- | --- | --- | --- | | data | Object | 必填 | 部门树数据,推荐格式为 { root, bottomNode },也支持根节点直接带 bottomNode。 | | summary-color | String | #111827 | 底部汇总线和汇总框颜色。 | | summary-width | Number | 560 | 底部汇总框布局宽度,会影响注释锚点计算。 | | summary-height | Number | 150 | 底部汇总框布局高度,会影响注释锚点计算。 | | 其它 <vue3-tree-org> 属性 | 同原组件 | 同原组件 | 例如 centerpropstool-barhorizontalcollapsablefilter-node-methodlazyloadannotationsannotation-editabledraggablescalablenode-draggableselected-keydefine-menus 等都可以继续使用。 | | disabled | Boolean | false | 同原组件禁用规则;在部门 tree 中也会禁用“编辑名称”。 |

Vue3TreeOrg Props

| 参数 | 类型 | 默认值 | 说明 | | --- | --- | --- | --- | | data | Object | 必填 | 组织树数据源。 | | props | Object | { id, pid, label, expand, children } | 字段映射配置。 | | center | Boolean | false | 是否水平居中。 | | tool-bar | Object \| Boolean | { expand, scale, zoom, restore, fullscreen } | 工具栏配置。 | | disabled | Boolean | false | 是否禁用默认新增、编辑、删除。 | | scalable | Boolean | true | 是否允许缩放。 | | draggable | Boolean | true | 是否允许拖拽画布。 | | draggable-on-node | Boolean | false | 是否在节点上触发画布拖拽。 | | node-draggable | Boolean | true | 是否允许节点拖拽改变结构。 | | clone-node-drag | Boolean | true | 是否复制节点后拖拽。 | | only-one-node | Boolean | true | 是否只拖动当前节点。 | | click-delay | Number | 260 | 单击延迟,用于区分单击和双击。 | | lazy | Boolean | false | 是否开启懒加载。 | | load | Function | — | 懒加载方法,格式为 Function(node, resolve)。 | | default-expand-level | Number | — | 默认展开层级。 | | default-expand-keys | Array | [] | 默认展开节点 id 数组。 | | annotations | Array | [] | 注释配置。 | | annotation-editable | Boolean | false | 是否开启内置右键注释编辑。 | | before-drag-end | Function | — | 节点拖拽结束前钩子,返回 false 或 rejected Promise 可阻止拖拽。 | | horizontal | Boolean | false | 是否横向布局。 | | selected-key | Array \| String \| Number | — | 选中的节点 id 或 id 数组。 | | collapsable | Boolean | false | 是否可以展开/收起节点。 | | render-content | Function | — | 自定义节点渲染函数。 | | label-style | Object | — | 自定义节点样式。 | | label-class-name | Function \| String | — | 自定义节点 class。 | | selected-class-name | Function \| String | — | 自定义选中节点 class。 | | define-menus | Array \| Function | 默认菜单 | 自定义右键菜单。 | | node-add | Function | — | 覆盖默认新增节点行为。 | | node-delete | Function | — | 覆盖默认删除节点行为。 | | node-edit | Function | — | 覆盖默认编辑节点行为。 | | node-copy | Function | — | 覆盖默认复制文本行为。 | | filter-node-method | Function | — | 搜索过滤方法。 |

工具栏 tool-bar 配置

const toolBar = {
  scale: true,
  restore: true,
  expand: true,
  zoom: true,
  fullscreen: true
}

| 字段 | 说明 | | --- | --- | | scale | 显示当前缩放百分比。 | | restore | 显示还原按钮。 | | expand | 显示全部展开/收起按钮。 | | zoom | 显示放大/缩小按钮。 | | fullscreen | 显示全屏按钮。 |

如果不需要工具栏:

<vue3-tree-org :data="data" :tool-bar="false" />
<department-tree :data="data" :tool-bar="false" />

Events

普通组织树事件:

| 事件名 | 返回值 | 说明 | | --- | --- | --- | | on-node-click | (event, data, node) | 节点点击。 | | on-node-dblclick | (event, data, node) | 节点双击。 | | on-node-mouseenter | (event, data, node) | 鼠标进入节点。 | | on-node-mouseleave | (event, data, node) | 鼠标离开节点。 | | on-node-focus | (event, data, node) | 节点进入编辑。 | | on-node-blur | (event, data, node) | 节点结束编辑。 | | on-contextmenu | { command, node, data } | 右键菜单点击。 | | on-node-copy | text | 复制节点文本。 | | on-node-delete | data | 删除节点。 | | on-node-drag-start | node | 节点拖拽开始。 | | on-node-drag | node | 节点拖拽中。 | | on-node-drag-end | (node, targetNode) | 节点拖拽结束。 | | on-expand | (event, data, node) | 节点展开/收起。 | | on-expand-all | boolean | 全部展开/收起。 | | on-zoom | scale | 缩放。 | | on-drag | { x, y } | 画布拖拽中。 | | on-drag-stop | { x, y } | 画布拖拽结束。 | | update:annotations | annotations | 注释数组更新,支持 v-model:annotations。 | | on-annotations-change | { action, annotation, annotations } | 注释新增、编辑、删除。 |

部门 tree 事件:

| 事件名 | 返回值 | 说明 | | --- | --- | --- | | on-member-name-change | { name, oldName, member, scope, index } | 成员姓名编辑完成。 | | 其它 <vue3-tree-org> 事件 | 同原组件 | 例如 on-node-clickon-contextmenuon-zoomon-dragupdate:annotations 等都可以继续监听。部门 tree 的“编辑名称”菜单向外仍以 command: 'edit' 抛出。 |

Methods

通过 ref 调用:

<vue3-tree-org ref="treeRef" :data="data" />
<department-tree ref="departmentTreeRef" :data="departmentData" />
const treeRef = ref()

treeRef.value.filter('财务')
const keys = treeRef.value.getExpandKeys()
treeRef.value.setExpandKeys(['root', 'safety'])

departmentTreeRef.value.filter('安全')
departmentTreeRef.value.setExpandKeys(['department-root'])

| 方法 | 参数 | 说明 | | --- | --- | --- | | filter | value | 根据 filter-node-method 过滤节点。 | | getExpandKeys | — | 获取当前展开节点 id 数组。 | | setExpandKeys | keys | 设置展开节点 id 数组。 |

Slots

普通组织树:

| 插槽名 | 参数 | 说明 | | --- | --- | --- | | 默认插槽 | { node } | 自定义节点内容。 | | expand | { node } | 自定义展开按钮。 | | annotation | { annotation, nodes } | 自定义注释框内容。 |

部门 tree:

| 插槽名 | 参数 | 说明 | | --- | --- | --- | | node | { node } | 自定义部门树组织节点。 | | summary | { annotation, bottomNode, groups } | 自定义底部汇总框。 |

样式覆盖

组件样式需要先引入:

import '@wsh704/vue3-tree-org-department/lib/vue3-tree-org.css'

常用样式覆盖示例:

.department-tree-card {
  border-radius: 6px;
}

.department-tree-summary__group {
  border-color: #94a3b8;
}

.department-tree-member-name__editor {
  width: 6em;
}

如果在 Vue 单文件组件里使用 scoped,需要用 :deep()

<style scoped>
:deep(.department-tree-card) {
  border-radius: 6px;
}
</style>

在业务项目中的推荐接入步骤

  1. 安装依赖。
npm install @wsh704/vue3-tree-org-department
  1. 在入口文件引入组件库和 CSS。
import treeOrg from '@wsh704/vue3-tree-org-department'
import '@wsh704/vue3-tree-org-department/lib/vue3-tree-org.css'

app.use(treeOrg)
  1. 普通组织树使用 <vue3-tree-org>

  2. 部门 tree 使用 <department-tree>,数据格式保持 { root, bottomNode }

  3. 原来传给 <vue3-tree-org> 的属性和事件,可以继续传给 <department-tree>;只需要把原树数据放到 root,并额外增加 bottomNode

  4. 如果需要姓名编辑,监听 on-member-name-change,在事件里保存到业务状态或调用接口。

  5. 如果需要禁用编辑,给 department-tree 设置 :disabled="true"

  6. 如果需要自定义 UI,优先通过外层容器和 CSS 覆盖;只有确实需要完全重写时再使用 nodesummary 插槽。

常见问题

页面没有样式

确认是否引入了 CSS:

import '@wsh704/vue3-tree-org-department/lib/vue3-tree-org.css'

<department-tree> 不显示

检查:

  • 是否执行了 app.use(treeOrg)
  • 是否从正确包名引入:@wsh704/vue3-tree-org-department
  • data 是否包含 bottomNode。推荐格式是 { root, bottomNode };如果直接传根节点,则根节点上需要有 bottomNode
  • 容器是否有高度,例如 height: 760px

部门 tree 为什么只连接最后一级

这是当前业务规则。bottomNode.members 只匹配组织树最后一级叶子节点,不匹配部门、根节点等上级节点。

部门 tree 可以编辑 title 吗

不可以。当前规则是:

  • title 固定。
  • position 固定。
  • 只有姓名 members[].name 可以右键编辑。

多个姓名编辑时点击另一个输入框会退出吗

不会。同一个组织节点或底部分组内,多个姓名输入框可以互相点击切换,只有点击到编辑范围外或按 Enter 失焦后才退出编辑态。

右键没有“编辑名称”

检查:

  • 是否右键在组织节点卡片或底部汇总分组上。
  • department-tree 是否设置了 disabled=true
  • 当前节点或分组是否有 members

需要同时保留旧组织树功能和新增部门 tree

可以。这个包同时导出并注册:

  • <vue3-tree-org>
  • <department-tree>

在同一个项目里可以同时使用,两者不会冲突。<department-tree> 本身也是基于原 <vue3-tree-org> 的增强封装,原组件的大部分属性、事件、方法都能继续使用;特色能力只来自额外的 bottomNode

验证记录

已在 treeOrg/vue3-demo 中完成真实 npm 安装和多功能验证:

  • npm run build 通过。
  • 基础树、自定义节点、自定义注释、注释连线、缩放通过。
  • 搜索过滤通过。
  • 懒加载通过。
  • 默认右键菜单编辑、新增、删除通过。
  • 自定义右键菜单通过。
  • 部门 tree 渲染、叶子匹配、底部汇总、姓名编辑、disabled 禁用编辑通过。
  • 页面 runtime errors 为空。

验证页面:

http://localhost:8088/#/tree-org-npm