很多人第一次看 Odoo 企业版 home menu,会把它当作“一个更漂亮的应用九宫格”。但 web_enterprise 这套实现真正做的是导航壳层:它既要承接 action service,又要管理应用列表、搜索、拖拽排序、快捷键和订阅状态提示。
一、Home Menu 不是静态页面,而是 action service 的一个 action
home_menu_service.js 里专门定义了 HomeMenuAction,并注册到 actions 分类里。toggle() 也不是简单 show/hide DOM,而是通过 env.services.action.doAction("menu") 与 restore() 切换。
这意味着 home menu 在架构上不是一个悬浮组件,而是WebClient 导航状态的一部分。
二、应用列表不是写死的,而是从 menu tree 动态算出来
HomeMenuAction.homeMenuProps 会从 menu.getMenuAsTree("root") 计算 apps,再根据 user.settings?.homemenu_config 调用 reorderApps() 应用用户排序。也就是说,展示层拿到的是一个已经过 menu tree 计算和用户偏好修饰后的应用集合。
所以拖拽排序真正改变的不是 DOM 顺序,而是用户菜单配置。
三、拖拽、搜索和快捷键共用的是同一个组件状态
home_menu.js 里的 HomeMenu 既维护 focusedIndex、输入框 ref,也通过 useSortable() 支持拖拽排序,通过 hotkey 支持键盘导航。它不是“搜索组件 + 拖拽组件 + 菜单组件”的简单拼装,而是把这几种交互压进同一份状态机。
这也是为什么 home menu 看起来简单,改起来却常常牵一发而动全身:焦点、过滤结果、拖拽启停、移动端差异都互相关联。
四、订阅到期提示是导航壳层的一部分,不是随便插个 banner
ExpirationPanel 直接挂在 HomeMenu 组件里,通过 enterprise_subscription service 决定 alert 类型、文案和注册码提交流程。换句话说,企业订阅状态并不是后台设置页才关心,导航首页本身就是企业版状态感知的一层入口。
这很有意思:Home Menu 同时承担“应用入口”和“实例状态入口”两种职责。
五、新手误区
1. 以为拖拽排序只要前端改数组
实际还要和用户配置、menu helper、action 壳层联动。
2. 以为 home menu 关闭就是隐藏一个组件
源码里是真正恢复之前的 action 背景。
3. 以为到期 banner 是独立小组件
它依赖 enterprise subscription service,并和 Home Menu 生命周期强绑定。
六、实战注意事项
- 改 app 排序逻辑时,优先看
reorderApps()和用户设置,不要只改渲染层; - 键盘导航和移动端触控体验要一起测,Home Menu 明显是双端兼顾的;
- 如果自定义企业订阅提示,尽量保持在 Home Menu service/组件边界内,别散落多个入口;
- 排查“回不到原页面”时,重点看
toggle()和 action restore 分支。
结语
企业版 Home Menu 真正解决的问题,不是“把图标排得更好看”,而是把应用导航、用户偏好、交互状态和企业订阅感知收敛进 WebClient 壳层。理解这层,前端改动才不容易把导航逻辑弄崩。
DISCUSSION
评论区