看板拖拽、可排序列表、文件投放区,是现代 Web 交互的标配。Odoo 虽自带看板,但自定义仪表板、排程控件、POS 界面仍需自行实现拖拽。Odoo 19 / OWL 3 可直接使用浏览器原生 HTML5 Drag and Drop API,不必引入第三方库。
对照:How to Implement Drag-and-Drop in OWL Components。下文用「待办 / 已完成」任务板示例完整说明。
一、组件职责
OWL 组件维护响应式任务列表;卡片拖到新列时先更新本地状态(界面瞬时反馈),再通过 ORM write 回写阶段;失败则回滚本地状态,保证与服务器一致。
二、关键事件
dragstart:记录被拖卡片 ID,写入dataTransferdragenter/dragleave:高亮目标列dragover:必须preventDefault(),否则无法触发 dropdrop:读取 ID,改 stage,调用orm.write
三、实现要点(JS)
onDragStart(ev, taskId) {
this.state.activeDragCardId = taskId;
ev.dataTransfer.setData("text/plain", String(taskId));
ev.dataTransfer.effectAllowed = "move";
}
async onDrop(ev, targetColumn) {
ev.preventDefault();
const taskId = parseInt(ev.dataTransfer.getData("text/plain"));
const task = this.state.tasks.find(t => t.id === taskId);
if (!task || task.stage === targetColumn) return;
const prev = task.stage;
task.stage = targetColumn; // OWL 立即重绘
try {
await this.orm.write("project.task", [taskId], {
stage_id: targetColumn === "done" ? 3 : 1,
});
} catch (e) {
task.stage = prev; // 失败回滚
}
}
四、模板绑定
列容器绑定 dragenter/dragover/dragleave/drop;卡片 draggable="true" 并绑定 dragstart。可用 class 在拖入时加边框高亮。
五、最佳实践
- dragover 必须 preventDefault,否则 drop 不触发。
- 写库包 try/catch,校验失败或锁冲突时回滚 UI。
- 用响应式状态做视觉反馈,拖拽中勿做重计算绑定,保持流畅。
六、适用场景
自定义看板、仓拣规划、生产排程、POS 单据整理、仪表板卡片重排、文件投放区等。
七、FAQ
为何用原生 API?无额外依赖,与 OWL 契合,多数 Odoo 场景足够。
为何 dragover 要 preventDefault?浏览器默认禁止放置,需显式允许。
UI 如何自动更新?useState 列表变更后 OWL 细粒度补丁 DOM。
中国Odoo网原创中文改写|保留事件流、代码要点、最佳实践与 FAQ。Odoo用户手册 · 中文文档 · 实操。