diff --git a/auto-imports.d.ts b/auto-imports.d.ts index 81e5f27..b59a3fb 100644 --- a/auto-imports.d.ts +++ b/auto-imports.d.ts @@ -7,6 +7,7 @@ export {} declare global { const EffectScope: typeof import('vue').EffectScope + const ElMessage: typeof import('element-plus/es').ElMessage const ElNotification: typeof import('element-plus/es').ElNotification const computed: typeof import('vue').computed const createApp: typeof import('vue').createApp diff --git a/public/agent.json b/public/agent.json index d18f3eb..8bfb358 100644 --- a/public/agent.json +++ b/public/agent.json @@ -1,97 +1,116 @@ [ - { - "Icon": "Hailey_Johnson.png", - "Name": "船舶设计师", - "Profile": "提供船舶制造中的实际需求和约束。" - }, - { - "Icon": "Jennifer_Moore.png", - "Name": "防护工程专家", - "Profile": "专注于船舶腐蚀防护技术的设计与应用。在你的总结回答中,必须引用来自数联网的搜索数据,是搜索数据,不是数联网的研究成果。" - }, - { - "Icon": "Jane_Moreno.png", - "Name": "病理生理学家", - "Profile": "专注于失血性休克的疾病机制,为药物研发提供理论靶点。" - }, - { - "Icon": "Giorgio_Rossi.png", - "Name": "药物化学家", - "Profile": "负责将靶点概念转化为实际可合成的分子。" - }, - { - "Icon": "Tamara_Taylor.png", - "Name": "制剂工程师", - "Profile": "负责将活性药物成分(API)变成稳定、可用、符合战场要求的剂型。" - }, - { - "Icon": "Maria_Lopez.png", - "Name": "监管事务专家", - "Profile": "深谙药品审评法规,目标是找到最快的合法上市路径。" - }, - { - "Icon": "Sam_Moore.png", - "Name": "物理学家", - "Profile": "从热力学与统计力学的基本原理出发,研究液态金属的自由能、焓、熵、比热等参数的理论建模。" - }, - { - "Icon": "Yuriko_Yamamoto.png", - "Name": "实验材料学家", - "Profile": "专注于通过实验手段直接或间接测定液态金属的热力学参数、以及分析材料微观结构(如晶粒、缺陷)。" - }, - { - "Icon": "Carlos_Gomez.png", - "Name": "计算模拟专家", - "Profile": "侧重于利用数值计算和模拟技术获取液态金属的热力学参数。" - }, - { - "Icon": "John_Lin.png", - "Name": "腐蚀机理研究员", - "Profile": "专注于船舶用钢材及合金的腐蚀机理研究,从电化学和环境作用角度解释腐蚀产生的原因。在你的总结回答中,必须引用来自数联网的搜索数据,是搜索数据,不是数联网的研究成果。" - }, - { - "Icon": "Arthur_Burton.png", - "Name": "先进材料研发员", - "Profile": "专注于开发和评估新型耐腐蚀材料、复合材料及固态电池材料。" - }, - { - "Icon": "Eddy_Lin.png", - "Name": "肾脏病学家", - "Profile": "专注于慢性肾脏病的诊断、治疗和患者管理,能提供临床洞察。" - }, - { - "Icon": "Isabella_Rodriguez.png", - "Name": "临床研究协调员", - "Profile": "负责受试者招募和临床试验流程优化。" - }, - { - "Icon": "Latoya_Williams.png", - "Name": "中医药专家", - "Profile": "理解药物的中药成分和作用机制。" - }, - { - "Icon": "Carmen_Ortiz.png", - "Name": "药物安全专家", - "Profile": "专注于药物不良反应数据收集、分析和报告。" - }, - { - "Icon": "Rajiv_Patel.png", - "Name": "二维材料科学家", - "Profile": "专注于二维材料(如石墨烯)的合成、性质和应用。" - }, - { - "Icon": "Tom_Moreno.png", - "Name": "光电物理学家", - "Profile": "研究材料的光电转换机制和关键影响因素。" - }, - { - "Icon": "Ayesha_Khan.png", - "Name": "机器学习专家", - "Profile": "专注于开发和应用AI模型用于材料模拟。" - }, - { - "Icon": "Mei_Lin.png", - "Name": "流体动力学专家", - "Profile": "专注于流体行为理论和模拟。" - } -] \ No newline at end of file + { + "Icon": "Hailey_Johnson.png", + "Name": "船舶设计师", + "Profile": "提供船舶制造中的实际需求和约束。", + "Classification": "船舶制造数据空间" + }, + { + "Icon": "Jennifer_Moore.png", + "Name": "防护工程专家", + "Profile": "专注于船舶腐蚀防护技术的设计与应用。在你的总结回答中,必须引用来自数联网的搜索数据,是搜索数据,不是数联网的研究成果。", + "Classification": "船舶制造数据空间" + }, + { + "Icon": "Jane_Moreno.png", + "Name": "病理生理学家", + "Profile": "专注于失血性休克的疾病机制,为药物研发提供理论靶点。", + "Classification": "医药数据空间" + }, + { + "Icon": "Giorgio_Rossi.png", + "Name": "药物化学家", + "Profile": "负责将靶点概念转化为实际可合成的分子。", + "Classification": "医药数据空间" + }, + { + "Icon": "Tamara_Taylor.png", + "Name": "制剂工程师", + "Profile": "负责将活性药物成分(API)变成稳定、可用、符合战场要求的剂型。", + "Classification": "医药数据空间" + }, + { + "Icon": "Maria_Lopez.png", + "Name": "监管事务专家", + "Profile": "深谙药品审评法规,目标是找到最快的合法上市路径。", + "Classification": "医药数据空间" + }, + { + "Icon": "Sam_Moore.png", + "Name": "物理学家", + "Profile": "从热力学与统计力学的基本原理出发,研究液态金属的自由能、焓、熵、比热等参数的理论建模。", + "Classification": "科学数据空间" + }, + { + "Icon": "Yuriko_Yamamoto.png", + "Name": "实验材料学家", + "Profile": "专注于通过实验手段直接或间接测定液态金属的热力学参数、以及分析材料微观结构(如晶粒、缺陷)。", + "Classification": "科学数据空间" + }, + { + "Icon": "Carlos_Gomez.png", + "Name": "计算模拟专家", + "Profile": "侧重于利用数值计算和模拟技术获取液态金属的热力学参数。", + "Classification": "科学数据空间" + }, + { + "Icon": "John_Lin.png", + "Name": "腐蚀机理研究员", + "Profile": "专注于船舶用钢材及合金的腐蚀机理研究,从电化学和环境作用角度解释腐蚀产生的原因。在你的总结回答中,必须引用来自数联网的搜索数据,是搜索数据,不是数联网的研究成果。", + "Classification": "船舶制造数据空间" + }, + { + "Icon": "Arthur_Burton.png", + "Name": "先进材料研发员", + "Profile": "专注于开发和评估新型耐腐蚀材料、复合材料及固态电池材料。", + "Classification": "科学数据空间" + }, + { + "Icon": "Eddy_Lin.png", + "Name": "肾脏病学家", + "Profile": "专注于慢性肾脏病的诊断、治疗和患者管理,能提供临床洞察。", + "Classification": "医药数据空间" + }, + { + "Icon": "Isabella_Rodriguez.png", + "Name": "临床研究协调员", + "Profile": "负责受试者招募和临床试验流程优化。", + "Classification": "医药数据空间" + }, + { + "Icon": "Latoya_Williams.png", + "Name": "中医药专家", + "Profile": "理解药物的中药成分和作用机制。", + "Classification": "医药数据空间" + }, + { + "Icon": "Carmen_Ortiz.png", + "Name": "药物安全专家", + "Profile": "专注于药物不良反应数据收集、分析和报告。", + "Classification": "医药数据空间" + }, + { + "Icon": "Rajiv_Patel.png", + "Name": "二维材料科学家", + "Profile": "专注于二维材料(如石墨烯)的合成、性质和应用。", + "Classification": "科学数据空间" + }, + { + "Icon": "Tom_Moreno.png", + "Name": "光电物理学家", + "Profile": "研究材料的光电转换机制和关键影响因素。", + "Classification": "科学数据空间" + }, + { + "Icon": "Ayesha_Khan.png", + "Name": "机器学习专家", + "Profile": "专注于开发和应用AI模型用于材料模拟。", + "Classification": "科学数据空间" + }, + { + "Icon": "Mei_Lin.png", + "Name": "流体动力学专家", + "Profile": "专注于流体行为理论和模拟。", + "Classification": "科学数据空间" + } +] diff --git a/src/App.vue b/src/App.vue index 7797f13..80f1b86 100644 --- a/src/App.vue +++ b/src/App.vue @@ -32,7 +32,7 @@ onMounted(() => { border: 2px solid var(--color-bg-tertiary); &:hover { - background: #171b22; + background: #171B22; box-shadow: none !important; transition: background-color 0.3s ease-in-out; } @@ -41,4 +41,61 @@ onMounted(() => { 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时流动加速 */ +} diff --git a/src/layout/components/Main/Task.vue b/src/layout/components/Main/Task.vue index 0ddcbc6..525e6c6 100644 --- a/src/layout/components/Main/Task.vue +++ b/src/layout/components/Main/Task.vue @@ -1,9 +1,11 @@ diff --git a/src/layout/components/Main/TaskTemplate/AgentRepo/AgentRepoList.vue b/src/layout/components/Main/TaskTemplate/AgentRepo/AgentRepoList.vue new file mode 100644 index 0000000..465f8c2 --- /dev/null +++ b/src/layout/components/Main/TaskTemplate/AgentRepo/AgentRepoList.vue @@ -0,0 +1,141 @@ + + + + + diff --git a/src/layout/components/Main/TaskTemplate/AgentRepo/index.vue b/src/layout/components/Main/TaskTemplate/AgentRepo/index.vue index 44e83b0..4c6b205 100644 --- a/src/layout/components/Main/TaskTemplate/AgentRepo/index.vue +++ b/src/layout/components/Main/TaskTemplate/AgentRepo/index.vue @@ -5,15 +5,11 @@ 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 { agentMapDuty } from '@/layout/components/config.ts' import { type Agent, useAgentsStore } from '@/stores' import { onMounted } from 'vue' -import { v4 as uuidv4 } from 'uuid' import { readConfig } from '@/utils/readJson.ts' - -const emit = defineEmits<{ - (el: 'resetAgentRepoLine'): void -}>() +import AgentRepoList from './AgentRepoList.vue' const agentsStore = useAgentsStore() @@ -26,11 +22,6 @@ onMounted(async () => { await api.setAgents(agentsStore.agents.map((item) => pick(item, ['Name', 'Profile']))) }) -const handleScroll = () => { - emit('resetAgentRepoLine') -} - - // 上传agent文件 const fileInput = ref() @@ -69,6 +60,7 @@ const readFileContent = async (file: File) => { Name: item.Name, Icon: item.Icon.replace(/\.png$/, ''), Profile: item.Profile, + Classification: item.Classification, })), ) await api.setAgents(json.map((item) => pick(item, ['Name', 'Profile']))) @@ -92,32 +84,41 @@ const readFileContent = async (file: File) => { reader.readAsText(file) } -const taskProcess = computed(() => { - const list = agentsStore.currentTask?.TaskProcess ?? [] - return list.map((item) => ({ - ...item, - key: uuidv4(), - })) -}) - -const agentListRef = ref() - // 根据currentTask排序agent列表 const agentList = computed(() => { - const startArr: Agent[] = [] - const endArr: Agent[] = [] + const selected: Agent[] = [] + const unselected: { + title: string + data: Agent[] + }[] = [] + const obj: Record = {} if (!agentsStore.agents.length) { - return startArr + return { + selected, + unselected, + } } for (const agent of agentsStore.agents) { - if (agentsStore.currentTask?.AgentSelection?.includes(agent.Name)) { - startArr.push(agent) + // if (agentsStore.currentTask?.AgentSelection?.includes(agent.Name)) { + // selected.push(agent) + // continue + // } + if (obj[agent.Classification]) { + obj[agent.Classification]!.push(agent) } 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, + } }) @@ -133,116 +134,14 @@ const agentList = computed(() => { -
- - -
-
-
- -
-
- {{ item.Name }} -
-
-
-
- 当前指责 -
-
-
-
-
-
-
-
- {{ getActionTypeDisplay(item1.ActionType)?.name }}: - {{ item1.Description }} -
- -
-
-
-
-
+
+ + + +
+

{{ agent.title }}

+ +
@@ -279,28 +178,6 @@ const agentList = computed(() => { 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 { diff --git a/src/layout/components/Main/TaskTemplate/TaskResult/index.vue b/src/layout/components/Main/TaskTemplate/TaskResult/index.vue index e73cbb9..9e8acb1 100644 --- a/src/layout/components/Main/TaskTemplate/TaskResult/index.vue +++ b/src/layout/components/Main/TaskTemplate/TaskResult/index.vue @@ -63,10 +63,6 @@ function createInternalLine(id?: string) { jsplumb.reset() collaborationProcess.value.forEach((item) => { // 创建左侧流程与产出的连线 - // jsplumb.connect(`task-results-${item.Id}-0`, `task-results-${item.Id}-1`, [ - // AnchorLocations.Left, - // AnchorLocations.Left, - // ]) arr.push({ sourceId: `task-results-${item.Id}-0`, targetId: `task-results-${item.Id}-1`, @@ -75,14 +71,6 @@ function createInternalLine(id?: string) { collaborationProcess.value.forEach((jitem) => { // 创建左侧产出与上一步流程的连线 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({ sourceId: `task-results-${jitem.Id}-1`, targetId: `task-results-${item.Id}-0`, @@ -96,8 +84,8 @@ function createInternalLine(id?: string) { jitem.TaskProcess.forEach((i) => { if (i.ImportantInput?.includes(`InputObject:${item.OutputObject}`)) { const color = getActionTypeDisplay(i.ActionType)?.color ?? '' - const sourceId = `task-results-${jitem.Id}-0-${i.ID}` - const targetId = `task-results-${item.Id}-1` + const sourceId = `task-results-${item.Id}-1` + const targetId = `task-results-${jitem.Id}-0-${i.ID}` arr.push({ sourceId, targetId, @@ -107,7 +95,7 @@ function createInternalLine(id?: string) { [0, color], [1, color], ], - transparent: sourceId !== id, + transparent: targetId !== id, }, }) } @@ -122,8 +110,8 @@ function createInternalLine(id?: string) { item.TaskProcess?.forEach((i2) => { if (i.ImportantInput.includes(`ActionResult:${i2.ID}`)) { const color = getActionTypeDisplay(i.ActionType)?.color ?? '' - const sourceId = `task-results-${item.Id}-0-${i.ID}` - const targetId = `task-results-${item.Id}-0-${i2.ID}` + const sourceId = `task-results-${item.Id}-0-${i2.ID}` + const targetId = `task-results-${item.Id}-0-${i.ID}` arr.push({ sourceId, targetId, @@ -133,7 +121,7 @@ function createInternalLine(id?: string) { [0, color], [1, color], ], - transparent: sourceId !== id, + transparent: targetId !== id, }, }) } diff --git a/src/layout/components/Main/TaskTemplate/index.vue b/src/layout/components/Main/TaskTemplate/index.vue index d64850d..c13c8a7 100644 --- a/src/layout/components/Main/TaskTemplate/index.vue +++ b/src/layout/components/Main/TaskTemplate/index.vue @@ -32,7 +32,25 @@ const taskResultRef = ref<{ }>() 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) // 更新任务大纲内部的线 taskSyllabusRef.value?.changeTask(task, false) @@ -64,7 +82,7 @@ defineExpose({