Vue 接入
@org-tree/vue 提供交互画布和节点渲染层。它不创建数据源,必须与一个 OrgTreeCore 实例配合使用。
组件关系
OrgTreeProvider
└─ DomCanvas 拖拽、缩放、画布尺寸
└─ OrgTree 订阅 Core 的布局与状态
├─ 连接线
└─ OrgTreeNode 节点容器、展开按钮、自定义 slot标准组合方式:
<OrgTreeProvider :sdk="sdk">
<OrgTree />
</OrgTreeProvider>OrgTreeProvider 的 sdk 是必填属性。OrgTree 当前没有公开 props。
生命周期
推荐在 onMounted 中创建 SDK,并使用 shallowRef 保存类实例:
<script setup lang="ts">
import type { OrgTreeCore as OrgTreeCoreInstance } from '@org-tree/core'
import { OrgTreeCore } from '@org-tree/core'
import { OrgTree, OrgTreeProvider } from '@org-tree/vue'
import { onMounted, ref, shallowRef } from 'vue'
const root = ref<HTMLElement>()
const sdk = shallowRef<OrgTreeCoreInstance>()
onMounted(async () => {
const instance = new OrgTreeCore({
el: root.value!,
layout: { spaceX: 32, spaceY: 56 },
data,
})
sdk.value = instance
await instance.loadData()
})
</script>
<template>
<div ref="root">
<OrgTreeProvider v-if="sdk" :sdk="sdk">
<OrgTree />
</OrgTreeProvider>
</div>
</template>OrgTree 会在挂载时订阅 Core 事件,并在卸载时自动取消自己的订阅。业务代码通过 sdk.onUpdate() 等方法建立的订阅仍需自行清理。
自定义节点
不提供默认插槽时,内置卡片只显示 nodeId。正式项目通常通过 OrgTree 的默认 scoped slot 渲染业务卡片。
<OrgTree v-slot="{ node, isLeaf, expanded, loading }">
<article
class="employee-card"
:style="{
width: `${node.width}px`,
height: `${node.height}px`,
}"
>
<strong>{{ node.info.name }}</strong>
<span>{{ node.info.title }}</span>
<small v-if="loading">正在加载…</small>
<small v-else-if="!isLeaf">
{{ expanded ? '已展开' : '可展开' }}
</small>
</article>
</OrgTree>插槽上下文:
| 字段 | 类型 | 说明 |
|---|---|---|
node | PositionedNode | 坐标、尺寸和原始业务数据 |
isLeaf | boolean | 当前节点是否已确认没有子节点 |
expanded | boolean | 当前节点是否展开 |
loading | boolean | 是否正在加载直接子节点 |
展开按钮由节点容器统一渲染,不需要业务卡片重复实现。
让卡片感知完整状态
插槽直接提供加载与展开状态。需要读取 highlight、visible 等完整状态时,在自定义卡片组件中注入 ORG_TREE_STATUS_KEY:
<script setup lang="ts">
import type { PositionedNode } from '@org-tree/core'
import { ORG_TREE_STATUS_KEY } from '@org-tree/vue'
import { computed, inject } from 'vue'
const props = defineProps<{ node: PositionedNode }>()
const statusMap = inject(ORG_TREE_STATUS_KEY)
const highlighted = computed(
() => statusMap?.get(props.node.nodeId)?.highlight === true,
)
</script>
<template>
<article
class="node-card"
:class="{ 'node-card--highlighted': highlighted }"
:style="{
width: `${node.width}px`,
height: `${node.height}px`,
}"
>
{{ node.info.name }}
</article>
</template>然后在树中使用:
<OrgTree v-slot="{ node }">
<BusinessNodeCard :node="node" />
</OrgTree>状态与样式解耦
Core 只维护状态,内置默认卡片不会自动为 highlight 或 visible 添加业务样式。自定义卡片应根据状态决定高亮、隐藏或禁用效果。
节点点击
点击事件直接绑定在业务卡片上:
<OrgTree v-slot="{ node }">
<button
class="node-card"
:style="{ width: `${node.width}px`, height: `${node.height}px` }"
@click="selectedNode = node"
>
{{ node.info.name }}
</button>
</OrgTree>节点容器会阻止 mousedown 冒泡,点击卡片不会误触画布拖拽。
定位节点
渲染层为每个节点生成稳定的 DOM ID:org-tree-node-${nodeId}。外部搜索面板可以使用它定位:
function locateNode(nodeId: string) {
document
.getElementById(`org-tree-node-${nodeId}`)
?.scrollIntoView({
behavior: 'smooth',
block: 'center',
inline: 'center',
})
}位于 OrgTree 后代中的组件也可以注入 ORG_TREE_LOCATE_KEY,调用渲染层的精确定位函数。
画布行为
内置 DomCanvas 提供:
- 鼠标或触控拖动画布
- 控制按钮缩放
Ctrl + 滚轮或双指缩放- 50%~300% 的缩放范围
当前画布视口高度使用 100vh。将树放在弹窗或局部面板时,需要结合页面布局检查滚动和层叠关系。
运行时重建
间距、字段映射、节点尺寸或 transformChildren 改变时,推荐先卸载旧的 Provider,再创建一个新的 SDK:
async function rebuildTree() {
sdk.value = undefined
await nextTick()
const instance = new OrgTreeCore(createConfig())
sdk.value = instance
await instance.loadData()
}只切换方向时可复用 SDK,调用 setDirection() 后再调用 loadData()。