上传反馈

Odoo 多附件上传为什么界面不卡死:Overlay、Notification 与文件上传服务的协作边界讲透

Odoo 的上传反馈之所以看起来顺滑,不是因为某个上传框“更高级”,而是因为前端把三件事明确拆开了:`file_upload` service 负责传输和任务状态,`overlay` service 负责临时覆盖层生命周期,`notification` 负责轻量错误与结果反馈。本文基于 web 源码把这三层边界讲透。

前端 框架
进阶 开发者 1 分钟阅读
0 评论 0 点赞 0 收藏 11 阅读

很多系统上传文件时给人的感觉都是一样的:

  • 拖进去;
  • 页面变慢;
  • 某处转圈;
  • 成了或错了再弹一下。

Odoo 的体验通常更顺一些,尤其是在 Kanban、会计看板、拖拽上传这些场景里。原因不在于某一个组件写得多复杂,而在于它前端分层做得比较清楚。

file_upload_service.jsoverlay_service.jsnotification_container.js 再加上 account 侧的上传视图实现连起来看,官方其实把上传反馈拆成了三类问题:

  1. 上传任务本身怎么追踪
  2. 界面上哪些临时覆盖层怎么显示和移除
  3. 错误或结果消息怎么提示给用户

这三件事如果混在一起,就很容易出现“整个页面像被上传逻辑绑架”。

一、file_upload service 负责的是任务状态,不是遮罩也不是提示框

file_upload_service.js 里最核心的是:

  • uploads = reactive({})
  • bus = new EventBus()
  • upload() 返回一个带 progress / state / loaded / total 的任务对象

也就是说,上传服务真正抽象的是:

一批可观察的上传任务。

它关心的事情包括:

  • 传了多少字节;
  • 当前是 pending / loading / loaded / error / abort 哪种状态;
  • 这次上传的标题是什么;
  • 什么时候触发 FILE_UPLOAD_ADDED / LOADED / ERROR 事件。

但它不负责决定:

  • 页面要不要灰掉;
  • 是否弹一个全屏浮层;
  • 错误应该是 toast、dialog 还是静默处理。

这点特别重要,因为很多二开恰恰喜欢把这些都塞进上传服务里,最后服务层越来越像 UI 组件。

二、overlay service 负责的是“临时覆盖内容的生命周期”

overlay_service.js 则是另一套完全不同的抽象。

它维护的是 overlays 这个响应式集合,并通过 add(component, props, options) 返回一个 remove 函数。

这套服务解决的问题是:

  • 某个临时浮层何时加入主界面;
  • 它用哪个 component 渲染;
  • 移除时需不需要跑 onRemove
  • 多个 overlay 的层级顺序怎么排。

也就是说,overlay 关心的是“有一层临时 UI 覆盖在界面上”,而不是“底层上传进度是多少”。

这和 file_upload service 是两个完全不同维度。

一个是任务状态,一个是展示容器。

三、Notification 更轻:它负责的是离散消息,不负责持续状态

NotificationContainer 更容易被忽略,因为看起来很简单:

  • 遍历 notifications;
  • 每条通知用 Notification 组件渲染;
  • 通过 Transition 做出入场动画。

但它边界非常清楚。

Notification 适合承载的是:

  • 上传失败一句话;
  • 某个文件处理结果提示;
  • 一条需要用户知道的轻量反馈。

它不适合承载的是:

  • 长时间持续变化的进度;
  • 复杂交互浮层;
  • 需要自己管理移除时机的大块 UI。

所以 notification 是“消息”,不是“过程”。

四、为什么这三层拆开后,拖拽上传体验会自然很多

以 account 的 FileUploadKanbanRenderer 为例,它在拖文件进来时,会把 dropzoneState.visible 置为 true,并且支持 onPaste() 直接走上传。

注意这里处理的是当前视图对拖拽行为的响应,不是底层上传传输本身。

再看 DashboardKanbanRenderer,它会在 dragenter / dragleave / drop 里维护 dashboardState.isDragging,决定卡片 dropzone 是否该显示。

这再次说明:

  • 拖拽可见态,是视图层状态;
  • 上传进度,是上传服务状态;
  • 错误提示,是通知层状态;
  • 真要弹覆盖层,则交给 overlay service。

职责一分清,视图自然不会被某个全能上传组件绑死。

五、为什么“上传失败弹通知”不代表“通知就是上传状态中心”

file_upload_service.js 里在 onError() 发生时,默认会调用 notificationService.add(...)

很多人看到这段,会误以为 notification 就是上传服务的 UI。

其实不是。

更准确地说,是:

  • 上传服务自己识别到错误;
  • 默认借 notification 给用户一个轻量反馈;
  • 但这只是默认消费方式,不是唯一消费方式。

源码里甚至留了 displayErrorNotification 开关,明确允许你关闭默认通知,改走更显式的错误处理。

这再次证明官方知道:

上传状态源和错误展示渠道,应该解耦。

六、二开时最容易混掉的几个边界

误区 1:把 overlay 当成进度状态仓库

结果就是关闭浮层时,上传状态也跟着丢。

误区 2:把 notification 当持续进度条

通知适合短消息,不适合承载一直跳变的进度过程。

误区 3:在上传服务里硬编码具体界面行为

这样服务就失去跨视图复用价值。

误区 4:视图层直接各自维护一套上传状态

最后列表、看板、拖拽区各有一份,行为一定会漂。

七、一个更稳的实现心智

如果你要扩展 Odoo 上传体验,最稳的思路通常是:

  • 上传服务:只做任务状态、事件和请求;
  • 视图层:只做拖拽区域、进度展示、局部可见态;
  • overlay:处理需要覆盖界面的临时组件;
  • notification:处理离散提示。

一旦按这个心智拆,很多设计题会自动变简单。

例如:

  • “上传时页面是否遮罩?”——这是 overlay 问题;
  • “上传失败怎么提醒?”——这是 notification 问题;
  • “进度从哪来?”——这是 file_upload service 问题;
  • “拖拽进来卡片是否高亮?”——这是当前 renderer 问题。

八、结论

Odoo 上传反馈体验之所以不容易乱,不是因为它有一个“万能上传组件”,而是因为它明确把三类东西拆开了:

  • file_upload service 负责任务状态;
  • overlay service 负责临时覆盖层生命周期;
  • notification 负责离散结果提示。

所以真正值得学的不是某个拖拽样式,而是这条前端分层原则:

持续过程、临时覆盖和瞬时消息,最好永远不要塞进同一层抽象里。

DISCUSSION

评论区

想参与讨论?先 登录 再发表评论。
还没有评论,你可以成为第一个留言的人。