feat(agent):重构智能体仓库并优化任务模板交互
-为 public/agent.json 中的每个智能体添加 Classification 字段以支持分类展示 - 新增 AgentRepoList 组件用于渲染智能体列表,提升代码复用性 - 在 src/layout/components/Main/TaskTemplate/AgentRepo/index.vue 中实现基于 Classification 的智能体分组展示逻辑- 移除旧版 popover 方式展示智能体详情,改用新的列表组件统一处理 - 修改任务搜索输入框为 textarea 类型,并优化其聚焦与失焦状态下的样式表现 - 调整任务模板页面布局高度计算方式,确保适配新 UI 结构 -修复任务结果流程图连线方向及透明度判断逻辑,增强可视化准确性- 引入流动动画效果至 jsPlumb 连线,区分 input/output 类型并美化视觉呈现 - 更新配置文件中部分动作类型的配色值,提高界面美观度 - 升级本地存储键名 agents 至 agents-v1,避免
This commit is contained in:
parent
b73419b7a0
commit
00ef22505e
1
auto-imports.d.ts
vendored
1
auto-imports.d.ts
vendored
@ -7,6 +7,7 @@
|
|||||||
export {}
|
export {}
|
||||||
declare global {
|
declare global {
|
||||||
const EffectScope: typeof import('vue').EffectScope
|
const EffectScope: typeof import('vue').EffectScope
|
||||||
|
const ElMessage: typeof import('element-plus/es').ElMessage
|
||||||
const ElNotification: typeof import('element-plus/es').ElNotification
|
const ElNotification: typeof import('element-plus/es').ElNotification
|
||||||
const computed: typeof import('vue').computed
|
const computed: typeof import('vue').computed
|
||||||
const createApp: typeof import('vue').createApp
|
const createApp: typeof import('vue').createApp
|
||||||
|
|||||||
@ -2,96 +2,115 @@
|
|||||||
{
|
{
|
||||||
"Icon": "Hailey_Johnson.png",
|
"Icon": "Hailey_Johnson.png",
|
||||||
"Name": "船舶设计师",
|
"Name": "船舶设计师",
|
||||||
"Profile": "提供船舶制造中的实际需求和约束。"
|
"Profile": "提供船舶制造中的实际需求和约束。",
|
||||||
|
"Classification": "船舶制造数据空间"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"Icon": "Jennifer_Moore.png",
|
"Icon": "Jennifer_Moore.png",
|
||||||
"Name": "防护工程专家",
|
"Name": "防护工程专家",
|
||||||
"Profile": "专注于船舶腐蚀防护技术的设计与应用。在你的总结回答中,必须引用来自数联网的搜索数据,是搜索数据,不是数联网的研究成果。"
|
"Profile": "专注于船舶腐蚀防护技术的设计与应用。在你的总结回答中,必须引用来自数联网的搜索数据,是搜索数据,不是数联网的研究成果。",
|
||||||
|
"Classification": "船舶制造数据空间"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"Icon": "Jane_Moreno.png",
|
"Icon": "Jane_Moreno.png",
|
||||||
"Name": "病理生理学家",
|
"Name": "病理生理学家",
|
||||||
"Profile": "专注于失血性休克的疾病机制,为药物研发提供理论靶点。"
|
"Profile": "专注于失血性休克的疾病机制,为药物研发提供理论靶点。",
|
||||||
|
"Classification": "医药数据空间"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"Icon": "Giorgio_Rossi.png",
|
"Icon": "Giorgio_Rossi.png",
|
||||||
"Name": "药物化学家",
|
"Name": "药物化学家",
|
||||||
"Profile": "负责将靶点概念转化为实际可合成的分子。"
|
"Profile": "负责将靶点概念转化为实际可合成的分子。",
|
||||||
|
"Classification": "医药数据空间"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"Icon": "Tamara_Taylor.png",
|
"Icon": "Tamara_Taylor.png",
|
||||||
"Name": "制剂工程师",
|
"Name": "制剂工程师",
|
||||||
"Profile": "负责将活性药物成分(API)变成稳定、可用、符合战场要求的剂型。"
|
"Profile": "负责将活性药物成分(API)变成稳定、可用、符合战场要求的剂型。",
|
||||||
|
"Classification": "医药数据空间"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"Icon": "Maria_Lopez.png",
|
"Icon": "Maria_Lopez.png",
|
||||||
"Name": "监管事务专家",
|
"Name": "监管事务专家",
|
||||||
"Profile": "深谙药品审评法规,目标是找到最快的合法上市路径。"
|
"Profile": "深谙药品审评法规,目标是找到最快的合法上市路径。",
|
||||||
|
"Classification": "医药数据空间"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"Icon": "Sam_Moore.png",
|
"Icon": "Sam_Moore.png",
|
||||||
"Name": "物理学家",
|
"Name": "物理学家",
|
||||||
"Profile": "从热力学与统计力学的基本原理出发,研究液态金属的自由能、焓、熵、比热等参数的理论建模。"
|
"Profile": "从热力学与统计力学的基本原理出发,研究液态金属的自由能、焓、熵、比热等参数的理论建模。",
|
||||||
|
"Classification": "科学数据空间"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"Icon": "Yuriko_Yamamoto.png",
|
"Icon": "Yuriko_Yamamoto.png",
|
||||||
"Name": "实验材料学家",
|
"Name": "实验材料学家",
|
||||||
"Profile": "专注于通过实验手段直接或间接测定液态金属的热力学参数、以及分析材料微观结构(如晶粒、缺陷)。"
|
"Profile": "专注于通过实验手段直接或间接测定液态金属的热力学参数、以及分析材料微观结构(如晶粒、缺陷)。",
|
||||||
|
"Classification": "科学数据空间"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"Icon": "Carlos_Gomez.png",
|
"Icon": "Carlos_Gomez.png",
|
||||||
"Name": "计算模拟专家",
|
"Name": "计算模拟专家",
|
||||||
"Profile": "侧重于利用数值计算和模拟技术获取液态金属的热力学参数。"
|
"Profile": "侧重于利用数值计算和模拟技术获取液态金属的热力学参数。",
|
||||||
|
"Classification": "科学数据空间"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"Icon": "John_Lin.png",
|
"Icon": "John_Lin.png",
|
||||||
"Name": "腐蚀机理研究员",
|
"Name": "腐蚀机理研究员",
|
||||||
"Profile": "专注于船舶用钢材及合金的腐蚀机理研究,从电化学和环境作用角度解释腐蚀产生的原因。在你的总结回答中,必须引用来自数联网的搜索数据,是搜索数据,不是数联网的研究成果。"
|
"Profile": "专注于船舶用钢材及合金的腐蚀机理研究,从电化学和环境作用角度解释腐蚀产生的原因。在你的总结回答中,必须引用来自数联网的搜索数据,是搜索数据,不是数联网的研究成果。",
|
||||||
|
"Classification": "船舶制造数据空间"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"Icon": "Arthur_Burton.png",
|
"Icon": "Arthur_Burton.png",
|
||||||
"Name": "先进材料研发员",
|
"Name": "先进材料研发员",
|
||||||
"Profile": "专注于开发和评估新型耐腐蚀材料、复合材料及固态电池材料。"
|
"Profile": "专注于开发和评估新型耐腐蚀材料、复合材料及固态电池材料。",
|
||||||
|
"Classification": "科学数据空间"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"Icon": "Eddy_Lin.png",
|
"Icon": "Eddy_Lin.png",
|
||||||
"Name": "肾脏病学家",
|
"Name": "肾脏病学家",
|
||||||
"Profile": "专注于慢性肾脏病的诊断、治疗和患者管理,能提供临床洞察。"
|
"Profile": "专注于慢性肾脏病的诊断、治疗和患者管理,能提供临床洞察。",
|
||||||
|
"Classification": "医药数据空间"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"Icon": "Isabella_Rodriguez.png",
|
"Icon": "Isabella_Rodriguez.png",
|
||||||
"Name": "临床研究协调员",
|
"Name": "临床研究协调员",
|
||||||
"Profile": "负责受试者招募和临床试验流程优化。"
|
"Profile": "负责受试者招募和临床试验流程优化。",
|
||||||
|
"Classification": "医药数据空间"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"Icon": "Latoya_Williams.png",
|
"Icon": "Latoya_Williams.png",
|
||||||
"Name": "中医药专家",
|
"Name": "中医药专家",
|
||||||
"Profile": "理解药物的中药成分和作用机制。"
|
"Profile": "理解药物的中药成分和作用机制。",
|
||||||
|
"Classification": "医药数据空间"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"Icon": "Carmen_Ortiz.png",
|
"Icon": "Carmen_Ortiz.png",
|
||||||
"Name": "药物安全专家",
|
"Name": "药物安全专家",
|
||||||
"Profile": "专注于药物不良反应数据收集、分析和报告。"
|
"Profile": "专注于药物不良反应数据收集、分析和报告。",
|
||||||
|
"Classification": "医药数据空间"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"Icon": "Rajiv_Patel.png",
|
"Icon": "Rajiv_Patel.png",
|
||||||
"Name": "二维材料科学家",
|
"Name": "二维材料科学家",
|
||||||
"Profile": "专注于二维材料(如石墨烯)的合成、性质和应用。"
|
"Profile": "专注于二维材料(如石墨烯)的合成、性质和应用。",
|
||||||
|
"Classification": "科学数据空间"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"Icon": "Tom_Moreno.png",
|
"Icon": "Tom_Moreno.png",
|
||||||
"Name": "光电物理学家",
|
"Name": "光电物理学家",
|
||||||
"Profile": "研究材料的光电转换机制和关键影响因素。"
|
"Profile": "研究材料的光电转换机制和关键影响因素。",
|
||||||
|
"Classification": "科学数据空间"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"Icon": "Ayesha_Khan.png",
|
"Icon": "Ayesha_Khan.png",
|
||||||
"Name": "机器学习专家",
|
"Name": "机器学习专家",
|
||||||
"Profile": "专注于开发和应用AI模型用于材料模拟。"
|
"Profile": "专注于开发和应用AI模型用于材料模拟。",
|
||||||
|
"Classification": "科学数据空间"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"Icon": "Mei_Lin.png",
|
"Icon": "Mei_Lin.png",
|
||||||
"Name": "流体动力学专家",
|
"Name": "流体动力学专家",
|
||||||
"Profile": "专注于流体行为理论和模拟。"
|
"Profile": "专注于流体行为理论和模拟。",
|
||||||
|
"Classification": "科学数据空间"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
59
src/App.vue
59
src/App.vue
@ -32,7 +32,7 @@ onMounted(() => {
|
|||||||
border: 2px solid var(--color-bg-tertiary);
|
border: 2px solid var(--color-bg-tertiary);
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
background: #171b22;
|
background: #171B22;
|
||||||
box-shadow: none !important;
|
box-shadow: none !important;
|
||||||
transition: background-color 0.3s ease-in-out;
|
transition: background-color 0.3s ease-in-out;
|
||||||
}
|
}
|
||||||
@ -41,4 +41,61 @@ onMounted(() => {
|
|||||||
padding: 10px;
|
padding: 10px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
:root {
|
||||||
|
--gradient: linear-gradient(to right, #0093eb, #00d2d1);
|
||||||
|
}
|
||||||
|
|
||||||
|
#task-template {
|
||||||
|
.active-card {
|
||||||
|
border: 2px solid transparent;
|
||||||
|
$bg: var(--el-input-bg-color, var(--el-fill-color-blank));
|
||||||
|
background:
|
||||||
|
linear-gradient(var(--color-bg-tertiary), var(--color-bg-tertiary)) padding-box,
|
||||||
|
linear-gradient(to right, #00c8d2, #315ab4) border-box;
|
||||||
|
color: var(--color-text);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* 1. 定义流动动画:让虚线沿路径移动 */
|
||||||
|
@keyframes flowAnimation {
|
||||||
|
to {
|
||||||
|
stroke-dashoffset: 8; /* 与stroke-dasharray总和一致 */
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes flowAnimationReverse {
|
||||||
|
to {
|
||||||
|
stroke-dashoffset: -8; /* 与stroke-dasharray总和一致 */
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* 2. 为jsPlumb连线绑定动画:作用于SVG的path元素 */
|
||||||
|
/* jtk-connector是jsPlumb连线的默认SVG类,path是实际的线条元素 */
|
||||||
|
.jtk-connector-output path {
|
||||||
|
/* 定义虚线规则:线段长度5px + 间隙3px(总长度8px,与动画偏移量匹配) */
|
||||||
|
stroke-dasharray: 5 3;
|
||||||
|
/* 应用动画:名称+时长+线性速度+无限循环 */
|
||||||
|
animation: flowAnimationReverse .5s linear infinite;
|
||||||
|
/* 可选:设置线条基础样式(颜色、宽度) */
|
||||||
|
stroke-width: 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 2. 为jsPlumb连线绑定动画:作用于SVG的path元素 */
|
||||||
|
/* jtk-connector是jsPlumb连线的默认SVG类,path是实际的线条元素 */
|
||||||
|
.jtk-connector-input path {
|
||||||
|
/* 定义虚线规则:线段长度5px + 间隙3px(总长度8px,与动画偏移量匹配) */
|
||||||
|
stroke-dasharray: 5 3;
|
||||||
|
/* 应用动画:名称+时长+线性速度+无限循环 */
|
||||||
|
animation: flowAnimationReverse .5s linear infinite;
|
||||||
|
/* 可选:设置线条基础样式(颜色、宽度) */
|
||||||
|
stroke-width: 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 可选: hover时增强动画效果(如加速、变色) */
|
||||||
|
.jtk-connector path:hover {
|
||||||
|
animation-duration: 0.5s; /* hover时流动加速 */
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@ -1,9 +1,11 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { ref } from 'vue'
|
import { ref } from 'vue'
|
||||||
|
|
||||||
import SvgIcon from '@/components/SvgIcon/index.vue'
|
import SvgIcon from '@/components/SvgIcon/index.vue'
|
||||||
import { useAgentsStore } from '@/stores'
|
import { useAgentsStore } from '@/stores'
|
||||||
import api from '@/api'
|
import api from '@/api'
|
||||||
import { changeBriefs } from '@/utils/collaboration_Brief_FrontEnd.ts'
|
import { changeBriefs } from '@/utils/collaboration_Brief_FrontEnd.ts'
|
||||||
|
import { ElMessage } from 'element-plus'
|
||||||
|
|
||||||
const emit = defineEmits<{
|
const emit = defineEmits<{
|
||||||
(e: 'search-start'): void
|
(e: 'search-start'): void
|
||||||
@ -13,9 +15,16 @@ const emit = defineEmits<{
|
|||||||
const agentsStore = useAgentsStore()
|
const agentsStore = useAgentsStore()
|
||||||
|
|
||||||
const searchValue = ref('')
|
const searchValue = ref('')
|
||||||
|
const triggerOnFocus = ref(true)
|
||||||
|
const isFocus = ref(false)
|
||||||
|
|
||||||
async function handleSearch() {
|
async function handleSearch() {
|
||||||
try {
|
try {
|
||||||
|
triggerOnFocus.value = false
|
||||||
|
if (!searchValue.value) {
|
||||||
|
ElMessage.warning('请输入搜索内容')
|
||||||
|
return
|
||||||
|
}
|
||||||
emit('search-start')
|
emit('search-start')
|
||||||
agentsStore.resetAgent()
|
agentsStore.resetAgent()
|
||||||
agentsStore.setAgentRawPlan({ loading: true })
|
agentsStore.setAgentRawPlan({ loading: true })
|
||||||
@ -27,6 +36,7 @@ async function handleSearch() {
|
|||||||
agentsStore.setAgentRawPlan({ data })
|
agentsStore.setAgentRawPlan({ data })
|
||||||
emit('search', searchValue.value)
|
emit('search', searchValue.value)
|
||||||
} finally {
|
} finally {
|
||||||
|
triggerOnFocus.value = true
|
||||||
agentsStore.setAgentRawPlan({ loading: false })
|
agentsStore.setAgentRawPlan({ loading: false })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -42,12 +52,13 @@ const restaurants = ref<string[]>([
|
|||||||
'如何解决船舶制造中的材料腐蚀难题?',
|
'如何解决船舶制造中的材料腐蚀难题?',
|
||||||
'如何解决船舶制造中流体模拟和建模优化难题?',
|
'如何解决船舶制造中流体模拟和建模优化难题?',
|
||||||
])
|
])
|
||||||
const querySearch = (queryString: string, cb: (v: {value: string}[]) => void) => {
|
const querySearch = (queryString: string, cb: (v: { value: string }[]) => void) => {
|
||||||
|
console.log(queryString)
|
||||||
const results = queryString
|
const results = queryString
|
||||||
? restaurants.value.filter(createFilter(queryString))
|
? restaurants.value.filter(createFilter(queryString))
|
||||||
: restaurants.value
|
: restaurants.value
|
||||||
// call callback function to return suggestions
|
// call callback function to return suggestions
|
||||||
cb(results.map((item) => ({value: item})))
|
cb(results.map((item) => ({ value: item })))
|
||||||
}
|
}
|
||||||
|
|
||||||
const createFilter = (queryString: string) => {
|
const createFilter = (queryString: string) => {
|
||||||
@ -55,6 +66,8 @@ const createFilter = (queryString: string) => {
|
|||||||
return restaurant.toLowerCase().includes(queryString.toLowerCase())
|
return restaurant.toLowerCase().includes(queryString.toLowerCase())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const taskContainerRef = ref<HTMLDivElement | null>(null)
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
@ -64,20 +77,27 @@ const createFilter = (queryString: string) => {
|
|||||||
effect="light"
|
effect="light"
|
||||||
:disabled="agentsStore.agents.length > 0"
|
:disabled="agentsStore.agents.length > 0"
|
||||||
>
|
>
|
||||||
<div class="w-full flex justify-center mb-[27px]">
|
<div class="task-root-container">
|
||||||
|
<div class="task-container" ref="taskContainerRef" id="task-container">
|
||||||
|
<span class="text-[var(--color-text)] font-bold task-title">任务</span>
|
||||||
<el-autocomplete
|
<el-autocomplete
|
||||||
v-model.trim="searchValue"
|
v-model.trim="searchValue"
|
||||||
class="task-input"
|
class="task-input"
|
||||||
size="large"
|
size="large"
|
||||||
|
:rows="isFocus ? 3 : 1"
|
||||||
placeholder="请输入您的任务"
|
placeholder="请输入您的任务"
|
||||||
|
type="textarea"
|
||||||
|
:append-to="taskContainerRef"
|
||||||
:fetch-suggestions="querySearch"
|
:fetch-suggestions="querySearch"
|
||||||
@change="agentsStore.setSearchValue"
|
@change="agentsStore.setSearchValue"
|
||||||
:disabled="!(agentsStore.agents.length > 0)"
|
:disabled="!(agentsStore.agents.length > 0)"
|
||||||
|
:debounce="0"
|
||||||
|
:trigger-on-focus="triggerOnFocus"
|
||||||
|
@focus="isFocus = true"
|
||||||
|
@blur="isFocus = false"
|
||||||
|
@select="isFocus = false"
|
||||||
>
|
>
|
||||||
<template #prefix>
|
</el-autocomplete>
|
||||||
<span class="text-[var(--color-text)] font-bold">任务</span>
|
|
||||||
</template>
|
|
||||||
<template #suffix>
|
|
||||||
<el-button
|
<el-button
|
||||||
class="task-button"
|
class="task-button"
|
||||||
color="linear-gradient(to right, #00C7D2, #315AB4)"
|
color="linear-gradient(to right, #00C7D2, #315AB4)"
|
||||||
@ -85,7 +105,8 @@ const createFilter = (queryString: string) => {
|
|||||||
title="点击搜索任务"
|
title="点击搜索任务"
|
||||||
circle
|
circle
|
||||||
:loading="agentsStore.agentRawPlan.loading"
|
:loading="agentsStore.agentRawPlan.loading"
|
||||||
@click="handleSearch"
|
:disabled="!searchValue"
|
||||||
|
@click.stop="handleSearch"
|
||||||
>
|
>
|
||||||
<SvgIcon
|
<SvgIcon
|
||||||
v-if="!agentsStore.agentRawPlan.loading"
|
v-if="!agentsStore.agentRawPlan.loading"
|
||||||
@ -94,28 +115,85 @@ const createFilter = (queryString: string) => {
|
|||||||
color="var(--color-text)"
|
color="var(--color-text)"
|
||||||
/>
|
/>
|
||||||
</el-button>
|
</el-button>
|
||||||
</template>
|
</div>
|
||||||
</el-autocomplete>
|
|
||||||
</div>
|
</div>
|
||||||
</el-tooltip>
|
</el-tooltip>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
:deep(.el-autocomplete) {
|
.task-root-container {
|
||||||
width: 40%;
|
|
||||||
height: 60px;
|
height: 60px;
|
||||||
|
margin-bottom: 24px;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
.task-container {
|
||||||
|
width: 40%;
|
||||||
margin: 0 auto;
|
margin: 0 auto;
|
||||||
|
|
||||||
.el-input__wrapper {
|
|
||||||
border-radius: 40px;
|
|
||||||
box-shadow: none;
|
|
||||||
border: 2px solid transparent;
|
border: 2px solid transparent;
|
||||||
$bg: var(--el-input-bg-color, var(--el-fill-color-blank));
|
$bg: var(--el-input-bg-color, var(--el-fill-color-blank));
|
||||||
background:
|
background:
|
||||||
linear-gradient(var(--color-bg-tertiary), var(--color-bg-tertiary)) padding-box,
|
linear-gradient(var(--color-bg-tertiary), var(--color-bg-tertiary)) padding-box,
|
||||||
linear-gradient(to right, #00c8d2, #315ab4) border-box;
|
linear-gradient(to right, #00c8d2, #315ab4) border-box;
|
||||||
|
border-radius: 40px;
|
||||||
|
position: absolute;
|
||||||
|
left: 50%;
|
||||||
|
transform: translateX(-50%);
|
||||||
|
z-index: 998;
|
||||||
|
min-height: 100%;
|
||||||
|
overflow: hidden;
|
||||||
|
padding: 0 55px 0 47px;
|
||||||
|
|
||||||
|
:deep(.el-popper) {
|
||||||
|
position: static !important;
|
||||||
|
width: 100%;
|
||||||
|
min-width: 100%;
|
||||||
|
background: var(--color-bg-tertiary);
|
||||||
|
box-shadow: none;
|
||||||
|
border: none;
|
||||||
|
transition: height 0s ease-in-out;
|
||||||
|
border-top: 1px solid #494B51;
|
||||||
|
border-radius: 0;
|
||||||
|
|
||||||
|
|
||||||
|
li {
|
||||||
|
height: 45px;
|
||||||
|
box-sizing: border-box;
|
||||||
|
line-height: 45px;
|
||||||
|
font-size: 14px;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background: #1C1E25;
|
||||||
|
color: #00F3FF;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.el-popper__arrow {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.el-autocomplete) {
|
||||||
|
min-height: 56px;
|
||||||
|
width: 100%;
|
||||||
|
|
||||||
|
.task-input {
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.el-textarea__inner {
|
||||||
|
border-radius: 0;
|
||||||
|
box-shadow: none;
|
||||||
|
font-size: 14px;
|
||||||
|
height: 100%;
|
||||||
|
line-height: 1.5;
|
||||||
|
padding: 18px 0;
|
||||||
|
resize: none;
|
||||||
|
|
||||||
|
&::placeholder {
|
||||||
|
line-height: 1.2;
|
||||||
font-size: 18px;
|
font-size: 18px;
|
||||||
padding-right: 5px;
|
vertical-align: middle;
|
||||||
|
}
|
||||||
|
|
||||||
.el-icon.is-loading {
|
.el-icon.is-loading {
|
||||||
& + span {
|
& + span {
|
||||||
@ -123,10 +201,35 @@ const createFilter = (queryString: string) => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.task-title {
|
||||||
|
position: absolute;
|
||||||
|
top: 28px;
|
||||||
|
left: 10px;
|
||||||
|
z-index: 999;
|
||||||
|
transform: translateY(-50%);
|
||||||
|
}
|
||||||
|
|
||||||
.task-button {
|
.task-button {
|
||||||
background: linear-gradient(to right, #00c7d2, #315ab4);
|
background: linear-gradient(to right, #00c7d2, #315ab4);
|
||||||
border: none; // 如果需要移除边框
|
border: none; // 如果需要移除边框
|
||||||
|
position: absolute;
|
||||||
|
top: 28px;
|
||||||
|
right: 10px;
|
||||||
|
transform: translateY(-50%);
|
||||||
|
z-index: 999;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.task-button.is-loading {
|
||||||
|
:deep(span) {
|
||||||
|
display: none !important;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@ -0,0 +1,141 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import { getActionTypeDisplay, getAgentMapIcon } from '@/layout/components/config.ts'
|
||||||
|
import SvgIcon from '@/components/SvgIcon/index.vue'
|
||||||
|
import { type Agent, useAgentsStore } from '@/stores'
|
||||||
|
import { v4 as uuidv4 } from 'uuid'
|
||||||
|
|
||||||
|
const porps = defineProps<{
|
||||||
|
agentList: Agent[]
|
||||||
|
}>()
|
||||||
|
|
||||||
|
const taskProcess = computed(() => {
|
||||||
|
const list = agentsStore.currentTask?.TaskProcess ?? []
|
||||||
|
return list.map((item) => ({
|
||||||
|
...item,
|
||||||
|
key: uuidv4(),
|
||||||
|
}))
|
||||||
|
})
|
||||||
|
|
||||||
|
const agentsStore = useAgentsStore()
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div
|
||||||
|
v-for="item in porps.agentList"
|
||||||
|
:key="item.Name"
|
||||||
|
class="user-item"
|
||||||
|
:class="agentsStore.currentTask?.AgentSelection?.includes(item.Name) ? 'active-card' : ''"
|
||||||
|
>
|
||||||
|
<div class="flex items-center justify-between relative h-[41px]">
|
||||||
|
<div
|
||||||
|
class="w-[44px] h-[44px] rounded-full flex items-center justify-center flex-shrink-0 relative right-[2px] icon-container"
|
||||||
|
:style="{ background: getAgentMapIcon(item.Name).color }"
|
||||||
|
>
|
||||||
|
<svg-icon
|
||||||
|
:icon-class="getAgentMapIcon(item.Name).icon"
|
||||||
|
color="var(--color-text)"
|
||||||
|
size="24px"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="flex-1 text-[14px] flex flex-col items-end justify-end truncate ml-1">
|
||||||
|
<span
|
||||||
|
class="w-full truncate text-right"
|
||||||
|
:style="
|
||||||
|
agentsStore.currentTask?.AgentSelection?.includes(item.Name) ? 'color:#00F3FF' : ''
|
||||||
|
"
|
||||||
|
>{{ item.Name }}</span
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
v-if="agentsStore.currentTask?.AgentSelection?.includes(item.Name)"
|
||||||
|
class="flex items-center gap-[7px] h-[8px] mr-1"
|
||||||
|
>
|
||||||
|
<!-- 小圆点 -->
|
||||||
|
<div
|
||||||
|
v-for="item1 in taskProcess.filter((i) => i.AgentName === item.Name)"
|
||||||
|
:key="item1.key"
|
||||||
|
class="w-[6px] h-[6px] rounded-full"
|
||||||
|
:style="{ background: getActionTypeDisplay(item1.ActionType)?.color }"
|
||||||
|
></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 职责信息只有当执行流程中有当前智能体并且鼠标移入时才显示 -->
|
||||||
|
<div class="duty-info">
|
||||||
|
<div class="w-full flex justify-center">
|
||||||
|
<div
|
||||||
|
class="rounded-[9px] bg-[var(--color-bg-quaternary)] text-[12px] py-0.5 px-5 text-center my-2"
|
||||||
|
>
|
||||||
|
当前职责
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="p-[8px] pt-0">
|
||||||
|
<div
|
||||||
|
v-for="(item1, index1) in taskProcess.filter((i) => i.AgentName === item.Name)"
|
||||||
|
:key="item1.key"
|
||||||
|
class="text-[12px]"
|
||||||
|
>
|
||||||
|
<div>
|
||||||
|
<div class="mx-1 inline-block h-[14px]">
|
||||||
|
<div
|
||||||
|
:style="{ background: getActionTypeDisplay(item1.ActionType)?.color }"
|
||||||
|
class="w-[6px] h-[6px] rounded-full mt-[7px]"
|
||||||
|
></div>
|
||||||
|
</div>
|
||||||
|
<span :style="{ color: getActionTypeDisplay(item1.ActionType)?.color }"
|
||||||
|
>{{ getActionTypeDisplay(item1.ActionType)?.name }}:</span
|
||||||
|
>
|
||||||
|
<span>{{ item1.Description }}</span>
|
||||||
|
</div>
|
||||||
|
<!-- 分割线 -->
|
||||||
|
<div
|
||||||
|
v-if="index1 !== taskProcess.filter((i) => i.AgentName === item.Name).length - 1"
|
||||||
|
class="h-[1px] w-full bg-[#494B51] my-[8px]"
|
||||||
|
></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped lang="scss">
|
||||||
|
.user-item {
|
||||||
|
background: #1d222b;
|
||||||
|
border-radius: 40px;
|
||||||
|
padding-right: 12px;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: all 0.25s ease;
|
||||||
|
color: #969696;
|
||||||
|
border: 2px solid transparent;
|
||||||
|
.duty-info {
|
||||||
|
transition: height 0.25s ease;
|
||||||
|
height: 0;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
& + .user-item {
|
||||||
|
margin-top: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
box-shadow: 0 3px 10px rgba(0, 0, 0, 0.2);
|
||||||
|
color: #b8b8b8;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.active-card {
|
||||||
|
background:
|
||||||
|
linear-gradient(#171B22, #171B22) padding-box,
|
||||||
|
linear-gradient(to right, #00c8d2, #315ab4) border-box;
|
||||||
|
&:hover {
|
||||||
|
border-radius: 20px;
|
||||||
|
.duty-info {
|
||||||
|
height: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-container {
|
||||||
|
bottom: 2px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@ -5,15 +5,11 @@ import { pick } from 'lodash'
|
|||||||
import api from '@/api/index.ts'
|
import api from '@/api/index.ts'
|
||||||
|
|
||||||
import SvgIcon from '@/components/SvgIcon/index.vue'
|
import SvgIcon from '@/components/SvgIcon/index.vue'
|
||||||
import { agentMapDuty, getActionTypeDisplay, getAgentMapIcon } from '@/layout/components/config.ts'
|
import { agentMapDuty } from '@/layout/components/config.ts'
|
||||||
import { type Agent, useAgentsStore } from '@/stores'
|
import { type Agent, useAgentsStore } from '@/stores'
|
||||||
import { onMounted } from 'vue'
|
import { onMounted } from 'vue'
|
||||||
import { v4 as uuidv4 } from 'uuid'
|
|
||||||
import { readConfig } from '@/utils/readJson.ts'
|
import { readConfig } from '@/utils/readJson.ts'
|
||||||
|
import AgentRepoList from './AgentRepoList.vue'
|
||||||
const emit = defineEmits<{
|
|
||||||
(el: 'resetAgentRepoLine'): void
|
|
||||||
}>()
|
|
||||||
|
|
||||||
const agentsStore = useAgentsStore()
|
const agentsStore = useAgentsStore()
|
||||||
|
|
||||||
@ -26,11 +22,6 @@ onMounted(async () => {
|
|||||||
await api.setAgents(agentsStore.agents.map((item) => pick(item, ['Name', 'Profile'])))
|
await api.setAgents(agentsStore.agents.map((item) => pick(item, ['Name', 'Profile'])))
|
||||||
})
|
})
|
||||||
|
|
||||||
const handleScroll = () => {
|
|
||||||
emit('resetAgentRepoLine')
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// 上传agent文件
|
// 上传agent文件
|
||||||
const fileInput = ref<HTMLInputElement>()
|
const fileInput = ref<HTMLInputElement>()
|
||||||
|
|
||||||
@ -69,6 +60,7 @@ const readFileContent = async (file: File) => {
|
|||||||
Name: item.Name,
|
Name: item.Name,
|
||||||
Icon: item.Icon.replace(/\.png$/, ''),
|
Icon: item.Icon.replace(/\.png$/, ''),
|
||||||
Profile: item.Profile,
|
Profile: item.Profile,
|
||||||
|
Classification: item.Classification,
|
||||||
})),
|
})),
|
||||||
)
|
)
|
||||||
await api.setAgents(json.map((item) => pick(item, ['Name', 'Profile'])))
|
await api.setAgents(json.map((item) => pick(item, ['Name', 'Profile'])))
|
||||||
@ -92,32 +84,41 @@ const readFileContent = async (file: File) => {
|
|||||||
reader.readAsText(file)
|
reader.readAsText(file)
|
||||||
}
|
}
|
||||||
|
|
||||||
const taskProcess = computed(() => {
|
|
||||||
const list = agentsStore.currentTask?.TaskProcess ?? []
|
|
||||||
return list.map((item) => ({
|
|
||||||
...item,
|
|
||||||
key: uuidv4(),
|
|
||||||
}))
|
|
||||||
})
|
|
||||||
|
|
||||||
const agentListRef = ref<HTMLElement | null>()
|
|
||||||
|
|
||||||
// 根据currentTask排序agent列表
|
// 根据currentTask排序agent列表
|
||||||
const agentList = computed(() => {
|
const agentList = computed(() => {
|
||||||
const startArr: Agent[] = []
|
const selected: Agent[] = []
|
||||||
const endArr: Agent[] = []
|
const unselected: {
|
||||||
|
title: string
|
||||||
|
data: Agent[]
|
||||||
|
}[] = []
|
||||||
|
const obj: Record<string, Agent[]> = {}
|
||||||
if (!agentsStore.agents.length) {
|
if (!agentsStore.agents.length) {
|
||||||
return startArr
|
return {
|
||||||
|
selected,
|
||||||
|
unselected,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
for (const agent of agentsStore.agents) {
|
for (const agent of agentsStore.agents) {
|
||||||
if (agentsStore.currentTask?.AgentSelection?.includes(agent.Name)) {
|
// if (agentsStore.currentTask?.AgentSelection?.includes(agent.Name)) {
|
||||||
startArr.push(agent)
|
// selected.push(agent)
|
||||||
|
// continue
|
||||||
|
// }
|
||||||
|
if (obj[agent.Classification]) {
|
||||||
|
obj[agent.Classification]!.push(agent)
|
||||||
} else {
|
} else {
|
||||||
endArr.push(agent)
|
const arr = [agent]
|
||||||
|
obj[agent.Classification] = arr
|
||||||
|
unselected.push({
|
||||||
|
title: agent.Classification,
|
||||||
|
data: arr,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return [...startArr, ...endArr]
|
return {
|
||||||
|
selected,
|
||||||
|
unselected: unselected,
|
||||||
|
}
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
@ -133,116 +134,14 @@ const agentList = computed(() => {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<!-- 智能体列表 -->
|
<!-- 智能体列表 -->
|
||||||
<div
|
<div class="pt-[18px] flex-1 overflow-y-auto relative">
|
||||||
class="mt-[18px] flex-1 overflow-y-auto relative"
|
<!-- 已选中的智能体 -->
|
||||||
ref="agentListRef"
|
<AgentRepoList :agent-list="agentList.selected" />
|
||||||
@scroll="handleScroll"
|
<!-- 为选择的智能体 -->
|
||||||
>
|
<div v-for="agent in agentList.unselected" :key="agent.title">
|
||||||
<el-popover
|
<p class="text-[12px] font-bold py-[8px]">{{ agent.title }}</p>
|
||||||
v-for="item in agentList"
|
<AgentRepoList :agent-list="agent.data" />
|
||||||
:key="item.Name"
|
|
||||||
trigger="hover"
|
|
||||||
placement="bottom"
|
|
||||||
:show-arrow="false"
|
|
||||||
:disabled="!agentsStore.currentTask?.AgentSelection?.includes(item.Name)"
|
|
||||||
popper-class="agent-repo-item-popover active-card"
|
|
||||||
:append-to="agentListRef"
|
|
||||||
width="100%"
|
|
||||||
>
|
|
||||||
<template #reference>
|
|
||||||
<div
|
|
||||||
class="flex items-center justify-between user-item relative h-[41px]"
|
|
||||||
:class="
|
|
||||||
agentsStore.currentTask?.AgentSelection?.includes(item.Name) ? 'active-card' : ''
|
|
||||||
"
|
|
||||||
>
|
|
||||||
<div
|
|
||||||
class="w-[41px] h-[41px] rounded-full flex items-center justify-center flex-shrink-0"
|
|
||||||
:style="{ background: getAgentMapIcon(item.Name).color }"
|
|
||||||
>
|
|
||||||
<svg-icon
|
|
||||||
:icon-class="getAgentMapIcon(item.Name).icon"
|
|
||||||
color="var(--color-text)"
|
|
||||||
size="24px"
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="flex-1 text-[14px] flex flex-col items-end justify-end truncate ml-1">
|
|
||||||
<span
|
|
||||||
class="w-full truncate text-right"
|
|
||||||
:style="
|
|
||||||
agentsStore.currentTask?.AgentSelection?.includes(item.Name)
|
|
||||||
? 'color:#00F3FF'
|
|
||||||
: ''
|
|
||||||
"
|
|
||||||
>{{ item.Name }}</span
|
|
||||||
>
|
|
||||||
<div
|
|
||||||
v-if="agentsStore.currentTask?.AgentSelection?.includes(item.Name)"
|
|
||||||
class="flex items-center gap-[7px] h-[8px] mr-1"
|
|
||||||
>
|
|
||||||
<!-- 小圆点 -->
|
|
||||||
<div
|
|
||||||
v-for="item1 in taskProcess.filter((i) => i.AgentName === item.Name)"
|
|
||||||
:key="item1.key"
|
|
||||||
class="w-[6px] h-[6px] rounded-full"
|
|
||||||
:style="{ background: getActionTypeDisplay(item1.ActionType)?.color }"
|
|
||||||
></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
<div>
|
|
||||||
<div class="flex items-center justify-between">
|
|
||||||
<div
|
|
||||||
class="w-[41px] h-[41px] rounded-full flex items-center justify-center flex-shrink-0 relative right-[2px] bottom-[2px] self-start"
|
|
||||||
:style="{ background: getAgentMapIcon(item.Name).color }"
|
|
||||||
>
|
|
||||||
<svg-icon
|
|
||||||
:icon-class="getAgentMapIcon(item.Name).icon"
|
|
||||||
color="var(--color-text)"
|
|
||||||
size="24px"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
class="flex-1 text-[14px] flex flex-col items-center justify-center break-all ml-1 text-[14px] text-[#00F3FF] p-[8px] pl-[0]"
|
|
||||||
>
|
|
||||||
{{ item.Name }}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="w-full flex justify-center">
|
|
||||||
<div
|
|
||||||
class="rounded-[9px] bg-[var(--color-bg-quaternary)] text-[12px] py-0.5 px-5 text-center my-2"
|
|
||||||
>
|
|
||||||
当前指责
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="p-[8px] pt-0">
|
|
||||||
<div
|
|
||||||
v-for="(item1, index1) in taskProcess.filter((i) => i.AgentName === item.Name)"
|
|
||||||
:key="item1.key"
|
|
||||||
class="text-[12px]"
|
|
||||||
>
|
|
||||||
<div>
|
|
||||||
<div class="mx-1 inline-block h-[14px]">
|
|
||||||
<div
|
|
||||||
:style="{ background: getActionTypeDisplay(item1.ActionType)?.color }"
|
|
||||||
class="w-[6px] h-[6px] rounded-full mt-[7px]"
|
|
||||||
></div>
|
|
||||||
</div>
|
|
||||||
<span :style="{ color: getActionTypeDisplay(item1.ActionType)?.color }"
|
|
||||||
>{{ getActionTypeDisplay(item1.ActionType)?.name }}:</span
|
|
||||||
>
|
|
||||||
<span>{{ item1.Description }}</span>
|
|
||||||
</div>
|
|
||||||
<!-- 分割线 -->
|
|
||||||
<div
|
|
||||||
v-if="index1 !== taskProcess.filter((i) => i.AgentName === item.Name).length - 1"
|
|
||||||
class="h-[1px] w-full bg-[#494B51] my-[8px]"
|
|
||||||
></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</el-popover>
|
|
||||||
</div>
|
</div>
|
||||||
<!-- 底部提示栏 -->
|
<!-- 底部提示栏 -->
|
||||||
<div class="w-full grid grid-cols-3 gap-x-[10px] bg-[#1d222b] rounded-[20px] p-[8px] mt-[10px]">
|
<div class="w-full grid grid-cols-3 gap-x-[10px] bg-[#1d222b] rounded-[20px] p-[8px] mt-[10px]">
|
||||||
@ -279,28 +178,6 @@ const agentList = computed(() => {
|
|||||||
box-shadow: 0 1px 4px rgba(0, 0, 0, 0.15);
|
box-shadow: 0 1px 4px rgba(0, 0, 0, 0.15);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.user-item {
|
|
||||||
background: #1d222b;
|
|
||||||
border-radius: 40px;
|
|
||||||
padding-right: 12px;
|
|
||||||
cursor: pointer;
|
|
||||||
transition: all 0.25s ease;
|
|
||||||
color: #969696;
|
|
||||||
|
|
||||||
& + .user-item {
|
|
||||||
margin-top: 8px;
|
|
||||||
}
|
|
||||||
|
|
||||||
&:hover {
|
|
||||||
box-shadow: 0 3px 10px rgba(0, 0, 0, 0.2);
|
|
||||||
color: #b8b8b8;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.active-card {
|
|
||||||
border-left: none !important;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#agent-repo {
|
#agent-repo {
|
||||||
|
|||||||
@ -63,10 +63,6 @@ function createInternalLine(id?: string) {
|
|||||||
jsplumb.reset()
|
jsplumb.reset()
|
||||||
collaborationProcess.value.forEach((item) => {
|
collaborationProcess.value.forEach((item) => {
|
||||||
// 创建左侧流程与产出的连线
|
// 创建左侧流程与产出的连线
|
||||||
// jsplumb.connect(`task-results-${item.Id}-0`, `task-results-${item.Id}-1`, [
|
|
||||||
// AnchorLocations.Left,
|
|
||||||
// AnchorLocations.Left,
|
|
||||||
// ])
|
|
||||||
arr.push({
|
arr.push({
|
||||||
sourceId: `task-results-${item.Id}-0`,
|
sourceId: `task-results-${item.Id}-0`,
|
||||||
targetId: `task-results-${item.Id}-1`,
|
targetId: `task-results-${item.Id}-1`,
|
||||||
@ -75,14 +71,6 @@ function createInternalLine(id?: string) {
|
|||||||
collaborationProcess.value.forEach((jitem) => {
|
collaborationProcess.value.forEach((jitem) => {
|
||||||
// 创建左侧产出与上一步流程的连线
|
// 创建左侧产出与上一步流程的连线
|
||||||
if (item.InputObject_List!.includes(jitem.OutputObject ?? '')) {
|
if (item.InputObject_List!.includes(jitem.OutputObject ?? '')) {
|
||||||
// jsplumb.connect(
|
|
||||||
// `task-results-${jitem.Id}-1`,
|
|
||||||
// `task-results-${item.Id}-0`,
|
|
||||||
// [AnchorLocations.Left, AnchorLocations.Left],
|
|
||||||
// {
|
|
||||||
// type: 'output',
|
|
||||||
// },
|
|
||||||
// )
|
|
||||||
arr.push({
|
arr.push({
|
||||||
sourceId: `task-results-${jitem.Id}-1`,
|
sourceId: `task-results-${jitem.Id}-1`,
|
||||||
targetId: `task-results-${item.Id}-0`,
|
targetId: `task-results-${item.Id}-0`,
|
||||||
@ -96,8 +84,8 @@ function createInternalLine(id?: string) {
|
|||||||
jitem.TaskProcess.forEach((i) => {
|
jitem.TaskProcess.forEach((i) => {
|
||||||
if (i.ImportantInput?.includes(`InputObject:${item.OutputObject}`)) {
|
if (i.ImportantInput?.includes(`InputObject:${item.OutputObject}`)) {
|
||||||
const color = getActionTypeDisplay(i.ActionType)?.color ?? ''
|
const color = getActionTypeDisplay(i.ActionType)?.color ?? ''
|
||||||
const sourceId = `task-results-${jitem.Id}-0-${i.ID}`
|
const sourceId = `task-results-${item.Id}-1`
|
||||||
const targetId = `task-results-${item.Id}-1`
|
const targetId = `task-results-${jitem.Id}-0-${i.ID}`
|
||||||
arr.push({
|
arr.push({
|
||||||
sourceId,
|
sourceId,
|
||||||
targetId,
|
targetId,
|
||||||
@ -107,7 +95,7 @@ function createInternalLine(id?: string) {
|
|||||||
[0, color],
|
[0, color],
|
||||||
[1, color],
|
[1, color],
|
||||||
],
|
],
|
||||||
transparent: sourceId !== id,
|
transparent: targetId !== id,
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -122,8 +110,8 @@ function createInternalLine(id?: string) {
|
|||||||
item.TaskProcess?.forEach((i2) => {
|
item.TaskProcess?.forEach((i2) => {
|
||||||
if (i.ImportantInput.includes(`ActionResult:${i2.ID}`)) {
|
if (i.ImportantInput.includes(`ActionResult:${i2.ID}`)) {
|
||||||
const color = getActionTypeDisplay(i.ActionType)?.color ?? ''
|
const color = getActionTypeDisplay(i.ActionType)?.color ?? ''
|
||||||
const sourceId = `task-results-${item.Id}-0-${i.ID}`
|
const sourceId = `task-results-${item.Id}-0-${i2.ID}`
|
||||||
const targetId = `task-results-${item.Id}-0-${i2.ID}`
|
const targetId = `task-results-${item.Id}-0-${i.ID}`
|
||||||
arr.push({
|
arr.push({
|
||||||
sourceId,
|
sourceId,
|
||||||
targetId,
|
targetId,
|
||||||
@ -133,7 +121,7 @@ function createInternalLine(id?: string) {
|
|||||||
[0, color],
|
[0, color],
|
||||||
[1, color],
|
[1, color],
|
||||||
],
|
],
|
||||||
transparent: sourceId !== id,
|
transparent: targetId !== id,
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@ -32,7 +32,25 @@ const taskResultRef = ref<{
|
|||||||
}>()
|
}>()
|
||||||
const taskResultJsplumb = new Jsplumb('task-template')
|
const taskResultJsplumb = new Jsplumb('task-template')
|
||||||
|
|
||||||
function setCurrentTask(task: IRawStepTask) {
|
|
||||||
|
function scrollToElementTop(elementId: string) {
|
||||||
|
const element = document.getElementById(elementId);
|
||||||
|
if (element) {
|
||||||
|
element.scrollIntoView({
|
||||||
|
behavior: 'smooth',
|
||||||
|
block: 'start'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function handleTaskSyllabusCurrentTask(task: IRawStepTask) {
|
||||||
|
scrollToElementTop(`task-results-${task.Id}-0`)
|
||||||
|
agentsStore.setCurrentTask(task)
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleTaskResultCurrentTask(task: IRawStepTask) {
|
||||||
|
scrollToElementTop(`task-syllabus-flow-${task.Id}`)
|
||||||
agentsStore.setCurrentTask(task)
|
agentsStore.setCurrentTask(task)
|
||||||
// 更新任务大纲内部的线
|
// 更新任务大纲内部的线
|
||||||
taskSyllabusRef.value?.changeTask(task, false)
|
taskSyllabusRef.value?.changeTask(task, false)
|
||||||
@ -64,7 +82,7 @@ defineExpose({
|
|||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div
|
<div
|
||||||
class="task-template flex gap-6 items-center h-[calc(100%-67px)] relative overflow-hidden"
|
class="task-template flex gap-6 items-center h-[calc(100%-84px)] relative overflow-hidden"
|
||||||
id="task-template"
|
id="task-template"
|
||||||
>
|
>
|
||||||
<!-- 智能体库 -->
|
<!-- 智能体库 -->
|
||||||
@ -76,7 +94,7 @@ defineExpose({
|
|||||||
<TaskSyllabus
|
<TaskSyllabus
|
||||||
ref="taskSyllabusRef"
|
ref="taskSyllabusRef"
|
||||||
@resetAgentRepoLine="resetAgentRepoLine"
|
@resetAgentRepoLine="resetAgentRepoLine"
|
||||||
@set-current-task="setCurrentTask"
|
@set-current-task="handleTaskSyllabusCurrentTask"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<!-- 执行结果 -->
|
<!-- 执行结果 -->
|
||||||
@ -84,7 +102,7 @@ defineExpose({
|
|||||||
<TaskResult
|
<TaskResult
|
||||||
ref="taskResultRef"
|
ref="taskResultRef"
|
||||||
@refresh-line="taskResultJsplumb.repaintEverything"
|
@refresh-line="taskResultJsplumb.repaintEverything"
|
||||||
@set-current-task="setCurrentTask"
|
@set-current-task="handleTaskResultCurrentTask"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -102,19 +120,3 @@ defineExpose({
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
<style lang="scss">
|
|
||||||
:root {
|
|
||||||
--gradient: linear-gradient(to right, #0093eb, #00d2d1);
|
|
||||||
}
|
|
||||||
|
|
||||||
#task-template {
|
|
||||||
.active-card {
|
|
||||||
border: 2px solid transparent;
|
|
||||||
$bg: var(--el-input-bg-color, var(--el-fill-color-blank));
|
|
||||||
background:
|
|
||||||
linear-gradient(var(--color-bg-secondary), var(--color-bg-secondary)) padding-box,
|
|
||||||
linear-gradient(to right, #00c8d2, #315ab4) border-box;
|
|
||||||
color: var(--color-text);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
|||||||
@ -58,14 +58,14 @@ export class Jsplumb {
|
|||||||
getStops = (type?: 'input' | 'output'): [[number, string], [number, string]] => {
|
getStops = (type?: 'input' | 'output'): [[number, string], [number, string]] => {
|
||||||
if (type === 'input') {
|
if (type === 'input') {
|
||||||
return [
|
return [
|
||||||
[0, '#0093EB'],
|
[0, '#FF6161'],
|
||||||
[1, '#00D2D1'],
|
[1, '#D76976'],
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
return [
|
return [
|
||||||
[0, '#FF6161'],
|
[0, '#0093EB'],
|
||||||
[1, '#D76976'],
|
[1, '#00D2D1'],
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -113,6 +113,7 @@ export class Jsplumb {
|
|||||||
type: DotEndpoint.type,
|
type: DotEndpoint.type,
|
||||||
options: { radius: 5 },
|
options: { radius: 5 },
|
||||||
},
|
},
|
||||||
|
cssClass: `jtk-connector-${config.type}`
|
||||||
} as unknown as ConnectParams<unknown>)
|
} as unknown as ConnectParams<unknown>)
|
||||||
|
|
||||||
// 为源元素添加端点
|
// 为源元素添加端点
|
||||||
|
|||||||
@ -13,7 +13,7 @@ function handleSearch() {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="p-[27px] h-[calc(100%-60px)]">
|
<div class="p-[24px] h-[calc(100%-60px)]">
|
||||||
<Task @search="handleSearch" @search-start="taskTemplateRef?.clear" />
|
<Task @search="handleSearch" @search-start="taskTemplateRef?.clear" />
|
||||||
<TaskTemplate ref="taskTemplateRef" />
|
<TaskTemplate ref="taskTemplateRef" />
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -121,7 +121,7 @@ export const agentMapDuty: Record<string, AgentMapDuty> = {
|
|||||||
Propose: {
|
Propose: {
|
||||||
name: '提议',
|
name: '提议',
|
||||||
key: 'propose',
|
key: 'propose',
|
||||||
color: '#0060FF',
|
color: '#06A3FF',
|
||||||
},
|
},
|
||||||
Critique: {
|
Critique: {
|
||||||
name: '评审',
|
name: '评审',
|
||||||
@ -131,12 +131,12 @@ export const agentMapDuty: Record<string, AgentMapDuty> = {
|
|||||||
Improve: {
|
Improve: {
|
||||||
name: '改进',
|
name: '改进',
|
||||||
key: 'improve',
|
key: 'improve',
|
||||||
color: '#9808FF',
|
color: '#BF65FF',
|
||||||
},
|
},
|
||||||
Finalize: {
|
Finalize: {
|
||||||
name: '总结',
|
name: '总结',
|
||||||
key: 'summary',
|
key: 'summary',
|
||||||
color: '#FF6F08',
|
color: '#FFA236',
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -11,6 +11,7 @@ export interface Agent {
|
|||||||
Name: string
|
Name: string
|
||||||
Profile: string
|
Profile: string
|
||||||
Icon: string
|
Icon: string
|
||||||
|
Classification: string
|
||||||
}
|
}
|
||||||
|
|
||||||
type HslColorVector = [number, number, number]
|
type HslColorVector = [number, number, number]
|
||||||
@ -50,7 +51,7 @@ export interface IRawPlanResponse {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const useAgentsStore = defineStore('counter', () => {
|
export const useAgentsStore = defineStore('counter', () => {
|
||||||
const agents = useStorage<Agent[]>('agents', [])
|
const agents = useStorage<Agent[]>('agents-v1', [])
|
||||||
function setAgents(agent: Agent[]) {
|
function setAgents(agent: Agent[]) {
|
||||||
agents.value = agent
|
agents.value = agent
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user