feat: 重构前后端架构,添加Go后端和Python Agent服务
- 新增 Go 语言后端服务(server/),包含用户认证、Agent管理、数据库连接等API - 新增 Python Agent 服务(agent/),实现Agent核心逻辑和工具集 - 前端从原生HTML迁移到Vue.js框架(web/src/) - 添加 Docker Compose 支持(docker-compose.yml) - 添加项目架构文档(docs/ARCHITECTURE.md) - 添加环境变量示例(.env.example)和本地启动脚本(start-local.ps1) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -46,6 +46,27 @@ html.dark .el-select .el-select__wrapper {
|
||||
min-height: 42px;
|
||||
}
|
||||
|
||||
html.dark .el-select:hover .el-input__wrapper,
|
||||
html.dark .el-select:hover .el-select__wrapper {
|
||||
background-color: #1a1c25 !important;
|
||||
}
|
||||
|
||||
/* 修复鼠标移出后出现白色背景的问题 */
|
||||
html.dark .el-select .el-input__wrapper:hover,
|
||||
html.dark .el-select .el-select__wrapper:hover {
|
||||
background-color: #1a1c25 !important;
|
||||
}
|
||||
|
||||
html.dark .el-select .el-input__wrapper,
|
||||
html.dark .el-select .el-select__wrapper {
|
||||
background-color: #1a1c25 !important;
|
||||
}
|
||||
|
||||
html.dark .el-form-item__content .el-input__wrapper,
|
||||
html.dark .el-form-item__content .el-select .el-input__wrapper {
|
||||
background-color: #1a1c25 !important;
|
||||
}
|
||||
|
||||
html.dark .el-select.el-select--large .el-input__wrapper {
|
||||
padding: 5px 11px;
|
||||
min-height: 46px;
|
||||
@@ -188,6 +209,31 @@ html.dark .el-select .el-tag .el-tag__close:hover {
|
||||
color: #ffffff;
|
||||
}
|
||||
|
||||
/* el-checkbox 暗色主题 - 金黄色选中 */
|
||||
html.dark .el-checkbox {
|
||||
--el-checkbox-checked-text-color: #ffb700;
|
||||
--el-checkbox-checked-bg-color: #ffb700;
|
||||
--el-checkbox-checked-border-color: #ffb700;
|
||||
--el-checkbox-input-border-color-hover: #ffb700;
|
||||
}
|
||||
|
||||
html.dark .el-checkbox .el-checkbox__input.is-checked .el-checkbox__inner {
|
||||
background-color: #ffb700;
|
||||
border-color: #ffb700;
|
||||
}
|
||||
|
||||
html.dark .el-checkbox .el-checkbox__input.is-checked .el-checkbox__inner::after {
|
||||
border-color: #1f2937;
|
||||
}
|
||||
|
||||
html.dark .el-checkbox .el-checkbox__label {
|
||||
color: #e5e7eb;
|
||||
}
|
||||
|
||||
html.dark .el-checkbox:hover .el-checkbox__inner {
|
||||
border-color: #ffb700;
|
||||
}
|
||||
|
||||
/* 柱状图增长动画 */
|
||||
@keyframes bar-grow {
|
||||
from {
|
||||
@@ -222,3 +268,344 @@ html.dark .el-select .el-tag .el-tag__close:hover {
|
||||
opacity: 0;
|
||||
animation: progress-grow 2.4s cubic-bezier(0.4, 0, 0.2, 1) forwards;
|
||||
}
|
||||
|
||||
/* ===== 通用组件交互优化 ===== */
|
||||
|
||||
/* 通用弹窗动画 */
|
||||
@keyframes modal-fade-in {
|
||||
0% {
|
||||
opacity: 0;
|
||||
}
|
||||
100% {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes modal-scale-in {
|
||||
0% {
|
||||
opacity: 0;
|
||||
transform: scale(0.95) translateY(10px);
|
||||
}
|
||||
100% {
|
||||
opacity: 1;
|
||||
transform: scale(1) translateY(0);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes modal-slide-up {
|
||||
0% {
|
||||
opacity: 0;
|
||||
transform: translateY(20px);
|
||||
}
|
||||
100% {
|
||||
opacity: 1;
|
||||
transform: translateY(0);
|
||||
}
|
||||
}
|
||||
|
||||
.modal-overlay {
|
||||
animation: modal-fade-in 0.2s ease-out forwards;
|
||||
}
|
||||
|
||||
.modal-content {
|
||||
animation: modal-scale-in 0.3s cubic-bezier(0.16, 1, 0.3, 1) forwards;
|
||||
}
|
||||
|
||||
/* 按钮交互优化 */
|
||||
.btn-primary {
|
||||
@apply bg-gradient-to-r from-primary-orange to-red-500 text-white px-4 py-2 rounded-lg font-medium flex items-center gap-2 transition-all duration-200;
|
||||
}
|
||||
|
||||
.btn-primary:hover {
|
||||
@apply from-orange-500 to-red-600;
|
||||
transform: translateY(-1px);
|
||||
box-shadow: 0 4px 12px rgba(255, 107, 53, 0.3);
|
||||
}
|
||||
|
||||
.btn-primary:active {
|
||||
transform: translateY(0);
|
||||
box-shadow: 0 2px 8px rgba(255, 107, 53, 0.2);
|
||||
}
|
||||
|
||||
.btn-primary:disabled {
|
||||
@apply opacity-50 cursor-not-allowed;
|
||||
transform: none;
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
.btn-secondary {
|
||||
@apply bg-dark-600 text-gray-300 px-4 py-2 rounded-lg border border-dark-500 transition-all duration-200;
|
||||
}
|
||||
|
||||
.btn-secondary:hover {
|
||||
@apply bg-dark-500 border-gray-500;
|
||||
}
|
||||
|
||||
.btn-secondary:active {
|
||||
transform: scale(0.98);
|
||||
}
|
||||
|
||||
.btn-icon {
|
||||
@apply p-2 rounded-lg transition-all duration-150;
|
||||
}
|
||||
|
||||
.btn-icon:hover {
|
||||
@apply bg-dark-500;
|
||||
transform: scale(1.05);
|
||||
}
|
||||
|
||||
.btn-icon:active {
|
||||
transform: scale(0.95);
|
||||
}
|
||||
|
||||
/* 表单输入框交互 */
|
||||
.input-field {
|
||||
@apply w-full bg-dark-600 border border-dark-500 rounded-lg px-4 py-2.5 text-white placeholder-gray-500 transition-all duration-200;
|
||||
}
|
||||
|
||||
.input-field:focus {
|
||||
@apply outline-none border-primary-orange;
|
||||
box-shadow: 0 0 0 3px rgba(255, 149, 0, 0.15);
|
||||
}
|
||||
|
||||
.input-field:hover:not(:focus) {
|
||||
@apply border-gray-500;
|
||||
}
|
||||
|
||||
/* 表格样式优化 */
|
||||
.table-row {
|
||||
@apply border-t border-dark-600 transition-all duration-200;
|
||||
}
|
||||
|
||||
.table-row:hover {
|
||||
@apply bg-dark-600/50;
|
||||
}
|
||||
|
||||
.table-row:active {
|
||||
@apply bg-dark-600;
|
||||
}
|
||||
|
||||
/* 表格头部 */
|
||||
.table-header {
|
||||
@apply bg-dark-600 text-sm font-medium text-gray-400;
|
||||
}
|
||||
|
||||
/* 搜索框交互 */
|
||||
.search-input {
|
||||
@apply bg-dark-600 border border-dark-500 rounded-lg py-2 pl-10 pr-4 text-white placeholder-gray-500 transition-all duration-200;
|
||||
}
|
||||
|
||||
.search-input:focus {
|
||||
@apply outline-none border-primary-orange;
|
||||
box-shadow: 0 0 0 3px rgba(255, 149, 0, 0.15);
|
||||
}
|
||||
|
||||
.search-input:hover:not(:focus) {
|
||||
@apply border-gray-500;
|
||||
}
|
||||
|
||||
/* 空状态优化 */
|
||||
.empty-state {
|
||||
@apply py-12 text-center transition-all duration-300;
|
||||
}
|
||||
|
||||
.empty-state-icon {
|
||||
@apply text-gray-500 text-4xl mb-3 transition-transform duration-300;
|
||||
}
|
||||
|
||||
.empty-state:hover .empty-state-icon {
|
||||
@apply text-gray-400;
|
||||
transform: scale(1.1);
|
||||
}
|
||||
|
||||
/* 卡片悬停效果 */
|
||||
.card-hover {
|
||||
@apply transition-all duration-200;
|
||||
}
|
||||
|
||||
.card-hover:hover {
|
||||
@apply shadow-lg;
|
||||
transform: translateY(-2px);
|
||||
}
|
||||
|
||||
/* 标签/徽章样式 */
|
||||
.badge {
|
||||
@apply px-2 py-1 rounded text-sm font-medium transition-all duration-200;
|
||||
}
|
||||
|
||||
.badge-success {
|
||||
@apply bg-green-500/20 text-green-400;
|
||||
}
|
||||
|
||||
.badge-warning {
|
||||
@apply bg-yellow-500/20 text-yellow-400;
|
||||
}
|
||||
|
||||
.badge-error {
|
||||
@apply bg-red-500/20 text-red-400;
|
||||
}
|
||||
|
||||
.badge-info {
|
||||
@apply bg-blue-500/20 text-blue-400;
|
||||
}
|
||||
|
||||
.badge-default {
|
||||
@apply bg-gray-500/20 text-gray-400;
|
||||
}
|
||||
|
||||
/* 状态指示点 */
|
||||
.status-dot {
|
||||
@apply w-2 h-2 rounded-full transition-all duration-200;
|
||||
}
|
||||
|
||||
.status-dot-active {
|
||||
@apply bg-primary-success;
|
||||
box-shadow: 0 0 8px rgba(34, 197, 94, 0.5);
|
||||
}
|
||||
|
||||
.status-dot-inactive {
|
||||
@apply bg-gray-500;
|
||||
}
|
||||
|
||||
.status-dot-error {
|
||||
@apply bg-primary-danger;
|
||||
box-shadow: 0 0 8px rgba(239, 68, 68, 0.5);
|
||||
}
|
||||
|
||||
/* 加载动画 */
|
||||
@keyframes pulse-dot {
|
||||
0%, 100% {
|
||||
opacity: 1;
|
||||
}
|
||||
50% {
|
||||
opacity: 0.5;
|
||||
}
|
||||
}
|
||||
|
||||
.loading-pulse {
|
||||
animation: pulse-dot 1.5s ease-in-out infinite;
|
||||
}
|
||||
|
||||
/* 步骤指示器 */
|
||||
.step-indicator {
|
||||
@apply flex items-center justify-center gap-4 py-4;
|
||||
}
|
||||
|
||||
.step-item {
|
||||
@apply flex items-center gap-2;
|
||||
}
|
||||
|
||||
.step-circle {
|
||||
@apply w-8 h-8 rounded-full flex items-center justify-center text-sm font-medium transition-all duration-300;
|
||||
}
|
||||
|
||||
.step-line {
|
||||
@apply w-16 h-0.5 transition-all duration-300;
|
||||
}
|
||||
|
||||
/* 复选框优化 */
|
||||
.checkbox-custom {
|
||||
@apply w-4 h-4 rounded border-dark-500 bg-dark-600 text-primary-cyan transition-all duration-200;
|
||||
}
|
||||
|
||||
.checkbox-custom:focus {
|
||||
box-shadow: 0 0 0 3px rgba(34, 211, 238, 0.2);
|
||||
}
|
||||
|
||||
.checkbox-custom:checked {
|
||||
@apply bg-primary-cyan border-primary-cyan;
|
||||
}
|
||||
|
||||
/* 工具提示 */
|
||||
.tooltip {
|
||||
@apply absolute z-50 px-2 py-1 text-xs rounded bg-dark-700 text-white shadow-lg pointer-events-none opacity-0 transition-opacity duration-200;
|
||||
}
|
||||
|
||||
.tooltip-visible {
|
||||
@apply opacity-100;
|
||||
}
|
||||
|
||||
/* 滚动条美化 */
|
||||
::-webkit-scrollbar {
|
||||
@apply w-2 h-2;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-track {
|
||||
@apply bg-dark-800 rounded-full;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-thumb {
|
||||
@apply bg-dark-600 rounded-full;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-thumb:hover {
|
||||
@apply bg-gray-600;
|
||||
}
|
||||
|
||||
/* 渐变文字 */
|
||||
.text-gradient {
|
||||
@apply bg-clip-text text-transparent bg-gradient-to-r from-primary-orange to-red-500;
|
||||
}
|
||||
|
||||
/* 玻璃拟态效果 */
|
||||
.glass {
|
||||
@apply bg-dark-700/80 backdrop-blur-md border border-dark-500/50;
|
||||
}
|
||||
|
||||
/* 焦点环 */
|
||||
.focus-ring {
|
||||
@apply focus:outline-none focus:ring-2 focus:ring-primary-orange/50 focus:ring-offset-2 focus:ring-offset-dark-900;
|
||||
}
|
||||
|
||||
/* ===== 加载动画 ===== */
|
||||
|
||||
/* 简洁旋转 Loading */
|
||||
@keyframes loading-spin {
|
||||
0% { transform: rotate(0deg); }
|
||||
100% { transform: rotate(360deg); }
|
||||
}
|
||||
|
||||
.loading-spin {
|
||||
animation: loading-spin 1s linear infinite;
|
||||
}
|
||||
|
||||
/* 三个点脉冲 */
|
||||
@keyframes loading-dots {
|
||||
0%, 80%, 100% { transform: scale(0.6); opacity: 0.5; }
|
||||
40% { transform: scale(1); opacity: 1; }
|
||||
}
|
||||
|
||||
.loading-dots span {
|
||||
display: inline-block;
|
||||
width: 8px;
|
||||
height: 8px;
|
||||
margin: 0 4px;
|
||||
border-radius: 50%;
|
||||
animation: loading-dots 1.4s ease-in-out infinite both;
|
||||
}
|
||||
|
||||
.loading-dots span:nth-child(1) { animation-delay: -0.32s; }
|
||||
.loading-dots span:nth-child(2) { animation-delay: -0.16s; }
|
||||
.loading-dots span:nth-child(3) { animation-delay: 0s; }
|
||||
|
||||
/* 进度条动画 */
|
||||
@keyframes loading-progress {
|
||||
0% { width: 0%; }
|
||||
50% { width: 70%; }
|
||||
100% { width: 100%; }
|
||||
}
|
||||
|
||||
.loading-progress-bar {
|
||||
animation: loading-progress 2s ease-in-out infinite;
|
||||
}
|
||||
|
||||
/* 骨架屏闪烁 */
|
||||
@keyframes loading-skeleton {
|
||||
0% { opacity: 0.4; }
|
||||
50% { opacity: 0.7; }
|
||||
100% { opacity: 0.4; }
|
||||
}
|
||||
|
||||
.loading-skeleton {
|
||||
animation: loading-skeleton 1.5s ease-in-out infinite;
|
||||
}
|
||||
|
||||
@@ -231,7 +231,7 @@ const statusClass = (status: string) => {
|
||||
<i class="fa-solid fa-robot text-gray-400"></i>
|
||||
<span class="font-medium">Agents</span>
|
||||
</div>
|
||||
<button @click="openCreate" class="bg-gradient-to-r from-primary-orange to-red-500 hover:from-orange-500 hover:to-red-600 text-white px-4 py-2 rounded-lg font-medium flex items-center gap-2 transition-all">
|
||||
<button @click="openCreate" class="btn-primary">
|
||||
<i class="fa-solid fa-plus"></i>
|
||||
New Agent
|
||||
</button>
|
||||
@@ -245,7 +245,7 @@ const statusClass = (status: string) => {
|
||||
v-model="searchQuery"
|
||||
type="text"
|
||||
placeholder="Search agents..."
|
||||
class="w-full bg-dark-600 border border-dark-500 rounded-lg py-2 pl-10 pr-4 text-white placeholder-gray-500 focus:outline-none focus:border-primary-orange"
|
||||
class="search-input w-full"
|
||||
>
|
||||
</div>
|
||||
<el-select v-model="filterStatus" placeholder="Select" class="w-40" size="large">
|
||||
@@ -271,7 +271,7 @@ const statusClass = (status: string) => {
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr v-for="agent in filteredAgents()" :key="agent.id" class="border-t border-dark-600 hover:bg-dark-600/50 transition-colors">
|
||||
<tr v-for="agent in filteredAgents()" :key="agent.id" class="table-row">
|
||||
<td class="px-5 py-4">
|
||||
<div class="font-medium">{{ agent.name }}</div>
|
||||
<div class="text-sm text-gray-500">{{ agent.description }}</div>
|
||||
@@ -294,21 +294,21 @@ const statusClass = (status: string) => {
|
||||
<div class="flex items-center justify-end gap-2">
|
||||
<button
|
||||
@click="toggleStatus(agent)"
|
||||
class="p-2 rounded-lg hover:bg-dark-500 transition-colors"
|
||||
class="btn-icon"
|
||||
:title="agent.status === 'running' ? 'Stop' : 'Start'"
|
||||
>
|
||||
<i :class="['fa-solid', agent.status === 'running' ? 'fa-stop' : 'fa-play', 'text-gray-400 hover:text-white']"></i>
|
||||
<i :class="['fa-solid', agent.status === 'running' ? 'fa-stop' : 'fa-play', 'text-gray-400']"></i>
|
||||
</button>
|
||||
<button
|
||||
@click="openEdit(agent)"
|
||||
class="p-2 rounded-lg hover:bg-dark-500 transition-colors"
|
||||
class="btn-icon"
|
||||
title="Edit"
|
||||
>
|
||||
<i class="fa-solid fa-pen text-gray-400 hover:text-white"></i>
|
||||
<i class="fa-solid fa-pen text-gray-400"></i>
|
||||
</button>
|
||||
<button
|
||||
@click="deleteAgent(agent.id)"
|
||||
class="p-2 rounded-lg hover:bg-dark-500 transition-colors"
|
||||
class="btn-icon"
|
||||
title="Delete"
|
||||
>
|
||||
<i class="fa-solid fa-trash text-gray-400 hover:text-primary-danger"></i>
|
||||
@@ -328,7 +328,7 @@ const statusClass = (status: string) => {
|
||||
|
||||
<!-- 编辑弹窗 -->
|
||||
<Teleport to="body">
|
||||
<div v-if="isEditing" class="fixed inset-0 bg-black/60 flex items-center justify-center z-50" @click.self="cancelEdit">
|
||||
<div v-if="isEditing" class="fixed inset-0 bg-black/60 flex items-center justify-center z-50">
|
||||
<div class="bg-dark-700 rounded-2xl w-full max-w-lg border border-dark-500 shadow-2xl">
|
||||
<!-- 弹窗头部 -->
|
||||
<div class="flex items-center justify-between p-5 border-b border-dark-500">
|
||||
@@ -345,7 +345,7 @@ const statusClass = (status: string) => {
|
||||
<input
|
||||
v-model="editForm.name"
|
||||
type="text"
|
||||
class="w-full bg-dark-600 border border-dark-500 rounded-lg px-4 py-2.5 text-white focus:outline-none focus:border-primary-orange"
|
||||
class="input-field"
|
||||
>
|
||||
</div>
|
||||
|
||||
@@ -374,7 +374,7 @@ const statusClass = (status: string) => {
|
||||
<textarea
|
||||
v-model="editForm.description"
|
||||
rows="3"
|
||||
class="w-full bg-dark-600 border border-dark-500 rounded-lg px-4 py-2.5 text-white focus:outline-none focus:border-primary-orange resize-none"
|
||||
class="input-field resize-none"
|
||||
></textarea>
|
||||
</div>
|
||||
</div>
|
||||
@@ -383,13 +383,13 @@ const statusClass = (status: string) => {
|
||||
<div class="flex items-center justify-end gap-3 p-5 border-t border-dark-500">
|
||||
<button
|
||||
@click="cancelEdit"
|
||||
class="px-4 py-2 rounded-lg bg-dark-600 text-gray-300 hover:bg-dark-500 transition-colors"
|
||||
class="btn-secondary"
|
||||
>
|
||||
Cancel
|
||||
</button>
|
||||
<button
|
||||
@click="saveEdit"
|
||||
class="px-4 py-2 rounded-lg bg-gradient-to-r from-primary-orange to-red-500 text-white hover:from-orange-500 hover:to-red-600 transition-all"
|
||||
class="btn-primary"
|
||||
>
|
||||
Save Changes
|
||||
</button>
|
||||
@@ -400,7 +400,7 @@ const statusClass = (status: string) => {
|
||||
|
||||
<!-- 新建 Agent 模态框 -->
|
||||
<Teleport to="body">
|
||||
<div v-if="isCreating" class="fixed inset-0 bg-black/80 flex items-center justify-center z-50 p-4" @click.self="closeCreate">
|
||||
<div v-if="isCreating" class="fixed inset-0 bg-black/80 flex items-center justify-center z-50 p-4">
|
||||
<div class="bg-dark-800 rounded-2xl w-full max-w-6xl h-[85vh] border border-dark-600 shadow-2xl overflow-hidden flex flex-col animate-modal-in">
|
||||
<!-- 模态框头部 -->
|
||||
<div class="flex items-center justify-between p-5 border-b border-dark-600 bg-dark-700/50">
|
||||
@@ -625,13 +625,13 @@ const statusClass = (status: string) => {
|
||||
<div class="flex items-center gap-3">
|
||||
<button
|
||||
@click="closeCreate"
|
||||
class="px-6 py-2.5 rounded-xl bg-dark-600 text-gray-300 hover:bg-dark-500 border border-dark-500 transition-all hover:scale-105"
|
||||
class="btn-secondary px-6 py-2.5"
|
||||
>
|
||||
Cancel
|
||||
</button>
|
||||
<button
|
||||
@click="saveNewAgent"
|
||||
class="px-6 py-2.5 rounded-xl bg-gradient-to-r from-primary-orange to-red-500 text-white hover:from-orange-500 hover:to-red-600 transition-all hover:scale-105 shadow-lg shadow-primary-orange/30 flex items-center gap-2"
|
||||
class="btn-primary px-6 py-2.5"
|
||||
>
|
||||
<i class="fa-solid fa-plus"></i>
|
||||
Create Agent
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,6 +1,5 @@
|
||||
<script setup lang="ts">
|
||||
import { ref, onMounted, onUnmounted, nextTick } from 'vue'
|
||||
import * as echarts from 'echarts'
|
||||
import { ref, nextTick } from 'vue'
|
||||
|
||||
interface MCPServer {
|
||||
id: number
|
||||
@@ -321,19 +320,28 @@ const isEditingFileClosing = ref(false)
|
||||
// AI 对话面板
|
||||
const isChatOpen = ref(false)
|
||||
|
||||
// 图谱生成相关
|
||||
// 流程生成相关
|
||||
const isGeneratingGraph = ref(false)
|
||||
const isGraphGenerated = ref(false)
|
||||
|
||||
const generateGraph = () => {
|
||||
isGeneratingGraph.value = true
|
||||
// 模拟生成图谱的等待过程
|
||||
// 模拟生成流程的等待过程
|
||||
setTimeout(() => {
|
||||
isGeneratingGraph.value = false
|
||||
isGraphGenerated.value = true
|
||||
nextTick(() => {
|
||||
initGraphChart()
|
||||
})
|
||||
// 初始化树结构 - 默认展开第一层
|
||||
if (workflowData.value.length > 0) {
|
||||
workflowData.value[0].expanded = true
|
||||
if (workflowData.value[0].children) {
|
||||
workflowData.value[0].children.forEach(child => {
|
||||
child.expanded = false
|
||||
if (child.children) {
|
||||
child.children.forEach(gc => gc.expanded = false)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
}, 2500)
|
||||
}
|
||||
|
||||
@@ -424,254 +432,106 @@ const sendMessage = () => {
|
||||
}, 1000)
|
||||
}
|
||||
|
||||
// 图谱相关
|
||||
const graphChartRef = ref<HTMLElement | null>(null)
|
||||
let chartInstance: echarts.ECharts | null = null
|
||||
// 流程相关 - 金字塔结构
|
||||
|
||||
const initGraphChart = () => {
|
||||
if (!graphChartRef.value) return
|
||||
|
||||
// 节点数据 - 更有科技感的节点,添加初始位置
|
||||
const nodes = [
|
||||
{ id: '1', name: '用户问题', category: 0, symbolSize: 70, x: 0, y: -200, desc: '用户提出的原始问题' },
|
||||
{ id: '2', name: '问题分析', category: 1, symbolSize: 55, x: 0, y: -80, desc: '对问题进行拆解和分析' },
|
||||
{ id: '3', name: '数据获取', category: 1, symbolSize: 50, x: -120, y: 20, desc: '从数据源获取相关信息' },
|
||||
{ id: '4', name: '方案生成', category: 2, symbolSize: 55, x: 0, y: 80, desc: '基于分析结果生成解决方案' },
|
||||
{ id: '5', name: '方案评估', category: 2, symbolSize: 45, x: 0, y: 180, desc: '评估方案的可行性和效果' },
|
||||
{ id: '6', name: '最终输出', category: 3, symbolSize: 60, x: 0, y: 280, desc: '生成最终的问题解答' },
|
||||
{ id: '7', name: '知识库', category: 0, symbolSize: 40, x: 150, y: 0, desc: '存储领域知识' },
|
||||
{ id: '8', name: '规则引擎', category: 2, symbolSize: 40, x: 120, y: 120, desc: '业务规则处理' },
|
||||
{ id: '9', name: 'AI模型', category: 3, symbolSize: 45, x: -100, y: 150, desc: '大语言模型调用' },
|
||||
{ id: '10', name: '向量检索', category: 1, symbolSize: 38, x: -150, y: -20, desc: '相似度检索' },
|
||||
]
|
||||
|
||||
const links = [
|
||||
{ source: '1', target: '2', label: '分析' },
|
||||
{ source: '2', target: '3', label: '获取' },
|
||||
{ source: '2', target: '7', label: '查询' },
|
||||
{ source: '2', target: '10', label: '检索' },
|
||||
{ source: '3', target: '4', label: '生成' },
|
||||
{ source: '10', target: '4', label: '增强' },
|
||||
{ source: '4', target: '5', label: '评估' },
|
||||
{ source: '4', target: '8', label: '校验' },
|
||||
{ source: '4', target: '9', label: '调用' },
|
||||
{ source: '5', target: '6', label: '输出' },
|
||||
{ source: '7', target: '4', label: '支撑' },
|
||||
]
|
||||
|
||||
// 深邃科技配色 - 更赛博朋克的感觉
|
||||
const CAT_COLORS = [
|
||||
{ main: '#00D9FF', glow: '#00D9FF', gradient: ['#0077B6', '#00D9FF'] }, // 青色 - 语义对象
|
||||
{ main: '#10B981', glow: '#10B981', gradient: ['#059669', '#34D399'] }, // 绿色 - 对象行为
|
||||
{ main: '#F59E0B', glow: '#F59E0B', gradient: ['#D97706', '#FBBF24'] }, // 琥珀色 - 约束规则
|
||||
{ main: '#8B5CF6', glow: '#A78BFA', gradient: ['#6D28D9', '#A78BFA'] }, // 紫色 - 编排流程
|
||||
]
|
||||
|
||||
const option = {
|
||||
backgroundColor: 'transparent',
|
||||
// 全局特效
|
||||
graphic: [
|
||||
{
|
||||
type: 'group',
|
||||
children: [
|
||||
// 背景光晕1
|
||||
{
|
||||
type: 'circle',
|
||||
shape: { cx: '30%', cy: '30%', r: 200 },
|
||||
style: { fill: 'radialGradient', gradient: { type: 'radial', colorStops: [{ offset: 0, color: 'rgba(0, 217, 255, 0.15)' }, { offset: 1, color: 'transparent' }] } },
|
||||
},
|
||||
// 背景光晕2
|
||||
{
|
||||
type: 'circle',
|
||||
shape: { cx: '70%', cy: '70%', r: 180 },
|
||||
style: { fill: 'radialGradient', gradient: { type: 'radial', colorStops: [{ offset: 0, color: 'rgba(139, 92, 246, 0.12)' }, { offset: 1, color: 'transparent' }] } },
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
tooltip: {
|
||||
trigger: 'item',
|
||||
backgroundColor: 'rgba(10, 14, 26, 0.95)',
|
||||
borderColor: 'rgba(0, 217, 255, 0.3)',
|
||||
borderWidth: 1,
|
||||
textStyle: { color: '#e6edf3', fontSize: 12 },
|
||||
extraCssText: 'box-shadow: 0 0 20px rgba(0, 217, 255, 0.2); border-radius: 8px;',
|
||||
},
|
||||
series: [{
|
||||
type: 'graph',
|
||||
layout: 'force',
|
||||
// 持续动画 - 节点漂浮
|
||||
animation: true,
|
||||
animationDuration: 1500,
|
||||
animationEasingUpdate: 'quinticInOut',
|
||||
// 布局参数 - 修复边连接问题
|
||||
force: {
|
||||
repulsion: 350,
|
||||
edgeLength: [80, 180],
|
||||
gravity: 0.1,
|
||||
layoutAnimation: true,
|
||||
friction: 0.85,
|
||||
alphaDecay: 0.02,
|
||||
},
|
||||
// 节点数据
|
||||
data: nodes.map(n => {
|
||||
const color = CAT_COLORS[n.category]
|
||||
return {
|
||||
id: n.id,
|
||||
name: n.name,
|
||||
category: n.category,
|
||||
symbolSize: n.symbolSize,
|
||||
x: n.x,
|
||||
y: n.y,
|
||||
// 渐变色节点
|
||||
symbol: 'circle',
|
||||
// 标签设置
|
||||
label: {
|
||||
show: true,
|
||||
position: 'bottom',
|
||||
distance: 8,
|
||||
fontSize: 11,
|
||||
fontWeight: 600,
|
||||
color: '#E2E8F0',
|
||||
textBorderColor: '#0a0e1a',
|
||||
textBorderWidth: 3,
|
||||
formatter: '{b}',
|
||||
},
|
||||
// 节点样式 - 科技感
|
||||
itemStyle: {
|
||||
color: {
|
||||
type: 'radial',
|
||||
x: 0.3,
|
||||
y: 0.3,
|
||||
r: 0.7,
|
||||
colorStops: [
|
||||
{ offset: 0, color: color.gradient[1] },
|
||||
{ offset: 0.7, color: color.gradient[0] },
|
||||
{ offset: 1, color: color.main },
|
||||
],
|
||||
},
|
||||
shadowBlur: 25,
|
||||
shadowColor: color.glow,
|
||||
borderColor: 'rgba(255,255,255,0.3)',
|
||||
borderWidth: 1,
|
||||
},
|
||||
// 呼吸效果通过 emphasis 实现
|
||||
emphasis: {
|
||||
focus: 'adjacency',
|
||||
scale: 1.15,
|
||||
itemStyle: {
|
||||
shadowBlur: 40,
|
||||
shadowColor: color.glow,
|
||||
borderColor: '#fff',
|
||||
borderWidth: 2,
|
||||
},
|
||||
},
|
||||
// 原始数据
|
||||
_data: n,
|
||||
}
|
||||
}),
|
||||
// 边数据
|
||||
links: links.map((l) => {
|
||||
const sourceNode = nodes.find(n => n.id === l.source)
|
||||
const color = sourceNode ? CAT_COLORS[sourceNode.category] : CAT_COLORS[0]
|
||||
return {
|
||||
source: l.source,
|
||||
target: l.target,
|
||||
// 边标签
|
||||
label: {
|
||||
show: true,
|
||||
formatter: l.label,
|
||||
fontSize: 9,
|
||||
fontWeight: 500,
|
||||
color: '#94A3B8',
|
||||
backgroundColor: 'rgba(10, 14, 26, 0.8)',
|
||||
padding: [2, 6],
|
||||
borderRadius: 4,
|
||||
textBorderColor: 'transparent',
|
||||
},
|
||||
// 边样式 - 科技感
|
||||
lineStyle: {
|
||||
color: {
|
||||
type: 'linear',
|
||||
x: 0, y: 0, x2: 1, y2: 1,
|
||||
colorStops: [
|
||||
{ offset: 0, color: color.main + '40' },
|
||||
{ offset: 0.5, color: color.main + '80' },
|
||||
{ offset: 1, color: color.main + '40' },
|
||||
],
|
||||
},
|
||||
width: 1.5,
|
||||
curveness: 0.15,
|
||||
opacity: 0.7,
|
||||
type: 'solid',
|
||||
},
|
||||
// 箭头 - 修复连线不连接到节点的问题
|
||||
edgeSymbol: 'none',
|
||||
edgeSymbolSize: 0,
|
||||
}
|
||||
}),
|
||||
// 允许拖拽和缩放
|
||||
roam: true,
|
||||
draggable: true,
|
||||
// 边标签
|
||||
edgeLabel: {
|
||||
show: true,
|
||||
fontSize: 10,
|
||||
color: '#94A3B8',
|
||||
},
|
||||
// 连线特效
|
||||
emphasis: {
|
||||
focus: 'adjacency',
|
||||
scale: true,
|
||||
lineStyle: {
|
||||
width: 3,
|
||||
opacity: 1,
|
||||
shadowBlur: 15,
|
||||
shadowColor: '#00D9FF',
|
||||
},
|
||||
},
|
||||
// 顶部图例(隐藏)
|
||||
legend: { show: false },
|
||||
}],
|
||||
// 视觉映射
|
||||
visualMap: {
|
||||
show: false,
|
||||
type: 'continuous',
|
||||
dimension: 2,
|
||||
inRange: {
|
||||
color: ['#00D9FF', '#10B981', '#F59E0B', '#8B5CF6'],
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
chartInstance = echarts.init(graphChartRef.value)
|
||||
chartInstance.setOption(option)
|
||||
|
||||
// 初始布局动画后停止 - 使用 disableLayoutAnimation 来停止持续抖动
|
||||
setTimeout(() => {
|
||||
if (chartInstance) {
|
||||
chartInstance.setOption({
|
||||
series: [{
|
||||
force: {
|
||||
layoutAnimation: false, // 关闭布局动画,停止抖动
|
||||
},
|
||||
}],
|
||||
})
|
||||
}
|
||||
}, 3000)
|
||||
|
||||
// Handle resize
|
||||
const handleResize = () => chartInstance?.resize()
|
||||
window.addEventListener('resize', handleResize)
|
||||
// 流程节点 - 树形结构
|
||||
interface WorkflowNode {
|
||||
id: string
|
||||
name: string
|
||||
description: string
|
||||
icon: string
|
||||
color: string
|
||||
bgColor: string
|
||||
borderColor: string
|
||||
status: 'pending' | 'processing' | 'completed'
|
||||
expanded: boolean
|
||||
children?: WorkflowNode[]
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
// 图谱通过点击按钮生成,不再自动初始化
|
||||
})
|
||||
const workflowData = ref<WorkflowNode[]>([
|
||||
{
|
||||
id: 'output',
|
||||
name: '最终输出',
|
||||
description: '生成最终结果返回给用户',
|
||||
icon: 'fa-check-circle',
|
||||
color: 'text-green-400',
|
||||
bgColor: 'from-green-500/20 to-green-500/5',
|
||||
borderColor: 'border-green-500/30',
|
||||
status: 'pending',
|
||||
expanded: false,
|
||||
children: [
|
||||
{
|
||||
id: 'generate',
|
||||
name: '方案生成',
|
||||
description: '基于分析结果构建解决方案',
|
||||
icon: 'fa-lightbulb',
|
||||
color: 'text-primary-orange',
|
||||
bgColor: 'from-primary-orange/20 to-primary-orange/5',
|
||||
borderColor: 'border-primary-orange/30',
|
||||
status: 'pending',
|
||||
expanded: false,
|
||||
children: [
|
||||
{
|
||||
id: 'analyze',
|
||||
name: '问题分析',
|
||||
description: '拆解问题、提取关键信息、理解用户意图',
|
||||
icon: 'fa-microscope',
|
||||
color: 'text-purple-400',
|
||||
bgColor: 'from-purple-500/20 to-purple-500/5',
|
||||
borderColor: 'border-purple-500/30',
|
||||
status: 'pending',
|
||||
expanded: false,
|
||||
children: [
|
||||
{
|
||||
id: 'question',
|
||||
name: '用户问题',
|
||||
description: '输入原始需求和问题描述',
|
||||
icon: 'fa-question',
|
||||
color: 'text-primary-cyan',
|
||||
bgColor: 'from-primary-cyan/20 to-primary-cyan/5',
|
||||
borderColor: 'border-primary-cyan/30',
|
||||
status: 'pending',
|
||||
expanded: false,
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
id: 'data',
|
||||
name: '数据获取',
|
||||
description: '从知识库、向量数据库获取相关信息',
|
||||
icon: 'fa-database',
|
||||
color: 'text-green-400',
|
||||
bgColor: 'from-green-500/20 to-green-500/5',
|
||||
borderColor: 'border-green-500/30',
|
||||
status: 'pending',
|
||||
expanded: false,
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
id: 'evaluate',
|
||||
name: '方案评估',
|
||||
description: '验证方案可行性和效果',
|
||||
icon: 'fa-clipboard-check',
|
||||
color: 'text-yellow-400',
|
||||
bgColor: 'from-yellow-500/20 to-yellow-500/5',
|
||||
borderColor: 'border-yellow-500/30',
|
||||
status: 'pending',
|
||||
expanded: false,
|
||||
}
|
||||
]
|
||||
}
|
||||
])
|
||||
|
||||
onUnmounted(() => {
|
||||
chartInstance?.dispose()
|
||||
})
|
||||
// 展开/收起节点
|
||||
const toggleNode = (node: WorkflowNode) => {
|
||||
node.expanded = !node.expanded
|
||||
}
|
||||
|
||||
// 递归渲染节点
|
||||
const renderWorkflow = (nodes: WorkflowNode[], level: number = 0) => {
|
||||
return nodes
|
||||
}
|
||||
|
||||
const openCreate = () => {
|
||||
newSkillForm.value = {
|
||||
@@ -828,7 +688,7 @@ const statusClass = (status: string) => {
|
||||
animation: scale-in 0.5s ease-out forwards;
|
||||
}
|
||||
|
||||
/* 图谱节点动画 */
|
||||
/* 流程节点动画 */
|
||||
.graph-node {
|
||||
transition: transform 0.3s ease;
|
||||
}
|
||||
@@ -963,7 +823,7 @@ const statusClass = (status: string) => {
|
||||
</div>
|
||||
|
||||
<Teleport to="body">
|
||||
<div v-if="isEditing" class="fixed inset-0 bg-black/60 flex items-center justify-center z-50" @click.self="cancelEdit">
|
||||
<div v-if="isEditing" class="fixed inset-0 bg-black/60 flex items-center justify-center z-50">
|
||||
<div class="bg-dark-700 rounded-2xl w-full max-w-lg border border-dark-500 shadow-2xl">
|
||||
<div class="flex items-center justify-between p-5 border-b border-dark-500">
|
||||
<h3 class="text-lg font-semibold">Edit Skill</h3>
|
||||
@@ -1032,7 +892,7 @@ const statusClass = (status: string) => {
|
||||
|
||||
<!-- 新建 Skill 模态框 -->
|
||||
<Teleport to="body">
|
||||
<div v-if="isCreating" class="fixed inset-0 bg-black/80 flex items-center justify-center z-50 p-6" @click.self="createStep === 1 && closeCreate()">
|
||||
<div v-if="isCreating" class="fixed inset-0 bg-black/80 flex items-center justify-center z-50 p-6">
|
||||
<!-- 步骤1:基本信息 -->
|
||||
<div v-if="createStep === 1" class="bg-dark-800 rounded-2xl w-full max-w-lg border border-dark-600 shadow-2xl overflow-hidden animate-modal-in">
|
||||
<!-- 头部 -->
|
||||
@@ -1299,100 +1159,319 @@ const statusClass = (status: string) => {
|
||||
:disabled="isGeneratingGraph || isGraphGenerated"
|
||||
class="w-full bg-gradient-to-r from-primary-orange to-red-500 hover:from-orange-500 hover:to-red-600 text-white font-medium py-2 px-3 rounded-lg transition-all flex items-center justify-center gap-2 disabled:opacity-50 disabled:cursor-not-allowed"
|
||||
>
|
||||
<i :class="['fa-solid fa-project-diagram', isGraphGenerated ? 'text-green-400' : '']"></i>
|
||||
{{ isGraphGenerated ? '图谱已生成' : (isGeneratingGraph ? '生成中...' : '生成流程图谱') }}
|
||||
<i :class="['fa-solid fa-sitemap', isGraphGenerated ? 'text-green-400' : '']"></i>
|
||||
{{ isGraphGenerated ? '流程已生成' : (isGeneratingGraph ? '生成中...' : '生成流程') }}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 中间:图谱视图 -->
|
||||
<!-- 中间:流程视图 -->
|
||||
<div class="flex-1 relative overflow-hidden" style="background: linear-gradient(135deg, #0a0e1a 0%, #0f172a 50%, #0a0e1a 100%);">
|
||||
<!-- 未生成图谱时的提示 -->
|
||||
<!-- 未生成流程时的提示 -->
|
||||
<div v-if="!isGraphGenerated && !isGeneratingGraph" class="absolute inset-0 flex flex-col items-center justify-center z-10">
|
||||
<div class="w-24 h-24 rounded-2xl bg-dark-700 flex items-center justify-center mb-4">
|
||||
<i class="fa-solid fa-project-diagram text-4xl text-gray-600"></i>
|
||||
<i class="fa-solid fa-sitemap text-4xl text-gray-600"></i>
|
||||
</div>
|
||||
<p class="text-gray-500 text-sm">点击左侧按钮生成流程图谱</p>
|
||||
<p class="text-gray-500 text-sm">点击左侧按钮生成流程</p>
|
||||
</div>
|
||||
|
||||
<!-- 生成中的加载动画 -->
|
||||
<!-- 生成中的加载动画 - 现代化设计 -->
|
||||
<div v-else-if="isGeneratingGraph" class="absolute inset-0 flex flex-col items-center justify-center z-10">
|
||||
<!-- 科幻风格加载动画 -->
|
||||
<div class="relative">
|
||||
<!-- 外层六边形 -->
|
||||
<div class="w-40 h-40 relative animate-spin-slow">
|
||||
<svg viewBox="0 0 100 100" class="w-full h-full">
|
||||
<!-- 外圈 -->
|
||||
<polygon points="50,2 95,27 95,73 50,98 5,73 5,27" fill="none" stroke="rgba(0,217,255,0.3)" stroke-width="1"/>
|
||||
<polygon points="50,8 90,29 90,71 50,92 10,71 10,29" fill="none" stroke="rgba(0,217,255,0.5)" stroke-width="1.5"/>
|
||||
<!-- 内圈 -->
|
||||
<polygon points="50,15 82,33 82,67 50,85 18,67 18,33" fill="none" stroke="rgba(139,92,246,0.4)" stroke-width="1"/>
|
||||
<!-- 旋转的连接线 -->
|
||||
<line x1="50" y1="2" x2="50" y2="20" stroke="rgba(0,217,255,0.8)" stroke-width="2" class="animate-pulse"/>
|
||||
<line x1="98" y1="50" x2="80" y2="50" stroke="rgba(139,92,246,0.8)" stroke-width="2" class="animate-pulse"/>
|
||||
<line x1="50" y1="98" x2="50" y2="80" stroke="rgba(0,217,255,0.8)" stroke-width="2" class="animate-pulse"/>
|
||||
<line x1="2" y1="50" x2="20" y2="50" stroke="rgba(139,92,246,0.8)" stroke-width="2" class="animate-pulse"/>
|
||||
</svg>
|
||||
</div>
|
||||
|
||||
<!-- 中心核心 -->
|
||||
<div class="absolute inset-0 flex items-center justify-center">
|
||||
<div class="w-16 h-16 rounded-full bg-gradient-to-br from-dark-900 to-dark-800 border-2 border-primary-cyan/50 flex items-center justify-center shadow-[0_0_30px_rgba(0,217,255,0.3)]">
|
||||
<div class="w-10 h-10 rounded-full bg-gradient-to-br from-primary-cyan to-purple-500 animate-pulse-slow flex items-center justify-center">
|
||||
<i class="fa-solid fa-brain text-white text-lg"></i>
|
||||
</div>
|
||||
<!-- 现代化加载动画 -->
|
||||
<div class="flex flex-col items-center">
|
||||
<!-- 多层旋转环 + 脉冲点 -->
|
||||
<div class="relative w-24 h-24 mb-8">
|
||||
<!-- 外环 -->
|
||||
<div class="absolute inset-0 border-2 border-primary-cyan/20 rounded-full"></div>
|
||||
<div class="absolute inset-1 border-2 border-t-primary-cyan rounded-full loading-spin" style="animation-duration: 1.5s;"></div>
|
||||
<!-- 中环 - 反向 -->
|
||||
<div class="absolute inset-2 border-2 border-purple-500/30 rounded-full" style="animation: loading-spin 2s linear infinite reverse;"></div>
|
||||
<!-- 内环 -->
|
||||
<div class="absolute inset-4 border-2 border-primary-cyan/40 rounded-full" style="animation: loading-spin 1s linear infinite;"></div>
|
||||
<!-- 中心脉冲 -->
|
||||
<div class="absolute inset-0 flex items-center justify-center">
|
||||
<div class="w-3 h-3 bg-primary-cyan rounded-full animate-pulse"></div>
|
||||
</div>
|
||||
<!-- 周围脉冲点 -->
|
||||
<div class="absolute inset-0 flex items-center justify-center" style="animation: loading-dots 1.4s ease-in-out infinite both;">
|
||||
<span class="absolute w-2 h-2 bg-primary-cyan rounded-full" style="top: 10%;"></span>
|
||||
<span class="absolute w-2 h-2 bg-purple-500 rounded-full" style="right: 10%;"></span>
|
||||
<span class="absolute w-2 h-2 bg-primary-orange rounded-full" style="bottom: 10%;"></span>
|
||||
<span class="absolute w-2 h-2 bg-green-500 rounded-full" style="left: 10%;"></span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 浮动粒子 -->
|
||||
<div class="absolute top-2 left-1/2 w-2 h-2 rounded-full bg-primary-cyan animate-float-particle"></div>
|
||||
<div class="absolute bottom-4 right-2 w-1.5 h-1.5 rounded-full bg-purple-400 animate-float-particle" style="animation-delay: 0.5s"></div>
|
||||
<div class="absolute top-1/3 left-2 w-1 h-1 rounded-full bg-cyan-300 animate-float-particle" style="animation-delay: 1s"></div>
|
||||
</div>
|
||||
|
||||
<!-- 文字信息 -->
|
||||
<div class="mt-8 text-center">
|
||||
<div class="flex items-center justify-center gap-2 mb-2">
|
||||
<span class="w-2 h-2 rounded-full bg-primary-cyan animate-ping"></span>
|
||||
<span class="text-white font-medium tracking-wider">正在构建知识图谱</span>
|
||||
<!-- 文字 -->
|
||||
<div class="text-center mb-6">
|
||||
<p class="text-white font-medium text-lg mb-2">正在生成执行流程</p>
|
||||
<p class="text-gray-400 text-sm">Building your workflow...</p>
|
||||
</div>
|
||||
<p class="text-gray-400 text-sm">Neural Network Analysis</p>
|
||||
</div>
|
||||
|
||||
<!-- 科幻进度条 -->
|
||||
<div class="mt-8 w-64">
|
||||
<div class="relative h-1 bg-dark-800 rounded-full overflow-hidden border border-dark-600">
|
||||
<!-- 扫描线 -->
|
||||
<div class="absolute inset-0">
|
||||
<div class="h-full bg-gradient-to-r from-transparent via-primary-cyan/30 to-transparent animate-scan"></div>
|
||||
<!-- 步骤指示器 -->
|
||||
<div class="flex items-center gap-2 mb-6">
|
||||
<div class="flex items-center gap-1.5">
|
||||
<div class="w-2 h-2 rounded-full bg-primary-cyan loading-pulse"></div>
|
||||
<span class="text-xs text-gray-500">分析</span>
|
||||
</div>
|
||||
<div class="w-8 h-px bg-dark-600"></div>
|
||||
<div class="flex items-center gap-1.5">
|
||||
<div class="w-2 h-2 rounded-full bg-gray-600"></div>
|
||||
<span class="text-xs text-gray-500">规划</span>
|
||||
</div>
|
||||
<div class="w-8 h-px bg-dark-600"></div>
|
||||
<div class="flex items-center gap-1.5">
|
||||
<div class="w-2 h-2 rounded-full bg-gray-600"></div>
|
||||
<span class="text-xs text-gray-500">执行</span>
|
||||
</div>
|
||||
<div class="w-8 h-px bg-dark-600"></div>
|
||||
<div class="flex items-center gap-1.5">
|
||||
<div class="w-2 h-2 rounded-full bg-gray-600"></div>
|
||||
<span class="text-xs text-gray-500">完成</span>
|
||||
</div>
|
||||
<!-- 进度 -->
|
||||
<div class="h-full bg-gradient-to-r from-primary-cyan via-purple-400 to-primary-cyan animate-progress-width bg-[length:200%_100%]"></div>
|
||||
</div>
|
||||
<div class="flex justify-between mt-2 text-xs text-gray-500 font-mono">
|
||||
<span>INITIALIZING</span>
|
||||
<span>0%</span>
|
||||
|
||||
<!-- 进度条 -->
|
||||
<div class="w-64">
|
||||
<div class="h-1 bg-dark-700 rounded-full overflow-hidden">
|
||||
<div class="h-full bg-gradient-to-r from-primary-cyan via-purple-500 to-primary-orange loading-progress-bar rounded-full"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 背景光效层 -->
|
||||
<div v-show="isGraphGenerated" class="absolute inset-0 overflow-hidden">
|
||||
<!-- 青色光晕 -->
|
||||
<div class="absolute top-0 left-0 w-[500px] h-[500px] rounded-full opacity-20" style="background: radial-gradient(circle, rgba(0, 217, 255, 0.3) 0%, transparent 70%); filter: blur(60px);"></div>
|
||||
<!-- 紫色光晕 -->
|
||||
<div class="absolute bottom-0 right-0 w-[400px] h-[400px] rounded-full opacity-15" style="background: radial-gradient(circle, rgba(139, 92, 246, 0.3) 0%, transparent 70%); filter: blur(60px);"></div>
|
||||
<!-- 网格线 -->
|
||||
<div class="absolute inset-0 opacity-10" style="background-image: linear-gradient(rgba(0, 217, 255, 0.3) 1px, transparent 1px), linear-gradient(90deg, rgba(0, 217, 255, 0.3) 1px, transparent 1px); background-size: 50px 50px;"></div>
|
||||
<!-- 青色光晕 - 更亮更柔和 -->
|
||||
<div class="absolute top-0 left-0 w-[500px] h-[500px] rounded-full opacity-30" style="background: radial-gradient(circle, rgba(0, 217, 255, 0.4) 0%, transparent 70%); filter: blur(80px);"></div>
|
||||
<!-- 紫色光晕 - 更亮更柔和 -->
|
||||
<div class="absolute bottom-0 right-0 w-[400px] h-[400px] rounded-full opacity-25" style="background: radial-gradient(circle, rgba(139, 92, 246, 0.4) 0%, transparent 70%); filter: blur(80px);"></div>
|
||||
<!-- 中心补充光效 -->
|
||||
<div class="absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 w-[600px] h-[600px] rounded-full opacity-15" style="background: radial-gradient(circle, rgba(255, 255, 255, 0.1) 0%, transparent 70%); filter: blur(100px);"></div>
|
||||
</div>
|
||||
|
||||
<!-- ECharts 图谱容器 -->
|
||||
<div v-show="isGraphGenerated" ref="graphChartRef" class="w-full h-full relative z-10"></div>
|
||||
<!-- 金字塔/树形流程图展示 -->
|
||||
<div v-show="isGraphGenerated" class="w-full h-full relative z-10 overflow-auto p-6">
|
||||
<!-- 流程标题 -->
|
||||
<div class="sticky top-0 bg-slate-900/80 backdrop-blur-sm px-4 py-3 border-b border-slate-700/50 mb-6 z-20">
|
||||
<div class="flex items-center justify-between">
|
||||
<div class="flex items-center gap-2">
|
||||
<i class="fa-solid fa-sitemap text-primary-cyan"></i>
|
||||
<span class="text-sm font-medium text-white">执行流程</span>
|
||||
<span class="px-2 py-0.5 rounded-full bg-slate-700 text-xs text-slate-300">4层</span>
|
||||
</div>
|
||||
<span class="text-xs text-slate-400">点击节点展开详情</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 树形结构 - 深邃版本 -->
|
||||
<div class="flex flex-col items-center">
|
||||
<!-- 递归渲染树节点 -->
|
||||
<template v-for="node in workflowData" :key="node.id">
|
||||
<div class="w-full flex flex-col items-center">
|
||||
<!-- 根节点 - 深邃设计 -->
|
||||
<div
|
||||
@click="toggleNode(node)"
|
||||
class="w-full max-w-lg cursor-pointer group"
|
||||
>
|
||||
<!-- 外层发光效果 -->
|
||||
<div
|
||||
class="relative p-1 rounded-2xl transition-all duration-500"
|
||||
:class="node.expanded ? 'opacity-100' : 'opacity-80 hover:opacity-100'"
|
||||
>
|
||||
<!-- 内层背景 -->
|
||||
<div
|
||||
class="relative bg-slate-800/70 backdrop-blur-xl border rounded-2xl p-5 overflow-hidden"
|
||||
:class="node.expanded ? `border-2 ${node.borderColor} shadow-lg shadow-${node.color.includes('green') ? 'green' : node.color.includes('orange') ? 'orange' : node.color.includes('purple') ? 'purple' : 'cyan'}-500/30` : 'border-slate-600/50'"
|
||||
>
|
||||
<!-- 背景光效 -->
|
||||
<div
|
||||
class="absolute inset-0 opacity-30 transition-opacity duration-500"
|
||||
:class="node.expanded ? 'opacity-100' : 'opacity-0'"
|
||||
:style="`background: linear-gradient(135deg, ${node.color.includes('green') ? 'rgba(34, 197, 94, 0.15)' : node.color.includes('orange') ? 'rgba(249, 115, 22, 0.15)' : node.color.includes('purple') ? 'rgba(168, 85, 247, 0.15)' : 'rgba(6, 182, 212, 0.15)'} 0%, transparent 100%);`"
|
||||
></div>
|
||||
|
||||
<!-- 网格纹理 -->
|
||||
<div class="absolute inset-0 opacity-10" style="background-image: linear-gradient(rgba(255,255,255,0.1) 1px, transparent 1px), linear-gradient(90deg, rgba(255,255,255,0.1) 1px, transparent 1px); background-size: 20px 20px;"></div>
|
||||
|
||||
<!-- 主内容 -->
|
||||
<div class="relative flex items-center gap-4">
|
||||
<!-- 图标容器 - 多层设计 -->
|
||||
<div class="relative">
|
||||
<!-- 外层光晕 -->
|
||||
<div
|
||||
class="absolute inset-0 rounded-2xl blur-xl opacity-40 transition-all duration-500"
|
||||
:class="node.expanded ? 'scale-150 opacity-40' : 'scale-100 opacity-0'"
|
||||
:style="`background: ${node.color.includes('green') ? '#22c55e' : node.color.includes('orange') ? '#f97316' : node.color.includes('purple') ? '#a855f7' : '#06b6d4'};`"
|
||||
></div>
|
||||
<!-- 图标框 -->
|
||||
<div
|
||||
class="relative w-16 h-16 rounded-2xl bg-gradient-to-br flex items-center justify-center border-2 backdrop-blur-sm"
|
||||
:class="[node.bgColor, node.borderColor]"
|
||||
>
|
||||
<i :class="['fa-solid', node.icon, node.color, 'text-2xl']"></i>
|
||||
</div>
|
||||
<!-- 状态指示点 -->
|
||||
<div
|
||||
class="absolute -bottom-1 -right-1 w-4 h-4 rounded-full border-2 border-slate-700 flex items-center justify-center"
|
||||
:class="node.status === 'completed' ? 'bg-green-500' : node.status === 'processing' ? 'bg-yellow-500' : 'bg-gray-500'"
|
||||
>
|
||||
<i v-if="node.status === 'completed'" class="fa-solid fa-check text-[8px] text-white"></i>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 文字内容 -->
|
||||
<div class="flex-1">
|
||||
<div class="flex items-center gap-2 mb-1">
|
||||
<h4 class="text-white font-bold text-lg tracking-wide">{{ node.name }}</h4>
|
||||
<span
|
||||
class="px-2.5 py-1 rounded-full text-xs font-semibold uppercase tracking-wider backdrop-blur-sm"
|
||||
:class="[
|
||||
node.status === 'completed' ? 'bg-green-500/30 text-green-400 border border-green-500/50' :
|
||||
node.status === 'processing' ? 'bg-yellow-500/30 text-yellow-400 border border-yellow-500/50' :
|
||||
'bg-slate-500/30 text-slate-300 border border-slate-500/50'
|
||||
]"
|
||||
>
|
||||
{{ node.status === 'completed' ? '✓ 完成' : node.status === 'processing' ? '◐ 进行中' : '○ 待处理' }}
|
||||
</span>
|
||||
</div>
|
||||
<p class="text-slate-300 text-sm">{{ node.description }}</p>
|
||||
<!-- 底部信息 -->
|
||||
<div class="flex items-center gap-4 mt-2 text-xs text-slate-400">
|
||||
<span class="flex items-center gap-1">
|
||||
<i class="fa-solid fa-layer-group"></i>
|
||||
Level 1
|
||||
</span>
|
||||
<span class="flex items-center gap-1">
|
||||
<i class="fa-solid fa-code-branch"></i>
|
||||
{{ node.children?.length || 0 }} 个子任务
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 展开箭头 -->
|
||||
<div
|
||||
v-if="node.children && node.children.length > 0"
|
||||
class="flex items-center justify-center w-8 h-8 rounded-lg bg-slate-700/50 border border-slate-600"
|
||||
>
|
||||
<i
|
||||
:class="['fa-solid fa-chevron-down text-slate-300 transition-transform duration-300', node.expanded ? 'rotate-180' : '']"
|
||||
></i>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 连接线 + 子节点 -->
|
||||
<div v-if="node.expanded && node.children" class="w-full flex flex-col items-center mt-1">
|
||||
<!-- 垂直连接线 - 复杂设计 -->
|
||||
<div class="relative flex flex-col items-center">
|
||||
<!-- 主线 -->
|
||||
<div class="w-0.5 h-8 bg-gradient-to-b from-green-500/40 to-purple-500/40"></div>
|
||||
<!-- 发光点 -->
|
||||
<div class="absolute top-1/2 w-2 h-2 rounded-full bg-cyan-400 shadow-lg shadow-cyan-400/40"></div>
|
||||
</div>
|
||||
|
||||
<!-- 子节点容器 -->
|
||||
<div class="w-full flex justify-center gap-6">
|
||||
<div v-for="(child, idx) in node.children" :key="child.id" class="flex-1 flex flex-col items-center max-w-md">
|
||||
<!-- 水平连接线 -->
|
||||
<div class="w-full h-0.5 bg-gradient-to-r from-transparent via-purple-500/40 to-transparent mb-3"></div>
|
||||
|
||||
<!-- 子节点 - 深邃设计 -->
|
||||
<div
|
||||
@click.stop="toggleNode(child)"
|
||||
class="w-full cursor-pointer group"
|
||||
>
|
||||
<div
|
||||
class="relative p-0.5 rounded-xl transition-all duration-300"
|
||||
:class="child.expanded ? 'opacity-100' : 'opacity-80 hover:opacity-100'"
|
||||
>
|
||||
<div
|
||||
class="relative bg-slate-800/60 backdrop-blur-xl border rounded-xl p-4"
|
||||
:class="child.expanded ? `border-2 ${child.borderColor}` : 'border-slate-600/50'"
|
||||
>
|
||||
<!-- 背景微光 -->
|
||||
<div
|
||||
class="absolute inset-0 opacity-20 rounded-xl"
|
||||
:style="`background: linear-gradient(135deg, ${child.color.includes('green') ? 'rgba(34, 197, 94, 0.2)' : child.color.includes('orange') ? 'rgba(249, 115, 22, 0.2)' : child.color.includes('yellow') ? 'rgba(234, 179, 8, 0.2)' : 'rgba(6, 182, 212, 0.2)'} 0%, transparent 100%);`"
|
||||
></div>
|
||||
|
||||
<div class="relative flex items-center gap-3">
|
||||
<!-- 图标 -->
|
||||
<div class="relative">
|
||||
<div
|
||||
class="w-12 h-12 rounded-xl bg-gradient-to-br flex items-center justify-center border"
|
||||
:class="[child.bgColor, child.borderColor]"
|
||||
>
|
||||
<i :class="['fa-solid', child.icon, child.color, 'text-xl']"></i>
|
||||
</div>
|
||||
<!-- 序号标签 -->
|
||||
<div class="absolute -top-2 -left-2 w-5 h-5 rounded-full bg-slate-700 border border-slate-500 flex items-center justify-center text-[10px] text-slate-300 font-bold">
|
||||
{{ idx + 1 }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 文字 -->
|
||||
<div class="flex-1 min-w-0">
|
||||
<div class="flex items-center gap-2 mb-0.5">
|
||||
<h5 class="text-white font-semibold">{{ child.name }}</h5>
|
||||
<span
|
||||
v-if="child.children && child.children.length > 0"
|
||||
class="px-1.5 py-0.5 rounded bg-slate-700 text-slate-300 text-xs"
|
||||
>
|
||||
{{ child.children.length }}
|
||||
</span>
|
||||
</div>
|
||||
<p class="text-slate-400 text-xs truncate">{{ child.description }}</p>
|
||||
</div>
|
||||
|
||||
<!-- 箭头 -->
|
||||
<i
|
||||
v-if="child.children && child.children.length > 0"
|
||||
:class="['fa-solid fa-chevron-down text-slate-400 text-xs transition-transform duration-300', child.expanded ? 'rotate-180' : '']"
|
||||
></i>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 孙子节点 -->
|
||||
<div v-if="child.expanded && child.children" class="mt-2 ml-4 space-y-2">
|
||||
<div
|
||||
v-for="(grandchild, gidx) in child.children"
|
||||
:key="grandchild.id"
|
||||
class="relative pl-4 border-l-2 border-slate-600/50"
|
||||
>
|
||||
<!-- 连接点 -->
|
||||
<div class="absolute -left-1 top-1/2 -translate-y-1/2 w-2 h-2 rounded-full bg-slate-500"></div>
|
||||
|
||||
<div class="flex items-center gap-3 p-2 rounded-lg bg-slate-700/30 hover:bg-slate-700/60 transition-colors cursor-pointer border border-transparent hover:border-slate-500">
|
||||
<div
|
||||
class="w-8 h-8 rounded-lg bg-gradient-to-br flex items-center justify-center border"
|
||||
:class="[grandchild.bgColor, grandchild.borderColor]"
|
||||
>
|
||||
<i :class="['fa-solid', grandchild.icon, grandchild.color, 'text-sm']"></i>
|
||||
</div>
|
||||
<div class="flex-1 min-w-0">
|
||||
<h6 class="text-white font-medium text-sm">{{ grandchild.name }}</h6>
|
||||
<p class="text-slate-400 text-xs truncate">{{ grandchild.description }}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 底部提示 -->
|
||||
<div v-show="isGraphGenerated" class="absolute bottom-4 left-1/2 -translate-x-1/2 bg-dark-700/80 backdrop-blur-sm border border-dark-500/50 rounded-lg px-4 py-2 text-xs text-gray-400 shadow-lg">
|
||||
<i class="fa-solid fa-info-circle mr-1 text-primary-cyan"></i>
|
||||
拖拽节点 · 滚轮缩放 · 点击查看详情
|
||||
<div v-show="isGraphGenerated" class="absolute bottom-4 left-1/2 -translate-x-1/2 bg-slate-800/80 backdrop-blur-sm border border-slate-600/50 rounded-lg px-4 py-2 text-xs text-slate-300 shadow-lg">
|
||||
<i class="fa-solid fa-sitemap mr-1 text-primary-cyan"></i>
|
||||
金字塔结构 · 4层执行流程 · 点击展开
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -211,7 +211,7 @@ const providerIcon = (provider: string) => {
|
||||
</div>
|
||||
|
||||
<Teleport to="body">
|
||||
<div v-if="isEditing" class="fixed inset-0 bg-black/60 flex items-center justify-center z-50" @click.self="cancelEdit">
|
||||
<div v-if="isEditing" class="fixed inset-0 bg-black/60 flex items-center justify-center z-50">
|
||||
<div class="bg-dark-700 rounded-2xl w-full max-w-lg border border-dark-500 shadow-2xl">
|
||||
<div class="flex items-center justify-between p-5 border-b border-dark-500">
|
||||
<h3 class="text-lg font-semibold">Edit Model API</h3>
|
||||
|
||||
Reference in New Issue
Block a user