rxdb-model
框架无关的 RxDB 实体模型核心库:从实体元数据(EntityMetadata)派生表单、详情页、可编辑表格与可视化查询构建器所需的全部纯逻辑,并配套 VTable 表格运行时基础设施。
定位与边界
- 唯一输入源:
@aiao/rxdb的实体装饰器体系产出的EntityMetadata。 - 框架无关:本包不含任何 Angular / React / Vue 依赖,可被任意前端技术栈消费;三框架 UI 组件层见
@aiao/rxdb-model-angular(React / Vue 版本陆续提供)。 - 与
@aiao/rxdb的边界:核心包的entity-field.utils(Field Descriptor 字段语义元数据)与entity-value.utils(parseEntityFieldValue/formatEntityFieldValue/validateFieldValue等)服务于核心内部与插件契约;本包的同名模块面向「实体管理界面」场景(表单字段提取、字段值解析/格式化/校验、表格列构建),语义不同、请勿混用。 - 表格引擎:
@visactor/vtable与@visactor/vtable-editors为 peer 依赖(~1.26.x),由消费方提供。
模块一览
| 目录 | 职责 |
|---|---|
根目录 entity-field.utils / entity-value.utils / structural-equal | 字段配置提取、字段值解析/格式化/校验、键序无关的结构化深比较 |
entity-form/ | 元数据 → 表单字段配置(create/edit/view 三模式)、实体与表单数据双向转换(仅变化字段)、表单校验 |
entity-detail/ | 元数据 → 详情页 Tab 结构(基础表单 + 每关系一个表格 Tab) |
entity-table/ | 可编辑表格列构建、15 个 VTable 单元格编辑器、剪贴板 / 键盘 / 主题 / tooltip / 拖拽重排等运行时基础设施 |
query-builder/ | 可视化查询构建器引擎:状态服务、操作符注册表、查询转换、schema 解析、即时校验、字段树等工具 |
使用
import { buildFormFields, buildEditableColumns, buildDetailTabs } from '@aiao/rxdb-model';
const fields = buildFormFields(metadata, 'create');
const columns = buildEditableColumns(metadata);
const tabs = buildDetailTabs(metadata);
查询构建器(框架无关服务层):
import { createQueryBuilderService } from '@aiao/rxdb-model';
const qb = createQueryBuilderService<MyEntity>({ schema: { fields } });
qb.state$.subscribe(state => console.log(state));
qb.addRule({ field: 'title', operator: 'contains', value: 'a' });
const rxdbQuery = qb.toRxDBQuery();
开发
pnpm nx test rxdb-model # Vitest(node 默认环境,DOM 用例经文件头 pragma 切 happy-dom)
pnpm nx build rxdb-model # Vite 库构建(ES 单格式 + dts)
RxDB Model - 核心实体和数据模型定义 提供实体字段处理、实体值处理、结构相等性判断等基础功能
Classes
| Class | Description |
|---|---|
| CellTooltipManager | 单元格错误 tooltip 生命周期管理 |
| DateEditor | 日期时间选择编辑器 |
| EnumEditor | 枚举下拉选择编辑器 |
| GlobalOverlayEditor | 全局浮层列表编辑器抽象基类 |
| 支持图标 + 文字的单选列表编辑器 | |
| JsonEditor | JSON / KeyValue 编辑器 |
| KeyValueEditor | KeyValue(Record<string, unknown>)editor — Schema mode only |
| MultiSelectEditor | VTable 自定义多选编辑器 |
| NumberEditor | 数字输入编辑器 |
| OperatorRegistry | 操作符注册表服务 管理可用操作符及其元数据 |
| QueryBuilderService | QueryBuilder 核心服务 基于 RxJS 的响应式状态管理 |
| QueryConverter | Query 转换服务 负责 QueryBuilder 状态与 RxDB Query 之间的双向转换 |
| RelationEditor | 关系字段搜索下拉编辑器(oneToOne / manyToOne) |
| SafeListEditor | Workaround for VTable ListEditor bug: onEnd throws TypeError when element.parentNode is null during keyboard-triggered cell navigation. |
| SchemaParser | Schema 解析服务 负责将 RxDB EntityMetadata 转换为 QueryBuilder 可用的字段元数据 |
| TableClipboardManager | 内部剪贴板管理器 — 封装系统剪贴板 API 交互 + 内部备份 |
| TagsEditor | 标签(数组)输入编辑器 |
| ValidationService | 校验服务 负责验证 QueryBuilder 规则和值 |
Interfaces
Type Aliases
| Type Alias | Description |
|---|---|
| ClipboardContent | 剪贴板内容:行优先二维数组 [rowOffset][colOffset] |
| DetailTab | 详情页标签配置 |
| DetailTabType | 详情页标签类型 |
| EntityFieldType | 实体字段类型 |
| EntityInstance | RxDB 实体实例(用于 CRUD 操作)。 |
| FilterQuery | 筛选查询结构(与用户筛选条件做 AND 合并)。 |
| FormMode | 表单模式 |
| IconData | Lucide 图标数据 — [tagName, attributes] 元组数组 |
| ManualOrderListMetadata | 列表排序判定需要的实体元数据 |
| OperatorName | 所有操作符 |
| PropertyType | 属性类型 |
| PropertyTypeString | 实体属性类型字符串字面量 与 @aiao/rxdb 的 PropertyType 枚举值一一对应,另扩展关系类型与计算属性 |
| QueryBuilderOperator | 操作符类型(直接复用 RxDB 定义) |
| QueryBuilderRule | 查询规则(复用 RxDB Rule 类型 + UI 元数据) |
| QueryTableHandle | 查询表格的命令面:成员与 EntityTableHandle 相同,全部委托给内部实体表格。 |
| RecordDeletePredicate | 判定某一行能否删除;actionsColumn 逐行调用 |
| RelatedItemsProviderFactory | 关联实体候选 provider 工厂:根据外键字段和关联实体名返回 provider 函数 |
| Rule | 查询规则 |
| RuleGroup | 查询规则组(自动计算类型) |
| SwitchDisableCallback | switch 禁用状态判断回调 |
| TagsMode | 标签编辑器模式:'string' 字符串标签 / 'number' 数字标签 |
| ValidationGroupInput | 校验规则组输入(宽松递归结构) |
Variables
| Variable | Description |
|---|---|
| BUILTIN_VALIDATION_RULES | 内置验证规则 |
| DAISY_COLORS | daisyUI v5 主题色引用(CSS 变量,主题自适应) |
| DEFAULT_COLUMN_SIZES | 各属性类型的默认列宽及最小/最大宽度 |
| DEFAULT_OPERATORS | 默认操作符定义列表 |
| DELETE_COLOR | 删除图标默认颜色 |
| disabledEditorForReadonly | 不可编辑的行:禁用 editor |
| DISPLAY_ONLY_FIELDS | 仅展示(不可编辑)的系统字段 |
| ROW_SERIES_COL_OFFSET | VTable rowSeriesNumber 列占据 col=0,数据列从 col=1 开始。 将 VTable 列索引映射到 columns 数组索引时需减此偏移。 |
| SKIP_FIELDS | 跳过(不显示为独立列)的系统字段 |
| switchDisabledForReadonly | 禁用 switch:只读行不可操作 |
| UUID_RE | UUID 字符串格式校验正则(不区分大小写) |
Functions
| Function | Description |
|---|---|
| _resetEditorRegistry | 重置编辑器注册状态(仅供测试使用) |
| actionsColumn | 操作列:可选包含查看(view)和删除按钮 |
| applyClipboard | 从内部剪贴板收集待写入操作(按列位置匹配,类 Excel 行为)。 cellType 必须一致;行/列数不足时循环复用。 若目标列定义了 menuList,则跳过不在枚举范围内的值。 |
| applyPopoverPosition | 将 calcPopoverPosition 的结果直接应用到弹出层 DOM 元素。 |
| applySystemText | 从系统剪贴板文本(TSV)收集待写入操作。 布尔列自动将文本转为布尔值(true/1/yes/是 → true,false/0/no/否/空 → false), 无法识别的值跳过;枚举列跳过不在 menuList 中的值;行/列数不足时循环复用。 |
| buildDetailTabs | 根据实体元数据构建详情页标签列表 |
| buildEditableColumns | 根据 EntityMetadata 构建可编辑的 VTable 列定义 |
| buildFieldTree | 将扁平字段列表转换为树结构 |
| buildFormFields | 根据实体元数据和表单模式构建表单字段列表 |
| buildPropertyColumn | 根据属性类型配置构建 VTable ColumnDefine |
| buildTableOptions | 构建 ListTable 初始化选项 |
| calcPopoverPosition | 计算弹出层相对于触发元素的位置,优先下方展示,空间不足时向上。 |
| canReorderEntityList | 实体列表当前是否允许拖拽排序 |
| changeCellValue | 回写单元格值(不触发编辑事件) |
| cn | 拼接 CSS 类名(clsx 的薄封装)。 |
| collectDeleteWrites | 从选区收集 Delete/Backspace 需要清空的单元格写入列表。 |
| collectReorderedIds | 收集拖拽排序后的非只读行 ID 列表 |
| commitRowMove | 协调一次行拖放的落库 |
| completeTableEdit | 完成当前单元格编辑 |
| computeTooltipPosition | 计算 tooltip 在页面上的绝对位置 |
| createDefaultFormData | 根据字段配置创建默认表单数据 |
| createFieldNodeMap | 创建字段名到节点的映射 |
| createListTable | 创建 VTable ListTable 实例 |
| createOperatorRegistry | 创建 OperatorRegistry 实例的工厂函数 |
| createQueryBuilderService | 创建 QueryBuilderService 实例的工厂函数 |
| createQueryConverter | 创建 QueryConverter 实例的工厂函数 |
| createSchemaFromEntities | 从多个 EntityMetadata 批量创建字段元数据 |
| createSchemaFromEntity | 从 EntityMetadata 创建字段元数据 |
| createSchemaParser | 创建 SchemaParser 实例的工厂函数 |
| createTheme | 基于 daisyUI CSS 变量构建 VTable 主题 |
| createValidationService | 创建 ValidationService 实例的工厂函数 |
| defaultListOrderBy | 列表未按列排序时的游标排序 |
| deriveEntityCapabilities | 由实体元数据派生界面能力 |
| ensureBaseEditorRegistered | 注册 VTable 基础编辑器(全局单例,重复调用仅注册一次) |
| entityToFormData | 将实体对象转换为表单数据 |
| extractEntityFields | 从实体元数据中提取全部业务字段配置 |
| extractFieldsFromMetadata | 从 EntityMetadata 提取字段(支持递归关系) |
| extractKeyValueFields | 从 keyValue 属性的 properties 提取子字段列表 |
| extractRelationTargetFields | 提取关系目标实体的字段(用于 EXISTS 子查询) |
| extractSystemFields | 从实体元数据中提取系统字段配置 |
| filterVisibleFields | 过滤掉标记为隐藏的字段 |
| findNodeByValue | 在树中查找节点 |
| findOptionLabel | 在弹出层选项列表中查找匹配项的 label。 |
| flattenFieldTree | 将字段树展平为选项列表 |
| formatAsTsv | 将 ClipboardContent 序列化为 TSV 字符串 |
| formatDateToYmd | 将日期格式化为 YYYY-MM-DD 字符串。 |
| formatDateValue | 格式化日期值为本地字符串 |
| formatEntityFieldValue | 将字段值格式化为展示用字符串 |
| formDataToEntityChanges | 计算表单数据相对原实体对象的变更集 |
| generateId | 生成唯一 ID 用于 QueryBuilder 规则和规则组的标识 |
| getCellRecord | 通过 VTable 回调参数安全获取当前行记录 |
| getCSSVariables | 从 CSS 自定义属性读取 daisyUI 主题色 |
| getCSSVariableValue | 读取单个 CSS 变量值,为空时回退到默认值 |
| getDefaultColors | 返回默认颜色集合 |
| getDefaultOperatorRegistry | 获取默认操作符注册表 |
| getDefaultValueForType | 返回指定输入控件类型的默认值。 |
| getDisplayNamePath | 获取完整的 displayName 路径 |
| getDocumentRootStyle | 获取文档根元素的计算样式 |
| getInputType | 根据字段类型和运算符确定查询条件值的输入控件类型。 |
| getOperatorsForType | 根据字段类型获取适用的操作符 |
| getOptionGroupChildren | 获取 CascadeSelect 的 optionGroupChildren 配置 |
| getRelationIgnoreKeys | 获取关系需要忽略的键(避免循环引用) |
| getTableContainer | 获取表格 DOM 容器元素 |
| handleTableKeydown | 表格容器 keydown 事件处理 — 剪贴板快捷键 + 布尔切换 + 删除。 编辑状态下不拦截剪贴板快捷键与删除键。 |
| isDocumentDarkMode | 判断当前文档是否处于暗色模式 |
| isReadonly | 检查记录是否为只读行 |
| isReadonlyOrAddRow | 检查记录是否为只读行或新增模板行 |
| isRowDragEnabled | 表格当前是否允许行拖动 |
| isRule | 类型守卫:判断是否为规则 |
| isRuleGroup | 类型守卫:判断是否为规则组 |
| isTableEditing | 检查表格是否正在编辑中 |
| lucideToSvgElement | 将 LucideIconData 转为内联 SVG 元素(DOM API 构造,免疫标记注入) |
| makeDeleteIcons | 生成删除按钮的图标选项(SVG 图标 + 文字标签) |
| makeEditorForReadonly | 构建编辑器函数:非只读行使用指定编辑器名称,只读行返回 undefined |
| makeViewIcons | 生成查看按钮的图标选项(仅 SVG,避免 text 撑满剩余宽度) |
| mapPropertyType | 将 PropertyType 映射到 FieldMetadata 的 type |
| normalizeKVSchemaEntryType | 将实体属性类型归一化为 keyValue Schema 值类型 |
| organizeFields | 对字段进行分组和排序 |
| parseCommaSeparatedInput | 将逗号分隔的字符串解析为数组。 当 fieldType 为 'number' 时返回数字数组,否则返回字符串数组。 |
| parseEntityFieldValue | 将原始值解析为指定字段类型的目标值 |
| parseEntityFieldValueStrict | 严格解析表单输入;失败时返回结构化原因,调用方不得覆盖原始输入。 |
| parsePropertyColumnValue | 将 input-editor 输入的字符串值解析回属性原始类型 |
| parseYmdToLocalDate | 将 YYYY-MM-DD 字符串解析为本地时区的 Date。 |
| patchDragIconForReadonlyRows | HACK: VTable 不提供禁用特定行拖拽的 API,通过 monkey-patch 内部属性实现 |
| pinsSingleOrderDomain | 固定查询是否恰好钉住一条完整的排序域 |
| positionOverlayPanel | 将浮层面板定位到单元格下方(空间不足时翻转到上方) |
| readRowMove | 从一次行拖放读出被拖行与落点邻居 |
| refocusTable | 编辑结束后将焦点还给 VTable root element(tabindex=0 的 .vtable div = ctx.container = table.getElement()),使方向键导航继续工作 |
| registerEditor | 注册自定义编辑器 |
| restoreTableRecords | 把表格行恢复成最近一次提交的顺序 |
| scheduleEditorSetup | 延迟执行编辑器初始化(注册外部点击监听 + 聚焦),避免 VTable 事件干扰 |
| setCellSwitchState | 设置 switch/checkbox/radio 单元格视觉状态 |
| setRowDragEnabled | 运行时开关表格的行拖动手柄 |
| snapshotSelection | 将当前 VTable 选区快照为剪贴板内容。 |
| sortFormFields | 按字段的 order 升序排序 |
| structuralEqual | 结构化深比较,替代 JSON.stringify 比较。 递归比较对象和数组,不依赖 key 序列化顺序。 支持循环引用检测,避免无限递归。 |
| syncHeaderSortIcon | 同步列头排序图标,不执行客户端排序 |
| updateTableRecords | 更新表格数据并恢复列宽(按 field 名匹配,容忍列增删) |
| validateEntityFieldValue | 校验单个字段值是否满足字段配置约束 |
| validateField | 校验单个表单字段值 |
| validateForm | 校验整份表单数据 |