适用版本:ChengOS v0.1.0+ | 最后核对:2026-08-12 | 来源:
crates/cheng-nodes/src/nodes/builtin/ui、crates/cheng-nodes/src/nodes/builtin/ui/common/types.rs、chengflow-ui/src/runtime、chengflow-ui/scripts/generate-pages.ts
ChengOS 里的页面不是模板文件,而是工作流的输出:每个 UI 节点描述一个区域,ui/page_root 把它们组装起来,产出 PageLayout JSON,再由运行时渲染。
这层间接正是要点。因为页面是工作流的输出,它的内容可以被计算出来——一张表格的数据可以来自同一张图里的数据库查询节点。
组装方式
ui/top_nav ──────────┐
ui/left_sidebar ─────┤
ui/right_sidebar ────┼──► ui/page_root ──► PageLayout JSON ──► 运行时渲染器
ui/header ───────────┤
ui/content ──────────┤
ui/footer ───────────┘
每个区域节点产出一个配置对象;ui/page_root 在具名输入端口上收集它们并生成布局。没连的区域就不会出现。
| 节点 | 区域 |
|---|---|
ui/page_root |
骨架——收集一切,输出 PageLayout |
ui/top_nav |
顶部导航:Logo、菜单、操作按钮 |
ui/left_sidebar |
左侧导航:菜单项、图标、可折叠 |
ui/right_sidebar |
右侧面板:属性、帮助、上下文信息 |
ui/header |
页头:标题、面包屑、操作、标签页 |
ui/content |
主内容区:区块 |
ui/footer |
页脚:版权、链接、社交 |
ui/route |
路由树中的一个节点 |
ui/rbac_guard |
访问控制——见页面访问控制 |
页面根节点
ui/page_root 承载页面身份和全局外观:
| 字段 | 含义 |
|---|---|
site_id |
该页面属于哪个站点 |
page_id |
运行时加载配置所用的页面标识 |
page_name |
人类可读的名字,用于文档与面包屑 |
theme |
light(默认)或 dark |
layout_template |
default、sidebar、topnav 或 blank |
logo、favicon、primary_color |
品牌设置,primary_color 为十六进制,如 #1890ff |
output_base_dir |
归档根目录,默认 site_archive |
归档路径是 {output_base_dir}/{site_id}/pages/{page_id}.json。
想要完全没有外框时就选 blank——嵌入式视图或落地页。
内容区块
ui/content 持有一个区块列表。区块被刻意设计得很通用:
{
"blockType": "table",
"title": "用户列表",
"span": 24,
"config": { "columns": [{ "key": "name", "title": "姓名" }] }
}
| 字段 | 含义 |
|---|---|
blockType |
card、table、form、chart、raw_html、iframe、tabs、grid…… |
title |
可选的区块标题 |
span |
栅格占比,1–24 |
config |
形状由区块类型决定 |
栅格是 24 列:span: 24 是整宽,span: 12 是一半,span: 8 是三分之一。span 为 0 会被拒绝,而不是被悄悄当成整宽。
ui/content 还接受 padding——一个数字,或详细的 {top, right, bottom, left}——以及 background 背景色。
config 刻意不做类型约束:形状由区块类型决定,因此在运行时新增一种区块类型不需要改后端 schema。
路由
ui/route 节点构成一棵树:子路由连到父路由的子节点端口。
| 字段 | 含义 |
|---|---|
path |
静态(users)、动态(:id)或通配(*) |
name |
显示在菜单与标题中 |
icon |
菜单图标 |
meta |
SEO 与扩展数据——title、requiresAuth、keywords、description |
page_workflow_id |
渲染该路由页面的工作流 |
site_id |
仅根节点配置;子节点通过子端口继承 |
output_base_dir |
仅根节点,默认 site_archive |
路由绑定一个页面工作流:访问该路由会执行那个工作流来渲染页面。路由还可以覆盖布局模板,其优先级高于页面工作流自身的默认值。
路由带有状态——ready(页面工作流已配置且可用)、pending(尚未配置)或 failed(页面工作流执行失败)——因此一个半成品站点仍可导航,其缺口是显式可见的,而不是变成一堆白页。
运行时与构建期
有两个渲染器消费同一份 PageLayout:
- 运行时——
chengflow-ui/src/runtime/把 PageLayout JSON 渲染成实时页面与区块:一个自带路由、布局和区块组件的微型应用渲染引擎。 - 构建期——
scripts/generate-pages.ts(pnpm gen:pages)针对后端执行工作流,提取PageLayout和路由树,同时写入public/site_archive/{siteId}/以及pages和routes数据表。通过scripts/pages-config.json配置。
这正是 pnpm build 需要后端在运行的原因:gen:pages 是它的一部分,而不执行产出页面的那些工作流,它就渲染不出页面。见开发环境搭建。
静态生成适合内容不随请求变化的页面,快且可缓存;运行时渲染适合内容取决于「谁在问」的页面。同一个站点里可以两者混用。
实用形态
页面工作流:
数据源节点 ────────────────► ui/content ──┐
ui/top_nav ───────────────────────────────┼──► ui/page_root
ui/left_sidebar ──────────────────────────┘
路由工作流:
ui/route(根,含 site_id)──► ui/route(子,path ":id")
│
└── page_workflow_id → 上面那个页面工作流
一个工作流只做一个页面。一个组装三个页面的工作流比三个工作流更难测试,而路由树本身已经提供了组合能力。
下一步
- 工作流发布为应用——给它一个 URL。
- 页面访问控制与 RBAC Guard——限制路由访问。
- 表格工作区——表格区块天然的数据源。

暂无评论内容