feat(studio): 补齐剩余工作台聚合接口与真实对接
This commit is contained in:
@@ -1,27 +1,83 @@
|
||||
<script setup lang="ts">
|
||||
import { computed, onMounted, ref } from 'vue';
|
||||
import { Connection, Cpu, VideoPlay } from '@element-plus/icons-vue';
|
||||
|
||||
import { traceSteps, workflowEdges, workflowNodes } from '@/data/studioMock';
|
||||
import type { WorkflowWorkspace } from '@/api/workflow';
|
||||
import { getWorkflowWorkspace } from '@/api/workflow';
|
||||
|
||||
const nodeById = Object.fromEntries(workflowNodes.map((node) => [node.id, node]));
|
||||
const canvasEdges = workflowEdges.flatMap((edge) => {
|
||||
const from = nodeById[edge.from];
|
||||
const to = nodeById[edge.to];
|
||||
const loading = ref(false);
|
||||
const projectId = ref('101');
|
||||
const workflowId = ref('201');
|
||||
const workspace = ref<WorkflowWorkspace | null>(null);
|
||||
|
||||
if (!from || !to) {
|
||||
const nodeLibrary = ['Start', 'LLM', 'Knowledge Retrieval', 'MCP Tool', 'Skill', 'Condition', 'Answer'];
|
||||
|
||||
const workflowNodes = computed(() => {
|
||||
const graphJson = workspace.value?.versions?.[0]?.graphJson;
|
||||
if (!graphJson) {
|
||||
return [];
|
||||
}
|
||||
try {
|
||||
const parsed = JSON.parse(graphJson) as { nodes?: Array<Record<string, unknown>> };
|
||||
return (parsed.nodes ?? []).map((node, index) => ({
|
||||
id: String(node.id ?? `node-${index}`),
|
||||
type: String(node.type ?? 'NODE'),
|
||||
label: String(node.label ?? node.id ?? `Node ${index + 1}`),
|
||||
description: String(node.description ?? node.prompt ?? ''),
|
||||
x: Number(node.x ?? 10 + index * 18),
|
||||
y: Number(node.y ?? 42),
|
||||
}));
|
||||
} catch {
|
||||
return [];
|
||||
}
|
||||
});
|
||||
|
||||
return [
|
||||
{
|
||||
id: `${edge.from}-${edge.to}`,
|
||||
const workflowEdges = computed(() => {
|
||||
const graphJson = workspace.value?.versions?.[0]?.graphJson;
|
||||
if (!graphJson) {
|
||||
return [];
|
||||
}
|
||||
try {
|
||||
const parsed = JSON.parse(graphJson) as { edges?: Array<Record<string, unknown>> };
|
||||
return (parsed.edges ?? []).map((edge, index) => ({
|
||||
id: `edge-${index}`,
|
||||
from: String(edge.from ?? edge.source ?? ''),
|
||||
to: String(edge.to ?? edge.target ?? ''),
|
||||
}));
|
||||
} catch {
|
||||
return [];
|
||||
}
|
||||
});
|
||||
|
||||
const canvasEdges = computed(() => {
|
||||
const nodeById = Object.fromEntries(workflowNodes.value.map((node) => [node.id, node]));
|
||||
return workflowEdges.value.flatMap((edge) => {
|
||||
const from = nodeById[edge.from];
|
||||
const to = nodeById[edge.to];
|
||||
if (!from || !to) {
|
||||
return [];
|
||||
}
|
||||
return [{
|
||||
id: edge.id,
|
||||
x1: from.x + 5,
|
||||
y1: from.y + 4,
|
||||
x2: to.x,
|
||||
y2: to.y + 4,
|
||||
},
|
||||
];
|
||||
}];
|
||||
});
|
||||
});
|
||||
|
||||
async function loadWorkspace() {
|
||||
loading.value = true;
|
||||
try {
|
||||
const response = await getWorkflowWorkspace(projectId.value, workflowId.value);
|
||||
workspace.value = response.data;
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
onMounted(loadWorkspace);
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@@ -37,26 +93,20 @@ const canvasEdges = workflowEdges.flatMap((edge) => {
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<div class="workflow-layout">
|
||||
<div class="workflow-layout" v-loading="loading">
|
||||
<aside class="studio-panel node-library">
|
||||
<div class="panel-heading compact">
|
||||
<h2>节点库</h2>
|
||||
<span>JSON Graph</span>
|
||||
</div>
|
||||
<button>Start</button>
|
||||
<button>LLM</button>
|
||||
<button>Knowledge Retrieval</button>
|
||||
<button>MCP Tool</button>
|
||||
<button>Skill</button>
|
||||
<button>Condition</button>
|
||||
<button>Answer</button>
|
||||
<button v-for="node in nodeLibrary" :key="node">{{ node }}</button>
|
||||
</aside>
|
||||
|
||||
<main class="studio-panel workflow-canvas">
|
||||
<div class="canvas-toolbar">
|
||||
<span><el-icon><Connection /></el-icon> workflow-support-rag</span>
|
||||
<span>版本快照 v7</span>
|
||||
<span>环境: Dev</span>
|
||||
<span><el-icon><Connection /></el-icon> {{ workspace?.workflowCode || 'workflow' }}</span>
|
||||
<span>版本快照 v{{ workspace?.currentPublishedVersionNo || workspace?.versions?.[0]?.versionNo || '-' }}</span>
|
||||
<span>环境: {{ workspace?.environment || 'Dev' }}</span>
|
||||
</div>
|
||||
<div class="canvas-surface">
|
||||
<svg class="edge-layer" viewBox="0 0 100 100" preserveAspectRatio="none">
|
||||
@@ -73,8 +123,9 @@ const canvasEdges = workflowEdges.flatMap((edge) => {
|
||||
v-for="node in workflowNodes"
|
||||
:key="node.id"
|
||||
class="workflow-node"
|
||||
:class="{ selected: node.id === 'llm' }"
|
||||
:class="{ selected: node.type === 'LLM' }"
|
||||
:style="{ left: `${node.x}%`, top: `${node.y}%` }"
|
||||
:data-test="`workflow-node-${node.id}`"
|
||||
>
|
||||
<span>{{ node.type }}</span>
|
||||
<strong>{{ node.label }}</strong>
|
||||
@@ -83,9 +134,9 @@ const canvasEdges = workflowEdges.flatMap((edge) => {
|
||||
</div>
|
||||
<div class="run-trace-drawer">
|
||||
<strong>Run Trace</strong>
|
||||
<div v-for="step in traceSteps" :key="step.node">
|
||||
<span>{{ step.node }}</span>
|
||||
<em>{{ step.status }} · {{ step.duration }} · {{ step.output }}</em>
|
||||
<div v-for="run in workspace?.recentRuns ?? []" :key="run.requestId" :data-test="`workflow-run-${run.requestId}`">
|
||||
<span>{{ run.requestId }}</span>
|
||||
<em>{{ run.status }} · {{ run.durationMs || 0 }}ms · {{ run.outputJson || '-' }}</em>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
@@ -93,17 +144,17 @@ const canvasEdges = workflowEdges.flatMap((edge) => {
|
||||
<aside class="studio-panel inspector-panel">
|
||||
<div class="panel-heading compact">
|
||||
<h2>节点 Inspector</h2>
|
||||
<span>LLM</span>
|
||||
<span>{{ workflowNodes[0]?.type || 'LLM' }}</span>
|
||||
</div>
|
||||
<dl class="inspector-list">
|
||||
<dt>任务类型</dt>
|
||||
<dd>RAG_ANSWER</dd>
|
||||
<dt>输入 Schema</dt>
|
||||
<dd>question, retrieved_chunks, conversation</dd>
|
||||
<dt>输出 Schema</dt>
|
||||
<dd>answer, citations, safety_flags</dd>
|
||||
<dt>路由策略</dt>
|
||||
<dd>primary qwen-plus / fallback deepseek-v3</dd>
|
||||
<dt>工作流</dt>
|
||||
<dd>{{ workspace?.workflowName || '-' }}</dd>
|
||||
<dt>发布状态</dt>
|
||||
<dd>{{ workspace?.publishStatus || '-' }}</dd>
|
||||
<dt>当前版本</dt>
|
||||
<dd>v{{ workspace?.currentPublishedVersionNo || workspace?.versions?.[0]?.versionNo || '-' }}</dd>
|
||||
<dt>最近请求</dt>
|
||||
<dd>{{ workspace?.latestRequestId || '-' }}</dd>
|
||||
</dl>
|
||||
<button class="blue-command"><el-icon><Cpu /></el-icon> 打开模型路由</button>
|
||||
</aside>
|
||||
|
||||
Reference in New Issue
Block a user