From 0c1f630dcac5e4f6c4db2b2cea92ce14a938ed95 Mon Sep 17 00:00:00 2001 From: wangjia <809946525@qq.com> Date: Tue, 30 Jun 2026 09:04:42 +0800 Subject: [PATCH] =?UTF-8?q?=E5=89=8D=E7=AB=AF=EF=BC=9A=E4=BB=BB=E5=8A=A1?= =?UTF-8?q?=E8=AF=A6=E6=83=85=E5=B1=95=E7=A4=BA=E5=AE=A1=E6=89=B9/?= =?UTF-8?q?=E9=A9=B3=E5=9B=9E=E8=AE=B0=E5=BD=95=20+=20=E4=BB=BB=E5=8A=A1?= =?UTF-8?q?=E8=A1=8C=20ID=20=E4=B8=8D=E8=A2=AB=E9=95=BF=E6=A0=87=E9=A2=98?= =?UTF-8?q?=E6=8C=A4=E6=8E=89=20+=20i18n=20=E4=BA=94=E8=AF=AD=20(tsk=5FEmZ?= =?UTF-8?q?6vjtVbrOk)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - TaskDetail 渲染 _raw.approvals:闸名·通过/驳回 + 审批人 + 时间, 驳回用红色边框+红底高亮块突出 reason(t.rejectReason 标签), 通过用绿色、reason 走低调 ↳ 行;无审批记录不渲染该分区。 - 安全:reason/actor/gate 全部走 React 文本节点(默认转义), 不经 MaestroRichDoc/innerHTML,避免同源存储型 XSS。 - TaskRow:taskid 拆为独立 flex:none span,title 加 minWidth:0, 长标题省略号截断时 id 完整不被挤掉;ArchiveRow 同步处理保持一致。 - i18n:zh/en/es/ja/fr 五语新增 rejectReason 键。 Co-Authored-By: Claude Opus 4.8 --- design/ui_kits/console/ArchiveSection.jsx | 5 ++- design/ui_kits/console/TaskTree.jsx | 45 ++++++++++++++++++++++- design/ui_kits/console/i18n.js | 10 ++--- 3 files changed, 51 insertions(+), 9 deletions(-) diff --git a/design/ui_kits/console/ArchiveSection.jsx b/design/ui_kits/console/ArchiveSection.jsx index b644475..106694c 100644 --- a/design/ui_kits/console/ArchiveSection.jsx +++ b/design/ui_kits/console/ArchiveSection.jsx @@ -11,9 +11,10 @@ function ArchiveRow({ item, t, onOpen }) { borderBottom: '1px dashed var(--line-soft)', background: hover ? 'var(--panel)' : 'transparent', }}> - - {item.title}{item.id} + + {item.title} + {item.id} {item.subs ? {t.archiveSubs.replace('{n}', item.subs)} : null} diff --git a/design/ui_kits/console/TaskTree.jsx b/design/ui_kits/console/TaskTree.jsx index e65e49f..7a301c6 100644 --- a/design/ui_kits/console/TaskTree.jsx +++ b/design/ui_kits/console/TaskTree.jsx @@ -227,9 +227,10 @@ function TaskRow({ task, depth, ctx, expandedId, setExpandedId, openIds, toggleO }}> {kids.length ? '▶' : '·'} - - {task.title}{task.id} + + {task.title} + {task.id} {task.deps ? ( @@ -384,6 +385,46 @@ function TaskDetail({ task, t, byId, onJump, onTakeover, taskOps }) { ) : null} + {/* 审批记录:读 _raw.approvals,按记录列出闸名·通过/驳回 + 审批人 + 时间;驳回突出 reason。 + 安全:reason/actor/gate 全部走 React 文本节点(默认转义),绝不经 MaestroRichDoc / innerHTML,避免同源存储型 XSS。 */} + {(() => { + const approvals = (task._raw && task._raw.approvals) || []; + if (!approvals.length) return null; + return ( +
+
{t.approvalsLabel}
+
+ {approvals.map((a, i) => { + const rej = a.action === 'reject'; + const at = (a.at || '').replace('T', ' ').slice(0, 19); + const gate = (t.gates && t.gates[a.gate]) || a.gate; + return ( +
+
+ + {gate} · {rej ? t.rejected : t.accepted} + + {t.approver} {a.actor} · {at} +
+ {rej && a.reason ? ( +
+ {t.rejectReason}{a.reason} +
+ ) : a.reason ? ( +
↳ {a.reason}
+ ) : null} +
+ ); + })} +
+
+ ); + })()} ); } diff --git a/design/ui_kits/console/i18n.js b/design/ui_kits/console/i18n.js index 793d2be..742efac 100644 --- a/design/ui_kits/console/i18n.js +++ b/design/ui_kits/console/i18n.js @@ -38,7 +38,7 @@ window.MAESTRO_I18N = { fStatus: '状态', fAssignee: '执行者', assignees: { agent: '智能体', human: '人工' }, fullRead: '全屏阅读', depsLabel: 'DEPS · 依赖(全部完成才可执行)', depDone: '完成', depWait: '等待', gateSection: '需你处理', gateNeedsHuman: '需人工', gRequeue: '↻ 重投', gTakeover: '⌨ 接管', gCancelTask: '⊘ 取消', accept: '✓ 通过', reject: '✕ 驳回', confirmReject: '确认驳回', - rejectPlaceholder: '改进意见(必填)', + rejectPlaceholder: '改进意见(必填)', rejectReason: '驳回原因', taskSection: '任务树', newTask: '+ 新建任务', titleLabel: '标题', titlePlaceholder: '要做什么', complexity: '复杂度', cplxAuto: '智能', parentTask: '父任务', topLevel: '(顶层)', priority: '优先级', p0: 'P0 · 高', p1: 'P1 · 中', p2: 'P2 · 低', create: '创建任务', cancel: '取消', @@ -92,7 +92,7 @@ window.MAESTRO_I18N = { fStatus: 'Status', fAssignee: 'Assignee', assignees: { agent: 'Agent', human: 'Human' }, fullRead: 'Read fullscreen', depsLabel: 'DEPS (all must complete to run)', depDone: 'done', depWait: 'waiting', gateSection: 'Needs you', gateNeedsHuman: 'Needs human', gRequeue: '↻ Requeue', gTakeover: '⌨ Take over', gCancelTask: '⊘ Cancel', accept: '✓ Accept', reject: '✕ Reject', confirmReject: 'Confirm reject', - rejectPlaceholder: 'Improvement feedback (required)', + rejectPlaceholder: 'Improvement feedback (required)', rejectReason: 'Reject reason', taskSection: 'Task tree', newTask: '+ New task', titleLabel: 'Title', titlePlaceholder: 'What needs doing', complexity: 'Complexity', cplxAuto: 'AUTO', parentTask: 'Parent', topLevel: '(top level)', priority: 'Priority', p0: 'P0 · high', p1: 'P1 · medium', p2: 'P2 · low', create: 'Create task', cancel: 'Cancel', @@ -146,7 +146,7 @@ window.MAESTRO_I18N = { fStatus: 'Estado', fAssignee: 'Ejecutor', assignees: { agent: 'Agente', human: 'Humano' }, fullRead: 'Pantalla completa', depsLabel: 'DEPS (todas deben completarse)', depDone: 'hecha', depWait: 'esperando', gateSection: 'Requiere acción', gateNeedsHuman: 'Manual', gRequeue: '↻ Reencolar', gTakeover: '⌨ Tomar', gCancelTask: '⊘ Cancelar', accept: '✓ Aceptar', reject: '✕ Rechazar', confirmReject: 'Confirmar rechazo', - rejectPlaceholder: 'Comentario de mejora (obligatorio)', + rejectPlaceholder: 'Comentario de mejora (obligatorio)', rejectReason: 'Motivo del rechazo', taskSection: 'Árbol de tareas', newTask: '+ Nueva tarea', titleLabel: 'Título', titlePlaceholder: 'Qué hay que hacer', complexity: 'Complejidad', cplxAuto: 'AUTO', parentTask: 'Padre', topLevel: '(raíz)', priority: 'Prioridad', p0: 'P0 · alta', p1: 'P1 · media', p2: 'P2 · baja', create: 'Crear tarea', cancel: 'Cancelar', @@ -200,7 +200,7 @@ window.MAESTRO_I18N = { fStatus: 'ステータス', fAssignee: '担当', assignees: { agent: 'エージェント', human: '人手' }, fullRead: '全画面で読む', depsLabel: 'DEPS · 依存(全完了で実行可)', depDone: '完了', depWait: '待機', gateSection: '要対応', gateNeedsHuman: '要人手', gRequeue: '↻ 再投入', gTakeover: '⌨ 引継ぎ', gCancelTask: '⊘ 取消', accept: '✓ 承認', reject: '✕ 却下', confirmReject: '却下を確定', - rejectPlaceholder: '改善フィードバック(必須)', + rejectPlaceholder: '改善フィードバック(必須)', rejectReason: '却下理由', taskSection: 'タスクツリー', newTask: '+ 新規タスク', titleLabel: 'タイトル', titlePlaceholder: '何をしますか', complexity: '複雑度', cplxAuto: '智能', parentTask: '親タスク', topLevel: '(トップ)', priority: '優先度', p0: 'P0 · 高', p1: 'P1 · 中', p2: 'P2 · 低', create: 'タスク作成', cancel: 'キャンセル', @@ -254,7 +254,7 @@ window.MAESTRO_I18N = { fStatus: 'Statut', fAssignee: 'Exécutant', assignees: { agent: 'Agent', human: 'Humain' }, fullRead: 'Plein écran', depsLabel: 'DEPS (toutes requises)', depDone: 'terminée', depWait: 'en attente', gateSection: 'À traiter', gateNeedsHuman: 'Manuel', gRequeue: '↻ Relancer', gTakeover: '⌨ Reprendre', gCancelTask: '⊘ Annuler', accept: '✓ Accepter', reject: '✕ Rejeter', confirmReject: 'Confirmer le rejet', - rejectPlaceholder: "Retour d'amélioration (obligatoire)", + rejectPlaceholder: "Retour d'amélioration (obligatoire)", rejectReason: 'Motif du rejet', taskSection: 'Arbre des tâches', newTask: '+ Nouvelle tâche', titleLabel: 'Titre', titlePlaceholder: 'Que faut-il faire', complexity: 'Complexité', cplxAuto: 'AUTO', parentTask: 'Parent', topLevel: '(racine)', priority: 'Priorité', p0: 'P0 · haute', p1: 'P1 · moyenne', p2: 'P2 · basse', create: 'Créer la tâche', cancel: 'Annuler',