feat:1.数据库存储功能添加(初版)2.后端REST API版本代码清理

This commit is contained in:
liailing1026
2026-02-25 10:55:51 +08:00
parent f736cd104a
commit 2140cfaf92
35 changed files with 3912 additions and 2981 deletions

View File

@@ -36,6 +36,7 @@ const handleFileSelect = (event: Event) => {
if (input.files && input.files[0]) {
const file = input.files[0]
readFileContent(file)
input.value = ''
}
}

View File

@@ -0,0 +1,376 @@
<template>
<div class="history-list">
<div class="header">
<h3>📋 历史任务</h3>
<el-button type="primary" link @click="fetchPlans">
<el-icon><Refresh /></el-icon>
刷新
</el-button>
</div>
<!-- 加载状态 -->
<div v-if="loading" class="loading">
<el-icon class="is-loading"><Loading /></el-icon>
<span>加载中...</span>
</div>
<!-- 空状态 -->
<el-empty v-else-if="plans.length === 0" description="暂无历史任务" />
<!-- 任务列表 -->
<div v-else class="plan-list">
<div
v-for="plan in plans"
:key="plan.id"
class="plan-item"
:class="{ active: selectedPlanId === plan.id }"
@click="selectPlan(plan)"
>
<div class="plan-info">
<div class="plan-goal">{{ plan.general_goal || '未知任务' }}</div>
<div class="plan-meta">
<el-tag size="small" :type="getStatusType(plan.status)">
{{ getStatusText(plan.status) }}
</el-tag>
<span class="plan-time">{{ formatTime(plan.created_at) }}</span>
</div>
<div class="plan-stats">
<span>执行次数: {{ plan.execution_count }}</span>
</div>
</div>
<div class="plan-actions">
<el-button
type="primary"
size="small"
@click.stop="restorePlan(plan)"
:disabled="restoring"
>
恢复
</el-button>
<el-button
type="danger"
size="small"
link
@click.stop="deletePlan(plan)"
>
<el-icon><Delete /></el-icon>
</el-button>
</div>
</div>
</div>
<!-- 分页 -->
<div v-if="plans.length > 0" class="pagination">
<span> {{ total }} 个任务</span>
</div>
<!-- 删除确认对话框 -->
<el-dialog
v-model="dialogVisible"
title="删除确认"
width="400px"
:close-on-click-modal="false"
>
<span>确定要删除任务 "{{ planToDelete?.general_goal }}" 此操作不可恢复</span>
<template #footer>
<el-button @click="dialogVisible = false">取消</el-button>
<el-button type="danger" @click="confirmDelete" :loading="deleting">确定删除</el-button>
</template>
</el-dialog>
</div>
</template>
<script setup lang="ts">
import { ref, onMounted, onUnmounted } from 'vue'
import { ElMessage } from 'element-plus'
import { Refresh, Loading, Delete } from '@element-plus/icons-vue'
import websocket from '@/utils/websocket'
// 事件定义
const emit = defineEmits<{
(e: 'restore', plan: PlanInfo): void
(e: 'close'): void
}>()
// 数据类型
interface PlanInfo {
id: string // 对应数据库 task_id
general_goal: string // 对应数据库 query
status: string
execution_count: number
created_at: string
// 完整恢复数据
task_outline?: any
assigned_agents?: any
agent_scores?: any
agents_info?: any[]
}
// 响应式数据
const plans = ref<PlanInfo[]>([])
const loading = ref(false)
const restoring = ref(false)
const selectedPlanId = ref<string | null>(null)
const total = ref(0)
const isConnected = ref(false)
// 删除对话框相关
const dialogVisible = ref(false)
const planToDelete = ref<PlanInfo | null>(null)
const deleting = ref(false)
// 生成唯一请求ID
let requestIdCounter = 0
const generateRequestId = () => `ws_req_${Date.now()}_${++requestIdCounter}`
// WebSocket 监听器引用(用于清理)
const historyUpdatedHandler = () => {
console.log('📡 收到历史列表更新通知,自动刷新')
fetchPlans()
}
// 获取任务列表
const fetchPlans = async () => {
loading.value = true
const reqId = generateRequestId()
try {
const result = await websocket.send('get_plans', { id: reqId })
plans.value = (result.data || []) as PlanInfo[]
total.value = plans.value.length
isConnected.value = true
} catch (error) {
console.error('获取任务列表失败:', error)
ElMessage.error('获取任务列表失败')
isConnected.value = false
} finally {
loading.value = false
}
}
// 选择任务
const selectPlan = (plan: PlanInfo) => {
selectedPlanId.value = plan.id
}
// 恢复任务
const restorePlan = async (plan: PlanInfo) => {
if (restoring.value) return
console.log('🔍 [HistoryList] 恢复计划:', plan)
console.log('🔍 [HistoryList] plan.id:', plan.id)
if (!plan.id) {
ElMessage.error('任务 ID 为空,无法恢复')
return
}
restoring.value = true
selectedPlanId.value = plan.id
const reqId = generateRequestId()
try {
const result = await websocket.send('restore_plan', {
id: reqId,
data: { plan_id: plan.id }
})
const planData = (result.data || result) as any
// 发送恢复事件
emit('restore', {
...plan,
...planData
})
ElMessage.success('任务已恢复')
} catch (error) {
console.error('恢复任务失败:', error)
ElMessage.error('恢复任务失败')
} finally {
restoring.value = false
}
}
// 删除任务
const deletePlan = (plan: PlanInfo) => {
planToDelete.value = plan
dialogVisible.value = true
}
// 确认删除
const confirmDelete = async () => {
if (!planToDelete.value) return
deleting.value = true
const reqId = generateRequestId()
try {
await websocket.send('delete_plan', {
id: reqId,
data: { plan_id: planToDelete.value.id }
})
dialogVisible.value = false
ElMessage.success('删除成功')
// 删除成功后会自动通过 history_updated 事件刷新列表
} catch (error) {
console.error('删除任务失败:', error)
ElMessage.error('删除任务失败')
} finally {
deleting.value = false
}
}
// 格式化时间
const formatTime = (timeStr: string | undefined) => {
if (!timeStr) return ''
try {
const date = new Date(timeStr)
return date.toLocaleString('zh-CN', {
month: '2-digit',
day: '2-digit',
hour: '2-digit',
minute: '2-digit'
})
} catch {
return timeStr
}
}
// 获取状态类型
const getStatusType = (status: string): string => {
const statusMap: Record<string, string> = {
'generating': 'warning',
'executing': 'warning',
'completed': 'success',
'stopped': 'danger'
}
return statusMap[status] || 'info'
}
// 获取状态文本
const getStatusText = (status: string): string => {
const statusMap: Record<string, string> = {
'generating': '生成中',
'executing': '执行中',
'completed': '已完成',
'stopped': '已停止'
}
return statusMap[status] || status
}
// 生命周期
onMounted(() => {
fetchPlans()
// 监听历史列表更新事件(多标签页实时同步)
websocket.on('history_updated', historyUpdatedHandler)
})
onUnmounted(() => {
// 移除事件监听
websocket.off('history_updated', historyUpdatedHandler)
})
</script>
<style scoped lang="scss">
.history-list {
padding: 16px;
height: 100%;
display: flex;
flex-direction: column;
}
.header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 16px;
h3 {
margin: 0;
font-size: 16px;
font-weight: 600;
}
}
.loading {
display: flex;
align-items: center;
justify-content: center;
padding: 40px;
color: #909399;
gap: 8px;
}
.plan-list {
flex: 1;
overflow-y: auto;
}
.plan-item {
display: flex;
justify-content: space-between;
align-items: center;
padding: 12px;
margin-bottom: 8px;
border-radius: 8px;
background: #f5f7fa;
cursor: pointer;
transition: all 0.2s;
&:hover {
background: #ecf5ff;
}
&.active {
background: #ecf5ff;
border-left: 3px solid #409eff;
}
}
.plan-info {
flex: 1;
min-width: 0;
}
.plan-goal {
font-size: 14px;
font-weight: 500;
margin-bottom: 6px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.plan-meta {
display: flex;
align-items: center;
gap: 8px;
margin-bottom: 4px;
}
.plan-time {
font-size: 12px;
color: #909399;
}
.plan-stats {
font-size: 12px;
color: #606266;
}
.plan-actions {
display: flex;
gap: 8px;
margin-left: 12px;
}
.pagination {
display: flex;
justify-content: center;
margin-top: 16px;
color: #909399;
font-size: 13px;
}
</style>

View File

@@ -7,6 +7,7 @@ import {
useAgentsStore,
useSelectionStore,
type IRawStepTask,
type TaskProcess,
type IApiAgentAction
} from '@/stores'
import { getAgentMapIcon, getActionTypeDisplay } from '@/layout/components/config.ts'
@@ -32,9 +33,6 @@ const branchInputRef = ref<InstanceType<typeof HTMLInputElement>>()
// 分支加载状态
const branchLoading = ref(false)
// Mock 数据配置
const USE_MOCK_DATA = false
// 节点和边数据
const nodes = ref<Node[]>([])
const edges = ref<Edge[]>([])
@@ -51,6 +49,191 @@ const BRANCHES_INIT_KEY_PREFIX = 'plan-task-branches-initialized-'
//最后选中的分支ID
const LAST_SELECTED_BRANCH_KEY = 'plan-task-last-selected-branch'
// ==================== 公共函数提取 ====================
// 获取 agent 个人简介
const getAgentProfile = (agentName: string): string => {
return (
agentsStore.agents.find((agent: any) => agent.Name === agentName)?.Profile || '暂无个人简介'
)
}
// 创建 VueFlow 边的通用函数
const createFlowEdge = (
source: string,
target: string,
sourceHandle: string = 'right',
targetHandle: string = 'left',
strokeColor: string = '#43a8aa',
isFirstNode: boolean = false
): Edge => {
return {
id: `edge-${source}-${target}`,
source,
target,
sourceHandle,
targetHandle,
type: 'smoothstep',
animated: true,
style: {
stroke: strokeColor,
strokeWidth: 2,
...(isFirstNode ? { strokeDasharray: '5,5' } : {})
},
markerEnd: {
type: 'arrow' as any,
color: '#43a8aa',
width: 20,
height: 20,
strokeWidth: 2
}
}
}
// 创建分支 agent 节点的通用函数
const createBranchAgentNode = (
agentNodeId: string,
action: IApiAgentAction,
position: { x: number; y: number }
): Node => {
const agentInfo = getAgentMapIcon(action.agent)
const actionTypeInfo = getActionTypeDisplay(action.type)
const agentProfile = getAgentProfile(action.agent)
return {
id: agentNodeId,
type: 'agent',
position,
data: {
agentName: action.agent,
agentIcon: agentInfo.icon,
agentColor: agentInfo.color,
actionTypeColor: actionTypeInfo?.color || '#909399',
agentDescription: action.description || '暂无描述',
agentProfile: agentProfile,
actionTypeName: actionTypeInfo?.name || '未知职责',
actionTypeKey: actionTypeInfo?.key || 'unknown',
isRoot: false,
isBranchTask: true
}
}
}
// 保存分支到 store 的通用函数
const saveBranchToStore = (
branchType: 'root' | 'task',
newBranchNodes: Node[],
newBranchEdges: Edge[],
branchTasks: any[]
) => {
if (newBranchNodes.length === 0) return
const taskStepId = currentTask.value?.Id || ''
const currentAgents = currentTask.value?.AgentSelection || []
isSyncing = true
selectionStore.addTaskProcessBranch(taskStepId, currentAgents, {
parentNodeId: addingBranchNodeId.value!,
branchContent: branchInput.value.trim(),
branchType,
nodes: JSON.parse(JSON.stringify(newBranchNodes)),
edges: JSON.parse(JSON.stringify(newBranchEdges)),
tasks: JSON.parse(JSON.stringify(branchTasks))
})
setTimeout(() => {
isSyncing = false
}, 100)
saveTaskProcessBranchesToDB()
}
// 解析分支 API 响应的通用函数
const parseBranchResponse = (response: any): IApiAgentAction[] => {
const newAgentActions: IApiAgentAction[] = []
const responseData = response.data || response
if (responseData && responseData.length > 0) {
const firstBranch = responseData[0]
firstBranch.forEach((action: any) => {
newAgentActions.push({
id: action.ID || uuidv4(),
type: action.ActionType,
agent: action.AgentName,
description: action.Description,
inputs: action.ImportantInput || []
})
})
}
return newAgentActions
}
// 获取主流程节点 ID 列表的通用函数
const getMainProcessNodeIds = (nodeList?: Node[]): string[] => {
const targetNodes = nodeList || nodes.value
return targetNodes.filter(n => !n.data.isBranchTask && n.id !== 'root').map(n => n.id)
}
// 设置主流程高亮的通用函数
const highlightMainProcess = (nodeList?: Node[]) => {
const mainProcessNodes = getMainProcessNodeIds(nodeList)
selectedNodeIds.value = new Set(mainProcessNodes)
}
// 创建分支节点和边的通用函数
const createBranchNodesAndEdges = (
newAgentActions: IApiAgentAction[],
branchStartX: number,
branchStartY: number,
parentNodeId: string,
strokeColor: string = '#67c23a'
): { nodes: Node[]; edges: Edge[] } => {
const newBranchNodes: Node[] = []
const newBranchEdges: Edge[] = []
const timestamp = Date.now()
const agentNodeIds: string[] = []
newAgentActions.forEach((action, index) => {
const agentNodeId = `branch-agent-${parentNodeId}-${timestamp}-${index}`
agentNodeIds.push(agentNodeId)
// 计算位置:横向排列
const nodeX = branchStartX + (parentNodeId === 'root' ? 100 : 0) + index * 120
const nodeY = branchStartY
// 创建 agent 节点
const newAgentNode = createBranchAgentNode(agentNodeId, action, { x: nodeX, y: nodeY })
nodes.value.push(newAgentNode)
newBranchNodes.push(newAgentNode)
// 创建连接边
if (index === 0) {
// 第一个 agent 连接到父节点
const newEdge = createFlowEdge(parentNodeId, agentNodeId, 'bottom', 'left', strokeColor, true)
edges.value.push(newEdge)
newBranchEdges.push(newEdge)
} else {
// 后续 agent 连接到前一个 agent
const prevAgentNodeId = agentNodeIds[index - 1]!
const newEdge = createFlowEdge(
prevAgentNodeId,
agentNodeId,
'right',
'left',
strokeColor,
false
)
edges.value.push(newEdge)
newBranchEdges.push(newEdge)
}
})
return { nodes: newBranchNodes, edges: newBranchEdges }
}
// ==================== 原有函数保持不变 ====================
// 获取分支的所有节点
const getAllBranchNodes = (startNodeId: string): string[] => {
const visited = new Set<string>()
@@ -272,14 +455,44 @@ const initializeFlow = () => {
)}`
const branchesInitialized = sessionStorage.getItem(branchesInitKey) === 'true'
if (branchesInitialized && savedBranches.length > 0) {
nodes.value = []
edges.value = []
// 【修复】优先检查 store 中是否有数据,而不是依赖 sessionStorage 标记
// 原逻辑if (branchesInitialized && savedBranches.length > 0)
// 问题从历史记录恢复时store 有数据但 sessionStorage 无标记,导致重复创建"初始流程"分支
if (savedBranches.length > 0) {
// 先创建根节点和主流程节点(作为分支的基础)
nodes.value = newNodes
edges.value = newEdges
savedBranches.forEach(branch => {
// 恢复节点
branch.nodes.forEach(node => {
nodes.value.push(JSON.parse(JSON.stringify(node)))
// 找出非初始流程的分支(只处理真正需要偏移的分支)
const actualBranches = savedBranches.filter(branch => branch.branchContent !== '初始流程')
// 统计每个父节点下有多少个分支,用于计算垂直偏移
const parentBranchIndex: Record<string, number> = {}
actualBranches.forEach(branch => {
parentBranchIndex[branch.parentNodeId] = parentBranchIndex[branch.parentNodeId] || 0
})
// 恢复分支时,按顺序处理,同一父节点的分支依次向下偏移
actualBranches.forEach(branch => {
const parentNodeId = branch.parentNodeId
// 获取父节点的Y坐标
const parentNode = nodes.value.find(n => n.id === parentNodeId)
const parentNodeY = parentNode?.position.y || 150
// 获取该分支在同一父节点下的索引
const branchIndex = parentBranchIndex[parentNodeId] ?? 0
parentBranchIndex[parentNodeId] = branchIndex + 1
// 计算分支起始Y坐标父节点Y + 200 + 分支索引 * 250
const branchStartY = parentNodeY + 200 + branchIndex * 250
// 恢复节点计算新的Y坐标
branch.nodes.forEach((node, nodeIndex) => {
const restoredNode = JSON.parse(JSON.stringify(node))
// 计算新位置分支起始Y + 节点索引 * 120横向排列
restoredNode.position.y = branchStartY
nodes.value.push(restoredNode)
})
// 恢复边
branch.edges.forEach(edge => {
@@ -287,6 +500,11 @@ const initializeFlow = () => {
})
})
// 【修复】补充标记已初始化,避免后续重复创建"初始流程"分支
if (!branchesInitialized) {
sessionStorage.setItem(branchesInitKey, 'true')
}
// 恢复最后选中的分支
const lastSelectedBranchId = sessionStorage.getItem(LAST_SELECTED_BRANCH_KEY)
if (lastSelectedBranchId) {
@@ -297,9 +515,7 @@ const initializeFlow = () => {
if (lastSelectedBranch.branchContent === '初始流程') {
// 初始流程高亮所有主流程节点从恢复的nodes中获取
nodesToHighlight = nodes.value
.filter(n => !n.data.isBranchTask && n.id !== 'root')
.map(n => n.id)
nodesToHighlight = getMainProcessNodeIds()
} else {
// 其他分支:高亮主流程路径 + 分支节点(支持多级分支)
const firstBranchNode = lastSelectedBranch.nodes[0]
@@ -311,7 +527,7 @@ const initializeFlow = () => {
let currentNode = nodes.value.find(n => n.id === incomingEdge.source)
while (currentNode && currentNode.id !== 'root') {
nodesToHighlight.unshift(currentNode.id)
const prevEdge = edges.value.find(e => e.target === currentNode.id)
const prevEdge = edges.value.find(e => e.target === currentNode?.id)
currentNode = prevEdge
? nodes.value.find(n => n.id === prevEdge.source)
: undefined
@@ -330,17 +546,11 @@ const initializeFlow = () => {
selectedNodeIds.value = new Set(nodesToHighlight)
} else {
// 找不到最后选中的分支,默认选中初始流程的高亮状态
const mainProcessNodes = nodes.value
.filter(n => !n.data.isBranchTask && n.id !== 'root')
.map(n => n.id)
selectedNodeIds.value = new Set(mainProcessNodes)
highlightMainProcess()
}
} else {
// 没有保存的选中分支,默认选中初始流程的高亮状态
const mainProcessNodes = nodes.value
.filter(n => !n.data.isBranchTask && n.id !== 'root')
.map(n => n.id)
selectedNodeIds.value = new Set(mainProcessNodes)
highlightMainProcess()
}
} else {
// 首次初始化:设置节点和边,并保存为"初始流程"分支
@@ -362,14 +572,14 @@ const initializeFlow = () => {
// 标记已初始化(针对该任务步骤和 agent 组合)
sessionStorage.setItem(branchesInitKey, 'true')
// 保存任务过程分支到数据库
saveTaskProcessBranchesToDB()
// 首次初始化时,设置初始流程为当前选中分支
selectionStore.setActiveTaskProcessBranch(taskStepId, currentAgents, initialBranchId)
// 默认选中"初始流程"的高亮状态
const mainProcessNodes = newNodes
.filter(n => !n.data.isBranchTask && n.id !== 'root')
.map(n => n.id)
selectedNodeIds.value = new Set(mainProcessNodes)
highlightMainProcess(newNodes)
}
}
}
@@ -426,7 +636,7 @@ const onNodeClick = (event: any) => {
let topBranchNodeId: string | null = null
if (branchParentChain.length > 0) {
// 取父节点链的最后一个(最顶层的分支节点)
topBranchNodeId = branchParentChain[branchParentChain.length - 1]
topBranchNodeId = branchParentChain[branchParentChain.length - 1]!
} else {
// 如果没有分支父节点,当前节点就是最顶层
topBranchNodeId = nodeId
@@ -460,8 +670,12 @@ const onNodeClick = (event: any) => {
if (currentTask.value) {
const completeTaskProcess: any[] = []
// 类型守卫:检查是否为 TaskProcess 类型(有 Description 字段)
const isTaskProcess = (data: any): data is TaskProcess =>
data && 'Description' in data
// 从 store 中获取"初始流程"副本(而不是使用 taskProcess.value
const taskStepId = currentTask.value.Id
const taskStepId = currentTask.value.Id!
const currentAgents = currentTask.value.AgentSelection || []
const branches = selectionStore.getTaskProcessBranches(taskStepId, currentAgents)
const initialBranch = branches.find(branch => branch.branchContent === '初始流程')
@@ -478,7 +692,7 @@ const onNodeClick = (event: any) => {
// 主流程节点:从初始流程数据中获取
const originalIndex = node.data.originalIndex
const processData = mainProcessData[originalIndex]
if (processData && processData.ID && processData.AgentName && processData.Description) {
if (processData && isTaskProcess(processData) && processData.ID && processData.AgentName && processData.Description) {
completeTaskProcess.push(processData)
}
} else if (node.data.isBranchTask) {
@@ -490,7 +704,7 @@ const onNodeClick = (event: any) => {
const nodeIndex = parentBranch.nodes.findIndex(n => n.id === nodeId)
if (nodeIndex !== -1 && parentBranch.tasks[nodeIndex]) {
const taskData = parentBranch.tasks[nodeIndex]
if (taskData.ID && taskData.AgentName && taskData.Description) {
if (isTaskProcess(taskData) && taskData.ID && taskData.AgentName && taskData.Description) {
completeTaskProcess.push(taskData)
}
}
@@ -508,7 +722,7 @@ const onNodeClick = (event: any) => {
}
selectionStore.setActiveTaskProcessData(
currentTask.value.Id,
taskStepId,
currentAgents,
completeTaskProcess
)
@@ -521,11 +735,7 @@ const onNodeClick = (event: any) => {
}
} else {
// 点击的是主流程节点,高亮所有主流程节点(初始流程)
const mainProcessNodes = nodes.value
.filter(n => !n.data.isBranchTask && n.id !== 'root')
.map(n => n.id)
selectedNodeIds.value = new Set(mainProcessNodes)
highlightMainProcess()
// 点击主流程节点时,从 store 读取"初始流程"分支的副本
if (currentTask.value) {
@@ -542,7 +752,7 @@ const onNodeClick = (event: any) => {
selectionStore.setActiveTaskProcessData(taskStepId, currentAgents, initialBranch.tasks)
// 同步更新 currentTask.TaskProcess实现全局数据联动
agentsStore.setCurrentTaskProcess(initialBranch.tasks)
agentsStore.setCurrentTaskProcess(initialBranch.tasks as unknown as TaskProcess[])
// 保存选中的分支ID到 sessionStorage
sessionStorage.setItem(LAST_SELECTED_BRANCH_KEY, initialBranch.id)
@@ -626,181 +836,48 @@ const submitBranch = async () => {
// 判断是根节点还是 agent 节点
if (parentNodeId === 'root') {
// 根节点分支
let newAgentActions: IApiAgentAction[] = []
if (USE_MOCK_DATA) {
// 使用 Mock API
const generalGoal = agentsStore.agentRawPlan.data?.['General Goal'] || ''
// 调用真实 API
const generalGoal = agentsStore.agentRawPlan.data?.['General Goal'] || ''
// 根节点分支:从零开始生成完整方案
// Baseline_Completion = 0 表示没有已完成的部分,需要生成所有阶段
// Existing_Steps 传空数组,不传递初始流程信息
const response = await api.mockBranchTaskProcess({
branch_Number: 1,
Modification_Requirement: branchContent,
Existing_Steps: [], // ← 根节点分支不传递现有步骤
Baseline_Completion: 0, // ← 从零开始
stepTaskExisting: currentTask.value,
goal: generalGoal
})
// 根节点分支:从零开始生成完整方案
// Baseline_Completion = 0 表示没有已完成的部分,需要生成所有阶段
// Existing_Steps 传空数组,不传递初始流程信息
const response = await api.branchTaskProcess({
branch_Number: 1,
Modification_Requirement: branchContent,
Existing_Steps: [], // ← 根节点分支不传递现有步骤
Baseline_Completion: 0, // ← 从零开始
stepTaskExisting: currentTask.value,
goal: generalGoal
})
// 后端返回格式: [[action1, action2], [action3, action4]]
// 取第一个分支
if (response && response.length > 0) {
const firstBranch = response[0]
// WebSocket 返回格式: { data: [[action1, action2], [action3, action4]], ... }
// REST API 返回格式: [[action1, action2], [action3, action4]]
// 使用通用函数解析 API 响应
const newAgentActions = parseBranchResponse(response)
// 直接遍历 action 数组
firstBranch.forEach((action: any) => {
// 直接使用接口返回的 ActionType
newAgentActions.push({
id: action.ID || uuidv4(),
type: action.ActionType,
agent: action.AgentName,
description: action.Description,
inputs: action.ImportantInput || []
})
})
}
ElMessage.success('[Mock] 任务流程分支创建成功')
} else {
// 调用真实 API
const generalGoal = agentsStore.agentRawPlan.data?.['General Goal'] || ''
// 根节点分支:从零开始生成完整方案
// Baseline_Completion = 0 表示没有已完成的部分,需要生成所有阶段
// Existing_Steps 传空数组,不传递初始流程信息
const response = await api.branchTaskProcess({
branch_Number: 1,
Modification_Requirement: branchContent,
Existing_Steps: [], // ← 根节点分支不传递现有步骤
Baseline_Completion: 0, // ← 从零开始
stepTaskExisting: currentTask.value,
goal: generalGoal
})
// WebSocket 返回格式: { data: [[action1, action2], [action3, action4]], ... }
// REST API 返回格式: [[action1, action2], [action3, action4]]
const responseData = response.data || response
// 后端返回格式: [[action1, action2], [action3, action4]]
// 取第一个分支
if (responseData && responseData.length > 0) {
const firstBranch = responseData[0]
// 直接遍历 action 数组
firstBranch.forEach((action: any) => {
// 直接使用接口返回的 ActionType
newAgentActions.push({
id: action.ID || uuidv4(),
type: action.ActionType,
agent: action.AgentName,
description: action.Description,
inputs: action.ImportantInput || []
})
})
}
ElMessage.success('任务流程分支创建成功')
}
ElMessage.success('任务流程分支创建成功')
// 创建新的 agent 节点
if (newAgentActions.length > 0) {
// 计算分支起始位置:在根节点下方(固定位置)
const branchStartX = parentNode.position.x
const branchStartY = parentNode.position.y + 200 // 固定位置父节点下方200px
const timestamp = Date.now()
const agentNodeIds: string[] = []
const branchStartY = parentNode.position.y + 200
// 为每个动作创建一个 agent 节点
newAgentActions.forEach((action, index) => {
const agentNodeId = `branch-agent-${parentNodeId}-${timestamp}-${index}`
agentNodeIds.push(agentNodeId)
const agentInfo = getAgentMapIcon(action.agent)
const actionTypeInfo = getActionTypeDisplay(action.type)
// 从 agentsStore 中获取 agent 的个人简介
const agentProfile =
agentsStore.agents.find((agent: any) => agent.Name === action.agent)?.Profile ||
'暂无个人简介'
// 计算位置:横向排列
const nodeX = branchStartX + 100 + index * 120
const nodeY = branchStartY
// 创建 agent 节点
const newAgentNode: Node = {
id: agentNodeId,
type: 'agent',
position: { x: nodeX, y: nodeY },
data: {
agentName: action.agent,
agentIcon: agentInfo.icon,
agentColor: agentInfo.color,
actionTypeColor: actionTypeInfo?.color || '#909399',
agentDescription: action.description || '暂无描述',
agentProfile: agentProfile,
actionTypeName: actionTypeInfo?.name || '未知职责',
actionTypeKey: actionTypeInfo?.key || 'unknown',
isRoot: false,
isBranchTask: true // 标记为分支任务
}
}
nodes.value.push(newAgentNode)
newBranchNodes.push(newAgentNode)
// 创建连接边
if (index === 0) {
// 第一个 agent 连接到父节点(根节点)
const newEdge: Edge = {
id: `edge-${parentNodeId}-${agentNodeId}`,
source: parentNodeId,
target: agentNodeId,
sourceHandle: 'bottom',
targetHandle: 'left',
type: 'smoothstep',
animated: true,
style: { stroke: '#67c23a', strokeWidth: 2, strokeDasharray: '5,5' },
markerEnd: {
type: 'arrow' as any,
color: '#43a8aa',
width: 20,
height: 20,
strokeWidth: 2
}
}
edges.value.push(newEdge)
newBranchEdges.push(newEdge)
} else {
// 后续 agent 连接到前一个 agent
const prevAgentNodeId = agentNodeIds[index - 1]
const newEdge: Edge = {
id: `edge-${prevAgentNodeId}-${agentNodeId}`,
source: prevAgentNodeId,
target: agentNodeId,
sourceHandle: 'right',
targetHandle: 'left',
type: 'smoothstep',
animated: true,
style: { stroke: '#67c23a', strokeWidth: 2 },
markerEnd: {
type: 'arrow' as any,
color: '#43a8aa',
width: 20,
height: 20,
strokeWidth: 2
}
}
edges.value.push(newEdge)
newBranchEdges.push(newEdge)
}
})
// 使用通用函数创建分支节点和边
const result = createBranchNodesAndEdges(
newAgentActions,
branchStartX,
branchStartY,
parentNodeId,
'#67c23a' // 根节点分支颜色
)
newBranchNodes.push(...result.nodes)
newBranchEdges.push(...result.edges)
// 保存分支数据到 store
if (newBranchNodes.length > 0) {
// 将 IApiAgentAction 转换为 TaskProcess 格式用于存储
// 与 fill-step-task-mock.ts 中的 TaskProcess 格式保持一致
const branchTasks = newAgentActions.map(action => ({
ID: action.id || uuidv4(),
ActionType: action.type,
@@ -809,291 +886,94 @@ const submitBranch = async () => {
ImportantInput: action.inputs || []
}))
// 使用任务过程分支存储
// 注意:需要对 nodes 和 edges 进行深拷贝,避免保存响应式引用
const taskStepId = currentTask.value?.Id || ''
const currentAgents = currentTask.value?.AgentSelection || []
isSyncing = true // 设置标志,避免 watch 触发重复同步
selectionStore.addTaskProcessBranch(taskStepId, currentAgents, {
parentNodeId: parentNodeId,
branchContent: branchContent,
branchType: 'root',
nodes: JSON.parse(JSON.stringify(newBranchNodes)),
edges: JSON.parse(JSON.stringify(newBranchEdges)),
tasks: JSON.parse(JSON.stringify(branchTasks))
})
setTimeout(() => {
isSyncing = false
}, 100)
saveBranchToStore('root', newBranchNodes, newBranchEdges, branchTasks)
}
}
} else {
// Agent 节点分支
const parentIsBranchTask = parentNode.data.isBranchTask || false
const parentOriginalIndex = parentNode.data.originalIndex ?? 0
let newAgentActions: IApiAgentAction[] = []
if (USE_MOCK_DATA) {
// 使用 Mock API
const generalGoal = agentsStore.agentRawPlan.data?.['General Goal'] || ''
const currentTaskProcess = taskProcess.value || []
// 调用真实 API
const generalGoal = agentsStore.agentRawPlan.data?.['General Goal'] || ''
const currentTaskProcess = taskProcess.value || []
// 根据父节点类型构建 existingSteps
let existingSteps: any[] = []
let baselineCompletion = 100
// 根据父节点类型构建 existingSteps
let existingSteps: any[] = []
let baselineCompletion = 100
if (!parentIsBranchTask) {
// 父节点是主流程节点:传递主流程从 0 到父节点的步骤
baselineCompletion =
currentTaskProcess.length > 0
? Math.round(((parentOriginalIndex + 1) / currentTaskProcess.length) * 100)
: 100
if (!parentIsBranchTask) {
// 父节点是主流程节点:传递主流程从 0 到父节点的步骤
baselineCompletion =
currentTaskProcess.length > 0
? Math.round(((parentOriginalIndex + 1) / currentTaskProcess.length) * 100)
: 100
existingSteps = currentTaskProcess.slice(0, parentOriginalIndex + 1).map((p: any) => ({
ID: p.ID,
ActionType: p.ActionType,
AgentName: p.AgentName,
Description: p.Description,
ImportantInput: p.ImportantInput || []
}))
} else {
// 父节点是分支节点:从分支数据中获取步骤
const taskStepId = currentTask.value?.Id || ''
const currentAgents = currentTask.value?.AgentSelection || []
const branches = selectionStore.getTaskProcessBranches(taskStepId, currentAgents)
// 找到父节点所属的分支
const parentBranch = branches.find(branch =>
branch.nodes.some(n => n.id === parentNode.id)
)
if (parentBranch && parentBranch.tasks) {
// 获取分支中从第一个节点到父节点的所有步骤
const parentIndexInBranch = parentBranch.nodes.findIndex(n => n.id === parentNode.id)
existingSteps = parentBranch.tasks.slice(0, parentIndexInBranch + 1).map((p: any) => ({
ID: p.ID,
ActionType: p.ActionType,
AgentName: p.AgentName,
Description: p.Description,
ImportantInput: p.ImportantInput || []
}))
// 分支节点的基线完成度:根据主流程进度计算
baselineCompletion =
currentTaskProcess.length > 0
? Math.round(((parentOriginalIndex + 1) / currentTaskProcess.length) * 100)
: 100
}
}
// 调用 Mock API
const response = await api.mockBranchTaskProcess({
branch_Number: 1,
Modification_Requirement: branchContent,
Existing_Steps: existingSteps,
Baseline_Completion: baselineCompletion,
stepTaskExisting: currentTask.value,
goal: generalGoal
})
// 后端返回格式: [[action1, action2], [action3, action4]]
// 取第一个分支
if (response && response.length > 0) {
const firstBranch = response[0]
// 直接遍历 action 数组
firstBranch.forEach((action: any) => {
// 直接使用接口返回的 ActionType
newAgentActions.push({
id: action.ID || uuidv4(),
type: action.ActionType,
agent: action.AgentName,
description: action.Description,
inputs: action.ImportantInput || []
})
})
}
ElMessage.success('[Mock] 任务流程分支创建成功')
existingSteps = currentTaskProcess.slice(0, parentOriginalIndex + 1).map((p: any) => ({
ID: p.ID,
ActionType: p.ActionType,
AgentName: p.AgentName,
Description: p.Description,
ImportantInput: p.ImportantInput || []
}))
} else {
// 调用真实 API
const generalGoal = agentsStore.agentRawPlan.data?.['General Goal'] || ''
const currentTaskProcess = taskProcess.value || []
// 父节点是分支节点:从分支数据中获取步骤
const taskStepId = currentTask.value?.Id || ''
const currentAgents = currentTask.value?.AgentSelection || []
const branches = selectionStore.getTaskProcessBranches(taskStepId, currentAgents)
// 根据父节点类型构建 existingSteps
let existingSteps: any[] = []
let baselineCompletion = 100
// 找到父节点所属的分支
const parentBranch = branches.find(branch => branch.nodes.some(n => n.id === parentNode.id))
if (!parentIsBranchTask) {
// 父节点是主流程节点:传递主流程从 0 到父节点的步骤
baselineCompletion =
currentTaskProcess.length > 0
? Math.round(((parentOriginalIndex + 1) / currentTaskProcess.length) * 100)
: 100
existingSteps = currentTaskProcess.slice(0, parentOriginalIndex + 1).map((p: any) => ({
if (parentBranch && parentBranch.tasks) {
// 获取分支中从第一个节点到父节点的所有步骤
const parentIndexInBranch = parentBranch.nodes.findIndex(n => n.id === parentNode.id)
existingSteps = parentBranch.tasks.slice(0, parentIndexInBranch + 1).map((p: any) => ({
ID: p.ID,
ActionType: p.ActionType,
AgentName: p.AgentName,
Description: p.Description,
ImportantInput: p.ImportantInput || []
}))
} else {
// 父节点是分支节点:从分支数据中获取步骤
const taskStepId = currentTask.value?.Id || ''
const currentAgents = currentTask.value?.AgentSelection || []
const branches = selectionStore.getTaskProcessBranches(taskStepId, currentAgents)
// 找到父节点所属的分支
const parentBranch = branches.find(branch =>
branch.nodes.some(n => n.id === parentNode.id)
)
if (parentBranch && parentBranch.tasks) {
// 获取分支中从第一个节点到父节点的所有步骤
const parentIndexInBranch = parentBranch.nodes.findIndex(n => n.id === parentNode.id)
existingSteps = parentBranch.tasks.slice(0, parentIndexInBranch + 1).map((p: any) => ({
ID: p.ID,
ActionType: p.ActionType,
AgentName: p.AgentName,
Description: p.Description,
ImportantInput: p.ImportantInput || []
}))
// 分支节点的基线完成度:根据主流程进度计算
baselineCompletion =
currentTaskProcess.length > 0
? Math.round(((parentOriginalIndex + 1) / currentTaskProcess.length) * 100)
: 100
}
// 分支节点的基线完成度:根据主流程进度计算
baselineCompletion =
currentTaskProcess.length > 0
? Math.round(((parentOriginalIndex + 1) / currentTaskProcess.length) * 100)
: 100
}
const response = await api.branchTaskProcess({
branch_Number: 1,
Modification_Requirement: branchContent,
Existing_Steps: existingSteps,
Baseline_Completion: baselineCompletion,
stepTaskExisting: currentTask.value,
goal: generalGoal
})
// WebSocket 返回格式: { data: [[action1, action2], [action3, action4]], ... }
// REST API 返回格式: [[action1, action2], [action3, action4]]
const responseData = response.data || response
// 后端返回格式: [[action1, action2], [action3, action4]]
// 取第一个分支
if (responseData && responseData.length > 0) {
const firstBranch = responseData[0]
// 直接遍历 action 数组
firstBranch.forEach((action: any) => {
// 直接使用接口返回的 ActionType
newAgentActions.push({
id: action.ID || uuidv4(),
type: action.ActionType,
agent: action.AgentName,
description: action.Description,
inputs: action.ImportantInput || []
})
})
}
ElMessage.success('任务流程分支创建成功')
}
const response = await api.branchTaskProcess({
branch_Number: 1,
Modification_Requirement: branchContent,
Existing_Steps: existingSteps,
Baseline_Completion: baselineCompletion,
stepTaskExisting: currentTask.value,
goal: generalGoal
})
// 使用通用函数解析 API 响应
const newAgentActions = parseBranchResponse(response)
ElMessage.success('任务流程分支创建成功')
// 创建新的 agent 节点
if (newAgentActions.length > 0) {
// 计算分支起始位置:在父 agent 节点右下方(固定位置)
const branchStartX = parentNode.position.x + 150
const branchStartY = parentNode.position.y + 200 // 固定位置父节点下方200px
const timestamp = Date.now()
const agentNodeIds: string[] = []
const branchStartY = parentNode.position.y + 200
// 为每个动作创建一个 agent 节点
newAgentActions.forEach((action, index) => {
const agentNodeId = `branch-agent-${parentNodeId}-${timestamp}-${index}`
agentNodeIds.push(agentNodeId)
const agentInfo = getAgentMapIcon(action.agent)
const actionTypeInfo = getActionTypeDisplay(action.type)
// 从 agentsStore 中获取 agent 的个人简介
const agentProfile =
agentsStore.agents.find((agent: any) => agent.Name === action.agent)?.Profile ||
'暂无个人简介'
// 计算位置:横向排列
const nodeX = branchStartX + index * 120
const nodeY = branchStartY
// 创建 agent 节点
const newAgentNode: Node = {
id: agentNodeId,
type: 'agent',
position: { x: nodeX, y: nodeY },
data: {
agentName: action.agent,
agentIcon: agentInfo.icon,
agentColor: agentInfo.color,
actionTypeColor: actionTypeInfo?.color || '#909399',
agentDescription: action.description || '暂无描述',
agentProfile: agentProfile,
actionTypeName: actionTypeInfo?.name || '未知职责',
actionTypeKey: actionTypeInfo?.key || 'unknown',
isRoot: false,
isBranchTask: true // 标记为分支任务
}
}
nodes.value.push(newAgentNode)
newBranchNodes.push(newAgentNode)
// 创建连接边
if (index === 0) {
// 第一个 agent 连接到父节点
const newEdge: Edge = {
id: `edge-${parentNodeId}-${agentNodeId}`,
source: parentNodeId,
target: agentNodeId,
sourceHandle: 'bottom',
targetHandle: 'left',
type: 'smoothstep',
animated: true,
style: { stroke: '#409eff', strokeWidth: 2, strokeDasharray: '5,5' },
markerEnd: {
type: 'arrow' as any,
color: '#43a8aa',
width: 20,
height: 20,
strokeWidth: 2
}
}
edges.value.push(newEdge)
newBranchEdges.push(newEdge)
} else {
// 后续 agent 连接到前一个 agent
const prevAgentNodeId = agentNodeIds[index - 1]
const newEdge: Edge = {
id: `edge-${prevAgentNodeId}-${agentNodeId}`,
source: prevAgentNodeId,
target: agentNodeId,
sourceHandle: 'right',
targetHandle: 'left',
type: 'smoothstep',
animated: true,
style: { stroke: '#409eff', strokeWidth: 2 },
markerEnd: {
type: 'arrow' as any,
color: '#43a8aa',
width: 20,
height: 20,
strokeWidth: 2
}
}
edges.value.push(newEdge)
newBranchEdges.push(newEdge)
}
})
// 使用通用函数创建分支节点和边
const result = createBranchNodesAndEdges(
newAgentActions,
branchStartX,
branchStartY,
parentNodeId,
'#409eff' // Agent 节点分支颜色
)
newBranchNodes.push(...result.nodes)
newBranchEdges.push(...result.edges)
// 保存分支数据到 store
if (newBranchNodes.length > 0) {
@@ -1105,22 +985,7 @@ const submitBranch = async () => {
ImportantInput: action.inputs || []
}))
// 使用任务过程分支存储
// 注意:需要对 nodes 和 edges 进行深拷贝,避免保存响应式引用
const taskStepId = currentTask.value?.Id || ''
const currentAgents = currentTask.value?.AgentSelection || []
isSyncing = true // 设置标志,避免 watch 触发重复同步
selectionStore.addTaskProcessBranch(taskStepId, currentAgents, {
parentNodeId: parentNodeId,
branchContent: branchContent,
branchType: 'task',
nodes: JSON.parse(JSON.stringify(newBranchNodes)),
edges: JSON.parse(JSON.stringify(newBranchEdges)),
tasks: JSON.parse(JSON.stringify(branchTasks))
})
setTimeout(() => {
isSyncing = false
}, 100)
saveBranchToStore('task', newBranchNodes, newBranchEdges, branchTasks)
}
}
}
@@ -1154,6 +1019,20 @@ const handleBranchKeydown = (event: KeyboardEvent) => {
}
onConnect(params => addEdges(params))
// 保存任务过程分支到数据库
const saveTaskProcessBranchesToDB = async () => {
const TaskID = (window as any).__CURRENT_TASK_ID__
if (TaskID) {
try {
await selectionStore.saveTaskProcessBranchesToDB(TaskID)
} catch (error) {
console.error('保存任务过程分支数据失败:', error)
}
} else {
console.warn('[saveTaskProcessBranchesToDB] 未找到 TaskID跳过保存')
}
}
</script>
<template>

View File

@@ -1,183 +0,0 @@
// 模拟后端原始返回格式的 Mock 数据 - fill_stepTask_TaskProcess 接口
// 后端返回格式: IRawStepTask { StepName, TaskContent, InputObject_List, OutputObject, AgentSelection, TaskProcess, Collaboration_Brief_frontEnd }
import type { IRawStepTask } from '@/stores'
// TaskProcess 项格式
interface RawTaskProcessItem {
ID: string
ActionType: string
AgentName: string
Description: string
ImportantInput: string[]
}
// Collaboration_Brief_frontEnd 数据项格式
interface RawBriefDataItem {
text: string
color: number[] // [h, s, l]
}
// 后端返回的完整数据格式
export interface RawAgentTaskProcessResponse {
StepName: string
TaskContent: string
InputObject_List: string[]
OutputObject: string
AgentSelection: string[]
TaskProcess: RawTaskProcessItem[]
Collaboration_Brief_frontEnd?: {
template: string
data: Record<string, RawBriefDataItem>
}
}
// 模拟后端返回的原始数据结构(与后端缓存数据格式一致)
// 使用与 AgentAssignmentBackendMock 相同的 agent 列表
export const mockBackendAgentTaskProcessData: RawAgentTaskProcessResponse = {
StepName: '腐蚀类型识别',
TaskContent: '分析船舶制造中常见的材料腐蚀类型及其成因。',
InputObject_List: [],
OutputObject: '腐蚀类型及成因列表',
AgentSelection: ['腐蚀机理研究员', '实验材料学家', '防护工程专家'],
TaskProcess: [
{
ID: 'action_101',
ActionType: 'Propose',
AgentName: '腐蚀机理研究员',
Description: '分析海洋环境下的腐蚀机理,确定关键防护要素',
ImportantInput: ['海洋环境参数', '防护性能指标'],
},
{
ID: 'action_102',
ActionType: 'Critique',
AgentName: '实验材料学家',
Description: '基于腐蚀机理分析结果,设计涂层材料的基础配方',
ImportantInput: ['腐蚀机理分析结果', '涂层材料配方'],
},
{
ID: 'action_103',
ActionType: 'Improve',
AgentName: '防护工程专家',
Description: '筛选适用于防护涂层的二维材料,评估其性能潜力',
ImportantInput: ['材料配方设计', '涂层材料配方'],
},
{
ID: 'action_104',
ActionType: 'Finalize',
AgentName: '实验材料学家',
Description: '制定涂层材料性能测试实验方案,包括测试指标和方法',
ImportantInput: ['二维材料筛选结果', '防护性能指标'],
},
{
ID: 'action_105',
ActionType: 'Critique',
AgentName: '防护工程专家',
Description: '模拟海洋流体环境对涂层材料的影响,优化涂层结构',
ImportantInput: ['实验方案', '海洋环境参数'],
},
{
ID: 'action_106',
ActionType: 'Improve',
AgentName: '腐蚀机理研究员',
Description: '综合评估涂层材料的防护性能,提出改进建议',
ImportantInput: ['流体力学模拟结果', '实验材料学测试结果', '二维材料性能数据'],
},
{
ID: 'action_107',
ActionType: 'Improve',
AgentName: '实验材料学家',
Description: '整理研发数据和测试结果,撰写完整的研发报告',
ImportantInput: ['综合性能评估', '所有研发数据'],
},
],
Collaboration_Brief_frontEnd: {
template: '基于!<0>!、!<1>!和!<2>!!<3>!、!<4>!、!<5>!和!<6>!执行!<7>!任务,以获得!<8>!。',
data: {
'0': {
text: '涂层材料配方',
color: [120, 60, 70], // hsl(120, 60%, 70%)
},
'1': {
text: '海洋环境参数',
color: [120, 60, 70], // hsl(120, 60%, 70%)
},
'2': {
text: '防护性能指标',
color: [120, 60, 70], // hsl(120, 60%, 70%)
},
'3': {
text: '腐蚀机理研究员',
color: [0, 0, 90], // hsl(0, 0%, 90%)
},
'4': {
text: '先进材料研发员',
color: [0, 0, 90], // hsl(0, 0%, 90%)
},
'5': {
text: '二维材料科学家',
color: [0, 0, 90], // hsl(0, 0%, 90%)
},
'6': {
text: '实验材料学家',
color: [0, 0, 90], // hsl(0, 0%, 90%)
},
'7': {
text: '研发适用于海洋环境的耐腐蚀防护涂层材料,并进行性能测试与评估',
color: [0, 0, 87], // hsl(0, 0%, 87%)
},
'8': {
text: '防护涂层材料研发报告',
color: [30, 100, 80], // hsl(30, 100%, 80%)
},
},
},
}
// 模拟后端API调用 - fill_stepTask_TaskProcess
export const mockBackendFillAgentTaskProcess = async (
goal: string,
stepTask: any,
agents: string[],
): Promise<RawAgentTaskProcessResponse> => {
// 模拟网络延迟 500ms
await new Promise((resolve) => setTimeout(resolve, 500))
// 在真实场景中,后端会根据传入的 goal、stepTask 和 agents 生成不同的 TaskProcess
// 这里我们直接返回预设的 Mock 数据
// 可以根据传入的 agents 动态修改 AgentSelection 和 TaskProcess
// 确保 agents 数组不为空
const safeAgents = agents.length > 0 ? agents : ['腐蚀机理研究员']
const responseData: RawAgentTaskProcessResponse = {
...mockBackendAgentTaskProcessData,
AgentSelection: agents,
TaskProcess: mockBackendAgentTaskProcessData.TaskProcess.map((action, index) => ({
...action,
AgentName: safeAgents[index % safeAgents.length],
})),
Collaboration_Brief_frontEnd: mockBackendAgentTaskProcessData.Collaboration_Brief_frontEnd
? {
template: mockBackendAgentTaskProcessData.Collaboration_Brief_frontEnd.template,
data: { ...mockBackendAgentTaskProcessData.Collaboration_Brief_frontEnd.data },
}
: undefined,
}
// 更新 Collaboration_Brief_frontEnd.data 中的 agent 引用
if (responseData.Collaboration_Brief_frontEnd?.data) {
const agentCount = Math.min(safeAgents.length, 4) // 最多4个agent
for (let i = 0; i < agentCount; i++) {
const key = String(i + 3) // agent从索引3开始
if (responseData.Collaboration_Brief_frontEnd.data[key]) {
responseData.Collaboration_Brief_frontEnd.data[key] = {
...responseData.Collaboration_Brief_frontEnd.data[key],
text: safeAgents[i]!,
}
}
}
}
return responseData
}

View File

@@ -14,9 +14,15 @@ import websocket from '@/utils/websocket'
import Notification from '@/components/Notification/Notification.vue'
import { useNotification } from '@/composables/useNotification'
// 定义组件 props
const props = defineProps<{
TaskID?: string // 任务唯一标识,用于写入数据库
}>()
// 定义组件事件
const emit = defineEmits<{
(e: 'refreshLine'): void
(el: 'setCurrentTask', task: IRawStepTask): void
(e: 'setCurrentTask', task: IRawStepTask): void
}>()
const agentsStore = useAgentsStore()
@@ -411,7 +417,10 @@ async function executeBatchSteps(readySteps: IRawStepTask[]) {
isStreaming.value = true
currentExecutionId.value = executionId
},
currentExecutionId.value || undefined
currentExecutionId.value || undefined,
undefined,
undefined,
props.TaskID || undefined
)
})
}
@@ -961,7 +970,8 @@ async function restartFromStep(stepIndex: number) {
},
newExecutionId, // 传入前端生成的 execution_id
stepIndex,
truncatedLog
truncatedLog,
props.TaskID || undefined // 传入 TaskID 用于更新数据库
)
success('重新执行', `正在从步骤 ${stepIndex + 1} 重新执行...`)

View File

@@ -1,155 +0,0 @@
// branch_TaskProcess 接口的 Mock 数据和 Mock API
export interface BranchAction {
ID: string
ActionType: string
AgentName: string
Description: string
ImportantInput: string[]
}
export type BranchTaskProcessResponse = BranchAction[][]
// Mock 数据模拟后端返回的原始任务流程数据2D 数组)
// 格式:[[action1, action2], [action3, action4]]
const mockBranchTaskProcessDataRaw: BranchAction[][] = [
// 第一个任务分支方案
[
{
ID: 'agent3',
ActionType: 'Critique',
AgentName: '实验材料学家',
Description: '详细分析用户需求文档',
ImportantInput: ['agent2'],
},
{
ID: 'agent4',
ActionType: 'Critique',
AgentName: '腐蚀机理研究员',
Description: '设计系统整体架构',
ImportantInput: ['agent3'],
},
{
ID: 'agent5',
ActionType: 'Improve',
AgentName: '防护工程专家',
Description: '实现系统核心功能',
ImportantInput: ['agent4'],
},
{
ID: 'agent6',
ActionType: 'Finalize',
AgentName: '实验材料学家',
Description: '进行系统集成测试',
ImportantInput: ['agent5'],
},
],
// 第二个任务分支方案
[
{
ID: 'agent7',
ActionType: 'Critique',
AgentName: '实验材料学家',
Description: '深入分析用户需求和技术约束',
ImportantInput: ['agent2'],
},
{
ID: 'agent8',
ActionType: 'Critique',
AgentName: '防护工程专家',
Description: '设计系统技术架构和数据流',
ImportantInput: ['agent8'],
},
{
ID: 'agent9',
ActionType: 'Improve',
AgentName: '腐蚀机理研究员',
Description: '评估系统安全性',
ImportantInput: ['agent4'],
},
{
ID: 'agent10',
ActionType: 'Finalize',
AgentName: '实验材料学家',
Description: '完成系统安全测试',
ImportantInput: ['agent9'],
},
],
//第三个任务分支方案
[
{
ID: 'agent12',
ActionType: 'Critique',
AgentName: '腐蚀机理研究员',
Description: '设计系统整体架构',
ImportantInput: ['agent11'],
},
{
ID: 'agent13',
ActionType: 'Improve',
AgentName: '防护工程专家',
Description: '实现系统核心功能',
ImportantInput: ['agent12'],
},
{
ID: 'agent14',
ActionType: 'Finalize',
AgentName: '实验材料学家',
Description: '进行系统集成测试',
ImportantInput: ['agent13'],
},
],
]
/**
* Mock API模拟后端 branch_TaskProcess 接口调用
*
* @param branch_Number - 分支数量
* @param Modification_Requirement - 修改需求
* @param Existing_Steps - 现有步骤列表(包含完整信息的对象数组)
* @param Baseline_Completion - 基线完成度
* @param stepTaskExisting - 现有任务
* @param General_Goal - 总体目标
* @returns Promise<BranchAction[][]> - 返回 2D 数组,与后端格式完全一致
*/
export const mockBranchTaskProcessAPI = async (params: {
branch_Number: number
Modification_Requirement: string
Existing_Steps: BranchAction[]
Baseline_Completion: number
stepTaskExisting: any
General_Goal: string
}): Promise<BranchAction[][]> => {
// 模拟网络延迟 800ms
await new Promise((resolve) => setTimeout(resolve, 800))
console.log('[Mock API] branch_TaskProcess 调用参数:', params)
// 🆕 使用轮询方式选择分支方案(依次循环使用所有分支方案)
const totalBranches = mockBranchTaskProcessDataRaw.length
const sessionKey = `branch-task-process-index-${params.stepTaskExisting?.Id || 'default'}`
// 获取上一次的选择索引
let lastIndex = parseInt(sessionStorage.getItem(sessionKey) || '0')
// 计算本次的选择索引(轮询到下一个分支)
const selectedBranchIndex = (lastIndex + 1) % totalBranches
// 保存本次的选择索引
sessionStorage.setItem(sessionKey, selectedBranchIndex.toString())
const rawBranchData = mockBranchTaskProcessDataRaw[selectedBranchIndex] || []
console.log(
'[Mock API] branch_TaskProcess 选择分支方案:',
selectedBranchIndex + 1,
'/',
totalBranches,
)
console.log('[Mock API] branch_TaskProcess 返回数据:', rawBranchData)
// 直接返回 2D 数组,与后端格式完全一致
return [rawBranchData]
}
export default mockBranchTaskProcessAPI

View File

@@ -83,8 +83,23 @@ const handleSubmit = async () => {
}
try {
isAddingDimension.value = true
// 获取大任务ID和小任务信息
const dbTaskId = (window as any).__CURRENT_TASK_ID__
const stepTask = currentTask.value
? {
Id: currentTask.value.Id,
StepName: currentTask.value.StepName,
TaskContent: currentTask.value.TaskContent,
InputObject_List: currentTask.value.InputObject_List,
OutputObject: currentTask.value.OutputObject
}
: undefined
const response = await api.agentSelectModifyAddAspect({
aspectList: [...scoreDimensions.value, newDimension]
aspectList: [...scoreDimensions.value, newDimension],
stepTask: stepTask,
TaskID: dbTaskId
})
scoreDimensions.value.push(response.aspectName)
@@ -103,9 +118,10 @@ const handleSubmit = async () => {
})
//异步更新 store等前端显示完成后再更新避免触发重新初始化
await nextTick()
// 使用 Id 作为 key
const taskId = currentTask.value?.Id
//更新按任务ID的存储
//更新按任务Id的存储
if (taskId) {
const existingTaskData = agentsStore.getTaskScoreData(taskId)
if (existingTaskData) {
@@ -172,7 +188,28 @@ const confirmAgentSelection = async () => {
selectedAgents.value = new Set(existingGroup)
return
}
// 获取大任务ID
const dbTaskId = (window as any).__CURRENT_TASK_ID__
// 添加到内存 store
agentsStore.addConfirmedAgentGroup(currentTask.value.Id, agentArray)
// 🆕 保存 confirmed_groups 到数据库
if (dbTaskId && currentTask.value.Id) {
try {
const updatedGroups = agentsStore.getConfirmedAgentGroups(currentTask.value.Id)
await api.updateAssignedAgents({
task_id: dbTaskId,
step_id: currentTask.value.Id,
agents: agentArray,
confirmed_groups: updatedGroups
})
} catch (error) {
console.error('❌ 保存 confirmed_groups 失败:', error)
}
}
try {
isLoadingConfirm.value = true
const stepTaskForApi = agentsStore.createStepTaskForApi(agentArray)
@@ -186,7 +223,8 @@ const confirmAgentSelection = async () => {
const filledTask = await api.fillStepTaskTaskProcess({
goal,
stepTask: stepTaskForApi,
agents: agentArray
agents: agentArray,
TaskID: dbTaskId
})
selectionStore.setAgentTaskProcess(currentTask.value.Id, agentArray, filledTask)
} catch (error) {
@@ -212,6 +250,7 @@ const selectAgentGroup = async (agentNames: string[]) => {
if (currentTask.value?.Id && agentNames.length > 0) {
let taskProcessData = selectionStore.getAgentTaskProcess(currentTask.value.Id, agentNames)
// 如果 store 中没有数据,调用 API 生成
if (!taskProcessData) {
try {
isLoadingSelectGroup.value = true
@@ -225,9 +264,17 @@ const selectAgentGroup = async (agentNames: string[]) => {
const filledTask = await api.fillStepTaskTaskProcess({
goal,
stepTask: stepTaskForApi,
agents: agentNames
agents: agentNames,
TaskID: (window as any).__CURRENT_TASK_ID__
})
// 🆕 先存储到 store确保 getAgentCombinations 能获取到
// 注意:需要转换为后端期望的格式 { process, brief }
const groupKey = selectionStore.getAgentGroupKey(agentNames)
selectionStore.setAgentTaskProcess(currentTask.value.Id, agentNames, {
process: filledTask.process || [],
brief: filledTask.brief || {}
})
selectionStore.setAgentTaskProcess(currentTask.value.Id, agentNames, filledTask)
taskProcessData = filledTask
} catch (error) {
console.error('❌ 加载 TaskProcess 数据失败:', error)
@@ -239,10 +286,16 @@ const selectAgentGroup = async (agentNames: string[]) => {
if (taskProcessData) {
const convertedTaskProcess = convertToTaskProcess(taskProcessData.process || [])
agentsStore.updateCurrentAgentSelection(
// 🆕 获取所有 agent combinations包含新存储的数据
// getAgentCombinations 现在使用 stepId 作为第一层 key
const agentCombinations = selectionStore.getAgentCombinations(currentTask.value.Id)
await agentsStore.updateCurrentAgentSelection(
[...agentNames],
convertedTaskProcess,
taskProcessData.brief || currentTask.value.Collaboration_Brief_frontEnd
taskProcessData.brief || currentTask.value.Collaboration_Brief_frontEnd,
agentCombinations
)
}
@@ -345,9 +398,9 @@ const calculateAgentAverage = (agentData: AgentHeatmapData, selectedDimensions?:
// API调用 - 获取智能体评分数据
const fetchAgentScores = async () => {
// 使用 Id 作为 key
const taskId = currentTask.value?.Id
if (!taskId) {
console.warn('⚠️ fetchAgentScores: 当前任务没有 Id')
return null
}
//先检查 store 中是否有该任务的评分数据
@@ -369,14 +422,15 @@ const fetchAgentScores = async () => {
// 检查是否已停止
if (agentsStore.isStopping || agentsStore.hasStoppedFilling) {
console.log('检测到停止信号,跳过获取智能体评分')
return null
}
// 调用 API 获取评分(如果没有缓存或预加载)
const dbTaskId = (window as any).__CURRENT_TASK_ID__
const agentScores = await api.agentSelectModifyInit({
goal: agentsStore.agentRawPlan.data?.['General Goal'] || '',
stepTask: currentTask.value
stepTask: currentTask.value,
TaskID: dbTaskId
})
// 再次检查是否已停止API 调用后)
@@ -388,9 +442,7 @@ const fetchAgentScores = async () => {
const firstAgent = Object.keys(agentScores)[0]
const aspectList = firstAgent ? Object.keys(agentScores[firstAgent] || {}) : []
console.log('✅ 获取到的维度列表:', aspectList)
// 🆕 保存到 store按任务ID存储
// 🆕 保存到 store按 Id 存储)
agentsStore.setTaskScoreData(taskId, {
aspectList,
agentScores
@@ -438,7 +490,6 @@ const initHeatmapData = async () => {
// 获取接口数据
const scoreData = await fetchAgentScores()
if (!scoreData) {
console.warn('⚠️ initHeatmapData: 没有获取到评分数据')
return
}

View File

@@ -1,116 +0,0 @@
// Mock数据 - 用于agentSelectModifyAddAspect接口
// 模拟用户输入新维度后所有agent在该维度上的评分数据
import { vueAgentList } from './AgentAssignmentMock'
// 类型定义
export interface NewDimensionScore {
score: number
reason: string
}
export type NewDimensionScoreData = Record<string, NewDimensionScore>
// 模拟接口返回的数据结构
export interface AgentAddAspectResponse {
aspectName: string // 新添加的维度名称
agentScores: NewDimensionScoreData // 所有agent在该维度上的评分
}
// 生成指定维度名称的mock评分数据
export const generateMockDimensionScores = (dimensionName: string): AgentAddAspectResponse => {
const agentScores: NewDimensionScoreData = {}
vueAgentList.forEach((agent) => {
// 随机生成1-5的评分
const score = Math.floor(Math.random() * 5) + 1
// 根据评分生成不同的原因描述
let reason = ''
switch (score) {
case 5:
reason = `在"${dimensionName}"方面表现卓越,展现出杰出的能力和深刻的理解`
break
case 4:
reason = `在"${dimensionName}"方面表现优秀,具有良好的专业能力和执行力`
break
case 3:
reason = `在"${dimensionName}"方面表现合格,能够完成相关任务`
break
case 2:
reason = `在"${dimensionName}"方面表现一般,仍有提升空间`
break
case 1:
reason = `在"${dimensionName}"方面需要加强,建议进一步提升相关能力`
break
}
agentScores[agent] = { score, reason }
})
return {
aspectName: dimensionName,
agentScores,
}
}
// 预设的一些常用维度及其评分数据
export const presetDimensionScores: Record<string, AgentAddAspectResponse> = {
创新性: generateMockDimensionScores('创新性'),
技术能力: generateMockDimensionScores('技术能力'),
沟通技巧: generateMockDimensionScores('沟通技巧'),
问题解决: generateMockDimensionScores('问题解决'),
团队协作: generateMockDimensionScores('团队协作'),
学习能力: generateMockDimensionScores('学习能力'),
执行力: generateMockDimensionScores('执行力'),
责任心: generateMockDimensionScores('责任心'),
适应性: generateMockDimensionScores('适应性'),
领导力: generateMockDimensionScores('领导力'),
}
// 模拟API调用函数用于前端测试
export const mockAgentAddAspectApi = async (
aspectList: string[],
): Promise<AgentAddAspectResponse[]> => {
// 获取新增的维度(最后一个)
const newAspect = aspectList[aspectList.length - 1]
// 模拟网络延迟 500ms
await new Promise((resolve) => setTimeout(resolve, 20000))
// 如果是预设维度,返回预设数据
if (presetDimensionScores[newAspect]) {
return [presetDimensionScores[newAspect]]
}
// 否则动态生成新的评分数据
return [generateMockDimensionScores(newAspect)]
}
// Vue Composition API 兼容的hook
export const useAgentAddAspectMock = () => {
const addNewDimension = async (dimensionName: string) => {
const response = await mockAgentAddAspectApi([dimensionName])
return response[0]
}
const getMultipleDimensions = async (dimensionNames: string[]) => {
const responses: AgentAddAspectResponse[] = []
for (const dimension of dimensionNames) {
if (presetDimensionScores[dimension]) {
responses.push(presetDimensionScores[dimension])
} else {
responses.push(generateMockDimensionScores(dimension))
}
}
return responses
}
return {
addNewDimension,
getMultipleDimensions,
generateMockDimensionScores,
}
}

View File

@@ -1,192 +0,0 @@
// 模拟后端原始返回格式的Mock数据 - 维度 -> agent -> { Reason, Score }
import { vueAgentList, vueAspectList } from './AgentAssignmentMock'
// 后端返回的评分项格式
export interface BackendScoreItem {
Reason: string
Score: number
}
// 后端返回的完整数据格式
export type BackendAgentScoreResponse = Record<string, Record<string, BackendScoreItem>>
// 模拟后端返回的原始数据结构(维度 -> agent -> { Reason, Score }
export const mockBackendAgentScoreData: BackendAgentScoreResponse = {
: {
: { Reason: '展现出卓越的创造力和创新思维', Score: 4 },
: { Reason: '展现出杰出的创造性问题解决能力', Score: 5 },
: { Reason: '具有中等创造技能,有待提升', Score: 3 },
: { Reason: '在大多数情况下展现较强的创造性思维', Score: 4 },
: { Reason: '展现出胜任的创造能力', Score: 3 },
: { Reason: '具有较强的创造性表达能力', Score: 4 },
: { Reason: '擅长创新性思维方法', Score: 5 },
: { Reason: '展现出卓越的创造性思维和创新能力', Score: 5 },
: { Reason: '展现出良好的创造性问题解决能力', Score: 4 },
: { Reason: '展现出卓越的创造性问题解决能力', Score: 5 },
: { Reason: '展现出平衡的创造能力', Score: 4 },
: { Reason: '展现出卓越的创造天赋', Score: 5 },
: { Reason: '展现出胜任的创造性思维', Score: 3 },
: { Reason: '展现出较强的创造性主动性', Score: 4 },
: { Reason: '具有发展中的创造技能', Score: 3 },
: { Reason: '展现出卓越的创造愿景', Score: 5 },
: { Reason: '展现出卓越的创造性执行力', Score: 4 },
: { Reason: '具有较强的创造性问题解决能力', Score: 4 },
: { Reason: '展现出胜任的创造能力', Score: 3 },
},
: {
: { Reason: '展现出卓越的共情能力和社会意识', Score: 5 },
: { Reason: '具有较强的情绪调节和人际交往技能', Score: 4 },
: { Reason: '展现出卓越的情感智力', Score: 5 },
: { Reason: '在大多数情况下展现平均的情感智力', Score: 3 },
: { Reason: '具有良好的情绪意识和沟通能力', Score: 4 },
: { Reason: '在情绪意识方面偶尔表现不足', Score: 3 },
: { Reason: '具有较强的情绪理解能力', Score: 4 },
: { Reason: '展现出卓越的共情能力和社交技能', Score: 5 },
: { Reason: '具有良好的情绪调节能力', Score: 4 },
: { Reason: '展现出卓越的情感智力和社会意识', Score: 5 },
: { Reason: '具有发展中的情绪意识', Score: 3 },
: { Reason: '擅长人际交往和建立关系', Score: 5 },
: { Reason: '展现出平衡的情感智力', Score: 4 },
: { Reason: '具有基本的情绪理解能力', Score: 3 },
: { Reason: '展现出良好的情绪调节能力', Score: 4 },
: { Reason: '展现出卓越的社会意识', Score: 5 },
: { Reason: '在情感智力方面需要提升', Score: 3 },
: { Reason: '具有较强的共情能力', Score: 4 },
: { Reason: '具有良好的情绪沟通技能', Score: 4 },
},
: {
: { Reason: '展现出胜任的哲学推理技能', Score: 3 },
: { Reason: '展现出卓越的逻辑推理和分析能力', Score: 5 },
: { Reason: '展现出深刻的哲学洞察力和批判性思维', Score: 2 },
: { Reason: '展现出良好的哲学理解能力', Score: 1 },
: { Reason: '具有基础哲学推理能力,存在一些局限', Score: 3 },
: { Reason: '展现出较强的分析思维能力', Score: 4 },
: { Reason: '展现出卓越的哲学深度', Score: 5 },
: { Reason: '展现出卓越的专业分析和推理能力', Score: 5 },
: { Reason: '具有良好的批判性思维能力', Score: 4 },
: { Reason: '具有较强的专业性分析和推理能力', Score: 4 },
: { Reason: '展现出卓越的逻辑推理能力', Score: 5 },
: { Reason: '具有基础的哲学理解能力', Score: 3 },
: { Reason: '展现出平衡的哲学推理能力', Score: 4 },
: { Reason: '需要在哲学思维方面发展', Score: 3 },
: { Reason: '展现出良好的分析技能', Score: 4 },
: { Reason: '具有较强的哲学洞察力', Score: 4 },
: { Reason: '擅长批判性思维和分析', Score: 5 },
: { Reason: '具有基础哲学推理能力', Score: 3 },
: { Reason: '展现出卓越的哲学才能', Score: 5 },
},
: {
: { Reason: '在任务完成方面展现出卓越的效率', Score: 4 },
: { Reason: '擅长高效的工作流程管理', Score: 5 },
: { Reason: '展现出平均的效率,有提升空间', Score: 3 },
: { Reason: '具有良好的时间管理技能', Score: 4 },
: { Reason: '展现出胜任的效率', Score: 3 },
: { Reason: '展现出卓越的生产力', Score: 5 },
: { Reason: '具有强大的任务执行能力', Score: 4 },
: { Reason: '具有良好的工作效率和时间管理', Score: 4 },
: { Reason: '展现出良好的工作流程优化能力', Score: 4 },
: { Reason: '展现出卓越的效率和工作流程管理', Score: 5 },
: { Reason: '展现出足够的效率', Score: 3 },
: { Reason: '擅长快速完成任务', Score: 5 },
: { Reason: '展现出良好的生产力', Score: 4 },
: { Reason: '具有中等的效率水平', Score: 3 },
: { Reason: '具有较强的任务效率', Score: 4 },
: { Reason: '具有良好的执行速度', Score: 4 },
: { Reason: '展现出卓越的效率', Score: 5 },
: { Reason: '展现出平均的生产力', Score: 3 },
: { Reason: '在执行方面具有良好的效率', Score: 4 },
},
: {
: { Reason: '展现出卓越的细节关注度', Score: 5 },
: { Reason: '展现出卓越的准确性和精确度', Score: 5 },
: { Reason: '展现出卓越的精确度', Score: 5 },
: { Reason: '展现出良好的准确性', Score: 4 },
: { Reason: '展现出中等的准确性,有提升空间', Score: 3 },
: { Reason: '具有较强的细节关注度', Score: 4 },
: { Reason: '在精确度和准确性方面表现卓越', Score: 5 },
: { Reason: '展现出卓越的细节导向和精确度', Score: 5 },
: { Reason: '展现出平均的准确性', Score: 3 },
: { Reason: '展现出卓越的准确性和精确技能', Score: 5 },
: { Reason: '展现出卓越的准确性', Score: 5 },
: { Reason: '展现出较强的精确度', Score: 4 },
: { Reason: '展现出中等的准确性', Score: 3 },
: { Reason: '具有良好的细节导向能力', Score: 4 },
: { Reason: '在准确性和精确度方面表现卓越', Score: 5 },
: { Reason: '展现出较强的细节关注度', Score: 4 },
: { Reason: '展现出平均的准确性水平', Score: 3 },
: { Reason: '在工作中具有良好的精确度', Score: 4 },
: { Reason: '展现出卓越的准确性', Score: 5 },
},
: {
: { Reason: '展现出卓越的协作技能', Score: 4 },
: { Reason: '在团队合作和协作方面表现卓越', Score: 5 },
: { Reason: '具有较强的协作能力', Score: 4 },
: { Reason: '具有中等的协作技能', Score: 3 },
: { Reason: '展现出良好的团队合作精神', Score: 4 },
: { Reason: '具有较强的合作能力', Score: 4 },
: { Reason: '展现出平均的协作技能', Score: 3 },
: { Reason: '在团队协作方面表现卓越', Score: 5 },
: { Reason: '展现出良好的合作工作能力', Score: 4 },
: { Reason: '在团队协作和合作方面表现卓越', Score: 5 },
: { Reason: '具有中等的协作水平', Score: 3 },
: { Reason: '展现出良好的协作技能', Score: 4 },
: { Reason: '在协调和团队合作方面表现卓越', Score: 5 },
: { Reason: '具有较强的合作能力', Score: 4 },
: { Reason: '展现出平均的协作水平', Score: 3 },
: { Reason: '展现出良好的团队合作精神', Score: 4 },
: { Reason: '具有较强的协作技能', Score: 4 },
: { Reason: '在团队协作方面表现卓越', Score: 5 },
: { Reason: '具有中等的协作能力', Score: 3 },
},
}
// 模拟后端API调用 - agentSelectModifyInit
export const mockBackendAgentSelectModifyInit = async (): Promise<BackendAgentScoreResponse> => {
// 模拟网络延迟 300ms
await new Promise(resolve => setTimeout(resolve, 300))
return mockBackendAgentScoreData
}
// 模拟后端API调用 - agentSelectModifyAddAspect添加新维度
export const mockBackendAgentSelectModifyAddAspect = async (
aspectList: string[]
): Promise<BackendAgentScoreResponse> => {
// 模拟网络延迟 500ms
await new Promise(resolve => setTimeout(resolve, 500))
// 获取新添加的维度(最后一个)
const newAspect = aspectList[aspectList.length - 1]
if (!newAspect) {
return {}
}
// 生成该维度下所有agent的评分
const aspectData: Record<string, BackendScoreItem> = {}
vueAgentList.forEach(agent => {
const score = Math.floor(Math.random() * 5) + 1
let reason = ''
switch (score) {
case 5:
reason = `在"${newAspect}"方面表现卓越,展现出杰出的能力和深刻的理解`
break
case 4:
reason = `在"${newAspect}"方面表现优秀,具有良好的专业能力和执行力`
break
case 3:
reason = `在"${newAspect}"方面表现合格,能够完成相关任务`
break
case 2:
reason = `在"${newAspect}"方面表现一般,仍有提升空间`
break
case 1:
reason = `在"${newAspect}"方面需要加强,建议进一步提升相关能力`
break
}
aspectData[agent] = { Reason: reason, Score: score }
})
return {
[newAspect]: aspectData
}
}

View File

@@ -1,314 +0,0 @@
// Vue兼容的mock数据 - 6个维度19个智能体
export const vueAgentList = [
'船舶设计师',
'防护工程专家',
'病理生理学家',
'药物化学家',
'制剂工程师',
'监管事务专家',
'物理学家',
'实验材料学家',
'计算模拟专家',
'腐蚀机理研究员',
'先进材料研发员',
'肾脏病学家',
'临床研究协调员',
'中医药专家',
'药物安全专家',
'二维材料科学家',
'光电物理学家',
'机器学习专家',
'流体动力学专家',
]
export const vueAspectList = ['能力', '可用性', '专业性', '效率', '准确性', '协作性']
// 类型定义
export type AgentName = (typeof vueAgentList)[number]
export type AspectName = (typeof vueAspectList)[number]
export interface AgentScore {
score: number
reason: string
}
export type IAgentSelectModifyAddRequest = Record<AspectName, Record<AgentName, AgentScore>>
// Vue友好的数据结构 - agent -> 维度 -> 评分(与后端返回格式一致)
export const vueAgentScoreData: Record<AgentName, Record<AspectName, AgentScore>> = {
: {
: { score: 4, reason: '展现出卓越的创造力和创新思维' },
: { score: 5, reason: '展现出卓越的共情能力和社会意识' },
: { score: 3, reason: '展现出胜任的哲学推理技能' },
: { score: 4, reason: '在任务完成方面展现出卓越的效率' },
: { score: 5, reason: '展现出卓越的细节关注度' },
: { score: 4, reason: '展现出卓越的协作技能' },
},
: {
: { score: 5, reason: '展现出杰出的创造性问题解决能力' },
: { score: 4, reason: '具有较强的情绪调节和人际交往技能' },
: { score: 5, reason: '展现出卓越的逻辑推理和分析能力' },
: { score: 5, reason: '擅长高效的工作流程管理' },
: { score: 5, reason: '展现出卓越的准确性和精确度' },
: { score: 5, reason: '在团队合作和协作方面表现卓越' },
},
: {
: { score: 3, reason: '具有中等创造技能,有待提升' },
: { score: 5, reason: '展现出卓越的情感智力' },
: { score: 2, reason: '展现出深刻的哲学洞察力和批判性思维' },
: { score: 3, reason: '展现出平均的效率,有提升空间' },
: { score: 5, reason: '展现出卓越的精确度' },
: { score: 4, reason: '具有较强的协作能力' },
},
: {
: { score: 4, reason: '在大多数情况下展现较强的创造性思维' },
: { score: 3, reason: '在大多数情况下展现平均的情感智力' },
: { score: 1, reason: '展现出良好的哲学理解能力' },
: { score: 4, reason: '具有良好的时间管理技能' },
: { score: 4, reason: '展现出良好的准确性' },
: { score: 3, reason: '具有中等的协作技能' },
},
: {
: { score: 3, reason: '展现出胜任的创造能力' },
: { score: 4, reason: '具有良好的情绪意识和沟通能力' },
: { score: 3, reason: '具有基础哲学推理能力,存在一些局限' },
: { score: 3, reason: '展现出胜任的效率' },
: { score: 3, reason: '展现出中等的准确性,有提升空间' },
: { score: 4, reason: '展现出良好的团队合作精神' },
},
: {
: { score: 4, reason: '具有较强的创造性表达能力' },
: { score: 3, reason: '在情绪意识方面偶尔表现不足' },
: { score: 4, reason: '展现出较强的分析思维能力' },
: { score: 5, reason: '展现出卓越的生产力' },
: { score: 4, reason: '具有较强的细节关注度' },
: { score: 4, reason: '具有较强的合作能力' },
},
: {
: { score: 5, reason: '擅长创新性思维方法' },
: { score: 4, reason: '具有较强的情绪理解能力' },
: { score: 5, reason: '展现出卓越的哲学深度' },
: { score: 4, reason: '具有强大的任务执行能力' },
: { score: 5, reason: '在精确度和准确性方面表现卓越' },
: { score: 3, reason: '展现出平均的协作技能' },
},
: {
: { score: 5, reason: '展现出卓越的创造性思维和创新能力' },
: { score: 5, reason: '展现出卓越的共情能力和社交技能' },
: { score: 5, reason: '展现出卓越的专业分析和推理能力' },
: { score: 4, reason: '具有良好的工作效率和时间管理' },
: { score: 5, reason: '展现出卓越的细节导向和精确度' },
: { score: 5, reason: '在团队协作方面表现卓越' },
},
: {
: { score: 4, reason: '展现出良好的创造性问题解决能力' },
: { score: 4, reason: '具有良好的情绪调节能力' },
: { score: 4, reason: '具有良好的批判性思维能力' },
: { score: 4, reason: '展现出良好的工作流程优化能力' },
: { score: 3, reason: '展现出平均的准确性' },
: { score: 4, reason: '展现出良好的合作工作能力' },
},
: {
: { score: 5, reason: '展现出卓越的创造性问题解决能力' },
: { score: 5, reason: '展现出卓越的情感智力和社会意识' },
: { score: 4, reason: '具有较强的专业性分析和推理能力' },
: { score: 5, reason: '展现出卓越的效率和工作流程管理' },
: { score: 5, reason: '展现出卓越的准确性和精确技能' },
: { score: 5, reason: '在团队协作和合作方面表现卓越' },
},
: {
: { score: 4, reason: '展现出平衡的创造能力' },
: { score: 3, reason: '具有发展中的情绪意识' },
: { score: 5, reason: '展现出卓越的逻辑推理能力' },
: { score: 3, reason: '展现出足够的效率' },
: { score: 5, reason: '展现出卓越的准确性' },
: { score: 3, reason: '具有中等的协作水平' },
},
: {
: { score: 5, reason: '展现出卓越的创造天赋' },
: { score: 5, reason: '擅长人际交往和建立关系' },
: { score: 3, reason: '具有基础的哲学理解能力' },
: { score: 5, reason: '擅长快速完成任务' },
: { score: 4, reason: '展现出较强的精确度' },
: { score: 4, reason: '展现出良好的协作技能' },
},
: {
: { score: 3, reason: '展现出胜任的创造性思维' },
: { score: 4, reason: '展现出平衡的情感智力' },
: { score: 4, reason: '展现出平衡的哲学推理能力' },
: { score: 4, reason: '展现出良好的生产力' },
: { score: 3, reason: '展现出中等的准确性' },
: { score: 5, reason: '在协调和团队合作方面表现卓越' },
},
: {
: { score: 4, reason: '展现出较强的创造性主动性' },
: { score: 3, reason: '具有基本的情绪理解能力' },
: { score: 3, reason: '需要在哲学思维方面发展' },
: { score: 3, reason: '具有中等的效率水平' },
: { score: 4, reason: '具有良好的细节导向能力' },
: { score: 4, reason: '具有较强的合作能力' },
},
: {
: { score: 3, reason: '具有发展中的创造技能' },
: { score: 4, reason: '展现出良好的情绪调节能力' },
: { score: 4, reason: '展现出良好的分析技能' },
: { score: 4, reason: '具有较强的任务效率' },
: { score: 5, reason: '在准确性和精确度方面表现卓越' },
: { score: 3, reason: '展现出平均的协作水平' },
},
: {
: { score: 5, reason: '展现出卓越的创造愿景' },
: { score: 5, reason: '展现出卓越的社会意识' },
: { score: 4, reason: '具有较强的哲学洞察力' },
: { score: 4, reason: '具有良好的执行速度' },
: { score: 4, reason: '展现出较强的细节关注度' },
: { score: 4, reason: '展现出良好的团队合作精神' },
},
: {
: { score: 4, reason: '展现出卓越的创造性执行力' },
: { score: 3, reason: '在情感智力方面需要提升' },
: { score: 5, reason: '擅长批判性思维和分析' },
: { score: 5, reason: '展现出卓越的效率' },
: { score: 3, reason: '展现出平均的准确性水平' },
: { score: 4, reason: '具有较强的协作技能' },
},
: {
: { score: 4, reason: '具有较强的创造性问题解决能力' },
: { score: 4, reason: '具有较强的共情能力' },
: { score: 3, reason: '具有基础哲学推理能力' },
: { score: 3, reason: '展现出平均的生产力' },
: { score: 4, reason: '在工作中具有良好的精确度' },
: { score: 5, reason: '在团队协作方面表现卓越' },
},
: {
: { score: 3, reason: '展现出胜任的创造能力' },
: { score: 4, reason: '具有良好的情绪沟通技能' },
: { score: 5, reason: '展现出卓越的哲学才能' },
: { score: 4, reason: '在执行方面具有良好的效率' },
: { score: 5, reason: '展现出卓越的准确性' },
: { score: 3, reason: '具有中等的协作能力' },
},
}
// Vue友好的智能体选择配置
export const vueAgentSelections = {
balanced: { agents: ['船舶设计师', '防护工程专家', '病理生理学家'] },
creative: { agents: ['防护工程专家', '物理学家', '二维材料科学家'] },
emotional: { agents: ['船舶设计师', '病理生理学家', '实验材料学家'] },
philosophical: { agents: ['病理生理学家', '物理学家', '光电物理学家'] },
mixed: { agents: ['药物化学家', '先进材料研发员', '肾脏病学家', '机器学习专家'] },
}
export const vueCurrentAgentSelection = 'balanced'
// Vue兼容的工具函数
export const vueCalculateAgentAverages = () => {
const averages: Record<string, number> = {}
vueAgentList.forEach((agent) => {
let total = 0
let count = 0
vueAspectList.forEach((aspect) => {
// 数据结构agentScores[agent][aspect]
const scoreData = vueAgentScoreData[agent]?.[aspect]
if (scoreData) {
total += scoreData.score
count++
}
})
averages[agent] = count > 0 ? Number((total / count).toFixed(2)) : 0
})
return averages
}
// 获取按平均分排序的智能体列表
export const vueGetSortedAgentsByAverage = () => {
const averages = vueCalculateAgentAverages()
return [...vueAgentList].sort((a, b) => {
return averages[b] - averages[a]
})
}
// Vue Composition API 兼容的hook
export const useAgentMockData = () => {
// 在Vue中可以使用ref或reactive包装数据
const agentScores = vueAgentScoreData
const agentSelections = vueAgentSelections
const currentSelection = vueCurrentAgentSelection
// 计算平均分的响应式函数
const calculateAverages = () => {
return vueCalculateAgentAverages()
}
// 获取特定维度的评分(返回该维度下所有智能体的评分)
const getScoresByAspect = (aspect: AspectName) => {
const result: Record<AgentName, AgentScore> = {}
// 数据结构agentScores[agent][aspect]需要遍历所有agent提取指定aspect
vueAgentList.forEach((agent) => {
if (agentScores[agent]?.[aspect]) {
result[agent] = agentScores[agent][aspect]!
}
})
return result
}
// 获取特定智能体的所有维度评分
const getScoresByAgent = (agent: AgentName) => {
// 数据结构agentScores[agent][aspect]直接返回agent的所有维度评分
return agentScores[agent] || {}
}
return {
agentScores,
agentSelections,
currentSelection,
calculateAverages,
getScoresByAspect,
getScoresByAgent,
getSortedAgents: vueGetSortedAgentsByAverage,
}
}
// Vue 2.x 兼容的选项式API版本
export const vueMockMixin = {
data() {
return {
vueAgentScores: vueAgentScoreData,
vueAgentSelections: vueAgentSelections,
vueCurrentSelection: vueCurrentAgentSelection,
vueAgentList: vueAgentList,
vueAspectList: vueAspectList,
}
},
computed: {
vueAgentAverages() {
return vueCalculateAgentAverages()
},
vueSortedAgents() {
return vueGetSortedAgentsByAverage()
},
},
methods: {
vueGetScoresByAspect(aspect: AspectName) {
const agentScores = (this as any).vueAgentScores
const agentList = (this as any).vueAgentList
// 数据结构agentScores[agent][aspect]遍历所有agent提取指定aspect
const result: Record<string, { score: number; reason: string }> = {}
agentList.forEach((agent: string) => {
if (agentScores[agent]?.[aspect]) {
result[agent] = agentScores[agent][aspect]
}
})
return result
},
vueGetScoresByAgent(agent: AgentName) {
const agentScores = (this as any).vueAgentScores
// 数据结构agentScores[agent][aspect]直接返回agent的所有维度评分
return agentScores[agent] || {}
},
},
}

View File

@@ -3,6 +3,7 @@ import SvgIcon from '@/components/SvgIcon/index.vue'
import { getAgentMapIcon } from '@/layout/components/config.ts'
import { type ConnectArg, Jsplumb } from '@/layout/components/Main/TaskTemplate/utils.ts'
import { type IRawStepTask, useAgentsStore } from '@/stores'
// import api from '@/api'
import { computed, nextTick, watch, onMounted } from 'vue'
import { AnchorLocations } from '@jsplumb/browser-ui'
import { Loading } from '@element-plus/icons-vue'
@@ -109,7 +110,7 @@ watch(
)
// 保存编辑内容
const handleContentSave = (taskId: string, content: string) => {
const handleContentSave = async (taskId: string, content: string) => {
const taskToUpdate = collaborationProcess.value.find(item => item.Id === taskId)
if (taskToUpdate && content !== taskToUpdate.TaskContent) {
taskToUpdate.TaskContent = content
@@ -118,6 +119,18 @@ const handleContentSave = (taskId: string, content: string) => {
if (stepIndex >= 0) {
agentsStore.addModifiedStep(stepIndex)
}
//暂时注释掉直接保存 task_outline 的操作,避免覆盖整个主流程
// 如果需要持久化,应该只更新变化的字段或使用专门的 API
// const dbTaskId = (window as any).__CURRENT_TASK_ID__
// if (dbTaskId && agentsStore.agentRawPlan.data) {
// try {
// await api.updateTaskOutline(dbTaskId, agentsStore.agentRawPlan.data)
// console.log('[handleContentSave] 主流程已保存到数据库')
// } catch (error) {
// console.error('[handleContentSave] 保存主流程失败:', error)
// }
// }
}
}

View File

@@ -6,6 +6,12 @@ import { Jsplumb } from './utils.ts'
import { type IRawStepTask, useAgentsStore } from '@/stores'
import { BezierConnector } from '@jsplumb/browser-ui'
import { ref } from 'vue'
// 定义组件 props
const props = defineProps<{
TaskID?: string // 任务唯一标识,用于写入数据库
}>()
const agentsStore = useAgentsStore()
// 智能体库
@@ -100,6 +106,7 @@ defineExpose({
<div class="flex-1 h-full">
<TaskResult
ref="taskResultRef"
:TaskID="props.TaskID"
@refresh-line="taskResultJsplumb.repaintEverything"
@set-current-task="handleTaskResultCurrentTask"
/>