feat:任务大纲编辑文字悬浮边框重构
This commit is contained in:
@@ -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<any>(null) // 用于恢复的完整大纲数据
|
||||
const historyDialogVisible = ref(false) // 历史记录弹窗控制
|
||||
|
||||
// 解析URL参数
|
||||
function getUrlParam(param: string): string | null {
|
||||
@@ -165,11 +165,14 @@ async function handleStop() {
|
||||
|
||||
// 发送停止请求(不等待响应,后端设置 should_stop = True)
|
||||
if (websocket.connected && currentGenerationId.value) {
|
||||
websocket.send('stop_generation', {
|
||||
websocket
|
||||
.send('stop_generation', {
|
||||
generation_id: currentGenerationId.value
|
||||
}).then((result: any) => {
|
||||
})
|
||||
.then((result: any) => {
|
||||
console.log('停止生成响应:', result)
|
||||
}).catch((error: any) => {
|
||||
})
|
||||
.catch((error: any) => {
|
||||
console.log('停止生成请求失败(可能已经停止):', error?.message)
|
||||
})
|
||||
}
|
||||
@@ -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<InstanceType<typeof UnifiedSettingsPanel> | null>(null)
|
||||
|
||||
// 打开设置面板
|
||||
const openSettingsPanel = () => {
|
||||
unifiedSettingsPanelRef.value?.open()
|
||||
}
|
||||
|
||||
// 暴露给父组件
|
||||
defineExpose({
|
||||
currentTaskID,
|
||||
openHistoryDialog
|
||||
openHistoryDialog,
|
||||
restoreFromHistory,
|
||||
openSettingsPanel
|
||||
})
|
||||
</script>
|
||||
|
||||
@@ -566,29 +587,14 @@ defineExpose({
|
||||
</el-button>
|
||||
</div>
|
||||
<AssignmentButton v-if="planReady" @click="openAgentAllocationDialog" />
|
||||
<!-- 历史记录按钮 -->
|
||||
<el-button
|
||||
class="history-button"
|
||||
circle
|
||||
:title="'历史记录'"
|
||||
@click.stop="openHistoryDialog"
|
||||
>
|
||||
<el-icon size="18px"><Clock /></el-icon>
|
||||
<!-- 设置按钮 -->
|
||||
<el-button class="setting-button" circle title="设置" @click="openSettingsPanel">
|
||||
<el-icon size="18px"><Setting /></el-icon>
|
||||
</el-button>
|
||||
<!-- 统一设置面板 -->
|
||||
<UnifiedSettingsPanel ref="unifiedSettingsPanelRef" />
|
||||
</div>
|
||||
</el-tooltip>
|
||||
|
||||
<!-- 历史记录弹窗 -->
|
||||
<el-dialog
|
||||
v-model="historyDialogVisible"
|
||||
title="历史任务"
|
||||
width="600px"
|
||||
:close-on-click-modal="true"
|
||||
destroy-on-close
|
||||
append-to-body
|
||||
>
|
||||
<HistoryList @restore="handleRestorePlan" />
|
||||
</el-dialog>
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss">
|
||||
@@ -728,8 +734,8 @@ defineExpose({
|
||||
}
|
||||
}
|
||||
|
||||
// 历史记录按钮
|
||||
.history-button {
|
||||
// 设置按钮
|
||||
.setting-button {
|
||||
position: absolute;
|
||||
right: 70px;
|
||||
top: 28px;
|
||||
@@ -741,7 +747,7 @@ defineExpose({
|
||||
|
||||
&:hover {
|
||||
background: var(--color-bg-hover);
|
||||
color: var(--color-text-hover);
|
||||
color: var(--color-text-taskbar);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -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({
|
||||
<template #reference>
|
||||
<el-button
|
||||
:class="executeBtnClass"
|
||||
:color="variables.tertiary"
|
||||
:title="isStreaming ? (isPaused ? '点击继续执行' : '点击暂停执行') : executeBtnTitle"
|
||||
:disabled="
|
||||
!agentsStore.agentRawPlan.data || (!isStreaming && loading) || isButtonLoading
|
||||
@@ -1338,7 +1337,7 @@ defineExpose({
|
||||
/>
|
||||
|
||||
<!-- 默认状态:显示 action 图标 -->
|
||||
<svg-icon v-else icon-class="action" />
|
||||
<svg-icon v-else icon-class="action" color="var(--color-text-title-header)" />
|
||||
|
||||
<span v-if="showExecuteText && !isStreaming" class="btn-text">任务执行</span>
|
||||
<span v-else-if="isStreaming && isPaused" class="btn-text">继续执行</span>
|
||||
@@ -1773,15 +1772,23 @@ defineExpose({
|
||||
animation: fadeInRight 0.3s ease forwards;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// .btn-text {
|
||||
// display: inline-block !important;
|
||||
// font-size: 14px;
|
||||
// font-weight: 500;
|
||||
// margin-left: 4px;
|
||||
// opacity: 1;
|
||||
// animation: fadeIn 0.3s ease forwards;
|
||||
// }
|
||||
// 任务执行按钮渐变样式
|
||||
.task-execute-btn {
|
||||
background: linear-gradient(to right, #00c5d1, #305ab3) !important;
|
||||
background-color: transparent !important;
|
||||
background-origin: border-box !important;
|
||||
background-clip: border-box !important;
|
||||
border-radius: 20px !important;
|
||||
transition: background 0.3s ease !important;
|
||||
|
||||
&:hover {
|
||||
background: linear-gradient(to right, #6431b4, #315ab4) !important;
|
||||
background-color: transparent !important;
|
||||
background-origin: border-box !important;
|
||||
background-clip: border-box !important;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes fadeInLeft {
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
import AgentRepo from './AgentRepo/index.vue'
|
||||
import TaskSyllabus from './TaskSyllabus/index.vue'
|
||||
import TaskResult from './TaskResult/index.vue'
|
||||
import HistoryList from './HistoryList/index.vue'
|
||||
import SvgIcon from '@/components/SvgIcon/index.vue'
|
||||
import { Jsplumb } from './utils.ts'
|
||||
import { type IRawStepTask, useAgentsStore } from '@/stores'
|
||||
import { BezierConnector } from '@jsplumb/browser-ui'
|
||||
@@ -12,8 +14,29 @@ const props = defineProps<{
|
||||
TaskID?: string // 任务唯一标识,用于写入数据库
|
||||
}>()
|
||||
|
||||
// 定义事件
|
||||
const emit = defineEmits<{
|
||||
(e: 'restore', plan: any): void
|
||||
}>()
|
||||
|
||||
const agentsStore = useAgentsStore()
|
||||
|
||||
// 历史记录弹窗控制
|
||||
const historyDialogVisible = ref(false)
|
||||
|
||||
// 打开历史记录弹窗
|
||||
const openHistoryDialog = () => {
|
||||
historyDialogVisible.value = true
|
||||
}
|
||||
|
||||
// 处理历史任务恢复
|
||||
const handleRestorePlan = (plan: any) => {
|
||||
// 关闭弹窗
|
||||
historyDialogVisible.value = false
|
||||
// 发送恢复事件给父组件
|
||||
emit('restore', plan)
|
||||
}
|
||||
|
||||
// 智能体库
|
||||
const agentRepoJsplumb = new Jsplumb('task-template', {
|
||||
connector: {
|
||||
@@ -78,6 +101,7 @@ function clear() {
|
||||
}
|
||||
|
||||
defineExpose({
|
||||
openHistoryDialog,
|
||||
changeTask,
|
||||
resetAgentRepoLine,
|
||||
clear
|
||||
@@ -111,6 +135,22 @@ defineExpose({
|
||||
@set-current-task="handleTaskResultCurrentTask"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<!-- 历史记录弹窗 -->
|
||||
<div v-if="historyDialogVisible" class="history-drawer">
|
||||
<div class="history-drawer-content">
|
||||
<div class="history-drawer-header">
|
||||
<span class="history-drawer-title">历史记录</span>
|
||||
<button class="history-drawer-close" @click="historyDialogVisible = false">
|
||||
<SvgIcon icon-class="close" size="20px" />
|
||||
</button>
|
||||
</div>
|
||||
<div class="h-[1px] w-full bg-[var(--color-border-separate)] my-[8px]"></div>
|
||||
<div class="history-drawer-body">
|
||||
<HistoryList @restore="handleRestorePlan" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -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
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="p-[24px] h-[calc(100%-60px)]">
|
||||
<Task ref="taskRef" @search="handleSearch" @search-start="taskTemplateRef?.clear" />
|
||||
<TaskTemplate ref="taskTemplateRef" :TaskID="taskRef?.currentTaskID" />
|
||||
<Task
|
||||
ref="taskRef"
|
||||
@search="handleSearch"
|
||||
@search-start="taskTemplateRef?.clear"
|
||||
@open-history="openHistoryDialog"
|
||||
/>
|
||||
<TaskTemplate
|
||||
ref="taskTemplateRef"
|
||||
:TaskID="taskRef?.currentTaskID"
|
||||
@restore="handleRestorePlan"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss"></style>
|
||||
|
||||
@@ -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(() => {
|
||||
|
||||
<template>
|
||||
<div class="h-full relative">
|
||||
<!-- 主题切换按钮 - 放置在页面右上角 -->
|
||||
<!-- 左上角按钮组 -->
|
||||
<div class="absolute top-4 left-4 z-50 flex gap-3">
|
||||
<!-- 历史记录按钮 -->
|
||||
<button
|
||||
@click="openHistory"
|
||||
class="w-10 h-10 rounded-full transition-all duration-300 flex items-center justify-center"
|
||||
:class="{
|
||||
'bg-[#ebebeb] hover:bg-[var(--color-bg-hover)]': !isDarkMode,
|
||||
'bg-[#0e131c] hover:bg-[var(--color-bg-hover)]': isDarkMode
|
||||
}"
|
||||
title="历史记录"
|
||||
>
|
||||
<el-icon size="20px"><Clock /></el-icon>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<!-- 右上角按钮 -->
|
||||
<div class="absolute top-4 right-4 z-50">
|
||||
<button
|
||||
@click="toggleTheme"
|
||||
@@ -107,7 +124,7 @@ onMounted(() => {
|
||||
</div>
|
||||
|
||||
<Header />
|
||||
<Main />
|
||||
<Main ref="mainRef" />
|
||||
|
||||
<FloatWindow
|
||||
v-if="agentsStore.planModificationWindow"
|
||||
|
||||
@@ -114,6 +114,10 @@
|
||||
--color-tab-text: #FDFFFF;
|
||||
// 智能体标签文字颜色(未选中)
|
||||
--color-tab-text-inactive: rgba(253, 255, 255, 0.4);
|
||||
// 任务编辑框悬浮边框颜色
|
||||
--color-task-edit-hover-border: #D6D6D6;
|
||||
// 编辑图标背景色
|
||||
--color-edit-icon-bg: rgba(255, 255, 255, 0.5);
|
||||
|
||||
}
|
||||
|
||||
@@ -233,6 +237,10 @@ html.dark {
|
||||
--color-tab-text: #FDFFFF;
|
||||
// 智能体标签文字颜色(未选中)
|
||||
--color-tab-text-inactive: rgba(253, 255, 255, 0.4);
|
||||
// 任务编辑框悬浮边框颜色
|
||||
--color-task-edit-hover-border: #ADA9A7;
|
||||
// 编辑图标背景色
|
||||
--color-edit-icon-bg: rgba(255, 255, 255, 0.5);
|
||||
|
||||
--el-fill-color-blank: transparent !important;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user