
核心问题:大模型已经能高质量地"设计"UI/UE——直接产出可运行的 HTML 原型 (我们的全部原型即由 LLM 生成)。但企业内部的落地目标往往是私有技术框架 (自研组件引擎、私有组件库),二者之间隔着组件模型、样式体系与布局引擎的巨大差异。 "设计产出"到"框架程序"之间的转换效率,决定了设计迭代能否当天生效。
路径 | 做法 | 问题 |
|---|---|---|
代码直译 / 转义翻译 | LLM 把 HTML 逐段"翻译"成私有框架代码 | 每次生成不可复现、token 成本高、样式靠猜、无法回归审计;框架 API 一变全部重来 |
样式解析 + 规则引擎 | 解析 CSS/内联样式,按规则映射到框架组件与属性 | 确定性最好,但规则要落在一份稳定的结构化数据上——直接对 DOM 写规则会与 DOM 细节耦合 |
多模态反推 | 模型看设计稿截图直接生成框架代码 | 视觉损失大(字号/颜色/间距全是估的),不可审计,无法批量 |
设计规范适配层 | 约束设计稿使用标准组件,再一一映射 | 牺牲设计自由度;LLM 生成的原型恰恰是无约束的 HTML/CSS |
这是编译器的经典思路:前端语言无关,后端目标无关。 任何来源(HTML/CSS 原型、未来的设计稿格式)都编译成同一份 IR; IR 再由各自的代码生成器输出到任意私有框架。选它的三个决定性理由:
OODUI 是自研的移动端优先 UI 引擎,本管的转换目标即它:
直转器(HTML 直接映射组件)很快撞上三堵墙:
IR(中间表示)把这三件事一次性编译完成:先物化出每个节点的计算样式与规范化结构, 后续所有语义判定、组件映射、审计对账都只依赖 IR、只读不猜。

图 1 | 五级管线与三条保障带、审计闭环
提取层 html2ir.mjs 把 CSS 级联 + 内联 style 展开为每节点的计算样式; 语义层 semantic2ouc.mjs 完成角色推断、组件映射与样式注入,产出 .ouc.json, 服务端反序列化为 .cls 组件树交引擎渲染。

图 2 | IRNode 字段与三大消费方
{
"tag": "section", "classes": ["gt-stat-grid"],
"children": [ /* 规范化 DOM 树 */ ],
"icon": "ri-user-line", "src": null,
"text": ["872", "刷题总数"],
"style": {
"layout": { "display": "grid", "position": "absolute", "top": "0" },
"paint": { "background": "linear-gradient(...)", "color": "#fff" },
"typography": { "font-size": "1.667em", "font-weight": "700" },
"box": { "width": "394px", "border-radius": "1em" }
}
}级联展开算法:收集所有命中节点的规则(外部 CSS 按 specificity × 出现序、 <style> 次之、内联 style 最高),排序后逐属性覆盖写入 flat, 再按属性分类归入四组。这一步等价于浏览器 getComputedStyle 的离线实现, 是整条管线的地基——初版漏掉内联 style 时,字号/颜色覆盖率长期失真、进度条完全无法识别。

图 3 | inferRole 从上到下短路命中;三个历史事故全部源于顺序错误
⚠ 同构签名 = tag + classes(阈值 ≥3),类名仅用于结构分组、严禁参与语义推断; 复杂子项(item 内层仍是列表)递归拆分为嵌套节点,避免整段内容被压扁丢弃。
语义角色 | OUC 组件 | 内容/样式槽位 | 已定论的关键约束 |
|---|---|---|---|
PageHeader / TabBar | ood.UI.ToolBar | items[] | 引擎无 bottom 停靠,TabBar 实际渲染在顶部(已知差距) |
HeroBanner | ood.UI.Block | KEY=渐变+display:block;子节点 Label/Button 绝对定位 | CS.PANEL 对 Block 不生效 →properties.background='transparent'触发 action 打透;无 IR 高度时须兜底 |
StatGroup | ood.UI.Gallery | items[].value / caption;width:100% | VALUE 节点会渲染(旧"不渲染 value"已勘误,勿再双写 comment) |
GridGroup | ood.UI.Gallery | caption / imageClass / comment | 图标必须走 imageClass(iconFontCode 会被当文本画出类名) |
NavList / CardList | ood.UI.List | caption / ext / imageClass | List 渲染 {ext} 不渲染 {sub};TreeListItem 无 iconFontCode |
ProgressItem | ood.UI.ProgressBar | value / caption | 识别依赖内联样式并入(原型宽度写在 style 上) |
Title / Caption | ood.UI.Label | caption + CS.CAPTION | properties.fontColor 必须是 CSS 变量名,排版走 CS.CAPTION 直注 |
Icon | ood.UI.ContentBlock | imageClass | 判定必须先于 EmptyState(texts≤2 包含 0) |
EmptyState | ood.UI.ContentBlock | caption / subtext / imageClass | 必须写 imageClass,否则产出空壳(曾 29 个,已清零) |
Action | ood.UI.Button | caption + CS.KEY | 默认灰底 rgb(245,245,245)+display:block → 须覆盖为 inline-block/透明 |
Panel(兜底容器) | ood.UI.Block | conLayoutColumns:1 + KEY | 壳层默认 inline-block 会收缩 → 强制 display:block |
对照表各槽位在真实页面上的落地效果——首页集中了 Banner、Gallery 宫格、统计组、List、ToolBar 五类组件:

首页 | 左:HTML 原型,右:产物在 OODUI 引擎渲染(HeroBanner + GridGroup + StatGroup + List + TabBar)

个人中心 | 两组 StatGroup(含彩色数值)与 NavList,"value 会渲染"结论的来源页面
容器布局只有两种合法形态:A 自由布局(conLayoutColumns:1,子节点交引擎列布局)、 B 绝对定位(展示型容器,工具从 IR 算出子节点 left/top/height 显式下发)。 配套约束:容器去 padding(折进子坐标防双计)、去 flex/gap 无效属性但 display 必须定为 block。
审计闭环:IR 与产物同构,审计工具逐页量化文本/图标承载与样式保真。 修复一轮后(17 页):文本 81.1%→86.2%、图标 49.5%→80.4%、空壳组件 29→0。 宿主页 ?probe=1 输出组件 computed box+背景、?shot=1 支持无头截图—— 所有观察都可复现。
布局归一的效果在"视频课程"页最为直观:顶部深红渐变 Banner(HeroBanner)满宽上色、 分区卡片(Panel + Gallery)撑满列宽——这两处正是"白 PANEL 盖渐变"与"壳层收缩"两个通病的主战场:

视频课程 | 布局归一后的 Banner 渐变与满宽卡片
智能家居首页覆盖了问候区、快捷场景宫格、房间 2×2 宫格与设备列表的嵌套组合, 是多组件协同转换的代表性页面(首页/个人中心/视频课程的截图已按主题分布在前文 §3、§4):

智能家居 | 场景宫格、房间 2×2 宫格、设备列表(左原型 / 右引擎渲染)
剩余差距:Banner 内头像/图标定位有偏差;无头环境图标字体偶发缺字。均在审计"未解决"清单中。
—— 完 ——
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。