指南

多树根对齐 OkrTreeGroup

OkrTreeGroup 多树根对齐的测量机制与自动重测触发点

align-root 让每棵树的根节点在自身容器内居中。多棵 OKR 树并排对比、而容器宽度装不下最深的一侧时,各树"各自居中"的坐标就不一致——这正是原版需要"结合业务层手动测量 DOM"的场景。<OkrTreeGroup> 把它收进组件:

App.vue
<okr-tree-group>
  <vue-okr-tree :data="a" :left-data="leftA" only-both-tree direction="horizontal" node-key="id" />
  <vue-okr-tree :data="b" :left-data="leftB" only-both-tree direction="horizontal" node-key="id" />
</okr-tree-group>
名称说明
alignprop,boolean,默认 true;false 时各树独立排布
refresh()方法,手动重新测量(字体加载完成、外部样式变化等特殊场景)

成员树需开启 align-root(默认已开)。

测量机制

组读取每个成员左子树容器的自然宽度取最大值,统一写回容器上的 --okr-group-left-width,于是各树根节点落在同一条竖线上。测量期间组根带 is-measuring、完成后带 is-measured(可用于自己加过渡样式)。

自动重测的触发点:成员挂载 / 更新、ResizeObserver 观察到的组内尺寸变化、以及 document.fonts.ready(字体落地后文字宽度会变)。所以 refresh() 只在极少数外部样式绕过这些信号时才需要手动调——比如异步注入的样式表改了节点内边距。

与画布、SSR 的组合

  • 可以放进 <OkrTreeViewport> 内一起用;缩放平移不影响对齐结果(测量读的是布局宽度,不含 transform)。
  • SSR 下服务端渲染的是未测量状态(没有 --okr-group-left-width),首屏各树按自身容器居中,挂载后测量完成才对齐。这是浏览器 API 依赖,不是缺陷;若首屏对齐对你是硬需求,把整组包在客户端组件里或预留等宽容器。

OKR 展示模式

该模式的出现,是为了实现跟飞书 OKR 展示的视图一样效果,所以在 Tree 的模式下,扩展成左右两棵子树并根据最长的一个根进行根对齐。

第二棵树的左侧层级更深;OkrTreeGroup 测量组内最大左子树宽度并统一,两棵树的根节点严格对齐。关闭可对比原始行为
(左)研发-前端
(左)研发-后端
(左)UI 设计
(左)产品研发部
(左)销售一部
(左)销售二部
(左)销售部
(左)财务部
xxx科技有有限公司
产品研发部
研发-前端
研发-后端
UI 设计
销售部
销售一部
销售二部
财务部
(左)前端一部
(左)前端二部
(左)研发-前端
(左)研发-后端
(左)UI 设计
(左)产品研发部
(左)销售一部
(左)销售二部
(左)销售部
(左)财务部
xxx科技有有限公司
产品研发部
研发-前端
研发-后端
UI 设计
销售部
销售一部
销售二部
财务部
该模式必须设置 onlyBothTree,以及通过 leftData 表示左子树的结构。Vue 3 版内建 align-root(默认开启,纯 CSS)让每棵树的根节点在自身容器内居中,展开/收起不会位移。 多棵树并排且宽度不足时,用 <OkrTreeGroup> 包裹:它会测量组内所有左子树容器的最大自然宽度并统一设置,使各树根节点水平坐标完全一致——即原版 README 里"结合业务层手动测量 DOM 实现对齐"的正规替代。组件自动响应成员的挂载 / 更新 / 尺寸变化,也可通过 ref.refresh() 手动触发。
<template>
  <!-- 多棵树并排对比时用 OkrTreeGroup 包裹,组内根节点严格对齐 -->
  <okr-tree-group>
    <vue-okr-tree :data="testData" :left-data="testLeftData" only-both-tree direction="horizontal" show-collapsable node-key="id" default-expand-all />
    <vue-okr-tree :data="testData" :left-data="testLeftData2" only-both-tree direction="horizontal" show-collapsable node-key="id" default-expand-all />
  </okr-tree-group>
</template>

<script setup>
import { ref } from 'vue'
import { VueOkrTree, OkrTreeGroup } from 'vue3-okr-tree'

const testData = ref([{
  id: 1, label: 'xxx科技有有限公司',
  children: [{
    id: 2, label: '产品研发部',
    children: [{ id: 3, label: '研发-前端' }, { id: 4, label: '研发-后端' }, { id: 5, label: 'UI 设计' }]
  }, {
    id: 6, label: '销售部',
    children: [{ id: 7, label: '销售一部' }, { id: 8, label: '销售二部' }]
  }, {
    id: 9, label: '财务部'
  }]
}])

const testLeftData = ref([{
  id: 1, label: 'xxx科技有有限公司',
  children: [{
    id: 12, label: '(左)产品研发部',
    children: [{ id: 13, label: '(左)研发-前端' }, { id: 14, label: '(左)研发-后端' }, { id: 15, label: '(左)UI 设计' }]
  }, {
    id: 16, label: '(左)销售部',
    children: [{ id: 17, label: '(左)销售一部' }, { id: 18, label: '(左)销售二部' }]
  }, {
    id: 19, label: '(左)财务部'
  }]
}])

// 左侧层级更深的第二棵树(根对齐对比)
const testLeftData2 = ref([
  { id: 1, label: 'xxx科技有有限公司', children: [
    { id: 12, label: '(左)产品研发部', children: [
      { id: 13, label: '(左)研发-前端', children: [
        { id: 131, label: '(左)前端一部' },
        { id: 132, label: '(左)前端二部' }
      ] },
      { id: 14, label: '(左)研发-后端' },
      { id: 15, label: '(左)UI 设计' }
    ] },
    { id: 16, label: '(左)销售部', children: [
      { id: 17, label: '(左)销售一部' },
      { id: 18, label: '(左)销售二部' }
    ] },
    { id: 19, label: '(左)财务部' }
  ] }
])
</script>
显示代码
Copyright © 2026