feat(task):优化任务执行与智能体展示功能
- 更新action.svg图标样式- 重构AgentRepo组件,优化智能体列表展示逻辑 - 改进ExecutePlan组件,支持object类型节点渲染 - 完善TaskResult组件,增加执行计划存储与清理机制 - 调整TaskSyllabus组件,优化卡片激活状态样式 - 在Task组件中添加搜索建议功能 - 更新主题配色变量和全局样式- 替换ElInput为ElAutocomplete组件 - 清理无用的jsplumb连接代码- 优化组件间通信与状态管理
This commit is contained in:
parent
0c571dec21
commit
974af053ca
@ -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
2
components.d.ts
vendored
@ -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']
|
||||
|
||||
@ -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 |
@ -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 {
|
||||
|
||||
@ -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>()
|
||||
|
||||
// 根据currentTask排序agent列表
|
||||
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>
|
||||
|
||||
@ -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 为 object直接渲染Content
|
||||
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>
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
@ -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>
|
||||
|
||||
@ -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>
|
||||
|
||||
@ -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>
|
||||
|
||||
@ -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,
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user