PropertyGrid 属性表
通用布局能力:
RenderPropertyGrid实例统一支持width、height、min/max、margin和槽位对齐;构造器是否接收这些字段以参数表为准。详见组件通用布局属性。
RenderPropertyGrid 用于展示和编辑对象属性。它适合打印设计器、报表设计器、控件检查器、配置面板等场景:左侧是属性名,右侧是当前值,并按 group 分组显示。
属性表本身不反射业务对象,也不直接保存业务模型。推荐做法是由业务层提供 PropertyGridRow[],或者使用轻量 schema API 把对象状态转换成 rows。提交时仍由业务层通过 onValueCommit 决定是否接受变更。
API 总览
import { RenderPropertyGrid, buildPropertyGridRows, definePropertyGridSchema, type PropertyGridField, type PropertyGridRow, type PropertyGridSchema,} from 'ds-ui'
| API | 类型 | 用途 |
|---|---|---|
RenderPropertyGrid |
class | 属性表控件,负责分组绘制、滚动、选中、键盘导航和编辑请求。 |
PropertyGridRow |
type | 单个属性行。 |
PropertyGridEditorKind |
type | 编辑器类型,例如 text、number、boolean、enum、color、date、image-source。 |
buildPropertyGridRows() |
function | 根据 schema 和上下文生成 PropertyGridRow[]。 |
definePropertyGridSchema() |
function | 定义属性 schema,主要用于保留泛型上下文。 |
PropertyGridSchema / PropertyGridField |
type | 属性描述模型。 |
何时使用
- 设计器右侧属性面板。
- 对象检查器、调试面板、配置面板。
- 需要按分组展示大量键值属性。
- 需要 enum、boolean、color、date 等不同编辑入口。
- 需要键盘上下移动选中行,并通过 Enter 或 F2 打开编辑。
不适合:
最小示例
const grid = new RenderPropertyGrid({ rows: [ { group: 'Text', name: 'Text', value: '姓名', editable: true, editor: 'text', propPath: 'object.text' }, { group: 'Text', name: 'Color', value: '#111827', editable: true, editor: 'color', propPath: 'object.color' }, { group: 'Text', name: 'Wrap', value: 'true', editable: true, editor: 'boolean', propPath: 'object.wrap' }, ], onEditRequested: (row, context) => { // 打开文本、颜色、日期或枚举弹窗,并通过 context.commit() 提交。 }, onValueCommit: (row, nextValue) => { // 更新业务对象;返回 false 可拒绝提交。 return true },})
nameColumnWidth 用于设置属性名列的初始宽度,也可以在运行时通过同名属性读写。用户可直接拖动属性名与属性值之间的分隔线调整宽度;组件会为两侧保留最小可用宽度。
Schema 生成 rows
schema 是一个轻量适配层。它不要求业务对象有 attribute,也不做运行时反射;每个字段显式声明如何从当前上下文取值、显示、编辑和分组。
interface TextObjectContext { object: { text: string color?: string wrap?: boolean }} const textObjectSchema = definePropertyGridSchema<TextObjectContext>([ { group: 'Text', name: 'Text', propPath: 'object.text', value: (context: TextObjectContext) => context.object.text, editor: 'text', editable: true, }, { group: 'Text', name: 'Color', propPath: 'object.color', value: (context: TextObjectContext) => context.object.color ?? '#111827', editor: 'color', editable: true, }, { group: 'Text', name: 'Wrap', propPath: 'object.wrap', value: (context: TextObjectContext) => context.object.wrap !== false, editor: 'boolean', editable: true, },]) const object = { text: '姓名', color: '#111827', wrap: true,} const rows = buildPropertyGridRows({ object }, textObjectSchema)
PropertyGridField 常用字段:
| 字段 | 说明 |
|---|---|
group / name |
分组和属性名。 |
id |
稳定行 id。存在同名属性时建议提供。 |
propPath |
业务属性路径,提交、定位、错误映射常用。 |
value |
静态值或 (context) => value。 |
format |
把原始值格式化为显示文本,例如 210 mm。 |
editor |
指定编辑器类型。 |
editable |
是否可编辑,可根据上下文动态计算。 |
enumItems |
enum 编辑器的候选项。 |
visibleWhen |
条件显示字段。 |
description / status / errorText |
辅助说明和状态提示。 |
unit / step |
数字编辑的单位和步进元数据。 |
交互行为
- 单击属性行会选中当前行。
- 拖动属性名列与属性值列之间的分隔线可调整两列宽度。
- 属性名或属性值被截断时,悬浮会显示完整文本;未截断的内容不会显示冗余提示。
- 单击 value 单元格会请求编辑可编辑文本、数字、枚举、颜色、日期等字段。
- boolean 行可通过鼠标或 Space 直接切换。
- ArrowUp 和 ArrowDown 在属性行之间移动选中项。
- Enter 或 F2 对当前选中可编辑行发起编辑。
rowRectById()可用稳定 id 获取弹窗锚点,避免同组同名属性定位错误。
属性说明面板
设置 showDescriptionPanel: true 后,PropertyGrid 会在底部显示当前选中属性的名称、description 和状态信息。鼠标点击属性或通过 ArrowUp、ArrowDown 切换属性时,说明同步更新;悬浮只负责属性名和值的截断提示,不会改变说明内容。
const grid = new RenderPropertyGrid({ showDescriptionPanel: true, descriptionEmptyText: '选择属性查看用途和取值规则。', selectedRowId: 'maximum-decimals', rows: [{ id: 'maximum-decimals', group: 'Format', name: 'Maximum Decimals', value: '2', description: '格式化结果最多保留的小数位数。超过的部分会按照当前数值格式执行四舍五入。', }],})
说明面板按可用宽度对标题、说明和 errorText 完整换行,并根据实际行数自动调整高度,不使用省略号或依赖 Tooltip。PropertyGrid 高度不足时会优先保留可操作的属性列表区域;只有完整说明确实无法同时放下时,说明正文才启用独立滚动。
属性列表与说明面板之间的横向分隔条支持拖动:
- 默认使用自动高度。
- 拖动分隔条后进入当前实例的手动高度模式。
- 双击分隔条或调用
resetDescriptionPanelHeight()恢复自动高度。 - 宽度、选中属性或 rows 变化后,自动模式会重新测量完整说明。
showDescriptionPanel 默认为 false,因此现有 PropertyGrid 不会因为升级而改变布局。属性密集、业务含义复杂的设计器和检查器应显式开启。
编辑器协议
PropertyGrid 不依赖 JS attribute,也不反射业务对象。编辑入口由每一行的 editor 字段声明,再通过 editorHandlers、内建编辑器或 onEditRequested 执行。
const grid = new RenderPropertyGrid({ rows: [ { group: 'Image', name: 'Source', value: '', editable: true, editor: 'image-source', propPath: 'object.source' }, ], editorHandlers: { 'image-source': context => { // 可覆盖内建图片选择行为,例如接入资源库。 context.commit('data:image/png;base64,...') }, }, onEditRequested: (row, context) => { // 处理 text、number、color、date、expression 等宿主自定义弹窗。 context.commit(row.value) }, onEditError: (row, message) => { // 把编辑错误映射回业务状态或属性行错误。 },})
编辑处理优先级:
editorHandlers[editor]:开发者显式接管某类编辑器。- 内建编辑器:当前内建
image-source,会打开本地图片选择并读取为 base64 image data URI。 onEditRequested(row, context):宿主打开自己的弹窗或编辑器。
编辑处理函数是同步接管协议:返回 false 表示当前处理器不处理,PropertyGrid 会继续尝试后续处理器;返回 true 或不返回值表示已经接管。需要异步工作的编辑器,例如文件选择、资源库弹窗或外部对话框,应在处理器内部启动异步流程,并在完成后调用 context.commit() 或 context.setError()。
PropertyGridEditContext 提供:
| 字段 | 说明 |
|---|---|
row / editor |
当前属性行和编辑器类型。 |
commit(nextValue) |
提交值,会继续走 onValueCommit 校验。 |
updateDisplayValue(value) |
只更新属性表显示值,不提交业务对象。 |
setError(message) |
通知宿主编辑失败。 |
image-source 的内建行为只负责本地文件选择和 FileReader.readAsDataURL() 转换。它提交的是 data:image/...;base64,...;业务层仍应在 onValueCommit 中决定是否接受该值。远程 URL 不属于这个内建编辑器的默认契约。
设计边界
RenderPropertyGrid 只处理通用交互和展示。业务应该负责:
- 根据当前对象生成 rows。
- 根据
propPath或id提交变更。 - 打开具体编辑器弹窗。
- 校验输入并把错误映射回属性行。
这种边界让属性表可以同时服务打印设计器、医疗编辑器和普通业务配置面板,而不需要引入语言级 attribute 或业务反射模型。