NocoBase Meta V2 草案与首批迁移清单
1. 背景
当前 RGB 的 NocoBase 方案,已经具备了 SQLAlchemy meta -> admin_server metadata -> NocoBase sync 的基础链路,但离还原 bo/admin 的显示和编辑体验还有一个明显缺口:
FieldInfo.type只有 10 个粗类型:string/int/decimal/bool/datetime/enum/color/date/text/arrayadmin_server暴露给前端的字段元信息也只覆盖type/editable/ref/enum_options/sensitivesync_meta.py当前仍然把:text -> Input.TextAreaarray -> Input.JSON
- 老后台实际使用了图片预览、链接跳转、时间戳、富文本、颜色、金额、多选等大量语义化组件
这导致当前 meta 更像“数据库字段类型映射”,而不是“显示层字段语义”。
NocoBase 官方文档本身是支持这类增强的,尤其是:
- 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/
- Rich text: https://v2.docs.nocobase.com/data-sources/data-modeling/collection-fields/media/rich-text
- 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
结论很明确:下一步不应该继续只扩 type,而应该把“底层值类型”和“显示/编辑语义”拆开。
2. 当前代码里最核心的缺口
相关文件:
servers_v2/shared/rgb_db/src/rgb_db/models/meta.pyservers/admin_server/app/metadata/types.pyservers/admin_server/app/metadata/registry.pytools/nocobase/sync_meta.pytools/nocobase/plugin-rgb-preset-actions/dist/client/index.js
2.1 现在的 type 表达力不够
以下场景目前都被压成了 string 或 text:
- 图片 URL
- 外链 URL
- 站内路径
- 裸域名
- 邮箱
- 富文本 HTML
- 普通多行备注
- Markdown
这会带来两个后果:
- NocoBase 无法自动选出最贴近老后台的字段界面
- 下游消费方只能继续靠硬编码补丁识别字段语义
2.2 文档、同步代码、旧后台三者已经漂移
当前仓库里同时存在三种认知:
- 本地文档认为
text -> Markdown - 同步代码实际是
text -> TextArea - 老后台
EditText用的是 CKEditor HTML,不是 Markdown
这说明 text 至少应该拆成:
plain_textrichtext_htmlmarkdown
2.3 下游消费方已经在偷偷使用另一套字段词汇
plugin-rgb-preset-actions 客户端渲染器当前识别的是:
textjsonintfloatboolselectcolordatetimestamp
这套词汇与 SQLAlchemy FieldInfo.type 并不一致,说明继续把所有语义都挤进一个 type 会越来越难维护。
3. 建议的 FieldInfo V2 结构
推荐思路:保留“值类型”,新增“UI 语义”和“显示提示”。
3.1 建议保留的 primitive type
PrimitiveType = Literal[
"string",
"int",
"decimal",
"bool",
"date",
"datetime",
"text",
"array",
"json",
]
3.2 建议新增的 ui_kind
UiKind = Literal[
"default",
"url",
"attachment_url",
"internal_path",
"domain",
"email",
"phone",
"richtext_html",
"markdown",
"unix_timestamp",
"money",
"percent",
"odds",
"m2m_array",
"multi_select",
]
3.3 建议新增的字段属性
@dataclass(frozen=True)
class FieldInfo:
type: PrimitiveType
label: dict[str, str] = field(default_factory=dict)
editable: bool = False
bulk_editable: bool = False
display: bool = True
sensitive: bool = False
mask_type: Literal["password", "phone", "bank_num"] | None = None
ref: RefInfo | None = None
enum_options: dict[int, dict[str, str]] | None = None
# v2 additions
ui_kind: UiKind = "default"
copyable: bool = False
preview: Literal["none", "link", "image", "download"] = "none"
timezone: str | None = None
upload_strategy: Literal["none", "base64_image", "attachment", "external_url"] = "none"
validator: Literal["none", "internal_path", "domain", "url"] = "none"
format_hint: str | None = None
3.4 为什么不建议继续只扩 type
因为下面这些字段虽然底层都是 string,但显示和编辑语义完全不同:
banner.jump_urlbanner.jump_pathplayer_notice.outsite_urlplayer_notice.insite_urlbanner.img_urldownload.urlplayer_level.domain
它们不该继续共用同一个 type="string" 再靠调用方猜。
4. NocoBase 映射建议
4.1 primitive type -> 默认界面
保持当前思路,但需注意 NocoBase 侧的 type 精度:
| RGB primitive | NocoBase type | NocoBase interface | x-component | 注意 |
|---|---|---|---|---|
string | string | input | Input | |
int | bigInt(RGB 的 BIGINT 列)或 integer | integer | InputNumber | MySQL BIGINT 必须用 bigInt,否则 JS 精度丢失 |
decimal | decimal | number | InputNumber | |
bool | boolean | checkbox | Checkbox | |
date | dateOnly | dateOnly | DatePicker | 不应映射到 datetimeNoTz,那是带时间的 |
datetime | datetimeNoTz | datetimeNoTz | DatePicker | showTime: true |
text | text | textarea | Input.TextArea | 不是 Markdown |
json | json | json | Input.JSON |
4.2 ui_kind -> 覆盖/增强规则
实现状态说明:下表中标 ✅ 的 ui_kind,NocoBase 已有原生 interface/plugin 支持。
sync_meta.py已通过UI_KIND_OVERRIDE字典实现第一批原生映射(url、email、phone、percent、unix_timestamp、multi_select),这些 ui_kind 现在直接输出对应的原生interface+x-component。标 🔧 的为官方无直接对应、仍走TYPE_MAP默认值 +rgbMetahint 供RGBPrettyField自定义渲染。注意:即使使用了原生 interface,rgbMeta仍然会写入options,确保页面层RGBPrettyField在需要时仍能读到 hint。
| ui_kind | 推荐 NocoBase 能力 | 说明 | 原生支持 |
|---|---|---|---|
url | URL field(interface: url, x-component: Input.URL) | 用于外部链接 | ✅ |
attachment_url | Attachment (URL) ⚠️ 付费插件 @nocobase/plugin-field-attachment-url(Community Edition+);未启用时可用 JS Field 或 RGBPrettyField 替代只读预览 | 用于图片/文件预览 | |
internal_path | String + validator + copyable(需 Text Copy 插件) | 用于 /event/123 这类站内路径 | 🔧 |
domain | String + validator + copyable(需 Text Copy 插件) | 用于裸域名 | 🔧 |
email | Email field(interface: email) | 邮件语义 | ✅ |
phone | Phone field(interface: phone) | 手机号语义 | ✅ |
richtext_html | 只读:JS Column 渲染 HTML;编辑:Rich text(需确认与 CKEditor HTML 兼容性)或 JS Field | 保留 HTML 内容 | 🔧 部分 |
markdown | Markdown(Vditor)(插件 @nocobase/plugin-field-markdown-vditor,内置但默认不启用) | 真正使用 Markdown 时再启用 | ✅ 插件 |
unix_timestamp | Unix timestamp field(interface: unixTimestamp) | 表格和表单都按时间处理 | ✅ |
money | Number + money formatter / JS Column | 数值展示语义 | 🔧 |
percent | Percent field(interface: percent) | 百分比展示 | ✅ |
odds | Number + odds formatter / JS Column | 体育赔率展示 | 🔧 |
m2m_array | Many-to-Many(Array)(interface: m2m) | 代替 JSON textarea | ✅ |
multi_select | Multiple Select(interface: multipleSelect) | 用于纯枚举多选 | ✅ |
4.3 一个关键原则
如果旧后台是“读写协议特殊”,只改字段类型还不够。
典型例子:
- 老后台编辑横幅、弹窗、推广时,提交的是
img_b64 - 数据库存的是
img_url
这类字段在 NocoBase 里可以先恢复“显示预览”,但要恢复“编辑上传”,必须同时补:
- 上传接口
- 上传后回填 URL
- 或自定义 JS Field / action workflow
5. 首批字段迁移清单
本节按三类拆:
- A 类:可直接改 meta,风险低
- B 类:需要接口/上传协议配合
- C 类:先做显示增强,不急着改底层写入
5.1 A 类:可直接改 meta
| 字段 | 当前 | 目标 | 说明 |
|---|---|---|---|
download.url | string | url | 下载链接 |
coin_transaction.web_url | string | url | 外链 |
player_notice.outsite_url | string | url | 站外跳转 |
banner.jump_url | string | url | 外链跳转 |
coin_transaction.link | string | url | 外部链接 |
daebak_email.email | string | email | 邮件语义 |
*_ts / ts / start_ts / end_ts / expire_ts / settle_ts / event_ts / block_ts | int | unix_timestamp | 全局批量规则 |
*_percent / percent / percentage | decimal/int | percent | 百分比展示 |
odds | decimal | odds | 体育赔率展示 |
phone_num | string | phone | 可结合 mask |
wallet_address | string | default + copyable | 钱包地址适合复制 |
ip / register_ip / last_login_ip / user_ip | string | default + copyable | 便于排查 |
5.2 B 类:需要接口/上传协议配合
| 字段 | 当前 | 目标 | 说明 |
|---|---|---|---|
banner.img_url | string | attachment_url | 旧后台编辑用 img_b64 |
player_notice.img_url | string | attachment_url | 旧后台编辑用 img_b64 |
promotion.img | string | attachment_url | 旧后台编辑用图片上传 |
player_level.icon_url | string | attachment_url | 旧后台编辑可改图标 |
game.icon_url | string | attachment_url | 若未来要在 NocoBase 编辑图标,需补上传 |
provider.entrance_icon_url | string | attachment_url | 若需编辑则要补上传 |
provider.category_icon_url | string | attachment_url | 若需编辑则要补上传 |
promotion.content | text | richtext_html | 旧后台是 CKEditor HTML |
agent_notice.content | text | richtext_html | 旧后台是富文本弹窗内容 |
agent_announcement.content | text | richtext_html | 跑马灯/公告内容 |
player_message.content | text | richtext_html | 站内信内容 |
5.3 C 类:先做显示增强
| 字段 | 当前 | 目标 | 说明 |
|---|---|---|---|
banner.jump_path | string | internal_path | 不要误判成 URL |
player_notice.insite_url | string | internal_path | 站内路由 |
agent_domain.domain | string | domain | 裸域名 |
player_level.domain | string | domain | 主域名 |
player.register_domain | string | domain | 注册域名 |
player.last_login_domain | string | domain | 登录域名 |
player_message.tags | array | m2m_array | 不该是 JSON 文本框 |
player_message.levels | array | m2m_array | 不该是 JSON 文本框 |
player_message.groups | array | m2m_array | 不该是 JSON 文本框 |
attendance_event.levels | array | m2m_array | 多选关系 |
deposit_event.groups | array | m2m_array | 多选关系 |
deposit_event.levels | array | m2m_array | 多选关系 |
deposit_event.weeks | array | multi_select | 星期多选 |
bank_stat_day.deposit_amount | string | money | 旧后台按 money 列展示 |
bank_stat_day.withdraw_amount | string | money | 旧后台按 money 列展示 |
coin_transaction.rate | string | decimal/money | 需要确认是否可改底层类型 |
6. 具体业务页面与字段语义映射
6.1 图片预览
老后台组件:
bo/admin/src/ui/components/ElTable/ElTableColumnImg.vuebo/admin/src/ui/components/ElForm/ElFormItemImage.vuebo/admin/src/ui/views/desktop/GameProvider/GameProviderView.vue
对应 meta 建议:
ui_kind = "attachment_url"preview = "image"
6.2 链接跳转
老后台组件:
bo/admin/src/ui/components/ElTable/ElTableColumnLink.vue
对应 meta 建议:
- 外链:
ui_kind = "url" - 站内路径:
ui_kind = "internal_path" - 域名:
ui_kind = "domain"
6.3 时间戳
老后台组件:
bo/admin/src/ui/components/ElTable/ElTableColumnTs.vuebo/admin/src/ui/components/ElForm/ElFormItemDateTime.vue
对应 meta 建议:
ui_kind = "unix_timestamp"timezone = "Asia/Seoul"或在显示层统一配置时区
6.4 富文本
老后台组件:
bo/admin/src/ui/components/EditText.vuebo/admin/src/ui/views/desktop/Promotion/InnerAdd.vuebo/admin/src/ui/views/desktop/Web/Content/index.vue
对应 meta 建议:
- HTML 内容:
ui_kind = "richtext_html" - 普通备注:保持
text + plain textarea - Markdown:只在业务明确转 Markdown 后启用
7. 建议的落地顺序
Phase 1: 先扩 meta 结构,不动业务接口
目标:
- 在
FieldInfo/FieldMeta/ metadata JSON 里加ui_kind等字段 - 不立即修改现有 PATCH/create 协议
- 先让 NocoBase 能准确显示
涉及文件:
servers_v2/shared/rgb_db/src/rgb_db/models/meta.pyservers/admin_server/app/metadata/types.pyservers/admin_server/app/metadata/scanner.pyservers/admin_server/app/metadata/registry.py
Phase 2: 升级 sync_meta 映射器
目标:
- 从
type改为type + ui_kind array不再默认变Input.JSONtext不再一刀切unix_timestamp/attachment_url/url等开始落到 NocoBase
涉及文件:
tools/nocobase/sync_meta.py
Phase 3: 升级 NocoBase 侧消费方
目标:
plugin-rgb-preset-actions表单渲染器识别ui_kindplugin-rgb-ai-builder/ 页面生成器输出更合理的字段展示- 图片上传、富文本编辑、数组选择等场景再逐步替换为定制化 UI
涉及文件:
tools/nocobase/plugin-rgb-preset-actions/dist/client/index.jstools/nocobase/plugin-rgb-ai-builder/dist/server/index.jstools/nocobase/gen_pages.py
8. 推荐的首批实施范围
如果只做一轮最划算的改造,建议顺序是:
unix_timestampurlattachment_urlinternal_path/domainm2m_arrayrichtext_htmlmoney/percent/odds
原因:
- 这几类字段最直接影响 NocoBase 的可用性
- 与
bo/admin的体感差异最大 - 官方文档已有对应能力,落地阻力最小
9. 本文档的判断边界
本文是静态梳理结果,未连生产/测试 NocoBase 实例做在线校验。
因此以下内容仍需二次确认:
- Attachment(URL):
@nocobase/plugin-field-attachment-url是 Community Edition+ 付费插件(内置但默认不启用)。需确认当前 NocoBase 许可证是否已覆盖。若未覆盖,图片预览走 JS Field/RGBPrettyField 替代,编辑上传走 RGBPatchAction 自定义组件(参见 guide 中方案 B) - richtext_html 的读写分层:
- 只读展示:推荐 JS Column(直接渲染 HTML),或 RGBPrettyField(以纯文本预览)
- 编辑模式:NocoBase 的 Rich Text 组件底层是 TinyMCE/Quill,输出格式可能与老后台 CKEditor 的 HTML 不完全兼容,需在测试环境做双向验证(CKEditor HTML → Rich Text 加载 → 保存后 HTML 是否变形)
- 如果兼容性不好,编辑模式可走 JS Field 嵌入 CKEditor CDN
money/rate这类字段是否允许同步修正数据库底层类型- 时区最终统一使用 KST、UTC 还是按前端 locale 渲染
10. 实现优先级建议
基于"官方原生能力优先、自定义 rgbMeta 兜底"的原则:
第一批(直接切原生 interface,sync_meta.py TYPE_MAP 改动):
url→ interface:urlemail→ interface:emailphone→ interface:phonepercent→ interface:percentunix_timestamp→ interface:unixTimestampmulti_select→ interface:multipleSelect
第二批(需启用插件或验证兼容性):
markdown→ 启用 Markdown(Vditor) 插件attachment_url→ 启用 Attachment(URL) 插件,或走 RGBPrettyField + RGBPatchActioncopyable→ 启用 Text Copy 插件
第三批(官方无直接对应,保留 rgbMeta + 自定义渲染):
internal_path、domain、money、odds、richtext_html、m2m_array
注意:切换原生 interface 后,已生成的页面 schema 中写死的 RGBPrettyField 节点不会自动切换,需同步更新 gen_pages/rgb-builder 的字段判断逻辑(有原生 interface 时不再强制 RGBPrettyField)。