NocoBase 数据源元数据配置指南
概述
NocoBase 使用 PostgreSQL (rgb schema) 存储 collection 和 field 的元数据。当通过 db2cm(Database to Collection Map)方式从已有数据库同步表结构时,生成的元数据往往不完整且组件名错误,需要手动修正。
本文档记录了正确的元数据格式、常见问题及修复方法。
1. 环境信息
| 项目 | 值 |
|---|---|
| NocoBase 版本 | 2.0.16 |
| PostgreSQL | Supabase (supabase-db 容器) |
| PG Schema | rgb |
| 元数据表 | rgb.collections、rgb.fields、rgb."uiSchemas" |
| 连接方式 | docker exec supabase-db psql -U supabase_admin -d postgres |
2. Collection 配置 (rgb.collections)
2.1 正确格式(以 player_deposit 为参考)
{
"schema": "rgb",
"tableName": "player_deposit",
"timestamps": false,
"autoGenId": false,
"filterTargetKey": "guid",
"underscored": false
}
2.2 db2cm 同步的错误格式(修复前)
{
"from": "db2cm",
"autoGenId": false,
"createdAt": false,
"createdBy": false,
"updatedAt": false,
"updatedBy": false,
"timestamps": false
}
2.3 关键字段说明
| 字段 | 必须 | 说明 |
|---|---|---|
schema | ✅ | 数据库 schema 名称,固定 "rgb" |
tableName | ✅ | 数据库实际表名,通常与 collection name 一致 |
autoGenId | ✅ | 业务表必须为 false(使用自定义主键 guid) |
filterTargetKey | ✅ | 记录标识字段,业务表统一为 "guid"。缺失会导致关联查询失败 |
timestamps | ✅ | 是否自动管理时间戳,业务表为 false |
underscored | ✅ | 字段名是否使用下划线风格,设为 false。RGB 的 MySQL 表/列本身已是 snake_case,设 false 告诉 NocoBase 不要再做二次命名转换 |
from | - | "db2cm" 表示通过数据库同步创建,可保留 |
2.4 批量修复 SQL
-- 1. 为所有 db2cm 表补充 schema/tableName/underscored
UPDATE rgb.collections
SET options = options::jsonb
|| jsonb_build_object('schema', 'rgb')
|| jsonb_build_object('tableName', name)
|| jsonb_build_object('underscored', false)
WHERE options->>'from' = 'db2cm'
AND options->>'schema' IS NULL;
-- 2. 为所有 db2cm 同步的业务表补充 filterTargetKey
UPDATE rgb.collections
SET options = jsonb_set(options::jsonb, '{filterTargetKey}', '"guid"')
WHERE (options->>'autoGenId')::text = 'false'
AND options->>'filterTargetKey' IS NULL
AND options->>'from' = 'db2cm';
3. Field 配置 (rgb.fields)
3.1 正确格式对照表
以下是 NocoBase 各数据类型的正确 interface 和 x-component 配置:
| DB 类型 (type) | interface | x-component | x-component-props |
|---|---|---|---|
string | input | Input | {"style":{"width":"100%"}} |
integer | integer | InputNumber | {"stringMode":true,"step":"1"} |
bigInt | integer | InputNumber | {"stringMode":true,"step":"1"} |
decimal | number | InputNumber | {"stringMode":true,"step":"1"} |
float | number | InputNumber | {"stringMode":true,"step":"1"} |
boolean | checkbox | Checkbox | - |
datetimeNoTz | datetimeNoTz | DatePicker | {"showTime":false,"utc":false} |
datetime | datetime | DatePicker | {"showTime":true} |
json | json | Input.JSON | - |
text | textarea | Input.TextArea | - |
belongsTo | m2o | AssociationField | {"fieldNames":{"value":"guid","label":"..."}} |
password | password | Password | - |
date | dateOnly | DatePicker | {"showTime":false,"utc":false} |
url | url | Input.URL | {"style":{"width":"100%"}} |
email | email | Input | {"style":{"width":"100%"}} |
phone | phone | Input | {"style":{"width":"100%"}} |
percent | percent | InputNumber | {"stringMode":true,"step":"1"} |
unixTimestamp | unixTimestamp | DatePicker | {"showTime":true,"utc":false} |
multipleSelect | multipleSelect | Select | {"mode":"multiple"} |
注:
dateOnly、url、phone、percent、unixTimestamp、multipleSelect由sync_meta.py的UI_KIND_OVERRIDE机制生成,利用 NocoBase 原生 interface 获得内置渲染与验证支持。
3.2 db2cm 常见错误
db2cm 同步时,interface 和 x-component 经常被设为与 type 相同的值:
| 错误 interface | 正确 interface | 错误 x-component | 正确 x-component |
|---|---|---|---|
string | input | — | Input |
bigInt | integer | — | InputNumber |
decimal | number | Number | InputNumber |
float | number | — | InputNumber |
| — | — | Integer | InputNumber |
⚠️
Integer和Number不是 NocoBase 注册的组件,使用会导致 React error #130(Element type is invalid: expected a string or class/function but got: undefined)
3.3 完整 field options 结构
正确示例(player_deposit.amount):
{
"allowNull": false,
"primaryKey": false,
"unique": false,
"possibleTypes": ["bigInt", "snowflakeId", "unixTimestamp", "sort"],
"rawType": "BIGINT",
"field": "amount",
"uiSchema": {
"x-component": "InputNumber",
"x-component-props": { "style": { "width": "100%" } },
"title": "金额"
}
}
错误示例(db2cm 同步的 player.balance):
{
"allowNull": false,
"uiSchema": {
"title": "余额",
"x-component": "Number" // ← 不存在的组件!
}
// 缺少: field, rawType, possibleTypes, primaryKey, unique
}
3.4 批量修复 SQL
-- 1. 修复 interface 值
UPDATE rgb.fields SET "interface" = 'input' WHERE "interface" = 'string';
UPDATE rgb.fields SET "interface" = 'integer' WHERE "interface" = 'bigInt';
UPDATE rgb.fields SET "interface" = 'number' WHERE "interface" = 'decimal';
UPDATE rgb.fields SET "interface" = 'number' WHERE "interface" = 'float';
-- 2. 修复 x-component: Integer → InputNumber
UPDATE rgb.fields
SET options = jsonb_set(
jsonb_set(options::jsonb, '{uiSchema,x-component}', '"InputNumber"'),
'{uiSchema,x-component-props}', '{"stringMode":true,"step":"1"}'
)
WHERE options->'uiSchema'->>'x-component' = 'Integer';
-- 3. 修复 x-component: Number → InputNumber
UPDATE rgb.fields
SET options = jsonb_set(
jsonb_set(options::jsonb, '{uiSchema,x-component}', '"InputNumber"'),
'{uiSchema,x-component-props}', '{"stringMode":true,"step":"1"}'
)
WHERE options->'uiSchema'->>'x-component' = 'Number';
-- 4. 补充缺失的 field 属性(DB 列名映射)
UPDATE rgb.fields
SET options = options::jsonb || jsonb_build_object('field', name)
WHERE options->>'field' IS NULL
AND type NOT IN ('belongsTo', 'hasMany', 'belongsToMany', 'hasOne');
3.5 RGB 语义提示(options.rgbMeta)
为了让 NocoBase 尽量还原旧 bo/admin 的展示层语义,当前同步脚本会在 rgb.fields.options 下额外写入一层 rgbMeta。这一层不直接替代 NocoBase 官方字段配置,而是作为“显示语义补充”,供后续页面生成器、自定义字段或插件读取。
示例(banner.img_url):
{
"field": "img_url",
"uiSchema": {
"title": "图片",
"x-component": "Input"
},
"rgbMeta": {
"uiKind": "attachment_url",
"preview": "image",
"uploadStrategy": "base64_image"
}
}
当前约定的重点语义:
rgbMeta.uiKind | 典型字段 | 说明 |
|---|---|---|
url | download.url banner.jump_url | 外链,适合链接跳转/下载 |
attachment_url | banner.img_url game.icon_url | 外部资源 URL,适合图片预览 |
internal_path | banner.jump_path player_notice.insite_url | 站内路径,不应做 URL 校验 |
domain | player.register_domain player_level.domain | 裸域名,走域名校验 |
email | daebak_email.email | 邮箱字段 |
phone | phone_num | 电话输入与脱敏语义 |
richtext_html | promotion.content agent_notice.content | 富文本 HTML,不是 Markdown |
unix_timestamp | *_ts | Unix 时间戳,展示层应转时间控件/时间列 |
money | bank_stat_day.deposit_amount | 金额显示 |
percent | provider.percent | 百分比显示 |
odds | deposit_event.odds_need | 赔率显示 |
m2m_array | player_message.tags | 多对多数组,不建议裸 JSON 编辑 |
multi_select | deposit_event.weeks | 有限选项数组 |
辅助字段说明:
| 字段 | 说明 |
|---|---|
rgbMeta.preview | image / link / download |
rgbMeta.copyable | 是否建议展示“复制”按钮 |
rgbMeta.uploadStrategy | base64_image / attachment / external_url |
rgbMeta.validator | url / domain / internal_path |
rgbMeta.timezone | 时间字段建议时区 |
rgbMeta.formatHint | 轻量格式提示,预留给复杂数字格式 |
注意:
rgbMeta是 RGB 侧扩展,不是 NocoBase 官方内置协议。它的目标是先把“唯一真相”里的显示语义保留下来,再由 NocoBase 页面层逐步消费这些 hint。
4. uiSchema 配置 (rgb."uiSchemas")
4.1 TableV2 的 rowKey 问题
NocoBase 客户端创建新的 Table Block 时,会硬编码 rowKey: "id"。对于使用 guid 作为主键的业务表,这会导致渲染异常。
修复方式:
插件服务端 (plugin-rgb-preset-actions) 已添加中间件,在 uiSchemas:insertAdjacent 请求中自动将 rowKey: "id" 替换为 collection 的 filterTargetKey(通常为 "guid")。
如需手动修复已有的 uiSchema:
-- 查找所有 rowKey 为 id 的 TableV2 schema
SELECT "x-uid", schema::text
FROM rgb."uiSchemas"
WHERE schema::text LIKE '%"rowKey":"id"%'
AND schema::text LIKE '%TableV2%';
-- 替换为 guid(谨慎操作,建议逐条确认)
UPDATE rgb."uiSchemas"
SET schema = replace(schema::text, '"rowKey":"id"', '"rowKey":"guid"')::jsonb
WHERE schema::text LIKE '%"rowKey":"id"%'
AND schema::text LIKE '%TableV2%';
4.2 RGBPrettyField 约定
对于表格列、详情区块这类只读展示场景,当前 RGB 侧页面生成器不再一律写死为 CollectionField。如果字段带有 rgbMeta 语义提示,会改用自定义组件 RGBPrettyField:
{
"x-collection-field": "banner.img_url",
"x-component": "RGBPrettyField",
"x-component-props": {
"fieldName": "img_url",
"uiKind": "attachment_url",
"preview": "image",
"copyable": false,
"validator": "none",
"ellipsis": false
},
"x-read-pretty": true
}
RGBPrettyField 当前主要负责:
| 场景 | 行为 |
|---|---|
attachment_url + image | 直接图片预览 |
url / email / phone / domain | 直接渲染成可点击链接 |
unix_timestamp | 格式化为可读时间 |
richtext_html | 以纯文本预览 HTML 内容 |
m2m_array / multi_select | 数组转逗号分隔文本 |
copyable=true | 增加复制能力 |
依赖关系说明:
| 组件/工具 | 作用 | 依赖 |
|---|---|---|
plugin-rgb-preset-actions | 注册客户端组件 RGBPrettyField | 运行时必须存在 |
plugin-rgb-ai-builder | 生成带 RGBPrettyField 的 uiSchema | 依赖上面的客户端插件 |
rgb-builder.js / gen_pages.py | 直接写 PG 生成页面 | 依赖上面的客户端插件 |
默认部署约定是这些插件/工具同时存在。如果运行时移除了 plugin-rgb-preset-actions,已经生成的 RGBPrettyField 节点会失去组件注册来源。
时间显示约定:
RGBPrettyField的unix_timestamp展示统一强制按 韩国时区(UTC+9 / Asia/Seoul) 格式化。- PATCH 抽屉里的时间控件也按同一套韩国时区换算显示与回写。
- 即使字段元数据里带了
rgbMeta.timezone,当前 NocoBase 显示层也不会覆盖这条约定,展示仍以韩国时区为准。
这样可以在不强依赖 NocoBase 官方字段接口名的前提下,先把 RGB 自己的显示语义恢复出来。
5. 修改后的注意事项
5.1 必须触发数据源同步
直接修改 PG 中的元数据后,NocoBase 的内存缓存不会自动刷新。必须执行以下操作之一:
方式一:API 调用(推荐)
# 获取 token
TOKEN=$(curl -s http://localhost:8880/api/auth:signIn -X POST \
-H 'Content-Type: application/json' \
-d '{"account":"nocobase","password":"admin123"}' \
| python3 -c "import sys,json;print(json.load(sys.stdin)['data']['token'])")
# 刷新数据源
curl -s -H "Authorization: Bearer $TOKEN" \
"http://localhost:8880/api/dataSources:refresh?filterByTk=main" -X POST
方式二:UI 操作
进入 NocoBase → 设置 → 数据源 → 主数据源 → 点击「刷新」按钮
方式三:重启容器
docker restart nocobase
⚠️ 单纯重启可能不够,建议重启后再通过 API 调用
dataSources:refresh
辅助方式:rgb-builder.js clearCache
# 清除 NocoBase 应用层缓存(不等同于 dataSources:refresh,仅清理 UI 层缓存)
node tools/nocobase/rgb-builder.js clearCache
注意:
clearCache只清理 NocoBase 应用缓存并提示重启,不会触发数据源元数据重新加载。正式同步后仍需使用上面的 API 调用或 UI 刷新。
5.2 验证检查清单
修改完成后,按以下步骤验证:
-- 1. 检查是否有非法 interface
SELECT DISTINCT "interface", count(*)
FROM rgb.fields
WHERE "interface" IN ('string', 'bigInt', 'decimal', 'float')
GROUP BY "interface";
-- 期望: 0 rows
-- 2. 检查是否有非法 x-component
SELECT DISTINCT options->'uiSchema'->>'x-component' as comp, count(*)
FROM rgb.fields
WHERE options->'uiSchema'->>'x-component' IN ('Integer', 'Number')
GROUP BY comp;
-- 期望: 0 rows
-- 3. 检查 autoGenId=false 表是否都有 filterTargetKey
SELECT name
FROM rgb.collections
WHERE (options->>'autoGenId')::text = 'false'
AND options->>'filterTargetKey' IS NULL
AND options->>'from' = 'db2cm';
-- 期望: 0 rows (或仅有 NocoBase 内部表)
-- 4. 检查 collection 是否有 schema/tableName
SELECT name
FROM rgb.collections
WHERE options->>'from' = 'db2cm'
AND options->>'schema' IS NULL;
-- 期望: 0 rows
-- 5. 通过 API 验证字段元数据
curl -s -H "Authorization: Bearer $TOKEN" \
"http://localhost:8880/api/fields:list?filter[collectionName]=player&paginate=false" \
| python3 -c "
import sys,json
for f in json.load(sys.stdin)['data']:
comp = (f.get('uiSchema') or {}).get('x-component','?')
print(f'{f[\"name\"]:20s} interface={f.get(\"interface\",\"?\"):15s} x-component={comp}')
"
5.3 新增业务表时的操作流程
- 在 MySQL 中创建新表(确保有
guid主键) - 在 NocoBase 数据源管理中点击「刷新」同步表结构
- 使用 NocoBase UI「配置字段 → 从数据库同步」来创建字段
- 不要使用 db2cm 批量同步,会导致元数据不完整
- 如果已经通过 db2cm 同步,需要按本文档的 SQL 进行修复
6. 排错指南
React error #130
现象: 前端报 Minified React error #130; Element type is invalid: expected a string or class/function but got: undefined
排查步骤:
- 下载 NocoBase 日志(设置 → 系统日志 → 下载)
- 在
system_error日志中搜索Diagnostic information - 查看 diagnostic schema 中的
x-decorator-props.collection确定是哪个表 - 分层检查
x-component是否合法
字段层(rgb.fields)— 检查字段元数据中的组件是否为 NocoBase 注册组件:
SELECT name, options->'uiSchema'->>'x-component' as comp
FROM rgb.fields
WHERE "collectionName" = '<table_name>'
AND options->'uiSchema'->>'x-component' NOT IN (
-- NocoBase 原生字段组件
'Input', 'Input.TextArea', 'Input.URL', 'Input.JSON',
'InputNumber', 'DatePicker', 'Select',
'Checkbox', 'Switch', 'Radio.Group', 'Checkbox.Group',
'AssociationField', 'Markdown', 'Password', 'ColorPicker',
'Upload.Attachment',
-- sync_meta UI_KIND_OVERRIDE 生成的原生组件(interface 不同但组件复用)
-- url→Input.URL, email/phone→Input, percent→InputNumber,
-- unixTimestamp→DatePicker, multipleSelect→Select
-- 组件本身已在上方列出,此注释仅说明来源
);
💡
UI_KIND_OVERRIDE生成的字段使用的组件(Input.URL、Input、InputNumber、DatePicker、Select)均为 NocoBase 原生注册组件,不会触发 React #130。新增的 interface 值为:dateOnly、url、phone、percent、unixTimestamp、multipleSelect。
页面层(rgb."uiSchemas")— 检查页面 schema 中的组件是否已注册(含 RGB 自定义组件):
SELECT "x-uid", schema->>'x-component' as comp
FROM rgb."uiSchemas"
WHERE schema::text LIKE '%<table_name>%'
AND schema->>'x-component' NOT IN (
-- NocoBase 原生字段组件(同上)
'Input', 'Input.TextArea', 'Input.URL', 'Input.JSON',
'InputNumber', 'DatePicker', 'Select',
'Checkbox', 'Switch', 'Radio.Group', 'Checkbox.Group',
'AssociationField', 'Markdown', 'Password', 'ColorPicker',
'Upload.Attachment',
-- NocoBase 原生布局/区块组件
'CollectionField', 'TableV2', 'Grid', 'Grid.Row', 'Grid.Col',
'CardItem', 'FormItem', 'ActionBar', 'Action', 'Space',
-- RGB 自定义组件(需 plugin-rgb-preset-actions)
'RGBPrettyField', 'RGBPostAction', 'RGBPatchAction'
)
AND schema->>'x-component' IS NOT NULL;
Association not found
现象: association xxx_ref in yyy not found
排查步骤:
- 检查 belongsTo 字段的
targetcollection 是否存在 - 检查
targetKey字段是否在目标表中存在 - 检查
foreignKey字段是否在当前表中存在且有isForeignKey: true
SELECT f.name, f.options->>'target' as target,
f.options->>'targetKey' as tk, f.options->>'foreignKey' as fk
FROM rgb.fields f
WHERE f."collectionName" = '<table_name>' AND f.type = 'belongsTo';
7. 参考
- NocoBase 官方文档(v2): https://v2.docs.nocobase.com
- Collection API: https://v2.docs.nocobase.com/api/database/collection
- Field API: https://v2.docs.nocobase.com/api/database/field
- Collection Fields: https://v2.docs.nocobase.com/data-sources/data-modeling/collection-fields/
- Attachment (URL) 插件: https://v2.docs.nocobase.com/data-sources/field-attachment-url/
- Markdown(Vditor) 插件: https://v2.docs.nocobase.com/plugins/%40nocobase/plugin-field-markdown-vditor/
- Text Copy 插件: https://v2.docs.nocobase.com/plugins/%40nocobase/plugin-text-copy
- JS Column: https://v2.docs.nocobase.com/interface-builder/fields/specific/js-column
- JS Field: https://v2.docs.nocobase.com/interface-builder/fields/specific/js-field
- 插件源码:
tools/nocobase/plugin-rgb-preset-actions/