feat(config): 添加配置文件支持动态标题和提示词
- 新增 public/config.json 配置文件,包含网站标题、副标题及任务提示词 - 在 Header 组件中读取并应用配置中的标题信息-为 Task 组件的搜索建议框引入配置中的提示词列表 - 创建 useConfigStore 管理全局配置状态,并在应用初始化时加载配置 - 更新 main.ts 在应用启动时设置文档标题 - 移除了 Task.vue 中硬编码的提示词数组,改由配置驱动-修复了 agents store 中版本标识监听逻辑,实现存储清理功能 - 添加 MultiLineTooltip 组件用于文本溢出时显示完整内容 -重构 TaskSyllabus 页面布局与样式,提升视觉效果与交互体验 - 引入 Bg
This commit is contained in:
parent
00ef22505e
commit
041986f5cd
1
components.d.ts
vendored
1
components.d.ts
vendored
@ -18,6 +18,7 @@ declare module 'vue' {
|
|||||||
ElCollapseItem: typeof import('element-plus/es')['ElCollapseItem']
|
ElCollapseItem: typeof import('element-plus/es')['ElCollapseItem']
|
||||||
ElPopover: typeof import('element-plus/es')['ElPopover']
|
ElPopover: typeof import('element-plus/es')['ElPopover']
|
||||||
ElTooltip: typeof import('element-plus/es')['ElTooltip']
|
ElTooltip: typeof import('element-plus/es')['ElTooltip']
|
||||||
|
MultiLineTooltip: typeof import('./src/components/MultiLineTooltip/index.vue')['default']
|
||||||
RouterLink: typeof import('vue-router')['RouterLink']
|
RouterLink: typeof import('vue-router')['RouterLink']
|
||||||
RouterView: typeof import('vue-router')['RouterView']
|
RouterView: typeof import('vue-router')['RouterView']
|
||||||
SvgIcon: typeof import('./src/components/SvgIcon/index.vue')['default']
|
SvgIcon: typeof import('./src/components/SvgIcon/index.vue')['default']
|
||||||
|
|||||||
19
public/config.json
Normal file
19
public/config.json
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
{
|
||||||
|
"title": "数联网",
|
||||||
|
"subTitle": "众创智能体",
|
||||||
|
"centerTitle": "多智能体协同平台",
|
||||||
|
"taskPromptWords": [
|
||||||
|
"如何快速筛选慢性肾脏病药物潜在受试者?",
|
||||||
|
"如何补充“丹芍活血胶囊”不良反应数据?",
|
||||||
|
"如何快速研发用于战场失血性休克的药物?",
|
||||||
|
"二维材料的光电性质受哪些关键因素影响?",
|
||||||
|
"如何通过AI模拟的方法分析材料的微观结构?",
|
||||||
|
"如何分析获取液态金属热力学参数?",
|
||||||
|
"如何解决固态电池的成本和寿命难题?",
|
||||||
|
"如何解决船舶制造中的材料腐蚀难题?",
|
||||||
|
"如何解决船舶制造中流体模拟和建模优化难题?"
|
||||||
|
],
|
||||||
|
"agentRepository": {
|
||||||
|
"storageVersionIdentifier": "1"
|
||||||
|
}
|
||||||
|
}
|
||||||
93
src/components/MultiLineTooltip/index.vue
Normal file
93
src/components/MultiLineTooltip/index.vue
Normal file
@ -0,0 +1,93 @@
|
|||||||
|
<template>
|
||||||
|
<el-tooltip :disabled="!isOverflow" effect="light" placement="top" :content="text">
|
||||||
|
<div
|
||||||
|
ref="containerRef"
|
||||||
|
class="multi-line-ellipsis"
|
||||||
|
:style="containerStyle"
|
||||||
|
@mouseenter="handleMouseEnter"
|
||||||
|
@mouseleave="handleMouseLeave"
|
||||||
|
>
|
||||||
|
<slot>
|
||||||
|
{{ text }}
|
||||||
|
</slot>
|
||||||
|
</div>
|
||||||
|
</el-tooltip>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { computed, type HTMLAttributes, nextTick, onMounted, ref } from 'vue'
|
||||||
|
import { ElTooltip } from 'element-plus'
|
||||||
|
|
||||||
|
// 定义组件 props 类型
|
||||||
|
interface Props {
|
||||||
|
text?: string
|
||||||
|
lines?: number
|
||||||
|
maxWidth?: string | number
|
||||||
|
}
|
||||||
|
|
||||||
|
const props = withDefaults(defineProps<Props>(), {
|
||||||
|
text: '',
|
||||||
|
lines: 3,
|
||||||
|
maxWidth: '100%',
|
||||||
|
})
|
||||||
|
|
||||||
|
const isOverflow = ref(false)
|
||||||
|
const containerRef = ref<HTMLElement | null>(null)
|
||||||
|
|
||||||
|
// 计算容器样式
|
||||||
|
const containerStyle = computed(
|
||||||
|
() =>
|
||||||
|
({
|
||||||
|
maxWidth: props.maxWidth,
|
||||||
|
display: '-webkit-box',
|
||||||
|
WebkitBoxOrient: 'vertical',
|
||||||
|
WebkitLineClamp: props.lines,
|
||||||
|
overflow: 'hidden',
|
||||||
|
textOverflow: 'ellipsis',
|
||||||
|
lineHeight: '1.5',
|
||||||
|
wordBreak: 'break-all',
|
||||||
|
}) as HTMLAttributes['style'],
|
||||||
|
)
|
||||||
|
|
||||||
|
// 检查文字是否溢出
|
||||||
|
const checkOverflow = (element: HTMLElement): boolean => {
|
||||||
|
// 单行情况下使用宽度判断
|
||||||
|
if (props.lines === 1) {
|
||||||
|
return element.scrollWidth > element.clientWidth
|
||||||
|
}
|
||||||
|
// 多行情况下使用高度判断
|
||||||
|
else {
|
||||||
|
return element.scrollHeight > element.clientHeight
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 鼠标进入处理
|
||||||
|
const handleMouseEnter = (event: MouseEvent) => {
|
||||||
|
const element = event.target as HTMLElement
|
||||||
|
console.log(checkOverflow(element))
|
||||||
|
isOverflow.value = checkOverflow(element)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 鼠标离开处理
|
||||||
|
const handleMouseLeave = () => {
|
||||||
|
isOverflow.value = false
|
||||||
|
}
|
||||||
|
|
||||||
|
// 初始化时检查溢出状态
|
||||||
|
onMounted(() => {
|
||||||
|
nextTick(() => {
|
||||||
|
if (containerRef.value) {
|
||||||
|
isOverflow.value = checkOverflow(containerRef.value)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.multi-line-ellipsis {
|
||||||
|
cursor: default;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
white-space: normal;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@ -1,15 +1,21 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
|
import { useConfigStore } from '@/stores'
|
||||||
|
|
||||||
defineOptions({
|
defineOptions({
|
||||||
name: 'AppHeader'
|
name: 'AppHeader'
|
||||||
})
|
})
|
||||||
|
|
||||||
|
const configStore = useConfigStore()
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="bg-[var(--color-bg-secondary)] h-[60px] relative pl-[27px] font-[900] text-[24px]">
|
<div class="bg-[var(--color-bg-secondary)] h-[60px] relative pl-[27px] font-[900] text-[24px]">
|
||||||
<div class="absolute left-0 h-full flex items-center">
|
<div class="absolute left-0 h-full flex items-center">
|
||||||
<img class="w-[36.8px] h-[36.8px] rounded-full mr-[12px]" src="/logo.jpg" alt="logo">
|
<img class="w-[36.8px] h-[36.8px] rounded-full mr-[12px]" src="/logo.jpg" alt="logo" />
|
||||||
<span class="text-[#9E0000]">数联网</span>众创智能体
|
<span class="text-[#9E0000]">{{ configStore.config.title }}</span>{{ configStore.config.subTitle }}
|
||||||
|
</div>
|
||||||
|
<div class="text-center h-full w-full tracking-[8.5px] flex items-center justify-center">
|
||||||
|
{{ configStore.config.centerTitle }}
|
||||||
</div>
|
</div>
|
||||||
<div class="text-center h-full w-full tracking-[8.5px] flex items-center justify-center">多智能体协同平台</div>
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@ -2,7 +2,7 @@
|
|||||||
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, useConfigStore } 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'
|
import { ElMessage } from 'element-plus'
|
||||||
@ -13,6 +13,7 @@ const emit = defineEmits<{
|
|||||||
}>()
|
}>()
|
||||||
|
|
||||||
const agentsStore = useAgentsStore()
|
const agentsStore = useAgentsStore()
|
||||||
|
const configStore = useConfigStore()
|
||||||
|
|
||||||
const searchValue = ref('')
|
const searchValue = ref('')
|
||||||
const triggerOnFocus = ref(true)
|
const triggerOnFocus = ref(true)
|
||||||
@ -41,22 +42,10 @@ async function handleSearch() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const restaurants = ref<string[]>([
|
|
||||||
'如何快速筛选慢性肾脏病药物潜在受试者?',
|
|
||||||
'如何补充“丹芍活血胶囊”不良反应数据?',
|
|
||||||
'如何快速研发用于战场失血性休克的药物?',
|
|
||||||
'二维材料的光电性质受哪些关键因素影响?',
|
|
||||||
'如何通过AI模拟的方法分析材料的微观结构?',
|
|
||||||
'如何分析获取液态金属热力学参数?',
|
|
||||||
'如何解决固态电池的成本和寿命难题?',
|
|
||||||
'如何解决船舶制造中的材料腐蚀难题?',
|
|
||||||
'如何解决船舶制造中流体模拟和建模优化难题?',
|
|
||||||
])
|
|
||||||
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))
|
? configStore.config.taskPromptWords.filter(createFilter(queryString))
|
||||||
: restaurants.value
|
: configStore.config.taskPromptWords
|
||||||
// call callback function to return suggestions
|
// call callback function to return suggestions
|
||||||
cb(results.map((item) => ({ value: item })))
|
cb(results.map((item) => ({ value: item })))
|
||||||
}
|
}
|
||||||
|
|||||||
@ -15,14 +15,19 @@ const md = new MarkdownIt({
|
|||||||
html: true,
|
html: true,
|
||||||
linkify: true,
|
linkify: true,
|
||||||
typographer: true,
|
typographer: true,
|
||||||
|
breaks: true,
|
||||||
})
|
})
|
||||||
|
|
||||||
function sanitize(str?: string) {
|
function sanitize(str?: string) {
|
||||||
if (!str) {
|
if (!str) {
|
||||||
return ''
|
return ''
|
||||||
}
|
}
|
||||||
const html = md.render(str)
|
const cleanStr = str
|
||||||
return DOMPurify.sanitize(html)
|
.replace(/\\n/g, '\n')
|
||||||
|
.replace(/\n\s*\d+\./g, '\n$&')
|
||||||
|
const html = md.render(cleanStr)
|
||||||
|
return html
|
||||||
|
// return DOMPurify.sanitize(html)
|
||||||
}
|
}
|
||||||
|
|
||||||
interface Data {
|
interface Data {
|
||||||
|
|||||||
31
src/layout/components/Main/TaskTemplate/TaskSyllabus/Bg.vue
Normal file
31
src/layout/components/Main/TaskTemplate/TaskSyllabus/Bg.vue
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
<template>
|
||||||
|
<div class="absolute inset-0 flex items-start gap-[14%]">
|
||||||
|
<!-- 左侧元素 -->
|
||||||
|
<div class="flex-1 relative h-full flex justify-center">
|
||||||
|
<!-- 背景那一根线 -->
|
||||||
|
<div
|
||||||
|
class="h-full bg-[var(--color-bg-tertiary)] w-[5px]"
|
||||||
|
>
|
||||||
|
<!-- 线底部的小圆球 -->
|
||||||
|
<div
|
||||||
|
class="absolute bottom-0 left-1/2 transform -translate-x-1/2 bg-[var(--color-bg-tertiary)] w-[15px] h-[15px] rounded-full"
|
||||||
|
></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<!-- 右侧元素 -->
|
||||||
|
<div class="flex-1 relative h-full flex justify-center">
|
||||||
|
<!-- 背景那一根线 -->
|
||||||
|
<div
|
||||||
|
class="h-full bg-[var(--color-bg-tertiary)] w-[5px]"
|
||||||
|
>
|
||||||
|
<!-- 线底部的小圆球 -->
|
||||||
|
<div
|
||||||
|
class="absolute bottom-0 left-1/2 transform -translate-x-1/2 bg-[var(--color-bg-tertiary)] w-[15px] h-[15px] rounded-full"
|
||||||
|
></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
</script>
|
||||||
@ -1,14 +1,13 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import SvgIcon from '@/components/SvgIcon/index.vue'
|
import SvgIcon from '@/components/SvgIcon/index.vue'
|
||||||
import { getAgentMapIcon } from '@/layout/components/config.ts'
|
import { getAgentMapIcon } from '@/layout/components/config.ts'
|
||||||
import {
|
import { type ConnectArg, Jsplumb } from '@/layout/components/Main/TaskTemplate/utils.ts'
|
||||||
type ConnectArg,
|
|
||||||
Jsplumb,
|
|
||||||
type JsplumbConfig,
|
|
||||||
} from '@/layout/components/Main/TaskTemplate/utils.ts'
|
|
||||||
import { type IRawStepTask, useAgentsStore } from '@/stores'
|
import { type IRawStepTask, useAgentsStore } from '@/stores'
|
||||||
import { computed } from 'vue'
|
import { computed } from 'vue'
|
||||||
import { AnchorLocations } from '@jsplumb/browser-ui'
|
import { AnchorLocations } from '@jsplumb/browser-ui'
|
||||||
|
import MultiLineTooltip from '@/components/MultiLineTooltip/index.vue'
|
||||||
|
|
||||||
|
import Bg from './Bg.vue'
|
||||||
|
|
||||||
const emit = defineEmits<{
|
const emit = defineEmits<{
|
||||||
(el: 'resetAgentRepoLine'): void
|
(el: 'resetAgentRepoLine'): void
|
||||||
@ -51,7 +50,7 @@ function handleCurrentTask(task: IRawStepTask, transparent: boolean): ConnectArg
|
|||||||
config: {
|
config: {
|
||||||
type: 'output',
|
type: 'output',
|
||||||
transparent,
|
transparent,
|
||||||
}
|
},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@ -90,124 +89,116 @@ defineExpose({
|
|||||||
class="flex-1 w-full overflow-y-auto relative"
|
class="flex-1 w-full overflow-y-auto relative"
|
||||||
@scroll="handleScroll"
|
@scroll="handleScroll"
|
||||||
>
|
>
|
||||||
<div class="flex items-center gap-[14%] w-full px-5 relative" id="task-syllabus">
|
<div v-show="collaborationProcess.length > 0" class="w-full relative min-h-full" id="task-syllabus">
|
||||||
<!-- 流程 -->
|
<Bg />
|
||||||
<div
|
|
||||||
v-if="agentsStore.agentRawPlan.data"
|
<div class="w-full flex items-center gap-[14%] mb-[35px]">
|
||||||
class="w-[43%] min-h-full relative flex justify-center"
|
<div class="flex-1 flex justify-center">
|
||||||
>
|
|
||||||
<!-- 流程内容 -->
|
|
||||||
<div class="relative min-h-full z-10 flex flex-col items-center card-box pb-[100px]">
|
|
||||||
<!-- 背景那一根线 -->
|
|
||||||
<div
|
|
||||||
class="absolute h-full left-1/2 transform -translate-x-1/2 bg-[var(--color-bg-tertiary)] w-[5px] z-[1]"
|
|
||||||
>
|
|
||||||
<!-- 线底部的小圆球 -->
|
|
||||||
<div
|
|
||||||
class="absolute bottom-0 left-1/2 transform -translate-x-1/2 bg-[var(--color-bg-tertiary)] w-[15px] h-[15px] rounded-full"
|
|
||||||
></div>
|
|
||||||
</div>
|
|
||||||
<!-- 固定的标题 -->
|
|
||||||
<div
|
<div
|
||||||
class="card-item w-[45%] h-[41px] flex justify-center relative z-99 items-center rounded-[20px] bg-[var(--color-bg-tertiary)]"
|
class="card-item w-[45%] h-[41px] flex justify-center relative z-99 items-center rounded-[20px] bg-[var(--color-bg-tertiary)]"
|
||||||
>
|
>
|
||||||
流程
|
流程
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<el-card
|
|
||||||
v-for="item in collaborationProcess"
|
|
||||||
:key="item.Id"
|
|
||||||
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)"
|
|
||||||
>
|
|
||||||
<div class="text-[18px] font-bold text-center">{{ item.StepName }}</div>
|
|
||||||
<div class="h-[1px] w-full bg-[#494B51] my-[8px]"></div>
|
|
||||||
<div class="text-[14px] line-clamp-3 text-[var(--color-text-secondary)]">
|
|
||||||
{{ item.TaskContent }}
|
|
||||||
</div>
|
|
||||||
<div class="h-[1px] w-full bg-[#494B51] my-[8px]"></div>
|
|
||||||
<div class="flex items-center gap-2 flex-wrap relative">
|
|
||||||
<!-- 连接到智能体库的连接点 -->
|
|
||||||
<div
|
|
||||||
class="absolute left-[-10px] top-1/2 transform -translate-y-1/2"
|
|
||||||
:id="`task-syllabus-flow-agents-${item.Id}`"
|
|
||||||
></div>
|
|
||||||
<el-tooltip
|
|
||||||
v-for="agentSelection in item.AgentSelection"
|
|
||||||
:key="agentSelection"
|
|
||||||
effect="light"
|
|
||||||
placement="right"
|
|
||||||
>
|
|
||||||
<template #content>
|
|
||||||
<div class="w-[150px]">
|
|
||||||
<div class="text-[18px] font-bold">{{ agentSelection }}</div>
|
|
||||||
<div class="h-[1px] w-full bg-[#494B51] my-[8px]"></div>
|
|
||||||
<div>
|
|
||||||
{{
|
|
||||||
item.TaskProcess.find((i) => i.AgentName === agentSelection)?.Description
|
|
||||||
}}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
<div
|
|
||||||
class="w-[31px] h-[31px] rounded-full flex items-center justify-center"
|
|
||||||
:style="{ background: getAgentMapIcon(agentSelection).color }"
|
|
||||||
>
|
|
||||||
<svg-icon
|
|
||||||
:icon-class="getAgentMapIcon(agentSelection).icon"
|
|
||||||
color="var(--color-text)"
|
|
||||||
size="24px"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</el-tooltip>
|
|
||||||
</div>
|
|
||||||
</el-card>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
<!-- 产出 -->
|
<div class="flex-1 flex justify-center">
|
||||||
<div
|
<div
|
||||||
v-if="agentsStore.agentRawPlan.data"
|
class="card-item w-[45%] h-[41px] flex justify-center relative z-99 items-center rounded-[20px] bg-[var(--color-bg-tertiary)]"
|
||||||
class="w-[43%] h-full relative flex justify-center"
|
>
|
||||||
>
|
产物
|
||||||
<div class="min-h-full w-full relative">
|
|
||||||
<!-- 产出内容 -->
|
|
||||||
<div class="min-h-full relative z-10 flex flex-col items-center card-box pb-[100px]">
|
|
||||||
<!-- 背景那一根线 -->
|
|
||||||
<div
|
|
||||||
class="absolute h-full left-1/2 transform -translate-x-1/2 bg-[var(--color-bg-tertiary)] w-[5px] z-[1]"
|
|
||||||
>
|
|
||||||
<!-- 线底部的小圆球 -->
|
|
||||||
<div
|
|
||||||
class="absolute bottom-0 left-1/2 transform -translate-x-1/2 bg-[var(--color-bg-tertiary)] w-[15px] h-[15px] rounded-full"
|
|
||||||
></div>
|
|
||||||
</div>
|
|
||||||
<!-- 固定的标题 -->
|
|
||||||
<div
|
|
||||||
class="card-item w-[45%] h-[41px] flex justify-center items-center rounded-[20px] bg-[var(--color-bg-tertiary)] relative z-99"
|
|
||||||
>
|
|
||||||
产出
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
v-for="item in collaborationProcess"
|
|
||||||
:key="item.Id"
|
|
||||||
class="h-[158px] overflow-y-auto flex items-center w-full card-item relative z-99"
|
|
||||||
>
|
|
||||||
<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>
|
|
||||||
</el-card>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div
|
||||||
|
v-for="item in collaborationProcess"
|
||||||
|
:key="item.Id"
|
||||||
|
class="card-item w-full flex items-center gap-[14%]"
|
||||||
|
>
|
||||||
|
<!-- 流程卡片 -->
|
||||||
|
<el-card
|
||||||
|
class="w-[43%] overflow-y-auto relative z-99 task-syllabus-flow-card"
|
||||||
|
:class="agentsStore.currentTask?.StepName === item.StepName ? 'active-card' : ''"
|
||||||
|
shadow="hover"
|
||||||
|
:id="`task-syllabus-flow-${item.Id}`"
|
||||||
|
@click="changeTask(item, true)"
|
||||||
|
>
|
||||||
|
<MultiLineTooltip placement="right" :text="item.StepName" :lines="2">
|
||||||
|
<div class="text-[18px] font-bold text-center">
|
||||||
|
{{ item.StepName }}
|
||||||
|
</div>
|
||||||
|
</MultiLineTooltip>
|
||||||
|
<div class="h-[1px] w-full bg-[#494B51] my-[8px]"></div>
|
||||||
|
<MultiLineTooltip placement="right" :text="item.StepName" :lines="3">
|
||||||
|
<div
|
||||||
|
class="text-[14px] text-[var(--color-text-secondary)]"
|
||||||
|
:title="item.TaskContent"
|
||||||
|
>
|
||||||
|
{{ item.TaskContent }}
|
||||||
|
</div>
|
||||||
|
</MultiLineTooltip>
|
||||||
|
<div class="h-[1px] w-full bg-[#494B51] my-[8px]"></div>
|
||||||
|
<div
|
||||||
|
class="flex items-center gap-2 overflow-y-auto flex-wrap relative w-full max-h-[72px]"
|
||||||
|
>
|
||||||
|
<!-- 连接到智能体库的连接点 -->
|
||||||
|
<div
|
||||||
|
class="absolute left-[-10px] top-1/2 transform -translate-y-1/2"
|
||||||
|
:id="`task-syllabus-flow-agents-${item.Id}`"
|
||||||
|
></div>
|
||||||
|
<el-tooltip
|
||||||
|
v-for="agentSelection in item.AgentSelection"
|
||||||
|
:key="agentSelection"
|
||||||
|
effect="light"
|
||||||
|
placement="right"
|
||||||
|
>
|
||||||
|
<template #content>
|
||||||
|
<div class="w-[150px]">
|
||||||
|
<div class="text-[18px] font-bold">{{ agentSelection }}</div>
|
||||||
|
<div class="h-[1px] w-full bg-[#494B51] my-[8px]"></div>
|
||||||
|
<div>
|
||||||
|
{{
|
||||||
|
item.TaskProcess.find((i) => i.AgentName === agentSelection)?.Description
|
||||||
|
}}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<div
|
||||||
|
class="w-[31px] h-[31px] rounded-full flex items-center justify-center"
|
||||||
|
:style="{ background: getAgentMapIcon(agentSelection).color }"
|
||||||
|
>
|
||||||
|
<svg-icon
|
||||||
|
:icon-class="getAgentMapIcon(agentSelection).icon"
|
||||||
|
color="var(--color-text)"
|
||||||
|
size="24px"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</el-tooltip>
|
||||||
|
</div>
|
||||||
|
</el-card>
|
||||||
|
<!-- 产物卡片 -->
|
||||||
|
<el-card
|
||||||
|
class="w-[43%] 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>
|
||||||
|
</el-card>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.task-syllabus-flow-card {
|
||||||
|
:deep(.el-card__body) {
|
||||||
|
height: 100%;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
overflow: auto;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|||||||
19
src/main.ts
19
src/main.ts
@ -7,12 +7,17 @@ import './styles/tailwindcss.css'
|
|||||||
import 'element-plus/theme-chalk/dark/css-vars.css'
|
import 'element-plus/theme-chalk/dark/css-vars.css'
|
||||||
import 'virtual:svg-icons-register'
|
import 'virtual:svg-icons-register'
|
||||||
import { initService } from '@/utils/request.ts'
|
import { initService } from '@/utils/request.ts'
|
||||||
import { setupStore } from '@/stores'
|
import { setupStore, useConfigStore } from '@/stores'
|
||||||
|
|
||||||
const app = createApp(App)
|
async function init() {
|
||||||
|
const app = createApp(App)
|
||||||
|
setupStore(app)
|
||||||
|
initService()
|
||||||
|
const configStore = useConfigStore()
|
||||||
|
await configStore.initConfig()
|
||||||
|
document.title = configStore.config.centerTitle
|
||||||
|
app.use(router)
|
||||||
|
app.mount('#app')
|
||||||
|
}
|
||||||
|
|
||||||
initService()
|
void init()
|
||||||
setupStore(app)
|
|
||||||
app.use(router)
|
|
||||||
|
|
||||||
app.mount('#app')
|
|
||||||
|
|||||||
@ -9,3 +9,4 @@ export function setupStore(app: App<Element>) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export * from './modules/agents.ts'
|
export * from './modules/agents.ts'
|
||||||
|
export * from './modules/config.ts'
|
||||||
|
|||||||
@ -5,7 +5,7 @@ import { v4 as uuidv4 } from 'uuid'
|
|||||||
import { store } from '../index'
|
import { store } from '../index'
|
||||||
import { useStorage } from '@vueuse/core'
|
import { useStorage } from '@vueuse/core'
|
||||||
import type { IExecuteRawResponse } from '@/api'
|
import type { IExecuteRawResponse } from '@/api'
|
||||||
|
import { useConfigStore } from '@/stores/modules/config.ts'
|
||||||
|
|
||||||
export interface Agent {
|
export interface Agent {
|
||||||
Name: string
|
Name: string
|
||||||
@ -17,12 +17,15 @@ export interface Agent {
|
|||||||
type HslColorVector = [number, number, number]
|
type HslColorVector = [number, number, number]
|
||||||
|
|
||||||
export interface IRichText {
|
export interface IRichText {
|
||||||
template: string;
|
template: string
|
||||||
data: Record<string, {
|
data: Record<
|
||||||
text: string;
|
string,
|
||||||
style?: Record<string, string>
|
{
|
||||||
color?: HslColorVector
|
text: string
|
||||||
}>;
|
style?: Record<string, string>
|
||||||
|
color?: HslColorVector
|
||||||
|
}
|
||||||
|
>
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface TaskProcess {
|
export interface TaskProcess {
|
||||||
@ -50,18 +53,45 @@ export interface IRawPlanResponse {
|
|||||||
'Collaboration Process'?: IRawStepTask[]
|
'Collaboration Process'?: IRawStepTask[]
|
||||||
}
|
}
|
||||||
|
|
||||||
export const useAgentsStore = defineStore('counter', () => {
|
const storageKey = '$agents' as const
|
||||||
const agents = useStorage<Agent[]>('agents-v1', [])
|
|
||||||
|
// 清除所有以 storageKey 开头的 localStorage
|
||||||
|
function clearStorageByVersion() {
|
||||||
|
Object.keys(localStorage)
|
||||||
|
.filter((key) => key.startsWith(storageKey))
|
||||||
|
.forEach((key) => localStorage.removeItem(key))
|
||||||
|
}
|
||||||
|
|
||||||
|
export const useAgentsStore = defineStore('agents', () => {
|
||||||
|
const configStore = useConfigStore()
|
||||||
|
|
||||||
|
const agents = useStorage<Agent[]>(`${storageKey}-repository`, [])
|
||||||
function setAgents(agent: Agent[]) {
|
function setAgents(agent: Agent[]) {
|
||||||
agents.value = agent
|
agents.value = agent
|
||||||
}
|
}
|
||||||
|
|
||||||
// 任务搜索的内容
|
// 任务搜索的内容
|
||||||
const searchValue = useStorage<string>('agent-search-value', '')
|
const searchValue = useStorage<string>(`${storageKey}-search-value`, '')
|
||||||
function setSearchValue(value: string) {
|
function setSearchValue(value: string) {
|
||||||
searchValue.value = value
|
searchValue.value = value
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const storageVersionIdentifier = useStorage<string>(`${storageKey}-storage-version-identifier`, '')
|
||||||
|
// 监听 configStore.config.agentRepository.storageVersionIdentifier 改变
|
||||||
|
watch(
|
||||||
|
() => configStore.config.agentRepository.storageVersionIdentifier,
|
||||||
|
(value) => {
|
||||||
|
// value与storageVersionIdentifier不一致清除所有storageKey开头的localStorage
|
||||||
|
if (value !== storageVersionIdentifier.value) {
|
||||||
|
clearStorageByVersion()
|
||||||
|
storageVersionIdentifier.value = value
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
immediate: true,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
// 当前的展示的任务流程
|
// 当前的展示的任务流程
|
||||||
const currentTask = ref<IRawStepTask>()
|
const currentTask = ref<IRawStepTask>()
|
||||||
function setCurrentTask(task: IRawStepTask) {
|
function setCurrentTask(task: IRawStepTask) {
|
||||||
@ -72,7 +102,7 @@ export const useAgentsStore = defineStore('counter', () => {
|
|||||||
|
|
||||||
function setAgentRawPlan(plan: { data?: IRawPlanResponse; loading?: boolean }) {
|
function setAgentRawPlan(plan: { data?: IRawPlanResponse; loading?: boolean }) {
|
||||||
if (plan.data) {
|
if (plan.data) {
|
||||||
plan.data['Collaboration Process'] = plan.data['Collaboration Process']?.map(item => ({
|
plan.data['Collaboration Process'] = plan.data['Collaboration Process']?.map((item) => ({
|
||||||
...item,
|
...item,
|
||||||
Id: uuidv4(),
|
Id: uuidv4(),
|
||||||
}))
|
}))
|
||||||
@ -97,7 +127,6 @@ export const useAgentsStore = defineStore('counter', () => {
|
|||||||
executePlan.value = []
|
executePlan.value = []
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
agents,
|
agents,
|
||||||
setAgents,
|
setAgents,
|
||||||
|
|||||||
39
src/stores/modules/config.ts
Normal file
39
src/stores/modules/config.ts
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
import { defineStore } from 'pinia'
|
||||||
|
import { readConfig } from '@/utils/readJson.ts'
|
||||||
|
import { store } from '@/stores'
|
||||||
|
|
||||||
|
export interface Config {
|
||||||
|
title: string
|
||||||
|
subTitle: string
|
||||||
|
centerTitle: string
|
||||||
|
taskPromptWords: string[]
|
||||||
|
agentRepository: {
|
||||||
|
storageVersionIdentifier: string
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export const useConfigStore = defineStore('config', () => {
|
||||||
|
const config = ref<Config>({} as Config)
|
||||||
|
|
||||||
|
// 异步调用readConfig
|
||||||
|
async function initConfig() {
|
||||||
|
config.value = await readConfig<Config>('config.json')
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
return {
|
||||||
|
config,
|
||||||
|
initConfig
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 用于在组件外部(如在Pinia Store 中)使用 Pinia 提供的 store 实例。
|
||||||
|
* 官方文档解释了如何在组件外部使用 Pinia Store:
|
||||||
|
* https://pinia.vuejs.org/core-concepts/outside-component-usage.html#using-a-store-outside-of-a-component
|
||||||
|
*/
|
||||||
|
export function useConfigStoreHook() {
|
||||||
|
return useConfigStore(store)
|
||||||
|
}
|
||||||
@ -3,7 +3,7 @@ import axios from 'axios'
|
|||||||
import qs from 'qs'
|
import qs from 'qs'
|
||||||
import type { Ref } from 'vue'
|
import type { Ref } from 'vue'
|
||||||
import { ElNotification } from 'element-plus'
|
import { ElNotification } from 'element-plus'
|
||||||
import { ref, onMounted } from 'vue'
|
import { ref } from 'vue'
|
||||||
|
|
||||||
// 创建 axios 实例
|
// 创建 axios 实例
|
||||||
let service: AxiosInstance
|
let service: AxiosInstance
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user