某数字员工前端项目 前端框架升级总结与复盘
项目:某数字员工前端项目,基于 某前端主项目 / 某前端基础库 二次开发
升级分支:upgrade/framework-825af6
框架基线:6f6370657b,8.1.3-beta
阶段分析点:825af6fe4,2026-07-02
实际追踪点:dcdf661aa,包含上周五前后进入框架主线的前端增量
825af6 前上游差异:1200 个文件,+43182 / -36721
825af6 到 dcdf661aa 追加增量:9 个前端提交,13 个文件,+1825 / -831
升级分支实际落地:39 个提交,362 个文件,+16284 / -6684
配套沉淀:UPGRADE-ANALYSIS.md、UPGRADE-IMPLEMENTATION.md、UPGRADE-CHECKLIST.md
一、摘要
某数字员工前端项目 是基于 某前端主项目 8.1.3-beta 做二次开发的前端项目。二开期间,项目新增了数字员工、流程画布、知识库、技能、工具、资产中心、资源管理、行业首页等业务模块,也删除了 wflow、mobile、file-manager、form-create-ai、form-create-designer 等暂时不用的应用和包。
框架侧在同一段时间继续演进。最初分析时以 825af6fe4 作为阶段点,但实际升级过程中继续核对并吸收了 825af6 之后的前端增量,最终以 dcdf661aa 作为本次上游追踪口径。追踪到 dcdf661aa 不代表把所有上游文件都拷回来。mobile、form-create-ai 等二开已删除应用的增量不回灌;admin 下角色权限、组织账号选择、API 类型等仍在二开保留区内的增量需要处理。
这次升级通过 39 个提交,实际落地 362 个文件,接入框架近两个月的修复和新能力,同时保留二开的业务模块、设计样式、后端契约和运行时约定。
这篇文档按“背景、框架更新、我们做了什么、最终结果、经验复盘”的顺序记录整次升级,方便后续项目复用。
二、项目背景
2.1 二开项目从哪里来
项目最初从 某前端主项目 的 8.1.3-beta,也就是 commit 6f6370657b,整体拷贝出来做二开。它不是一个标准 fork,也不是长期同步上游的分支,而是从框架某个发布点拷贝后独立演进。
二开期间,项目新增了大量业务代码:
| 类别 | 内容 |
|---|---|
| 核心业务 | 数字员工、流程画布、知识库、技能、工具、资产中心、资源管理。 |
| 首页和业务入口 | 行业首页、数字员工广场、资产广场等。 |
| 业务依赖 | vue-flow、element-ai-vue、codemirror、marked、markdown-it、lucide-vue-next 等。 |
| 业务 API | digital-employee、chat、skill、knowledge、process、asset-center、tool、model-config、token-usage 等。 |
二开也主动删除了一些框架应用和包:
| 删除内容 | 处理原则 |
|---|---|
| wflow | 不恢复。框架侧有 JS 到 TS 的大重构,但二开业务暂不需要。 |
| mobile | 不恢复。二开已删除移动端应用。 |
| file-manager | 不恢复。 |
| form-create-ai | 不恢复。 |
| form-create-designer | 不恢复。 |
所以这次升级不是“上游有什么就拿什么”。每个文件都要先判断它是否属于当前二开项目的边界。
2.2 框架侧发生了什么
框架从 8.1.3-beta 到 825af6fe4 已经有 1200 个文件的上游差异。825af6 之后,框架前端又继续有增量,直到 dcdf661aa。后续增量里,admin 相关内容仍然对二开有价值,尤其是角色权限改造。
825af6 之后的前端增量大致如下:
| 区域 | 文件数 | 是否回灌 | 说明 |
|---|---|---|---|
| apps/admin | 7 | 是,按二开边界合并 | 角色权限、组织账号选择、菜单类型、角色 API 等。 |
| apps/mobile | 2 | 否 | 二开已删除 mobile。 |
| apps/form-create-ai | 4 | 否 | 二开已删除 form-create-ai。 |
这也是为什么文档不能只写“升级到 825af6”。825af6 是分析阶段的重要节点,但不是实际吸收框架前端增量的终点。
三、这次框架更新了什么
3.1 基础设施和 packages 层
框架在 internal、packages、component-ui、基础UI包、@core ui-kit、effects 等区域都有更新。这里的改动不一定直接表现为业务页面变化,但会影响构建、布局、基础组件和生产构建产物。
| 区域 | 更新内容 |
|---|---|
| internal | tailwind 扫描、vite importmap 等构建链更新。 |
| component-ui | 表格、表单、业务卡片、状态组件、搜索、导出按钮等基础业务组件更新。 |
| 基础UI包 | 基础 UI 组件更新。 |
| @core ui-kit | 菜单、布局、弹窗、tabs、shadcn 组件更新。 |
| effects | 布局 widgets、request-client、hooks、plugins 等更新。 |
| design-tokens | 框架新增一批语义色和主题变量。 |
其中 table 组件是一个典型例子。框架版本比二开版本多出 reserveSelection、横向溢出检测、卡片视图、布局切换等能力。它不能简单覆盖,也不适合普通三方合并,而是要以框架新版为底,再加回二开的少量默认偏好。
3.2 admin 系统模块
admin 是二开保留的主应用,也是这次升级的主战场。框架在 sys 模块里更新了大量页面和能力。
| 模块 | 框架更新 |
|---|---|
| 角色权限 | 从旧权限模式升级到应用树、菜单树、接口表、权限配置弹窗,权限粒度细到 interfaceId。825af6 之后又补了搜索、批量操作和样式优化。 |
| 账号管理 | 头像、初始密码、扩展属性、租户参数等更新。 |
| 组织管理 | 新增组织扩展属性管理,列表列宽优化。 |
| 租户管理 | tenantId 参数透传、组织树筛选、扩展属性动态列等。 |
| LLM 管理 | 大文件拆分为主页面和子组件。 |
| 邮件短信 | 新增聚合页,更新账号、模板、日志、黑白名单、限流等子页面。 |
| 字典、岗位、栏目、菜单、图标、数据接口、定时任务、印章、通知等 | 多个 P1 修复和 P2 增强。 |
这些更新有些可以直接接收,有些要和二开定制合并,有些则因为二开已有产品决策而不能接收。
3.3 运行时机制
框架还涉及一些跨文件机制:
| 机制 | 框架变化 | 二开处理 |
|---|---|---|
| 菜单换肤 | store 字段、布局渲染、sys-config 回填、配置表单形成完整链路。 | 接入并启用,但保留二开偏好面板裁剪。 |
| 通知中心 | 多数页面是净化和样式调整。 | 页面改动可合并,顶栏铃铛继续关闭。 |
| WebSocket | 框架有初始化和清理逻辑。 | 二开刻意关闭,继续保持关闭。 |
| icons | 框架维护 gen:icons 和 icons-builtin.json。 | 二开不恢复 gen:icons,继续使用在线 iconify。 |
| wujie-submenu | 框架传 applicationId。 | 二开保留 initialPath,这是已部署子应用的运行时契约。 |
四、升级难点在哪里
4.1 框架和二开同时改了同一批文件
如果一个文件只有框架改、二开没改,处理很简单,确认边界后覆盖即可。如果一个文件只有二开改、框架没改,也可以保留二开。但这次最麻烦的是第三种情况:框架和二开都改过。
这类文件必须看三方版本:
| 版本 | 含义 |
|---|---|
| base | 6f6370657b,也就是二开拷贝出来时的框架版本。 |
| ours | 某数字员工前端项目 当前二开版本。 |
| theirs | 框架目标版本,按本次追踪口径到 dcdf661aa。 |
判断逻辑是:以框架新版为底,把二开相对 base 的业务、样式、契约重新加回去。
4.2 二开资产很多,不能被框架覆盖
这次必须保护的二开资产包括:
| 资产 | 示例 |
|---|---|
| 业务模块 | 数字员工、流程画布、知识库、技能、工具、资产中心、资源管理。 |
| 业务 API | digital-employee、chat、skill、knowledge、process 等。 |
| 设计样式 | packages/styles、design-tokens 主色、各页面 bg-background、状态标签风格。 |
| 后端契约 | skipAuth、successCode 兼容、token 失效兜底、登录接口传参。 |
| 运行时约定 | DEFAULT_LOGIN_REDIRECT_PATH、wujie initialPath、WebSocket 关闭、通知铃铛关闭。 |
这些内容很多在代码上不会产生冲突标记。最危险的覆盖往往是“干干净净地覆盖成功”,然后运行时才发现业务错了。
4.3 一些功能跨文件强耦合
登录、菜单、跳转是一条强耦合链。DEFAULT_LOGIN_REDIRECT_PATH 只存在于二开,框架没有。它牵涉 constants、guard、auth store、SSO、login-form、routes、access 等多个文件。
WebSocket、wujie、icons 也是类似问题。单独看每个文件都像小改动,合起来才知道它们是运行时契约。
五、我们做了什么
5.1 先做差异分析
升级前先产出 UPGRADE-ANALYSIS.md,对框架差异、二开差异、冲突文件、风险等级做分类。
核心分类如下:
| 档位 | 区域 | 处理方式 |
|---|---|---|
| 零冲突区 | internal、packages 未碰文件、根配置 | 确认二开没动过后,取框架新版。 |
| 三方合并区 | packages 冲突文件、apps/admin 冲突文件 | 逐文件三方合并。 |
| 决策区 | 依赖、图标机制、菜单换肤、通知、被删 app 等 | 先定策略,再合并。 |
这个阶段的价值是把升级从“凭感觉合文件”变成“按分类执行”。
5.2 再写实施手册
UPGRADE-IMPLEMENTATION.md 把升级拆成 S0 到 S8。
| 阶段 | 内容 |
|---|---|
| S0 | 准备与备份。 |
| S1 | 根工作区配置和依赖。 |
| S2 | internal 构建链。 |
| S3 | packages 零冲突文件覆盖。 |
| S4 | admin 轻度文件。 |
| S5 | packages 布局、菜单、主题链路。 |
| S6 | admin 中等文件。 |
| S7 | admin 重灾区文件。 |
| S8 | 全量回归。 |
实施手册不是简单步骤列表,它还记录了样式铁律、后端契约铁律、机制保护铁律、登录强耦合链、table 组件专项等内容。
5.3 最后做执行 Checklist
UPGRADE-CHECKLIST.md 把关键约束变成执行时的勾选项。它解决的是“人会忘”的问题。
比如:
- packages/styles 不能覆盖;
- build/dev 不能恢复 gen:icons 前置脚本;
- bootstrap.ts 不能带回 initBuiltinIcons;
- auth.ts 不能恢复 WebSocket connect;
- wujie-submenu 不能把 initialPath 改成 applicationId;
- login 目录不能整目录覆盖;
- basic.vue 不能恢复通知铃铛;
- table 组件要单独处理。
5.4 按模块分批提交
实际执行中,升级分支相对 dev 产生了 39 个提交。提交信息统一是“升级框架前端@other”,每个提交尽量对应一组模块或一组风险相近的文件。
实际推进顺序大致如下:
| 顺序 | 内容 |
|---|---|
| 1 | internal 构建链。 |
| 2 | packages 基础覆盖。 |
| 3 | admin 轻度文件。 |
| 4 | packages 布局菜单合并。 |
| 5 | 登录和基础布局。 |
| 6 | table 组件专项。 |
| 7 | 系统配置、资源分配等重灾区。 |
| 8 | 角色权限、LLM、邮件短信、扩展属性。 |
| 9 | dict、column、post、icon、menu、user、tenant、template-config、data-interface、quartz、seal、notice、msg 等后续模块。 |
分批提交的目的是降低回滚成本。升级不能做成一个大提交,否则出了问题只能整支回退。
六、最终升级结果
6.1 版本和范围
本次升级不是止于 825af6。实际处理时继续核对 825af6 之后的框架前端增量,并以 dcdf661aa 作为最终追踪口径。
| 口径 | 数量 | 说明 |
|---|---|---|
| beta 到 825af6 | 1200 文件 | 框架阶段差异。 |
| 825af6 到 dcdf661aa | 13 文件 | 追加前端增量,admin 相关 7 个文件需要处理。 |
| 升级分支实际落地 | 362 文件 | 二开项目最终接收、合并或新增的文件。 |
6.2 已接入的框架能力
| 能力 | 结果 |
|---|---|
| 角色权限接口维度改造 | 已合入,包含 interfaceId 维度、权限配置弹窗、后续搜索和样式增量。 |
| LLM 管理拆分 | 已合入,采用框架拆分后的组件结构。 |
| 邮件短信聚合页 | 已合入 manage 和 strategy 聚合页及子页面更新。 |
| 账号和组织扩展属性 | 已合入相关 API、类型和组件。 |
| 租户参数透传 | 已合入查询、删除、重置密码、导入导出等链路。 |
| 菜单换肤 | 已按 store、渲染、消费、回填、配置入口链路接入。 |
| component-ui / 基础UI包 基础更新 | 已选择性合入。 |
| sys 模块 P1 / P2 更新 | 已按模块分批合入。 |
6.3 已保留的二开资产
| 资产 | 结果 |
|---|---|
| 数字员工、流程画布、知识库、技能、工具、资产、资源等业务模块 | 保留。 |
| 业务 API 模块 | 保留。 |
| packages/styles 和二开样式 | 保留。 |
| 主题主色 hsl(217 86% 54%) | 保留,同时补框架新语义色。 |
| skipAuth 请求链路 | 保留。 |
| DEFAULT_LOGIN_REDIRECT_PATH | 保留。 |
| wujie initialPath | 保留。 |
| WebSocket 关闭状态 | 保留。 |
| 通知铃铛关闭状态 | 保留。 |
| 偏好面板裁剪 | 保留。 |
6.4 明确不回灌的内容
| 内容 | 原因 |
|---|---|
| wflow | 二开已删除,业务不需要。 |
| mobile | 二开已删除。 |
| file-manager | 二开已删除。 |
| form-create-ai | 二开已删除。 |
| form-create-designer | 二开已删除。 |
| gen:icons 脚本链 | 二开使用在线 iconify,不恢复本地图标生成。 |
| 顶栏通知铃铛 | 二开产品决策是关闭。 |
| 框架默认 WebSocket 连接 | 二开刻意关闭。 |
七、关键决策
这 15 个决策贯穿整个升级过程。
| 编号 | 决策 | 结论 |
|---|---|---|
| D1 | element-plus 版本 | 保留二开的 ^2.14.1。 |
| D2 | lefthook | 不恢复。 |
| D3 | gen:icons | 不恢复。 |
| D4 | stores 菜单换肤能力 | 并入并启用。 |
| D5 | apps/demo | 保持现状。 |
| D6 | 被删 app 和 package | 不恢复。 |
| D7 | 租户文件分叉 | 保留 employee-open.vue。 |
| D8 | 组织管理新功能 | 接受。 |
| D9 | 通知中心 | 合并无害净化改动,继续关闭铃铛。 |
| D10 | 二开样式 | 最严格保留。 |
| D11 | design-tokens | 保主色,补语义色。 |
| D12 | 换肤后端 API | 后端已就绪。 |
| D13 | wujie-submenu | 保留 initialPath。 |
| D14 | WebSocket | 保持关闭。 |
| D15 | 偏好面板 | 保留裁剪。 |
这些决策要写在执行文档最前面。执行到具体冲突文件时,人会自然倾向于解决眼前冲突,而忘掉全局约束。
八、实施过程
8.1 基础层先行
先处理 internal 和 packages。internal 里 importmap 可直接跟框架,tailwind-config 要保留二开的扫描过滤逻辑。packages 里要排除 styles、design-tokens、table、preferences、request-client 契约文件和布局菜单冲突文件。
覆盖 packages 后还有一个容易忽略的问题:生产构建可能读 dist,而 dev 读 src。覆盖 src 后如果不重建内部包,生产产物可能仍然用旧 dist。
8.2 再处理 admin 轻度文件
admin 轻度文件包括 .env、bootstrap.ts、wujie-submenu、tenant API、sys-config API、router/access 等。这类文件差异不大,但陷阱很多。
几个关键点:
- bootstrap.ts 不能带回 initBuiltinIcons;
- wujie-submenu 保留 initialPath;
- .env 保留二开标题和环境配置;
- router/access 要保留二开的动态菜单和应用路径规则;
- api/request.ts、api/login/index.ts、store/auth.ts 不作为普通轻度文件处理,它们属于后端契约保护范围。
8.3 packages 布局菜单单独合并
menu.vue、layout-sidebar.vue、vben-layout.vue、user-dropdown.vue 不能简单覆盖。它们同时涉及框架新能力和二开视觉样式。
处理方式是:逻辑和模板按三方合并,style 块以二开为准。菜单换肤链路在这一阶段开始接入,但完整端到端还依赖 admin/basic.vue 和 sys-config/base-form.vue。
8.4 admin 重灾区后置
login/index.vue、login-form.vue、layouts/basic.vue、sys-config/base-form.vue、resource-assign/detail/index.vue 都是重灾区。它们不适合依赖自动合并。
处理原则是:先列约束,再合并代码。
比如 basic.vue 必须同时满足:
- 不恢复通知铃铛;
- 不恢复 WebSocket 初始化;
- 接入菜单换肤回填;
- 保留二开 style 块。
九、重点模块处理
9.1 table 组件
table 组件以框架新版为底,补回二开的少量偏好。这样可以拿到 reserveSelection、横向溢出检测、卡片视图、布局切换等框架能力,同时保留二开默认表格风格。
9.2 登录、菜单、跳转
这是最高风险链路。DEFAULT_LOGIN_REDIRECT_PATH 只在二开存在,框架没有。它牵涉 constants、guard、auth store、SSO、login-form、routes、access 等文件。
这组文件要整体看,不能按文件大小分散处理。框架值得 cherry-pick 的内容包括 guard.ts 的 runWhenIdle 延迟加载优化、access.ts 的 resolveMenuIcons 图标转换等。二开的登录跳转、skipAuth、WebSocket 关闭必须保留。
9.3 角色权限
角色权限接收了框架的接口维度权限改造。825af6 之后的增量也在这里体现,包括 interface-definition.vue、role-permission.vue、role/index.vue、api/role/index.ts、api/menu/types.ts 等文件的更新。
验证时要走完整闭环:角色列表、搜索、删除约束、权限配置弹窗、接口权限保存、菜单权限保存。
9.4 LLM、邮件短信、扩展属性
LLM 采用框架拆分后的组件结构,再补二开背景样式。邮件短信接入聚合页和子页面更新。扩展属性要同步组件、API、类型定义,不能只拷页面。
9.5 租户和组织
租户是三方合并难度最高的一组之一。框架新增 tenantId 透传、组织树筛选、扩展属性动态列,二开有 employee-open.vue、详情自动打开、导入导出和状态标签定制。最终做法是接收框架能力,同时保留二开分叉和样式。
组织管理二开改动少,框架新增组织扩展属性管理,按决策接收。
9.6 P1 / P2 sys 模块
后半段处理了字典、栏目、岗位、图标、菜单、数据接口、定时任务、印章、通知、消息、敏感词、日志等模块。
这些模块的共同点是:单个文件不一定大,但数量多,适合按目录和模块分批合入。每个模块合完后做局部冒烟,避免等到最后全量回归才发现问题。
十、踩坑记录
10.1 bg-background 容易丢
二开页面约定在 MainContainer 内包一层 bg-background。框架新版经常没有这层。丢了以后页面不报错,但背景、留白和卡片层次会不对。每个页面覆盖后都要查这一层。
10.2 状态标签容易被替换
框架新增了 BusinessStatusBadge,很多列表状态列会被替换。二开已有 StatusTag、Tag、GenderTag 等组件。状态列是视觉风格的一部分,不能简单跟框架。
10.3 二开主动删掉的卡片会回来
角色、字典等页面里,二开曾经主动删除统计卡片。框架新版还保留这些卡片。整文件覆盖会让卡片重新出现。遇到这种情况要回看二开历史,确认删除是不是产品决策。
10.4 类型错误总数没有参考价值
项目本来就有存量类型错误。如果只看 vue-tsc 总错误数,很难判断升级有没有引入新问题。更有效的做法是按本次改动模块过滤,只看相关文件有没有新增错误。
10.5 dist 旧产物会误导生产构建
packages 里有些包 dev 读 src,生产构建读 dist。覆盖 src 后如果不重建内部包,build 可能仍用旧 dist。尤其 design 包,需要单独确认构建产物。
10.6 日期过滤会漏样式
packages/styles 的定制提交比较早,用 git log --since 会漏。判断样式改动只能看文件内容 diff。
10.7 范围容易扩散
升级过程中很容易看到相似问题就顺手改。比如本来只处理字典样式,却扫到其他模块的状态组件。这样会扩大风险。每次操作都要限定范围,说改 A 就只改 A。
十一、验证口径
升级验证不能只跑一个命令。
| 验证层级 | 要看什么 |
|---|---|
| install | pnpm install 后确认 element-plus、element-ai-vue、vue-flow、marked 等版本符合预期。 |
| typecheck | 不只看总数,要过滤本次改动路径。 |
| dev 启动 | 登录、菜单、布局、user-dropdown、tab、动态路由都要走一遍。 |
| build | 先重建内部包 dist,再跑 admin 构建。 |
| 视觉 | 主色、圆角、按钮、表格、滚动条、页面背景要和二开设计稿一致。 |
| 业务 | 数字员工、流程画布、知识库、技能、工具、资产、资源、sys 各模块至少做冒烟。 |
| 契约 | skipAuth、DEFAULT_LOGIN_REDIRECT_PATH、wujie initialPath、WebSocket 关闭、通知铃铛关闭都要确认。 |
验证失败时优先按提交粒度回滚。39 个提交的价值就在这里:它们把一次大升级拆成了能定位、能回退的小块。
十二、方法沉淀
这次升级沉淀出一套可以复用的方法。
| 方法 | 用途 |
|---|---|
| 三方分类 | 输入 base、ours、theirs,把文件分成 zero、custom、missing、equal。 |
| 决策先行 | 对依赖、被删应用、通知、WebSocket、图标、样式先拍板。 |
| 样式块核对 | Vue 文件逻辑正常合并,style 块单独判断。 |
| 强耦合组检查 | 登录、菜单、跳转、WebSocket、wujie、icons 不按单文件孤立处理。 |
| 模块级提交 | 每个提交尽量能独立验证和回滚。 |
| 分层验证 | install、typecheck、dev、build、视觉、业务、契约分开看。 |
这套方法已经沉淀为 framework-merge-upgrade skill。后续类似二开项目升级框架,可以先用这套方法做分析和执行计划。
十三、总结
这次升级最核心的经验是:框架升级不是把新代码拉下来,而是在三个版本之间做判断。
base 告诉我们二开从哪里开始。theirs 告诉我们框架后来改了什么。ours 告诉我们二开已经形成了哪些业务约定。合并的工作,就是把框架新版作为底子,把二开的业务、样式、后端契约和运行时约定重新放回去。
判断比合并更重要。判断错了,合得越快,返工越多。
这次升级完成后,项目接入了框架近两个月的修复和新能力,包括接口级权限、角色权限搜索和批量操作、LLM 管理拆分、扩展属性、菜单换肤、邮件短信聚合页、多个 sys 模块优化等。同时,二开的数字员工、流程画布、知识库、技能、工具、资产、资源、设计样式、后端契约和运行时约定继续保留。
下一次再做类似升级,先不要急着拷文件。先列清楚三件事:框架能带来什么,二开绝对不能丢什么,哪些文件看似独立但实际绑在一起。把这三件事讲清楚,升级就已经成功了一半。