feat:三个窗口接口联调版本

This commit is contained in:
liailing1026
2026-01-09 13:54:32 +08:00
parent 5847365eee
commit 920588b063
26 changed files with 4133 additions and 1856 deletions

View File

@@ -100,6 +100,7 @@ async function handleSearch() {
})
data['Collaboration Process'] = changeBriefs(data['Collaboration Process'])
agentsStore.setAgentRawPlan({ data })
console.log('agentsStore.agentRawPlan', agentsStore.agentRawPlan)
emit('search', searchValue.value)
} finally {
triggerOnFocus.value = true

View File

@@ -1,5 +1,5 @@
<script setup lang="ts">
import { ref } from 'vue'
import { ref, computed } from 'vue'
import { getActionTypeDisplay } from '@/layout/components/config.ts'
import { useAgentsStore } from '@/stores'
import BranchButton from './components/TaskButton.vue'
@@ -23,6 +23,20 @@ const emit = defineEmits<{
(e: 'save-edit', stepId: string, processId: string, value: string): void
}>()
// 🔄 从 currentTask 中获取数据(与分支切换联动)
const currentTaskProcess = computed(() => {
// ✅ 优先使用 currentTask包含分支切换后的数据
const currentTask = agentsStore.currentTask
if (currentTask && currentTask.Id === props.step.Id && currentTask.TaskProcess) {
return currentTask.TaskProcess
}
// ⚠️ 降级:从 agentRawPlan 中获取原始数据(不受分支切换影响)
const collaborationProcess = agentsStore.agentRawPlan.data?.['Collaboration Process'] || []
const rawData = collaborationProcess.find((task: any) => task.Id === props.step.Id)
return rawData?.TaskProcess || []
})
// 当前正在编辑的process ID
const editingProcessId = ref<string | null>(null)
// 编辑框的值
@@ -30,6 +44,17 @@ const editValue = ref('')
// 鼠标悬停的process ID
const hoverProcessId = ref<string | null>(null)
// 🆕 处理卡片点击事件(非编辑模式下)
function handleCardClick() {
// 如果正在编辑,不处理点击
if (editingProcessId.value) return
// 设置当前任务,与任务大纲联动
if (props.step.Id) {
agentsStore.setCurrentTask(props.step)
}
}
// 检测当前是否是深色模式
function isDarkMode(): boolean {
return document.documentElement.classList.contains('dark')
@@ -116,12 +141,12 @@ function handleCancel() {
</script>
<template>
<div class="process-card">
<div class="process-card" @click="handleCardClick">
<div class="process-content">
<!-- 显示模式 -->
<div class="display-content">
<span
v-for="process in step.TaskProcess"
v-for="process in currentTaskProcess"
:key="process.ID"
class="process-segment"
@mouseenter="handleMouseEnter(process.ID)"
@@ -190,10 +215,13 @@ function handleCancel() {
{{ process.Description }}
</span>
<span class="separator" v-if="!process.Description.endsWith('。')"></span>
<span class="separator" v-if="process.Description && !process.Description.endsWith('。')"
></span
>
</span>
</div>
</div>
<!-- 按钮点击不会冒泡到卡片 -->
<BranchButton :step="step" />
</div>
</template>
@@ -207,6 +235,8 @@ function handleCancel() {
background: var(--color-bg-list);
border: 1px solid var(--color-border-default);
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
cursor: pointer;
transition: all 0.2s ease;
.process-content {
min-height: 20px;

View File

@@ -21,11 +21,28 @@ const branchCount = computed(() => {
const branches = selectionStore.getTaskProcessBranches(taskStepId)
// 主分支(1) + 额外分支数量
return 1 + branches.length
return branches.length || 1
})
const handleClick = (event: MouseEvent) => {
event.stopPropagation() // 阻止冒泡,避免触发卡片点击
// 🆕 判断按钮是否可点击(只有当前按钮对应的任务是任务大纲中选中的任务时才可点击)
const isClickable = computed(() => {
if (!props.step?.Id || !agentsStore.currentTask?.Id) {
return false
}
return props.step.Id === agentsStore.currentTask.Id
})
const handleClick = (event?: MouseEvent) => {
// 🆕 只有可点击时才执行操作
if (!isClickable.value) {
return
}
// 阻止冒泡,避免触发卡片点击
if (event) {
event.stopPropagation()
}
emit('click')
// 设置当前任务
if (props.step) {
@@ -39,9 +56,9 @@ const handleClick = (event: MouseEvent) => {
<template>
<div
class="task-button"
:class="{ 'has-branches': branchCount > 1 }"
:class="{ 'has-branches': branchCount > 0, 'is-disabled': !isClickable }"
@click="handleClick"
:title="`${branchCount} 个分支`"
:title="isClickable ? `${branchCount} 个分支` : '请先在任务大纲中选中此任务'"
>
<!-- 流程图标 -->
<svg-icon icon-class="branch" size="20px" class="task-icon" />
@@ -83,6 +100,17 @@ const handleClick = (event: MouseEvent) => {
filter: brightness(0.9);
}
// 🆕 禁用状态
&.is-disabled {
background-color: #bdc3c7;
cursor: not-allowed;
opacity: 0.6;
&:hover {
filter: none;
}
}
&.has-branches::after {
content: '';
position: absolute;

View File

@@ -1,142 +0,0 @@
// /api/fill_stepTask 接口的Vue适用mock数据
import type { IApiStepTask, IRawStepTask } from '@/stores/modules/agents'
// 模拟接口响应数据
export const mockFillStepTaskResponse: IApiStepTask = {
name: '需求分析与原型设计',
content: '分析用户需求并创建产品原型',
inputs: ['用户调研报告', '竞品分析文档'],
output: '产品原型设计稿',
agents: ['实验材料学家', '腐蚀机理研究员', '防护工程专家'],
brief: {
template: '基于!<0>!和!<1>!!<2>!、!<3>!和!<4>!执行!<5>!任务,以获得!<6>!。',
data: {
'0': {
text: '用户调研报告',
style: {
background: 'hsl(120, 60%, 70%)',
},
},
'1': {
text: '竞品分析文档',
style: {
background: 'hsl(120, 60%, 70%)',
},
},
'2': {
text: '实验材料学家',
style: {
background: 'hsl(0, 0%, 90%)',
boxShadow: '1px 1px 4px 1px rgba(0,0,0,0.2)',
},
},
'3': {
text: '腐蚀机理研究员',
style: {
background: 'hsl(0, 0%, 90%)',
boxShadow: '1px 1px 4px 1px rgba(0,0,0,0.2)',
},
},
'4': {
text: '防护工程专家',
style: {
background: 'hsl(0, 0%, 90%)',
boxShadow: '1px 1px 4px 1px rgba(0,0,0,0.2)',
},
},
'5': {
text: '分析用户需求并创建产品原型',
style: {
background: 'hsl(0, 0%, 87%)',
border: '1.5px solid #ddd',
},
},
'6': {
text: '产品原型设计稿',
style: {
background: 'hsl(30, 100%, 80%)',
},
},
},
},
process: [
{
id: 'action_001',
type: '需求分析',
agent: '实验材料学家',
description: '分析用户调研报告,识别核心需求点',
inputs: ['用户调研报告'],
},
{
id: 'action_002',
type: '竞品分析',
agent: '实验材料学家',
description: '对比竞品功能,确定产品差异化优势',
inputs: ['竞品分析文档'],
},
{
id: 'action_003',
type: '信息架构设计',
agent: '防护工程专家',
description: '设计产品信息结构和用户流程',
inputs: ['需求分析结果'],
},
{
id: 'action_004',
type: '界面原型设计',
agent: '腐蚀机理研究员',
description: '创建高保真界面原型',
inputs: ['信息架构设计'],
},
{
id: 'action_005',
type: '原型评审',
agent: '实验材料学家',
description: '组织团队评审原型设计',
inputs: ['界面原型设计'],
},
],
}
// 请求参数类型
export interface IFillStepTaskRequest {
goal: string
stepTask: IApiStepTask
}
// Vue composable
export const useFillStepTaskMock = () => {
const fillStepTask = async (
goal: string,
stepTask: IApiStepTask,
): Promise<{ data: IApiStepTask }> => {
return new Promise((resolve) => {
setTimeout(() => {
resolve({
data: mockFillStepTaskResponse,
})
}, 500)
})
}
return {
fillStepTask,
}
}
// Vue组件使用示例
export const fillStepTaskExampleRequest: IFillStepTaskRequest = {
goal: '开发一个智能协作平台',
stepTask: {
name: '需求分析与原型设计',
content: '分析用户需求并创建产品原型',
inputs: ['用户调研报告', '竞品分析文档'],
output: '产品原型设计稿',
agents: [],
brief: {
template: '',
data: {},
},
process: [],
},
}

View File

@@ -1,159 +0,0 @@
// /api/fill_stepTask_TaskProcess 接口的Vue适用mock数据
import type { IApiStepTask } from '@/stores'
// 模拟接口响应数据
export const mockFillAgentSelectionResponse: IApiStepTask = {
name: '技术方案设计与开发',
content: '设计技术架构并完成核心功能开发',
inputs: ['产品需求文档', '技术选型指南'],
output: '可运行的产品版本',
agents: ['架构师', '后端工程师', '前端工程师', '测试工程师'],
brief: {
template: '基于!<0>!和!<1>!!<2>!、!<3>!、!<4>!和!<5>!执行!<6>!任务,以获得!<7>!。',
data: {
'0': {
text: '产品需求文档',
style: {
background: 'hsl(120, 60%, 70%)',
},
},
'1': {
text: '技术选型指南',
style: {
background: 'hsl(120, 60%, 70%)',
},
},
'2': {
text: '架构师',
style: {
background: 'hsl(0, 0%, 90%)',
boxShadow: '1px 1px 4px 1px rgba(0,0,0,0.2)',
},
},
'3': {
text: '后端工程师',
style: {
background: 'hsl(0, 0%, 90%)',
boxShadow: '1px 1px 4px 1px rgba(0,0,0,0.2)',
},
},
'4': {
text: '前端工程师',
style: {
background: 'hsl(0, 0%, 90%)',
boxShadow: '1px 1px 4px 1px rgba(0,0,0,0.2)',
},
},
'5': {
text: '测试工程师',
style: {
background: 'hsl(0, 0%, 90%)',
boxShadow: '1px 1px 4px 1px rgba(0,0,0,0.2)',
},
},
'6': {
text: '设计技术架构并完成核心功能开发',
style: {
background: 'hsl(0, 0%, 87%)',
border: '1.5px solid #ddd',
},
},
'7': {
text: '可运行的产品版本',
style: {
background: 'hsl(30, 100%, 80%)',
},
},
},
},
process: [
{
id: 'action_101',
type: '技术架构设计',
agent: '架构师',
description: '设计系统架构和技术栈选型',
inputs: ['产品需求文档', '技术选型指南'],
},
{
id: 'action_102',
type: '数据库设计',
agent: '后端工程师',
description: '设计数据库表结构和关系',
inputs: ['技术架构设计'],
},
{
id: 'action_103',
type: '后端API开发',
agent: '后端工程师',
description: '实现RESTful API接口',
inputs: ['数据库设计'],
},
{
id: 'action_104',
type: '前端界面开发',
agent: '前端工程师',
description: '开发用户界面和交互功能',
inputs: ['后端API开发'],
},
{
id: 'action_105',
type: '单元测试',
agent: '测试工程师',
description: '编写和执行单元测试用例',
inputs: ['前端界面开发'],
},
{
id: 'action_106',
type: '集成测试',
agent: '测试工程师',
description: '进行系统集成测试',
inputs: ['单元测试'],
},
],
}
// 请求参数类型
export interface IFillAgentSelectionRequest {
goal: string
stepTask: IApiStepTask
agents: string[]
}
// Vue composable
export const useFillAgentSelectionMock = () => {
const fillAgentSelection = async (
goal: string,
stepTask: IApiStepTask,
agents: string[],
): Promise<{ data: IApiStepTask }> => {
return new Promise((resolve) => {
setTimeout(() => {
resolve({
data: mockFillAgentSelectionResponse,
})
}, 500)
})
}
return {
fillAgentSelection,
}
}
// Vue组件使用示例
export const fillAgentSelectionExampleRequest: IFillAgentSelectionRequest = {
goal: '开发一个智能协作平台',
stepTask: {
name: '技术方案设计与开发',
content: '设计技术架构并完成核心功能开发',
inputs: ['产品需求文档', '技术选型指南'],
output: '可运行的产品版本',
agents: [],
brief: {
template: '',
data: {},
},
process: [],
},
agents: ['架构师', '后端工程师', '前端工程师', '测试工程师'],
}

View File

@@ -1,5 +1,5 @@
<script setup lang="ts">
import { computed, onUnmounted, ref, reactive, nextTick } from 'vue'
import { computed, onUnmounted, ref, reactive, nextTick, watch, onMounted } from 'vue'
import { throttle } from 'lodash'
import { AnchorLocations, BezierConnector } from '@jsplumb/browser-ui'
import AdditionalOutputCard from './AdditionalOutputCard.vue'
@@ -19,12 +19,9 @@ const emit = defineEmits<{
const agentsStore = useAgentsStore()
const drawerVisible = ref(false)
const collaborationProcess = computed(() => {
const data = agentsStore.agentRawPlan.data?.['Collaboration Process'] ?? []
// console.log('data:', data)
return data
// return agentsStore.agentRawPlan.data?.['Collaboration Process'] ?? []
const collaborationProcess = computed(() => {
return agentsStore.agentRawPlan.data?.['Collaboration Process'] ?? []
})
// 监听额外产物变化
@@ -201,6 +198,12 @@ async function handleTaskProcess() {
drawerVisible.value = true
}
// 重置执行结果
function handleRefresh() {
agentsStore.setExecutePlan([])
console.log('🔄 已重置执行结果')
}
// 添加滚动状态标识
const isScrolling = ref(false)
let scrollTimer: ReturnType<typeof setTimeout> | null = null
@@ -226,20 +229,58 @@ const handleMouseEnter = throttle(id => {
if (!isScrolling.value) {
createInternalLine(id)
}
}, 100)
}, 0)
const handleMouseLeave = throttle(() => {
if (!isScrolling.value) {
createInternalLine()
}
}, 100)
}, 0)
function clear() {
jsplumb.reset()
}
// 🆕 封装连线重绘方法
const redrawInternalLines = (highlightId?: string) => {
console.log('🔄 TaskResult: 重新绘制连线', highlightId ? `高亮: ${highlightId}` : '')
// 等待 DOM 更新完成
nextTick(() => {
// 清除旧连线
jsplumb.reset()
// 等待 DOM 稳定后重新绘制
setTimeout(() => {
createInternalLine(highlightId)
console.log('✅ TaskResult: 连线重绘完成,任务数:', collaborationProcess.value.length)
}, 100)
})
}
// 🆕 监听 collaborationProcess 变化,自动重绘连线
watch(
() => collaborationProcess,
() => {
console.log('🔍 TaskResult: collaborationProcess 发生变化,触发重绘')
redrawInternalLines()
},
{ deep: true }
)
// 🆕 组件挂载后初始化连线
onMounted(() => {
// 初始化时绘制连线
nextTick(() => {
setTimeout(() => {
createInternalLine()
console.log('✅ TaskResult: 初始化连线完成')
}, 100)
})
})
//按钮交互状态管理
const buttonHoverState = ref<'process' | 'execute' | null>(null)
const buttonHoverState = ref<'process' | 'execute' | 'refresh' | null>(null)
let buttonHoverTimer: ReturnType<typeof setTimeout> | null = null
const handleProcessMouseEnter = () => {
if (buttonHoverTimer) {
@@ -259,6 +300,16 @@ const handleExecuteMouseEnter = () => {
}
}
const handleRefreshMouseEnter = () => {
if (buttonHoverTimer) {
clearTimeout(buttonHoverTimer)
buttonHoverTimer = null
}
if (agentsStore.executePlan.length > 0) {
buttonHoverState.value = 'refresh'
}
}
const handleButtonMouseLeave = () => {
// 添加防抖,防止快速切换时的抖动
if (buttonHoverTimer) {
@@ -277,18 +328,31 @@ onUnmounted(() => {
})
// 计算按钮类名
const processBtnClass = computed(() => {
// 当刷新或执行按钮悬停时,过程按钮变圆形
if (buttonHoverState.value === 'refresh' || buttonHoverState.value === 'execute') {
return 'circle'
}
return buttonHoverState.value === 'process' ? 'ellipse' : 'circle'
})
const executeBtnClass = computed(() => {
// 鼠标悬停在过程按钮上时,执行按钮变圆形
if (buttonHoverState.value === 'process') {
// 鼠标悬停在过程按钮或刷新按钮上时,执行按钮变圆形
if (buttonHoverState.value === 'process' || buttonHoverState.value === 'refresh') {
return 'circle'
}
//如果有任务数据就显示椭圆形,否则显示圆形
return agentsStore.agentRawPlan.data ? 'ellipse' : 'circle'
})
const refreshBtnClass = computed(() => {
// 当过程或执行按钮悬停时,刷新按钮变圆形
if (buttonHoverState.value === 'process' || buttonHoverState.value === 'execute') {
return 'circle'
}
// 有执行结果就显示椭圆形,否则显示圆形
return agentsStore.executePlan.length > 0 ? 'ellipse' : 'circle'
})
// 计算按钮是否显示文字
const showProcessText = computed(() => {
return buttonHoverState.value === 'process'
@@ -301,6 +365,10 @@ const showExecuteText = computed(() => {
return agentsStore.agentRawPlan.data
})
const showRefreshText = computed(() => {
return buttonHoverState.value === 'refresh'
})
// 计算按钮标题
const processBtnTitle = computed(() => {
return buttonHoverState.value === 'process' ? '任务过程' : '点击查看任务过程'
@@ -310,6 +378,10 @@ const executeBtnTitle = computed(() => {
return showExecuteText.value ? '任务执行' : '点击运行'
})
const refreshBtnTitle = computed(() => {
return showRefreshText.value ? '重置执行结果' : '点击重置执行状态'
})
defineExpose({
createInternalLine,
clear
@@ -329,6 +401,19 @@ defineExpose({
class="flex items-center justify-end gap-[14px] task-button-group min-w-[175px]"
@mouseleave="handleButtonMouseLeave"
>
<!-- 刷新按钮 -->
<el-button
:class="refreshBtnClass"
:color="variables.tertiary"
:title="refreshBtnTitle"
:disabled="agentsStore.executePlan.length === 0"
@mouseenter="handleRefreshMouseEnter"
@click="handleRefresh"
style="order: 0"
>
<svg-icon icon-class="refresh" />
<span v-if="showRefreshText" class="btn-text">重置</span>
</el-button>
<!-- 任务过程按钮 -->
<el-button
:class="processBtnClass"
@@ -680,21 +765,33 @@ defineExpose({
display: inline-flex !important;
align-items: center !important;
justify-content: center !important;
transition: all 0.35s cubic-bezier(0.175, 0.885, 0.32, 1.275) !important;
transition: width 0.2s ease-out, padding 0.2s ease-out, border-radius 0.2s ease-out, transform 0.2s ease-out, box-shadow 0.2s ease-out, filter 0.2s ease-out !important;
overflow: hidden !important;
white-space: nowrap !important;
border: none !important;
border: 1px solid transparent !important;
border-color: transparent !important;
color: var(--color-text-primary) !important;
position: relative;
background-color: var(--color-bg-tertiary);
background-color: var(--color-bg-tertiary) !important;
gap: 0px !important;
outline: none !important;
box-shadow: none !important;
-webkit-tap-highlight-color: transparent !important;
backface-visibility: hidden !important;
-webkit-backface-visibility: hidden !important;
transform: translateZ(0) !important;
will-change: transform, width, padding, border-radius !important;
&::before,
&::after {
display: none !important;
}
&:hover {
transform: translateY(-2px) !important;
transform: translateY(-2px) translateZ(0) !important;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15) !important;
filter: brightness(1.1) !important;
border-color: transparent !important;
}
&.is-disabled {

View File

@@ -9,57 +9,23 @@
<el-card
class="task-node-card"
:class="{ 'is-editing': isEditing, 'is-active': isActive }"
:class="{
'is-editing': isEditing,
'is-active': isActive,
'is-branch-selected': props.isBranchSelected
}"
:shadow="true"
>
<!-- 任务名称 -->
<div class="task-name">{{ task.StepName }}</div>
<!-- <div class="divider"></div> -->
<!-- 任务内容 -->
<!-- <div v-if="isEditing" class="task-content-editing">
<el-input
v-model="editingContent"
type="textarea"
:autosize="{ minRows: 2, maxRows: 4 }"
placeholder="请输入任务内容"
@keydown="handleKeydown"
class="task-content-editor"
size="small"
/>
<div class="edit-actions">
<svg-icon
icon-class="Check"
size="18px"
color="#328621"
class="cursor-pointer"
@click="saveEdit"
title="保存"
/>
<svg-icon
icon-class="Cancel"
size="18px"
color="#8e0707"
class="cursor-pointer ml-2"
@click="cancelEdit"
title="取消"
/>
</div>
</div>
<div v-else class="task-content" @dblclick="startEdit">
{{ task.TaskContent || '暂无内容' }}
</div> -->
<!-- <div class="divider"></div> -->
<!-- 智能体列表 -->
<div class="agents-container">
<el-tooltip
v-for="agentSelection in task.AgentSelection"
:key="agentSelection"
effect="light"
placement="right"
placement="top"
:show-after="500"
popper-class="task-syllabus-tooltip-popper"
>
@@ -137,6 +103,7 @@ const props = defineProps<{
id: string
data: TaskNodeData
isAddingBranch?: boolean
isBranchSelected?: boolean // 是否属于选中的分支路径
[key: string]: any
}>()
@@ -250,6 +217,19 @@ const handleBranchKeydown = (event: KeyboardEvent) => {
box-shadow: 0 0 0 2px rgba(64, 158, 255, 0.2);
}
// 分支选中高亮样式(绿色)
&.is-branch-selected {
border-color: #67c23a;
box-shadow: 0 0 0 3px rgba(103, 194, 58, 0.3);
background-color: rgba(103, 194, 58, 0.05);
&:hover {
border-color: #67c23a;
box-shadow: 0 0 0 3px rgba(103, 194, 58, 0.4);
background-color: rgba(103, 194, 58, 0.08);
}
}
&.is-editing {
cursor: default;
}

View File

@@ -1,203 +1,246 @@
// branch_PlanOutline 接口返回mock 数据
// 类型: IApiStepTask[][] (二维数组)
// branch_PlanOutline 接口的 Mock 数据和 Mock API
// 模拟后端返回的原始数据格式IRawPlanResponse
import type { IApiStepTask } from '@/stores/modules/agents'
import type { IRawPlanResponse, IRawStepTask } from '@/stores'
const mockPlanBranchData: IApiStepTask[][] = [
// 后端返回的数据格式
export type BranchPlanOutlineResponse = IRawPlanResponse
// Mock 数据:模拟后端返回的原始分支数据(不含 Collaboration_Brief_FrontEnd
// 注意:这里模拟的是 branch_PlanOutline 函数返回的数据,不是前端转换后的数据
const mockBranchDataRaw: IRawStepTask[][] = [
// 第一个分支方案
[
{
name: '需求分析与规划',
content: '分析用户需求,制定项目开发计划',
inputs: ['用户需求文档', '技术规范'],
output: '项目开发计划书',
agents: ['腐蚀机理研究员', '实验材料学家'],
brief: {
template: '!<项目经理>!负责!<需求分析>!!<产品经理>!负责!<规划制定>!',
data: {
: { text: '项目经理', style: { background: 'hsl(210, 70%, 50%)' } },
: { text: '需求分析', style: { background: 'hsl(120, 70%, 50%)' } },
: { text: '产品经理', style: { background: 'hsl(30, 70%, 50%)' } },
: { text: '规划制定', style: { background: 'hsl(300, 70%, 50%)' } },
},
StepName: '分析用户需求',
TaskContent: '分析用户需求,制定项目开发计划',
InputObject_List: ['腐蚀类型及成因列表'],
OutputObject: '项目开发计划书',
AgentSelection: ['腐蚀机理研究员', '实验材料学家'],
Collaboration_Brief_frontEnd: {
template: '',
data: {},
},
process: [
{
id: 'action-1',
type: '分析',
agent: '腐蚀机理研究员',
description: '详细分析用户需求文档',
inputs: ['用户需求文档'],
},
{
id: 'action-2',
type: '规划',
agent: '实验材料学家',
description: '制定项目开发计划',
inputs: ['技术规范'],
},
],
TaskProcess: [],
},
{
name: '系统设计与架构',
content: '设计系统架构和数据库结构',
inputs: ['项目开发计划书'],
output: '系统设计文档',
agents: ['腐蚀机理研究员', '防护工程专家'],
brief: {
template: '!<架构师>!负责!<系统架构设计>!!<数据库工程师>!负责!<数据库设计>!',
data: {
: { text: '架构师', style: { background: 'hsl(180, 70%, 50%)' } },
: { text: '系统架构设计', style: { background: 'hsl(240, 70%, 50%)' } },
: { text: '数据库工程师', style: { background: 'hsl(60, 70%, 50%)' } },
: { text: '数据库设计', style: { background: 'hsl(0, 70%, 50%)' } },
},
StepName: '系统设计与架构',
TaskContent: '设计系统架构和数据库结构',
InputObject_List: ['项目开发计划书'],
OutputObject: '系统设计文档',
AgentSelection: ['腐蚀机理研究员', '防护工程专家'],
Collaboration_Brief_frontEnd: {
template: '',
data: {},
},
process: [
{
id: 'action-3',
type: '设计',
agent: '腐蚀机理研究员',
description: '设计系统整体架构',
inputs: ['项目开发计划书'],
},
{
id: 'action-4',
type: '设计',
agent: '防护工程专家',
description: '设计数据库表结构',
inputs: ['项目开发计划书'],
},
],
TaskProcess: [],
},
],
// 第二个分支方案(替代方案)
// 第二个分支方案(快速原型方案)
[
{
name: '敏捷开发规划',
content: '采用敏捷开发方法制定迭代计划',
inputs: ['用户需求文档', '敏捷开发指南'],
output: '敏捷开发迭代计划',
agents: ['敏捷教练', '开发团队负责人'],
brief: {
template: '!<敏捷教练>!指导!<敏捷流程>!!<开发团队负责人>!制定!<迭代计划>!',
data: {
: { text: '敏捷教练', style: { background: 'hsl(270, 70%, 50%)' } },
: { text: '敏捷流程', style: { background: 'hsl(90, 70%, 50%)' } },
: { text: '开发团队负责人', style: { background: 'hsl(150, 70%, 50%)' } },
: { text: '迭代计划', style: { background: 'hsl(330, 70%, 50%)' } },
},
StepName: '需求快速原型',
TaskContent: '构建快速原型验证核心功能',
InputObject_List: ['腐蚀类型及成因列表'],
OutputObject: '原型系统',
AgentSelection: ['实验材料学家', '防护工程专家'],
Collaboration_Brief_frontEnd: {
template: '',
data: {},
},
process: [
{
id: 'action-5',
type: '指导',
agent: '敏捷教练',
description: '指导敏捷开发流程',
inputs: ['敏捷开发指南'],
},
{
id: 'action-6',
type: '规划',
agent: '开发团队负责人',
description: '制定迭代开发计划',
inputs: ['用户需求文档'],
},
],
TaskProcess: [],
},
{
name: '微服务架构设计',
content: '设计基于微服务的系统架构',
inputs: ['敏捷开发迭代计划'],
output: '微服务架构设计文档',
agents: ['微服务架构师', 'DevOps工程师'],
brief: {
template: '!<微服务架构师>!设计!<微服务架构>!!<DevOps工程师>!规划!<部署流程>!',
data: {
: { text: '微服务架构师', style: { background: 'hsl(210, 70%, 50%)' } },
: { text: '微服务架构', style: { background: 'hsl(120, 70%, 50%)' } },
DevOps工程师: { text: 'DevOps工程师', style: { background: 'hsl(30, 70%, 50%)' } },
: { text: '部署流程', style: { background: 'hsl(300, 70%, 50%)' } },
},
StepName: '原型测试与优化',
TaskContent: '测试原型并根据反馈快速迭代',
InputObject_List: ['原型系统'],
OutputObject: '优化后的原型',
AgentSelection: ['腐蚀机理研究员', '实验材料学家'],
Collaboration_Brief_frontEnd: {
template: '',
data: {},
},
process: [
{
id: 'action-7',
type: '设计',
agent: '微服务架构师',
description: '设计微服务拆分方案',
inputs: ['敏捷开发迭代计划'],
},
{
id: 'action-8',
type: '规划',
agent: 'DevOps工程师',
description: '规划CI/CD部署流程',
inputs: ['敏捷开发迭代计划'],
},
],
TaskProcess: [],
},
],
// 第三个分支方案(简化方案)
// 第三个分支方案(质量优先方案)
[
{
name: '快速原型开发',
content: '快速开发系统原型验证需求',
inputs: ['用户需求文档'],
output: '系统原型',
agents: ['全栈开发工程师'],
brief: {
template: '!<全栈开发工程师>!负责!<快速原型开发>!',
data: {
: { text: '全栈开发工程师', style: { background: 'hsl(180, 70%, 50%)' } },
: { text: '快速原型开发', style: { background: 'hsl(240, 70%, 50%)' } },
},
StepName: '需求深度分析',
TaskContent: '深入分析用户需求和技术可行性',
InputObject_List: ['腐蚀类型及成因列表'],
OutputObject: '详细需求分析报告',
AgentSelection: ['腐蚀机理研究员', '防护工程专家'],
Collaboration_Brief_frontEnd: {
template: '',
data: {},
},
process: [
{
id: 'action-9',
type: '开发',
agent: '全栈开发工程师',
description: '快速开发系统原型',
inputs: ['用户需求文档'],
},
],
TaskProcess: [],
},
{
name: '用户反馈收集',
content: '收集用户对原型的反馈意见',
inputs: ['系统原型'],
output: '用户反馈报告',
agents: ['产品经理', '用户体验设计师'],
brief: {
template: '!<产品经理>!收集!<用户反馈>!!<用户体验设计师>!分析!<用户体验>!',
data: {
: { text: '产品经理', style: { background: 'hsl(60, 70%, 50%)' } },
: { text: '用户反馈', style: { background: 'hsl(0, 70%, 50%)' } },
: { text: '用户体验设计师', style: { background: 'hsl(270, 70%, 50%)' } },
: { text: '用户体验', style: { background: 'hsl(90, 70%, 50%)' } },
},
StepName: '质量保障设计',
TaskContent: '设计质量保障体系和测试方案',
InputObject_List: ['详细需求分析报告'],
OutputObject: '质量保障方案',
AgentSelection: ['实验材料学家', '防护工程专家'],
Collaboration_Brief_frontEnd: {
template: '',
data: {},
},
process: [
{
id: 'action-10',
type: '收集',
agent: '产品经理',
description: '收集用户对原型的反馈',
inputs: ['系统原型'],
},
{
id: 'action-11',
type: '分析',
agent: '用户体验设计师',
description: '分析用户体验问题',
inputs: ['系统原型'],
},
],
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: [],
},
],
]
export default mockPlanBranchData
/**
* 模拟后端的 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: string[]
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

View File

@@ -1,189 +1,155 @@
// branch_TaskProcess 接口返回mock 数据
// 类型: IApiAgentAction[][] (二维数组)
// branch_TaskProcess 接口的 Mock 数据和 Mock API
export interface BranchAction {
ID: string
ActionType: string
AgentName: string
Description: string
ImportantInput: string[]
}
import type { IApiAgentAction } from '@/stores/modules/agents'
export type BranchTaskProcessResponse = BranchAction[][]
const mockTaskProcessData: IApiAgentAction[][] = [
// Mock 数据模拟后端返回的原始任务流程数据2D 数组)
// 格式:[[action1, action2], [action3, action4]]
const mockBranchTaskProcessDataRaw: BranchAction[][] = [
// 第一个任务分支方案
[
{
id: 'analyze-requirements-1',
type: '分析',
agent: '实验材料学家',
description: '详细分析用户需求文档',
inputs: ['用户需求文档'],
ID: 'agent3',
ActionType: 'Critique',
AgentName: '实验材料学家',
Description: '详细分析用户需求文档',
ImportantInput: ['agent2'],
},
{
id: 'design-architecture-1',
type: '设计',
agent: '腐蚀机理研究员',
description: '设计系统整体架构',
inputs: ['需求分析结果'],
ID: 'agent4',
ActionType: 'Critique',
AgentName: '腐蚀机理研究员',
Description: '设计系统整体架构',
ImportantInput: ['agent3'],
},
{
id: 'implement-core-1',
type: '实现',
agent: '防护工程专家',
description: '实现系统核心功能',
inputs: ['系统架构设计'],
ID: 'agent5',
ActionType: 'Improve',
AgentName: '防护工程专家',
Description: '实现系统核心功能',
ImportantInput: ['agent4'],
},
{
id: 'test-system-1',
type: '测试',
agent: '实验材料学家',
description: '进行系统集成测试',
inputs: ['系统核心功能'],
ID: 'agent6',
ActionType: 'Finalize',
AgentName: '实验材料学家',
Description: '进行系统集成测试',
ImportantInput: ['agent5'],
},
],
// 第二个任务分支方案
[
{
ID: 'agent7',
ActionType: 'Critique',
AgentName: '实验材料学家',
Description: '深入分析用户需求和技术约束',
ImportantInput: ['agent2'],
},
{
ID: 'agent8',
ActionType: 'Critique',
AgentName: '防护工程专家',
Description: '设计系统技术架构和数据流',
ImportantInput: ['agent8'],
},
{
ID: 'agent9',
ActionType: 'Improve',
AgentName: '腐蚀机理研究员',
Description: '评估系统安全性',
ImportantInput: ['agent4'],
},
{
ID: 'agent10',
ActionType: 'Finalize',
AgentName: '实验材料学家',
Description: '完成系统安全测试',
ImportantInput: ['agent9'],
},
],
// 第二个任务分支方案(替代方案)
//第三个任务分支方案
[
{
id: 'quick-prototype-2',
type: '原型',
agent: '实验材料学家',
description: '快速开发系统原型',
inputs: ['用户需求文档'],
ID: 'agent12',
ActionType: 'Critique',
AgentName: '腐蚀机理研究员',
Description: '设计系统整体架构',
ImportantInput: ['agent11'],
},
{
id: 'user-feedback-2',
type: '收集',
agent: '腐蚀机理研究员',
description: '收集用户对原型的反馈',
inputs: ['系统原型'],
ID: 'agent13',
ActionType: 'Improve',
AgentName: '防护工程专家',
Description: '实现系统核心功能',
ImportantInput: ['agent12'],
},
{
id: 'iterate-design-2',
type: '迭代',
agent: '防护工程专家',
description: '根据反馈迭代设计',
inputs: ['用户反馈'],
},
{
id: 'final-implement-2',
type: '实现',
agent: '实验材料学家',
description: '实现最终用户界面',
inputs: ['迭代设计稿'],
},
],
// 第三个任务分支方案(敏捷开发方案)
[
{
id: 'sprint-planning-3',
type: '规划',
agent: '实验材料学家',
description: '制定冲刺计划',
inputs: ['用户故事', '技术债务'],
},
{
id: 'code-review-3',
type: '评审',
agent: '防护工程专家',
description: '进行代码审查',
inputs: ['开发代码'],
},
{
id: 'unit-test-3',
type: '测试',
agent: '腐蚀机理研究员',
description: '编写单元测试',
inputs: ['功能代码'],
},
{
id: 'deploy-staging-3',
type: '部署',
agent: '实验材料学家',
description: '部署到测试环境',
inputs: ['测试通过代码'],
},
{
id: 'acceptance-test-3',
type: '验收',
agent: '腐蚀机理研究员',
description: '进行验收测试',
inputs: ['测试环境系统'],
},
],
// 第四个任务分支方案(微服务方案)
[
{
id: 'service-split-4',
type: '拆分',
agent: '实验材料学家',
description: '拆分单体应用为微服务',
inputs: ['单体应用代码'],
},
{
id: 'api-design-4',
type: '设计',
agent: '防护工程专家',
description: '设计微服务API接口',
inputs: ['服务拆分方案'],
},
{
id: 'service-implement-4',
type: '实现',
agent: '防护工程专家',
description: '实现各个微服务',
inputs: ['API设计文档'],
},
{
id: 'orchestration-4',
type: '编排',
agent: '腐蚀机理研究员',
description: '配置服务编排和负载均衡',
inputs: ['微服务实现'],
},
{
id: 'monitoring-setup-4',
type: '监控',
agent: '实验材料学家',
description: '设置监控和日志系统',
inputs: ['运行中的微服务'],
},
],
// 第五个任务分支方案(数据驱动方案)
[
{
id: 'data-analysis-5',
type: '分析',
agent: '腐蚀机理研究员',
description: '分析业务数据和需求',
inputs: ['业务数据', '用户行为数据'],
},
{
id: 'ml-model-5',
type: '建模',
agent: '实验材料学家',
description: '构建预测模型',
inputs: ['分析结果', '历史数据'],
},
{
id: 'data-pipeline-5',
type: '构建',
agent: '防护工程专家',
description: '构建数据处理流水线',
inputs: ['数据源', '模型需求'],
},
{
id: 'api-integration-5',
type: '集成',
agent: '实验材料学家',
description: '集成数据服务到应用',
inputs: ['数据处理流水线', '预测模型'],
},
{
id: 'performance-optimize-5',
type: '优化',
agent: '腐蚀机理研究员',
description: '优化系统性能',
inputs: ['运行数据', '性能指标'],
ID: 'agent14',
ActionType: 'Finalize',
AgentName: '实验材料学家',
Description: '进行系统集成测试',
ImportantInput: ['agent13'],
},
],
]
export default mockTaskProcessData
/**
* Mock API模拟后端 branch_TaskProcess 接口调用
*
* @param branch_Number - 分支数量
* @param Modification_Requirement - 修改需求
* @param Existing_Steps - 现有步骤名称列表
* @param Baseline_Completion - 基线完成度
* @param stepTaskExisting - 现有任务
* @param General_Goal - 总体目标
* @returns Promise<BranchAction[][]> - 返回 2D 数组,与后端格式完全一致
*/
export const mockBranchTaskProcessAPI = async (params: {
branch_Number: number
Modification_Requirement: string
Existing_Steps: string[]
Baseline_Completion: number
stepTaskExisting: any
General_Goal: string
}): Promise<BranchAction[][]> => {
// 模拟网络延迟 800ms
await new Promise((resolve) => setTimeout(resolve, 800))
console.log('[Mock API] branch_TaskProcess 调用参数:', params)
// 🆕 使用轮询方式选择分支方案(依次循环使用所有分支方案)
const totalBranches = mockBranchTaskProcessDataRaw.length
const sessionKey = `branch-task-process-index-${params.stepTaskExisting?.Id || 'default'}`
// 获取上一次的选择索引
let lastIndex = parseInt(sessionStorage.getItem(sessionKey) || '0')
// 计算本次的选择索引(轮询到下一个分支)
const selectedBranchIndex = (lastIndex + 1) % totalBranches
// 保存本次的选择索引
sessionStorage.setItem(sessionKey, selectedBranchIndex.toString())
const rawBranchData = mockBranchTaskProcessDataRaw[selectedBranchIndex] || []
console.log(
'[Mock API] branch_TaskProcess 选择分支方案:',
selectedBranchIndex + 1,
'/',
totalBranches,
)
console.log('[Mock API] branch_TaskProcess 返回数据:', rawBranchData)
// 直接返回 2D 数组,与后端格式完全一致
return [rawBranchData]
}
export default mockBranchTaskProcessAPI

View File

@@ -29,7 +29,7 @@ const scoreDimensions = ref<string[]>([])
interface AgentHeatmapData {
agentName: string
scores: number[] // 各维度的评分
scoreDetails?: Array<{ dimension: string; score: number; reason: string }> // 详细信息(可选)
scoreDetails?: Array<{ dimension: string; score: number; reason: string }> // 详细信息
}
// 所有agent的热力图数据
@@ -57,6 +57,9 @@ const searchValue = ref('')
// 标志位:防止重复初始化
const isInitializing = ref(false)
const isAddingDimension = ref(false)
const isLoadingConfirm = ref(false) // 确认 agent 组合时的加载状态
const isLoadingSelectGroup = ref(false) // 选择已保存组合时的加载状态
const isLoadingInitialTask = ref(false) // 首次加载任务时的加载状态
// 处理搜索提交
const handleSubmit = async () => {
@@ -143,19 +146,8 @@ const confirmAgentSelection = async () => {
if (selectedAgents.value.size > 0 && currentTask.value?.Id) {
const agentArray = Array.from(selectedAgents.value)
// 检查该agent组合是否已存在包括初始agent组合和用户添加的组合)
// 检查该agent组合是否已存在统一检查,包括初始组合和用户添加的组合)
const existingGroups = agentsStore.getConfirmedAgentGroups(currentTask.value.Id)
const initialGroup = currentTaskAgents.value
// 检查是否与初始组合相同
if (areAgentGroupsEqual(agentArray, initialGroup)) {
console.log('该agent组合与初始组合相同只选中卡片不添加重复')
selectedAssignmentGroup.value = [...initialGroup]
selectedAgents.value = new Set(initialGroup)
return
}
// 检查是否与已存在的用户添加组合相同
const existingDuplicateIndex = existingGroups.findIndex(group =>
areAgentGroupsEqual(agentArray, group)
)
@@ -176,7 +168,8 @@ const confirmAgentSelection = async () => {
// 调用 Mock API 填充任务流程
try {
console.log('=== 开始调用 fillStepTaskTaskProcess Mock API ===')
isLoadingConfirm.value = true
console.log('=== 开始调用 mockFillStepTaskTaskProcess Mock API ===')
console.log('1. 当前任务数据 (IRawStepTask 格式):', currentTask.value)
console.log('2. 选中的 agents:', agentArray)
@@ -218,12 +211,14 @@ const confirmAgentSelection = async () => {
console.log('✅ Mock API 调用成功,数据已存储到 selectionStore')
} catch (error) {
console.error('❌ Mock API 调用失败:', error)
} finally {
isLoadingConfirm.value = false
}
}
}
// 点击 Assignment 部分的 agent 组合卡片,更新 Comparison 部分的选中状态
const selectAgentGroup = (agentNames: string[]) => {
const selectAgentGroup = async (agentNames: string[]) => {
// 更新Assignment边框状态
selectedAssignmentGroup.value = [...agentNames]
@@ -237,6 +232,116 @@ const selectAgentGroup = (agentNames: string[]) => {
agentsStore.setSelectedAgentGroup(currentTask.value.Id, agentNames)
}
// 🆕 联动更新:更新 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 goal = agentsStore.agentRawPlan.data?.['General Goal'] || '开发智能协作系统'
console.log('📤 准备调用 API:', {
goal,
stepTask: stepTaskForApi,
agents: agentNames
})
// 调用 Mock API 加载数据
const filledTask = await api.mockFillStepTaskTaskProcess({
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)
return
} finally {
isLoadingSelectGroup.value = false
}
}
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 的"智能保留"逻辑阻止
agentsStore.updateCurrentAgentSelection(
[...agentNames],
convertedTaskProcess,
taskProcessData.brief || currentTask.value.Collaboration_Brief_frontEnd
)
console.log('✅ currentTask 已更新,新的 AgentSelection:', agentNames)
console.log('📋 验证更新结果:', agentsStore.currentTask)
}
}
// 重新排序被选中的agent排在前面其他agent按平均分降序
if (agentsHeatmapData.value.length > 0) {
agentsHeatmapData.value.sort((a, b) => {
@@ -406,18 +511,27 @@ const fetchAgentScores = async () => {
}
// TODO: 切换到真实API时取消注释下面这行
// const response = await api.agentSelectModifyInit({
// const agentScores = await api.agentSelectModifyInit({
// goal: agentsStore.agentRawPlan.data?.['General Goal'] || '',
// stepTask: currentTask.value
// })
// 使用 Mock API开发阶段
// 🆕 使用 Mock API开发阶段,传递当前任务的 AgentSelection 以获取对应的维度
const goal = agentsStore.agentRawPlan.data?.['General Goal'] || '开发智能协作系统'
console.log('📤 调用 mockAgentSelectModifyInit参数:', {
goal,
stepTask: currentTask.value,
agentSelection: currentTask.value?.AgentSelection
})
const agentScores = await api.mockAgentSelectModifyInit()
// 从转换后的数据中提取维度列表第一个agent的所有维度
const firstAgent = Object.keys(agentScores)[0]
const aspectList = firstAgent ? Object.keys(agentScores[firstAgent] || {}) : []
console.log('✅ 获取到的维度列表:', aspectList)
// 保存到 store
agentsStore.setAgentScoreData({
aspectList,
@@ -594,9 +708,10 @@ watch(
{ deep: true }
)
// 按需加载初始 agent 组合的 TaskProcess方案B
// 当用户第一次打开智能体探索窗口时,检查当前任务的初始 agent 组合是否已有 TaskProcess 数据
// 如果没有,则调用 fill_stepTask_TaskProcess API 获取并存储
// 初始化和任务切换处理
// 1. 首次加载时,自动将初始 agent 组合添加到 confirmedAgentGroups
// 2. 恢复之前的选择状态
// 3. 加载 TaskProcess 数据
watch(
() => currentTask.value,
async newTask => {
@@ -604,66 +719,82 @@ watch(
return
}
// 从 store 中恢复之前选择的 agent 组合
// 检查是否已经添加过初始组合
const existingGroups = agentsStore.getConfirmedAgentGroups(newTask.Id)
const hasInitialGroup = existingGroups.length > 0
// 首次初始化:自动添加初始组合(相当于系统自动执行了 confirmAgentSelection
if (!hasInitialGroup) {
console.log('🎯 首次初始化,自动添加初始 agent 组合到 Assignment')
agentsStore.addConfirmedAgentGroup(newTask.Id, [...newTask.AgentSelection])
// 调用 API 获取初始 agent 组合的 TaskProcess 数据
console.log('🔄 开始加载初始 agent 组合的 TaskProcess...')
// 检查是否已有数据
if (!selectionStore.hasAgentTaskProcess(newTask.Id, newTask.AgentSelection)) {
try {
isLoadingInitialTask.value = true
console.log('=== 开始加载初始 agent 组合的 TaskProcess ===')
console.log('1. 任务ID:', newTask.Id)
console.log('2. 初始 agents:', newTask.AgentSelection)
// 将 IRawStepTask 转换为 IApiStepTask 格式
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: []
}
console.log('3. 转换后的 API 请求参数:', stepTaskForApi)
const goal = agentsStore.agentRawPlan.data?.['General Goal'] || '开发智能协作系统'
console.log('4. General Goal:', goal)
// 调用 Mock API 获取 TaskProcess
const filledTask = await api.mockFillStepTaskTaskProcess({
goal,
stepTask: stepTaskForApi,
agents: newTask.AgentSelection
})
console.log('=== 初始 agent 组合 TaskProcess 加载成功 ===')
console.log('5. TaskProcess 流程数量:', filledTask.process?.length || 0)
// 存储到 selectionStore
selectionStore.setAgentTaskProcess(newTask.Id, newTask.AgentSelection, filledTask)
console.log('✅ 初始 agent 组合的 TaskProcess 已加载并存储')
} catch (error) {
console.error('❌ 加载初始 agent 组合的 TaskProcess 失败:', error)
} finally {
isLoadingInitialTask.value = false
}
} else {
console.log(' 初始 agent 组合已有 TaskProcess 数据,跳过加载')
}
}
// 从 store 中恢复之前选择的 agent 组合(如果有)
const savedAgentGroup = agentsStore.getSelectedAgentGroup(newTask.Id)
if (savedAgentGroup) {
console.log('📂 恢复之前选择的 agent 组合:', savedAgentGroup)
selectedAssignmentGroup.value = [...savedAgentGroup]
selectedAgents.value = new Set(savedAgentGroup)
} else {
// 没有保存的选择,使用默认的初始组合
selectedAssignmentGroup.value = [...newTask.AgentSelection]
selectedAgents.value = new Set(newTask.AgentSelection)
}
// 调用 API 获取初始 agent 组合的 TaskProcess 数据
console.log('🔄 开始加载初始 agent 组合的 TaskProcess...')
// 检查是否已有数据
if (selectionStore.hasAgentTaskProcess(newTask.Id, newTask.AgentSelection)) {
console.log(' 初始 agent 组合已有 TaskProcess 数据,跳过加载')
return
}
try {
console.log('=== 开始加载初始 agent 组合的 TaskProcess ===')
console.log('1. 任务ID:', newTask.Id)
console.log('2. 初始 agents:', newTask.AgentSelection)
// 将 IRawStepTask 转换为 IApiStepTask 格式
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 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])
}
console.log('3. 转换后的 API 请求参数:', stepTaskForApi)
const goal = agentsStore.agentRawPlan.data?.['General Goal'] || '开发智能协作系统'
console.log('4. General Goal:', goal)
// 调用 Mock API 获取 TaskProcess
const filledTask = await api.mockFillStepTaskTaskProcess({
goal,
stepTask: stepTaskForApi,
agents: newTask.AgentSelection
})
console.log('=== 初始 agent 组合 TaskProcess 加载成功 ===')
console.log('5. TaskProcess 流程数量:', filledTask.process?.length || 0)
// 存储到 selectionStore
selectionStore.setAgentTaskProcess(newTask.Id, newTask.AgentSelection, filledTask)
console.log('✅ 初始 agent 组合的 TaskProcess 已加载并存储')
} catch (error) {
console.error('❌ 加载初始 agent 组合的 TaskProcess 失败:', error)
}
},
{ immediate: true } // 立即执行一次
@@ -672,7 +803,7 @@ watch(
// 获取热力图颜色评分1-5颜色从浅到深
const getHeatmapColor = (score: number) => {
const colors = [
'#f0f0f0', // 1分 - 最浅
'#f7f7f7', // 1分 - 最浅
'#d4e5f7', // 2分
'#89b4e8', // 3分
'#4575b4', // 4分
@@ -686,49 +817,15 @@ const getHeatmapColor = (score: number) => {
<div class="agent-allocation-container">
<!-- 左侧区域 - 20% -->
<div class="allocation-left">
<div class="section-card">
<div class="section-card" v-loading="isLoadingSelectGroup">
<div class="section-title">Assignment</div>
<!-- 初始agent组合卡片 -->
<div
class="agents-grid"
:class="{ 'agent-group-selected': isAgentGroupSelected(currentTaskAgents) }"
@click="selectAgentGroup(currentTaskAgents)"
>
<el-tooltip
v-for="agent in currentTaskAgents"
:key="agent"
effect="light"
placement="top"
:teleported="false"
:show-after="500"
popper-class="agent-allocation-tooltip-popper"
>
<template #content>
<div class="agent-tooltip">
<div class="agent-tooltip-name">{{ agent }}</div>
<div class="agent-tooltip-separator"></div>
<div class="agent-tooltip-description">
{{
agentsStore.agents.find(a => a.Name === agent)?.Profile || '暂无描述'
}}
</div>
</div>
</template>
<div class="agent-item">
<div class="agent-icon" :style="{ background: getAgentMapIcon(agent).color }">
<svg-icon :icon-class="getAgentMapIcon(agent).icon" color="#fff" size="20px" />
</div>
</div>
</el-tooltip>
</div>
<!-- 确认的agent组合列表 -->
<!-- 所有 agent 组合卡片包括初始组合和用户创建的组合 -->
<div
v-for="(group, groupIndex) in confirmedAgentGroups"
:key="groupIndex"
class="agents-grid"
:class="{ 'agent-group-selected': isAgentGroupSelected(group) }"
style="margin-top: 12px"
:style="{ marginTop: groupIndex > 0 ? '12px' : '0' }"
@click="selectAgentGroup(group)"
>
<el-tooltip
@@ -767,7 +864,11 @@ const getHeatmapColor = (score: number) => {
<div class="allocation-right">
<div class="section-card">
<div class="section-title">Comparison</div>
<div v-if="allAgents.length > 0" class="comparison-content">
<div
v-if="allAgents.length > 0"
class="comparison-content"
v-loading="isInitializing || isLoadingInitialTask || isLoadingConfirm"
>
<!-- 热力矩阵图 -->
<div v-if="agentsHeatmapData.length > 0" class="heatmap-container">
<!-- 虚线选择框 - 包裹选中的agent头像 -->
@@ -777,7 +878,11 @@ const getHeatmapColor = (score: number) => {
:style="getSelectionBoxStyle()"
>
<!-- 确定按钮 -->
<div class="confirm-button" @click="confirmAgentSelection">
<div
class="confirm-button"
:class="{ 'is-loading': isLoadingConfirm }"
@click="confirmAgentSelection"
>
<svg-icon icon-class="Check" color="#328621" size="12px" />
</div>
</div>
@@ -874,7 +979,7 @@ const getHeatmapColor = (score: number) => {
</div>
<!-- 输入框区域 -->
<div class="search-input-container">
<div class="search-input-container" v-loading="isAddingDimension">
<el-input
v-model="searchValue"
placeholder="请输入新维度"
@@ -887,7 +992,7 @@ const getHeatmapColor = (score: number) => {
size="16px"
color="#409eff"
class="submit-icon"
:class="{ 'is-disabled': !searchValue }"
:class="{ 'is-disabled': !searchValue, 'is-loading': isAddingDimension }"
@click="searchValue && handleSubmit()"
/>
</template>
@@ -1039,6 +1144,25 @@ const getHeatmapColor = (score: number) => {
transform: scale(1.1);
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.3);
}
&.is-loading {
opacity: 0.6;
cursor: not-allowed;
pointer-events: none;
svg {
animation: spin 1s linear infinite;
}
}
}
@keyframes spin {
from {
transform: rotate(0deg);
}
to {
transform: rotate(360deg);
}
}
}
@@ -1102,16 +1226,19 @@ const getHeatmapColor = (score: number) => {
display: flex;
flex-direction: column;
justify-content: flex-start;
padding-top: 40px; // 对齐第一个评分单元格(跳过列头)
padding-top: 40px; // 对齐第一个评分单元格
flex-shrink: 0;
max-width: 200px; //
.dimension-label {
width: 60px;
min-width: 60px; // 改为最小宽度,允许根据内容自适应
width: auto; // 宽度自适应
height: 40px;
display: flex;
align-items: center;
justify-content: flex-start;
padding-left: 8px;
padding-right: 8px; // 添加右侧内边距,确保文字不会紧贴边缘
font-size: 12px;
font-weight: 600;
color: var(--color-text-title-header);
@@ -1119,6 +1246,7 @@ const getHeatmapColor = (score: number) => {
cursor: pointer;
user-select: none;
transition: all 0.2s ease;
white-space: nowrap; // 防止文字换行
&:hover {
color: #409eff;
@@ -1177,6 +1305,11 @@ const getHeatmapColor = (score: number) => {
opacity: 0.4;
cursor: not-allowed;
}
&.is-loading {
opacity: 0.7;
animation: spin 1s linear infinite;
}
}
}
}

View File

@@ -22,7 +22,7 @@ const agentGroupCount = computed(() => {
const confirmedGroups = agentsStore.getConfirmedAgentGroups(agentsStore.currentTask.Id)
// 当前任务agents(1) + 已确认的agent组合数量
return 1 + confirmedGroups.length
return confirmedGroups.length || 1
})
const handleClick = () => {

View File

@@ -11,9 +11,9 @@ const emit = defineEmits<{
// 获取分支数量 - 主分支(1) + 额外分支数量
const branchCount = computed(() => {
// flowBranches 包含所有通过 Branch 创建的分支
const extraBranches = selectionStore.flowBranches?.length || 0
const extraBranches = selectionStore.flowBranches?.length || 1
// 始终至少有1个主分支
return 1 + extraBranches
return extraBranches
})
const handleClick = () => {
@@ -26,7 +26,7 @@ const handleClick = () => {
<template>
<div
class="branch-button"
:class="{ 'has-branches': branchCount > 1 }"
:class="{ 'has-branches': branchCount > 0 }"
@click="handleClick"
:title="`${branchCount} 个分支`"
>

View File

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

View File

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

View File

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

View File

@@ -3,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 } from 'vue'
import { computed, ref, nextTick, watch, onMounted } from 'vue'
import { AnchorLocations } from '@jsplumb/browser-ui'
import MultiLineTooltip from '@/components/MultiLineTooltip/index.vue'
import Bg from './Bg.vue'
@@ -15,7 +15,6 @@ const planReady = computed(() => {
})
const openPlanModification = () => {
console.log('打开分支修改窗口')
agentsStore.openPlanModification()
}
@@ -202,6 +201,56 @@ function clear() {
jsplumb.reset()
}
// 🆕 封装连线重绘方法
const redrawConnections = () => {
console.log('🔄 重新绘制 jsplumb 连线')
// 等待 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)
console.log('✅ jsplumb 连线重绘完成,任务数:', collaborationProcess.value.length)
}, 100)
})
}
// 🆕 监听 collaborationProcess 变化,自动重绘连线
watch(
() => collaborationProcess,
() => {
console.log('🔍 collaborationProcess 发生变化,触发重绘')
redrawConnections()
},
{ deep: true }
)
// 🆕 组件挂载后初始化连线
onMounted(() => {
// 初始化时绘制连线
nextTick(() => {
setTimeout(() => {
const arr: ConnectArg[] = []
collaborationProcess.value.forEach(item => {
arr.push(...handleCurrentTask(item, true))
})
jsplumb.connects(arr)
console.log('✅ 初始化 jsplumb 连线完成')
}, 100)
})
})
defineExpose({
changeTask,
clear