DirectSurface UIDirectSurface UI
开始使用
文档/组件手册

ReviewSidebar 审阅侧栏

通用布局能力RenderReviewSidebar 实例统一支持 widthheight、min/max、margin 和槽位对齐;构造器是否接收这些字段以参数表为准。详见组件通用布局属性

LIVE CANVAS可运行组件示例
ON-DEMAND RUNTIME交互式示例将在进入视区时启动避免文档首屏同时初始化多个 Canvas Runtime

RenderReviewSidebar 是右侧审阅、批注、修订记录和诊断提示列表。它根据每个 item 的 anchorXanchorY 绘制连接线,把文档、编辑器或主内容区域中的位置连接到侧栏卡片。

它只负责审阅卡片的布局、滚动、激活、展开、编辑输入和操作按钮。审阅数据、权限、保存、定位主内容和变更合并应由业务页面或编辑器控制器维护。

API 总览

import {  RenderReviewSidebar,  type ReviewSidebarAction,  type ReviewSidebarItem,  type ReviewSidebarItemType,} from 'ds-ui'
API 类型 用途
RenderReviewSidebar class 审阅侧栏,按 anchor 位置排列卡片、绘制连接线并管理内部滚动。
ReviewSidebarItem type 单条批注、修订或提示卡片的数据模型。
ReviewSidebarAction type 卡片底部操作按钮配置。
ReviewSidebarItemType type item 类型,当前为 'comment' | 'track'

最小装配顺序是:业务根据主内容位置生成 ReviewSidebarItem[],创建 RenderReviewSidebar({ width, items }),在主内容布局变化后重新计算 anchorX/anchorY 并调用 setItems(items)

何时使用

  • 文档编辑器、病历编辑器、审批页面需要在右侧展示批注和修订。
  • 诊断或审计页面需要把主内容中的位置和侧边卡片连接起来。
  • 卡片数量中等,且每条记录有标题、正文、可选详情和操作按钮。

不要使用:

最小示例

const reviewItems: ReviewSidebarItem[] = [  {    key: 'comment-1',    type: 'comment',    title: '张医生',    meta: '10:24',    content: '建议补充过敏史。',    anchorX: -18,    anchorY: 96,    color: '#2563eb',    active: true,    onActivate: () => {      state.activeReviewKey = 'comment-1'    },  },] const reviewSidebar = new RenderReviewSidebar({  width: 280,  items: reviewItems,})

构造参数

参数 类型 默认值 说明
width number 250 侧栏期望宽度。布局时仍会被父约束限制。
items ReviewSidebarItem[] [] 初始审阅项。组件会按 anchorY 从小到大排序。

构造后可以继续调用 setItems() 更新数据。不要直接修改 items 数组后期待自动刷新。

ReviewSidebarItem

字段 类型 必填 说明
key string 稳定唯一键。用于复用卡片实例、识别 active 项和 debug state。
type 'comment' | 'track' 审阅类型。当前用于语义区分,视觉主要由其他字段决定。
title string 卡片标题。
meta string 标题右侧或下方的补充信息,例如时间、作者、状态。
content string 卡片正文。editable: true 时作为 TextArea 初始值。
reason string 正文下方的原因或说明。
badgeLabel string 标题区域的短徽标。
badgeColor string 徽标颜色。
anchorX / anchorY number 主内容中的连接点全局或同坐标系位置。
connector boolean 是否绘制连接线。默认绘制。
color string 卡片边框、左侧色条和连接线颜色。
editable boolean 是否把正文渲染成可编辑 TextArea。
onCommitText (value: string) => void 编辑提交回调。
actions ReviewSidebarAction[] 卡片底部操作按钮。
active boolean 是否当前激活。激活项会高亮并在变化后自动滚入可见区域。
onActivate () => void 点击非交互区域时触发。
expanded boolean 是否展示详情和审计记录。
onToggleExpanded () => void 点击展开按钮时触发。业务应更新 expanded 后重新 setItems()
detailLines string[] 展开后显示的详情文本。
auditTrail { key: string; label: string; meta?: string; detail?: string }[] 展开后显示的审计记录。

anchorX/anchorY 应在主内容完成布局后计算。如果主内容滚动或缩放,业务需要重新计算 item 并调用 setItems(),否则连接线会指向旧位置。

ReviewSidebarAction

字段 类型 必填 说明
key string 稳定 action 键。
label string 按钮文本。
disabled boolean 是否禁用按钮。
variant 'default' | 'primary' | 'danger' | 'text' | 'link' 按钮视觉类型。
onClick () => void 点击按钮回调。

操作按钮由侧栏内部创建为 RenderButton。权限和可见性由业务决定:没有权限时传 disabled: true 或不要放入对应 action。

属性和方法

API 类型 / 返回值 说明
items ReviewSidebarItem[] 当前已排序 item 数组。只读使用,不要直接修改。
setItems(items) void 替换审阅项、复用同 key 卡片、释放被移除卡片并触发布局。
setWidth(width) void 设置期望宽度。内部最小限制为 160
debugState() object 返回滚动位置、内容高度、视口高度和每个卡片的 offset/height。

没有公开的 scrollTo() 方法。需要定位某条记录时,把对应 item 标记为 active: true 后调用 setItems(),组件会在下一次布局中尝试滚入可见区域。

交互行为

操作 行为
滚轮 命中侧栏时按固定步长滚动内部卡片列表。
点击卡片非交互区域 调用该 item 的 onActivate()
点击展开按钮 调用 onToggleExpanded();展开状态由业务回写。
编辑正文 editable: true 时使用内置 RenderTextArea
提交编辑 TextArea commit 时调用 onCommitText(value)
点击 action 调用对应 ReviewSidebarAction.onClick()

侧栏自身不声明键盘焦点模型。键盘输入来自内部 TextArea 和按钮;卡片激活通常由主内容选中、点击卡片或业务命令驱动。

布局和滚动

  • item 会按 anchorY 排序,不按传入数组顺序绘制。
  • 侧栏宽度优先使用 width,但不能突破父约束。
  • 父高度无界时,默认使用内容高度;父高度有界时形成内部滚动视口。
  • 每张卡片的自然 y 会尽量靠近 anchorY - 8,同时避免和上一张卡片重叠。
  • 连接线从 anchorX/anchorY 连到卡片左边缘;connector: false 可以关闭单项连接线。
  • active 项变化后会尝试滚入视口,已有 active 项更新内容时会尽量保持它在视口中的相对位置。

生命周期

setItems() 会释放不再存在的卡片子树。侧栏 dispose() 时会随父类释放所有内部卡片、TextArea、按钮和滚动容器。

业务应避免在 ReviewSidebarItem 回调中持有已经关闭页面的大对象。页面、tab、窗口释放时,如果 item 的回调引用了页面状态,应一起释放侧栏或清空 items。

性能边界

ReviewSidebar 不做虚拟化。它适合几十到低百级审阅项;更多数据应先在业务层分页、分组或按可视区域筛选。

每次 setItems() 会计算签名、排序并同步卡片结构。高频滚动主内容时不要每一帧全量创建新对象;应只在 anchor 或审阅状态变化时更新,或在编辑器控制器里做节流。

业务组合示例

function buildReviewItems(records: Array<{ id: string; y: number; text: string }>): ReviewSidebarItem[] {  return records.map(record => ({    key: record.id,    type: 'comment',    title: '审阅意见',    content: record.text,    anchorX: -16,    anchorY: record.y,    color: '#2563eb',    actions: [      {        key: 'resolve',        label: '已处理',        variant: 'primary',        onClick: () => {          state.resolvedReviewId = record.id        },      },    ],  }))} const sidebar = new RenderReviewSidebar({ width: 280 })sidebar.setItems(buildReviewItems(data))

相关文档