diff --git a/frontend/src/layout/components/Main/Task.vue b/frontend/src/layout/components/Main/Task.vue index f153d5a..b975cf9 100644 --- a/frontend/src/layout/components/Main/Task.vue +++ b/frontend/src/layout/components/Main/Task.vue @@ -7,12 +7,14 @@ import websocket from '@/utils/websocket' import { changeBriefs } from '@/utils/collaboration_Brief_FrontEnd.ts' import { useNotification } from '@/composables/useNotification' import AssignmentButton from './TaskTemplate/TaskSyllabus/components/AssignmentButton.vue' -import HistoryList from './TaskTemplate/HistoryList/index.vue' import { withRetry } from '@/utils/retry' -import { Clock } from '@element-plus/icons-vue' +import UnifiedSettingsPanel from './TaskTemplate/UnifiedSettingsPanel.vue' +import { Setting } from '@element-plus/icons-vue' + const emit = defineEmits<{ (e: 'search-start'): void (e: 'search', value: string): void + (e: 'open-history'): void }>() const agentsStore = useAgentsStore() @@ -32,13 +34,11 @@ const currentGenerationId = ref('') const currentTaskID = ref('') // 后端数据库主键 // 监听 currentTaskID 变化,同步到全局变量(供分支保存使用) -watch(currentTaskID, (newVal) => { +watch(currentTaskID, newVal => { ;(window as any).__CURRENT_TASK_ID__ = newVal || '' - console.log('♻️ [Task] currentTaskID 同步到全局变量:', (window as any).__CURRENT_TASK_ID__) }) const currentPlanOutline = ref(null) // 用于恢复的完整大纲数据 -const historyDialogVisible = ref(false) // 历史记录弹窗控制 // 解析URL参数 function getUrlParam(param: string): string | null { @@ -165,13 +165,16 @@ async function handleStop() { // 发送停止请求(不等待响应,后端设置 should_stop = True) if (websocket.connected && currentGenerationId.value) { - websocket.send('stop_generation', { - generation_id: currentGenerationId.value - }).then((result: any) => { - console.log('停止生成响应:', result) - }).catch((error: any) => { - console.log('停止生成请求失败(可能已经停止):', error?.message) - }) + websocket + .send('stop_generation', { + generation_id: currentGenerationId.value + }) + .then((result: any) => { + console.log('停止生成响应:', result) + }) + .catch((error: any) => { + console.log('停止生成请求失败(可能已经停止):', error?.message) + }) } // 不清空 currentGenerationId,让 fillStepTask 循环检查 isStopping 来停止 } @@ -229,7 +232,12 @@ async function handleSearch() { outlineData = response.data.basePlan currentGenerationId.value = response.data.generation_id || '' currentTaskID.value = response.data.task_id || '' - console.log('📋 WebSocket格式: 从basePlan提取数据, generation_id:', currentGenerationId.value, 'TaskID:', currentTaskID.value) + console.log( + '📋 WebSocket格式: 从basePlan提取数据, generation_id:', + currentGenerationId.value, + 'TaskID:', + currentTaskID.value + ) } else if (response?.data) { // 可能是WebSocket旧格式或REST API格式 outlineData = response.data @@ -285,10 +293,10 @@ async function handleSearch() { TaskContent: step.TaskContent, InputObject_List: step.InputObject_List, OutputObject: step.OutputObject, - Id: step.Id, // 传递步骤ID用于后端定位 + Id: step.Id // 传递步骤ID用于后端定位 }, generation_id: fillTaskGenerationId, - TaskID: fillTaskTaskID, // 后端数据库主键 + TaskID: fillTaskTaskID // 后端数据库主键 }) console.log(`📥 fillStepTask 返回完成: 步骤=${step.StepName}`) updateStepDetail(step.StepName, detailedStep) @@ -296,8 +304,8 @@ async function handleSearch() { { maxRetries: 2, // 减少重试次数,因为是串行填充 initialDelayMs: 1000, // 使用较小的延迟 - shouldRetry: () => isFillingSteps.value && !agentsStore.isStopping, // 可取消的重试 - }, + shouldRetry: () => isFillingSteps.value && !agentsStore.isStopping // 可取消的重试 + } ) } } finally { @@ -367,17 +375,12 @@ onUnmounted(() => { // 打开历史记录弹窗 const openHistoryDialog = () => { - historyDialogVisible.value = true + emit('open-history') } -// 处理历史任务恢复 -const handleRestorePlan = (plan: any) => { - console.log('♻️ [Task] 恢复历史任务:', plan) - - // 1. 关闭弹窗 - historyDialogVisible.value = false - - // 2. 设置搜索值为历史任务的目标 +// 从历史记录恢复任务 +const restoreFromHistory = (plan: any) => { + // 1. 设置搜索值为历史任务的目标 searchValue.value = plan.general_goal // 3. 设置当前任务的 task_id(供分支保存使用) @@ -402,7 +405,10 @@ const handleRestorePlan = (plan: any) => { // 5. 如果有智能体评分数据,更新到 store if (plan.agent_scores && currentPlanOutline.value) { const tasks = currentPlanOutline.value['Collaboration Process'] || [] - console.log('🔍 [Task] 所有步骤名称:', tasks.map((t: any) => t.StepName)) + console.log( + '🔍 [Task] 所有步骤名称:', + tasks.map((t: any) => t.StepName) + ) console.log('🔍 [Task] agent_scores 的 key:', Object.keys(plan.agent_scores)) for (const task of tasks) { @@ -473,8 +479,13 @@ const handleRestorePlan = (plan: any) => { } // 恢复任务过程分支 - if (plan.branches.task_process_branches && typeof plan.branches.task_process_branches === 'object') { - console.log('♻️ [Task] 恢复任务过程分支:', { stepCount: Object.keys(plan.branches.task_process_branches).length }) + if ( + plan.branches.task_process_branches && + typeof plan.branches.task_process_branches === 'object' + ) { + console.log('♻️ [Task] 恢复任务过程分支:', { + stepCount: Object.keys(plan.branches.task_process_branches).length + }) selectionStore.restoreTaskProcessBranchesFromDB(plan.branches.task_process_branches) } } @@ -498,10 +509,20 @@ const handleRestorePlan = (plan: any) => { success('成功', '已恢复历史任务') } +// 设置面板引用 +const unifiedSettingsPanelRef = ref | null>(null) + +// 打开设置面板 +const openSettingsPanel = () => { + unifiedSettingsPanelRef.value?.open() +} + // 暴露给父组件 defineExpose({ currentTaskID, - openHistoryDialog + openHistoryDialog, + restoreFromHistory, + openSettingsPanel }) @@ -566,29 +587,14 @@ defineExpose({ - - - + + + + + - - - - - diff --git a/frontend/src/layout/components/Main/TaskTemplate/TaskResult/index.vue b/frontend/src/layout/components/Main/TaskTemplate/TaskResult/index.vue index 62aeeaf..f3761aa 100644 --- a/frontend/src/layout/components/Main/TaskTemplate/TaskResult/index.vue +++ b/frontend/src/layout/components/Main/TaskTemplate/TaskResult/index.vue @@ -968,10 +968,10 @@ async function restartFromStep(stepIndex: number) { isStreaming.value = true currentExecutionId.value = executionId }, - newExecutionId, // 传入前端生成的 execution_id + newExecutionId, // 传入前端生成的 execution_id stepIndex, truncatedLog, - props.TaskID || undefined // 传入 TaskID 用于更新数据库 + props.TaskID || undefined // 传入 TaskID 用于更新数据库 ) success('重新执行', `正在从步骤 ${stepIndex + 1} 重新执行...`) @@ -1197,9 +1197,9 @@ const processBtnClass = computed(() => { const executeBtnClass = computed(() => { if (buttonHoverState.value === 'process' || buttonHoverState.value === 'refresh') { - return 'circle' + return 'circle task-execute-btn' } - return agentsStore.agentRawPlan.data ? 'ellipse' : 'circle' + return agentsStore.agentRawPlan.data ? 'ellipse task-execute-btn' : 'circle task-execute-btn' }) const refreshBtnClass = computed(() => { @@ -1295,7 +1295,6 @@ defineExpose({ @@ -125,4 +165,86 @@ defineExpose({ padding-bottom: 20px; } } + +// 历史记录 Drawer 样式 +:deep(.history-drawer) { + // 强制使用绝对定位,相对于父容器 + position: absolute !important; + top: 0 !important; + bottom: 0 !important; + z-index: 1000; + // 高度由 top/bottom 控制 + height: auto !important; + + background-color: var(--color-bg-three); + border: 1px solid var(--color-card-border-three); + border-radius: 24px; + box-shadow: var(--color-card-shadow-three); + padding-top: 20px; + padding-bottom: 20px; + + // 动画 + animation: drawer-slide-in 0.5s ease-out; + + // 头部 + .history-drawer-header { + display: flex; + justify-content: space-between; + align-items: center; + padding: 0 16px; + background-color: var(--color-bg-three); + } + + // 标题 + .history-drawer-title { + color: var(--color-text-primary); + font-size: 16px; + font-weight: 600; + padding-left: 8px; + } + + // 关闭按钮 + .history-drawer-close { + background: none; + border: none; + cursor: pointer; + width: 24px; + height: 24px; + display: flex; + align-items: center; + justify-content: center; + border-radius: 4px; + color: var(--color-text-regular); + transition: transform 0.3s ease; + + &:hover { + transform: rotate(360deg); + } + } + + :deep(.el-drawer__body) { + background-color: var(--color-bg-three); + padding: 0; + opacity: 0; + animation: drawer-fade-in 0.5s ease-out forwards; + } +} + +@keyframes drawer-slide-in { + from { + transform: translateX(-100%); + } + to { + transform: translateX(0); + } +} + +@keyframes drawer-fade-in { + from { + opacity: 0; + } + to { + opacity: 1; + } +} diff --git a/frontend/src/layout/components/Main/index.vue b/frontend/src/layout/components/Main/index.vue index 8bbb359..275ef24 100644 --- a/frontend/src/layout/components/Main/index.vue +++ b/frontend/src/layout/components/Main/index.vue @@ -3,8 +3,12 @@ import Task from './Task.vue' import TaskTemplate from './TaskTemplate/index.vue' import { nextTick, ref } from 'vue' -const taskRef = ref<{ currentTaskID: string }>() -const taskTemplateRef = ref<{ changeTask: () => void; clear: () => void }>() +const taskRef = ref<{ currentTaskID: string; restoreFromHistory: (plan: any) => void }>() +const taskTemplateRef = ref<{ + changeTask: () => void + clear: () => void + openHistoryDialog: () => void +}>() function handleSearch() { nextTick(() => { @@ -17,16 +21,34 @@ function getTaskID(): string { return taskRef.value?.currentTaskID || '' } +// 打开历史记录弹窗 +function openHistoryDialog() { + taskTemplateRef.value?.openHistoryDialog() +} + +// 处理历史任务恢复 +function handleRestorePlan(plan: any) { + taskRef.value?.restoreFromHistory(plan) +} + defineExpose({ - getTaskID + getTaskID, + openHistoryDialog }) - - diff --git a/frontend/src/layout/index.vue b/frontend/src/layout/index.vue index a43b120..c0e49d3 100644 --- a/frontend/src/layout/index.vue +++ b/frontend/src/layout/index.vue @@ -7,15 +7,16 @@ import PlanModification from './components/Main/TaskTemplate/TaskSyllabus/Branch import { useAgentsStore } from '@/stores/modules/agents' import PlanTask from './components/Main/TaskTemplate/TaskProcess/components/PlanTask.vue' import AgentAllocation from './components/Main/TaskTemplate/TaskSyllabus/components/AgentAllocation.vue' +import { Clock } from '@element-plus/icons-vue' const isDarkMode = ref(false) const agentsStore = useAgentsStore() -const mainRef = ref<{ currentTaskID: string } | null>(null) +const mainRef = ref<{ currentTaskID: string; openHistoryDialog: () => void } | null>(null) -// 获取当前的 taskId -const currentTaskId = computed(() => { - return mainRef.value?.currentTaskID || '' -}) +// 打开历史记录 +const openHistory = () => { + mainRef.value?.openHistoryDialog() +} onMounted(() => { // 等待 Main 组件挂载后获取 taskId @@ -76,7 +77,23 @@ onMounted(() => {