finish work
This commit is contained in:
parent
fba7ed29f0
commit
27c3f24089
@ -1,13 +1,16 @@
|
||||
<template>
|
||||
<el-dialog v-model="showDiagram" width="800px" append-to-body class="no-padding-dialog">
|
||||
<template #title>
|
||||
<div style="display: flex; align-items: center; justify-content: space-between;">
|
||||
<div style="display: flex; align-items: center;">
|
||||
<span>Tool Diagram</span>
|
||||
 
|
||||
<el-button size="small" type="primary" @click="() => {}">重置</el-button>
|
||||
<el-button size="small" type="primary" @click="() => {}">开启自检程序</el-button>
|
||||
</div>
|
||||
</template>
|
||||
<el-scrollbar height="80vh">
|
||||
<Diagram />
|
||||
<!-- <Diagram /> -->
|
||||
<SwimPool />
|
||||
</el-scrollbar>
|
||||
</el-dialog>
|
||||
<!-- <el-button @click="showDiagram = true" type="primary" style="margin-bottom: 16px;">
|
||||
@ -18,7 +21,7 @@
|
||||
<script setup lang="ts">
|
||||
import { ref } from 'vue';
|
||||
import Diagram from './diagram.vue';
|
||||
|
||||
import SwimPool from './swim-pool.vue';
|
||||
const showDiagram = ref(true);
|
||||
|
||||
</script>
|
||||
|
@ -1,5 +1,7 @@
|
||||
<template>
|
||||
<div ref="svgContainer" class="diagram-container"></div>
|
||||
<div style="display: flex; align-items: center; gap: 16px;">
|
||||
<div ref="svgContainer" class="diagram-container"></div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
@ -105,7 +107,7 @@ function renderSvg() {
|
||||
.append('svg')
|
||||
.attr('width', width)
|
||||
.attr('height', height)
|
||||
.style('user-select', 'none');
|
||||
.style('user-select', 'none') as any;
|
||||
} else {
|
||||
svg.attr('width', width).attr('height', height);
|
||||
svg.selectAll('defs').remove();
|
||||
@ -116,21 +118,21 @@ function renderSvg() {
|
||||
.append('defs')
|
||||
.append('marker')
|
||||
.attr('id', 'arrow')
|
||||
.attr('viewBox', '0 0 10 10')
|
||||
.attr('refX', 8)
|
||||
.attr('refY', 5)
|
||||
.attr('markerWidth', 6)
|
||||
.attr('markerHeight', 6)
|
||||
.attr('viewBox', '0 0 8 8')
|
||||
.attr('refX', 6)
|
||||
.attr('refY', 4)
|
||||
.attr('markerWidth', 5)
|
||||
.attr('markerHeight', 5)
|
||||
.attr('orient', 'auto')
|
||||
.append('path')
|
||||
.attr('d', 'M 0 0 L 10 5 L 0 10 z')
|
||||
.attr('d', 'M 0 0 L 8 4 L 0 8 z')
|
||||
.attr('fill', 'var(--main-color)');
|
||||
|
||||
// Draw edges with enter animation
|
||||
const allSections: { id: string, section: any }[] = [];
|
||||
(state.edges || []).forEach(edge => {
|
||||
const sections = edge.sections || [];
|
||||
sections.forEach((section, idx) => {
|
||||
sections.forEach((section: any, idx: number) => {
|
||||
allSections.push({
|
||||
id: (edge.id || '') + '-' + (section.id || idx),
|
||||
section
|
||||
@ -201,6 +203,7 @@ function renderSvg() {
|
||||
|
||||
nodeGroup.exit().remove();
|
||||
|
||||
// 节点 enter
|
||||
const nodeGroupEnter = nodeGroup.enter()
|
||||
.append('g')
|
||||
.attr('class', 'node')
|
||||
@ -235,7 +238,7 @@ function renderSvg() {
|
||||
.attr('width', d => d.width)
|
||||
.attr('height', d => d.height)
|
||||
.attr('rx', 16)
|
||||
.attr('fill', d => state.selectedNodeId === d.id ? 'var(--main-color)' : 'var(--main-color)')
|
||||
.attr('fill', 'var(--main-color)')
|
||||
.attr('opacity', d => state.selectedNodeId === d.id ? 0.25 : 0.12)
|
||||
.attr('stroke', 'var(--main-color)')
|
||||
.attr('stroke-width', 2);
|
||||
@ -263,9 +266,35 @@ function renderSvg() {
|
||||
.ease(d3.easeCubicInOut)
|
||||
.attr('transform', d => `translate(${(d.x || 0) + 30}, ${(d.y || 0) + 30})`);
|
||||
|
||||
// 高亮选中节点动画
|
||||
nodeGroup.select('rect')
|
||||
.transition()
|
||||
.duration(400)
|
||||
.attr('opacity', d => state.selectedNodeId === d.id ? 0.55 : 0.12)
|
||||
.attr('stroke-width', d => state.selectedNodeId === d.id ? 4 : 2)
|
||||
.attr('stroke', d => state.selectedNodeId === d.id ? 'var(--main-color)' : 'var(--main-color)')
|
||||
.attr('fill', d => state.selectedNodeId === d.id ? 'var(--main-color)' : 'var(--main-color)');
|
||||
|
||||
// 渲染结束后保存当前快照
|
||||
prevNodes = state.nodes.map(n => ({ ...n }));
|
||||
prevEdges = (state.edges || []).map(e => ({ ...e, sections: e.sections ? e.sections.map(s => ({ ...s })) : [] }));
|
||||
prevEdges = (state.edges || []).map(e => ({ ...e, sections: e.sections ? e.sections.map((s: any) => ({ ...s })) : [] }));
|
||||
}
|
||||
|
||||
// 重置连接为链表结构
|
||||
function resetConnections() {
|
||||
if (!state.nodes.length) return;
|
||||
const edges = [];
|
||||
for (let i = 0; i < state.nodes.length - 1; ++i) {
|
||||
const prev = state.nodes[i];
|
||||
const next = state.nodes[i + 1];
|
||||
edges.push({
|
||||
id: prev.id + '-' + next.id,
|
||||
sources: [prev.id],
|
||||
targets: [next.id]
|
||||
});
|
||||
}
|
||||
state.edges = edges;
|
||||
recomputeLayout().then(renderSvg);
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
|
128
renderer/src/components/main-panel/tool/swim-pool.vue
Normal file
128
renderer/src/components/main-panel/tool/swim-pool.vue
Normal file
@ -0,0 +1,128 @@
|
||||
<template>
|
||||
<div class="swim-pool">
|
||||
<transition-group name="lane-move" tag="div">
|
||||
<div v-for="(lane, laneIdx) in lanes" :key="lane.id" class="swim-lane" @dragover.prevent
|
||||
@drop="onDrop(laneIdx)">
|
||||
<div class="lane-title">Group {{ laneIdx + 1 }}</div>
|
||||
<div v-for="tool in lane.tools" :key="tool.name" class="tool-card" draggable="true"
|
||||
@dragstart="onDragStart(tool, laneIdx)">
|
||||
{{ tool.name }}
|
||||
</div>
|
||||
</div>
|
||||
</transition-group>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import type { ToolItem } from '@/hook/type';
|
||||
import { mcpClientAdapter } from '@/views/connect/core';
|
||||
import { ref, onMounted } from 'vue';
|
||||
|
||||
// 工具类型
|
||||
interface Tool {
|
||||
id: string;
|
||||
name: string;
|
||||
}
|
||||
|
||||
interface Lane {
|
||||
id: string;
|
||||
tools: ToolItem[];
|
||||
}
|
||||
|
||||
// 工具列表
|
||||
const tools = ref<ToolItem[]>([]);
|
||||
|
||||
// 泳道列表
|
||||
const lanes = ref<Lane[]>([]);
|
||||
|
||||
// 获取所有工具
|
||||
const getAllTools = async () => {
|
||||
const items = [];
|
||||
for (const client of mcpClientAdapter.clients) {
|
||||
const clientTools = await client.getTools();
|
||||
items.push(...clientTools.values());
|
||||
}
|
||||
return items;
|
||||
};
|
||||
|
||||
// 初始化
|
||||
onMounted(async () => {
|
||||
tools.value = await getAllTools();
|
||||
console.log(tools.value);
|
||||
|
||||
lanes.value = tools.value.map((tool, idx) => ({
|
||||
id: `lane-${idx}`,
|
||||
tools: [tool]
|
||||
}));
|
||||
});
|
||||
|
||||
// 拖拽信息
|
||||
let dragInfo: { tool: ToolItem | null; fromLane: number } = { tool: null, fromLane: -1 };
|
||||
|
||||
// 拖拽开始
|
||||
function onDragStart(tool: ToolItem, fromLane: number) {
|
||||
dragInfo = { tool, fromLane };
|
||||
}
|
||||
|
||||
// 拖拽释放
|
||||
function onDrop(toLane: number) {
|
||||
if (dragInfo.tool && dragInfo.fromLane !== -1 && dragInfo.fromLane !== toLane) {
|
||||
// 从原泳道移除
|
||||
lanes.value[dragInfo.fromLane].tools = lanes.value[dragInfo.fromLane].tools.filter(
|
||||
t => t.name !== dragInfo.tool!.name
|
||||
);
|
||||
// 加入新泳道
|
||||
lanes.value[toLane].tools.push(dragInfo.tool);
|
||||
|
||||
// 如果原泳道已空,删除该泳道
|
||||
if (lanes.value[dragInfo.fromLane].tools.length === 0) {
|
||||
lanes.value.splice(dragInfo.fromLane, 1);
|
||||
}
|
||||
|
||||
// 重新排序,确保泳道顺序和索引一致
|
||||
lanes.value = lanes.value.map((lane, idx) => ({
|
||||
...lane,
|
||||
// 可选:如果你希望泳道 id 也随序号变化
|
||||
id: `lane-${idx}`
|
||||
}));
|
||||
}
|
||||
dragInfo = { tool: null, fromLane: -1 };
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.swim-pool {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.swim-lane {
|
||||
border: 1px solid var(--sidebar);
|
||||
min-height: 60px;
|
||||
margin-bottom: 10px;
|
||||
padding: 8px;
|
||||
border-radius: 8px;
|
||||
transition: box-shadow 0.3s;
|
||||
box-shadow: 0 2px 8px 0 rgba(0,0,0,0.08);
|
||||
}
|
||||
|
||||
.lane-title {
|
||||
font-weight: bold;
|
||||
margin-bottom: 6px;
|
||||
}
|
||||
|
||||
.tool-card {
|
||||
border: 1px solid var(--main-color);
|
||||
border-radius: 4px;
|
||||
padding: 4px 12px;
|
||||
margin-bottom: 4px;
|
||||
cursor: grab;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
/* 动画样式 */
|
||||
.lane-move-move {
|
||||
transition: transform 0.5s cubic-bezier(0.4, 0, 0.2, 1);
|
||||
}
|
||||
</style>
|
Loading…
x
Reference in New Issue
Block a user