ARTICLE DETAIL

资讯详情

深耕编程入门与网站建设的一线实战洞察。

跨框架统一:用Fiori Fundamentals让SAPUI5与Vue体验一致

跨框架统一:用Fiori Fundamentals让SAPUI5与Vue体验一致 上个月帮一家制造企业做前端治理打开他们的系统左边是 SAPUI5 的采购审批右边是 React 做的看板下面还挂着一个 Vue 2 的老门户。三个前端三种按钮风格光是一个“保存”按钮在同一个页面里就有三种圆角和三种 hover 状态。这种情况在 SAP 生态里太常见了——后端 ERP 是 SAP前端团队却早就用 Vue、React 写新功能了。两个世界一旦并存视觉和交互就会迅速四分五裂。解决这个问题的关键不是把 Vue 项目干掉也不是逼所有人回到 SAPUI5而是找到一层能跨框架复用的设计基础。SAP Fiori Fundamentals 就是干这件事的。这篇文章我会从实际项目角度拆解它到底解决什么问题、核心由哪几部分组成、怎么接入 Vue 项目、从 SAPUI5 迁移时最容易踩哪些坑以及最后怎么让“一致体验”不只是一次性工程而成为团队能长期维护的机制。1. 为什么偏偏是 Fiori FundamentalsSAPUI5 的“看不见的枷锁”1.1 SAPUI5 的稳定是建立在封闭之上的SAPUI5 本身是一个很成熟的企业级前端框架MVC 分层、模型绑定、国际化、主题切换、控件库都很完善。做 SAP 系统它确实是主力军但这套东西有一个很现实的问题它只服务于 SAP 生态。你可以把 SAPUI5 想象成一家高档餐厅的固定套餐。厨师把所有搭配都配好了端上来就是一道完整的菜稳定、好吃但你没法把这道菜的“盘子”单独拿出去装你自己做的菜。SAPUI5 的控件和主题是深度耦合的控件封装了 HTML 结构、CSS 样式、交互行为甚至 ARIA 属性。想在外部用一行new sap.m.Button()渲染出 Fiori 风格按钮就必须在 SAPUI5 运行时里跑其他框架根本使唤不动它。很多企业就是在这里被卡住的。SAP 侧的业务页面都有 Fiori 设计语言Vue 侧的新报表、看板、审批流却要用自己习惯的组件库。两边各做各的企业主品牌就被稀释成“三种圆角并存”的混搭风。不是视觉团队不努力而是缺少一个能让 SAPUI5 和 Vue 共用的设计底座。1.2 Fiori Fundamentals 的定位它是规范不是框架SAP Fiori Fundamentals 的定位可以简单理解为把 Fiori 这套设计语言从 SAPUI5 中“提炼”出来的开源底层。早期它叫 Fiori Fundamentals后来在社区里演化出 Fundamental Library Styles、Fundamental Vue、Fundamental NGX 等一堆衍生包。名字容易让人犯迷糊但核心思想一直没变提供一套不绑死框架的 CSS 样式、图标字体和主题变量让任何技术栈都能写出长得像 Fiori 的界面。它不是 Web Components也不是要替代 SAPUI5。它的工作方式更像一份“设计系统翻译器”SAPUI5 里一套 Fiori 外观被翻译成 HTML CSS 类名 CSS 自定义属性。Vue 项目拿到这份“翻译结果”就能在不引 SAPUI5 的前提下做出同款按钮、输入框、表格、对话框。这里的价值不是“抄一下风格”而是统一设计令牌。SAPUI5 的主题引擎背后有一套 SAP 主题参数比如品牌色、背景色、链接色、按钮各状态的边框色和文字色。Fiori Fundamentals 把这些参数用 CSS 变量暴露出来Vue 项目只要在同一套变量体系下写样式两边就天然趋同。我见过不少团队试图用“复制一段 SCSS 变量”或者“全局覆盖 Element Plus 主题”来对齐视觉最后都会在某个界面细节上翻车。原因很简单你只是改了几个颜色变量交互状态、密度、圆角、图标风格并没有真正对齐。Fiori Fundamentals 的价值在于它把整套规范都带过来而不是给你几个零散颜色。2. Fiori Fundamentals 的组成样式、图标、主题令牌2.1 样式层以fd-前缀为线索的组件类Fiori Fundamentals 的样式层命名很有辨识度几乎所有组件类都以fd-开头。比如按钮是fd-button输入框是fd-input表格是fd-table对话框是fd-dialog。它跟 Bootstrap 的思路类似但没有强行绑定 JavaScript 插件。比如你想做一个强调按钮只需要这样写button classfd-button fd-button--emphasized 保存 /button它内部会处理 Fiori 的圆角、边框、阴影、hover 态和 active 态。你不需要关心:hover时文字颜色怎么变因为样式库里已经定义好了。这种“类名组合”的设计让 Vue 组件封装变得非常轻量。我第一次接入时其实有点不适应因为之前用 Element Plus 或 Ant Design Vue 时按钮是el-button、a-button习惯了组件库的 API。但 Fiori Fundamentals 的样式类不承担行为逻辑它只管“长什么样”。行为、事件、数据绑定都交给前端框架解决。这反而让它在多前端场景下通用性极强因为 React、Vue、原生 HTML 都能使用同一套类名。2.2 图标层一套字体两处使用SAP 的图标体系是企业级后台里比较完整的一套购物车、审批、单据、过账、搜索这类场景都有对应图标。Fiori Fundamentals 把 SAP 图标字体也引入了开源生态在 Vue 项目里可以直接用sap-icon--前缀。大致使用方式是这样i classsap-icon--cart aria-hiddentrue/i如果页面里没有辅助文案最好加上aria-label或者用span包裹一个视觉隐藏文字保证读屏器能识别。这个细节我在后面“体验对齐”部分会再展开。图标字体听起来老派但在企业后台里非常实用。它是纯字体文件没有 SVG 组件树的维护成本切换品牌色也很方便直接继承color。当然你也可以在构建时把图标拆成 SVG但如果是追求“SAPUI5 一致体验”直接用同款字体是最省事的。2.3 主题令牌让颜色、圆角、间距不再各写各的现代视觉一致性最关键的不是组件长得像而是“改一个品牌色全端跟着变”。Fiori Fundamentals 的核心机制是 CSS 变量也就是设计令牌。SAPUI5 的主题引擎里有一组经典变量比如--sapBrandColor代表品牌主色--sapBackgroundColor代表全局背景色--sapButton_Background、--sapButton_BorderColor这些则控制按钮在不同状态下的视觉表现。Fiori Fundamentals 的样式内部大量引用了这类变量所以我们只需要在 Vue 项目的根节点覆盖它们就能让全站视觉跟着变。举个例子:root { --sapBrandColor: #0070f2; --sapLinkColor: #0070f2; --sapBackgroundColor: #f5f6f7; --sapButton_Background: #ffffff; --sapButton_BorderColor: #0070f2; --sapButton_TextColor: #0070f2; }这段代码表面上是几个变量实际作用是所有用到fd-button、fd-input、fd-table的地方只要内部消费了这些变量就会统一换肤。我见过很多团队在 Vue 项目里维护了一套variables.scss又在 SAPUI5 项目里维护另一套主题参数两边颜色经常对不齐。用 Fiori Fundamentals 后两套体系共用同一组 CSS 变量名虽然最终映射路径不完全相同但至少可以在根节点校验谁该是什么色值。这个“对账”成本远比维护两套完全独立的样式系统低。3. 在 Vue3 Vite 项目里把 Fiori Fundamentals 跑起来3.1 安装与入口加载如果你的 Vue 项目是 Vite 构建接入成本其实很低。以基础样式库fundamental-styles为例安装命令只需要npm install fundamental-styles然后在main.js或main.ts里一次性引入样式import fundamental-styles/dist/fundamental-styles.css如果还需要 SAP 主题和图标资源可以再安装配合的主题包但最核心的样式层其实就这一个入口。我建议不要一次性把一堆 SAP 相关依赖全塞进来先跑通样式再按需补图标和主题变量。这里有一个容易被忽略的点Vite 对 CSS 的依赖预构建通常没问题但如果你用 Sass 写业务样式并且希望在业务 SCSS 里引用 Fiori 的变量文件就需要配置css.preprocessorOptions.additionalData把变量文件注入到每个模块。这样做能省去在几百个业务组件里手动import的麻烦。3.2 先搭一个 Fiori 风格的页面壳子接入新样式库时不要一上来就重构整个页面。先做一个最小可用的页面壳子验证整体布局是否正常。Fiori 经典结构是顶部的 Shellbar、中部的页面内容、底部或侧边的导航。用 Fiori Fundamentals 写一个简化版页面壳子大概长这样template div classfd-page header classfd-shellbar div classfd-shellbar__group fd-shellbar__group--start span classfd-shellbar__title采购协同平台/span /div div classfd-shellbar__group fd-shellbar__group--end button classfd-button fd-button--transparent退出/button /div /header section classfd-section div classfd-row div classfd-col fd-col--8 div classfd-panel div classfd-panel__header h2 classfd-panel__title待办审批/h2 /div div classfd-panel__content 内容区域 /div /div /div div classfd-col fd-col--4 div classfd-panel div classfd-panel__header h2 classfd-panel__title关键指标/h2 /div div classfd-panel__content 指标卡片 /div /div /div /div /section /div /template不要在意fd-col--8和fd-col--4是否完全适配小屏先验证它在桌面端的栅格表现。Fiori Fundamentals 默认是 12 列栅格熟悉 Bootstrap 的人很快就能上手。先把页面结构铺出来再逐步往里填具体控件。3.3 把fd-类封装成 Vue 组件直接使用类名写业务代码虽然可行但一旦业务复杂模板里全是classfd-button fd-button--emphasized fd-button--compact这样的长字符串维护起来非常痛苦。我习惯在项目里做一层轻量封装把常用组件包成 Vue 组件业务代码只关心variant、disabled、type这类语义化属性。比如按钮可以这样封装script setup defineProps({ variant: { type: String, default: standard, }, type: { type: String, default: button, }, disabled: Boolean, }) /script template button :typetype :disableddisabled :class[fd-button, fd-button--${variant}] slot / /button /template这样业务里写AppButton variantemphasized保存/AppButton就可以了。需要全局统一修改圆角、补图标、加 loading 态时只需要改这一个组件。输入框的封装比按钮复杂一点因为要兼容v-model。用 Vue 3 组合式 API 可以这样写script setup defineProps({ modelValue: String, }) defineEmits([update:modelValue]) /script template input classfd-input :valuemodelValue input$emit(update:modelValue, $event.target.value) / /template记住这一条凡是封装带表单语义的组件必须处理好modelValue和update:modelValue的透传否则接入表单库时会出现“输入框能显示但表单值始终为空”的诡异 bug。这个问题我在项目里亲眼见过不止一次。3.4 主题切换从光亮到深色企业后台不一定都要求深色模式但一旦要做Fiori Fundamentals 的主题变量优势就体现出来了。只要在根节点切换主题属性CSS 变量就会换一套值。常见的做法是给html或body加上主题标记然后加载对应的主题样式。示例function switchTheme(themeName) { document.documentElement.setAttribute(data-sap-theme, themeName) }对应的 CSS 里可以这样处理:root[data-sap-themedark] { --sapBackgroundColor: #1a1d21; --sapBrandColor: #91c8f6; --sapButton_Background: #2b2f36; --sapButton_BorderColor: #91c8f6; --sapButton_TextColor: #91c8f6; }在我的实际项目里深色模式验收时最容易出问题的不是颜色而是边框和阴影对比度。业务组件如果硬编码了浅色阴影到深色模式下就会显得很突兀。所以团队规范里我始终强调所有颜色、阴影、圆角尽量走设计令牌业务代码里不要出现box-shadow: 0 2px 4px #ccc这种硬编码。4. 从 SAPUI5 迁到 Vue 时最难的不是功能而是体验对齐4.1 先从按钮和输入框开始做映射从 SAPUI5 迁移到 Vue很多人第一反应是“把页面做出来”但真正的难点在于让老用户感觉“还是同一个系统”。SAPUI5 的老用户已经被 Fiori 的交互习惯训练过了按钮的视觉层级、输入框的边框颜色、表格的行高、弹窗的关闭方式这些肌肉记忆不能丢。最直接的方法是建立一张控制映射表把 SAPUI5 控件和 Fiori Fundamentals 的样式类对应起来。我整理过一张常用对照如下业务场景SAPUI5 控件Fiori Fundamentals 对应样式普通按钮sap.m.Buttonfd-button强调按钮sap.m.ButtontypeEmphasizedfd-button--emphasized输入框sap.m.Inputfd-input下拉选择sap.m.Selectfd-select表格sap.ui.table.Tablefd-table对话框sap.m.Dialogfd-dialog对象状态sap.m.ObjectStatusfd-object-status消息提示sap.m.MessageToastfd-message-strip不要试图一天之内把所有控件都映射完。先从高频、低风险的按钮和输入框开始。因为这两个组件几乎出现在每个页面里改完它们用户能明显感受到视觉一致性提升了。4.2 布局Fiori 栅格不是 Bootstrap 栅格SAPUI5 的布局强依赖sap.m.Page、sap.layout.Grid这些容器。起源虽然是响应式设计但它对不同屏幕断点的处理逻辑和 Bootstrap 并不完全一致。迁移到 Vue 后如果你直接用 Element Plus 的栅格去排 Fiori 页面视觉上会对不齐。Fiori Fundamentals 自带的布局栅格采用类似 12 列体系但类名和行为都更贴近 Fiori 自身规范。我的建议是页面级布局沿用 Fiori Fundamentals 的栅格类业务卡片内部再用 Vue 的 flex 布局微调。这样能保证页面骨架和 SAPUI5 页面的观感一致同时又给 Vue 团队留了灵活性。还有一个容易被忽略的地方是fd-section和fd-panel之间的间距。Fiori 对内容区块的 padding 和 margin 有严格控制很多人迁移时把它当成通用 div 处理结果页面显得非常拥挤。最稳妥的做法是先看官方示例里“Page with sections”的模板再依葫芦画瓢。4.3 状态与校验ValueState 的迁移处理SAPUI5 里几乎所有表单控件都有ValueState这个概念用来表达 None、Success、Warning、Error 四种状态。视觉上错误状态通常表现为红色边框和红底消息提示。迁到 Vue 项目后不能只在输入框上简单加一个红色边框因为 Fiori 的校验反馈还包括图标、消息条、读屏器提示。用 Fiori Fundamentals 做错误状态时思路大概是这样的template div input classfd-input is-error aria-invalidtrue aria-describedbyerror-message / div iderror-message classfd-form-message fd-form-message--error 采购数量不能小于 0 /div /div /template这里最关键的细节是aria-invalid和aria-describedby。视觉上红框已经能提示用户出错但读屏器用户需要额外信息知道这个输入框为什么出错、错在哪。很多从 SAPUI5 迁过来的页面视觉做到了无障碍属性却没跟上这会让企业级系统的合规验收出问题。4.4 键盘交互与焦点管理Fiori Fundamentals 的样式层只负责视觉不负责键盘行为。这一点必须提醒所有做迁移的团队成员你以为用了fd-button就有了 Fiori 的完整交互其实焦点管理、Tab 顺序、弹窗关闭 ESC 键、下拉列表的方向键操作全部需要自己写。SAPUI5 控件内部已经把这些行为封装好了用户早就习惯了。迁移到 Vue 后用原生 input 和 button 问题不大浏览器天然支持 Tab 和 Enter。但遇到组合型组件比如下拉选择、日期选择、对话框就需要手工补焦点管理。通常的做法是写一个可复用的useFocusTrap组合函数来处理对话框焦点锁定按下 Tab 时焦点在弹窗内部循环按 ESC 时关闭弹窗并把焦点还原到打开弹窗的按钮上。这个细节不做用户操作一次就会感觉“这个系统不是原来的系统”。5. 让“一致体验”活下去团队协作与演进节奏5.1 先定样式基线再谈组件复用很多团队接完 Fiori Fundamentals 后第一件事就是急着封装一堆公司级组件。我的建议正好相反先用两周时间把样式基线定下来再开始做组件库。样式基线包括四件事主题变量、字体栈、间距体系、圆角规则。把这四样写进项目文档并给出几个必看示例页面。组件库只是基线的“具象化”如果基线没定清楚组件迟早会被各种业务需求带偏。实际项目中我会让一个资浅前端专门维护一个“样式对账表”里面记录 SAPUI5 页面里出现的按钮状态、输入框状态、表格行高、弹窗宽度然后一行一行和 Vue 侧的 CSS 变量对照。这个过程很机械但很有效能逼着团队发现很多“我以为已经对齐了”的差异。5.2 用设计令牌驱动少用硬编码颜色一个最容易被忽视的坑是Fiori Fundamentals 提供了完整的设计令牌但团队在写业务组件时还是会因为“这个按钮颜色比较特殊”而直接写#ff5500。一旦这种硬编码多了设计令牌就形同虚设。在代码评审里我通常会挂一条规则业务样式里不允许直接出现颜色值必须引用 CSS 变量。如果确实是新品牌色先同步设计系统再在全局变量里增加而不是在业务组件里临时写死。这一步会让人觉得繁琐但长期收益很大。等到品牌换色或者要做深色模式时不用再到处找色值所有改动只发生在全局变量文件里。这也是“多前端时代”里最容易达成一致体验的底层机制。5.3 视觉回归测试要放进发布流水线跨团队协作时最怕的是“A 团队改了主题变量B 团队没察觉”。视觉回归测试是最好的保险。不需要一开始就上复杂的 VRT 平台。先用 Playwright 或 Cypress 对几个核心页面做截图定期对比像素差异。页面数量控制在 10 个以内重点覆盖包含多种按钮状态、表单校验、表格、弹窗的高复用页面。只要主题变量或基础样式库版本变化就跑一遍回归脚本能把大部分视觉回归提前拦下。我踩过的一个真实坑是某次升级fundamental-styles后表格行高从 44px 变成了 48px单看表格并不觉得有问题但打印报表时页面多了几页用户立刻发现输出内容不一样了。后来我们才意识到几乎每次基础库升级都会有一些细小的视觉漂移没有自动化截图的团队只能靠用户反馈后知后觉。5.4 渐进式替换旧页面的顺序从 SAPUI5 迁到 Vue如果采用“一口吃成胖子”的节奏十有八九会失败。企业系统页面动辄几十上百个每个页面背后还有复杂的 OData 请求和权限逻辑。推荐顺序是先替换高频且独立的功能页比如“我的待办”、“通知中心”、“通用查询”。这类页面逻辑简单迁移风险低用户接触频率高能快速建立信心。再替换涉及主数据维护的页面这类页面表单比较复杂需要处理校验和状态映射。最后才动跨模块流程页面因为它们往往依赖 SAPUI5 的路由、启动器和全局上下文。替换时还有一个技巧尽可能保留原来的 URL 和路由结构。SAPUI5 的 Hash 路由和 Vue Router 在语义上有差异但用户不关心底层技术他们只关心收藏的链接是否还能打开。前期我建议用一层兼容处理把旧 hash 映射到新路由等用户适应后再逐步清理。我在实际迭代里的最后一个习惯是每次收到视觉走查意见先改设计令牌再改组件组件一次只改一个状态比如先处理 hover 再处理 disabled。这样三个月后SAPUI5 和 Vue 两边的主色、圆角、字体基本能做到像素级一致。这不是靠一次重构而是靠每次小步快跑攒下来的。多前端时代从来不是选边站而是找到一条让所有技术栈都能对同一套设计语言负责的路径Fiori Fundamentals 至少把这条路修通了。
返回列表