From c3e8e2519493cfbf6878bb175a4e0cb622e022f7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=8E=E9=B9=8F=E5=AE=87?= Date: Mon, 10 Aug 2026 15:08:40 +0800 Subject: [PATCH] =?UTF-8?q?=E5=BE=85=E5=8A=9E=E7=9C=8B=E6=9D=BF=E5=8B=BE?= =?UTF-8?q?=E9=80=89=E6=A1=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- soong_workflow/__manifest__.py | 2 + .../static/src/kanban_select/kanban_select.js | 131 ++++++++++++++++++ .../src/kanban_select/kanban_select.scss | 15 ++ .../soong_workflow_batch_approval_views.xml | 16 ++- 4 files changed, 161 insertions(+), 3 deletions(-) create mode 100644 soong_workflow/static/src/kanban_select/kanban_select.js create mode 100644 soong_workflow/static/src/kanban_select/kanban_select.scss diff --git a/soong_workflow/__manifest__.py b/soong_workflow/__manifest__.py index 7878ddfef..17e896081 100644 --- a/soong_workflow/__manifest__.py +++ b/soong_workflow/__manifest__.py @@ -56,6 +56,8 @@ 'soong_workflow/static/src/js/urgency_level_widget.xml', 'soong_workflow/static/src/diagram_plus_dialog/*', 'soong_workflow/static/src/wkf_record_list/*', + 'soong_workflow/static/src/kanban_select/kanban_select.js', + 'soong_workflow/static/src/kanban_select/kanban_select.scss', ], }, "installable": True, diff --git a/soong_workflow/static/src/kanban_select/kanban_select.js b/soong_workflow/static/src/kanban_select/kanban_select.js new file mode 100644 index 000000000..7c0ffefa0 --- /dev/null +++ b/soong_workflow/static/src/kanban_select/kanban_select.js @@ -0,0 +1,131 @@ +/** @odoo-module **/ + +import { kanbanView } from "@web/views/kanban/kanban_view"; +import { KanbanRenderer } from "@web/views/kanban/kanban_renderer"; +import { KanbanController } from "@web/views/kanban/kanban_controller"; +import { useService } from "@web/core/utils/hooks"; +import { registry } from "@web/core/registry"; +import { onMounted, onWillDestroy } from "@odoo/owl"; + +// 跨组件共享的勾选状态:key=记录 resId,value=true 表示已勾选 +// 放在模块级,renderer(写)与 controller(读)共用,且翻页/卡片重建不丢 +const kanbanSelection = { selected: {} }; + +// 清空勾选:重置状态对象 + 取消所有卡片 checkbox 的勾选 +function clearKanbanSelection() { + kanbanSelection.selected = {}; + document + .querySelectorAll(".wkf_card_check") + .forEach((el) => (el.checked = false)); +} + +// ----------------------------------------------------------------------------- +// 自定义 Renderer:在卡片上挂勾选框,并维护勾选状态 +// ----------------------------------------------------------------------------- +export class KanbanSelectRenderer extends KanbanRenderer { + setup() { + super.setup(); + this._onCardCheck = this._onCardCheck.bind(this); + // 在 kanban 渲染根节点上做事件委托,监听勾选框的点击与变更 + onMounted(() => this._bindCheckEvents()); + onWillDestroy(() => this._unbindCheckEvents()); + } + + _bindCheckEvents() { + const el = this.rootRef && this.rootRef.el; + if (!el) { + return; + } + // click 用捕获阶段拦截:卡片的打开记录是绑在卡片元素上的冒泡监听, + // 必须在事件到达卡片前 stopPropagation,否则勾选框一点就打开记录详情 + el.addEventListener("click", this._onCardCheck, true); + el.addEventListener("change", this._onCardCheck); + } + + _unbindCheckEvents() { + const el = this.rootRef && this.rootRef.el; + if (!el) { + return; + } + el.removeEventListener("click", this._onCardCheck, true); + el.removeEventListener("change", this._onCardCheck); + } + + _onCardCheck(ev) { + const target = ev.target; + // 只处理勾选框 + if (!target || !target.classList || !target.classList.contains("wkf_card_check")) { + return; + } + // 阻止冒泡:避免触发卡片的 oe_kanban_global_click 打开记录详情 + ev.stopPropagation(); + if (ev.type !== "change") { + return; + } + const id = target.dataset.id; + if (!id) { + return; + } + if (target.checked) { + kanbanSelection.selected[id] = true; + } else { + delete kanbanSelection.selected[id]; + } + } +} + +// ----------------------------------------------------------------------------- +// 自定义 Controller:拦截「一键审批」按钮,有勾选则只批勾选的、无勾选则批全部 +// ----------------------------------------------------------------------------- +export class KanbanSelectController extends KanbanController { + setup() { + super.setup(); + // 用于直接 rpc 调用现有 action_batch_approve 方法 + this.orm = useService("orm"); + } + + async beforeExecuteActionButton(clickParams) { + // 只拦截「一键审批」按钮(其 name 指向 action_batch_approve_all) + if (clickParams.name === "action_batch_approve_all") { + const ids = Object.keys(kanbanSelection.selected) + .map(Number) + .filter((id) => kanbanSelection.selected[id]); + if (ids.length) { + // 有勾选:自己调用 action_batch_approve(选中的 ids) 复用现有逐条引擎,并拦截默认全批 + try { + const result = await this.orm.call( + "soong.workflow.record", + "action_batch_approve", + [ids], + { context: this.props.context } + ); + if (result) { + await this.actionService.doAction(result); + } + } catch (e) { + await this.actionService.doAction({ + type: "ir.actions.client", + tag: "display_notification", + params: { + type: "danger", + message: (e && e.message) || "批量审批失败", + sticky: true, + }, + }); + } + clearKanbanSelection(); + // 重新加载视图,已批记录从待办消失 + await this.model.root.load(); + return false; // 返回 falsy 取消默认 action_batch_approve_all 执行 + } + // 无勾选:放行默认逻辑(action_batch_approve_all 批当前用户全部待办加班) + } + return true; + } +} + +registry.category("views").add("soong_workflow_kanban_select", { + ...kanbanView, + Controller: KanbanSelectController, + Renderer: KanbanSelectRenderer, +}); diff --git a/soong_workflow/static/src/kanban_select/kanban_select.scss b/soong_workflow/static/src/kanban_select/kanban_select.scss new file mode 100644 index 000000000..e804b05f9 --- /dev/null +++ b/soong_workflow/static/src/kanban_select/kanban_select.scss @@ -0,0 +1,15 @@ +// 看板卡片左上角勾选框样式(一键审批用) +.oe_kanban_card { + position: relative; // 作为勾选框绝对定位的参照 +} + +.wkf_card_check { + position: absolute; + top: 6px; + left: 6px; + width: 18px; + height: 18px; + z-index: 5; + cursor: pointer; + accent-color: #714b67; // 与 Odoo 主题紫色协调 +} diff --git a/soong_workflow/views/soong_workflow_batch_approval_views.xml b/soong_workflow/views/soong_workflow_batch_approval_views.xml index a774b855d..21f01ff7e 100644 --- a/soong_workflow/views/soong_workflow_batch_approval_views.xml +++ b/soong_workflow/views/soong_workflow_batch_approval_views.xml @@ -15,18 +15,28 @@ - + soong.workflow.record.kanban.batch.all soong.workflow.record + + + soong_workflow_kanban_select + + + + + +
-