feat(task):优化任务执行与智能体展示功能

- 更新action.svg图标样式- 重构AgentRepo组件,优化智能体列表展示逻辑
- 改进ExecutePlan组件,支持object类型节点渲染
- 完善TaskResult组件,增加执行计划存储与清理机制
- 调整TaskSyllabus组件,优化卡片激活状态样式
- 在Task组件中添加搜索建议功能
- 更新主题配色变量和全局样式- 替换ElInput为ElAutocomplete组件
- 清理无用的jsplumb连接代码- 优化组件间通信与状态管理
This commit is contained in:
zhaoweijie 2025-10-31 18:42:31 +08:00
parent 0c571dec21
commit 974af053ca
12 changed files with 372 additions and 194 deletions

View File

@ -12,7 +12,6 @@ RUN pnpm build
# The base for mode ENVIRONMENT=prod
FROM caddy:${CADDY_VERSION}-alpine as prod
# Workaround for https://github.com/alpinelinux/docker-alpine/issues/98#issuecomment-679278499
RUN sed -i 's/https/http/' /etc/apk/repositories \
&& apk add --no-cache bash

2
components.d.ts vendored
View File

@ -11,11 +11,11 @@ export {}
/* prettier-ignore */
declare module 'vue' {
export interface GlobalComponents {
ElAutocomplete: typeof import('element-plus/es')['ElAutocomplete']
ElButton: typeof import('element-plus/es')['ElButton']
ElCard: typeof import('element-plus/es')['ElCard']
ElCollapse: typeof import('element-plus/es')['ElCollapse']
ElCollapseItem: typeof import('element-plus/es')['ElCollapseItem']
ElInput: typeof import('element-plus/es')['ElInput']
ElPopover: typeof import('element-plus/es')['ElPopover']
ElTooltip: typeof import('element-plus/es')['ElTooltip']
RouterLink: typeof import('vue-router')['RouterLink']

View File

@ -1 +1 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1761368315799" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="16388" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><path d="" p-id="16389"></path><path d="M227.99812197 62.94566471v104.65219045l395.2990928 323.85960541v38.37336356l-395.2990928 325.39511198v105.81588629l140.50868178-0.00715021 470.18140399-398.97251069-0.01251283-103.35085308-471.70261015-395.77100635-36.83785704 0.01251287z" p-id="16390"></path></svg>
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1761736278335" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="5885" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><path d="M226.592 896C167.616 896 128 850.48 128 782.736V241.264C128 173.52 167.616 128 226.592 128c20.176 0 41.136 5.536 62.288 16.464l542.864 280.432C887.648 453.792 896 491.872 896 512s-8.352 58.208-64.272 87.088L288.864 879.536C267.712 890.464 246.768 896 226.592 896z m0-704.304c-31.008 0-34.368 34.656-34.368 49.568v541.472c0 14.896 3.344 49.568 34.368 49.568 9.6 0 20.88-3.2 32.608-9.248l542.864-280.432c21.904-11.328 29.712-23.232 29.712-30.608s-7.808-19.28-29.712-30.592L259.2 200.96c-11.728-6.048-23.008-9.264-32.608-9.264z" p-id="5886"></path></svg>

Before

Width:  |  Height:  |  Size: 631 B

After

Width:  |  Height:  |  Size: 886 B

View File

@ -6,6 +6,7 @@ import api from '@/api'
import { changeBriefs } from '@/utils/collaboration_Brief_FrontEnd.ts'
const emit = defineEmits<{
(e: 'search-start'): void
(e: 'search', value: string): void
}>()
@ -15,6 +16,8 @@ const searchValue = ref('')
async function handleSearch() {
try {
emit('search-start')
agentsStore.resetAgent()
agentsStore.setAgentRawPlan({ loading: true })
const data = await api.generateBasePlan({
goal: searchValue.value,
@ -27,6 +30,31 @@ async function handleSearch() {
agentsStore.setAgentRawPlan({ loading: false })
}
}
const restaurants = ref<string[]>([
'如何快速筛选慢性肾脏病药物潜在受试者?',
'如何补充“丹芍活血胶囊”不良反应数据?',
'如何快速研发用于战场失血性休克的药物?',
'二维材料的光电性质受哪些关键因素影响?',
'如何通过AI模拟的方法分析材料的微观结构?',
'如何分析获取液态金属热力学参数?',
'如何解决固态电池的成本和寿命难题?',
'如何解决船舶制造中的材料腐蚀难题?',
'如何解决船舶制造中流体模拟和建模优化难题?',
])
const querySearch = (queryString: string, cb: (v: {value: string}[]) => void) => {
const results = queryString
? restaurants.value.filter(createFilter(queryString))
: restaurants.value
// call callback function to return suggestions
cb(results.map((item) => ({value: item})))
}
const createFilter = (queryString: string) => {
return (restaurant: string) => {
return restaurant.toLowerCase().includes(queryString.toLowerCase())
}
}
</script>
<template>
@ -37,11 +65,12 @@ async function handleSearch() {
:disabled="agentsStore.agents.length > 0"
>
<div class="w-full flex justify-center mb-[27px]">
<el-input
<el-autocomplete
v-model.trim="searchValue"
class="task-input"
size="large"
placeholder="请输入您的任务"
:fetch-suggestions="querySearch"
@change="agentsStore.setSearchValue"
:disabled="!(agentsStore.agents.length > 0)"
>
@ -66,18 +95,18 @@ async function handleSearch() {
/>
</el-button>
</template>
</el-input>
</el-autocomplete>
</div>
</el-tooltip>
</template>
<style scoped lang="scss">
.task-input {
:deep(.el-autocomplete) {
width: 40%;
height: 60px;
margin: 0 auto;
:deep(.el-input__wrapper) {
.el-input__wrapper {
border-radius: 40px;
box-shadow: none;
border: 2px solid transparent;
@ -86,6 +115,7 @@ async function handleSearch() {
linear-gradient(var(--color-bg-tertiary), var(--color-bg-tertiary)) padding-box,
linear-gradient(to right, #00c8d2, #315ab4) border-box;
font-size: 18px;
padding-right: 5px;
.el-icon.is-loading {
& + span {

View File

@ -5,12 +5,8 @@ import { pick } from 'lodash'
import api from '@/api/index.ts'
import SvgIcon from '@/components/SvgIcon/index.vue'
import {
agentMapDuty,
getActionTypeDisplay,
getAgentMapIcon,
} from '@/layout/components/config.ts'
import { type TaskProcess, useAgentsStore } from '@/stores'
import { agentMapDuty, getActionTypeDisplay, getAgentMapIcon } from '@/layout/components/config.ts'
import { type Agent, useAgentsStore } from '@/stores'
import { onMounted } from 'vue'
import { v4 as uuidv4 } from 'uuid'
@ -31,23 +27,6 @@ onMounted(() => {
}
})
//
const tooltipVisibleKey = ref('')
const tooltipPosition = ref({ x: 0, y: 0 })
const showTooltip = (event: MouseEvent, item: TaskProcess & { key: string }) => {
tooltipVisibleKey.value = item.key
const rect = (event.target as HTMLElement).getBoundingClientRect()
tooltipPosition.value = {
x: rect.left + rect.width / 2,
y: rect.top - 10,
}
}
const hideTooltip = () => {
tooltipVisibleKey.value = ''
}
// agent
const fileInput = ref<HTMLInputElement>()
@ -116,10 +95,30 @@ const taskProcess = computed(() => {
key: uuidv4(),
}))
})
const agentListRef = ref<HTMLElement | null>()
// currentTaskagent
const agentList = computed(() => {
const startArr: Agent[] = []
const endArr: Agent[] = []
if (!agentsStore.agents.length) {
return startArr
}
for (const agent of agentsStore.agents) {
if (agentsStore.currentTask?.AgentSelection?.includes(agent.Name)) {
startArr.push(agent)
} else {
endArr.push(agent)
}
}
return [...startArr, ...endArr]
})
</script>
<template>
<div class="agent-repo h-full flex flex-col">
<div class="agent-repo h-full flex flex-col" id="agent-repo">
<!-- 头部 -->
<div class="flex items-center justify-between">
<span class="text-[18px] font-bold">智能体库</span>
@ -129,73 +128,112 @@ const taskProcess = computed(() => {
<svg-icon icon-class="plus" color="var(--color-text)" size="18px" />
</div>
</div>
<!-- 人员列表 -->
<div class="mt-[18px] flex-1 overflow-y-auto relative" @scroll="handleScroll">
<div
class="flex items-center justify-between user-item relative"
v-for="item in agentsStore.agents"
<!-- 智能体列表 -->
<div
class="mt-[18px] flex-1 overflow-y-auto relative"
ref="agentListRef"
@scroll="handleScroll"
>
<el-popover
v-for="item in agentList"
: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%"
>
<!-- 右侧链接点 -->
<div
class="absolute right-0 top-1/2 transform -translate-y-1/2"
:id="`agent-repo-${item.Name}`"
></div>
<div
class="w-[41px] h-[41px] rounded-full flex items-center justify-center"
:style="{ background: getAgentMapIcon(item.Name).color }"
>
<svg-icon
:icon-class="getAgentMapIcon(item.Name).icon"
color="var(--color-text)"
size="24px"
/>
</div>
<div class="text-[14px] flex flex-col items-end justify-end">
<div class="flex items-center gap-[7px]">
<template #reference>
<div
class="flex items-center justify-between user-item relative h-[41px]"
:class="
agentsStore.currentTask?.AgentSelection?.includes(item.Name) ? 'active-card' : ''
"
>
<div
v-for="item1 in taskProcess.filter((i) => i.AgentName === item.Name)"
:key="item1.key"
class="relative inline-block"
class="w-[41px] h-[41px] rounded-full flex items-center justify-center flex-shrink-0"
:style="{ background: getAgentMapIcon(item.Name).color }"
>
<el-popover
placement="bottom"
:width="200"
trigger="click"
:content="item1.Description"
:title="getActionTypeDisplay(item1.ActionType)?.name"
<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
>
<template #reference>
<div class="group relative inline-block">
<!-- 小圆点 -->
<div
class="w-[6px] h-[6px] rounded-full"
:style="{ background: getActionTypeDisplay(item1.ActionType)?.color }"
@mouseenter="(el) => showTooltip(el, item1)"
@mouseleave="hideTooltip"
></div>
<!-- 弹窗 -->
<teleport to="body">
<div
v-if="tooltipVisibleKey === item1.key"
class="fixed transform -translate-x-1/2 -translate-y-full mb-2 p-2 bg-[var(--el-bg-color-overlay)] text-sm rounded-[8px] z-50"
:style="{
left: tooltipPosition.x + 'px',
top: tooltipPosition.y + 'px',
}"
>
{{ getActionTypeDisplay(item1.ActionType)?.name }}
</div>
</teleport>
</div>
</template>
</el-popover>
<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>
<span class="mb-1">{{ item.Name }}</span>
</div>
</div>
</el-popover>
</div>
<!-- 底部提示栏 -->
<div class="w-full grid grid-cols-3 gap-x-[10px] bg-[#1d222b] rounded-[20px] p-[8px] mt-[10px]">
@ -214,6 +252,7 @@ const taskProcess = computed(() => {
<style scoped lang="scss">
.agent-repo {
padding: 0 8px;
.plus-button {
background: #1d2128;
width: 24px;
@ -234,7 +273,7 @@ const taskProcess = computed(() => {
.user-item {
background: #1d222b;
border-radius: 20px;
border-radius: 40px;
padding-right: 12px;
cursor: pointer;
transition: all 0.25s ease;
@ -249,5 +288,17 @@ const taskProcess = computed(() => {
color: #b8b8b8;
}
}
.active-card {
border-left: none !important;
}
}
#agent-repo {
:deep(.agent-repo-item-popover) {
padding: 0;
border-radius: 20px;
background: var(--color-bg-secondary);
}
}
</style>

View File

@ -17,35 +17,66 @@ const md = new MarkdownIt({
typographer: true,
})
const data = computed(() => {
function sanitize(str?: string) {
if (!str) {
return ''
}
const html = md.render(str)
return DOMPurify.sanitize(html)
}
interface Data {
Description: string
Content: string
LogNodeType: string
}
const data = computed<Data | null>(() => {
for (const result of props.executePlans) {
if (result.NodeId === props.nodeId && result.ActionHistory) {
if (result.NodeId === props.nodeId) {
// LogNodeType objectContent
if (result.LogNodeType === 'object') {
return {
Description: props.nodeId,
Content: sanitize(result.content),
LogNodeType: result.LogNodeType,
}
}
if (!result.ActionHistory) {
return null
}
for (const action of result.ActionHistory) {
if (action.ID === props.actionId) {
return action
return {
Description: action.Description,
Content: sanitize(action.Action_Result),
LogNodeType: result.LogNodeType,
}
}
}
}
}
return null
})
const action_Result = computed(() => {
const html = md.render(data.value?.Action_Result ?? '')
return DOMPurify.sanitize(html)
})
</script>
<template>
<div v-if="data" class="card-item w-full pl-[56px] pr-[41px]">
<!-- 分割线 -->
<div class="h-[1px] w-full bg-[#494B51] my-[8px]"></div>
<div class="text-[16px] flex items-center gap-1 text-[var(--color-text-secondary)]">
<div v-if="data.LogNodeType !== 'object'" class="h-[1px] w-full bg-[#494B51] my-[8px]"></div>
<div
v-if="data.Description"
class="text-[16px] flex items-center gap-1 text-[var(--color-text-secondary)] mb-1"
>
{{ data.Description }}
<Iod />
<Iod v-if="data.LogNodeType !== 'object'"/>
</div>
<div class="rounded-[8px] p-[15px] text-[14px] bg-[var(--color-bg-quaternary)] mt-1">
<div class="markdown-content max-h-[240px] overflow-y-auto" v-html="action_Result"></div>
<div class="rounded-[8px] p-[15px] text-[14px] bg-[var(--color-bg-quaternary)]">
<div
class="markdown-content max-h-[240px] overflow-y-auto max-w-full"
v-html="data.Content"
></div>
</div>
</div>
</template>
@ -54,4 +85,16 @@ const action_Result = computed(() => {
.card-item + .card-item {
margin-top: 10px;
}
.markdown-content {
:deep(code) {
display: block;
width: 100px;
max-width: 100%;
}
:deep(pre) {
overflow-x: auto;
max-width: 100%;
}
}
</style>

View File

@ -8,11 +8,9 @@ import { getActionTypeDisplay, getAgentMapIcon } from '@/layout/components/confi
import { type ConnectArg, Jsplumb } from '@/layout/components/Main/TaskTemplate/utils.ts'
import variables from '@/styles/variables.module.scss'
import { type IRawStepTask, useAgentsStore } from '@/stores'
import api, { type IExecuteRawResponse } from '@/api'
import api from '@/api'
import ExecutePlan from './ExecutePlan.vue'
import Iod from './Iod.vue'
const emit = defineEmits<{
(e: 'refreshLine'): void
@ -21,6 +19,7 @@ const emit = defineEmits<{
const agentsStore = useAgentsStore()
const collaborationProcess = computed(() => {
return agentsStore.agentRawPlan.data?.['Collaboration Process'] ?? []
})
@ -99,13 +98,6 @@ function createInternalLine(id?: string) {
const color = getActionTypeDisplay(i.ActionType)?.color ?? ''
const sourceId = `task-results-${jitem.Id}-0-${i.ID}`
const targetId = `task-results-${item.Id}-1`
// jsplumb.connect(sourceId, targetId, [AnchorLocations.Right, AnchorLocations.Right], {
// stops: [
// [0, color],
// [1, color],
// ],
// transparent: sourceId !== id,
// })
arr.push({
sourceId,
targetId,
@ -132,13 +124,6 @@ function createInternalLine(id?: string) {
const color = getActionTypeDisplay(i.ActionType)?.color ?? ''
const sourceId = `task-results-${item.Id}-0-${i.ID}`
const targetId = `task-results-${item.Id}-0-${i2.ID}`
// jsplumb.connect(sourceId, targetId, [AnchorLocations.Right, AnchorLocations.Right], {
// stops: [
// [0, color],
// [1, color],
// ],
// transparent: sourceId !== id,
// })
arr.push({
sourceId,
targetId,
@ -160,12 +145,12 @@ function createInternalLine(id?: string) {
jsplumb.repaintEverything()
}
const results = ref<IExecuteRawResponse[]>([])
const loading = ref(false)
async function handleRun() {
try {
loading.value = true
results.value = await api.executePlan(agentsStore.agentRawPlan.data!)
const d = await api.executePlan(agentsStore.agentRawPlan.data!)
agentsStore.setExecutePlan(d)
} finally {
loading.value = false
}
@ -204,8 +189,13 @@ const handleMouseLeave = throttle(() => {
}
}, 100)
function clear() {
jsplumb.reset()
}
defineExpose({
createInternalLine,
clear,
})
</script>
@ -218,9 +208,19 @@ defineExpose({
<el-button circle :color="variables.tertiary" disabled title="点击刷新">
<svg-icon icon-class="refresh" />
</el-button>
<el-button circle :color="variables.tertiary" title="点击运行" @click="handleRun">
<svg-icon icon-class="action" />
</el-button>
<el-popover :disabled="Boolean(agentsStore.agentRawPlan.data)" title="请先输入要执行的任务">
<template #reference>
<el-button
circle
:color="variables.tertiary"
title="点击运行"
:disabled="!agentsStore.agentRawPlan.data"
@click="handleRun"
>
<svg-icon icon-class="action" />
</el-button>
</template>
</el-popover>
</div>
</div>
<!-- 内容 -->
@ -233,6 +233,7 @@ defineExpose({
<div v-for="item in collaborationProcess" :key="item.Id" class="card-item">
<el-card
class="card-item w-full relative"
:class="agentsStore.currentTask?.StepName === item.StepName ? 'active-card' : ''"
shadow="hover"
:id="`task-results-${item.Id}-0`"
@click="emit('setCurrentTask', item)"
@ -240,24 +241,18 @@ defineExpose({
<div class="text-[18px] mb-[15px]">{{ item.StepName }}</div>
<!-- 折叠面板 -->
<el-collapse @change="handleCollapse">
<!-- <el-tooltip-->
<!-- :disabled="Boolean(true || results.length || loading)"-->
<!-- placement="top"-->
<!-- effect="light"-->
<!-- content="请点击右上角的循行按钮,查看运行结果"-->
<!-- >-->
<el-collapse-item
v-for="item1 in item.TaskProcess"
:key="`task-results-${item.Id}-${item1.ID}`"
:name="`task-results-${item.Id}-${item1.ID}`"
:disabled="Boolean(!results.length || loading)"
:disabled="Boolean(!agentsStore.executePlan.length || loading)"
@mouseenter="() => handleMouseEnter(`task-results-${item.Id}-0-${item1.ID}`)"
@mouseleave="handleMouseLeave"
>
<template v-if="loading" #icon>
<SvgIcon icon-class="loading" size="20px" class="animate-spin" />
</template>
<template v-else-if="!results.length" #icon>
<template v-else-if="!agentsStore.executePlan.length" #icon>
<span></span>
</template>
<template #title>
@ -288,20 +283,37 @@ defineExpose({
<ExecutePlan
:action-id="item1.ID"
:node-id="item.StepName"
:execute-plans="results"
:execute-plans="agentsStore.executePlan"
/>
</el-collapse-item>
<!-- </el-tooltip>-->
</el-collapse>
</el-card>
<el-card
class="card-item w-full relative"
class="card-item w-full relative output-object-card"
shadow="hover"
:class="agentsStore.currentTask?.StepName === item.StepName ? 'active-card' : ''"
:id="`task-results-${item.Id}-1`"
@click="emit('setCurrentTask', item)"
>
<div class="text-[18px]">{{ item.OutputObject }}</div>
<!-- <div class="text-[18px]">{{ item.OutputObject }}</div>-->
<el-collapse @change="handleCollapse">
<el-collapse-item
class="output-object"
:disabled="Boolean(!agentsStore.executePlan.length || loading)"
>
<template v-if="loading" #icon>
<SvgIcon icon-class="loading" size="20px" class="animate-spin" />
</template>
<template v-else-if="!agentsStore.executePlan.length" #icon>
<span></span>
</template>
<template #title>
<div class="text-[18px]">{{ item.OutputObject }}</div>
</template>
<ExecutePlan :node-id="item.OutputObject" :execute-plans="agentsStore.executePlan" />
</el-collapse-item>
</el-collapse>
</el-card>
</div>
</div>
@ -344,6 +356,27 @@ defineExpose({
}
}
.output-object {
.el-collapse-item__header {
background: none;
.el-collapse-item__title {
background: none;
}
}
.el-collapse-item__wrap {
background: none;
.card-item {
background: var(--color-bg-secondary);
padding: 25px;
padding-top: 10px;
border-radius: 7px;
}
}
}
.el-collapse-item__wrap {
border: none;
background: var(--color-bg-secondary);
@ -358,6 +391,20 @@ defineExpose({
}
}
.output-object-card {
:deep(.el-card__body) {
padding-top: 0;
padding-bottom: 0;
padding-right: 0;
}
}
.active-card {
background:
linear-gradient(var(--color-bg-tertiary), var(--color-bg-tertiary)) padding-box,
linear-gradient(to right, #00c8d2, #315ab4) border-box;
}
.card-item + .card-item {
margin-top: 10px;
}

View File

@ -40,14 +40,6 @@ function handleCurrentTask(task: IRawStepTask, transparent: boolean): ConnectArg
},
]
// jsplumb.connect(
// `task-syllabus-flow-${task.Id}`,
// `task-syllabus-output-object-${task.Id}`,
// [AnchorLocations.Right, AnchorLocations.Left],
// {
// transparent,
// },
// )
// 线
task.InputObject_List?.forEach((item) => {
const id = collaborationProcess.value.find((i) => i.OutputObject === item)?.Id
@ -61,15 +53,6 @@ function handleCurrentTask(task: IRawStepTask, transparent: boolean): ConnectArg
transparent,
}
})
// jsplumb.connect(
// `task-syllabus-output-object-${id}`,
// `task-syllabus-flow-${task.Id}`,
// [AnchorLocations.Left, AnchorLocations.Right],
// {
// type: 'output',
// transparent,
// },
// )
}
})
@ -88,8 +71,13 @@ function changeTask(task?: IRawStepTask, isEmit?: boolean) {
}
}
function clear() {
jsplumb.reset()
}
defineExpose({
changeTask,
clear,
})
</script>
@ -129,7 +117,8 @@ defineExpose({
<el-card
v-for="item in collaborationProcess"
:key="item.Id"
class="card-item w-full h-[158px] overflow-y-auto active-card relative z-99"
class="card-item w-full h-[158px] overflow-y-auto relative z-99"
:class="agentsStore.currentTask?.StepName === item.StepName ? 'active-card' : ''"
shadow="hover"
:id="`task-syllabus-flow-${item.Id}`"
@click="changeTask(item, true)"
@ -209,6 +198,7 @@ defineExpose({
<el-card
class="card-item w-full relative"
shadow="hover"
: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>
@ -221,15 +211,3 @@ defineExpose({
</div>
</div>
</template>
<style scoped lang="scss">
.card-box {
.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;
}
}
</style>

View File

@ -4,7 +4,7 @@ import TaskSyllabus from './TaskSyllabus/index.vue'
import TaskResult from './TaskResult/index.vue'
import { Jsplumb } from './utils.ts'
import { type IRawStepTask, useAgentsStore } from '@/stores'
import { AnchorLocations, BezierConnector } from '@jsplumb/browser-ui'
import { BezierConnector } from '@jsplumb/browser-ui'
const agentsStore = useAgentsStore()
@ -19,29 +19,20 @@ const agentRepoJsplumb = new Jsplumb('task-template', {
},
},
})
//
const taskSyllabusRef = ref<{ changeTask: (task?: IRawStepTask, isEmit?: boolean) => void }>()
const taskSyllabusRef = ref<{
changeTask: (task?: IRawStepTask, isEmit?: boolean) => void
clear: () => void
}>()
//
const taskResultRef = ref<{ createInternalLine: () => void }>()
const taskResultRef = ref<{
createInternalLine: () => void
clear: () => void
}>()
const taskResultJsplumb = new Jsplumb('task-template')
function setCurrentTask(task: IRawStepTask) {
// 线
agentRepoJsplumb.reset()
task.AgentSelection?.forEach((item) => {
agentRepoJsplumb.connect(
`agent-repo-${item}`,
`task-syllabus-flow-agents-${task.Id}`,
[AnchorLocations.Left, AnchorLocations.Right],
{ type: 'input' },
)
})
agentRepoJsplumb.repaintEverything()
// 线
taskResultJsplumb.reset()
taskResultJsplumb.connect(`task-syllabus-output-object-${task.Id}`, `task-results-${task.Id}-0`, [AnchorLocations.Right, AnchorLocations.Left])
taskResultJsplumb.connect(`task-syllabus-output-object-${task.Id}`, `task-results-${task.Id}-1`, [AnchorLocations.Right, AnchorLocations.Left])
taskResultJsplumb.repaintEverything()
agentsStore.setCurrentTask(task)
// 线
taskSyllabusRef.value?.changeTask(task, false)
@ -57,8 +48,17 @@ function resetAgentRepoLine() {
taskResultJsplumb.repaintEverything()
}
function clear() {
taskSyllabusRef.value?.clear()
taskResultRef.value?.clear()
agentRepoJsplumb.repaintEverything()
taskResultJsplumb.repaintEverything()
}
defineExpose({
changeTask,
resetAgentRepoLine,
clear,
})
</script>
@ -68,11 +68,11 @@ defineExpose({
id="task-template"
>
<!-- 智能体库 -->
<div class="w-[9.5%] min-w-[179px] h-full relative">
<div class="w-[9.5%] min-w-[179px] h-full relative flex-shrink-0">
<AgentRepo @resetAgentRepoLine="agentRepoJsplumb.repaintEverything" />
</div>
<!-- 任务大纲 -->
<div class="w-[40.5%] min-w-[600px] h-full px-[20px]">
<div class="w-[35.5%] min-w-[600px] h-full px-[20px] flex-shrink-0">
<TaskSyllabus
ref="taskSyllabusRef"
@resetAgentRepoLine="resetAgentRepoLine"
@ -96,14 +96,25 @@ defineExpose({
box-shadow: 0 0 5px 0 rgba(0, 0, 0, 0.8);
border-radius: 24px;
border: 1px solid #414752;
background: #29303c;
background: var(--color-bg-quinary);
padding-top: 20px;
padding-bottom: 20px;
}
}
</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>

View File

@ -3,7 +3,7 @@ import Task from './Task.vue'
import TaskTemplate from './TaskTemplate/index.vue'
import { nextTick } from 'vue'
const taskTemplateRef = ref<{ changeTask: () => void }>()
const taskTemplateRef = ref<{ changeTask: () => void, clear: () => void }>()
function handleSearch() {
nextTick(() => {
@ -14,7 +14,7 @@ function handleSearch() {
<template>
<div class="p-[27px] h-[calc(100%-60px)]">
<Task @search="handleSearch" />
<Task @search="handleSearch" @search-start="taskTemplateRef?.clear" />
<TaskTemplate ref="taskTemplateRef" />
</div>
</template>

View File

@ -4,6 +4,7 @@ import { v4 as uuidv4 } from 'uuid'
import { store } from '../index'
import { useStorage } from '@vueuse/core'
import type { IExecuteRawResponse } from '@/api'
export interface Agent {
@ -81,6 +82,20 @@ export const useAgentsStore = defineStore('counter', () => {
}
}
// 执行完任务的结果
const executePlan = ref<IExecuteRawResponse[]>([])
function setExecutePlan(plan: IExecuteRawResponse[]) {
executePlan.value = plan
}
function resetAgent() {
agentRawPlan.value = {
loading: false,
}
currentTask.value = undefined
executePlan.value = []
}
return {
agents,
@ -90,7 +105,10 @@ export const useAgentsStore = defineStore('counter', () => {
currentTask,
setCurrentTask,
agentRawPlan,
setAgentRawPlan
setAgentRawPlan,
executePlan,
setExecutePlan,
resetAgent,
}
})

View File

@ -10,6 +10,7 @@ html.dark {
--color-bg-secondary: #050505;
--color-bg-tertiary: #20222A;
--color-bg-quaternary: #24252A;
--color-bg-quinary: #29303c;
--color-text: #fff;
--color-text-secondary: #C9C9C9;
}