feat:代码优化与Mock数据清理
This commit is contained in:
2
frontend/components.d.ts
vendored
2
frontend/components.d.ts
vendored
@@ -22,13 +22,13 @@ declare module 'vue' {
|
||||
ElInput: typeof import('element-plus/es')['ElInput']
|
||||
ElPopover: typeof import('element-plus/es')['ElPopover']
|
||||
ElScrollbar: typeof import('element-plus/es')['ElScrollbar']
|
||||
ElTag: typeof import('element-plus/es')['ElTag']
|
||||
ElTooltip: typeof import('element-plus/es')['ElTooltip']
|
||||
MultiLineTooltip: typeof import('./src/components/MultiLineTooltip/index.vue')['default']
|
||||
Notification: typeof import('./src/components/Notification/Notification.vue')['default']
|
||||
RouterLink: typeof import('vue-router')['RouterLink']
|
||||
RouterView: typeof import('vue-router')['RouterView']
|
||||
SvgIcon: typeof import('./src/components/SvgIcon/index.vue')['default']
|
||||
TaskContentEditor: typeof import('./src/components/TaskContentEditor/index.vue')['default']
|
||||
}
|
||||
export interface GlobalDirectives {
|
||||
vLoading: typeof import('element-plus/es')['ElLoadingDirective']
|
||||
|
||||
@@ -1,21 +1,7 @@
|
||||
import request from '@/utils/request'
|
||||
import websocket from '@/utils/websocket'
|
||||
import type { Agent, IApiStepTask, IRawPlanResponse, IRawStepTask } from '@/stores'
|
||||
import {
|
||||
mockBackendAgentSelectModifyInit,
|
||||
mockBackendAgentSelectModifyAddAspect,
|
||||
type BackendAgentScoreResponse,
|
||||
} from '@/layout/components/Main/TaskTemplate/TaskSyllabus/components/mock/AgentAssignmentBackendMock'
|
||||
import {
|
||||
mockBackendFillAgentTaskProcess,
|
||||
type RawAgentTaskProcessResponse,
|
||||
} from '@/layout/components/Main/TaskTemplate/TaskProcess/components/mock/AgentTaskProcessBackendMock'
|
||||
import { mockBranchPlanOutlineAPI } from '@/layout/components/Main/TaskTemplate/TaskSyllabus/Branch/mock/branchPlanOutlineMock'
|
||||
import { mockFillStepTaskAPI } from '@/layout/components/Main/TaskTemplate/TaskSyllabus/Branch/mock/fill-step-task-mock'
|
||||
import {
|
||||
mockBranchTaskProcessAPI,
|
||||
type BranchAction,
|
||||
} from '@/layout/components/Main/TaskTemplate/TaskSyllabus/Branch/mock/branchTaskProcessMock'
|
||||
import { withRetry } from '@/utils/retry'
|
||||
|
||||
export interface ActionHistory {
|
||||
ID: string
|
||||
@@ -26,6 +12,14 @@ export interface ActionHistory {
|
||||
Action_Result: string
|
||||
}
|
||||
|
||||
export interface BranchAction {
|
||||
ID: string
|
||||
ActionType: string
|
||||
AgentName: string
|
||||
Description: string
|
||||
ImportantInput: string[]
|
||||
}
|
||||
|
||||
export type IExecuteRawResponse = {
|
||||
LogNodeType: string
|
||||
NodeId: string
|
||||
@@ -86,7 +80,10 @@ class Api {
|
||||
// 默认使用WebSocket
|
||||
private useWebSocketDefault = true
|
||||
|
||||
setAgents = (data: Pick<Agent, 'Name' | 'Profile' | 'apiUrl' | 'apiKey' | 'apiModel'>[], useWebSocket: boolean = this.useWebSocketDefault) => {
|
||||
setAgents = (
|
||||
data: Pick<Agent, 'Name' | 'Profile' | 'apiUrl' | 'apiKey' | 'apiModel'>[],
|
||||
useWebSocket: boolean = this.useWebSocketDefault,
|
||||
) => {
|
||||
// 如果启用WebSocket且已连接,使用WebSocket
|
||||
if (useWebSocket && websocket.connected) {
|
||||
return websocket.send('set_agents', data)
|
||||
@@ -107,19 +104,29 @@ class Api {
|
||||
apiKey?: string
|
||||
apiModel?: string
|
||||
useWebSocket?: boolean
|
||||
onProgress?: (progress: { status: string; stage?: string; message?: string; [key: string]: any }) => void
|
||||
onProgress?: (progress: {
|
||||
status: string
|
||||
stage?: string
|
||||
message?: string
|
||||
[key: string]: any
|
||||
}) => void
|
||||
}) => {
|
||||
const useWs = data.useWebSocket !== undefined ? data.useWebSocket : this.useWebSocketDefault
|
||||
|
||||
// 如果启用WebSocket且已连接,使用WebSocket
|
||||
if (useWs && websocket.connected) {
|
||||
return websocket.send('generate_base_plan', {
|
||||
'General Goal': data.goal,
|
||||
'Initial Input Object': data.inputs,
|
||||
apiUrl: data.apiUrl,
|
||||
apiKey: data.apiKey,
|
||||
apiModel: data.apiModel,
|
||||
}, undefined, data.onProgress)
|
||||
return websocket.send(
|
||||
'generate_base_plan',
|
||||
{
|
||||
'General Goal': data.goal,
|
||||
'Initial Input Object': data.inputs,
|
||||
apiUrl: data.apiUrl,
|
||||
apiKey: data.apiKey,
|
||||
apiModel: data.apiModel,
|
||||
},
|
||||
undefined,
|
||||
data.onProgress,
|
||||
)
|
||||
}
|
||||
|
||||
// 否则使用REST API
|
||||
@@ -180,18 +187,18 @@ class Api {
|
||||
enableDynamic?: boolean,
|
||||
onExecutionStarted?: (executionId: string) => void,
|
||||
executionId?: string,
|
||||
restartFromStepIndex?: number, // 新增:从指定步骤重新执行的索引
|
||||
rehearsalLog?: any[], // 新增:传递截断后的 RehearsalLog
|
||||
restartFromStepIndex?: number, // 新增:从指定步骤重新执行的索引
|
||||
rehearsalLog?: any[], // 新增:传递截断后的 RehearsalLog
|
||||
) => {
|
||||
const useWs = useWebSocket !== undefined ? useWebSocket : this.useWebSocketDefault
|
||||
|
||||
const data = {
|
||||
RehearsalLog: rehearsalLog || [], // ✅ 使用传递的 RehearsalLog
|
||||
RehearsalLog: rehearsalLog || [], // ✅ 使用传递的 RehearsalLog
|
||||
num_StepToRun: null,
|
||||
existingKeyObjects: existingKeyObjects || {},
|
||||
enable_dynamic: enableDynamic || false,
|
||||
execution_id: executionId || null,
|
||||
restart_from_step_index: restartFromStepIndex ?? null, // 新增:传递重新执行索引
|
||||
restart_from_step_index: restartFromStepIndex ?? null, // 新增:传递重新执行索引
|
||||
plan: {
|
||||
'Initial Input Object': plan['Initial Input Object'],
|
||||
'General Goal': plan['General Goal'],
|
||||
@@ -253,7 +260,7 @@ class Api {
|
||||
// onError
|
||||
(error) => {
|
||||
onError?.(error)
|
||||
}
|
||||
},
|
||||
)
|
||||
return
|
||||
}
|
||||
@@ -323,20 +330,30 @@ class Api {
|
||||
initialInputs: string[]
|
||||
goal: string
|
||||
useWebSocket?: boolean
|
||||
onProgress?: (progress: { status: string; stage?: string; message?: string; [key: string]: any }) => void
|
||||
onProgress?: (progress: {
|
||||
status: string
|
||||
stage?: string
|
||||
message?: string
|
||||
[key: string]: any
|
||||
}) => void
|
||||
}) => {
|
||||
const useWs = data.useWebSocket !== undefined ? data.useWebSocket : this.useWebSocketDefault
|
||||
|
||||
// 如果启用WebSocket且已连接,使用WebSocket
|
||||
if (useWs && websocket.connected) {
|
||||
return websocket.send('branch_plan_outline', {
|
||||
branch_Number: data.branch_Number,
|
||||
Modification_Requirement: data.Modification_Requirement,
|
||||
Existing_Steps: data.Existing_Steps,
|
||||
Baseline_Completion: data.Baseline_Completion,
|
||||
'Initial Input Object': data.initialInputs,
|
||||
'General Goal': data.goal,
|
||||
}, undefined, data.onProgress)
|
||||
return websocket.send(
|
||||
'branch_plan_outline',
|
||||
{
|
||||
branch_Number: data.branch_Number,
|
||||
Modification_Requirement: data.Modification_Requirement,
|
||||
Existing_Steps: data.Existing_Steps,
|
||||
Baseline_Completion: data.Baseline_Completion,
|
||||
'Initial Input Object': data.initialInputs,
|
||||
'General Goal': data.goal,
|
||||
},
|
||||
undefined,
|
||||
data.onProgress,
|
||||
)
|
||||
}
|
||||
|
||||
// 否则使用REST API
|
||||
@@ -365,20 +382,30 @@ class Api {
|
||||
stepTaskExisting: any
|
||||
goal: string
|
||||
useWebSocket?: boolean
|
||||
onProgress?: (progress: { status: string; stage?: string; message?: string; [key: string]: any }) => void
|
||||
onProgress?: (progress: {
|
||||
status: string
|
||||
stage?: string
|
||||
message?: string
|
||||
[key: string]: any
|
||||
}) => void
|
||||
}) => {
|
||||
const useWs = data.useWebSocket !== undefined ? data.useWebSocket : this.useWebSocketDefault
|
||||
|
||||
// 如果启用WebSocket且已连接,使用WebSocket
|
||||
if (useWs && websocket.connected) {
|
||||
return websocket.send('branch_task_process', {
|
||||
branch_Number: data.branch_Number,
|
||||
Modification_Requirement: data.Modification_Requirement,
|
||||
Existing_Steps: data.Existing_Steps,
|
||||
Baseline_Completion: data.Baseline_Completion,
|
||||
stepTaskExisting: data.stepTaskExisting,
|
||||
'General Goal': data.goal,
|
||||
}, undefined, data.onProgress)
|
||||
return websocket.send(
|
||||
'branch_task_process',
|
||||
{
|
||||
branch_Number: data.branch_Number,
|
||||
Modification_Requirement: data.Modification_Requirement,
|
||||
Existing_Steps: data.Existing_Steps,
|
||||
Baseline_Completion: data.Baseline_Completion,
|
||||
stepTaskExisting: data.stepTaskExisting,
|
||||
'General Goal': data.goal,
|
||||
},
|
||||
undefined,
|
||||
data.onProgress,
|
||||
)
|
||||
}
|
||||
|
||||
// 否则使用REST API
|
||||
@@ -400,20 +427,30 @@ class Api {
|
||||
goal: string
|
||||
stepTask: any
|
||||
useWebSocket?: boolean
|
||||
onProgress?: (progress: { status: string; stage?: string; message?: string; [key: string]: any }) => void
|
||||
onProgress?: (progress: {
|
||||
status: string
|
||||
stage?: string
|
||||
message?: string
|
||||
[key: string]: any
|
||||
}) => void
|
||||
}): Promise<IRawStepTask> => {
|
||||
const useWs = data.useWebSocket !== undefined ? data.useWebSocket : this.useWebSocketDefault
|
||||
let response: any
|
||||
|
||||
// 如果启用WebSocket且已连接,使用WebSocket
|
||||
if (useWs && websocket.connected) {
|
||||
response = await websocket.send('fill_step_task', {
|
||||
'General Goal': data.goal,
|
||||
stepTask: data.stepTask,
|
||||
}, undefined, data.onProgress)
|
||||
} else {
|
||||
// 定义实际的 API 调用逻辑
|
||||
const executeRequest = async (): Promise<any> => {
|
||||
if (useWs && websocket.connected) {
|
||||
return await websocket.send(
|
||||
'fill_step_task',
|
||||
{
|
||||
'General Goal': data.goal,
|
||||
stepTask: data.stepTask,
|
||||
},
|
||||
undefined,
|
||||
data.onProgress,
|
||||
)
|
||||
}
|
||||
// 否则使用REST API
|
||||
response = await request<
|
||||
return await request<
|
||||
{
|
||||
'General Goal': string
|
||||
stepTask: any
|
||||
@@ -446,6 +483,15 @@ class Api {
|
||||
})
|
||||
}
|
||||
|
||||
// 使用重试机制执行请求
|
||||
const response = await withRetry(executeRequest, {
|
||||
maxRetries: 3,
|
||||
initialDelayMs: 2000,
|
||||
onRetry: (error, attempt, delay) => {
|
||||
console.warn(`⚠️ [fillStepTask] 第${attempt}次重试,等待 ${delay}ms...`, error?.message)
|
||||
},
|
||||
})
|
||||
|
||||
const vec2Hsl = (color: number[]): string => {
|
||||
const [h, s, l] = color
|
||||
return `hsl(${h}, ${s}%, ${l}%)`
|
||||
@@ -455,9 +501,9 @@ class Api {
|
||||
if (response.Collaboration_Brief_FrontEnd?.data) {
|
||||
for (const [key, value] of Object.entries(response.Collaboration_Brief_FrontEnd.data)) {
|
||||
briefData[key] = {
|
||||
text: value.text,
|
||||
text: (value as { text: string; color: number[] }).text,
|
||||
style: {
|
||||
background: vec2Hsl(value.color),
|
||||
background: vec2Hsl((value as { text: string; color: number[] }).color),
|
||||
},
|
||||
}
|
||||
}
|
||||
@@ -485,26 +531,36 @@ class Api {
|
||||
stepTask: IApiStepTask
|
||||
agents: string[]
|
||||
useWebSocket?: boolean
|
||||
onProgress?: (progress: { status: string; stage?: string; message?: string; [key: string]: any }) => void
|
||||
onProgress?: (progress: {
|
||||
status: string
|
||||
stage?: string
|
||||
message?: string
|
||||
[key: string]: any
|
||||
}) => void
|
||||
}): Promise<IApiStepTask> => {
|
||||
const useWs = data.useWebSocket !== undefined ? data.useWebSocket : this.useWebSocketDefault
|
||||
let response: any
|
||||
|
||||
// 如果启用WebSocket且已连接,使用WebSocket
|
||||
if (useWs && websocket.connected) {
|
||||
response = await websocket.send('fill_step_task_process', {
|
||||
'General Goal': data.goal,
|
||||
stepTask_lackTaskProcess: {
|
||||
StepName: data.stepTask.name,
|
||||
TaskContent: data.stepTask.content,
|
||||
InputObject_List: data.stepTask.inputs,
|
||||
OutputObject: data.stepTask.output,
|
||||
AgentSelection: data.agents,
|
||||
},
|
||||
}, undefined, data.onProgress)
|
||||
} else {
|
||||
// 定义实际的 API 调用逻辑
|
||||
const executeRequest = async (): Promise<any> => {
|
||||
if (useWs && websocket.connected) {
|
||||
return await websocket.send(
|
||||
'fill_step_task_process',
|
||||
{
|
||||
'General Goal': data.goal,
|
||||
stepTask_lackTaskProcess: {
|
||||
StepName: data.stepTask.name,
|
||||
TaskContent: data.stepTask.content,
|
||||
InputObject_List: data.stepTask.inputs,
|
||||
OutputObject: data.stepTask.output,
|
||||
AgentSelection: data.agents,
|
||||
},
|
||||
},
|
||||
undefined,
|
||||
data.onProgress,
|
||||
)
|
||||
}
|
||||
// 否则使用REST API
|
||||
response = await request<
|
||||
return await request<
|
||||
{
|
||||
'General Goal': string
|
||||
stepTask_lackTaskProcess: {
|
||||
@@ -549,6 +605,15 @@ class Api {
|
||||
})
|
||||
}
|
||||
|
||||
// 使用重试机制执行请求
|
||||
const response = await withRetry(executeRequest, {
|
||||
maxRetries: 3,
|
||||
initialDelayMs: 2000,
|
||||
onRetry: (error, attempt, delay) => {
|
||||
console.warn(`⚠️ [fillStepTaskTaskProcess] 第${attempt}次重试,等待 ${delay}ms...`, error?.message)
|
||||
},
|
||||
})
|
||||
|
||||
const vec2Hsl = (color: number[]): string => {
|
||||
const [h, s, l] = color
|
||||
return `hsl(${h}, ${s}%, ${l}%)`
|
||||
@@ -558,9 +623,9 @@ class Api {
|
||||
if (response.Collaboration_Brief_FrontEnd?.data) {
|
||||
for (const [key, value] of Object.entries(response.Collaboration_Brief_FrontEnd.data)) {
|
||||
briefData[key] = {
|
||||
text: value.text,
|
||||
text: (value as { text: string; color: number[] }).text,
|
||||
style: {
|
||||
background: vec2Hsl(value.color),
|
||||
background: vec2Hsl((value as { text: string; color: number[] }).color),
|
||||
},
|
||||
}
|
||||
}
|
||||
@@ -589,31 +654,51 @@ class Api {
|
||||
}
|
||||
|
||||
/**
|
||||
* 为每个智能体评分
|
||||
* 为每个智能体评分(带重试机制)
|
||||
*/
|
||||
agentSelectModifyInit = async (data: {
|
||||
goal: string
|
||||
stepTask: any
|
||||
useWebSocket?: boolean
|
||||
onProgress?: (progress: { status: string; stage?: string; message?: string; [key: string]: any }) => void
|
||||
onProgress?: (progress: {
|
||||
status: string
|
||||
stage?: string
|
||||
message?: string
|
||||
[key: string]: any
|
||||
}) => void
|
||||
}): Promise<Record<string, Record<string, { reason: string; score: number }>>> => {
|
||||
const useWs = data.useWebSocket !== undefined ? data.useWebSocket : this.useWebSocketDefault
|
||||
let response: Record<string, Record<string, { Reason: string; Score: number }>>
|
||||
|
||||
// 如果启用WebSocket且已连接,使用WebSocket
|
||||
if (useWs && websocket.connected) {
|
||||
response = await websocket.send('agent_select_modify_init', {
|
||||
'General Goal': data.goal,
|
||||
stepTask: {
|
||||
StepName: data.stepTask.StepName || data.stepTask.name,
|
||||
TaskContent: data.stepTask.TaskContent || data.stepTask.content,
|
||||
InputObject_List: data.stepTask.InputObject_List || data.stepTask.inputs,
|
||||
OutputObject: data.stepTask.OutputObject || data.stepTask.output,
|
||||
},
|
||||
}, undefined, data.onProgress)
|
||||
} else {
|
||||
// 调试日志:打印请求参数
|
||||
const requestPayload = {
|
||||
'General Goal': data.goal,
|
||||
stepTask: {
|
||||
StepName: data.stepTask.StepName || data.stepTask.name,
|
||||
TaskContent: data.stepTask.TaskContent || data.stepTask.content,
|
||||
InputObject_List: data.stepTask.InputObject_List || data.stepTask.inputs,
|
||||
OutputObject: data.stepTask.OutputObject || data.stepTask.output,
|
||||
},
|
||||
}
|
||||
console.log('🔍 [agentSelectModifyInit] 请求参数:', {
|
||||
goal: requestPayload['General Goal'],
|
||||
stepTaskName: requestPayload.stepTask.StepName,
|
||||
stepTaskContentLength: requestPayload.stepTask.TaskContent?.length,
|
||||
useWebSocket: useWs && websocket.connected,
|
||||
wsConnected: websocket.connected,
|
||||
})
|
||||
|
||||
// 定义实际的 API 调用逻辑
|
||||
const executeRequest = async (): Promise<Record<string, Record<string, { Reason: string; Score: number }>>> => {
|
||||
if (useWs && websocket.connected) {
|
||||
return await websocket.send(
|
||||
'agent_select_modify_init',
|
||||
requestPayload,
|
||||
undefined,
|
||||
data.onProgress,
|
||||
)
|
||||
}
|
||||
// 否则使用REST API
|
||||
response = await request<
|
||||
return await request<
|
||||
{
|
||||
'General Goal': string
|
||||
stepTask: any
|
||||
@@ -622,18 +707,19 @@ class Api {
|
||||
>({
|
||||
url: '/agentSelectModify_init',
|
||||
method: 'POST',
|
||||
data: {
|
||||
'General Goal': data.goal,
|
||||
stepTask: {
|
||||
StepName: data.stepTask.StepName || data.stepTask.name,
|
||||
TaskContent: data.stepTask.TaskContent || data.stepTask.content,
|
||||
InputObject_List: data.stepTask.InputObject_List || data.stepTask.inputs,
|
||||
OutputObject: data.stepTask.OutputObject || data.stepTask.output,
|
||||
},
|
||||
},
|
||||
data: requestPayload,
|
||||
})
|
||||
}
|
||||
|
||||
// 使用重试机制执行请求
|
||||
const response = await withRetry(executeRequest, {
|
||||
maxRetries: 3,
|
||||
initialDelayMs: 2000,
|
||||
onRetry: (error, attempt, delay) => {
|
||||
console.warn(`⚠️ [agentSelectModifyInit] 第${attempt}次重试,等待 ${delay}ms...`, error?.message)
|
||||
},
|
||||
})
|
||||
|
||||
const transformedData: Record<string, Record<string, { reason: string; score: number }>> = {}
|
||||
|
||||
for (const [aspect, agents] of Object.entries(response)) {
|
||||
@@ -657,7 +743,12 @@ class Api {
|
||||
agentSelectModifyAddAspect = async (data: {
|
||||
aspectList: string[]
|
||||
useWebSocket?: boolean
|
||||
onProgress?: (progress: { status: string; stage?: string; message?: string; [key: string]: any }) => void
|
||||
onProgress?: (progress: {
|
||||
status: string
|
||||
stage?: string
|
||||
message?: string
|
||||
[key: string]: any
|
||||
}) => void
|
||||
}): Promise<{
|
||||
aspectName: string
|
||||
agentScores: Record<string, { score: number; reason: string }>
|
||||
@@ -667,9 +758,14 @@ class Api {
|
||||
|
||||
// 如果启用WebSocket且已连接,使用WebSocket
|
||||
if (useWs && websocket.connected) {
|
||||
response = await websocket.send('agent_select_modify_add_aspect', {
|
||||
aspectList: data.aspectList,
|
||||
}, undefined, data.onProgress)
|
||||
response = await websocket.send(
|
||||
'agent_select_modify_add_aspect',
|
||||
{
|
||||
aspectList: data.aspectList,
|
||||
},
|
||||
undefined,
|
||||
data.onProgress,
|
||||
)
|
||||
} else {
|
||||
// 否则使用REST API
|
||||
response = await request<
|
||||
@@ -711,164 +807,6 @@ class Api {
|
||||
agentScores,
|
||||
}
|
||||
}
|
||||
/**
|
||||
* ==================== Mock API(开发阶段使用)====================
|
||||
*为每个智能体评分
|
||||
*/
|
||||
mockAgentSelectModifyInit = async (): Promise<
|
||||
Record<string, Record<string, { reason: string; score: number }>>
|
||||
> => {
|
||||
const response: BackendAgentScoreResponse = await mockBackendAgentSelectModifyInit()
|
||||
|
||||
const transformedData: Record<string, Record<string, { reason: string; score: number }>> = {}
|
||||
|
||||
for (const [aspect, agents] of Object.entries(response)) {
|
||||
for (const [agentName, scoreInfo] of Object.entries(agents)) {
|
||||
if (!transformedData[agentName]) {
|
||||
transformedData[agentName] = {}
|
||||
}
|
||||
transformedData[agentName][aspect] = {
|
||||
reason: scoreInfo.Reason,
|
||||
score: scoreInfo.Score,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return transformedData
|
||||
}
|
||||
|
||||
mockAgentSelectModifyAddAspect = async (data: {
|
||||
aspectList: string[]
|
||||
}): Promise<{
|
||||
aspectName: string
|
||||
agentScores: Record<string, { score: number; reason: string }>
|
||||
}> => {
|
||||
const response: BackendAgentScoreResponse = await mockBackendAgentSelectModifyAddAspect(
|
||||
data.aspectList,
|
||||
)
|
||||
|
||||
const newAspect = data.aspectList[data.aspectList.length - 1]
|
||||
if (!newAspect) {
|
||||
throw new Error('aspectList is empty')
|
||||
}
|
||||
|
||||
const newAspectAgents = response[newAspect]
|
||||
const agentScores: Record<string, { score: number; reason: string }> = {}
|
||||
|
||||
if (newAspectAgents) {
|
||||
for (const [agentName, scoreInfo] of Object.entries(newAspectAgents)) {
|
||||
agentScores[agentName] = {
|
||||
score: scoreInfo.Score,
|
||||
reason: scoreInfo.Reason,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
aspectName: newAspect,
|
||||
agentScores,
|
||||
}
|
||||
}
|
||||
|
||||
mockFillStepTaskTaskProcess = async (data: {
|
||||
goal: string
|
||||
stepTask: IApiStepTask
|
||||
agents: string[]
|
||||
}): Promise<IApiStepTask> => {
|
||||
const response: RawAgentTaskProcessResponse = await mockBackendFillAgentTaskProcess(
|
||||
data.goal,
|
||||
data.stepTask,
|
||||
data.agents,
|
||||
)
|
||||
|
||||
const vec2Hsl = (color: number[]): string => {
|
||||
const [h, s, l] = color
|
||||
return `hsl(${h}, ${s}%, ${l}%)`
|
||||
}
|
||||
|
||||
const briefData: Record<string, { text: string; style: { background: string } }> = {}
|
||||
if (response.Collaboration_Brief_frontEnd?.data) {
|
||||
for (const [key, value] of Object.entries(response.Collaboration_Brief_frontEnd.data)) {
|
||||
briefData[key] = {
|
||||
text: value.text,
|
||||
style: {
|
||||
background: vec2Hsl(value.color),
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const process = (response.TaskProcess || []).map((action) => ({
|
||||
id: action.ID,
|
||||
type: action.ActionType,
|
||||
agent: action.AgentName,
|
||||
description: action.Description,
|
||||
inputs: action.ImportantInput,
|
||||
}))
|
||||
|
||||
return {
|
||||
name: response.StepName || '',
|
||||
content: response.TaskContent || '',
|
||||
inputs: response.InputObject_List || [],
|
||||
output: response.OutputObject || '',
|
||||
agents: response.AgentSelection || [],
|
||||
brief: {
|
||||
template: response.Collaboration_Brief_frontEnd?.template || '',
|
||||
data: briefData,
|
||||
},
|
||||
process,
|
||||
}
|
||||
}
|
||||
|
||||
mockBranchPlanOutline = async (data: {
|
||||
branch_Number: number
|
||||
Modification_Requirement: string
|
||||
Existing_Steps: IRawStepTask[]
|
||||
Baseline_Completion: number
|
||||
initialInputs: string[]
|
||||
goal: string
|
||||
}): Promise<IRawPlanResponse> => {
|
||||
const response = await mockBranchPlanOutlineAPI({
|
||||
branch_Number: data.branch_Number,
|
||||
Modification_Requirement: data.Modification_Requirement,
|
||||
Existing_Steps: data.Existing_Steps,
|
||||
Baseline_Completion: data.Baseline_Completion,
|
||||
InitialObject_List: data.initialInputs,
|
||||
General_Goal: data.goal,
|
||||
})
|
||||
|
||||
return response
|
||||
}
|
||||
|
||||
mockFillStepTask = async (data: { goal: string; stepTask: any }): Promise<any> => {
|
||||
const response = await mockFillStepTaskAPI({
|
||||
General_Goal: data.goal,
|
||||
stepTask: data.stepTask,
|
||||
})
|
||||
|
||||
return response
|
||||
}
|
||||
|
||||
mockBranchTaskProcess = async (data: {
|
||||
branch_Number: number
|
||||
Modification_Requirement: string
|
||||
Existing_Steps: BranchAction[]
|
||||
Baseline_Completion: number
|
||||
stepTaskExisting: any
|
||||
goal: string
|
||||
}): Promise<BranchAction[][]> => {
|
||||
const response = await mockBranchTaskProcessAPI({
|
||||
branch_Number: data.branch_Number,
|
||||
Modification_Requirement: data.Modification_Requirement,
|
||||
Existing_Steps: data.Existing_Steps,
|
||||
Baseline_Completion: data.Baseline_Completion,
|
||||
stepTaskExisting: data.stepTaskExisting,
|
||||
General_Goal: data.goal,
|
||||
})
|
||||
|
||||
return response
|
||||
}
|
||||
|
||||
/**
|
||||
* 向正在执行的任务追加新步骤
|
||||
* @param executionId 执行ID
|
||||
@@ -880,16 +818,16 @@ class Api {
|
||||
throw new Error('WebSocket未连接')
|
||||
}
|
||||
|
||||
const response = await websocket.send('add_steps_to_execution', {
|
||||
const response = (await websocket.send('add_steps_to_execution', {
|
||||
execution_id: executionId,
|
||||
new_steps: newSteps.map(step => ({
|
||||
new_steps: newSteps.map((step) => ({
|
||||
StepName: step.StepName,
|
||||
TaskContent: step.TaskContent,
|
||||
InputObject_List: step.InputObject_List,
|
||||
OutputObject: step.OutputObject,
|
||||
AgentSelection: step.AgentSelection,
|
||||
Collaboration_Brief_frontEnd: step.Collaboration_Brief_frontEnd,
|
||||
TaskProcess: step.TaskProcess.map(action => ({
|
||||
TaskProcess: step.TaskProcess.map((action) => ({
|
||||
ActionType: action.ActionType,
|
||||
AgentName: action.AgentName,
|
||||
Description: action.Description,
|
||||
@@ -897,7 +835,7 @@ class Api {
|
||||
ImportantInput: action.ImportantInput,
|
||||
})),
|
||||
})),
|
||||
}) as { added_count: number }
|
||||
})) as { added_count: number }
|
||||
|
||||
return response?.added_count || 0
|
||||
}
|
||||
|
||||
@@ -1,18 +1,11 @@
|
||||
<template>
|
||||
<teleport to="body">
|
||||
<div class="notification-container">
|
||||
<transition-group
|
||||
name="notification"
|
||||
tag="div"
|
||||
class="notification-list"
|
||||
>
|
||||
<transition-group name="notification" tag="div" class="notification-list">
|
||||
<div
|
||||
v-for="notification in notifications"
|
||||
:key="notification.id"
|
||||
:class="[
|
||||
'notification-item',
|
||||
`notification-${notification.type || 'info'}`
|
||||
]"
|
||||
:class="['notification-item', `notification-${notification.type || 'info'}`]"
|
||||
:style="{ zIndex: notification.zIndex || 1000 }"
|
||||
>
|
||||
<div class="notification-content">
|
||||
@@ -36,10 +29,7 @@
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="notification.showProgress" class="notification-progress">
|
||||
<div
|
||||
class="progress-bar"
|
||||
:style="{ width: `${notification.progress || 0}%` }"
|
||||
></div>
|
||||
<div class="progress-bar" :style="{ width: `${notification.progress || 0}%` }"></div>
|
||||
</div>
|
||||
</div>
|
||||
</transition-group>
|
||||
@@ -48,12 +38,10 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { computed } from 'vue'
|
||||
import {
|
||||
Close,
|
||||
SuccessFilled as IconSuccess,
|
||||
WarningFilled as IconWarning,
|
||||
CircleCloseFilled,
|
||||
InfoFilled
|
||||
} from '@element-plus/icons-vue'
|
||||
import type { NotificationItem } from '@/composables/useNotification'
|
||||
@@ -138,22 +126,6 @@ const getIcon = (type?: string) => {
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.notification-icon .success {
|
||||
color: #67c23a;
|
||||
}
|
||||
|
||||
.notification-icon .warning {
|
||||
color: #e6a23c;
|
||||
}
|
||||
|
||||
.notification-icon .error {
|
||||
color: #f56c6c;
|
||||
}
|
||||
|
||||
.notification-icon .info {
|
||||
color: #409eff;
|
||||
}
|
||||
|
||||
.notification-message {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
|
||||
100
frontend/src/components/TaskContentEditor/index.vue
Normal file
100
frontend/src/components/TaskContentEditor/index.vue
Normal file
@@ -0,0 +1,100 @@
|
||||
<script setup lang="ts">
|
||||
import { ref } from 'vue'
|
||||
import type { IRawStepTask } from '@/stores'
|
||||
import SvgIcon from '@/components/SvgIcon/index.vue'
|
||||
|
||||
const props = defineProps<{
|
||||
task: IRawStepTask
|
||||
}>()
|
||||
|
||||
const emit = defineEmits<{
|
||||
(e: 'save', taskId: string, content: string): void
|
||||
}>()
|
||||
|
||||
const isEditing = ref(false)
|
||||
const editingContent = ref('')
|
||||
|
||||
const startEditing = () => {
|
||||
editingContent.value = props.task.TaskContent || ''
|
||||
isEditing.value = true
|
||||
}
|
||||
|
||||
const save = () => {
|
||||
const trimmed = editingContent.value.trim()
|
||||
emit('save', props.task.Id!, trimmed)
|
||||
isEditing.value = false
|
||||
}
|
||||
|
||||
const cancel = () => {
|
||||
isEditing.value = false
|
||||
}
|
||||
|
||||
const handleKeydown = (event: KeyboardEvent) => {
|
||||
if (event.key === 'Enter') {
|
||||
event.preventDefault()
|
||||
save()
|
||||
} else if (event.key === 'Escape') {
|
||||
cancel()
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div v-if="isEditing" class="w-full">
|
||||
<div class="flex flex-col gap-3">
|
||||
<el-input
|
||||
v-model="editingContent"
|
||||
type="textarea"
|
||||
:autosize="{ minRows: 2, maxRows: 4 }"
|
||||
placeholder="请输入任务内容"
|
||||
@keydown="handleKeydown"
|
||||
class="task-content-editor"
|
||||
size="small"
|
||||
/>
|
||||
<div class="flex justify-end">
|
||||
<svg-icon
|
||||
icon-class="Check"
|
||||
size="20px"
|
||||
color="#328621"
|
||||
class="cursor-pointer mr-4"
|
||||
@click="save"
|
||||
title="保存"
|
||||
/>
|
||||
<svg-icon
|
||||
icon-class="Cancel"
|
||||
size="20px"
|
||||
color="#8e0707"
|
||||
class="cursor-pointer mr-1"
|
||||
@click="cancel"
|
||||
title="取消"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div v-else @dblclick="startEditing" class="w-full cursor-pointer">
|
||||
<slot name="display">
|
||||
<div class="text-[14px] text-[var(--color-text-secondary)] task-content-display">
|
||||
{{ task.TaskContent }}
|
||||
</div>
|
||||
</slot>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.task-content-editor {
|
||||
:deep(.el-textarea__inner) {
|
||||
font-size: 14px;
|
||||
color: var(--color-text-secondary);
|
||||
background: transparent;
|
||||
border: 1px solid #dcdfe6;
|
||||
border-radius: 4px;
|
||||
resize: none;
|
||||
}
|
||||
}
|
||||
|
||||
.task-content-display {
|
||||
min-height: 40px;
|
||||
word-break: break-word;
|
||||
white-space: pre-wrap;
|
||||
}
|
||||
</style>
|
||||
@@ -41,9 +41,9 @@ export function useNotification() {
|
||||
}
|
||||
|
||||
const removeNotification = (id: string) => {
|
||||
const index = notifications.value.findIndex((n) => n.id === id)
|
||||
if (index !== -1) {
|
||||
const notification = notifications.value[index]
|
||||
const notification = notifications.value.find((n) => n.id === id)
|
||||
if (notification) {
|
||||
const index = notifications.value.indexOf(notification)
|
||||
notifications.value.splice(index, 1)
|
||||
notification.onClose?.()
|
||||
}
|
||||
@@ -120,7 +120,7 @@ export function useNotification() {
|
||||
detailTitle: string,
|
||||
detailMessage: string,
|
||||
current?: number,
|
||||
total?: number
|
||||
total?: number,
|
||||
) => {
|
||||
const notification = notifications.value.find((n) => n.id === id)
|
||||
if (notification) {
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
<script setup lang="ts">
|
||||
import { ref, onMounted, computed, reactive, nextTick } from 'vue'
|
||||
import { ref, onMounted, onUnmounted, computed, reactive, nextTick } from 'vue'
|
||||
import SvgIcon from '@/components/SvgIcon/index.vue'
|
||||
import { useAgentsStore, useConfigStore } from '@/stores'
|
||||
import api from '@/api'
|
||||
import websocket from '@/utils/websocket'
|
||||
import { changeBriefs } from '@/utils/collaboration_Brief_FrontEnd.ts'
|
||||
import { ElMessage } from 'element-plus'
|
||||
import { useNotification } from '@/composables/useNotification'
|
||||
import AssignmentButton from './TaskTemplate/TaskSyllabus/components/AssignmentButton.vue'
|
||||
import { withRetry } from '@/utils/retry'
|
||||
const emit = defineEmits<{
|
||||
(e: 'search-start'): void
|
||||
(e: 'search', value: string): void
|
||||
@@ -14,14 +15,15 @@ const emit = defineEmits<{
|
||||
|
||||
const agentsStore = useAgentsStore()
|
||||
const configStore = useConfigStore()
|
||||
const { success, warning, error: notifyError } = useNotification()
|
||||
const searchValue = ref('')
|
||||
const triggerOnFocus = ref(true)
|
||||
const isFocus = ref(false)
|
||||
const hasAutoSearched = ref(false)
|
||||
const isExpanded = ref(false)
|
||||
// 添加一个状态来跟踪是否正在填充步骤数据
|
||||
const isFillingSteps = ref(false)
|
||||
// 存储当前填充任务的取消函数
|
||||
const isStopping = ref(false)
|
||||
const isStopPending = ref(false)
|
||||
const currentStepAbortController = ref<{ cancel: () => void } | null>(null)
|
||||
|
||||
// 解析URL参数
|
||||
@@ -63,56 +65,33 @@ const taskContainerRef = ref<HTMLDivElement | null>(null)
|
||||
// 处理失去焦点事件
|
||||
function handleBlur() {
|
||||
isFocus.value = false
|
||||
// 延迟收起搜索框,以便点击按钮等操作
|
||||
setTimeout(() => {
|
||||
isExpanded.value = false
|
||||
// 强制重置文本区域高度到最小行数
|
||||
resetTextareaHeight()
|
||||
}, 200)
|
||||
}
|
||||
|
||||
// 🆕 预加载所有任务的智能体评分数据(顺序加载,确保任务详情已填充)
|
||||
// 预加载所有任务的智能体评分数据
|
||||
async function preloadAllTaskAgentScores(outlineData: any, goal: string) {
|
||||
const tasks = outlineData['Collaboration Process'] || []
|
||||
|
||||
if (tasks.length === 0) {
|
||||
console.log('ℹ️ 没有任务需要预加载评分数据')
|
||||
return
|
||||
}
|
||||
|
||||
console.log(`🚀 开始预加载 ${tasks.length} 个任务的智能体评分数据...`)
|
||||
|
||||
// 🆕 顺序预加载:等待每个任务详情填充完成后再预加载其评分
|
||||
for (const task of tasks) {
|
||||
// 确保任务有 Id
|
||||
// 检查是否已停止
|
||||
if (agentsStore.isStopping || agentsStore.hasStoppedFilling) {
|
||||
return
|
||||
}
|
||||
|
||||
// 确保任务有唯一ID
|
||||
if (!task.Id) {
|
||||
task.Id = `task-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`
|
||||
}
|
||||
|
||||
const taskId = task.Id
|
||||
|
||||
// 检查是否已有缓存数据
|
||||
if (agentsStore.hasTaskScoreData(taskId)) {
|
||||
console.log(`⏭️ 任务 "${task.StepName}" (${taskId}) 已有缓存数据,跳过`)
|
||||
continue
|
||||
}
|
||||
|
||||
// 🆕 等待任务详情填充完成(通过检查 AgentSelection 是否存在)
|
||||
// 最多等待 60 秒,超时则跳过该任务
|
||||
let waitCount = 0
|
||||
const maxWait = 60 // 60 * 500ms = 30秒
|
||||
while (!task.AgentSelection && waitCount < maxWait) {
|
||||
await new Promise(resolve => setTimeout(resolve, 500))
|
||||
waitCount++
|
||||
}
|
||||
|
||||
if (!task.AgentSelection) {
|
||||
console.warn(`⚠️ 任务 "${task.StepName}" (${taskId}) 详情未填充完成,跳过评分预加载`)
|
||||
continue
|
||||
}
|
||||
|
||||
// 调用初始化接口获取评分数据
|
||||
try {
|
||||
// 调用初始化接口获取评分数据
|
||||
const agentScores = await api.agentSelectModifyInit({
|
||||
goal: goal,
|
||||
stepTask: {
|
||||
@@ -123,23 +102,18 @@ async function preloadAllTaskAgentScores(outlineData: any, goal: string) {
|
||||
}
|
||||
})
|
||||
|
||||
// 提取维度列表
|
||||
// 提取维度列表并存储
|
||||
const firstAgent = Object.keys(agentScores)[0]
|
||||
const aspectList = firstAgent ? Object.keys(agentScores[firstAgent] || {}) : []
|
||||
|
||||
// 存储到 store(按任务ID存储)
|
||||
agentsStore.setTaskScoreData(taskId, {
|
||||
agentsStore.setTaskScoreData(task.Id, {
|
||||
aspectList,
|
||||
agentScores
|
||||
})
|
||||
|
||||
console.log(`✅ 任务 "${task.StepName}" (${taskId}) 的评分数据预加载完成,维度数: ${aspectList.length}`)
|
||||
} catch (error) {
|
||||
console.error(`❌ 任务 "${task.StepName}" (${taskId}) 的评分数据预加载失败:`, error)
|
||||
console.error(`❌ 任务 "${task.StepName}" 的评分数据预加载失败:`, error)
|
||||
}
|
||||
}
|
||||
|
||||
console.log(`🎉 所有 ${tasks.length} 个任务的智能体评分数据预加载完成(或已跳过)`)
|
||||
}
|
||||
|
||||
// 重置文本区域高度到最小行数
|
||||
@@ -162,157 +136,120 @@ function resetTextareaHeight() {
|
||||
// 停止填充数据的处理函数
|
||||
async function handleStop() {
|
||||
try {
|
||||
// 通过 WebSocket 发送停止信号
|
||||
if (websocket.connected) {
|
||||
await websocket.send('stop_generation', {
|
||||
goal: searchValue.value
|
||||
})
|
||||
ElMessage.success('已发送停止信号,正在停止生成...')
|
||||
// 标记正在停止中,按钮显示 loading 状态
|
||||
isStopping.value = true
|
||||
isStopPending.value = true
|
||||
agentsStore.setIsStopping(true)
|
||||
success('提示', '正在停止,请稍候...')
|
||||
} else {
|
||||
ElMessage.warning('WebSocket 未连接,无法停止')
|
||||
warning('警告', 'WebSocket 未连接,无法停止')
|
||||
// 未连接时直接重置状态
|
||||
isFillingSteps.value = false
|
||||
currentStepAbortController.value = null
|
||||
agentsStore.setHasStoppedFilling(true)
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('停止生成失败:', error)
|
||||
ElMessage.error('停止生成失败')
|
||||
} finally {
|
||||
// 无论后端是否成功停止,都重置状态
|
||||
notifyError('错误', '停止生成失败')
|
||||
isFillingSteps.value = false
|
||||
currentStepAbortController.value = null
|
||||
// 标记用户已停止填充
|
||||
agentsStore.setHasStoppedFilling(true)
|
||||
}
|
||||
}
|
||||
|
||||
// 监听后端发送的停止完成事件
|
||||
function onGenerationStopped() {
|
||||
isStopping.value = false
|
||||
isStopPending.value = false
|
||||
success('成功', '已停止生成')
|
||||
}
|
||||
|
||||
// 处理按钮点击事件
|
||||
function handleButtonClick() {
|
||||
if (isFillingSteps.value) {
|
||||
// 如果正在填充数据,点击停止
|
||||
handleStop()
|
||||
} else {
|
||||
// 否则开始搜索
|
||||
handleSearch()
|
||||
}
|
||||
}
|
||||
|
||||
// 处理搜索函数
|
||||
async function handleSearch() {
|
||||
// 用于标记大纲是否成功加载
|
||||
let outlineLoaded = false
|
||||
triggerOnFocus.value = false
|
||||
if (!searchValue.value) {
|
||||
warning('提示', '请输入搜索内容')
|
||||
return
|
||||
}
|
||||
emit('search-start')
|
||||
|
||||
try {
|
||||
triggerOnFocus.value = false
|
||||
if (!searchValue.value) {
|
||||
ElMessage.warning('请输入搜索内容')
|
||||
return
|
||||
}
|
||||
emit('search-start')
|
||||
agentsStore.resetAgent()
|
||||
agentsStore.setAgentRawPlan({ loading: true })
|
||||
// 重置停止状态
|
||||
agentsStore.setHasStoppedFilling(false)
|
||||
agentsStore.resetAgent()
|
||||
agentsStore.setAgentRawPlan({ loading: true })
|
||||
agentsStore.setHasStoppedFilling(false)
|
||||
|
||||
// 获取大纲
|
||||
const outlineData = await api.generateBasePlan({
|
||||
goal: searchValue.value,
|
||||
inputs: []
|
||||
})
|
||||
// 获取大纲
|
||||
const outlineData = await api.generateBasePlan({
|
||||
goal: searchValue.value,
|
||||
inputs: []
|
||||
})
|
||||
|
||||
// 检查是否已被停止
|
||||
if (!isFillingSteps.value && currentStepAbortController.value) {
|
||||
return
|
||||
// 处理简报数据格式
|
||||
outlineData['Collaboration Process'] = changeBriefs(outlineData['Collaboration Process'])
|
||||
|
||||
// 立即显示大纲
|
||||
agentsStore.setAgentRawPlan({ data: outlineData, loading: false })
|
||||
emit('search', searchValue.value)
|
||||
|
||||
// 预加载所有任务的智能体评分数据
|
||||
preloadAllTaskAgentScores(outlineData, searchValue.value)
|
||||
|
||||
// 填充步骤详情
|
||||
isFillingSteps.value = true
|
||||
const steps = outlineData['Collaboration Process'] || []
|
||||
|
||||
// 串行填充所有步骤的详情
|
||||
for (const step of steps) {
|
||||
// 检查是否已停止
|
||||
if (!isFillingSteps.value || agentsStore.isStopping) {
|
||||
break
|
||||
}
|
||||
|
||||
// 处理简报数据格式
|
||||
outlineData['Collaboration Process'] = changeBriefs(outlineData['Collaboration Process'])
|
||||
|
||||
// 立即显示大纲
|
||||
agentsStore.setAgentRawPlan({ data: outlineData, loading: false })
|
||||
outlineLoaded = true
|
||||
emit('search', searchValue.value)
|
||||
|
||||
// 🆕 预加载所有任务的智能体评分数据(在后台静默执行)
|
||||
preloadAllTaskAgentScores(outlineData, searchValue.value)
|
||||
|
||||
// 开始填充步骤详情,设置状态
|
||||
isFillingSteps.value = true
|
||||
|
||||
// 并行填充所有步骤的详情
|
||||
const steps = outlineData['Collaboration Process'] || []
|
||||
|
||||
// 带重试的填充函数
|
||||
const fillStepWithRetry = async (step: any, retryCount = 0): Promise<void> => {
|
||||
const maxRetries = 2 // 最多重试2次
|
||||
|
||||
// 检查是否已停止
|
||||
if (!isFillingSteps.value) {
|
||||
console.log('检测到停止信号,跳过步骤填充')
|
||||
return
|
||||
}
|
||||
|
||||
try {
|
||||
if (!step.StepName) {
|
||||
console.warn('步骤缺少 StepName,跳过填充详情')
|
||||
return
|
||||
}
|
||||
|
||||
// 使用现有的 fillStepTask API 填充每个步骤的详情
|
||||
await withRetry(
|
||||
async () => {
|
||||
const detailedStep = await api.fillStepTask({
|
||||
goal: searchValue.value,
|
||||
stepTask: {
|
||||
StepName: step.StepName,
|
||||
TaskContent: step.TaskContent,
|
||||
InputObject_List: step.InputObject_List,
|
||||
OutputObject: step.OutputObject
|
||||
}
|
||||
OutputObject: step.OutputObject,
|
||||
},
|
||||
})
|
||||
|
||||
// 再次检查是否已停止(在 API 调用后)
|
||||
if (!isFillingSteps.value) {
|
||||
console.log('检测到停止信号,跳过更新步骤详情')
|
||||
return
|
||||
}
|
||||
|
||||
// 更新该步骤的详情到 store
|
||||
updateStepDetail(step.StepName, detailedStep)
|
||||
} catch (error) {
|
||||
console.error(
|
||||
`填充步骤 ${step.StepName} 详情失败 (尝试 ${retryCount + 1}/${maxRetries + 1}):`,
|
||||
error
|
||||
)
|
||||
|
||||
// 如果未达到最大重试次数,延迟后重试
|
||||
if (retryCount < maxRetries) {
|
||||
console.log(`正在重试步骤 ${step.StepName}...`)
|
||||
// 延迟1秒后重试,避免立即重试导致同样的问题
|
||||
await new Promise(resolve => setTimeout(resolve, 1000))
|
||||
return fillStepWithRetry(step, retryCount + 1)
|
||||
} else {
|
||||
console.error(`步骤 ${step.StepName} 在 ${maxRetries + 1} 次尝试后仍然失败`)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// // 为每个步骤并行填充详情(选人+过程)
|
||||
// const fillPromises = steps.map(step => fillStepWithRetry(step))
|
||||
// // 等待所有步骤填充完成(包括重试)
|
||||
// await Promise.all(fillPromises)
|
||||
|
||||
// 串行填充所有步骤的详情(避免字段混乱)
|
||||
for (const step of steps) {
|
||||
await fillStepWithRetry(step)
|
||||
}
|
||||
} finally {
|
||||
triggerOnFocus.value = true
|
||||
// 完成填充,重置状态
|
||||
isFillingSteps.value = false
|
||||
currentStepAbortController.value = null
|
||||
// 如果大纲加载失败,确保关闭loading
|
||||
if (!outlineLoaded) {
|
||||
agentsStore.setAgentRawPlan({ loading: false })
|
||||
}
|
||||
},
|
||||
{
|
||||
maxRetries: 2, // 减少重试次数,因为是串行填充
|
||||
initialDelayMs: 1000, // 使用较小的延迟
|
||||
shouldRetry: () => isFillingSteps.value && !agentsStore.isStopping, // 可取消的重试
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
// 重置状态
|
||||
triggerOnFocus.value = true
|
||||
if (isStopPending.value) {
|
||||
isStopping.value = false
|
||||
isStopPending.value = false
|
||||
agentsStore.setIsStopping(false)
|
||||
agentsStore.setHasStoppedFilling(true)
|
||||
}
|
||||
isFillingSteps.value = false
|
||||
currentStepAbortController.value = null
|
||||
}
|
||||
|
||||
// 辅助函数:更新单个步骤的详情
|
||||
//更新单个步骤的详情
|
||||
function updateStepDetail(stepId: string, detailedStep: any) {
|
||||
const planData = agentsStore.agentRawPlan.data
|
||||
if (!planData) return
|
||||
@@ -322,7 +259,7 @@ function updateStepDetail(stepId: string, detailedStep: any) {
|
||||
|
||||
const index = collaborationProcess.findIndex((s: any) => s.StepName === stepId)
|
||||
if (index !== -1 && collaborationProcess[index]) {
|
||||
// 保持响应式更新 - 使用 Vue 的响应式系统
|
||||
// 保持响应式更新
|
||||
Object.assign(collaborationProcess[index], {
|
||||
AgentSelection: detailedStep.AgentSelection || [],
|
||||
TaskProcess: detailedStep.TaskProcess || [],
|
||||
@@ -338,7 +275,7 @@ const querySearch = (queryString: string, cb: (v: { value: string }[]) => void)
|
||||
const results = queryString
|
||||
? configStore.config.taskPromptWords.filter(createFilter(queryString))
|
||||
: configStore.config.taskPromptWords
|
||||
// call callback function to return suggestions
|
||||
// 调用回调函数返回建议列表
|
||||
cb(results.map(item => ({ value: item })))
|
||||
}
|
||||
|
||||
@@ -351,6 +288,12 @@ const createFilter = (queryString: string) => {
|
||||
// 组件挂载时检查URL参数
|
||||
onMounted(() => {
|
||||
autoSearchFromUrl()
|
||||
websocket.on('generation_stopped', onGenerationStopped)
|
||||
})
|
||||
|
||||
// 组件卸载时移除事件监听
|
||||
onUnmounted(() => {
|
||||
websocket.off('generation_stopped', onGenerationStopped)
|
||||
})
|
||||
</script>
|
||||
|
||||
@@ -394,20 +337,20 @@ onMounted(() => {
|
||||
class="task-button"
|
||||
color="linear-gradient(to right, #00C7D2, #315AB4)"
|
||||
size="large"
|
||||
:title="isFillingSteps ? '点击停止生成' : '点击搜索任务'"
|
||||
:title="isFillingSteps && !isStopping ? '点击停止生成' : '点击搜索任务'"
|
||||
circle
|
||||
:loading="agentsStore.agentRawPlan.loading"
|
||||
:disabled="!searchValue"
|
||||
:loading="agentsStore.agentRawPlan.loading || isStopping"
|
||||
:disabled="!searchValue || isStopping"
|
||||
@click.stop="handleButtonClick"
|
||||
>
|
||||
<SvgIcon
|
||||
v-if="!agentsStore.agentRawPlan.loading && !isFillingSteps"
|
||||
v-if="!agentsStore.agentRawPlan.loading && !isFillingSteps && !isStopping"
|
||||
icon-class="paper-plane"
|
||||
size="18px"
|
||||
color="#ffffff"
|
||||
/>
|
||||
<SvgIcon
|
||||
v-if="!agentsStore.agentRawPlan.loading && isFillingSteps"
|
||||
v-if="!agentsStore.agentRawPlan.loading && isFillingSteps && !isStopping"
|
||||
icon-class="stoprunning"
|
||||
size="30px"
|
||||
color="#ffffff"
|
||||
@@ -429,7 +372,6 @@ onMounted(() => {
|
||||
width: 40%;
|
||||
margin: 0 auto;
|
||||
border: 2px solid transparent;
|
||||
$bg: var(--el-input-bg-color, var(--el-fill-color-blank));
|
||||
background: linear-gradient(var(--color-bg-taskbar), var(--color-bg-taskbar)) padding-box,
|
||||
linear-gradient(to right, #00c8d2, #315ab4) border-box;
|
||||
border-radius: 30px;
|
||||
@@ -556,58 +498,4 @@ onMounted(() => {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.drawer-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
width: 100%;
|
||||
.title {
|
||||
font-size: 18px;
|
||||
font-weight: bold;
|
||||
}
|
||||
}
|
||||
|
||||
.process-list {
|
||||
padding: 0 8px;
|
||||
}
|
||||
.process-item {
|
||||
margin-bottom: 16px;
|
||||
padding: 12px;
|
||||
border-radius: 8px;
|
||||
background: var(--color-bg-list);
|
||||
border: 1px solid var(--color-border-default);
|
||||
|
||||
.process-content {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
gap: 8px;
|
||||
|
||||
.agent-tag {
|
||||
display: inline-block;
|
||||
padding: 4px 8px;
|
||||
border-radius: 4px;
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
white-space: nowrap;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.process-text {
|
||||
line-height: 1.6;
|
||||
font-size: 14px;
|
||||
color: var(--el-text-color-primary);
|
||||
white-space: pre-wrap;
|
||||
}
|
||||
}
|
||||
|
||||
.edit-container {
|
||||
margin-top: 8px;
|
||||
}
|
||||
}
|
||||
|
||||
.process-item:hover {
|
||||
border-color: var(--el-border-color);
|
||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,14 +1,16 @@
|
||||
<script setup lang="ts">
|
||||
import { ref, computed, onMounted } from 'vue'
|
||||
import { pick } from 'lodash'
|
||||
import { ElMessage } from 'element-plus'
|
||||
import api from '@/api/index.ts'
|
||||
import SvgIcon from '@/components/SvgIcon/index.vue'
|
||||
import { agentMapDuty } from '@/layout/components/config.ts'
|
||||
import { type Agent, useAgentsStore } from '@/stores'
|
||||
import { readConfig } from '@/utils/readJson.ts'
|
||||
import { useNotification } from '@/composables/useNotification.ts'
|
||||
import AgentRepoList from './AgentRepoList.vue'
|
||||
|
||||
const { error, success } = useNotification()
|
||||
|
||||
const agentsStore = useAgentsStore()
|
||||
|
||||
// 如果agentsStore.agents不存在就读取默认配置的json文件
|
||||
@@ -58,7 +60,7 @@ const readFileContent = (file: File) => {
|
||||
return
|
||||
}
|
||||
|
||||
const validAgents = jsonData.filter((agent) => {
|
||||
const validAgents = jsonData.filter(agent => {
|
||||
// 验证必需字段
|
||||
if (!agent.Name || typeof agent.Name !== 'string') {
|
||||
return false
|
||||
@@ -87,27 +89,24 @@ const readFileContent = (file: File) => {
|
||||
apiKey: agent.apiKey,
|
||||
apiModel: agent.apiModel
|
||||
}))
|
||||
|
||||
agentsStore.setAgents(processedAgents)
|
||||
|
||||
// 调用API
|
||||
api
|
||||
.setAgents(processedAgents)
|
||||
.then(() => {
|
||||
ElMessage.success('智能体上传成功')
|
||||
success('智能体上传成功')
|
||||
})
|
||||
.catch(() => {
|
||||
ElMessage.error('智能体上传失败')
|
||||
error('智能体上传失败')
|
||||
})
|
||||
} catch {
|
||||
ElMessage.error('JSON解析错误')
|
||||
error('JSON解析错误')
|
||||
}
|
||||
}
|
||||
|
||||
reader.onerror = () => {
|
||||
ElMessage.error('文件读取错误')
|
||||
error('文件读取错误')
|
||||
}
|
||||
|
||||
reader.readAsText(file)
|
||||
}
|
||||
|
||||
|
||||
@@ -19,7 +19,6 @@ const props = defineProps<{
|
||||
}>()
|
||||
|
||||
const emit = defineEmits<{
|
||||
(e: 'open-edit', stepId: string, processId: string): void
|
||||
(e: 'save-edit', stepId: string, processId: string, value: string): void
|
||||
}>()
|
||||
|
||||
@@ -36,10 +35,8 @@ const currentTaskProcess = computed(() => {
|
||||
return rawData?.TaskProcess || []
|
||||
})
|
||||
|
||||
// 当前正在编辑的process ID
|
||||
const editingProcessId = ref<string | null>(null)
|
||||
const editValue = ref('')
|
||||
// 鼠标悬停的process ID
|
||||
const hoverProcessId = ref<string | null>(null)
|
||||
|
||||
// 处理卡片点击事件
|
||||
@@ -60,40 +57,25 @@ function isDarkMode(): boolean {
|
||||
|
||||
// 获取颜色浅两号的函数
|
||||
function getLightColor(color: string, level: number = 2): string {
|
||||
if (!color || color.length !== 7 || color[0] !== '#') return color
|
||||
|
||||
const r = parseInt(color.substr(1, 2), 16)
|
||||
const g = parseInt(color.substr(3, 2), 16)
|
||||
const b = parseInt(color.substr(5, 2), 16)
|
||||
|
||||
// 增加亮度(浅两号)
|
||||
const lightenAmount = level * 20
|
||||
const newR = Math.min(255, r + lightenAmount)
|
||||
const newG = Math.min(255, g + lightenAmount)
|
||||
const newB = Math.min(255, b + lightenAmount)
|
||||
|
||||
return `#${Math.round(newR).toString(16).padStart(2, '0')}${Math.round(newG)
|
||||
.toString(16)
|
||||
.padStart(2, '0')}${Math.round(newB).toString(16).padStart(2, '0')}`
|
||||
return adjustColor(color, level * 20)
|
||||
}
|
||||
|
||||
// 获取颜色深两号的函数
|
||||
function getDarkColor(color: string, level: number = 2): string {
|
||||
return adjustColor(color, -level * 20)
|
||||
}
|
||||
|
||||
// 通用的颜色调整函数(提取重复逻辑)
|
||||
function adjustColor(color: string, amount: number): string {
|
||||
if (!color || color.length !== 7 || color[0] !== '#') return color
|
||||
|
||||
const r = parseInt(color.substr(1, 2), 16)
|
||||
const g = parseInt(color.substr(3, 2), 16)
|
||||
const b = parseInt(color.substr(5, 2), 16)
|
||||
const r = Math.min(255, Math.max(0, parseInt(color.substr(1, 2), 16) + amount))
|
||||
const g = Math.min(255, Math.max(0, parseInt(color.substr(3, 2), 16) + amount))
|
||||
const b = Math.min(255, Math.max(0, parseInt(color.substr(5, 2), 16) + amount))
|
||||
|
||||
// 降低亮度(深两号)
|
||||
const darkenAmount = level * 20
|
||||
const newR = Math.max(0, r - darkenAmount)
|
||||
const newG = Math.max(0, g - darkenAmount)
|
||||
const newB = Math.max(0, b - darkenAmount)
|
||||
|
||||
return `#${Math.round(newR).toString(16).padStart(2, '0')}${Math.round(newG)
|
||||
return `#${r.toString(16).padStart(2, '0')}${g.toString(16).padStart(2, '0')}${b
|
||||
.toString(16)
|
||||
.padStart(2, '0')}${Math.round(newB).toString(16).padStart(2, '0')}`
|
||||
.padStart(2, '0')}`
|
||||
}
|
||||
|
||||
// 根据主题模式获取调整后的颜色
|
||||
@@ -115,11 +97,10 @@ function handleMouseLeave() {
|
||||
hoverProcessId.value = null
|
||||
}
|
||||
|
||||
// 处理双击编辑(针对单个process)
|
||||
// 处理双击编辑
|
||||
function handleDblClick(processId: string, currentDescription: string) {
|
||||
editingProcessId.value = processId
|
||||
editValue.value = currentDescription
|
||||
emit('open-edit', props.step.Id || '', processId)
|
||||
}
|
||||
|
||||
// 处理保存编辑
|
||||
@@ -262,7 +243,6 @@ function handleCancel() {
|
||||
margin-bottom: 8px;
|
||||
|
||||
.edit-card {
|
||||
//background: #f0f2f5;
|
||||
border-radius: 8px;
|
||||
padding: 16px;
|
||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
|
||||
@@ -282,13 +262,6 @@ function handleCancel() {
|
||||
color: var(--color-text-taskbar);
|
||||
}
|
||||
}
|
||||
|
||||
.edit-buttons {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
justify-content: flex-end;
|
||||
margin-top: 8px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -299,10 +272,6 @@ function handleCancel() {
|
||||
padding: 2px 4px;
|
||||
border-radius: 3px;
|
||||
transition: background-color 0.2s ease;
|
||||
|
||||
&.hovered {
|
||||
transition: background-color 0.2s ease;
|
||||
}
|
||||
}
|
||||
|
||||
.separator {
|
||||
|
||||
@@ -16,7 +16,6 @@ const props = defineProps<{
|
||||
const branchCount = computed(() => {
|
||||
if (!props.step?.Id) return 1
|
||||
|
||||
// 获取该任务步骤的分支数据
|
||||
const taskStepId = props.step.Id
|
||||
// 获取该任务的 agent 组合
|
||||
const agents = props.step.AgentSelection || []
|
||||
@@ -33,7 +32,6 @@ const isClickable = computed(() => {
|
||||
})
|
||||
|
||||
const handleClick = (event?: MouseEvent) => {
|
||||
// 只有可点击时才执行操作
|
||||
if (!isClickable.value) {
|
||||
return
|
||||
}
|
||||
@@ -48,7 +46,6 @@ const handleClick = (event?: MouseEvent) => {
|
||||
if (props.step) {
|
||||
agentsStore.setCurrentTask(props.step)
|
||||
}
|
||||
// 触发打开任务过程探索窗口
|
||||
agentsStore.openPlanTask()
|
||||
}
|
||||
</script>
|
||||
@@ -56,7 +53,7 @@ const handleClick = (event?: MouseEvent) => {
|
||||
<template>
|
||||
<div
|
||||
class="task-button"
|
||||
:class="{ 'has-branches': branchCount > 0, 'is-disabled': !isClickable }"
|
||||
:class="{ 'has-branches': branchCount > 1, 'is-disabled': !isClickable }"
|
||||
@click="handleClick"
|
||||
:title="isClickable ? `${branchCount} 个分支` : '请先在任务大纲中选中此任务'"
|
||||
>
|
||||
|
||||
@@ -1,168 +0,0 @@
|
||||
<!-- AdditionalOutputCard.vue -->
|
||||
<script setup lang="ts">
|
||||
import { computed, ref, nextTick } from 'vue'
|
||||
import { useAgentsStore } from '@/stores'
|
||||
import SvgIcon from '@/components/SvgIcon/index.vue'
|
||||
|
||||
const agentsStore = useAgentsStore()
|
||||
const props = defineProps<{
|
||||
index: number
|
||||
}>()
|
||||
|
||||
// 获取产物名称
|
||||
const currentOutput = computed(() => {
|
||||
return agentsStore.additionalOutputs[props.index] || ''
|
||||
})
|
||||
|
||||
// 编辑状态
|
||||
const isEditing = ref(false)
|
||||
const inputValue = ref('')
|
||||
const originalValue = ref('')
|
||||
const inputRef = ref<HTMLElement>()
|
||||
|
||||
// 点击编辑图标
|
||||
const handleEditClick = () => {
|
||||
isEditing.value = true
|
||||
originalValue.value = inputValue.value
|
||||
|
||||
// 等待 DOM 更新后聚焦输入框
|
||||
nextTick(() => {
|
||||
if (inputRef.value) {
|
||||
const inputEl = inputRef.value.querySelector('input')
|
||||
if (inputEl) {
|
||||
inputEl.focus()
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// 保存编辑
|
||||
const handleSave = () => {
|
||||
if (isEditing.value) {
|
||||
isEditing.value = false
|
||||
}
|
||||
}
|
||||
|
||||
// 取消编辑
|
||||
const handleCancel = () => {
|
||||
if (isEditing.value) {
|
||||
inputValue.value = originalValue.value // 恢复原始值
|
||||
isEditing.value = false
|
||||
}
|
||||
}
|
||||
|
||||
// 处理键盘事件
|
||||
const handleKeydown = (event: KeyboardEvent) => {
|
||||
if (event.key === 'Enter') {
|
||||
event.preventDefault()
|
||||
handleSave()
|
||||
} else if (event.key === 'Escape') {
|
||||
event.preventDefault()
|
||||
handleCancel()
|
||||
}
|
||||
}
|
||||
|
||||
// 输入框失去焦点处理
|
||||
const handleBlur = () => {
|
||||
// 延迟处理,避免点击按钮时立即触发
|
||||
setTimeout(() => {
|
||||
if (isEditing.value) {
|
||||
handleSave()
|
||||
}
|
||||
}, 200)
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<!-- 当产物存在时才显示 -->
|
||||
<div v-if="currentOutput" class="card-item">
|
||||
<el-card
|
||||
class="card-item w-full relative output-object-card"
|
||||
:shadow="true"
|
||||
:id="`additional-output-${index}`"
|
||||
>
|
||||
<!-- 显示产物名称 -->
|
||||
<div class="text-start w-[100%]">
|
||||
<div class="text-[18px] font-bold text-[var(--color-text)] mb-2">
|
||||
{{ currentOutput }}
|
||||
</div>
|
||||
<div ref="inputRef">
|
||||
<el-input
|
||||
v-model="inputValue"
|
||||
:readonly="!isEditing"
|
||||
:placeholder="isEditing ? '请输入内容...' : '点击编辑图标开始编辑...'"
|
||||
@keydown="handleKeydown"
|
||||
@blur="handleBlur"
|
||||
:class="{ editing: isEditing }"
|
||||
>
|
||||
<template #suffix>
|
||||
<!-- 只读状态:显示编辑图标 -->
|
||||
<div v-if="!isEditing" class="flex items-center">
|
||||
<svg-icon
|
||||
icon-class="Edit"
|
||||
size="20px"
|
||||
class="cursor-pointer hover:text-[#409eff] transition-colors"
|
||||
@click="handleEditClick"
|
||||
title="点击编辑"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
</el-input>
|
||||
</div>
|
||||
|
||||
<!-- 编辑状态下的提示 -->
|
||||
<div v-if="isEditing" class="mt-2 text-end text-xs text-gray-500">
|
||||
<svg-icon
|
||||
icon-class="Check"
|
||||
size="20px"
|
||||
color="#328621"
|
||||
class="cursor-pointer mr-4"
|
||||
@click="handleSave"
|
||||
title="保存"
|
||||
/>
|
||||
<svg-icon
|
||||
icon-class="Cancel"
|
||||
size="20px"
|
||||
color="#8e0707"
|
||||
class="cursor-pointer mr-4"
|
||||
@click="handleCancel"
|
||||
title="取消"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</el-card>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.card-item {
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.output-object-card {
|
||||
:deep(.el-card__body) {
|
||||
min-height: 80px;
|
||||
display: flex;
|
||||
align-items: start;
|
||||
justify-content: start;
|
||||
}
|
||||
}
|
||||
|
||||
/* 输入框样式 */
|
||||
:deep(.el-input .el-input__wrapper) {
|
||||
box-shadow: none;
|
||||
background-color: var(--color-bg-three);
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
|
||||
/* 编辑状态下的输入框样式 */
|
||||
:deep(.el-input.editing .el-input__wrapper) {
|
||||
border: 1px solid #dcdfe6;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
:deep(.el-input.editing .el-input__wrapper.is-focus) {
|
||||
border-color: #c0c4cc;
|
||||
box-shadow: 0 0 0 1px #c0c4cc;
|
||||
}
|
||||
</style>
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,12 +1,4 @@
|
||||
<script setup lang="ts">
|
||||
defineProps<{
|
||||
isAdding?: boolean
|
||||
}>()
|
||||
|
||||
defineEmits<{
|
||||
(e: 'start-add-output'): void
|
||||
}>()
|
||||
</script>
|
||||
<script setup lang="ts"></script>
|
||||
<template>
|
||||
<div class="absolute inset-0 flex items-start gap-[14%]">
|
||||
<!-- 左侧元素 -->
|
||||
@@ -24,21 +16,6 @@ defineEmits<{
|
||||
<div class="flex-1 relative h-full flex justify-center">
|
||||
<!-- 背景那一根线 -->
|
||||
<div class="h-full bg-[var(--color-bg-flow)] w-[5px]">
|
||||
<!-- 顶部加号区域 -->
|
||||
<!-- <div
|
||||
v-if="!isAdding"
|
||||
v-dev-only
|
||||
class="plus-area mt-[35px] ml-[-15px] w-[34px] h-[34px] flex items-center justify-center cursor-pointer rounded-full"
|
||||
@click="$emit('start-add-output')"
|
||||
> -->
|
||||
<!-- 加号图标 -->
|
||||
<!-- <svg-icon
|
||||
icon-class="plus"
|
||||
color="var(--color-text)"
|
||||
size="20px"
|
||||
class="plus-icon opacity-0 transition-opacity duration-200"
|
||||
/>
|
||||
</div> -->
|
||||
<!-- 线底部的小圆球 -->
|
||||
<div
|
||||
class="absolute bottom-0 left-1/2 transform -translate-x-1/2 bg-[var(--color-bg-flow)] w-[15px] h-[15px] rounded-full"
|
||||
|
||||
@@ -1,127 +0,0 @@
|
||||
# Mock 数据说明
|
||||
|
||||
本目录包含用于分支功能的 mock 数据,支持在开发环境中测试分支逻辑,无需调用真实后端 API。
|
||||
|
||||
## 文件说明
|
||||
|
||||
### 1. `branchPlanOutlineMock.ts`
|
||||
**用途**: 根节点级别的分支(任务大纲分支)
|
||||
|
||||
**类型**: `IApiStepTask[][]`
|
||||
|
||||
**说明**: 返回多个分支方案,每个方案是一个完整的任务流程(IApiStepTask[])
|
||||
|
||||
**示例结构**:
|
||||
```typescript
|
||||
[
|
||||
// 第一个分支方案(瀑布流开发)
|
||||
[task1, task2, ...],
|
||||
// 第二个分支方案(敏捷开发)
|
||||
[task1, task2, ...],
|
||||
// 第三个分支方案(快速原型)
|
||||
[task1, task2, ...]
|
||||
]
|
||||
```
|
||||
|
||||
### 2. `branchTaskProcessMock.ts`
|
||||
**用途**: 任务节点级别的分支(任务流程分支)
|
||||
|
||||
**类型**: `IApiAgentAction[][]`
|
||||
|
||||
**说明**: 返回多个分支方案,每个方案是一系列动作(IApiAgentAction[]),这些动作会追加到现有任务的 TaskProcess 中
|
||||
|
||||
**示例结构**:
|
||||
```typescript
|
||||
[
|
||||
// 第一个分支方案(标准开发流程)
|
||||
[action1, action2, action3, ...],
|
||||
// 第二个分支方案(快速原型流程)
|
||||
[action1, action2, ...],
|
||||
// ... 更多方案
|
||||
]
|
||||
```
|
||||
|
||||
## 如何使用
|
||||
|
||||
### 切换 Mock 数据开关
|
||||
|
||||
在 `PlanModification.vue` 文件中,找到以下配置:
|
||||
|
||||
```typescript
|
||||
// 开关:控制是否使用 mock 数据(开发时设置为 true,生产时设置为 false)
|
||||
const USE_MOCK_DATA = true
|
||||
```
|
||||
|
||||
- **开发阶段**: 设置为 `true`,使用 mock 数据
|
||||
- **生产环境**: 设置为 `false`,调用真实 API
|
||||
|
||||
### 数据转换流程
|
||||
|
||||
```
|
||||
IApiStepTask (API 格式)
|
||||
↓ convertToIRawStepTask()
|
||||
IRawStepTask (内部格式)
|
||||
↓
|
||||
更新到 agentsStore.agentRawPlan.data
|
||||
↓
|
||||
Vue Flow 流程图自动更新
|
||||
```
|
||||
|
||||
### Mock 数据特点
|
||||
|
||||
1. **模拟网络延迟**: Mock 数据调用会模拟 800ms 的网络延迟
|
||||
2. **多方案选择**: 每个分支接口提供多个备选方案(目前默认使用第一个)
|
||||
3. **完整数据结构**: Mock 数据包含完整的字段,与真实 API 返回格式一致
|
||||
4. **类型安全**: 使用 TypeScript 类型定义,确保类型正确
|
||||
|
||||
## 扩展 Mock 数据
|
||||
|
||||
### 添加新的分支方案
|
||||
|
||||
在对应的 mock 文件中添加新的数组元素:
|
||||
|
||||
```typescript
|
||||
// branchPlanOutlineMock.ts
|
||||
const mockPlanBranchData: IApiStepTask[][] = [
|
||||
// 现有方案...
|
||||
[
|
||||
// 新增方案
|
||||
{
|
||||
name: '新方案步骤1',
|
||||
content: '...',
|
||||
// ... 其他字段
|
||||
},
|
||||
{
|
||||
name: '新方案步骤2',
|
||||
content: '...',
|
||||
// ... 其他字段
|
||||
}
|
||||
]
|
||||
]
|
||||
```
|
||||
|
||||
### 随机选择方案
|
||||
|
||||
修改 `PlanModification.vue` 中的方案选择逻辑:
|
||||
|
||||
```typescript
|
||||
// 当前:固定选择第一个
|
||||
const mockBranchTasks = branchPlanOutlineMock[0]
|
||||
|
||||
// 改为:随机选择一个方案
|
||||
const randomIndex = Math.floor(Math.random() * branchPlanOutlineMock.length)
|
||||
const mockBranchTasks = branchPlanOutlineMock[randomIndex]
|
||||
```
|
||||
|
||||
## 注意事项
|
||||
|
||||
1. ⚠️ **生产环境**: 发布前务必将 `USE_MOCK_DATA` 设置为 `false`
|
||||
2. 🔍 **调试**: 查看控制台日志,以 `[Mock]` 开头的是 mock 数据相关日志
|
||||
3. 📝 **数据一致性**: 确保 mock 数据结构与真实 API 返回格式一致
|
||||
4. 🔄 **数据持久化**: Mock 数据仅存在于前端,刷新页面后会丢失
|
||||
|
||||
## 相关文件
|
||||
|
||||
- `PlanModification.vue`: 主要逻辑文件,包含分支添加和 mock 数据集成
|
||||
- `api/index.ts`: 真实 API 接口定义
|
||||
- `stores/modules/agents.ts`: 类型定义和数据存储
|
||||
@@ -1,246 +0,0 @@
|
||||
// branch_PlanOutline 接口的 Mock 数据和 Mock API
|
||||
// 模拟后端返回的原始数据格式(IRawPlanResponse)
|
||||
|
||||
import type { IRawPlanResponse, IRawStepTask } from '@/stores'
|
||||
|
||||
// 后端返回的数据格式
|
||||
export type BranchPlanOutlineResponse = IRawPlanResponse
|
||||
|
||||
// Mock 数据:模拟后端返回的原始分支数据(不含 Collaboration_Brief_FrontEnd)
|
||||
// 注意:这里模拟的是 branch_PlanOutline 函数返回的数据,不是前端转换后的数据
|
||||
const mockBranchDataRaw: IRawStepTask[][] = [
|
||||
// 第一个分支方案
|
||||
[
|
||||
{
|
||||
StepName: '分析用户需求',
|
||||
TaskContent: '分析用户需求,制定项目开发计划',
|
||||
InputObject_List: ['腐蚀类型及成因列表'],
|
||||
OutputObject: '项目开发计划书',
|
||||
AgentSelection: ['腐蚀机理研究员', '实验材料学家'],
|
||||
Collaboration_Brief_frontEnd: {
|
||||
template: '',
|
||||
data: {},
|
||||
},
|
||||
TaskProcess: [],
|
||||
},
|
||||
{
|
||||
StepName: '系统设计与架构',
|
||||
TaskContent: '设计系统架构和数据库结构',
|
||||
InputObject_List: ['项目开发计划书'],
|
||||
OutputObject: '系统设计文档',
|
||||
AgentSelection: ['腐蚀机理研究员', '防护工程专家'],
|
||||
Collaboration_Brief_frontEnd: {
|
||||
template: '',
|
||||
data: {},
|
||||
},
|
||||
TaskProcess: [],
|
||||
},
|
||||
],
|
||||
// 第二个分支方案(快速原型方案)
|
||||
[
|
||||
{
|
||||
StepName: '需求快速原型',
|
||||
TaskContent: '构建快速原型验证核心功能',
|
||||
InputObject_List: ['腐蚀类型及成因列表'],
|
||||
OutputObject: '原型系统',
|
||||
AgentSelection: ['实验材料学家', '防护工程专家'],
|
||||
Collaboration_Brief_frontEnd: {
|
||||
template: '',
|
||||
data: {},
|
||||
},
|
||||
TaskProcess: [],
|
||||
},
|
||||
{
|
||||
StepName: '原型测试与优化',
|
||||
TaskContent: '测试原型并根据反馈快速迭代',
|
||||
InputObject_List: ['原型系统'],
|
||||
OutputObject: '优化后的原型',
|
||||
AgentSelection: ['腐蚀机理研究员', '实验材料学家'],
|
||||
Collaboration_Brief_frontEnd: {
|
||||
template: '',
|
||||
data: {},
|
||||
},
|
||||
TaskProcess: [],
|
||||
},
|
||||
],
|
||||
// 第三个分支方案(质量优先方案)
|
||||
[
|
||||
{
|
||||
StepName: '需求深度分析',
|
||||
TaskContent: '深入分析用户需求和技术可行性',
|
||||
InputObject_List: ['腐蚀类型及成因列表'],
|
||||
OutputObject: '详细需求分析报告',
|
||||
AgentSelection: ['腐蚀机理研究员', '防护工程专家'],
|
||||
Collaboration_Brief_frontEnd: {
|
||||
template: '',
|
||||
data: {},
|
||||
},
|
||||
TaskProcess: [],
|
||||
},
|
||||
{
|
||||
StepName: '质量保障设计',
|
||||
TaskContent: '设计质量保障体系和测试方案',
|
||||
InputObject_List: ['详细需求分析报告'],
|
||||
OutputObject: '质量保障方案',
|
||||
AgentSelection: ['实验材料学家', '防护工程专家'],
|
||||
Collaboration_Brief_frontEnd: {
|
||||
template: '',
|
||||
data: {},
|
||||
},
|
||||
TaskProcess: [],
|
||||
},
|
||||
{
|
||||
StepName: '系统开发与测试',
|
||||
TaskContent: '按质量标准进行系统开发和测试',
|
||||
InputObject_List: ['质量保障方案'],
|
||||
OutputObject: '经过完整测试的系统',
|
||||
AgentSelection: ['腐蚀机理研究员', '实验材料学家'],
|
||||
Collaboration_Brief_frontEnd: {
|
||||
template: '',
|
||||
data: {},
|
||||
},
|
||||
TaskProcess: [],
|
||||
},
|
||||
],
|
||||
// 第四个分支方案(敏捷开发方案)
|
||||
[
|
||||
{
|
||||
StepName: '迭代规划',
|
||||
TaskContent: '制定敏捷开发迭代计划',
|
||||
InputObject_List: ['腐蚀类型及成因列表'],
|
||||
OutputObject: '迭代计划',
|
||||
AgentSelection: ['防护工程专家', '腐蚀机理研究员'],
|
||||
Collaboration_Brief_frontEnd: {
|
||||
template: '',
|
||||
data: {},
|
||||
},
|
||||
TaskProcess: [],
|
||||
},
|
||||
{
|
||||
StepName: '快速开发与验证',
|
||||
TaskContent: '快速开发并持续验证功能',
|
||||
InputObject_List: ['迭代计划'],
|
||||
OutputObject: '可运行的功能模块',
|
||||
AgentSelection: ['实验材料学家', '防护工程专家'],
|
||||
Collaboration_Brief_frontEnd: {
|
||||
template: '',
|
||||
data: {},
|
||||
},
|
||||
TaskProcess: [],
|
||||
},
|
||||
],
|
||||
]
|
||||
|
||||
/**
|
||||
* 模拟后端的 Add_Collaboration_Brief_FrontEnd 函数
|
||||
* 为每个任务添加前端协作简报(Collaboration_Brief_frontEnd)
|
||||
*/
|
||||
function Add_Collaboration_Brief_FrontEnd(branchList: IRawStepTask[][]): IRawStepTask[][] {
|
||||
return branchList.map((tasks) =>
|
||||
tasks.map((task) => ({
|
||||
...task,
|
||||
Collaboration_Brief_frontEnd: generateCollaborationBrief(task),
|
||||
})),
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成协作简报(Collaboration_Brief_frontEnd)
|
||||
* 根据 StepName、TaskContent、AgentSelection 生成模板和数据
|
||||
*/
|
||||
function generateCollaborationBrief(task: IRawStepTask): {
|
||||
template: string
|
||||
data: Record<string, any>
|
||||
} {
|
||||
const agents = task.AgentSelection || []
|
||||
const stepName = task.StepName || ''
|
||||
|
||||
// 为每个 agent 生成颜色
|
||||
const colors = [
|
||||
'hsl(210, 70%, 50%)',
|
||||
'hsl(30, 70%, 50%)',
|
||||
'hsl(120, 70%, 50%)',
|
||||
'hsl(270, 70%, 50%)',
|
||||
]
|
||||
|
||||
// 生成 data 对象
|
||||
const data: Record<string, any> = {}
|
||||
agents.forEach((agent, index) => {
|
||||
data[agent] = {
|
||||
text: agent,
|
||||
style: { background: colors[index % colors.length] },
|
||||
}
|
||||
})
|
||||
|
||||
// 生成 template(简化版本,实际应根据任务内容生成)
|
||||
const template =
|
||||
agents.length > 0
|
||||
? agents.map((agent, i) => `!<${agent}>!负责!<${stepName}-${i}>!`).join(',')
|
||||
: ''
|
||||
|
||||
return {
|
||||
template,
|
||||
data,
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Mock API:模拟后端 branch_PlanOutline 接口调用
|
||||
*
|
||||
* @param branch_Number - 分支数量
|
||||
* @param Modification_Requirement - 修改需求
|
||||
* @param Existing_Steps - 现有步骤列表(包含完整信息的对象数组)
|
||||
* @param Baseline_Completion - 基线完成度
|
||||
* @param InitialObject_List - 初始对象列表
|
||||
* @param General_Goal - 总体目标
|
||||
* @returns Promise<IRawPlanResponse> - 返回包含 'Collaboration Process' 的响应
|
||||
*/
|
||||
export const mockBranchPlanOutlineAPI = async (params: {
|
||||
branch_Number: number
|
||||
Modification_Requirement: string
|
||||
Existing_Steps: any[] // 临时使用 any[],因为这里没有 IRawStepTask 类型
|
||||
Baseline_Completion: number
|
||||
InitialObject_List: string[]
|
||||
General_Goal: string
|
||||
}): Promise<IRawPlanResponse> => {
|
||||
// 模拟网络延迟 800ms
|
||||
await new Promise((resolve) => setTimeout(resolve, 800))
|
||||
|
||||
console.log('[Mock API] branch_PlanOutline 调用参数:', params)
|
||||
|
||||
// 🆕 使用轮询方式选择分支方案(依次循环使用所有分支方案)
|
||||
const totalBranches = mockBranchDataRaw.length
|
||||
const sessionKey = `branch-plan-outline-index-${params.General_Goal || 'default'}`
|
||||
|
||||
// 获取上一次的选择索引
|
||||
let lastIndex = parseInt(sessionStorage.getItem(sessionKey) || '0')
|
||||
|
||||
// 计算本次的选择索引(轮询到下一个分支)
|
||||
const selectedBranchIndex = (lastIndex + 1) % totalBranches
|
||||
|
||||
// 保存本次的选择索引
|
||||
sessionStorage.setItem(sessionKey, selectedBranchIndex.toString())
|
||||
|
||||
const rawBranchData = mockBranchDataRaw[selectedBranchIndex] || []
|
||||
|
||||
console.log(
|
||||
'[Mock API] branch_PlanOutline 选择分支方案:',
|
||||
selectedBranchIndex + 1,
|
||||
'/',
|
||||
totalBranches,
|
||||
)
|
||||
|
||||
// 模拟后端处理:添加 Collaboration_Brief_FrontEnd
|
||||
const processedBranches = Add_Collaboration_Brief_FrontEnd([rawBranchData])
|
||||
|
||||
// 构造响应数据(符合后端返回格式)
|
||||
const response: IRawPlanResponse = {
|
||||
'Collaboration Process': processedBranches[0] || [],
|
||||
}
|
||||
|
||||
console.log('[Mock API] branch_PlanOutline 返回数据:', response)
|
||||
|
||||
return response
|
||||
}
|
||||
|
||||
export default mockBranchPlanOutlineAPI
|
||||
@@ -1,617 +0,0 @@
|
||||
// fill_stepTask 接口的 Mock API
|
||||
// 简化版本:硬编码多个流程卡片的填充内容(支持多个分支方案)
|
||||
|
||||
import type { IRawStepTask } from '@/stores'
|
||||
|
||||
// 后端返回的数据格式
|
||||
export type FillStepTaskResponse = IRawStepTask
|
||||
|
||||
/**
|
||||
* Mock API:模拟后端 fill_stepTask 接口调用
|
||||
*
|
||||
* 根据 stepTask 的 StepName 返回对应的填充内容
|
||||
* 支持多个分支方案的轮询选择
|
||||
*
|
||||
* @param General_Goal - 总体目标
|
||||
* @param stepTask - 待填充的任务(包含基本字段)
|
||||
* @returns Promise<IRawStepTask> - 返回填充了 AgentSelection 和 TaskProcess 的完整任务
|
||||
*/
|
||||
export const mockFillStepTaskAPI = async (params: {
|
||||
General_Goal: string
|
||||
stepTask: any
|
||||
}): Promise<IRawStepTask> => {
|
||||
// 模拟网络延迟 300ms
|
||||
await new Promise((resolve) => setTimeout(resolve, 300))
|
||||
|
||||
console.log('[Mock API] fill_stepTask 调用参数:', params)
|
||||
|
||||
const stepName = params.stepTask.StepName
|
||||
|
||||
// 🆕 使用轮询方式选择填充方案(依次循环使用所有分支方案)
|
||||
const branchIndexKey = `fill-step-task-index-${params.stepTask?.Id || stepName || 'default'}`
|
||||
let lastIndex = parseInt(sessionStorage.getItem(branchIndexKey) || '0')
|
||||
const totalBranches = 4 // 总共4个分支方案
|
||||
const selectedBranchIndex = (lastIndex + 1) % totalBranches
|
||||
sessionStorage.setItem(branchIndexKey, selectedBranchIndex.toString())
|
||||
|
||||
console.log('[Mock API] fill_stepTask 选择分支方案:', selectedBranchIndex + 1, '/', totalBranches)
|
||||
|
||||
// ==================== 第一个任务组 ====================
|
||||
if (stepName === '分析用户需求' || stepName === '需求快速原型' || stepName === '需求深度分析' || stepName === '迭代规划') {
|
||||
let filledTask: IRawStepTask
|
||||
|
||||
if (selectedBranchIndex === 0) {
|
||||
// 第一个分支方案:分析用户需求
|
||||
filledTask = {
|
||||
Id: params.stepTask.Id || 'task-1',
|
||||
StepName: '分析用户需求',
|
||||
TaskContent: '分析用户需求,制定项目开发计划',
|
||||
InputObject_List: ['用户需求文档', '技术规范'],
|
||||
OutputObject: '项目开发计划书',
|
||||
AgentSelection: ['腐蚀机理研究员', '实验材料学家'],
|
||||
Collaboration_Brief_frontEnd: {
|
||||
template: '!腐蚀机理研究员!负责!腐蚀类型识别-0!,!实验材料学家!负责!腐蚀类型识别-1!',
|
||||
data: {
|
||||
腐蚀机理研究员: {
|
||||
text: '腐蚀机理研究员',
|
||||
style: { background: 'hsl(210, 70%, 50%)' },
|
||||
},
|
||||
实验材料学家: {
|
||||
text: '实验材料学家',
|
||||
style: { background: 'hsl(120, 70%, 50%)' },
|
||||
},
|
||||
},
|
||||
},
|
||||
TaskProcess: [
|
||||
{
|
||||
ID: 'action-1-1',
|
||||
ActionType: 'Propose',
|
||||
AgentName: '腐蚀机理研究员',
|
||||
Description: '分析腐蚀环境和介质特征,识别潜在腐蚀类型',
|
||||
ImportantInput: ['InputObject:腐蚀类型及成因列表'],
|
||||
},
|
||||
{
|
||||
ID: 'action-1-2',
|
||||
ActionType: 'Propose',
|
||||
AgentName: '实验材料学家',
|
||||
Description: '设计腐蚀实验方案,确定测试参数',
|
||||
ImportantInput: ['ActionResult:action-1-1'],
|
||||
},
|
||||
{
|
||||
ID: 'action-1-3',
|
||||
ActionType: 'Critique',
|
||||
AgentName: '腐蚀机理研究员',
|
||||
Description: '评估实验方案的可行性',
|
||||
ImportantInput: [
|
||||
'ActionResult:action-1-2',
|
||||
'ActionResult:action-1-1',
|
||||
'InputObject:腐蚀类型及成因列表',
|
||||
],
|
||||
},
|
||||
{
|
||||
ID: 'action-1-4',
|
||||
ActionType: 'Improve',
|
||||
AgentName: '实验材料学家',
|
||||
Description: '优化实验设计,完善测试流程',
|
||||
ImportantInput: ['ActionResult:action-1-3'],
|
||||
},
|
||||
{
|
||||
ID: 'action-1-5',
|
||||
ActionType: 'Finalize',
|
||||
AgentName: '腐蚀机理研究员',
|
||||
Description: '确认最终的腐蚀类型识别方案',
|
||||
ImportantInput: ['ActionResult:action-1-4', 'ActionResult:action-1-3'],
|
||||
},
|
||||
],
|
||||
}
|
||||
} else if (selectedBranchIndex === 1) {
|
||||
// 第二个分支方案:需求快速原型
|
||||
filledTask = {
|
||||
Id: params.stepTask.Id || 'task-1',
|
||||
StepName: '需求快速原型',
|
||||
TaskContent: '构建快速原型验证核心功能',
|
||||
InputObject_List: ['腐蚀类型及成因列表'],
|
||||
OutputObject: '原型系统',
|
||||
AgentSelection: ['实验材料学家', '防护工程专家'],
|
||||
Collaboration_Brief_frontEnd: {
|
||||
template: '!实验材料学家!负责!需求快速原型-0!,!防护工程专家!负责!需求快速原型-1!',
|
||||
data: {
|
||||
实验材料学家: {
|
||||
text: '实验材料学家',
|
||||
style: { background: 'hsl(120, 70%, 50%)' },
|
||||
},
|
||||
防护工程专家: {
|
||||
text: '防护工程专家',
|
||||
style: { background: 'hsl(30, 70%, 50%)' },
|
||||
},
|
||||
},
|
||||
},
|
||||
TaskProcess: [
|
||||
{
|
||||
ID: 'action-1-1-branch2',
|
||||
ActionType: 'Propose',
|
||||
AgentName: '实验材料学家',
|
||||
Description: '基于腐蚀类型列表,设计快速原型验证方案',
|
||||
ImportantInput: ['InputObject:腐蚀类型及成因列表'],
|
||||
},
|
||||
{
|
||||
ID: 'action-1-2-branch2',
|
||||
ActionType: 'Propose',
|
||||
AgentName: '防护工程专家',
|
||||
Description: '实现原型核心功能和用户界面',
|
||||
ImportantInput: ['ActionResult:action-1-1-branch2'],
|
||||
},
|
||||
{
|
||||
ID: 'action-1-3-branch2',
|
||||
ActionType: 'Critique',
|
||||
AgentName: '实验材料学家',
|
||||
Description: '评估原型功能完整性和用户体验',
|
||||
ImportantInput: ['ActionResult:action-1-2-branch2'],
|
||||
},
|
||||
{
|
||||
ID: 'action-1-4-branch2',
|
||||
ActionType: 'Improve',
|
||||
AgentName: '防护工程专家',
|
||||
Description: '根据反馈优化原型功能和界面',
|
||||
ImportantInput: ['ActionResult:action-1-3-branch2'],
|
||||
},
|
||||
{
|
||||
ID: 'action-1-5-branch2',
|
||||
ActionType: 'Finalize',
|
||||
AgentName: '实验材料学家',
|
||||
Description: '确认最终版本的原型系统',
|
||||
ImportantInput: ['ActionResult:action-1-4-branch2'],
|
||||
},
|
||||
],
|
||||
}
|
||||
} else if (selectedBranchIndex === 2) {
|
||||
// 第三个分支方案:需求深度分析
|
||||
filledTask = {
|
||||
Id: params.stepTask.Id || 'task-1',
|
||||
StepName: '需求深度分析',
|
||||
TaskContent: '深入分析用户需求和技术可行性',
|
||||
InputObject_List: ['腐蚀类型及成因列表'],
|
||||
OutputObject: '详细需求分析报告',
|
||||
AgentSelection: ['腐蚀机理研究员', '防护工程专家'],
|
||||
Collaboration_Brief_frontEnd: {
|
||||
template: '!腐蚀机理研究员!负责!需求深度分析-0!,!防护工程专家!负责!需求深度分析-1!',
|
||||
data: {
|
||||
腐蚀机理研究员: {
|
||||
text: '腐蚀机理研究员',
|
||||
style: { background: 'hsl(210, 70%, 50%)' },
|
||||
},
|
||||
防护工程专家: {
|
||||
text: '防护工程专家',
|
||||
style: { background: 'hsl(30, 70%, 50%)' },
|
||||
},
|
||||
},
|
||||
},
|
||||
TaskProcess: [
|
||||
{
|
||||
ID: 'action-1-1-branch3',
|
||||
ActionType: 'Propose',
|
||||
AgentName: '腐蚀机理研究员',
|
||||
Description: '全面分析腐蚀环境和技术约束条件',
|
||||
ImportantInput: ['InputObject:腐蚀类型及成因列表'],
|
||||
},
|
||||
{
|
||||
ID: 'action-1-2-branch3',
|
||||
ActionType: 'Propose',
|
||||
AgentName: '防护工程专家',
|
||||
Description: '评估技术可行性和防护方案',
|
||||
ImportantInput: ['ActionResult:action-1-1-branch3'],
|
||||
},
|
||||
{
|
||||
ID: 'action-1-3-branch3',
|
||||
ActionType: 'Critique',
|
||||
AgentName: '腐蚀机理研究员',
|
||||
Description: '评审需求分析的完整性和准确性',
|
||||
ImportantInput: ['ActionResult:action-1-2-branch3', 'ActionResult:action-1-1-branch3'],
|
||||
},
|
||||
{
|
||||
ID: 'action-1-4-branch3',
|
||||
ActionType: 'Improve',
|
||||
AgentName: '防护工程专家',
|
||||
Description: '补充技术风险评估和应对策略',
|
||||
ImportantInput: ['ActionResult:action-1-3-branch3'],
|
||||
},
|
||||
{
|
||||
ID: 'action-1-5-branch3',
|
||||
ActionType: 'Finalize',
|
||||
AgentName: '腐蚀机理研究员',
|
||||
Description: '确认详细需求分析报告',
|
||||
ImportantInput: ['ActionResult:action-1-4-branch3', 'ActionResult:action-1-3-branch3'],
|
||||
},
|
||||
],
|
||||
}
|
||||
} else {
|
||||
// 第四个分支方案:迭代规划
|
||||
filledTask = {
|
||||
Id: params.stepTask.Id || 'task-1',
|
||||
StepName: '迭代规划',
|
||||
TaskContent: '制定敏捷开发迭代计划',
|
||||
InputObject_List: ['腐蚀类型及成因列表'],
|
||||
OutputObject: '迭代计划',
|
||||
AgentSelection: ['防护工程专家', '腐蚀机理研究员'],
|
||||
Collaboration_Brief_frontEnd: {
|
||||
template: '!防护工程专家!负责!迭代规划-0!,!腐蚀机理研究员!负责!迭代规划-1!',
|
||||
data: {
|
||||
防护工程专家: {
|
||||
text: '防护工程专家',
|
||||
style: { background: 'hsl(30, 70%, 50%)' },
|
||||
},
|
||||
腐蚀机理研究员: {
|
||||
text: '腐蚀机理研究员',
|
||||
style: { background: 'hsl(210, 70%, 50%)' },
|
||||
},
|
||||
},
|
||||
},
|
||||
TaskProcess: [
|
||||
{
|
||||
ID: 'action-1-1-branch4',
|
||||
ActionType: 'Propose',
|
||||
AgentName: '防护工程专家',
|
||||
Description: '制定敏捷开发总体框架和迭代周期',
|
||||
ImportantInput: ['InputObject:腐蚀类型及成因列表'],
|
||||
},
|
||||
{
|
||||
ID: 'action-1-2-branch4',
|
||||
ActionType: 'Propose',
|
||||
AgentName: '腐蚀机理研究员',
|
||||
Description: '规划各迭代阶段的技术验证重点',
|
||||
ImportantInput: ['ActionResult:action-1-1-branch4'],
|
||||
},
|
||||
{
|
||||
ID: 'action-1-3-branch4',
|
||||
ActionType: 'Critique',
|
||||
AgentName: '防护工程专家',
|
||||
Description: '评审迭代计划的合理性和可执行性',
|
||||
ImportantInput: ['ActionResult:action-1-2-branch4'],
|
||||
},
|
||||
{
|
||||
ID: 'action-1-4-branch4',
|
||||
ActionType: 'Improve',
|
||||
AgentName: '腐蚀机理研究员',
|
||||
Description: '优化迭代节奏和里程碑设置',
|
||||
ImportantInput: ['ActionResult:action-1-3-branch4'],
|
||||
},
|
||||
{
|
||||
ID: 'action-1-5-branch4',
|
||||
ActionType: 'Finalize',
|
||||
AgentName: '防护工程专家',
|
||||
Description: '确认最终迭代计划',
|
||||
ImportantInput: ['ActionResult:action-1-4-branch4', 'ActionResult:action-1-3-branch4'],
|
||||
},
|
||||
],
|
||||
}
|
||||
}
|
||||
|
||||
console.log('[Mock API] fill_stepTask 返回数据:', filledTask)
|
||||
return filledTask
|
||||
}
|
||||
// ==================== 第二个任务组 ====================
|
||||
else if (stepName === '系统设计与架构' || stepName === '原型测试与优化' || stepName === '质量保障设计' || stepName === '快速开发与验证') {
|
||||
let filledTask: IRawStepTask
|
||||
|
||||
if (selectedBranchIndex === 0) {
|
||||
// 第一个分支方案:系统设计与架构
|
||||
filledTask = {
|
||||
Id: params.stepTask.Id || 'task-2',
|
||||
StepName: '系统设计与架构',
|
||||
TaskContent: '设计系统架构和数据库结构',
|
||||
InputObject_List: ['项目开发计划书'],
|
||||
OutputObject: '系统设计文档',
|
||||
AgentSelection: ['腐蚀机理研究员', '防护工程专家'],
|
||||
Collaboration_Brief_frontEnd: {
|
||||
template: '!腐蚀机理研究员!负责!系统设计与架构-0!,!防护工程专家!负责!系统设计与架构-1!',
|
||||
data: {
|
||||
腐蚀机理研究员: {
|
||||
text: '腐蚀机理研究员',
|
||||
style: { background: 'hsl(210, 70%, 50%)' },
|
||||
},
|
||||
防护工程专家: {
|
||||
text: '防护工程专家',
|
||||
style: { background: 'hsl(30, 70%, 50%)' },
|
||||
},
|
||||
},
|
||||
},
|
||||
TaskProcess: [
|
||||
{
|
||||
ID: 'action-2-1',
|
||||
ActionType: 'Propose',
|
||||
AgentName: '腐蚀机理研究员',
|
||||
Description: '分析系统功能需求,提出整体架构方案',
|
||||
ImportantInput: ['InputObject:项目开发计划书'],
|
||||
},
|
||||
{
|
||||
ID: 'action-2-2',
|
||||
ActionType: 'Propose',
|
||||
AgentName: '防护工程专家',
|
||||
Description: '设计防护系统架构和数据库模型',
|
||||
ImportantInput: ['ActionResult:action-2-1'],
|
||||
},
|
||||
{
|
||||
ID: 'action-2-3',
|
||||
ActionType: 'Critique',
|
||||
AgentName: '腐蚀机理研究员',
|
||||
Description: '评审架构设计的合理性和可扩展性',
|
||||
ImportantInput: ['ActionResult:action-2-2', 'InputObject:项目开发计划书'],
|
||||
},
|
||||
{
|
||||
ID: 'action-2-4',
|
||||
ActionType: 'Improve',
|
||||
AgentName: '防护工程专家',
|
||||
Description: '优化架构设计,补充性能和安全方案',
|
||||
ImportantInput: ['ActionResult:action-2-3', 'ActionResult:action-2-2'],
|
||||
},
|
||||
{
|
||||
ID: 'action-2-5',
|
||||
ActionType: 'Finalize',
|
||||
AgentName: '腐蚀机理研究员',
|
||||
Description: '确认最终系统架构设计文档',
|
||||
ImportantInput: ['ActionResult:action-2-4', 'ActionResult:action-2-3'],
|
||||
},
|
||||
],
|
||||
}
|
||||
} else if (selectedBranchIndex === 1) {
|
||||
// 第二个分支方案:原型测试与优化
|
||||
filledTask = {
|
||||
Id: params.stepTask.Id || 'task-2',
|
||||
StepName: '原型测试与优化',
|
||||
TaskContent: '测试原型并根据反馈快速迭代',
|
||||
InputObject_List: ['原型系统'],
|
||||
OutputObject: '优化后的原型',
|
||||
AgentSelection: ['腐蚀机理研究员', '实验材料学家'],
|
||||
Collaboration_Brief_frontEnd: {
|
||||
template: '!腐蚀机理研究员!负责!原型测试与优化-0!,!实验材料学家!负责!原型测试与优化-1!',
|
||||
data: {
|
||||
腐蚀机理研究员: {
|
||||
text: '腐蚀机理研究员',
|
||||
style: { background: 'hsl(210, 70%, 50%)' },
|
||||
},
|
||||
实验材料学家: {
|
||||
text: '实验材料学家',
|
||||
style: { background: 'hsl(120, 70%, 50%)' },
|
||||
},
|
||||
},
|
||||
},
|
||||
TaskProcess: [
|
||||
{
|
||||
ID: 'action-2-1-branch2',
|
||||
ActionType: 'Propose',
|
||||
AgentName: '腐蚀机理研究员',
|
||||
Description: '制定原型测试计划和验证标准',
|
||||
ImportantInput: ['InputObject:原型系统'],
|
||||
},
|
||||
{
|
||||
ID: 'action-2-2-branch2',
|
||||
ActionType: 'Propose',
|
||||
AgentName: '实验材料学家',
|
||||
Description: '执行原型测试并收集性能数据',
|
||||
ImportantInput: ['ActionResult:action-2-1-branch2'],
|
||||
},
|
||||
{
|
||||
ID: 'action-2-3-branch2',
|
||||
ActionType: 'Critique',
|
||||
AgentName: '腐蚀机理研究员',
|
||||
Description: '分析测试结果和用户反馈',
|
||||
ImportantInput: ['ActionResult:action-2-2-branch2'],
|
||||
},
|
||||
{
|
||||
ID: 'action-2-4-branch2',
|
||||
ActionType: 'Improve',
|
||||
AgentName: '实验材料学家',
|
||||
Description: '根据测试结果优化原型功能和性能',
|
||||
ImportantInput: ['ActionResult:action-2-3-branch2'],
|
||||
},
|
||||
{
|
||||
ID: 'action-2-5-branch2',
|
||||
ActionType: 'Finalize',
|
||||
AgentName: '腐蚀机理研究员',
|
||||
Description: '确认优化后的原型版本',
|
||||
ImportantInput: ['ActionResult:action-2-4-branch2', 'ActionResult:action-2-3-branch2'],
|
||||
},
|
||||
],
|
||||
}
|
||||
} else if (selectedBranchIndex === 2) {
|
||||
// 第三个分支方案:质量保障设计
|
||||
filledTask = {
|
||||
Id: params.stepTask.Id || 'task-2',
|
||||
StepName: '质量保障设计',
|
||||
TaskContent: '设计质量保障体系和测试方案',
|
||||
InputObject_List: ['详细需求分析报告'],
|
||||
OutputObject: '质量保障方案',
|
||||
AgentSelection: ['实验材料学家', '防护工程专家'],
|
||||
Collaboration_Brief_frontEnd: {
|
||||
template: '!实验材料学家!负责!质量保障设计-0!,!防护工程专家!负责!质量保障设计-1!',
|
||||
data: {
|
||||
实验材料学家: {
|
||||
text: '实验材料学家',
|
||||
style: { background: 'hsl(120, 70%, 50%)' },
|
||||
},
|
||||
防护工程专家: {
|
||||
text: '防护工程专家',
|
||||
style: { background: 'hsl(30, 70%, 50%)' },
|
||||
},
|
||||
},
|
||||
},
|
||||
TaskProcess: [
|
||||
{
|
||||
ID: 'action-2-1-branch3',
|
||||
ActionType: 'Propose',
|
||||
AgentName: '实验材料学家',
|
||||
Description: '制定质量标准和测试指标体系',
|
||||
ImportantInput: ['InputObject:详细需求分析报告'],
|
||||
},
|
||||
{
|
||||
ID: 'action-2-2-branch3',
|
||||
ActionType: 'Propose',
|
||||
AgentName: '防护工程专家',
|
||||
Description: '设计质量保障流程和验证方案',
|
||||
ImportantInput: ['ActionResult:action-2-1-branch3'],
|
||||
},
|
||||
{
|
||||
ID: 'action-2-3-branch3',
|
||||
ActionType: 'Critique',
|
||||
AgentName: '实验材料学家',
|
||||
Description: '评审质量保障方案的完整性',
|
||||
ImportantInput: ['ActionResult:action-2-2-branch3'],
|
||||
},
|
||||
{
|
||||
ID: 'action-2-4-branch3',
|
||||
ActionType: 'Improve',
|
||||
AgentName: '防护工程专家',
|
||||
Description: '完善质量保障方案,补充风险控制',
|
||||
ImportantInput: ['ActionResult:action-2-3-branch3'],
|
||||
},
|
||||
{
|
||||
ID: 'action-2-5-branch3',
|
||||
ActionType: 'Finalize',
|
||||
AgentName: '实验材料学家',
|
||||
Description: '确认最终质量保障方案',
|
||||
ImportantInput: ['ActionResult:action-2-4-branch3', 'ActionResult:action-2-3-branch3'],
|
||||
},
|
||||
],
|
||||
}
|
||||
} else {
|
||||
// 第四个分支方案:快速开发与验证
|
||||
filledTask = {
|
||||
Id: params.stepTask.Id || 'task-2',
|
||||
StepName: '快速开发与验证',
|
||||
TaskContent: '快速开发并持续验证功能',
|
||||
InputObject_List: ['迭代计划'],
|
||||
OutputObject: '可运行的功能模块',
|
||||
AgentSelection: ['实验材料学家', '防护工程专家'],
|
||||
Collaboration_Brief_frontEnd: {
|
||||
template: '!实验材料学家!负责!快速开发与验证-0!,!防护工程专家!负责!快速开发与验证-1!',
|
||||
data: {
|
||||
实验材料学家: {
|
||||
text: '实验材料学家',
|
||||
style: { background: 'hsl(120, 70%, 50%)' },
|
||||
},
|
||||
防护工程专家: {
|
||||
text: '防护工程专家',
|
||||
style: { background: 'hsl(30, 70%, 50%)' },
|
||||
},
|
||||
},
|
||||
},
|
||||
TaskProcess: [
|
||||
{
|
||||
ID: 'action-2-1-branch4',
|
||||
ActionType: 'Propose',
|
||||
AgentName: '实验材料学家',
|
||||
Description: '根据迭代计划快速开发功能模块',
|
||||
ImportantInput: ['InputObject:迭代计划'],
|
||||
},
|
||||
{
|
||||
ID: 'action-2-2-branch4',
|
||||
ActionType: 'Propose',
|
||||
AgentName: '防护工程专家',
|
||||
Description: '实现功能防护机制和验证逻辑',
|
||||
ImportantInput: ['ActionResult:action-2-1-branch4'],
|
||||
},
|
||||
{
|
||||
ID: 'action-2-3-branch4',
|
||||
ActionType: 'Critique',
|
||||
AgentName: '实验材料学家',
|
||||
Description: '验证功能模块的正确性和性能',
|
||||
ImportantInput: ['ActionResult:action-2-2-branch4'],
|
||||
},
|
||||
{
|
||||
ID: 'action-2-4-branch4',
|
||||
ActionType: 'Improve',
|
||||
AgentName: '防护工程专家',
|
||||
Description: '根据验证结果优化功能实现',
|
||||
ImportantInput: ['ActionResult:action-2-3-branch4'],
|
||||
},
|
||||
{
|
||||
ID: 'action-2-5-branch4',
|
||||
ActionType: 'Finalize',
|
||||
AgentName: '实验材料学家',
|
||||
Description: '确认可运行的功能模块',
|
||||
ImportantInput: ['ActionResult:action-2-4-branch4', 'ActionResult:action-2-3-branch4'],
|
||||
},
|
||||
],
|
||||
}
|
||||
}
|
||||
|
||||
console.log('[Mock API] fill_stepTask 返回数据:', filledTask)
|
||||
return filledTask
|
||||
}
|
||||
// ==================== 第三个任务组(仅第三个分支方案) ====================
|
||||
else if (stepName === '系统开发与测试') {
|
||||
const filledTask: IRawStepTask = {
|
||||
Id: params.stepTask.Id || 'task-3',
|
||||
StepName: '系统开发与测试',
|
||||
TaskContent: '按质量标准进行系统开发和测试',
|
||||
InputObject_List: ['质量保障方案'],
|
||||
OutputObject: '经过完整测试的系统',
|
||||
AgentSelection: ['腐蚀机理研究员', '实验材料学家'],
|
||||
Collaboration_Brief_frontEnd: {
|
||||
template: '!腐蚀机理研究员!负责!系统开发与测试-0!,!实验材料学家!负责!系统开发与测试-1!',
|
||||
data: {
|
||||
腐蚀机理研究员: {
|
||||
text: '腐蚀机理研究员',
|
||||
style: { background: 'hsl(210, 70%, 50%)' },
|
||||
},
|
||||
实验材料学家: {
|
||||
text: '实验材料学家',
|
||||
style: { background: 'hsl(120, 70%, 50%)' },
|
||||
},
|
||||
},
|
||||
},
|
||||
TaskProcess: [
|
||||
{
|
||||
ID: 'action-3-1',
|
||||
ActionType: 'Propose',
|
||||
AgentName: '腐蚀机理研究员',
|
||||
Description: '根据质量标准制定系统开发计划',
|
||||
ImportantInput: ['InputObject:质量保障方案'],
|
||||
},
|
||||
{
|
||||
ID: 'action-3-2',
|
||||
ActionType: 'Propose',
|
||||
AgentName: '实验材料学家',
|
||||
Description: '按标准实施系统开发和单元测试',
|
||||
ImportantInput: ['ActionResult:action-3-1'],
|
||||
},
|
||||
{
|
||||
ID: 'action-3-3',
|
||||
ActionType: 'Critique',
|
||||
AgentName: '腐蚀机理研究员',
|
||||
Description: '评审系统开发质量和测试覆盖率',
|
||||
ImportantInput: ['ActionResult:action-3-2'],
|
||||
},
|
||||
{
|
||||
ID: 'action-3-4',
|
||||
ActionType: 'Improve',
|
||||
AgentName: '实验材料学家',
|
||||
Description: '根据评审结果完善系统功能和测试',
|
||||
ImportantInput: ['ActionResult:action-3-3'],
|
||||
},
|
||||
{
|
||||
ID: 'action-3-5',
|
||||
ActionType: 'Finalize',
|
||||
AgentName: '腐蚀机理研究员',
|
||||
Description: '确认经过完整测试的系统版本',
|
||||
ImportantInput: ['ActionResult:action-3-4', 'ActionResult:action-3-3'],
|
||||
},
|
||||
],
|
||||
}
|
||||
|
||||
console.log('[Mock API] fill_stepTask 返回数据:', filledTask)
|
||||
return filledTask
|
||||
}
|
||||
// ==================== 其他任务 ====================
|
||||
else {
|
||||
// 其他任务,返回空 TaskProcess
|
||||
console.warn('[Mock API] 未知的 StepName:', stepName)
|
||||
return {
|
||||
...params.stepTask,
|
||||
AgentSelection: [],
|
||||
TaskProcess: [],
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default mockFillStepTaskAPI
|
||||
@@ -1,11 +1,16 @@
|
||||
<script setup lang="ts">
|
||||
import { ref, computed, watch, nextTick } from 'vue'
|
||||
import { useAgentsStore, useSelectionStore } from '@/stores'
|
||||
import { convertToTaskProcess } from '@/stores/modules/agents'
|
||||
import { getAgentMapIcon } from '@/layout/components/config'
|
||||
import SvgIcon from '@/components/SvgIcon/index.vue'
|
||||
import api from '@/api'
|
||||
const agentsStore = useAgentsStore()
|
||||
const selectionStore = useSelectionStore()
|
||||
const agentsHeatmapData = ref<AgentHeatmapData[]>([])
|
||||
const selectedAgents = ref<Set<string>>(new Set())
|
||||
const selectedAssignmentGroup = ref<string[]>([])
|
||||
const selectedDimensions = ref<Set<string>>(new Set())
|
||||
|
||||
// 获取当前选中的任务
|
||||
const currentTask = computed(() => {
|
||||
@@ -32,19 +37,7 @@ interface AgentHeatmapData {
|
||||
scoreDetails?: Array<{ dimension: string; score: number; reason: string }> // 详细信息
|
||||
}
|
||||
|
||||
// 所有agent的热力图数据
|
||||
const agentsHeatmapData = ref<AgentHeatmapData[]>([])
|
||||
|
||||
// 选中的agent集合(用于Comparison部分)
|
||||
const selectedAgents = ref<Set<string>>(new Set())
|
||||
|
||||
// 当前选中的Assignment组合的agents(用于Assignment卡片的边框显示)
|
||||
const selectedAssignmentGroup = ref<string[]>([])
|
||||
|
||||
// 选中的维度集合
|
||||
const selectedDimensions = ref<Set<string>>(new Set())
|
||||
|
||||
// 确认的agent组合列表 - 从 store 读取(按当前任务ID)
|
||||
// 确认的agent组合列表 - 从 store 读取
|
||||
const confirmedAgentGroups = computed(() => {
|
||||
const taskId = currentTask.value?.Id
|
||||
if (!taskId) return []
|
||||
@@ -59,33 +52,44 @@ const isInitializing = ref(false)
|
||||
const isAddingDimension = ref(false)
|
||||
const isLoadingConfirm = ref(false) // 确认 agent 组合时的加载状态
|
||||
const isLoadingSelectGroup = ref(false) // 选择已保存组合时的加载状态
|
||||
const isLoadingInitialTask = ref(false) // 首次加载任务时的加载状态
|
||||
|
||||
// 热力图排序:选中的 agent 排在前面,其他按平均分降序
|
||||
const sortHeatmapBySelection = () => {
|
||||
if (agentsHeatmapData.value.length === 0) return
|
||||
agentsHeatmapData.value.sort((a, b) => {
|
||||
const aIsSelected = selectedAgents.value.has(a.agentName)
|
||||
const bIsSelected = selectedAgents.value.has(b.agentName)
|
||||
|
||||
// 如果一个是选中的 agent,另一个不是,选中的 agent 排前面
|
||||
if (aIsSelected && !bIsSelected) return -1
|
||||
if (!aIsSelected && bIsSelected) return 1
|
||||
|
||||
// 如果都是或都不是选中的 agent,按选中的维度平均分降序排列
|
||||
const selectedDimArray = Array.from(selectedDimensions.value)
|
||||
const aAverage = calculateAgentAverage(a, selectedDimArray)
|
||||
const bAverage = calculateAgentAverage(b, selectedDimArray)
|
||||
return bAverage - aAverage
|
||||
})
|
||||
}
|
||||
// 处理搜索提交
|
||||
const handleSubmit = async () => {
|
||||
if (!searchValue.value.trim() || isAddingDimension.value) return
|
||||
|
||||
const newDimension = searchValue.value.trim()
|
||||
|
||||
// 检查维度是否已存在
|
||||
if (scoreDimensions.value.includes(newDimension)) {
|
||||
searchValue.value = ''
|
||||
return
|
||||
}
|
||||
|
||||
try {
|
||||
isAddingDimension.value = true
|
||||
|
||||
// 使用 Mock API 进行测试(开发阶段)
|
||||
// 切换到真实API时,只需将 mockAgentSelectModifyAddAspect 改为 agentSelectModifyAddAspect
|
||||
const response = await api.agentSelectModifyAddAspect({
|
||||
aspectList: [...scoreDimensions.value, newDimension]
|
||||
})
|
||||
|
||||
// 1. 先添加新维度到本地维度列表(立即显示)
|
||||
scoreDimensions.value.push(response.aspectName)
|
||||
|
||||
// 2. 为每个agent添加新维度的评分(立即显示)
|
||||
// 为每个agent添加新维度的评分(立即显示)
|
||||
agentsHeatmapData.value.forEach(agentData => {
|
||||
const scoreInfo = response.agentScores[agentData.agentName]
|
||||
if (scoreInfo) {
|
||||
@@ -97,13 +101,11 @@ const handleSubmit = async () => {
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
// 3. 异步更新 store(等前端显示完成后再更新,避免触发重新初始化)
|
||||
//异步更新 store(等前端显示完成后再更新,避免触发重新初始化)
|
||||
await nextTick()
|
||||
|
||||
const taskId = currentTask.value?.Id
|
||||
|
||||
// 🆕 更新按任务ID的存储
|
||||
//更新按任务ID的存储
|
||||
if (taskId) {
|
||||
const existingTaskData = agentsStore.getTaskScoreData(taskId)
|
||||
if (existingTaskData) {
|
||||
@@ -120,7 +122,6 @@ const handleSubmit = async () => {
|
||||
}
|
||||
// 保存到 store
|
||||
agentsStore.setTaskScoreData(taskId, existingTaskData)
|
||||
console.log(`✅ 已更新任务 ${taskId} 的 store,新增维度: ${response.aspectName}`)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -136,7 +137,6 @@ const handleSubmit = async () => {
|
||||
}
|
||||
currentStoreData.agentScores[agentName][response.aspectName] = scoreInfo
|
||||
}
|
||||
console.log(`已更新全局 store(兼容旧版本)`)
|
||||
}
|
||||
|
||||
// 清空输入框
|
||||
@@ -148,7 +148,7 @@ const handleSubmit = async () => {
|
||||
}
|
||||
}
|
||||
|
||||
// 判断两个agent组合是否相同(不考虑顺序)
|
||||
// 判断两个agent组合是否相同
|
||||
const areAgentGroupsEqual = (group1: string[], group2: string[]): boolean => {
|
||||
if (group1.length !== group2.length) return false
|
||||
const sorted1 = [...group1].sort()
|
||||
@@ -160,7 +160,6 @@ const areAgentGroupsEqual = (group1: string[], group2: string[]): boolean => {
|
||||
const confirmAgentSelection = async () => {
|
||||
if (selectedAgents.value.size > 0 && currentTask.value?.Id) {
|
||||
const agentArray = Array.from(selectedAgents.value)
|
||||
|
||||
// 检查该agent组合是否已存在(统一检查,包括初始组合和用户添加的组合)
|
||||
const existingGroups = agentsStore.getConfirmedAgentGroups(currentTask.value.Id)
|
||||
const existingDuplicateIndex = existingGroups.findIndex(group =>
|
||||
@@ -168,64 +167,30 @@ const confirmAgentSelection = async () => {
|
||||
)
|
||||
|
||||
if (existingDuplicateIndex !== -1) {
|
||||
// 找到重复的组合,只选中该卡片,不添加重复
|
||||
console.log('该agent组合已存在,只选中卡片,不添加重复')
|
||||
const existingGroup = existingGroups[existingDuplicateIndex]!
|
||||
selectedAssignmentGroup.value = [...existingGroup]
|
||||
selectedAgents.value = new Set(existingGroup)
|
||||
return
|
||||
}
|
||||
|
||||
// 该组合不存在,添加新组合
|
||||
// 使用 store 的方法添加,按任务ID存储(不持久化到localStorage)
|
||||
agentsStore.addConfirmedAgentGroup(currentTask.value.Id, agentArray)
|
||||
console.log(`已确认 agent 组合 [任务 ${currentTask.value.Id}]:`, agentArray)
|
||||
|
||||
// 调用 Mock API 填充任务流程
|
||||
try {
|
||||
isLoadingConfirm.value = true
|
||||
console.log('=== 开始调用 fillStepTaskTaskProcess Mock API ===')
|
||||
console.log('1. 当前任务数据 (IRawStepTask 格式):', currentTask.value)
|
||||
console.log('2. 选中的 agents:', agentArray)
|
||||
const stepTaskForApi = agentsStore.createStepTaskForApi(agentArray)
|
||||
const goal = agentsStore.agentRawPlan.data?.['General Goal'] || '开发智能协作系统'
|
||||
|
||||
// 将 IRawStepTask 转换为 IApiStepTask 格式
|
||||
const stepTaskForApi = {
|
||||
name: currentTask.value.StepName || '',
|
||||
content: currentTask.value.TaskContent || '',
|
||||
inputs: currentTask.value.InputObject_List || [],
|
||||
output: currentTask.value.OutputObject || '',
|
||||
agents: agentArray,
|
||||
brief: currentTask.value.Collaboration_Brief_frontEnd || {
|
||||
template: '',
|
||||
data: {}
|
||||
},
|
||||
process: []
|
||||
if (agentsStore.isStopping || agentsStore.hasStoppedFilling) {
|
||||
isLoadingConfirm.value = false
|
||||
return
|
||||
}
|
||||
|
||||
console.log('3. 转换后的 API 请求参数 (IApiStepTask 格式):', stepTaskForApi)
|
||||
|
||||
// 获取 goal(从 agentRawPlan 中获取)
|
||||
const goal = agentsStore.agentRawPlan.data?.['General Goal'] || '开发智能协作系统'
|
||||
console.log('4. General Goal:', goal)
|
||||
|
||||
// 调用 Mock API
|
||||
const filledTask = await api.fillStepTaskTaskProcess({
|
||||
goal,
|
||||
stepTask: stepTaskForApi,
|
||||
agents: agentArray
|
||||
})
|
||||
|
||||
console.log('=== Mock API 返回成功 ===')
|
||||
console.log('5. 返回的完整数据 (IApiStepTask 格式):', filledTask)
|
||||
console.log('6. TaskProcess 流程数量:', filledTask.process?.length || 0)
|
||||
console.log('7. TaskProcess 详情:', filledTask.process)
|
||||
console.log('8. Collaboration Brief:', filledTask.brief)
|
||||
|
||||
// 存储到 selectionStore
|
||||
selectionStore.setAgentTaskProcess(currentTask.value.Id, agentArray, filledTask)
|
||||
console.log('✅ Mock API 调用成功,数据已存储到 selectionStore')
|
||||
} catch (error) {
|
||||
console.error('❌ Mock API 调用失败:', error)
|
||||
console.error('❌ API 调用失败:', error)
|
||||
} finally {
|
||||
isLoadingConfirm.value = false
|
||||
}
|
||||
@@ -234,84 +199,35 @@ const confirmAgentSelection = async () => {
|
||||
|
||||
// 点击 Assignment 部分的 agent 组合卡片,更新 Comparison 部分的选中状态
|
||||
const selectAgentGroup = async (agentNames: string[]) => {
|
||||
// 更新Assignment边框状态
|
||||
selectedAssignmentGroup.value = [...agentNames]
|
||||
|
||||
// 更新Comparison部分的选中状态
|
||||
selectedAgents.value = new Set(agentNames)
|
||||
// 触发响应式更新
|
||||
selectedAgents.value = new Set(selectedAgents.value)
|
||||
|
||||
// 保存到 store(持久化选择状态)
|
||||
// 保存到 store
|
||||
if (currentTask.value?.Id) {
|
||||
agentsStore.setSelectedAgentGroup(currentTask.value.Id, agentNames)
|
||||
}
|
||||
|
||||
// 🆕 联动更新:更新 currentTask 的 AgentSelection 和 TaskProcess
|
||||
// 更新 currentTask 的 AgentSelection 和 TaskProcess
|
||||
if (currentTask.value?.Id && agentNames.length > 0) {
|
||||
console.log('🔄 开始联动更新 currentTask 的 agent 组合:', agentNames)
|
||||
|
||||
// 从 selectionStore 获取该 agent 组合对应的 TaskProcess 数据
|
||||
let taskProcessData = selectionStore.getAgentTaskProcess(currentTask.value.Id, agentNames)
|
||||
|
||||
// 🆕 如果 selectionStore 中没有数据,自动调用 API 加载
|
||||
if (!taskProcessData) {
|
||||
console.log('⚠️ selectionStore 中没有该组合的 TaskProcess 数据,开始加载...')
|
||||
console.log('📋 当前任务信息:', {
|
||||
taskId: currentTask.value.Id,
|
||||
taskName: currentTask.value.StepName,
|
||||
agents: agentNames
|
||||
})
|
||||
|
||||
try {
|
||||
isLoadingSelectGroup.value = true
|
||||
// 将 IRawStepTask 转换为 IApiStepTask 格式
|
||||
const stepTaskForApi = {
|
||||
name: currentTask.value.StepName || '',
|
||||
content: currentTask.value.TaskContent || '',
|
||||
inputs: currentTask.value.InputObject_List || [],
|
||||
output: currentTask.value.OutputObject || '',
|
||||
agents: agentNames,
|
||||
brief: currentTask.value.Collaboration_Brief_frontEnd || {
|
||||
template: '',
|
||||
data: {}
|
||||
},
|
||||
process: []
|
||||
}
|
||||
|
||||
const stepTaskForApi = agentsStore.createStepTaskForApi(agentNames)
|
||||
const goal = agentsStore.agentRawPlan.data?.['General Goal'] || '开发智能协作系统'
|
||||
|
||||
console.log('📤 准备调用 API:', {
|
||||
goal,
|
||||
stepTask: stepTaskForApi,
|
||||
agents: agentNames
|
||||
})
|
||||
if (agentsStore.isStopping || agentsStore.hasStoppedFilling) {
|
||||
return
|
||||
}
|
||||
|
||||
// 调用 Mock API 加载数据
|
||||
const filledTask = await api.fillStepTaskTaskProcess({
|
||||
goal,
|
||||
stepTask: stepTaskForApi,
|
||||
agents: agentNames
|
||||
})
|
||||
|
||||
console.log('🔍 API 返回的完整响应:', filledTask)
|
||||
console.log('🔍 process 数据:', filledTask.process)
|
||||
console.log('🔍 process 长度:', filledTask.process?.length)
|
||||
console.log('✅ TaskProcess 数据加载成功:', {
|
||||
agents: agentNames,
|
||||
processCount: filledTask.process?.length || 0,
|
||||
processDetail: filledTask.process,
|
||||
briefDetail: filledTask.brief
|
||||
})
|
||||
|
||||
// 存储到 selectionStore
|
||||
selectionStore.setAgentTaskProcess(currentTask.value.Id, agentNames, filledTask)
|
||||
|
||||
console.log('💾 数据已存储到 selectionStore,验证存储是否成功...')
|
||||
// 验证存储
|
||||
const storedData = selectionStore.getAgentTaskProcess(currentTask.value.Id, agentNames)
|
||||
console.log('📦 验证存储结果:', storedData ? '成功' : '失败', storedData)
|
||||
|
||||
taskProcessData = filledTask
|
||||
} catch (error) {
|
||||
console.error('❌ 加载 TaskProcess 数据失败:', error)
|
||||
@@ -322,62 +238,17 @@ const selectAgentGroup = async (agentNames: string[]) => {
|
||||
}
|
||||
|
||||
if (taskProcessData) {
|
||||
console.log('✅ 找到 TaskProcess 数据,开始更新 currentTask:', {
|
||||
taskId: currentTask.value.Id,
|
||||
agents: agentNames,
|
||||
processCount: taskProcessData.process?.length || 0,
|
||||
processDetail: taskProcessData.process,
|
||||
briefDetail: taskProcessData.brief
|
||||
})
|
||||
|
||||
// 🆕 数据格式转换:IApiAgentAction[] → TaskProcess[]
|
||||
const convertedTaskProcess = (taskProcessData.process || []).map(action => ({
|
||||
ID: action.id,
|
||||
ActionType: action.type,
|
||||
AgentName: action.agent,
|
||||
Description: action.description,
|
||||
ImportantInput: action.inputs || []
|
||||
}))
|
||||
|
||||
console.log('🔄 数据格式转换完成:', {
|
||||
original: taskProcessData.process,
|
||||
converted: convertedTaskProcess
|
||||
})
|
||||
|
||||
// 更新 currentTask 的 AgentSelection 和 TaskProcess
|
||||
// 使用 updateCurrentAgentSelection 强制更新,避免被 setCurrentTask 的"智能保留"逻辑阻止
|
||||
const convertedTaskProcess = convertToTaskProcess(taskProcessData.process || [])
|
||||
agentsStore.updateCurrentAgentSelection(
|
||||
[...agentNames],
|
||||
convertedTaskProcess,
|
||||
taskProcessData.brief || currentTask.value.Collaboration_Brief_frontEnd
|
||||
)
|
||||
|
||||
console.log('✅ currentTask 已更新,新的 AgentSelection:', agentNames)
|
||||
console.log('📋 验证更新结果:', agentsStore.currentTask)
|
||||
}
|
||||
|
||||
sortHeatmapBySelection()
|
||||
}
|
||||
|
||||
// 重新排序:被选中的agent排在前面,其他agent按平均分降序
|
||||
if (agentsHeatmapData.value.length > 0) {
|
||||
agentsHeatmapData.value.sort((a, b) => {
|
||||
const aIsSelected = selectedAgents.value.has(a.agentName)
|
||||
const bIsSelected = selectedAgents.value.has(b.agentName)
|
||||
|
||||
// 如果一个是选中的agent,另一个不是,选中的agent排前面
|
||||
if (aIsSelected && !bIsSelected) return -1
|
||||
if (!aIsSelected && bIsSelected) return 1
|
||||
|
||||
// 如果都是或都不是选中的agent,按选中的维度平均分降序排列
|
||||
const selectedDimArray = Array.from(selectedDimensions.value)
|
||||
const aAverage = calculateAgentAverage(a, selectedDimArray)
|
||||
const bAverage = calculateAgentAverage(b, selectedDimArray)
|
||||
return bAverage - aAverage
|
||||
})
|
||||
}
|
||||
|
||||
console.log('选中 agent 组合:', Array.from(selectedAgents.value))
|
||||
}
|
||||
|
||||
// 切换维度选中状态
|
||||
const toggleDimensionSelection = (dimension: string) => {
|
||||
if (selectedDimensions.value.has(dimension)) {
|
||||
@@ -385,26 +256,8 @@ const toggleDimensionSelection = (dimension: string) => {
|
||||
} else {
|
||||
selectedDimensions.value.add(dimension)
|
||||
}
|
||||
// 触发响应式更新
|
||||
selectedDimensions.value = new Set(selectedDimensions.value)
|
||||
|
||||
// 重新排序agent
|
||||
if (agentsHeatmapData.value.length > 0) {
|
||||
agentsHeatmapData.value.sort((a, b) => {
|
||||
const aIsSelected = selectedAgents.value.has(a.agentName)
|
||||
const bIsSelected = selectedAgents.value.has(b.agentName)
|
||||
|
||||
// 如果一个是选中的agent,另一个不是,选中的agent排前面
|
||||
if (aIsSelected && !bIsSelected) return -1
|
||||
if (!aIsSelected && bIsSelected) return 1
|
||||
|
||||
// 如果都是或都不是选中的agent,按选中的维度平均分降序排列
|
||||
const selectedDimArray = Array.from(selectedDimensions.value)
|
||||
const aAverage = calculateAgentAverage(a, selectedDimArray)
|
||||
const bAverage = calculateAgentAverage(b, selectedDimArray)
|
||||
return bAverage - aAverage
|
||||
})
|
||||
}
|
||||
sortHeatmapBySelection()
|
||||
}
|
||||
|
||||
// 判断维度是否被选中
|
||||
@@ -419,31 +272,8 @@ const toggleAgentSelection = (agentName: string) => {
|
||||
} else {
|
||||
selectedAgents.value.add(agentName)
|
||||
}
|
||||
// 触发响应式更新
|
||||
selectedAgents.value = new Set(selectedAgents.value)
|
||||
|
||||
// 重新排序:被选中的agent排在前面,其他agent按平均分降序
|
||||
if (agentsHeatmapData.value.length > 0) {
|
||||
agentsHeatmapData.value.sort((a, b) => {
|
||||
const aIsSelected = selectedAgents.value.has(a.agentName)
|
||||
const bIsSelected = selectedAgents.value.has(b.agentName)
|
||||
|
||||
// 如果一个是选中的agent,另一个不是,选中的agent排前面
|
||||
if (aIsSelected && !bIsSelected) return -1
|
||||
if (!aIsSelected && bIsSelected) return 1
|
||||
|
||||
// 如果都是或都不是选中的agent,按选中的维度平均分降序排列
|
||||
const selectedDimArray = Array.from(selectedDimensions.value)
|
||||
const aAverage = calculateAgentAverage(a, selectedDimArray)
|
||||
const bAverage = calculateAgentAverage(b, selectedDimArray)
|
||||
return bAverage - aAverage
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// 判断agent是否被选中
|
||||
const isAgentSelected = (agentName: string) => {
|
||||
return selectedAgents.value.has(agentName)
|
||||
sortHeatmapBySelection()
|
||||
}
|
||||
|
||||
// 判断agent组合是否被选中(用于Assignment卡片的选中效果)
|
||||
@@ -513,24 +343,21 @@ const calculateAgentAverage = (agentData: AgentHeatmapData, selectedDimensions?:
|
||||
return count > 0 ? total / count : 0
|
||||
}
|
||||
|
||||
// 模拟API调用 - 获取智能体评分数据
|
||||
// API调用 - 获取智能体评分数据
|
||||
const fetchAgentScores = async () => {
|
||||
const taskId = currentTask.value?.Id
|
||||
if (!taskId) {
|
||||
console.warn('⚠️ fetchAgentScores: 当前任务没有 Id')
|
||||
return null
|
||||
}
|
||||
|
||||
// 🆕 先检查 store 中是否有该任务的评分数据
|
||||
//先检查 store 中是否有该任务的评分数据
|
||||
const storedData = agentsStore.getTaskScoreData(taskId)
|
||||
if (storedData && storedData.aspectList && storedData.aspectList.length > 0) {
|
||||
console.log('✅ 使用任务缓存的评分数据')
|
||||
return storedData
|
||||
}
|
||||
|
||||
// 🆕 如果正在预加载中,等待预加载完成
|
||||
// 如果正在预加载中,等待预加载完成
|
||||
if (agentsStore.isTaskPreloading(taskId)) {
|
||||
console.log('⏳ 任务正在预加载中,等待完成...')
|
||||
// 等待一小段时间,让预加载完成
|
||||
await new Promise(resolve => setTimeout(resolve, 1000))
|
||||
// 再次尝试获取
|
||||
@@ -540,12 +367,23 @@ const fetchAgentScores = async () => {
|
||||
}
|
||||
}
|
||||
|
||||
// 检查是否已停止
|
||||
if (agentsStore.isStopping || agentsStore.hasStoppedFilling) {
|
||||
console.log('检测到停止信号,跳过获取智能体评分')
|
||||
return null
|
||||
}
|
||||
|
||||
// 调用 API 获取评分(如果没有缓存或预加载)
|
||||
const agentScores = await api.agentSelectModifyInit({
|
||||
goal: agentsStore.agentRawPlan.data?.['General Goal'] || '',
|
||||
stepTask: currentTask.value
|
||||
})
|
||||
|
||||
// 再次检查是否已停止(API 调用后)
|
||||
if (agentsStore.isStopping || agentsStore.hasStoppedFilling) {
|
||||
return null
|
||||
}
|
||||
|
||||
// 从转换后的数据中提取维度列表(第一个agent的所有维度)
|
||||
const firstAgent = Object.keys(agentScores)[0]
|
||||
const aspectList = firstAgent ? Object.keys(agentScores[firstAgent] || {}) : []
|
||||
@@ -568,9 +406,7 @@ const fetchAgentScores = async () => {
|
||||
const initHeatmapData = async () => {
|
||||
const agents = allAgents.value
|
||||
if (agents.length === 0 || isInitializing.value || isAddingDimension.value) return
|
||||
|
||||
isInitializing.value = true
|
||||
|
||||
// 先创建一个只包含agent基本信息的临时数据,让agent头像先显示
|
||||
const tempData = agents.map(agent => ({
|
||||
agentName: agent.Name,
|
||||
@@ -582,16 +418,12 @@ const initHeatmapData = async () => {
|
||||
tempData.sort((a, b) => {
|
||||
const aIsAssigned = currentTaskAgents.value.includes(a.agentName)
|
||||
const bIsAssigned = currentTaskAgents.value.includes(b.agentName)
|
||||
|
||||
if (aIsAssigned && !bIsAssigned) return -1
|
||||
if (!aIsAssigned && bIsAssigned) return 1
|
||||
|
||||
return a.agentName.localeCompare(b.agentName, 'zh-CN')
|
||||
})
|
||||
|
||||
agentsHeatmapData.value = tempData
|
||||
|
||||
// 初始化选中状态:默认选中Assignment部分的agent(即currentTaskAgents)
|
||||
//默认选中Assignment部分的agent(即currentTaskAgents)
|
||||
if (currentTaskAgents.value.length > 0) {
|
||||
selectedAgents.value = new Set(currentTaskAgents.value)
|
||||
selectedAssignmentGroup.value = [...currentTaskAgents.value]
|
||||
@@ -612,10 +444,7 @@ const initHeatmapData = async () => {
|
||||
|
||||
const { aspectList, agentScores } = scoreData
|
||||
|
||||
// 设置评分维度
|
||||
scoreDimensions.value = aspectList
|
||||
|
||||
// 转换数据结构:为每个agent生成评分数据
|
||||
const heatmapData = agents.map(agent => {
|
||||
const agentName = agent.Name
|
||||
const scores: number[] = []
|
||||
@@ -623,7 +452,6 @@ const initHeatmapData = async () => {
|
||||
|
||||
// 按照维度顺序获取评分
|
||||
aspectList.forEach((dimension: string) => {
|
||||
// 数据结构:agentScores[agentName][dimension]
|
||||
const agentScoreData = agentScores[agentName]
|
||||
const scoreItem = agentScoreData ? agentScoreData[dimension] : null
|
||||
|
||||
@@ -635,7 +463,6 @@ const initHeatmapData = async () => {
|
||||
reason: scoreItem.reason
|
||||
})
|
||||
} else {
|
||||
// 如果没有评分数据,默认为1
|
||||
scores.push(1)
|
||||
}
|
||||
})
|
||||
@@ -646,9 +473,8 @@ const initHeatmapData = async () => {
|
||||
scoreDetails
|
||||
}
|
||||
})
|
||||
|
||||
// 排序逻辑:
|
||||
// 1. Assignment部分的agent(currentTaskAgents)排在前面
|
||||
// 1. Assignment部分的agent排在前面
|
||||
// 2. 其他agent按照平均分降序排列
|
||||
// 3. 同一组内也按平均分降序排列
|
||||
heatmapData.sort((a, b) => {
|
||||
@@ -670,28 +496,16 @@ const initHeatmapData = async () => {
|
||||
agentsHeatmapData.value = heatmapData
|
||||
} catch (error) {
|
||||
console.error('获取智能体评分数据失败:', error)
|
||||
// 降级处理:使用默认维度列表和随机数据
|
||||
const defaultAspects = ['能力', '可用性', '专业性', '效率', '准确性', '协作性']
|
||||
scoreDimensions.value = defaultAspects
|
||||
agentsHeatmapData.value = tempData.map(item => ({
|
||||
...item,
|
||||
scores: Array.from(
|
||||
{ length: defaultAspects.length },
|
||||
() => Math.floor(Math.random() * 5) + 1
|
||||
),
|
||||
scoreDetails: []
|
||||
}))
|
||||
} finally {
|
||||
isInitializing.value = false
|
||||
}
|
||||
}
|
||||
|
||||
// 🆕 监听agent列表变化或任务切换,初始化热力矩阵
|
||||
//监听agent列表变化或任务切换,初始化热力矩阵
|
||||
watch(
|
||||
[() => allAgents.value.length, () => currentTask.value?.Id],
|
||||
([agentLength, taskId]) => {
|
||||
if (agentLength > 0 && taskId) {
|
||||
console.log(`🔄 触发初始化热力图数据: agentCount=${agentLength}, taskId=${taskId}`)
|
||||
initHeatmapData()
|
||||
}
|
||||
},
|
||||
@@ -736,8 +550,6 @@ watch(
|
||||
)
|
||||
|
||||
// 初始化和任务切换处理
|
||||
// 1. 首次加载时,自动将初始 agent 组合添加到 confirmedAgentGroups(不调用 API,使用已有的 TaskProcess)
|
||||
// 2. 恢复之前的选择状态
|
||||
watch(
|
||||
() => currentTask.value,
|
||||
async newTask => {
|
||||
@@ -751,67 +563,35 @@ watch(
|
||||
|
||||
// 首次初始化:自动添加初始组合到 confirmedAgentGroups
|
||||
if (!hasInitialGroup) {
|
||||
console.log('🎯 首次初始化,自动添加初始 agent 组合到 Assignment')
|
||||
agentsStore.addConfirmedAgentGroup(newTask.Id, [...newTask.AgentSelection])
|
||||
|
||||
// 🆕 检查 newTask 是否已有 TaskProcess(步骤详情填充时已经包含)
|
||||
if (newTask.TaskProcess && newTask.TaskProcess.length > 0) {
|
||||
console.log('✅ 初始 agent 组合已有 TaskProcess 数据,直接使用')
|
||||
|
||||
// 直接存储已有的 TaskProcess 到 selectionStore(不调用 API)
|
||||
selectionStore.setAgentTaskProcess(newTask.Id, newTask.AgentSelection, {
|
||||
process: newTask.TaskProcess,
|
||||
brief: newTask.Collaboration_Brief_frontEnd || { template: '', data: {} }
|
||||
})
|
||||
console.log('✅ 初始 agent 组合的 TaskProcess 已从现有数据加载')
|
||||
} else {
|
||||
// 🆕 如果没有 TaskProcess,才调用 API 获取(兜底逻辑)
|
||||
console.log('⚠️ 初始 agent 组合没有 TaskProcess 数据,调用 API 获取...')
|
||||
try {
|
||||
isLoadingInitialTask.value = true
|
||||
|
||||
const stepTaskForApi = {
|
||||
name: newTask.StepName || '',
|
||||
content: newTask.TaskContent || '',
|
||||
inputs: newTask.InputObject_List || [],
|
||||
output: newTask.OutputObject || '',
|
||||
agents: newTask.AgentSelection,
|
||||
brief: newTask.Collaboration_Brief_frontEnd || {
|
||||
template: '',
|
||||
data: {}
|
||||
},
|
||||
process: []
|
||||
}
|
||||
|
||||
const goal = agentsStore.agentRawPlan.data?.['General Goal'] || '开发智能协作系统'
|
||||
|
||||
const filledTask = await api.fillStepTaskTaskProcess({
|
||||
goal,
|
||||
stepTask: stepTaskForApi,
|
||||
agents: newTask.AgentSelection
|
||||
})
|
||||
|
||||
console.log('✅ 初始 agent 组合 TaskProcess API 加载成功')
|
||||
selectionStore.setAgentTaskProcess(newTask.Id, newTask.AgentSelection, filledTask)
|
||||
} catch (error) {
|
||||
console.error('❌ 加载初始 agent 组合的 TaskProcess 失败:', error)
|
||||
} finally {
|
||||
isLoadingInitialTask.value = false
|
||||
}
|
||||
}
|
||||
// 初始化时 TaskProcess 一定存在,直接存储
|
||||
selectionStore.setAgentTaskProcess(newTask.Id, newTask.AgentSelection, {
|
||||
name: newTask.StepName || '',
|
||||
content: newTask.TaskContent || '',
|
||||
inputs: newTask.InputObject_List || [],
|
||||
output: newTask.OutputObject || '',
|
||||
agents: newTask.AgentSelection,
|
||||
brief: newTask.Collaboration_Brief_frontEnd || { template: '', data: {} },
|
||||
process: newTask.TaskProcess.map(action => ({
|
||||
id: action.ID,
|
||||
type: action.ActionType,
|
||||
agent: action.AgentName,
|
||||
description: action.Description,
|
||||
inputs: action.ImportantInput
|
||||
}))
|
||||
})
|
||||
}
|
||||
|
||||
// 从 store 中恢复之前选择的 agent 组合(如果有)
|
||||
// 从 store 中恢复之前选择的 agent 组合
|
||||
const savedAgentGroup = agentsStore.getSelectedAgentGroup(newTask.Id)
|
||||
if (savedAgentGroup) {
|
||||
console.log('📂 恢复之前选择的 agent 组合:', savedAgentGroup)
|
||||
selectedAssignmentGroup.value = [...savedAgentGroup]
|
||||
selectedAgents.value = new Set(savedAgentGroup)
|
||||
} else {
|
||||
// 没有保存的选择,默认选中第一个组合(即初始组合)
|
||||
// 没有保存的选择,默认选中初始组合
|
||||
const allGroups = agentsStore.getConfirmedAgentGroups(newTask.Id)
|
||||
if (allGroups.length > 0 && allGroups[0]) {
|
||||
console.log('🔄 默认选中第一个组合(初始组合):', allGroups[0])
|
||||
selectedAssignmentGroup.value = [...allGroups[0]]
|
||||
selectedAgents.value = new Set(allGroups[0])
|
||||
}
|
||||
@@ -819,7 +599,6 @@ watch(
|
||||
},
|
||||
{ immediate: true } // 立即执行一次
|
||||
)
|
||||
|
||||
// 获取热力图颜色(评分1-5,颜色从浅到深)
|
||||
const getHeatmapColor = (score: number) => {
|
||||
const colors = [
|
||||
@@ -886,7 +665,7 @@ const getHeatmapColor = (score: number) => {
|
||||
<div
|
||||
v-if="allAgents.length > 0"
|
||||
class="comparison-content"
|
||||
v-loading="isInitializing || isLoadingInitialTask || isLoadingConfirm"
|
||||
v-loading="isInitializing || isLoadingConfirm"
|
||||
>
|
||||
<!-- 热力矩阵图 -->
|
||||
<div v-if="agentsHeatmapData.length > 0" class="heatmap-container">
|
||||
|
||||
@@ -21,7 +21,7 @@ const agentGroupCount = computed(() => {
|
||||
// 获取该任务的已确认agent组合
|
||||
const confirmedGroups = agentsStore.getConfirmedAgentGroups(agentsStore.currentTask.Id)
|
||||
|
||||
// 当前任务agents(1) + 已确认的agent组合数量
|
||||
// 当前任务agents数量 + 已确认的agent组合数量
|
||||
return confirmedGroups.length || 1
|
||||
})
|
||||
|
||||
|
||||
@@ -10,22 +10,19 @@ const emit = defineEmits<{
|
||||
|
||||
// 获取分支数量
|
||||
const branchCount = computed(() => {
|
||||
// flowBranches 包含所有通过 Branch 创建的分支
|
||||
const extraBranches = selectionStore.flowBranches?.length || 1
|
||||
|
||||
return extraBranches
|
||||
})
|
||||
|
||||
const handleClick = () => {
|
||||
emit('click')
|
||||
// 触发打开分支窗口
|
||||
agentsStore.openPlanModification()
|
||||
}
|
||||
</script>
|
||||
<template>
|
||||
<div
|
||||
class="branch-button"
|
||||
:class="{ 'has-branches': branchCount > 0 }"
|
||||
:class="{ 'has-branches': branchCount > 1 }"
|
||||
@click="handleClick"
|
||||
:title="`${branchCount} 个分支`"
|
||||
>
|
||||
|
||||
@@ -39,7 +39,6 @@
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, computed, onMounted, onUnmounted, nextTick } from 'vue'
|
||||
import { debounce } from 'lodash-es'
|
||||
export interface IFloatingWindowProps {
|
||||
title?: string | any
|
||||
onClose?: () => void
|
||||
@@ -262,24 +261,20 @@ const handleResizeMove = (e: MouseEvent) => {
|
||||
emit('update:position', position.value)
|
||||
}
|
||||
|
||||
const handleResizeEnd = debounce(() => {
|
||||
const handleResizeEnd = () => {
|
||||
isResizing.value = false
|
||||
resizeDirection.value = null
|
||||
document.removeEventListener('mousemove', handleResizeMove)
|
||||
document.removeEventListener('mouseup', handleResizeEnd)
|
||||
|
||||
props.onResize?.()
|
||||
}, 50)
|
||||
}
|
||||
|
||||
const setResizeable = (value: boolean) => {
|
||||
resizeable.value = value
|
||||
}
|
||||
|
||||
const handleClose = () => {
|
||||
if (props.onClose) {
|
||||
props.onClose()
|
||||
}
|
||||
}
|
||||
const handleClose = () => props.onClose?.()
|
||||
|
||||
// 初始化
|
||||
onMounted(() => {
|
||||
|
||||
@@ -3,7 +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 { computed, ref, nextTick, watch, onMounted } from 'vue'
|
||||
import { computed, nextTick, watch, onMounted } from 'vue'
|
||||
import { AnchorLocations } from '@jsplumb/browser-ui'
|
||||
import { Loading } from '@element-plus/icons-vue'
|
||||
import MultiLineTooltip from '@/components/MultiLineTooltip/index.vue'
|
||||
@@ -11,6 +11,7 @@ import Bg from './Bg.vue'
|
||||
import BranchButton from './components/BranchButton.vue'
|
||||
import Notification from '@/components/Notification/Notification.vue'
|
||||
import { useNotification } from '@/composables/useNotification'
|
||||
import TaskContentEditor from '@/components/TaskContentEditor/index.vue'
|
||||
|
||||
// 判断计划是否就绪
|
||||
const planReady = computed(() => {
|
||||
@@ -24,8 +25,6 @@ const openPlanModification = () => {
|
||||
const emit = defineEmits<{
|
||||
(el: 'resetAgentRepoLine'): void
|
||||
(el: 'setCurrentTask', task: IRawStepTask): void
|
||||
(el: 'add-output', outputName: string): void
|
||||
(el: 'click-branch'): void
|
||||
}>()
|
||||
|
||||
const jsplumb = new Jsplumb('task-syllabus')
|
||||
@@ -40,7 +39,7 @@ const collaborationProcess = computed(() => {
|
||||
return agentsStore.agentRawPlan.data?.['Collaboration Process'] ?? []
|
||||
})
|
||||
|
||||
// 检测是否正在填充详情(有步骤但没有 AgentSelection)
|
||||
// 检测是否正在填充详情
|
||||
const isFillingDetails = computed(() => {
|
||||
const process = agentsStore.agentRawPlan.data?.['Collaboration Process'] || []
|
||||
return (
|
||||
@@ -59,8 +58,13 @@ const totalSteps = computed(() => {
|
||||
return agentsStore.agentRawPlan.data?.['Collaboration Process']?.length || 0
|
||||
})
|
||||
|
||||
// Notification system
|
||||
const { notifications, progress: showProgress, updateProgressDetail, removeNotification } = useNotification()
|
||||
// Notification 通知
|
||||
const {
|
||||
notifications,
|
||||
progress: showProgress,
|
||||
updateProgressDetail,
|
||||
removeNotification
|
||||
} = useNotification()
|
||||
const fillingProgressNotificationId = ref<string | null>(null)
|
||||
|
||||
// 监听填充进度,显示通知
|
||||
@@ -77,11 +81,7 @@ watch(
|
||||
if (filling && total > 0) {
|
||||
if (!fillingProgressNotificationId.value) {
|
||||
// 创建进度通知
|
||||
fillingProgressNotificationId.value = showProgress(
|
||||
'生成协作流程',
|
||||
completed,
|
||||
total
|
||||
)
|
||||
fillingProgressNotificationId.value = showProgress('生成协作流程', completed, total)
|
||||
updateProgressDetail(
|
||||
fillingProgressNotificationId.value,
|
||||
`${completed}/${total}`,
|
||||
@@ -108,138 +108,19 @@ watch(
|
||||
{ immediate: true }
|
||||
)
|
||||
|
||||
// 编辑状态管理
|
||||
const editingTaskId = ref<string | null>(null)
|
||||
const editingContent = ref('')
|
||||
|
||||
// 添加新产物状态管理
|
||||
const isAddingOutput = ref(false)
|
||||
const newOutputInputRef = ref<HTMLElement>()
|
||||
const newOutputName = ref('')
|
||||
|
||||
// 处理加号点击
|
||||
const handleAddOutputClick = () => {
|
||||
isAddingOutput.value = true
|
||||
newOutputName.value = ''
|
||||
|
||||
nextTick(() => {
|
||||
setTimeout(() => {
|
||||
if (newOutputInputRef.value) {
|
||||
newOutputInputRef.value?.focus()
|
||||
}
|
||||
jsplumb.instance.repaintEverything()
|
||||
}, 50)
|
||||
})
|
||||
}
|
||||
|
||||
// 保存新产物
|
||||
const saveNewOutput = () => {
|
||||
if (newOutputName.value.trim()) {
|
||||
const outputName = newOutputName.value.trim()
|
||||
const success = agentsStore.addNewOutput(outputName)
|
||||
if (success) {
|
||||
emit('add-output', outputName)
|
||||
isAddingOutput.value = false
|
||||
newOutputName.value = ''
|
||||
nextTick(() => {
|
||||
setTimeout(() => {
|
||||
jsplumb.instance.repaintEverything()
|
||||
}, 50)
|
||||
})
|
||||
console.log('添加新产物成功', outputName)
|
||||
} else {
|
||||
// 退出编辑状态
|
||||
isAddingOutput.value = false
|
||||
newOutputName.value = ''
|
||||
// 保存编辑内容
|
||||
const handleContentSave = (taskId: string, content: string) => {
|
||||
const taskToUpdate = collaborationProcess.value.find(item => item.Id === taskId)
|
||||
if (taskToUpdate && content !== taskToUpdate.TaskContent) {
|
||||
taskToUpdate.TaskContent = content
|
||||
// 记录修改过的步骤索引到store用于重新执行
|
||||
const stepIndex = collaborationProcess.value.findIndex(item => item.Id === taskId)
|
||||
if (stepIndex >= 0) {
|
||||
agentsStore.addModifiedStep(stepIndex)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 取消添加产物
|
||||
const cancelAddOutput = () => {
|
||||
isAddingOutput.value = false
|
||||
newOutputName.value = ''
|
||||
|
||||
nextTick(() => {
|
||||
setTimeout(() => {
|
||||
jsplumb.instance.repaintEverything()
|
||||
}, 50)
|
||||
})
|
||||
}
|
||||
|
||||
// 处理新产物的键盘事件
|
||||
const handleNewOutputKeydown = (event: KeyboardEvent) => {
|
||||
if (event.key === 'Enter') {
|
||||
event.preventDefault()
|
||||
saveNewOutput()
|
||||
} else if (event.key === 'Escape') {
|
||||
cancelAddOutput()
|
||||
}
|
||||
}
|
||||
|
||||
// 新产物输入框失去焦点处理
|
||||
const handleNewOutputBlur = () => {
|
||||
setTimeout(() => {
|
||||
if (newOutputName.value.trim() === '') {
|
||||
cancelAddOutput()
|
||||
}
|
||||
}, 100)
|
||||
}
|
||||
|
||||
// 开始编辑
|
||||
const startEditing = (task: IRawStepTask) => {
|
||||
if (!task.Id) {
|
||||
console.warn('Task ID is missing, cannot start editing')
|
||||
return
|
||||
}
|
||||
editingTaskId.value = task.Id
|
||||
editingContent.value = task.TaskContent || ''
|
||||
}
|
||||
|
||||
// 保存编辑
|
||||
const saveEditing = () => {
|
||||
if (editingTaskId.value && editingContent.value.trim()) {
|
||||
const taskToUpdate = collaborationProcess.value.find(item => item.Id === editingTaskId.value)
|
||||
if (taskToUpdate) {
|
||||
// 保存旧值用于比较
|
||||
const oldValue = taskToUpdate.TaskContent
|
||||
const newValue = editingContent.value.trim()
|
||||
|
||||
// 只有内容真正变化时才更新和记录修改
|
||||
if (newValue !== oldValue) {
|
||||
taskToUpdate.TaskContent = newValue
|
||||
|
||||
// 记录修改过的步骤索引到 store(用于重新执行)
|
||||
const stepIndex = collaborationProcess.value.findIndex(item => item.Id === editingTaskId.value)
|
||||
if (stepIndex >= 0) {
|
||||
agentsStore.addModifiedStep(stepIndex)
|
||||
console.log(`📝 步骤 ${stepIndex + 1} (${taskToUpdate.StepName}) 的 TaskContent 已被修改,将从该步骤重新执行`)
|
||||
}
|
||||
} else {
|
||||
console.log(`ℹ️ 步骤 ${taskToUpdate.StepName} 的 TaskContent 未发生变化,不记录修改`)
|
||||
}
|
||||
}
|
||||
}
|
||||
editingTaskId.value = null
|
||||
editingContent.value = ''
|
||||
}
|
||||
|
||||
// 取消编辑
|
||||
const cancelEditing = () => {
|
||||
editingTaskId.value = null
|
||||
editingContent.value = ''
|
||||
}
|
||||
|
||||
// 处理键盘事件
|
||||
const handleKeydown = (event: KeyboardEvent) => {
|
||||
if (event.key === 'Enter') {
|
||||
event.preventDefault()
|
||||
saveEditing()
|
||||
} else if (event.key === 'Escape') {
|
||||
cancelEditing()
|
||||
}
|
||||
}
|
||||
|
||||
function handleCurrentTask(task: IRawStepTask, transparent: boolean): ConnectArg[] {
|
||||
// 创建当前流程与产出的连线
|
||||
const arr: ConnectArg[] = [
|
||||
@@ -290,21 +171,14 @@ function clear() {
|
||||
|
||||
// 封装连线重绘方法
|
||||
const redrawConnections = () => {
|
||||
// 等待 DOM 更新完成
|
||||
nextTick(() => {
|
||||
// 清除旧连线
|
||||
jsplumb.reset()
|
||||
|
||||
// 等待 DOM 稳定后重新绘制
|
||||
setTimeout(() => {
|
||||
const arr: ConnectArg[] = []
|
||||
const currentTaskId = agentsStore.currentTask?.Id
|
||||
|
||||
// 重新绘制所有连线
|
||||
collaborationProcess.value.forEach(item => {
|
||||
arr.push(...handleCurrentTask(item, item.Id !== currentTaskId))
|
||||
})
|
||||
|
||||
jsplumb.connects(arr)
|
||||
}, 100)
|
||||
})
|
||||
@@ -321,7 +195,6 @@ watch(
|
||||
|
||||
// 组件挂载后初始化连线
|
||||
onMounted(() => {
|
||||
// 初始化时绘制连线
|
||||
nextTick(() => {
|
||||
setTimeout(() => {
|
||||
const arr: ConnectArg[] = []
|
||||
@@ -341,11 +214,8 @@ defineExpose({
|
||||
|
||||
<template>
|
||||
<div class="h-full flex flex-col">
|
||||
<!-- Notification 通知系统 -->
|
||||
<Notification
|
||||
:notifications="notifications"
|
||||
@close="(id) => removeNotification(id)"
|
||||
/>
|
||||
<!-- Notification 通知 -->
|
||||
<Notification :notifications="notifications" @close="id => removeNotification(id)" />
|
||||
|
||||
<div class="text-[18px] font-bold mb-[18px] text-[var(--color-text-title-header)]">
|
||||
任务大纲
|
||||
@@ -361,8 +231,7 @@ defineExpose({
|
||||
class="w-full relative min-h-full"
|
||||
id="task-syllabus"
|
||||
>
|
||||
<Bg :is-adding="isAddingOutput" @start-add-output="handleAddOutputClick" />
|
||||
|
||||
<Bg />
|
||||
<div class="w-full flex items-center gap-[14%] mb-[35px]">
|
||||
<div class="flex-1 flex justify-center">
|
||||
<div
|
||||
@@ -380,47 +249,6 @@ defineExpose({
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 添加新产物卡片 -->
|
||||
<div
|
||||
v-if="isAddingOutput"
|
||||
class="card-it w-full flex items-center gap-[14%] bg-[var(--color-card-bg)] add-output-form mb-[100px]"
|
||||
>
|
||||
<!-- 左侧空白的流程卡片占位 -->
|
||||
<div class="w-[43%] relative z-99" style="height: 20px"></div>
|
||||
|
||||
<!-- 右侧可编辑的产物卡片 -->
|
||||
<el-card
|
||||
class="w-[43%] relative task-syllabus-output-object-card border-dashed border-2 border-[var(--color-primary)]"
|
||||
>
|
||||
<div class="h-full flex items-center justify-center">
|
||||
<!-- 输入框 -->
|
||||
<el-input
|
||||
ref="newOutputInputRef"
|
||||
v-model="newOutputName"
|
||||
placeholder="Enter保存,ESC取消"
|
||||
@keydown="handleNewOutputKeydown"
|
||||
@blur="handleNewOutputBlur"
|
||||
size="large"
|
||||
class="w-full"
|
||||
/>
|
||||
</div>
|
||||
</el-card>
|
||||
</div>
|
||||
|
||||
<!-- 显示临时产物卡片 -->
|
||||
<div
|
||||
v-for="output in agentsStore.additionalOutputs"
|
||||
:key="output"
|
||||
class="card-it w-full flex items-center gap-[14%] bg-[var(--color-card-bg)] mb-[100px]"
|
||||
>
|
||||
<!-- 左侧空白的流程卡片占位 -->
|
||||
<div class="w-[43%] relative z-99" style="height: 100px"></div>
|
||||
|
||||
<!-- 右侧产物卡片 -->
|
||||
<el-card class="w-[43%] relative task-syllabus-output-object-card" :shadow="true">
|
||||
<div class="text-[18px] font-bold text-center">{{ output }}</div>
|
||||
</el-card>
|
||||
</div>
|
||||
<div
|
||||
v-for="item in collaborationProcess"
|
||||
:key="item.Id"
|
||||
@@ -440,49 +268,25 @@ defineExpose({
|
||||
<div class="h-[1px] w-full bg-[var(--color-border-separate)] my-[8px]"></div>
|
||||
|
||||
<!-- 任务内容区域 - 支持双击编辑 -->
|
||||
<div v-if="editingTaskId === item.Id" class="w-full">
|
||||
<div class="flex flex-col gap-3">
|
||||
<el-input
|
||||
v-model="editingContent"
|
||||
type="textarea"
|
||||
:autosize="{ minRows: 2, maxRows: 4 }"
|
||||
placeholder="请输入任务内容"
|
||||
@keydown="handleKeydown"
|
||||
class="task-content-editor"
|
||||
size="small"
|
||||
/>
|
||||
<div class="flex justify-end">
|
||||
<svg-icon
|
||||
icon-class="Check"
|
||||
size="20px"
|
||||
color="#328621"
|
||||
class="cursor-pointer mr-4"
|
||||
@click="saveEditing"
|
||||
title="保存"
|
||||
/>
|
||||
<svg-icon
|
||||
icon-class="Cancel"
|
||||
size="20px"
|
||||
color="#8e0707"
|
||||
class="cursor-pointer mr-1"
|
||||
@click="cancelEditing"
|
||||
title="取消"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div v-else @dblclick="startEditing(item)" class="w-full cursor-pointer">
|
||||
<MultiLineTooltip placement="right" :text="item.TaskContent" :lines="3">
|
||||
<div class="text-[14px] text-[var(--color-text-secondary)] task-content-display">
|
||||
{{ item.TaskContent }}
|
||||
</div>
|
||||
</MultiLineTooltip>
|
||||
</div>
|
||||
<TaskContentEditor :task="item" @save="handleContentSave">
|
||||
<template #display>
|
||||
<MultiLineTooltip placement="right" :text="item.TaskContent" :lines="3">
|
||||
<div class="text-[14px] text-[var(--color-text-secondary)] task-content-display">
|
||||
{{ item.TaskContent }}
|
||||
</div>
|
||||
</MultiLineTooltip>
|
||||
</template>
|
||||
</TaskContentEditor>
|
||||
|
||||
<div class="h-[1px] w-full bg-[var(--color-border-separate)] my-[8px]"></div>
|
||||
|
||||
<div
|
||||
class="flex items-center gap-2 flex-wrap relative w-full"
|
||||
:class="!item.AgentSelection || item.AgentSelection.length === 0 ? 'min-h-[40px]' : 'overflow-y-auto max-h-[72px]'"
|
||||
:class="
|
||||
!item.AgentSelection || item.AgentSelection.length === 0
|
||||
? 'min-h-[40px]'
|
||||
: 'overflow-y-auto max-h-[72px]'
|
||||
"
|
||||
>
|
||||
<!-- 连接到智能体库的连接点 -->
|
||||
<div
|
||||
@@ -492,13 +296,16 @@ defineExpose({
|
||||
|
||||
<!-- 未填充智能体时显示Loading -->
|
||||
<div
|
||||
v-if="(!item.AgentSelection || item.AgentSelection.length === 0) && !agentsStore.hasStoppedFilling"
|
||||
v-if="
|
||||
(!item.AgentSelection || item.AgentSelection.length === 0) &&
|
||||
!agentsStore.hasStoppedFilling
|
||||
"
|
||||
class="flex items-center gap-2 text-[var(--color-text-secondary)] text-[14px]"
|
||||
>
|
||||
<el-icon class="is-loading" :size="20">
|
||||
<Loading />
|
||||
</el-icon>
|
||||
<span>正在分配智能体...</span>
|
||||
<span>正在分配智能体<span class="loading-dots"></span></span>
|
||||
</div>
|
||||
|
||||
<!-- 已填充智能体时显示智能体列表 -->
|
||||
@@ -607,66 +414,6 @@ defineExpose({
|
||||
white-space: pre-wrap;
|
||||
}
|
||||
|
||||
.add-output-btn {
|
||||
opacity: 0.8;
|
||||
transition: opacity 0.2s ease;
|
||||
|
||||
&:hover {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
button {
|
||||
background: transparent;
|
||||
cursor: pointer;
|
||||
|
||||
&:hover {
|
||||
background-color: rgba(59, 130, 246, 0.05);
|
||||
transform: translateY(-1px);
|
||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
&:active {
|
||||
transform: translateY(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.add-output-form {
|
||||
animation: slideDown 0.3s ease-out;
|
||||
:deep(.el-card__body) {
|
||||
padding: 16px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
:deep(.el-input__wrapper) {
|
||||
border: 1px solid var(--color-text);
|
||||
background: transparent;
|
||||
box-shadow: none;
|
||||
|
||||
&.is-focus {
|
||||
border-color: var(--color-text);
|
||||
box-shadow: 0 0 0 1px var(--color-primary-light);
|
||||
}
|
||||
:deep(.el-input__inner) {
|
||||
font-size: 14px;
|
||||
text-align: center;
|
||||
font-weight: bold;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes slideDown {
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: translateY(-10px);
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: translateY(0);
|
||||
}
|
||||
}
|
||||
|
||||
// 输入框样式
|
||||
:deep(.el-input__wrapper) {
|
||||
background: transparent;
|
||||
@@ -730,4 +477,33 @@ defineExpose({
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 加载动画省略号
|
||||
.loading-dots {
|
||||
display: inline-block;
|
||||
width: 1.2em;
|
||||
text-align: left;
|
||||
|
||||
&::after {
|
||||
content: '';
|
||||
animation: dots 1.5s steps(4, end) infinite;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes dots {
|
||||
0%,
|
||||
20% {
|
||||
content: '';
|
||||
}
|
||||
40% {
|
||||
content: '.';
|
||||
}
|
||||
60% {
|
||||
content: '..';
|
||||
}
|
||||
80%,
|
||||
100% {
|
||||
content: '...';
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -47,10 +47,10 @@ function handleTaskSyllabusCurrentTask(task: IRawStepTask) {
|
||||
agentsStore.setCurrentTask(task)
|
||||
}
|
||||
|
||||
// 更新任务大纲内部的线
|
||||
function handleTaskResultCurrentTask(task: IRawStepTask) {
|
||||
scrollToElementTop(`task-syllabus-flow-${task.Id}`)
|
||||
agentsStore.setCurrentTask(task)
|
||||
// 更新任务大纲内部的线
|
||||
taskSyllabusRef.value?.changeTask(task, false)
|
||||
}
|
||||
|
||||
@@ -71,11 +71,6 @@ function clear() {
|
||||
taskResultJsplumb.repaintEverything()
|
||||
}
|
||||
|
||||
const additionalOutputs = ref<string[]>([])
|
||||
const handleAddOutput = (outputName: string) => {
|
||||
additionalOutputs.value.unshift(outputName)
|
||||
}
|
||||
|
||||
defineExpose({
|
||||
changeTask,
|
||||
resetAgentRepoLine,
|
||||
@@ -99,7 +94,6 @@ defineExpose({
|
||||
ref="taskSyllabusRef"
|
||||
@resetAgentRepoLine="resetAgentRepoLine"
|
||||
@set-current-task="handleTaskSyllabusCurrentTask"
|
||||
@add-output="handleAddOutput"
|
||||
/>
|
||||
</div>
|
||||
<!-- 执行结果 -->
|
||||
@@ -108,7 +102,6 @@ defineExpose({
|
||||
ref="taskResultRef"
|
||||
@refresh-line="taskResultJsplumb.repaintEverything"
|
||||
@set-current-task="handleTaskResultCurrentTask"
|
||||
:additional-outputs="additionalOutputs"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -96,6 +96,23 @@ export interface IRawPlanResponse {
|
||||
'Collaboration Process'?: IRawStepTask[]
|
||||
}
|
||||
|
||||
/**
|
||||
* IApiAgentAction[] → TaskProcess[] 格式转换
|
||||
* @param actions IApiAgentAction 数组
|
||||
* @returns TaskProcess 数组
|
||||
*/
|
||||
export function convertToTaskProcess(
|
||||
actions: { id: string; type: string; agent: string; description: string; inputs?: string[] }[]
|
||||
): TaskProcess[] {
|
||||
return actions.map(action => ({
|
||||
ID: action.id,
|
||||
ActionType: action.type,
|
||||
AgentName: action.agent,
|
||||
Description: action.description,
|
||||
ImportantInput: action.inputs || []
|
||||
}))
|
||||
}
|
||||
|
||||
const storageKey = '$agents' as const
|
||||
|
||||
// 清除所有以 storageKey 开头的 localStorage
|
||||
@@ -471,9 +488,6 @@ export const useAgentsStore = defineStore('agents', () => {
|
||||
hasStoppedFilling.value = false
|
||||
}
|
||||
|
||||
// 额外的产物列表
|
||||
const additionalOutputs = ref<string[]>([])
|
||||
|
||||
// 用户在 Assignment 中选择的 agent 组合(按任务ID分别存储)
|
||||
const selectedAgentGroupMap = ref<Map<string, string[]>>(new Map())
|
||||
|
||||
@@ -498,41 +512,42 @@ export const useAgentsStore = defineStore('agents', () => {
|
||||
selectedAgentGroupMap.value.clear()
|
||||
}
|
||||
|
||||
// 添加新产物
|
||||
function addNewOutput(outputObject: string) {
|
||||
if (!outputObject.trim()) return false
|
||||
|
||||
const trimmedOutput = outputObject.trim()
|
||||
if (!additionalOutputs.value.includes(trimmedOutput)) {
|
||||
additionalOutputs.value.unshift(trimmedOutput)
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// 删除额外产物
|
||||
function removeAdditionalOutput(outputObject: string) {
|
||||
const index = additionalOutputs.value.indexOf(outputObject)
|
||||
if (index > -1) {
|
||||
additionalOutputs.value.splice(index, 1)
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// 清除额外产物
|
||||
function clearAdditionalOutputs() {
|
||||
additionalOutputs.value = []
|
||||
}
|
||||
|
||||
// 标记是否用户已停止智能体分配过程
|
||||
const hasStoppedFilling = ref(false)
|
||||
|
||||
// 标记是否正在停止中(全局停止状态)
|
||||
const isStopping = ref(false)
|
||||
|
||||
// 设置停止状态
|
||||
function setHasStoppedFilling(value: boolean) {
|
||||
hasStoppedFilling.value = value
|
||||
}
|
||||
|
||||
// 设置正在停止状态
|
||||
function setIsStopping(value: boolean) {
|
||||
isStopping.value = value
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建 IApiStepTask 对象(用于 API 调用)
|
||||
* @param agents 选中的 agent 列表
|
||||
* @returns IApiStepTask 格式的对象
|
||||
*/
|
||||
function createStepTaskForApi(agents: string[]): IApiStepTask {
|
||||
return {
|
||||
name: currentTask.value?.StepName || '',
|
||||
content: currentTask.value?.TaskContent || '',
|
||||
inputs: currentTask.value?.InputObject_List || [],
|
||||
output: currentTask.value?.OutputObject || '',
|
||||
agents,
|
||||
brief: currentTask.value?.Collaboration_Brief_frontEnd || {
|
||||
template: '',
|
||||
data: {}
|
||||
},
|
||||
process: []
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
agents,
|
||||
setAgents,
|
||||
@@ -543,15 +558,12 @@ export const useAgentsStore = defineStore('agents', () => {
|
||||
setCurrentTaskProcess, // 🆕 设置当前任务的 TaskProcess
|
||||
updateCurrentAgentSelection, // 🆕 强制更新 AgentSelection(用于 AgentAllocation 切换组合)
|
||||
syncCurrentTaskToMainProcess, // 🆕 同步 currentTask 到主流程
|
||||
createStepTaskForApi, // 🆕 构建 IApiStepTask 对象
|
||||
agentRawPlan,
|
||||
setAgentRawPlan,
|
||||
executePlan,
|
||||
setExecutePlan,
|
||||
resetAgent,
|
||||
additionalOutputs,
|
||||
addNewOutput,
|
||||
removeAdditionalOutput,
|
||||
clearAdditionalOutputs,
|
||||
// 用户选择的 agent 组合
|
||||
selectedAgentGroupMap,
|
||||
setSelectedAgentGroup,
|
||||
@@ -593,6 +605,9 @@ export const useAgentsStore = defineStore('agents', () => {
|
||||
// 停止填充状态
|
||||
hasStoppedFilling,
|
||||
setHasStoppedFilling,
|
||||
// 正在停止状态
|
||||
isStopping,
|
||||
setIsStopping,
|
||||
// 重新执行相关
|
||||
modifiedSteps,
|
||||
addModifiedStep,
|
||||
|
||||
167
frontend/src/utils/retry.ts
Normal file
167
frontend/src/utils/retry.ts
Normal file
@@ -0,0 +1,167 @@
|
||||
/**
|
||||
* 重试工具函数
|
||||
* @description 提供通用的重试机制,支持指数退避和自定义重试条件
|
||||
*/
|
||||
|
||||
/**
|
||||
* 重试选项配置
|
||||
*/
|
||||
export interface RetryOptions {
|
||||
/** 最大重试次数,默认 3 */
|
||||
maxRetries?: number
|
||||
/** 初始延迟时间(毫秒),默认 2000 */
|
||||
initialDelayMs?: number
|
||||
/** 是否使用指数退避,默认 true */
|
||||
useExponentialBackoff?: boolean
|
||||
/** 最大延迟时间(毫秒),默认 30000 */
|
||||
maxDelayMs?: number
|
||||
/** 自定义重试条件函数,返回 true 表示应该重试 */
|
||||
shouldRetry?: (error: any, attempt: number) => boolean
|
||||
/** 重试前的回调函数 */
|
||||
onRetry?: (error: any, attempt: number, delay: number) => void
|
||||
}
|
||||
|
||||
/**
|
||||
* 默认重试条件 - 检查是否是 rate limiting 错误
|
||||
* @param error 错误对象
|
||||
* @returns 是否应该重试
|
||||
*/
|
||||
export function defaultShouldRetry(error: any): boolean {
|
||||
if (!error) return false
|
||||
const message = error?.message || String(error)
|
||||
// 检查 406 Not Acceptable、429 Too Many Requests 等 rate limiting 错误
|
||||
const isRateLimit =
|
||||
message.includes('406') ||
|
||||
message.includes('429') ||
|
||||
message.includes('Not Acceptable') ||
|
||||
message.includes('Too Many Requests') ||
|
||||
message.includes('rate limit')
|
||||
return isRateLimit
|
||||
}
|
||||
|
||||
/**
|
||||
* 带重试机制的异步函数执行器
|
||||
*
|
||||
* @example
|
||||
* ```typescript
|
||||
* // 基本用法
|
||||
* const result = await withRetry(() => api.someRequest())
|
||||
*
|
||||
* // 自定义重试次数和延迟
|
||||
* const result = await withRetry(() => api.request(), {
|
||||
* maxRetries: 5,
|
||||
* initialDelayMs: 1000,
|
||||
* useExponentialBackoff: true
|
||||
* })
|
||||
*
|
||||
* // 自定义重试条件
|
||||
* const result = await withRetry(() => api.request(), {
|
||||
* shouldRetry: (error) => error?.code === 'NETWORK_ERROR'
|
||||
* })
|
||||
*
|
||||
* // 带重试回调
|
||||
* const result = await withRetry(() => api.request(), {
|
||||
* onRetry: (error, attempt, delay) => {
|
||||
* console.log(`第 ${attempt} 次重试,等待 ${delay}ms`, error.message)
|
||||
* }
|
||||
* })
|
||||
* ```
|
||||
*
|
||||
* @param fn - 要执行的异步函数
|
||||
* @param options - 重试选项配置
|
||||
* @returns Promise resolves with the result of the function
|
||||
* @throws 如果重试次数用尽,抛出最后一次的错误
|
||||
*/
|
||||
export async function withRetry<T>(
|
||||
fn: () => Promise<T>,
|
||||
options: RetryOptions = {},
|
||||
): Promise<T> {
|
||||
const {
|
||||
maxRetries = 3,
|
||||
initialDelayMs = 2000,
|
||||
useExponentialBackoff = true,
|
||||
maxDelayMs = 30000,
|
||||
shouldRetry = defaultShouldRetry,
|
||||
onRetry,
|
||||
} = options
|
||||
|
||||
let lastError: any = null
|
||||
let currentDelay = initialDelayMs
|
||||
|
||||
for (let attempt = 1; attempt <= maxRetries + 1; attempt++) {
|
||||
try {
|
||||
// 执行目标函数
|
||||
return await fn()
|
||||
} catch (error: any) {
|
||||
lastError = error
|
||||
|
||||
// 判断是否应该重试
|
||||
const shouldRetryAttempt = shouldRetry(error, attempt)
|
||||
|
||||
// 如果是最后一次尝试 或者 不应该重试,则抛出错误
|
||||
if (attempt > maxRetries || !shouldRetryAttempt) {
|
||||
if (attempt > maxRetries) {
|
||||
console.error(
|
||||
`❌ [withRetry] 已达到最大重试次数 (${maxRetries}),放弃请求`,
|
||||
)
|
||||
}
|
||||
throw error
|
||||
}
|
||||
|
||||
// 执行重试回调
|
||||
if (onRetry) {
|
||||
onRetry(error, attempt, currentDelay)
|
||||
}
|
||||
|
||||
// 打印日志
|
||||
console.log(
|
||||
`⏳ [withRetry] 第 ${attempt} 次重试,等待 ${currentDelay}ms...`,
|
||||
error?.message || String(error),
|
||||
)
|
||||
|
||||
// 等待延迟时间
|
||||
await new Promise((resolve) => setTimeout(resolve, currentDelay))
|
||||
|
||||
// 计算下一次延迟时间(指数退避)
|
||||
if (useExponentialBackoff) {
|
||||
currentDelay = Math.min(currentDelay * 2, maxDelayMs)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//理论上不会到达这里,因为循环内会 throw
|
||||
throw lastError
|
||||
}
|
||||
|
||||
/**
|
||||
* 简化的重试装饰器 - 适用于类方法
|
||||
*
|
||||
* @example
|
||||
* ```typescript
|
||||
* class MyService {
|
||||
* @retryable({ maxRetries: 3, initialDelayMs: 1000 })
|
||||
* async fetchData() {
|
||||
* return await api.request()
|
||||
* }
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* @param options - 重试选项配置
|
||||
* @returns 装饰器函数
|
||||
*/
|
||||
export function retryable(options: RetryOptions = {}) {
|
||||
return function <T extends (...args: any[]) => Promise<any>>(
|
||||
_target: any,
|
||||
_propertyKey: string,
|
||||
descriptor: TypedPropertyDescriptor<T>,
|
||||
) {
|
||||
const originalMethod = descriptor.value
|
||||
if (!originalMethod) return descriptor
|
||||
|
||||
descriptor.value = function (this: any, ...args: Parameters<T>) {
|
||||
return withRetry(() => originalMethod.apply(this, args), options)
|
||||
} as T
|
||||
|
||||
return descriptor
|
||||
} as MethodDecorator
|
||||
}
|
||||
@@ -264,6 +264,24 @@ class WebSocketClient {
|
||||
get id(): string | undefined {
|
||||
return this.socket?.id
|
||||
}
|
||||
|
||||
/**
|
||||
* 监听事件
|
||||
*/
|
||||
on(event: string, callback: (...args: any[]) => void): void {
|
||||
if (this.socket) {
|
||||
this.socket.on(event, callback)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 取消监听事件
|
||||
*/
|
||||
off(event: string, callback?: (...args: any[]) => void): void {
|
||||
if (this.socket) {
|
||||
this.socket.off(event, callback)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 导出单例
|
||||
|
||||
Reference in New Issue
Block a user