快速开始
本节使用静态数据在 Vue 3 页面中渲染一棵可拖拽、可缩放和可展开收起的组织架构树。
环境要求
- Node.js 18 或更高版本
- Vue 3.3 或更高版本
- 支持 ESM 与 TypeScript 的构建工具,推荐 Vite 5
安装
当前尚未发布到公共 npm Registry
仓库中的三个包使用 workspace:* 互相引用,目前应从源码工作区运行。下面的包安装命令在正式发布后才可用。
从源码启动:
bash
git clone https://github.com/Liugq5713/tree.git
cd tree
pnpm install
pnpm dev:docs启动后可访问静态数据在线示例,或继续阅读下面的完整接入过程。
包发布后的 Vue 项目安装方式:
bash
pnpm add @org-tree/core @org-tree/vuebash
npm install @org-tree/core @org-tree/vuebash
yarn add @org-tree/core @org-tree/vue准备数据
SDK 默认使用 id 与 parentId 建立父子关系。根节点的 parentId 必须是 null。
ts
const data = [
{ id: 'company', parentId: null, name: '星河科技' },
{ id: 'product', parentId: 'company', name: '产品中心' },
{ id: 'engineering', parentId: 'company', name: '研发中心' },
{ id: 'frontend', parentId: 'engineering', name: '前端团队' },
{ id: 'backend', parentId: 'engineering', name: '后端团队' },
]业务字段不需要塞进额外的 info 对象。布局完成后,原始节点对象会保留在 PositionedNode.info 中。
创建组件
在 DOM 挂载后创建 OrgTreeCore,调用 loadData() 触发首次布局。OrgTreeProvider 提供画布和上下文,OrgTree 负责订阅并渲染布局结果。
vue
<script setup lang="ts">
import type { OrgTreeCore as OrgTreeCoreInstance, PositionedNode } from '@org-tree/core'
import { OrgTreeCore } from '@org-tree/core'
import { OrgTree, OrgTreeProvider } from '@org-tree/vue'
import { onMounted, ref, shallowRef } from 'vue'
const containerRef = ref<HTMLElement>()
const sdk = shallowRef<OrgTreeCoreInstance>()
const data = [
{ id: 'company', parentId: null, name: '星河科技' },
{ id: 'product', parentId: 'company', name: '产品中心' },
{ id: 'engineering', parentId: 'company', name: '研发中心' },
{ id: 'frontend', parentId: 'engineering', name: '前端团队' },
{ id: 'backend', parentId: 'engineering', name: '后端团队' },
]
onMounted(async () => {
const instance = new OrgTreeCore({
el: containerRef.value!,
direction: 'down',
layout: {
spaceX: 32,
spaceY: 56,
nodeSize: { width: 200, height: 80 },
},
data,
})
sdk.value = instance
await instance.loadData()
})
function nodeName(node: PositionedNode) {
return String(node.info.name ?? node.nodeId)
}
</script>
<template>
<div ref="containerRef" class="tree-container">
<OrgTreeProvider v-if="sdk" :sdk="sdk">
<OrgTree v-slot="{ node }">
<article class="org-card">
<strong>{{ nodeName(node) }}</strong>
<small>{{ node.nodeId }}</small>
</article>
</OrgTree>
</OrgTreeProvider>
</div>
</template>
<style scoped>
.tree-container {
width: 100%;
min-height: 640px;
overflow: hidden;
}
.org-card {
box-sizing: border-box;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
width: 200px;
height: 80px;
color: #202124;
background: #fff;
border: 1px solid #e5e7eb;
border-radius: 10px;
box-shadow: 0 6px 18px rgb(24 32 56 / 8%);
}
.org-card small {
margin-top: 6px;
color: #8a8f98;
}
</style>尺寸必须一致
示例同时把 layout.nodeSize 与 .org-card 设置为 200 × 80。如果两者不同,布局引擎使用的空间与真实 DOM 尺寸不一致,节点和连接线可能重叠。
添加方向切换
setDirection() 会更新方向并通知 Vue 层,但不会单独触发重排。切换方向后再次调用 loadData():
ts
async function changeDirection(direction: 'down' | 'right') {
if (!sdk.value)
return
sdk.value.setDirection(direction)
await sdk.value.loadData()
}添加节点操作
ts
await sdk.value?.expand({ nodeId: 'engineering' })
sdk.value?.collapse({ nodeId: 'engineering' })
sdk.value?.setNodeStatus('engineering', { highlight: true })静态数据会在首次加载时全部参与布局。调用 collapse() 后,后代节点会从当前布局中移除;再次 expand() 时从原始静态数据中恢复直接子节点。