优化: 审批弹窗全局化与体验打磨

This commit is contained in:
lxy
2026-08-10 08:04:30 +08:00
parent dedb9a8862
commit 6141a67b0e
10 changed files with 159 additions and 47 deletions
+10 -1
View File
@@ -133,7 +133,7 @@
<div v-for="(msg, mi) in convMessages" :key="mi" class="conv-msg" :class="'conv-msg--' + msg.role">
<!-- 头部:角色 + model + 时间 + token -->
<div class="conv-msg-head">
<span class="conv-msg-role">{{ msg.role === 'user' ? '🧑 用户' : msg.role === 'assistant' ? '🤖 助手' : msg.role === 'tool' ? '🔧 工具' : '⚙️ 系统' }}</span>
<span class="conv-msg-role">{{ convRoleLabel(msg.role) }}</span>
<span v-if="msg.model" class="conv-msg-model">{{ msg.model }}</span>
<span v-if="msg.tool_call_id" class="conv-msg-toolname">tool_call: {{ msg.tool_call_id }}</span>
<span v-if="msg.timestamp" class="conv-msg-time">{{ formatDate(msg.timestamp) }}</span>
@@ -419,6 +419,15 @@ function decidedClass(d: string): string {
return d === 'human' ? 'decided-human' : 'decided-auto'
}
// SA-2:对话角色 i18n 化(原硬编码 '🧑 用户'/'🤖 助手'/'🔧 工具'/'⚙️ 系统' 不随 locale)
// user/assistant/tool 映射固定 key,其他未知 role 回退到 '⚙️ 系统'(含 emoji,与其他四态视觉一致)
function convRoleLabel(role: string): string {
if (role === 'user') return t('auditLog.convRoleUser')
if (role === 'assistant') return t('auditLog.convRoleAssistant')
if (role === 'tool') return t('auditLog.convRoleTool')
return t('auditLog.convRoleSystem')
}
onMounted(() => {
void load()
if (activeTab.value === 'conversations') void loadConversations()
+28 -8
View File
@@ -362,14 +362,18 @@ async function confirmCapture() {
try {
// tags 逗号分隔 → JSON 字符串(对标 KnowledgeDetail.vue saveEdit split+trim+filter 模式)
const tags = newIdeaTags.value.split(',').map(t => t.trim()).filter(Boolean)
await store.createIdea({
const created = await store.createIdea({
title: newIdeaTitle.value.trim(),
description: newIdeaDesc.value.trim() || undefined,
priority: newIdeaPriority.value,
tags: JSON.stringify(tags),
source: newIdeaSource.value.trim() || undefined,
})
showCaptureModal.value = false
// WK-18:createIdea 返 null=失败(state.error 已置,顶部 error-banner 展示);成功关弹窗 + toast
if (created) {
showCaptureModal.value = false
Message.success(t('ideas.createdToast'))
}
} finally {
creating.value = false
}
@@ -449,9 +453,17 @@ async function onStatusChange(newStatus: IdeaStatus) {
}
// 接收 IdeaDetail 子组件 emit 的 'update-desc'(描述编辑保存)
async function onUpdateDesc(desc: string) {
if (!currentIdea.value) return
await store.updateIdea(currentIdea.value.id, 'description', desc)
// WK-16:返 boolean(成功 true/失败 false),失败 toast;子组件据 false 保持编辑态。
async function onUpdateDesc(desc: string): Promise<boolean> {
if (!currentIdea.value) return false
try {
await store.updateIdea(currentIdea.value.id, 'description', desc)
Message.success(t('ideas.descSaved'))
return true
} catch (e: any) {
Message.error(e?.toString() ?? t('ideas.descSaveFailed'))
return false
}
}
// 接收 IdeaDetail 子组件 emit 的 'update-related'(关联灵感编辑保存,双向同步)
@@ -463,9 +475,17 @@ async function onUpdateRelated(ids: string[]) {
}
// 接收 IdeaDetail 子组件 emit 的 'update-tags'(标签编辑保存)
async function onUpdateTags(tags: string) {
if (!currentIdea.value) return
await store.updateIdea(currentIdea.value.id, 'tags', tags)
// WK-16:返 boolean(成功 true/失败 false),失败 toast;子组件据 false 保持编辑态。
async function onUpdateTags(tags: string): Promise<boolean> {
if (!currentIdea.value) return false
try {
await store.updateIdea(currentIdea.value.id, 'tags', tags)
Message.success(t('ideas.tagsSaved'))
return true
} catch (e: any) {
Message.error(e?.toString() ?? t('ideas.tagsSaveFailed'))
return false
}
}
onMounted(async () => {
+2 -23
View File
@@ -359,14 +359,6 @@
</section>
</div>
<!-- 人工审批对话框(抽离至 components/project/ApprovalDialog.vue)-->
<ApprovalDialog
:visible="showApprovalDialog"
:store="store"
@cancel="handleCancelApproval"
@later="showApprovalDialog = false"
/>
<!-- 确认弹层(删除/重定位确认,替代原生 window.confirm/alert) -->
<ConfirmDialog :visible="confirmState.visible" :msg="confirmState.msg" :danger-label="confirmState.dangerLabel" @result="answerConfirm" />
@@ -429,7 +421,6 @@ import { parseStack } from '@/utils/project'
import { parseScores as parseScoresJson, assessmentClass, assessmentLabel as assessmentLabelI18n, scoreTier } from '@/utils/ideaEval'
import { projectStatusLabel, taskStatusLabel, taskStatusClass } from '../constants/project'
import ConfirmDialog from '@/components/ConfirmDialog.vue'
import ApprovalDialog from '@/components/project/ApprovalDialog.vue'
import FileExplorer from '@/components/project/FileExplorer.vue'
import DependencyGraph from '@/components/project/DependencyGraph.vue'
import WorkflowDagDisplay from '@/components/workflow/WorkflowDagDisplay.vue'
@@ -442,7 +433,6 @@ const router = useRouter()
const store = useProjectStore()
const { t, locale } = useI18n()
const logListRef = ref<HTMLElement | null>(null)
const showApprovalDialog = ref(false)
// A1[PD-P1-4]:任务列表随 AI 改动刷新 —— 本地 df-data-changed 监听(entity=task)重拉 loadProjectTasks。
// 对齐 TaskDetail B-260616-18 模式:onMounted 注册、onBeforeUnmount 释放;连发事件防抖合并,
@@ -876,12 +866,8 @@ async function handleImportDir() {
// 审批对话框 UI + 单/多选决策逻辑抽离至 components/project/ApprovalDialog.vue
// handleApproval/handleApprovalMulti/isMultipleSelect/multiDecisions 已迁移;
// submitting(UX-260618-17)在子组件自治,父级 submitting 仅用于 submitNewTask
// ── 取消审批(调 cancel_workflow_node IPC 置节点 Cancelled)──
async function handleCancelApproval() {
await store.cancelHumanApproval()
showApprovalDialog.value = false
}
// P0-C:ApprovalDialog + watch pendingApproval + handleCancelApproval 已提至 App.vue(全局),
// TaskDetail 等无 ApprovalDialog 的页面自动受益(原局部渲染致 HumanNode 超时失败)
// ── 删除项目(软删 → 回收站,可恢复)──
async function handleDelete() {
@@ -897,13 +883,6 @@ watch(formattedEvents, () => {
if (logListRef.value) logListRef.value.scrollTop = logListRef.value.scrollHeight
})
// 监听审批请求(pendingApproval → 开弹窗;多选清空已在 ApprovalDialog 内 watch)
watch(() => store.pendingApproval, (newApproval) => {
if (newApproval) {
showApprovalDialog.value = true
}
}, { immediate: true })
// ── 工具函数 ──
// formatDate 由 ../utils/time 提供(统一毫秒字符串解析,根治 Invalid Date)