feat(agent): 支持自定义API配置并优化UI交互
- 为agent.json添加apiUrl、apiKey、apiModel字段支持 - 更新API接口类型定义,支持传递自定义API配置 - 优化AgentRepoList组件UI样式和交互效果 - 增强JSON文件上传校验逻辑,支持API配置验证 - 改进任务结果页面布局和视觉呈现 - 添加任务过程查看抽屉功能 - 实现执行按钮动态样式和悬停效果 - 优化节点连接线渲染逻辑和性能
This commit is contained in:
@@ -3,10 +3,9 @@ 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 } from 'vue'
|
||||
import { computed, ref } from 'vue'
|
||||
import { AnchorLocations } from '@jsplumb/browser-ui'
|
||||
import MultiLineTooltip from '@/components/MultiLineTooltip/index.vue'
|
||||
|
||||
import Bg from './Bg.vue'
|
||||
|
||||
const emit = defineEmits<{
|
||||
@@ -26,6 +25,90 @@ const collaborationProcess = computed(() => {
|
||||
return agentsStore.agentRawPlan.data?.['Collaboration Process'] ?? []
|
||||
})
|
||||
|
||||
// 编辑状态管理
|
||||
const editingTaskId = ref<string | null>(null)
|
||||
const editingContent = ref('')
|
||||
|
||||
// 添加新产物状态管理
|
||||
const showAddOutputForm = ref(false)
|
||||
const newOutputName = ref('')
|
||||
|
||||
// 开始编辑
|
||||
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) {
|
||||
taskToUpdate.TaskContent = editingContent.value.trim()
|
||||
}
|
||||
}
|
||||
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()
|
||||
}
|
||||
}
|
||||
|
||||
// 显示添加产物表单
|
||||
const showAddOutputDialog = () => {
|
||||
showAddOutputForm.value = true
|
||||
newOutputName.value = ''
|
||||
}
|
||||
|
||||
// 添加新产物
|
||||
const addNewOutput = () => {
|
||||
if (newOutputName.value.trim()) {
|
||||
const success = agentsStore.addNewOutput(newOutputName.value.trim())
|
||||
if (success) {
|
||||
showAddOutputForm.value = false
|
||||
newOutputName.value = ''
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 取消添加产物
|
||||
const cancelAddOutput = () => {
|
||||
showAddOutputForm.value = false
|
||||
newOutputName.value = ''
|
||||
}
|
||||
|
||||
// 处理添加产物的键盘事件
|
||||
const handleAddOutputKeydown = (event: KeyboardEvent) => {
|
||||
if (event.key === 'Enter') {
|
||||
event.preventDefault()
|
||||
addNewOutput()
|
||||
} else if (event.key === 'Escape') {
|
||||
cancelAddOutput()
|
||||
}
|
||||
}
|
||||
|
||||
// 删除额外产物
|
||||
const removeAdditionalOutput = (output: string) => {
|
||||
agentsStore.removeAdditionalOutput(output)
|
||||
}
|
||||
|
||||
function handleCurrentTask(task: IRawStepTask, transparent: boolean): ConnectArg[] {
|
||||
// 创建当前流程与产出的连线
|
||||
const arr: ConnectArg[] = [
|
||||
@@ -34,14 +117,14 @@ function handleCurrentTask(task: IRawStepTask, transparent: boolean): ConnectArg
|
||||
targetId: `task-syllabus-output-object-${task.Id}`,
|
||||
anchor: [AnchorLocations.Right, AnchorLocations.Left],
|
||||
config: {
|
||||
transparent,
|
||||
},
|
||||
},
|
||||
transparent
|
||||
}
|
||||
}
|
||||
]
|
||||
|
||||
// 创建当前产出与流程的连线
|
||||
task.InputObject_List?.forEach((item) => {
|
||||
const id = collaborationProcess.value.find((i) => i.OutputObject === item)?.Id
|
||||
task.InputObject_List?.forEach(item => {
|
||||
const id = collaborationProcess.value.find(i => i.OutputObject === item)?.Id
|
||||
if (id) {
|
||||
arr.push({
|
||||
sourceId: `task-syllabus-output-object-${id}`,
|
||||
@@ -49,8 +132,8 @@ function handleCurrentTask(task: IRawStepTask, transparent: boolean): ConnectArg
|
||||
anchor: [AnchorLocations.Left, AnchorLocations.Right],
|
||||
config: {
|
||||
type: 'output',
|
||||
transparent,
|
||||
},
|
||||
transparent
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
@@ -61,7 +144,7 @@ function handleCurrentTask(task: IRawStepTask, transparent: boolean): ConnectArg
|
||||
function changeTask(task?: IRawStepTask, isEmit?: boolean) {
|
||||
jsplumb.reset()
|
||||
const arr: ConnectArg[] = []
|
||||
agentsStore.agentRawPlan.data?.['Collaboration Process']?.forEach((item) => {
|
||||
agentsStore.agentRawPlan.data?.['Collaboration Process']?.forEach(item => {
|
||||
arr.push(...handleCurrentTask(item, item.Id !== task?.Id))
|
||||
})
|
||||
jsplumb.connects(arr)
|
||||
@@ -76,26 +159,32 @@ function clear() {
|
||||
|
||||
defineExpose({
|
||||
changeTask,
|
||||
clear,
|
||||
clear
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="h-full flex flex-col">
|
||||
<div class="text-[18px] font-bold mb-[18px]">任务大纲</div>
|
||||
<div class="text-[18px] font-bold mb-[18px] text-[var(--color-text-title-header)]">
|
||||
任务大纲
|
||||
</div>
|
||||
|
||||
<div
|
||||
v-loading="agentsStore.agentRawPlan.loading"
|
||||
class="flex-1 w-full overflow-y-auto relative"
|
||||
@scroll="handleScroll"
|
||||
>
|
||||
<div v-show="collaborationProcess.length > 0" class="w-full relative min-h-full" id="task-syllabus">
|
||||
<div
|
||||
v-show="collaborationProcess.length > 0"
|
||||
class="w-full relative min-h-full"
|
||||
id="task-syllabus"
|
||||
>
|
||||
<Bg />
|
||||
|
||||
<div class="w-full flex items-center gap-[14%] mb-[35px]">
|
||||
<div class="flex-1 flex justify-center">
|
||||
<div
|
||||
class="card-item w-[45%] h-[41px] flex justify-center relative z-99 items-center rounded-[20px] bg-[var(--color-bg-tertiary)]"
|
||||
class="card-item w-[168px] h-[41px] flex justify-center relative z-99 items-center rounded-[20px] bg-[var(--color-bg-flow)]"
|
||||
>
|
||||
流程
|
||||
</div>
|
||||
@@ -103,7 +192,7 @@ defineExpose({
|
||||
|
||||
<div class="flex-1 flex justify-center">
|
||||
<div
|
||||
class="card-item w-[45%] h-[41px] flex justify-center relative z-99 items-center rounded-[20px] bg-[var(--color-bg-tertiary)]"
|
||||
class="card-item w-[168px] h-[41px] flex justify-center relative z-99 items-center rounded-[20px] bg-[var(--color-bg-flow)]"
|
||||
>
|
||||
产物
|
||||
</div>
|
||||
@@ -113,31 +202,50 @@ defineExpose({
|
||||
<div
|
||||
v-for="item in collaborationProcess"
|
||||
:key="item.Id"
|
||||
class="card-item w-full flex items-center gap-[14%]"
|
||||
class="card-it w-full flex items-center gap-[14%] bg-[var(--color-card-bg)]"
|
||||
>
|
||||
<!-- 流程卡片 -->
|
||||
<el-card
|
||||
class="w-[43%] overflow-y-auto relative z-99 task-syllabus-flow-card"
|
||||
:class="agentsStore.currentTask?.StepName === item.StepName ? 'active-card' : ''"
|
||||
shadow="hover"
|
||||
:shadow="true"
|
||||
:id="`task-syllabus-flow-${item.Id}`"
|
||||
@click="changeTask(item, true)"
|
||||
>
|
||||
<MultiLineTooltip placement="right" :text="item.StepName" :lines="2">
|
||||
<div class="text-[18px] font-bold text-center">
|
||||
{{ item.StepName }}
|
||||
</div>
|
||||
<div class="text-[18px] font-bold text-center">{{ item.StepName }}</div>
|
||||
</MultiLineTooltip>
|
||||
<div class="h-[1px] w-full bg-[#494B51] my-[8px]"></div>
|
||||
<MultiLineTooltip placement="right" :text="item.StepName" :lines="3">
|
||||
<div
|
||||
class="text-[14px] text-[var(--color-text-secondary)]"
|
||||
:title="item.TaskContent"
|
||||
>
|
||||
{{ item.TaskContent }}
|
||||
<div class="h-[1px] w-full bg-[#d6d6d6] 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 gap-2">
|
||||
<el-button @click="saveEditing" type="primary" size="small" class="px-3">
|
||||
√
|
||||
</el-button>
|
||||
<el-button @click="cancelEditing" size="small" class="px-3"> × </el-button>
|
||||
</div>
|
||||
</div>
|
||||
</MultiLineTooltip>
|
||||
<div class="h-[1px] w-full bg-[#494B51] my-[8px]"></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>
|
||||
|
||||
<div class="h-[1px] w-full bg-[#d6d6d6] my-[8px]"></div>
|
||||
<div
|
||||
class="flex items-center gap-2 overflow-y-auto flex-wrap relative w-full max-h-[72px]"
|
||||
>
|
||||
@@ -157,9 +265,7 @@ defineExpose({
|
||||
<div class="text-[18px] font-bold">{{ agentSelection }}</div>
|
||||
<div class="h-[1px] w-full bg-[#494B51] my-[8px]"></div>
|
||||
<div>
|
||||
{{
|
||||
item.TaskProcess.find((i) => i.AgentName === agentSelection)?.Description
|
||||
}}
|
||||
{{ item.TaskProcess.find(i => i.AgentName === agentSelection)?.Description }}
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
@@ -169,29 +275,106 @@ defineExpose({
|
||||
>
|
||||
<svg-icon
|
||||
:icon-class="getAgentMapIcon(agentSelection).icon"
|
||||
color="var(--color-text)"
|
||||
color="#fff"
|
||||
size="24px"
|
||||
/>
|
||||
</div>
|
||||
</el-tooltip>
|
||||
</div>
|
||||
</el-card>
|
||||
<!-- 产物卡片 -->
|
||||
<!-- 产物卡片 -->
|
||||
<el-card
|
||||
class="w-[43%] relative"
|
||||
shadow="hover"
|
||||
class="w-[43%] relative task-syllabus-output-object-card"
|
||||
:shadow="true"
|
||||
:class="agentsStore.currentTask?.StepName === item.StepName ? 'active-card' : ''"
|
||||
:id="`task-syllabus-output-object-${item.Id}`"
|
||||
>
|
||||
<div class="text-[18px] font-bold text-center">{{ item.OutputObject }}</div>
|
||||
</el-card>
|
||||
</div>
|
||||
|
||||
<!-- 额外的产物列表 -->
|
||||
<!-- <div
|
||||
v-for="(output, index) in agentsStore.additionalOutputs"
|
||||
:key="`additional-${index}`"
|
||||
class="card-item w-full flex items-center gap-[14%]"
|
||||
> -->
|
||||
<!-- 空的流程卡片位置 -->
|
||||
<!-- <div class="w-[43%]"></div> -->
|
||||
<!-- 额外产物卡片 -->
|
||||
<!-- <el-card class="w-[43%] relative additional-output-card group" :shadow="false">
|
||||
<div class="flex items-center justify-between">
|
||||
<div class="text-[18px] font-bold text-center flex-1">{{ output }}</div>
|
||||
<button
|
||||
@click="removeAdditionalOutput(output)"
|
||||
class="opacity-0 group-hover:opacity-100 text-red-500 hover:text-red-700 transition-all duration-200 ml-2 text-lg font-bold w-5 h-5 flex items-center justify-center rounded hover:bg-red-50"
|
||||
>
|
||||
×
|
||||
</button>
|
||||
</div>
|
||||
</el-card>
|
||||
</div> -->
|
||||
<!-- 添加新产物按钮 -->
|
||||
<!-- <div class="card-item w-full flex items-center gap-[14%] mt-[20px]"> -->
|
||||
<!-- 空的流程卡片位置 -->
|
||||
<!-- <div class="w-[43%]"></div> -->
|
||||
<!-- 添加新产物按钮 -->
|
||||
<!-- <div class="w-[43%] relative"> -->
|
||||
<!-- <div v-if="!showAddOutputForm" class="add-output-btn">
|
||||
<button
|
||||
@click="showAddOutputDialog"
|
||||
class="w-full h-[50px] border-2 border-dashed border-gray-300 rounded-lg flex items-center justify-center text-gray-500 hover:border-blue-500 hover:text-blue-500 transition-all duration-200 group"
|
||||
>
|
||||
<svg-icon icon-class="plus" size="20px" class="mr-2" />
|
||||
<span class="text-sm font-medium"></span>
|
||||
</button>
|
||||
</div> -->
|
||||
|
||||
<!-- 添加产物表单 -->
|
||||
<!-- <div v-else class="add-output-form">
|
||||
<el-card class="w-full" shadow="hover">
|
||||
<div class="p-3">
|
||||
<el-input
|
||||
v-model="newOutputName"
|
||||
placeholder="输入产物名称"
|
||||
size="small"
|
||||
@keydown="handleAddOutputKeydown"
|
||||
@blur="addNewOutput"
|
||||
class="w-full mb-3"
|
||||
/>
|
||||
<div class="flex gap-2">
|
||||
<el-button @click="addNewOutput" type="primary" size="small" class="flex-1">
|
||||
确定
|
||||
</el-button>
|
||||
<el-button @click="cancelAddOutput" size="small" class="flex-1">
|
||||
取消
|
||||
</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</el-card>
|
||||
</div> -->
|
||||
<!-- </div> -->
|
||||
<!-- </div> -->
|
||||
<!-- <div>
|
||||
<el-button type="info" size="medium" class="flex-1"> branch </el-button>
|
||||
</div> -->
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<style lang="scss" scoped>
|
||||
.task-syllabus-flow-card {
|
||||
background-color: var(--color-card-bg-task);
|
||||
border: 1px solid var(--color-card-border-task);
|
||||
box-sizing: border-box;
|
||||
transition: border-color 0.2s ease;
|
||||
// box-shadow: var(--color-card-shadow);
|
||||
margin-bottom: 100px;
|
||||
&:hover {
|
||||
background-color: var(--color-card-bg-task-hover);
|
||||
border-color: var(--color-card-border-hover);
|
||||
box-shadow: var(--color-card-shadow-hover);
|
||||
}
|
||||
:deep(.el-card__body) {
|
||||
height: 100%;
|
||||
display: flex;
|
||||
@@ -201,4 +384,150 @@ defineExpose({
|
||||
overflow: auto;
|
||||
}
|
||||
}
|
||||
|
||||
.task-syllabus-output-object-card {
|
||||
background-color: var(--color-card-bg-task);
|
||||
border: 1px solid var(--color-card-border-task);
|
||||
box-sizing: border-box;
|
||||
transition: border-color 0.2s ease;
|
||||
// box-shadow: var(--color-card-shadow-hover);
|
||||
&:hover {
|
||||
background-color: var(--color-card-bg-task-hover);
|
||||
border-color: var(--color-card-border-hover);
|
||||
box-shadow: var(--color-card-shadow-hover);
|
||||
}
|
||||
:deep(.el-card__body) {
|
||||
height: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
overflow: auto;
|
||||
}
|
||||
}
|
||||
|
||||
.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;
|
||||
}
|
||||
|
||||
.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;
|
||||
}
|
||||
|
||||
@keyframes slideDown {
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: translateY(-10px);
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: translateY(0);
|
||||
}
|
||||
}
|
||||
|
||||
// 添加产物表单样式
|
||||
:deep(.el-card__body) {
|
||||
padding: 16px;
|
||||
}
|
||||
|
||||
// 输入框样式
|
||||
:deep(.el-input__wrapper) {
|
||||
background: transparent;
|
||||
border: 1px solid #dcdfe6;
|
||||
border-radius: 4px;
|
||||
box-shadow: none;
|
||||
transition: all 0.2s ease;
|
||||
|
||||
&:hover {
|
||||
border-color: #c0c4cc;
|
||||
}
|
||||
|
||||
&.is-focus {
|
||||
border-color: #409eff;
|
||||
box-shadow: 0 0 0 2px rgba(64, 158, 255, 0.2);
|
||||
}
|
||||
}
|
||||
|
||||
:deep(.el-input__inner) {
|
||||
color: var(--color-text-primary);
|
||||
font-size: 14px;
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
// 任务内容编辑按钮样式 - 匹配执行结果编辑按钮样式
|
||||
.task-content-editor {
|
||||
.el-button {
|
||||
font-weight: bold;
|
||||
font-size: 16px;
|
||||
border-radius: 4px;
|
||||
|
||||
&.el-button--small {
|
||||
padding: 4px 12px;
|
||||
}
|
||||
}
|
||||
|
||||
// 在深色模式下,按钮背景会自动适配为深色
|
||||
html.dark & {
|
||||
.el-button {
|
||||
&.el-button--primary {
|
||||
background-color: var(--color-bg-detail);
|
||||
border-color: var(--color-border);
|
||||
color: var(--color-text);
|
||||
|
||||
&:hover {
|
||||
background-color: var(--color-bg-hover);
|
||||
border-color: var(--color-text-hover);
|
||||
}
|
||||
}
|
||||
|
||||
&:not(.el-button--primary) {
|
||||
background-color: var(--color-bg-detail);
|
||||
border-color: var(--color-border);
|
||||
color: var(--color-text);
|
||||
|
||||
&:hover {
|
||||
background-color: var(--color-bg-hover);
|
||||
border-color: var(--color-text-hover);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
Reference in New Issue
Block a user