Repository navigation
Releases: baiwumm/vue3-okr-tree
Release list
v1.16.0
1.16.0(2026-09-26)
- 新增
virtual虚拟滚动:同层可见兄弟数 ≥ 50 的行只渲染视口内窗口,DOM 数量与滚动流畅度不再随总数增长(1 父 + 10000 平铺子节点实测渲染节点 10001 → 13)。实现要点:未渲染兄弟的位置由等尺寸占位块顶住(float 行总宽与每个渲染节点的坐标和全量渲染逐像素一致),占位块自带连线段续接横线,行首 / 行末的边界帽(:first-child/:last-child的去线与圆角)语义由占位块自然继承;展开行的子容器按宽度模型显式定宽(float 的 shrink-to-fit 取min(max(min-content, 可用宽), max-content),单个巨宽占位块会把容器钉在 min-content 上、把渲染节点挤到第二行折断连线);折叠行不渲染占位块,折叠宽度与全量渲染一致。aria(aria-setsize/aria-posinset)、show-node-num计数、getVisibleNodes()全部按全量可见列表输出;scrollToNode与键盘漫游对窗口外目标先揭示再定位(逐条推进不跳焦)。要求数字型label-width(horizontal 布局还要求label-height),auto 尺寸下达标行退回全量渲染并警告;创建期快照,运行时变更输出警告。已知边界:万级数据首帧成本主要在 store 构建(1 万节点约 2.9s,与 virtual 无关、全量渲染同样存在),virtual 消除的是 DOM 数量与滚动 / 展开时的渲染卡顿;压缩后产物 +2.1 kB gzip(ESM / UMD 预算上调至 21 kB)。姊妹包 react-okr-tree 同批同形。
v1.15.0
1.15.0(2026-09-26)
一个功能 minor:Vue Devtools 面板(dev only,roadmap #16 最后一项,做完 #16 整项清空)与 DEFAULT_PROPS 导出补齐(两仓导出面最后一个不对称清零)。两项都是纯增量,无任何行为变化。姊妹包 react-okr-tree 同号跟随(DEFAULT_PROPS 侧早已导出、Devtools 面板无对等物,详见其 CHANGELOG)。
- 新增 Vue Devtools 面板(dev only):开发环境自动向 Vue Devtools 注册 OkrTree 面板——树实例列表(根节点文案 + 注册表节点数 tag,OKR 模式带标记)、概要(节点数 / 展开数 / 当前节点 / 勾选与半选计数)与右树 / 左树的节点注册表全量转储(文案 / 层级 / 展开 / 可见 / 勾选 / 半选 / 子节点数),数据按需拉取、不占渲染路径。实现零依赖:不引
@vue/devtools-api,直接按其 v6 线协议与window.__VUE_DEVTOOLS_GLOBAL_HOOK__握手(devtools-plugin:setup事件与__VUE_DEVTOOLS_PLUGINS__排队条目形状逐字同源,api 由 Devtools 后端回调传入)。门控与开发期警告同一套(运行时process.env.NODE_ENV,使用方打包器替换后整段消除,SSR 与无process的 UMD 场景视为生产环境不注册)——npm 产物增大约 1.5 kB gzip(ESM 16.31 → 17.81 / 预算 19),不会进入消费方的生产包,与 Pinia / Vue Router 内置 Devtools 集成同一模型。姊妹包 react-okr-tree 无对应物(React DevTools 不提供第三方自定义面板 API)。 - 补导出
DEFAULT_PROPS:字段映射的默认值常量({ children: 'children', label: 'label', disabled: 'disabled' })此前只在内部使用,而姊妹包 react-okr-tree 侧已导出——这是两仓导出面审计记录的最后一个不对称,本次清零。宿主自定义字段映射时可以{ ...DEFAULT_PROPS, label: 'name' }而不必写全所有字段。纯增量,无任何行为变化。
v1.14.2
1.14.2(2026-09-25)
四条修复的一批:Demo 交互层批次(G8)实测出来的三处库语义缺口 + 一处画布交互卡死。对外 API 形状零变化,按「修复 +1 patch」定档。其中 default-checked-keys 一条改的是可观察行为(运行时重放的判据从按引用改成按内容),唯一会感知到差异的用法是「传一个等值新数组来强制清空勾选」——那等于依赖每次渲染回滚用户操作,故仍按 patch 走,但如果你确实这么写,请改用 setCheckedKeys()。姊妹包 react-okr-tree 同号跟随。
修复
node-drag-end在成功放置后不再报 null 载荷:handleDrop先清掉dragOverNode/dragOverType(指示线不能留在屏幕上),而node-drag-end是 drop 之后才派发的,于是这条事件的dropNode/dropType恒为 null——文档站的拖拽用例每次成功拖动都记一句「未完成放置」。现在改为在 drop 里就地发出node-drag-end并收尾手势(同时清draggingNode,让真到达的那次 dragend 被守卫短路,不会补一条重复事件),载荷与node-drop一致;取消或被拒的拖拽仍按语义报 null。选这条路线而不是「在 dragend 里读落点备份」:跨父级移动会把源元素卸载重建,浏览器那次 dragend 落在已脱离文档的节点上,宿主侧收不到(react 侧正是这种形态)。- 过滤词只命中 OKR 左子树时,整棵树不再从 DOM 消失:共用的根节点自身不匹配、右树侧零命中时被判不可见,而节点根元素是
v-if="node.visible"——刚命中的左子树跟着被卸载,页面空掉(getVisibleNodes()读 0)。Q1「父节点保持可见」当年只修通了右树这一侧;现在左树命中也会把根保住(OKR 下filter先右后左的调用顺序是承重的,已写进注释)。 default-checked-keys运行时按内容比较,不再按引用重放:宿主每次渲染新建一个等值数组(计算属性、漏了useMemo的派生表达式都会这样),原先被当成「默认勾选变了」,先清空全部勾选再重放列表,把用户刚勾掉的、刚勾上的整片抹回去。现在只有列表内容真的变了才重放(String(key)归一去重后比对,与getCheckedKeys同口径),undefined与[]视作同一份「没有默认勾选」。API 表与文档站措辞同步更正。- 画布平移把手势甩出画布边界再松手,会卡住平移态并让悬停继续拖动画布:
OkrTreeViewport的pointerup/pointercancel只挂在画布根元素上(既没有setPointerCapture,也没有 window 级监听),指针在画布外松开时这两个处理器根本不执行——is-panning留在原地、panStart也不清,于是之后不带按键的悬停移动仍按panStart继续改写偏移(实测从translate(125px, -379.5px)一路走到translate(290px, 0px))。现在由一个挂载期注册、卸载时摘掉的常驻 window 监听收尾(常驻而不是按次添加,免得 1.14.1 修掉的监听堆叠换个形式回来);这条收尾不武装「吞掉一次 click」——松手在画布外时浏览器不会在画布里派发那次 click,武装了就会吃掉用户回到画布里的第一次正常点击。姊妹包 react-okr-tree 同批同形。
v1.14.1: ci: 四个 action 升到不再跑在 Node 20 上的第一个大版本
1.14.1(2026-09-24)
对外 API 零变化的一批:修掉 7 个真实缺陷(组对齐宽度被首量钉死、contains 未递归左子树导致可成环、换绑 data 把整棵后代摘出注册表、zoomIn / zoomOut 锚点取错、平移后吞点击堆积监听、产物丢失三家打包器的 ignore 注释、connector="svg" 稳态自持重排),另有 3 项性能收敛、10 条门禁补强与两站文档对齐。姊妹包 react-okr-tree 同号跟随。
性能
aria-setsize/aria-posinset改为按层算一次下发:旧实现是每个节点一个 computed,各自读parent.childNodes做filter+indexOf⇒ 同层 s 个节点扫两遍、O(s²)。新增src/lib/okr-tree/aria-set.ts的setPositions(list),由父节点(含根层的OkrTree)在渲染子列表时算好{size,pos}作为 props 传下去;不可见节点仍不输出这两个属性(props 为undefined),左右两树各自成组,口径与旧实现一致。react 侧本就是父下发形状,同批把其map内的indexOf换成同一套单遍计算。- 复选框一次点击从 4 次全树遍历降到 1 次:
check事件的 payload 原先由getCheckedNodes()/getCheckedKeys()/getHalfCheckedNodes()/getHalfCheckedKeys()四次调用拼成,而两个 key 版内部各自还要再调一次节点版 ⇒ 一次点击要走 4 遍全树(含整棵 OKR 左树)。新增collectCheckState(leafOnly)一次遍历算齐四份,四个公开方法原样保留。语义逐字不变:String(key)去重、遍历顺序、跳过空 key、checked 的节点不进半选列表。 - 同层整批换引用时
updateChildren的 key 回退降为线性:回退原本是oldNodes.find(n => !used.has(n) && n.key === k),同层 s 项全部换引用时第 i 项平均要扫 i 次 ⇒ 该层退化到 O(s²)。现改成一次建好的Map<key, TreeNode[]>索引。实测(把keygetter 包住计数):s=3200整轮换引用时 key 访问 16,005 次(=5s),而旧实现按公式是 5,121,600 次(s(s+1)/2),setData耗时从 35.9ms 降到 15.0ms。两处语义刻意保住——键不做String归一(原判断是===,11与'11'不该互配)、桶存数组而非单值(同层重复 key 时第二项要能命中第二个旧节点),各有一条用例钉住,换成朴素的Map<string, TreeNode>就当场红。
修复
-
zoomIn/zoomOut的锚点改用可视区尺寸:原先取getBoundingClientRect().width / 2,而fitToScreen与 react 侧都取clientWidth / 2——视口内出现滚动条时两者相差半条滚动条宽,同一个包的两条路径与姊妹包之间会各偏一点。现统一走新增的viewportCenter()(clientWidth / clientHeight),与 react 侧的center()同形。 -
contains把 OKR 左子树算进子树范围:moveNode的自环硬守卫与dropValid都靠TreeStore.contains()回答「目标是不是被拖节点的后代」,但它只递归childNodes,而 OKR 左子树挂在leftChildNodes上——于是moveNode(OKR 根, 其左子树内的节点, 'inner')放行,根与左子树互相指向,getVisibleNodes这类同时走两侧的遍历不再收敛。现一并递归leftChildNodes;同时钉一条「右树节点仍可移进左子树」的用例,防止把reassignSide那条合法的跨侧路径一起堵死。 -
connector="svg"稳态不再自持重排:redrawConnectors结尾原先无条件把新数组写进connectorEdges,于是「写 ref → 重渲染 →onUpdated再排一帧 → 再写」闭成环——空载页面也以每秒一帧的节奏重排全树、逐节点读一遍getBoundingClientRect(react 版实测同规模约 10,400 次/秒)。现改为逐条比对d,完全相同就保持原引用,与 react 版的sameEdges短路同形。顺带补上这条环原先兜着的缺口:onUpdated其实指望不上——节点的expanded只被OkrTreeNode读,父组件不重渲染就不触发它,所以展开态变更改走新增的onExpansionChanged()(同步受控 keys + 显式排一帧重绘),同时覆盖节点点击与expandNode/collapseNode两条路径;实测空闲 12 帧之后再收起节点,实体边仍会正确替换成收起残枝。 -
换绑 data 引用不再把整棵后代摘出注册表:
data数组引用保持不变、只把某一层对象换成同 key 的新对象(轮询接口的典型局部更新)时,TreeNode.updateChildren()的换绑分支原先调递归版store.deregisterNode()——它连带删掉该节点全部后代在nodesMap里的登记,而复用路径不会重新登记,于是这些后代的getNode/getNodePath/setCurrentKey/remove/moveNode等按 key 的公开方法一律静默失效,而节点仍照常渲染、仍可点击。改用新增的deregisterNodeSelf()(只摘本节点):该分支的匹配条件本就是 key 相等,后代的实例与 key 都没变,留在注册表里才是正确状态;真正被移除的节点仍由尾部注销循环递归清理。姊妹包 react-okr-tree 同批同形修复。 -
平移后吞点击不再往视口元素上堆监听:
handlePointerUp原先每次平移结束都addEventListener('click', …, { capture: true, once: true }),而触摸平移压根不派发 click——监听于是按平移次数累积挂在元素上,直到某一次真出现 click 才被一次性摘掉(组件卸载时仍挂着)。现改记一个swallowNextClick标志,由模板上常驻的@click.capture处理判断并消费;对外语义与改动前逐字一致(平移后紧跟的那一次 click 被吞,第二次照常送到节点),只是不再残留注册。两条新用例分别钉「吞一次且只吞一次」与「平移过程中元素上click注册数为 0」——把实现还原成 once 监听、或把标志赋值摘掉,各打红其中一条。与姊妹包 react-okr-tree 同批同形。 -
产物真的保住 html-to-image 的三条 ignore 注释(并把压缩器换成 Terser):可选 peer 靠「变量说明符的动态 import」避开打包,但消费者的打包器只认注释——
@vite-ignore管 Vite/Rollup,Next 16 的 Turbopack 只认webpackIgnore/turbopackIgnore,缺了就是下游构建期 Module not found(本包能发出去、对面装不上;文档站是第一个撞上的真实消费者)。此前源码只有@vite-ignore一条,而且就算补齐也没用:scripts/post-build.mjs会用 esbuild 把 ES 产物补压一次,连注释一起吃掉。实测 esbuild 四档压缩(minifyWhitespace单开 /legalComments: 'inline'/ 把注释写成 legal 形态 / 全量压缩)没有一档保留这三条。现改build.minify: 'terser'+format.comments: /vite-ignore|webpackIgnore|turbopackIgnore/,并删掉 post-build 那道补压(顺带 ES 的.map不再被删,与产物行号对齐)。体积不升反降:ESM gzip 16.77 → 16.12 kB、UMD 16.64 → 16.23 kB(Terser 比 esbuild 更小),三项预算余量一起变宽。scripts/verify-dist.mjs补 7 条门禁:ESM /.cjs/.umd各断「未静态引入可选 peer」+「三条注释齐全」,另断 ESM 仍有import(调用点——verify:dist 34 → 41 条。回归验证(把历史形态装回去):重新加上 post-build 的 esbuild 补压后,门禁当场红FAILED: ESM 保留可选 peer 的三家打包器 ignore 注释。新增 devDependencyterser。 -
组对齐宽度不再被首量钉死(
is-measuring此前从未落到 DOM 上):OkrTreeGroup.measure()原先在同一个同步块里「measuring = true→ 读 rect →false」,而这个类是绑在响应式状态上的、DOM 要到下一个 tick 才更新,于是临时测量态压根没生效,读到的一直是当前分配宽度;偏偏.is-measured又把左容器宽度钉成var(--okr-group-left-width),结果首量之后再也涨不上去——实测 320px 容器里首量得 140px,放宽到 1280px 仍是 140px;组里先放一棵窄树、再加入一棵左子树宽得多的成员,var 仍停在首量值(refresh()走同一条 measure,救不了)。现改为测量期间直接操作 DOMclassList:摘is-measured→ 加is-measuring→ 读 → 复原。两条缺一不可:只加不摘等于没加,因为style.css里.is-measured的钉宽规则排在.is-measuring的max-content之后且特异度相同(这条是踩出来的——先只做「加类」时新增的浏览器用例在变异下仍然全绿)。刻意不走await nextTick():那会让测量跨帧,真被渲染出来的测量态触发一次组件更新 → 子树onUpdated再请求测量,容易绕回自持环;同步 add → 读(读 rect 自带强制布局)→ 复原全程在一个任务内,浏览器不会绘制中间态,也不闪。回归两条:a11y-group.spec.ts的测量用例新增「读 rect 那一刻is-measuring必须在 DOM 上」(摘掉类操作 →expected [false, false] to equal [true, true]);新增浏览器级tests/visual/group-align.spec.ts断「后加入更宽成员时对齐宽度要涨」(同一变异当场红)。视觉基线 17 → 18 条全过、无需重拍。与姊妹包 react-okr-tree 同批同形。
工程化(无对外行为变更)
- CSS 几何常量钉进
verify:dist(与 react 侧 G17 同批同形):连接线的几何值全是相互咬合的硬编码,改一个就错位,而组件层没有任何断言能发现它。新增 21 条断言:六个变量(--okr-gap-level:20px/--okr-gap-sibling:5px/--okr-line-width:1px/--okr-line-radius:5px/--okr-btn-size:20px/--okr-gap-node-y:10px)各断「每处使用都带兜底」+「兜底值处处同值」;左子树短头width:12px/height:10px/left:calc(100% - 11px)与两处-1px/!important修正各断恰好出现一次(两次=有人复制规则没删原件,同特异度下后者说了算);okr-unstyled的中和规则断为五类选择器且基础 +:hover共两处;再断.is-measuring排在.is-measured之前——组对齐修复的承重前提正是这条同特异度顺序,颠倒之后measure()无论怎么改都会读回被钉住的宽度。匹配前先归一两种形态:声明体抹掉全部空白(绕开压缩器「逗号后有无空格」的差异),选择器只把空白折成单空格(.a b压成.ab就走形了)。顺带说明为什么不做「两仓 CSS 逐字 diff」:两仓src的 style.css 实测只差头注释与@import路径两处、transition.css全等,但产物由两套压缩器各写一遍(本仓 esbuild、react 走 rolldown 内置那套),会合并同声明体的相邻规则、重排声明、transparent→0 0、.3s height→height .3s、颜色折成#fffffff0,逐字比必然假红。五条变异在两仓各跑一遍、全部打红(兜底值20px→24px、规则顺序颠倒、五类降四类、-11px→-12px、12px→14px)。CSS 段现两端各 33 条、逐条同序同字面(此前两端各缺一条:本仓少「含打印与减弱动效块」、react 少「连接线颜色无残留硬编码」,现已补齐)。verify:dist41 → 62 条 ok。 - 缺 node-key 的受控 prop 警告族补齐三条断言(与 react 侧 G4 同批同形):
OkrTree.vue:312-321有四条「需要同时设置 node-key」的开发期警告,controlled.spec.ts此前只在一次 pooled 断言里命中了expanded-keys与current-key两条文案,default-expanded-keys/default-checked-keys两支从未被断过,currentNodeKey那一支也只是顺带过的——它的消息写着「current-key / current-node-key」,只传currentNodeKey时靠子串也能命中,把源码里的|| props.currentNodeKey !== undefined摘掉都不会红。现新增一条用例:三个 prop 各单独挂载一次、每次都断「这一次的消息集合」,另加一条反向守卫(补齐 node-key 后三条都不许出现)。反向守卫一开始是永真的:warn()默认按文案去重,第四次挂载前没清表,消息被前三次吃掉,实测把if (!props.nodeKey)改成无条件警告后这条守卫照样绿(红的只是文件里另一条老用例)——现每次挂载前resetWarnings(),改无条件后两条用例一起红。三条变异全部实测只红新用例(驱动_scratch/probe-warn-mutation.mjs,自带未变异基线、锚点存在性自检与 finally 还原)。单测 257 → 258 条。 - 受控锁定态补用例(与 react 侧 G8 同批同形):requirements 与第 1 轮报告都把「传值但不传回调」写成一种锁定,react 侧给的判据是「点 +/- 后断言视图不变」。照写会得到一条红用例——本包实测并不冻结视图:展开态的渲染源是 store,
expanded-keys只在创建期与宿主传入值变化时回灌,宿主不回写就没有第二次同步,点击照常折叠(react 侧同样三种写法逐字测过,结论一致,所以这不是复刻偏差而是两仓共同语义)。现按真实语义钉两条:① 只传 prop 不接update:expanded-keys→ 不报错不警告、点击生效;② 接了监听但监听什么都不做 → 监听照样收到新值(收起根节点即[])、视图不被拉回。三条变异验证判据有牙:V1 摘掉 emit 红 ②;V2 在 emit 后以 prop 为准setExpandedKeys拉回(=真冻结)红这两条;V3 去掉 node-key 守卫让锁定态也警告红 ①。V3 一开始是白断的:warn()按文案去重、去重表是模块级的,本文件前面的用例已经把那条文案发掉,不自己resetWarnings()的话not.toHaveBeenCalled()恒真(实测改无条件警告时它照样绿)。单测 258 → 260 条。 - 拖拽悬停的 DOM 写入落点判据(与 react 侧 G9 同批):「拖拽指示只更新相关节点」这句承诺两仓都只是实现、没有断言——把
handleDragEnter改成顺手写dragOverNode、dragend不清指示、或给每个 label 都挂一个「正在拖拽」的类,draggable.spec.ts的 11 条功能用例照样全绿。新增tests/components/local-update.spec.ts(与 react 同名文件对称),用MutationObserver按事件顺序断「被写了 class 的元素」恰好是谁:dragstart 0 处 → 首次悬停 1 处(label:branch-1#drop-inner)→ 同节点同分区重复 dragover 0 处 → 换目标 2 处(旧 label 清空 + 新 label 挂上)→ 同节点换分区 1 处 → dragEnter 0 处 → dragend 1 处。判据落点与 react 不同是机制决定的,不是偷懒:react 每个节点订阅自己的版本号,断「谁被 bump」;本包的dragOverNode是OkrTree.vueprovide 下来的shallowRef,18 个节点的labelWrapperClass都读它,实测换一次目标全部重新求值(计数探针_scratch/probe-recompute-count.mjs),但只有值真变了的 2 个元素被写入 DOM——所以这里钉的是 DOM 写入落点。五条变异逐个跑过、全部只红这条(摘 dragend 的清理 / dragEnter 顺手挂指示 / dragstart 就标成放置目标 / 全员加is-hovered/ 把drop-*从 label 外层挪到节点元素上;驱动_scratch/probe-drag-mutation-vue3.mjs,自带基线绿跑、锚点唯一性自检与 finally 还原)。踩到一条 jsdom 陷阱:MutationObserver的回调按微任务派发,await nextTick()之后直接takeRecords()会在「回调已经把记录取走」与「回调还没跑」之间随机拿到空数组——第一版探针因此把整棵树的 class 写入读成 0 条,看着像「实现本来就局部」的假绿。现收记录前先等一个宏任务边界,回调数组与takeRecords()两边都取。单测 260 → 261 条。 - OKR 根卡片「展开 / 收起不被推位」补几何断言(与 react 侧 G14 同批):
align-root的全部作用就是这一句,此前只有那条 CSS、align-root类名与两张展开态的像素基线,收起这个动作序列本身没有几何断言守着。tests/visual/visual.spec.ts新增一条:对#demo-10第一棵树的根卡片量getBoundingClientRect(),右子树收起 → 重新展开 → 左子树收起 → 重新展开,四次toBeCloseTo(x, 0)+ 两次宽度比对,每步同时断-children/-left-children上is-hidden的确切出现与消失(否则「坐标没变」可能只是按钮没生效)。量的是相对.org-chart-container的水平偏移而不是绝对left:点击时 Playwright 会把目标滚进视口,绝对坐标跟着滚动位置走(react 侧实测同一动作让绝对left从 962 跳到 591),拿绝对坐标只会得到一条与几何无关的红。只加断言、没加截图基线(png 仍是 28 张,视觉用例 18 → 19 条)。本仓配置没有 react 那句reducedMotion: 'reduce',收起仍走 200ms 过渡(animate-duration默认值),故每次切换后等 320ms 再量,避免一条只在机器慢时才红的用例。三条变异改的是playground/dist那份产物 CSS(gitignored,跑完逐字还原),射程按实测记:给.is-hidden追加display:none→ 本条红、两张 OKR 像素基线也红;flex:1 1 0→0 0 auto与根节点width:100%→auto→ 本条与两张基线都绿,是group-align.spec.ts把它们打红的。即这条守的是「收起不推根节点」,不是align-root的静态形状。与姊妹包 react-okr-tree 同批同形。 - 焦点环补计算样式断言(与 react 侧 G13 的不依赖像素那一半同批):环此前只有类名与人眼看图,视觉套件里没有任何计算样式断言守着。新增一条:
page.keyboard.press('Tab')循环走到第一个.org-chart-node(上限 80 次,走不到即判失败——否则后面的环断言全在空转),断该节点卡片本体的计算outlineStyle='solid'/outlineWidth='2px'/ 颜色非透明,并断焦点元素自身outlineStyle='none'。读的元素不是聚焦元素:style.css:686-695把.org-chart-node:focus清成outline: none,真正的环挂在.org-chart-node:focus-visible > .org-chart-node-label > .org-chart-node-label-inner,姊妹...
v1.14.0
1.14.0(2026-09-22)
对外只多一个导出(BUILT_IN_THEMES),其余全是发布前收口的测试与门禁断言。本版本同时是第一个由 CI 通过 OIDC Trusted Publishing 真实发包的版本。
新增
- 导出
BUILT_IN_THEMES:内置主题名清单(default / feishu / dark / auto / minimal / colorful)。此前它只用于themeprop 的校验告警、未从包入口导出,而姊妹包 react-okr-tree 已导出——本次补齐两端导出面对齐。需要枚举主题名的场景(主题选择器、设置面板)不再得硬编码字符串数组。
工程化
verify:dist补 3 条.cjs断言(对应验收表缺口 Q9 / G5):本包是"type": "module",Node 会把.umd.js按 ESM 解析,require()只能走.cjs这份产物,而这条路径此前全靠人工验证、构建端把 exports 条件写坏要到发包后才被发现。现在用createRequire真实加载产物,断言组件导出指向同一实现、BUILT_IN_THEMES为 6 项、createTypedOkrTree/TreeStore可取。shared/api.ts的 Methods 表补getNodeKey与store / root两行,README 随之重新生成。getNodeKey早在defineExpose里,但表上漏记,导致 README、文档站 API 页与 playground 三处同时查不到这个方法——react 侧审计的 §6.3 一度指出过,本包未修。- 新增
tests/api-surface.spec.ts:API 表与defineExpose的双向防漂移(表→实例、实例→表、行数与成员数一致),与 react 侧tests/api-surface.spec.tsx同构。上面的漏记正是这条用例存在的理由。 - 新增
tests/model/framework-boundary.spec.ts:模型层无 Vue 2 残留写法(new Vue(/eventBus/$on$emit$set$children$refs/Vue.prototype),且从vue只引入响应式 API(收口 Q7)。与 react 侧同名文件命名一致但内容不同——react 断的是「模型层不引用框架」,本包的node.ts刻意用了shallowReactive,照搬会写成假测试。 - 过渡相关补三组断言(收口 Q6 / Q8):
transition-robustness.spec.ts用it.each逐个断六种内置animateName的okr-anim-<name>接线(此前只钉okr-fade-in一种),并新增「点击 +/- 收起时子容器切is-hidden而非卸载、再点恢复」的用例;verify:dist的 CSS 侧由「只钉一组」改为六种逐个断enter-active/leave-active。 theme.spec.ts补[vue3-okr-tree]警告前缀断言(此前只断文案,前缀本身零覆盖)。vite.config.ts的test.include加tests/*.spec.ts,使根级api-surface用例被收集。- 门禁数字:单测 226 → 242(21 → 23 个文件),覆盖率 90.85 / 81.81 / 88.94 / 93.56,
verify:dist实跑 34 条ok。 - 新增
docs/acceptance.md验收证据表,结构与 react-okr-tree 的同名文档对齐:§7 五条验收项与 Q1–Q9 逐条给到文件:行 + 用例名,并记入与 react 侧的 props / 导出面双向差集实测结果。 docs/requirements.md与docs/development-plan.md的 Demo 用例数由 20 更正为 24(playground/components/demos/实际文件数)。
v1.13.0
1.13.0
新增
unstyledprop(2.x #13):去掉节点卡片的外观(背景 / 边框 / 圆角 / 阴影,含:hover态),只保留布局与连接线,供 Tailwind 或自有设计系统接管——此前唯一的退路是覆写全部--okr-node-*变量,而主题类会重新填回这些值。内边距、字号与文字色刻意不动:改padding会移动节点盒并牵动连接线的伪元素几何,需要调整请继续用--okr-node-*变量或label-class-name。- 实现上是一个
okr-unstyled容器类 + 一小段中和规则;选择器需要 5 个类才能压过方向专属规则(.org-chart-container .vertical .org-chart-node-label .org-chart-node-label-inner同为特异度且位于其后,同特异度时后者胜)。Visual 套件补了一条计算样式断言同时校验「阴影被清掉」与「节点盒尺寸不变」——写第一版时正因漏了.org-chart-node一层而被该断言当场抓到。 - 样式 gzip 3.83 → 3.88 kB(预算 4 kB,余量约 3%)。
- 实现上是一个
变更
html-to-image以可选 peerDependency 声明(^1.11.0+peerDependenciesMeta.optional):exportImage的这项外部依赖此前只写在文档和运行时报错里,包管理器侧没有任何声明可循;仍不进dependencies,用不到导出能力的消费者不会被拖进来。- 视觉门禁端口可覆盖(
OKR_VISUAL_PORT,默认仍是 4173):Windows 的 TCP 排除端口区间(netsh int ipv4 show excludedportrange protocol=tcp)会把 4173 整段保留,vite preview直接 EACCES,此前在这类机器上无法本地复现视觉门禁。