修复: aichat advance_task渲染显任务名 + 审批防双击 + time.ts销账

- UX-260618-14: advance_task 工具结果/审批卡显任务名替代UUID
  前端 ToolCard 取 r.title fallback r.id + TASK_ID_TOOL_ARG 审批卡 task_id→title 回显 + i18n
  后端 audit resolve_task_label 查 tasks 表(原误查 projects恒报不存在) + 删 tool_display_hint 短路让模板生效
- UX-260618-16: time.ts formatDate .value 已修(CR-24/25闭环),漏销账补登
- UX-260618-17: ProjectDetail handleApprovalMulti 补 submitting try/finally + 模板联动防双击
This commit is contained in:
2026-06-19 00:52:20 +08:00
parent 6d2244c03b
commit 1dc8b12ec4
8 changed files with 94 additions and 19 deletions

View File

@@ -473,9 +473,10 @@ function formatToolResult(tc: AiToolCallInfo): string {
: t('aiTool.updated')
case 'advance_task':
// UX-260618-09: review_rounds>0 显退回累加轮数,=0 回退原文案(首推无噪音)
// UX-260618-14: 后端返 TaskRecord(含 title),优先显 title,无则回退 id(防裸 UUID)
return typeof r.review_rounds === 'number' && r.review_rounds > 0
? t('aiTool.advancedTaskWithRounds', { id: r.id || '', status: r.status || '', n: r.review_rounds })
: t('aiTool.advancedTask', { id: r.id || '', status: r.status || '' })
? t('aiTool.advancedTaskWithRounds', { title: r.title || r.id || '', status: r.status || '', n: r.review_rounds })
: t('aiTool.advancedTask', { title: r.title || r.id || '', status: r.status || '' })
case 'delete_task':
return r.deleted ? t('aiTool.deleted') : t('aiTool.deleteIneffective')
case 'delete_file':
@@ -796,6 +797,16 @@ function projectNameById(id: string): string | undefined {
?? projectStore.deletedProjects.find(p => p.id === id)?.name
}
/**
* 按 id 查任务标题:审批 advance_task/run_workflow 的 task_id 时,把裸 UUID 转标题。
* 复用 project store 已加载的 state.tasks(仅当前项目范围),不新增网络请求。
* state.tasks 非「全量」(按当前项目/筛选加载),查不到时调用方走裸 id fallback——
* 审批卡 task_id 多属当前活动项目,命中率足够,跨项目任务降级显 id 可接受。
*/
function taskNameById(id: string): string | undefined {
return projectStore.tasks.find(t => t.id === id)?.title
}
/** 工具名 → 该工具中代表项目 id 的参数 key(仅项目类工具特化;delete_task 的 id 是任务 id,不在此列) */
const PROJECT_ID_TOOL_ARG: Record<string, 'id' | 'project_id'> = {
delete_project: 'id',
@@ -806,6 +817,15 @@ const PROJECT_ID_TOOL_ARG: Record<string, 'id' | 'project_id'> = {
create_task: 'project_id',
}
/**
* 工具名 → 该工具中代表任务 id 的参数 key(任务类工具特化)。
* advance_task 的任务 id 在 "id" key,run_workflow 在 "task_id" key。
*/
const TASK_ID_TOOL_ARG: Record<string, 'id' | 'task_id'> = {
advance_task: 'id',
run_workflow: 'task_id',
}
/**
* 审批参数值展示(AR-3):对项目类工具的 id/project_id 特化——优先回显项目名,查不到则显示
* 「项目已不存在」提示 + 原 id,避免用户只看到一串裸 id 不知道是什么数据。
@@ -821,6 +841,16 @@ function displayArgValue(arg: { key: string; val: unknown }): string {
return t('aiTool.projectIdNotFound', { id })
}
}
// UX-260618-14:advance_task/run_workflow 的 task_id → 任务标题(store.tasks 查,无则标 id)
const taskArgKey = TASK_ID_TOOL_ARG[props.tc.name]
if (taskArgKey && arg.key === taskArgKey) {
const id = typeof arg.val === 'string' ? arg.val : ''
if (id) {
const name = taskNameById(id)
if (name) return t('aiTool.taskLabel', { name })
return t('aiTool.taskIdNotFound', { id })
}
}
return formatArgValue(arg.val)
}
@@ -889,8 +919,9 @@ function toolResultSummary(tc: AiToolCallInfo): string {
? t('aiTool.updatedTaskField', { id: r.id || '', field: r.field || '' })
: t('aiTool.updated')
case 'advance_task': return typeof r.review_rounds === 'number' && r.review_rounds > 0
? t('aiTool.advancedTaskWithRounds', { id: r.id || '', status: r.status || '', n: r.review_rounds })
: t('aiTool.advancedTask', { id: r.id || '', status: r.status || '' })
// UX-260618-14: 优先显 title(后端返 TaskRecord),无则回退 id,防裸 UUID
? t('aiTool.advancedTaskWithRounds', { title: r.title || r.id || '', status: r.status || '', n: r.review_rounds })
: t('aiTool.advancedTask', { title: r.title || r.id || '', status: r.status || '' })
case 'delete_project':
case 'delete_task': return r.deleted ? t('aiTool.deleted') : t('aiTool.deleteIneffective')
case 'delete_file': return r.permanent

View File

@@ -64,9 +64,9 @@ export default {
// UX-260616-04: formatToolResult covers all tools with human-readable summaries (missing keys)
updatedTaskField: 'Task {id} updated field {field}',
advancedTask: 'Task {id} advanced to {status}',
// UX-260618-09: advance_task return-round accumulation (shown only when >0)
advancedTaskWithRounds: 'Task {id} advanced to {status} (returned {n} times)',
advancedTask: 'Task「{title}」 advanced to {status}',
// UX-260618-09: advance_task return-round accumulation (shown only when >0); UX-260618-14: {title} = task name (TaskRecord), fallback id
advancedTaskWithRounds: 'Task「{title}」 advanced to {status} (returned {n} times)',
commandOk: 'Command succeeded ({ms} ms)',
commandFailed: 'Command failed (exit code {code})',
commandOutputLines: '{n} lines of output',
@@ -97,6 +97,9 @@ export default {
projectLabel: '"{name}"',
projectIdNotFound: '(project not found, id={id})',
// UX-260618-14: advance_task/run_workflow task_id → task title (store.tasks lookup; fallback id)
taskLabel: '"{name}"',
taskIdNotFound: '(task not in current list, id={id})',
// AE-2025-05: High-risk approval second confirm (ask once more before approve; misclick cancellable)
confirmHighDelete: 'Permanently delete? This action cannot be undone',

View File

@@ -65,9 +65,9 @@ export default {
// UX-260616-04:formatToolResult 覆盖全部工具产出人类可读摘要(补缺失 key)
updatedTaskField: '任务 {id} 已更新字段 {field}',
advancedTask: '任务 {id} 已推进至 {status}',
// UX-260618-09:advance_task 退回累加轮数(>0 才显)
advancedTaskWithRounds: '任务 {id} 已推进至 {status}(已退回 {n} 次)',
advancedTask: '任务「{title}」已推进至 {status}',
// UX-260618-09:advance_task 退回累加轮数(>0 才显);UX-260618-14:{title} 显任务名(后端返 TaskRecord),回退 id
advancedTaskWithRounds: '任务「{title}」已推进至 {status}(已退回 {n} 次)',
commandOk: '命令成功({ms} 毫秒)',
commandFailed: '命令失败(退出码 {code}',
commandOutputLines: '{n} 行输出',
@@ -99,6 +99,9 @@ export default {
// 审批参数特化(id/project_id 回显项目名,查不到标注原 id)
projectLabel: '「{name}」',
projectIdNotFound: '(项目已不存在, id={id})',
// UX-260618-14:advance_task/run_workflow 的 task_id 回显任务标题(查 store.tasks,无则标 id)
taskLabel: '「{name}」',
taskIdNotFound: '(任务不在当前列表, id={id})',
// AE-2025-05:High 风险审批二次确认文案(批准前再问一次,误点可取消)
confirmHighDelete: '确定永久删除?此操作不可恢复',

View File

@@ -195,7 +195,7 @@
</div>
<button
class="btn btn-primary"
:disabled="multiDecisions.length === 0"
:disabled="submitting || multiDecisions.length === 0"
@click="handleApprovalMulti"
>
{{ $t('projectDetail.approvalConfirm', { count: multiDecisions.length }) }}
@@ -430,10 +430,15 @@ const multiDecisions = ref<string[]>([])
async function handleApprovalMulti() {
if (multiDecisions.value.length === 0) return
// 统一传 decisions 数组,store 按 select_type 分派单/多选
await store.approveHumanApproval([...multiDecisions.value])
multiDecisions.value = []
showApprovalDialog.value = false
submitting.value = true
try {
// 统一传 decisions 数组,store 按 select_type 分派单/多选
await store.approveHumanApproval([...multiDecisions.value])
multiDecisions.value = []
showApprovalDialog.value = false
} finally {
submitting.value = false
}
}
// ── 取消审批(调 cancel_workflow_node IPC 置节点 Cancelled──