优化: 审批弹窗全局化与体验打磨
This commit is contained in:
+10
-1
@@ -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
@@ -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 () => {
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user