feat: improve workflow observability and lighten admin frontend

This commit is contained in:
2026-06-22 06:34:53 +08:00
parent 1b835a4aad
commit e1ef4c1a70
11 changed files with 360 additions and 416 deletions
@@ -271,6 +271,14 @@ export interface WorkflowListItem {
}
/** 工作流运行记录 */
export interface WorkflowRunEvent {
type?: string;
content?: string;
message?: string;
message_type?: string;
[key: string]: any;
}
export interface ExecutionLogEntry {
node_id: string;
node_type: string;
@@ -285,6 +293,7 @@ export interface ExecutionLogEntry {
branch?: string;
branch_label?: string;
metadata?: Record<string, any>;
events?: WorkflowRunEvent[];
}
export interface WorkflowRunListItem {
-19
View File
@@ -1,22 +1,3 @@
export * from '../online-dev/form-data-api';
export * from '../online-dev/form-manager';
// 注意: login-log 模块请直接导入以避免 PaginatedResponse 类型冲突
// import { getLoginLogListApi, ... } from '#/api/core/login-log';
// 注意: ai-platform 模块请直接导入以避免类型冲突
// import { ... } from '#/api/core/ai-platform';
export * from '../online-dev/screen-design';
export * from './application';
export * from './auth';
export * from './data-source';
export * from './database-connection';
export * from './database-manager';
export * from './dept';
export * from './dict';
export * from './menu';
export * from './oauth';
export * from './permission';
export * from './post';
export * from './role';
export * from './scheduler';
export * from './server-monitor';
export * from './user';
+27 -1
View File
@@ -22,7 +22,33 @@ import zhTwLocale from 'element-plus/es/locale/lang/zh-tw';
const elementLocale = ref<Language>(defaultLocale);
const modules = import.meta.glob('./langs/**/*.json');
const modules = import.meta.glob([
'./langs/**/account-settings.json',
'./langs/**/ai-platform.json',
'./langs/**/announcement.json',
'./langs/**/authentication.json',
'./langs/**/chat.json',
'./langs/**/common.json',
'./langs/**/core.json',
'./langs/**/database-connection.json',
'./langs/**/database-manager.json',
'./langs/**/dept.json',
'./langs/**/dict.json',
'./langs/**/file-manager.json',
'./langs/**/menu-title.json',
'./langs/**/menu.json',
'./langs/**/message.json',
'./langs/**/page.json',
'./langs/**/permission.json',
'./langs/**/post.json',
'./langs/**/role.json',
'./langs/**/system.json',
'./langs/**/system-config.json',
'./langs/**/ui.json',
'./langs/**/user-avatar.json',
'./langs/**/user.json',
'./langs/**/workflow.json',
]);
const localesMap = loadLocalesMapFromDir(
/\.\/langs\/([^/]+)\/(.*)\.json$/,
-14
View File
@@ -175,27 +175,13 @@ async function generateAccess(options: GenerateMenuAndRoutesOptions) {
'../views/_core/agent-chat/**/*.vue',
'../views/_core/announcement/index.vue',
'../views/_core/announcement/list.vue',
'../views/_core/application/index.vue',
'../views/_core/authentication/login.vue',
'../views/_core/chat/**/*.vue',
'../views/_core/database-monitor/**/*.vue',
'../views/_core/dept/index.vue',
'../views/_core/dict/index.vue',
'../views/_core/fallback/**/*.vue',
'../views/_core/file-manager/index.vue',
'../views/_core/file-preview/index.vue',
'../views/_core/login-log/index.vue',
'../views/_core/menu/index.vue',
'../views/_core/message/index.vue',
'../views/_core/org-chart/index.vue',
'../views/_core/page-render/index.vue',
'../views/_core/permission/index.vue',
'../views/_core/post/index.vue',
'../views/_core/role/index.vue',
'../views/_core/redis-monitor/**/*.vue',
'../views/_core/server-monitor/index.vue',
'../views/_core/system-config/index.vue',
'../views/_core/ui-config/index.vue',
'../views/_core/user/index.vue',
'../views/ai-platform/agent/editor/index.vue',
'../views/ai-platform/agent/index.vue',
+14 -38
View File
@@ -1,50 +1,42 @@
export const LIGHT_MENU_NAMES = new Set([
'AccountSettings',
'Application',
'ControlCenter',
'AIAgent',
'AIAgentEditor',
'AgentChat',
'AIKnowledgeDetail',
'AIKnowledgeLegacyDetailRedirect',
'AIKnowledgeLegacyRedirect',
'AIModelConfig',
'AIPlatform',
'AIWorkflow',
'AIWorkflowEditor',
'AIWorkflowRuns',
'AnnouncementList',
'AnnouncementManage',
'CodexAgentChat',
'FileManager',
'Message',
'MessageCenter',
'MessageList',
'KnowledgeBase',
'LoginLog',
'OrgNode',
'DatabaseMonitor',
'RedisMonitor',
'ServerMonitor',
'StartChat',
'SystemDept',
'SystemDict',
'SystemFileManager',
'SystemLoginLog',
'SystemManagement',
'SystemOrgChart',
'SystemTools',
'SystemConfig',
'SystemConfigManager',
'SystemMonitoring',
'SystemMenu',
'SystemPermission',
'SystemPost',
'SystemRole',
'UIConfig',
'UIConfigManager',
'PageRenderMainHome',
'SubAppAIAgentEditor',
'SubAppAgentChat',
'SubAppAIKnowledgeDetail',
'SubAppAIKnowledgeLegacyDetailRedirect',
'SubAppAIKnowledgeLegacyRedirect',
'SubAppAIModelConfig',
'SubAppAIWorkflowEditor',
'SubAppAIWorkflowRuns',
'UserManagement',
'userCenter',
'userSettings',
]);
const LIGHT_ROUTE_PATH_PREFIXES = [
'/control-center',
'/ai-platform/agent',
'/ai-platform/knowledge',
'/ai-platform/knowledge-base',
@@ -52,30 +44,14 @@ const LIGHT_ROUTE_PATH_PREFIXES = [
'/ai-platform/workflow',
'/ai-platform/workflow-runs',
'/agent-chat',
'/application',
'/chat',
'/message/list',
'/message/announcement',
'/message/announcement-list',
'/page-render/main_home',
'/monitor',
'/core/ui-config',
'/system-config',
'/system/dept',
'/system/dict',
'/system/file-manager',
'/system/login-log',
'/system/org-chart',
'/system/post',
'/system/config',
'/system/menu',
'/system/new-permission',
'/system/role',
'/system/ui-config',
'/system/user',
'/tool/dict',
'/tool/file',
'/log/login-log',
'/user-center',
];
@@ -1,7 +1,8 @@
<script lang="ts" setup>
import { defineAsyncComponent, onMounted, ref, watch } from 'vue';
import { useRoute } from 'vue-router';
import { computed, defineAsyncComponent, onMounted, ref, watch } from 'vue';
import { useRoute, useRouter } from 'vue-router';
import { IconifyIcon } from '@vben/icons';
import { ElEmpty, ElMessage, ElScrollbar } from 'element-plus';
import { getPageByCodeApi } from '#/api/core/page-manager';
@@ -9,6 +10,7 @@ import { getPageByCodeApi } from '#/api/core/page-manager';
defineOptions({ name: 'PageRender' });
const route = useRoute();
const router = useRouter();
const DashboardRenderer = defineAsyncComponent(
() => import('#/components/dashboard-design/DashboardRenderer.vue'),
);
@@ -127,9 +129,30 @@ const opsHomeMenus = [
},
{
icon: 'lucide:sliders-horizontal',
id: 'ui-config',
path: '/system/ui-config',
title: '界面配置',
id: 'model-config',
path: '/ai-platform/model',
title: '模型配置',
},
];
const homeSections = [
{
description: '智能体、流程、知识库、模型和运行历史',
id: 'ai',
menus: aiHomeMenus,
title: 'AI 协作',
},
{
description: '用户、角色、菜单、权限和公告消息',
id: 'admin',
menus: adminHomeMenus,
title: '基础管理',
},
{
description: '需求分析、协作流程、可观测性和模型配置',
id: 'ops',
menus: opsHomeMenus,
title: '协作运维',
},
];
@@ -231,6 +254,12 @@ function getPageCode(): string {
return '';
}
const isMainHome = computed(() => getPageCode() === 'main_home');
function goTo(path: string) {
router.push(path);
}
async function loadPageData() {
const code = getPageCode();
if (!code) {
@@ -238,6 +267,12 @@ async function loadPageData() {
return;
}
if (code === 'main_home') {
pageConfig.value = '';
loading.value = false;
return;
}
loading.value = true;
try {
const page = await getPageByCodeApi(code);
@@ -269,7 +304,62 @@ watch(
<template>
<ElScrollbar class="rounded-[8px] py-3">
<div v-loading="loading" class="h-[calc(100vh-120px)] rounded-[8px] px-3">
<div
v-if="isMainHome"
class="min-h-[calc(100vh-120px)] rounded-[8px] px-3 pb-6"
>
<div class="mb-4 px-1">
<h1 class="text-foreground text-xl font-semibold">AI Agent Admin</h1>
<p class="text-muted-foreground mt-1 text-sm">
面向智能体协作的轻量后台
</p>
</div>
<div class="grid gap-3 xl:grid-cols-3">
<section
v-for="section in homeSections"
:key="section.id"
class="border-border bg-card rounded-[8px] border p-4"
>
<div class="mb-4">
<h2 class="text-foreground text-sm font-semibold">
{{ section.title }}
</h2>
<p class="text-muted-foreground mt-1 text-xs">
{{ section.description }}
</p>
</div>
<div class="grid grid-cols-2 gap-2">
<button
v-for="item in section.menus"
:key="item.id"
class="border-border hover:border-primary/70 hover:bg-accent group flex min-h-20 flex-col items-start justify-between rounded-[8px] border p-3 text-left transition-colors"
type="button"
@click="goTo(item.path)"
>
<span
class="flex size-8 items-center justify-center rounded-[6px] text-white shadow-sm"
:style="{ background: item.color || item.bgColor }"
>
<IconifyIcon :icon="item.icon" class="size-4" />
</span>
<span
class="text-foreground group-hover:text-primary mt-3 text-sm font-medium"
>
{{ item.title }}
</span>
</button>
</div>
</section>
</div>
</div>
<div
v-else
v-loading="loading"
class="h-[calc(100vh-120px)] rounded-[8px] px-3"
>
<DashboardRenderer v-if="pageConfig" :config="pageConfig" />
<ElEmpty v-else-if="!loading" description="暂无页面配置" />
</div>
@@ -1,9 +1,18 @@
<script setup lang="ts">
import type { ExecutionLogEntry } from '#/api/ai-platform/ai-platform';
import type {
ExecutionLogEntry,
WorkflowRunEvent,
} from '#/api/ai-platform/ai-platform';
import { $t } from '@vben/locales';
import { ElEmpty, ElScrollbar, ElTag, ElTimeline, ElTimelineItem } from 'element-plus';
import {
ElEmpty,
ElScrollbar,
ElTag,
ElTimeline,
ElTimelineItem,
} from 'element-plus';
import RunStatusTag from './RunStatusTag.vue';
@@ -31,9 +40,14 @@ function stringifyBrief(value: any, maxLength = 180) {
return text.length > maxLength ? `${text.slice(0, maxLength)}...` : text;
}
function getCommunication(log: ExecutionLogEntry) {
return log.metadata?.communication || {};
}
function getMetaItems(log: ExecutionLogEntry) {
const metadata = log.metadata || {};
const items: Array<{ label: string; type?: 'info' | 'primary' | 'success' | 'warning' }> = [];
const communication = getCommunication(log);
if (log.branch_label || log.branch) {
items.push({ label: `分支: ${log.branch_label || log.branch}`, type: 'primary' });
@@ -41,9 +55,15 @@ function getMetaItems(log: ExecutionLogEntry) {
if (metadata.subflow_name) {
items.push({ label: `子流程: ${metadata.subflow_name}`, type: 'success' });
}
if (metadata.model || metadata.model_id) {
items.push({ label: `模型: ${metadata.model || metadata.model_id}`, type: 'info' });
}
if (log.tokens_used) {
items.push({ label: `Token ${log.tokens_used}`, type: 'info' });
}
if (communication.channel) {
items.push({ label: getChannelLabel(communication.channel), type: 'info' });
}
if (log.status === 'waiting') {
items.push({ label: '等待用户输入', type: 'warning' });
}
@@ -51,12 +71,63 @@ function getMetaItems(log: ExecutionLogEntry) {
return items;
}
function getChannelLabel(channel: string) {
const labels: Record<string, string> = {
human_input: '人机协作',
parallel_branch: '并行分支',
subflow: '子流程',
workflow_edge: '流程传递',
};
return labels[channel] || channel;
}
function getEventTypeLabel(type?: string) {
const labels: Record<string, string> = {
answer: '回复',
complete: '完成',
error: '错误',
llm_chunk: 'LLM 输出',
message: '消息',
node_complete: '节点完成',
node_event: '节点事件',
node_start: '节点开始',
parallel_complete: '并行完成',
parallel_start: '并行开始',
start: '开始',
waiting_input: '等待输入',
};
return labels[type || ''] || type || '事件';
}
function getEventSummary(event: WorkflowRunEvent) {
if (event.content) return event.content;
if (event.message) return event.message;
if (event.error_message) return event.error_message;
if (event.error) return event.error;
if (event.event) return getEventSummary(event.event);
return stringifyBrief(event, 140);
}
function getLogEvents(log: ExecutionLogEntry): WorkflowRunEvent[] {
const directEvents = Array.isArray(log.events) ? log.events : [];
const metadataEvents = Array.isArray(log.metadata?.events)
? log.metadata.events
: [];
return [...directEvents, ...metadataEvents].slice(0, 4);
}
function getLogDetail(log: ExecutionLogEntry) {
const communication = getCommunication(log);
if (log.error) return log.error;
if (log.status === 'waiting') {
return stringifyBrief(log.metadata?.waiting_config || log.metadata?.config || log.output);
}
return stringifyBrief(log.output || log.metadata?.branch_results || log.metadata?.result);
return stringifyBrief(
log.output
|| log.metadata?.branch_results
|| log.metadata?.result
|| communication.message,
);
}
</script>
@@ -104,6 +175,26 @@ function getLogDetail(log: ExecutionLogEntry) {
>
{{ getLogDetail(log) }}
</div>
<div
v-if="getLogEvents(log).length"
class="bg-muted/40 border-border mt-2 rounded border px-2 py-1.5"
>
<div class="text-muted-foreground mb-1 text-xs font-medium">
运行事件
</div>
<div
v-for="(event, eventIndex) in getLogEvents(log)"
:key="`${log.node_id}-event-${eventIndex}`"
class="text-muted-foreground flex gap-2 text-xs"
>
<span class="shrink-0 font-medium">
{{ getEventTypeLabel(event.type || event.event?.type) }}
</span>
<span class="line-clamp-2 min-w-0 whitespace-pre-wrap">
{{ getEventSummary(event) }}
</span>
</div>
</div>
</button>
</ElTimelineItem>
</ElTimeline>
@@ -54,79 +54,12 @@ const components: Record<string, any> = {
),
// 循环节点
loop: defineAsyncComponent(() => import('../panels/LoopPanel.vue')),
// 表单节点
form_basic_info: defineAsyncComponent(
() => import('../panels/FormBasicInfoPanel.vue'),
),
form_database_design: defineAsyncComponent(
() => import('../panels/FormDatabaseDesignPanel.vue'),
),
form_database_create: defineAsyncComponent(
() => import('../panels/FormDatabaseCreatePanel.vue'),
),
form_ui_design: defineAsyncComponent(
() => import('../panels/FormUIDesignPanel.vue'),
),
form_list_design: defineAsyncComponent(
() => import('../panels/FormListDesignPanel.vue'),
),
form_create: defineAsyncComponent(
() => import('../panels/FormCreatePanel.vue'),
),
form_publish: defineAsyncComponent(
() => import('../panels/FormPublishPanel.vue'),
),
// 应用节点
app_create: defineAsyncComponent(
() => import('../panels/AppCreatePanel.vue'),
),
app_design: defineAsyncComponent(
() => import('../panels/AppDesignPanel.vue'),
),
app_settings: defineAsyncComponent(
() => import('../panels/AppSettingsPanel.vue'),
),
app_update: defineAsyncComponent(
() => import('../panels/AppUpdatePanel.vue'),
),
// 仪表盘节点
dashboard_basic_info: defineAsyncComponent(
() => import('../panels/DashboardBasicInfoPanel.vue'),
),
dashboard_design: defineAsyncComponent(
() => import('../panels/DashboardDesignPanel.vue'),
),
dashboard_create: defineAsyncComponent(
() => import('../panels/DashboardCreatePanel.vue'),
),
dashboard_publish: defineAsyncComponent(
() => import('../panels/DashboardPublishPanel.vue'),
),
// 系统总结节点
system_summary: defineAsyncComponent(
() => import('../panels/SystemSummaryPanel.vue'),
),
// 子流程节点
subflow: defineAsyncComponent(() => import('../panels/SubflowPanel.vue')),
// 表单数据节点
form_data_create: defineAsyncComponent(
() => import('../panels/FormDataPanel.vue'),
),
form_data_read: defineAsyncComponent(
() => import('../panels/FormDataPanel.vue'),
),
form_data_update: defineAsyncComponent(
() => import('../panels/FormDataPanel.vue'),
),
form_data_delete: defineAsyncComponent(
() => import('../panels/FormDataPanel.vue'),
),
form_data_list: defineAsyncComponent(
() => import('../panels/FormDataPanel.vue'),
),
form_schema_to_llm: defineAsyncComponent(
() => import('../panels/FormDataPanel.vue'),
),
// Text-to-SQL 节点
text_to_sql: defineAsyncComponent(
() => import('../panels/TextToSqlPanel.vue'),
@@ -27,14 +27,11 @@ import {
Database,
DatabaseBackup,
DatabaseZap,
FilePlus,
FileText,
Fullscreen,
GitBranch,
GitFork,
Globe,
HelpCircle,
LayoutDashboard,
LayoutTemplate,
ListChecks,
Map,
@@ -45,13 +42,9 @@ import {
Plus,
Redo2,
RefreshCw,
Save,
Search,
Send,
Settings,
Snowflake,
Square,
TableProperties,
Trash2,
Undo2,
Upload,
@@ -84,32 +77,16 @@ import ContextMenu from './components/ContextMenu.vue';
import Panel from './components/Panel.vue';
import VersionHistoryPanel from './components/VersionHistoryPanel.vue';
import AddButtonEdge from './edges/AddButtonEdge.vue';
import AppCreateNode from './nodes/AppCreateNode.vue';
import AppDesignNode from './nodes/AppDesignNode.vue';
import AppSettingsNode from './nodes/AppSettingsNode.vue';
import AppUpdateNode from './nodes/AppUpdateNode.vue';
import ChoiceNode from './nodes/ChoiceNode.vue';
import CodeNode from './nodes/CodeNode.vue';
import ConditionNode from './nodes/ConditionNode.vue';
import ConfirmNode from './nodes/ConfirmNode.vue';
import DashboardBasicInfoNode from './nodes/DashboardBasicInfoNode.vue';
import DashboardCreateNode from './nodes/DashboardCreateNode.vue';
import DashboardDesignNode from './nodes/DashboardDesignNode.vue';
import DashboardPublishNode from './nodes/DashboardPublishNode.vue';
import DbDeleteNode from './nodes/DbDeleteNode.vue';
import DbInsertNode from './nodes/DbInsertNode.vue';
import DbQueryNode from './nodes/DbQueryNode.vue';
import DbSqlNode from './nodes/DbSqlNode.vue';
import DbUpdateNode from './nodes/DbUpdateNode.vue';
import EndNode from './nodes/EndNode.vue';
import FormBasicInfoNode from './nodes/FormBasicInfoNode.vue';
import FormCreateNode from './nodes/FormCreateNode.vue';
import FormDatabaseCreateNode from './nodes/FormDatabaseCreateNode.vue';
import FormDatabaseDesignNode from './nodes/FormDatabaseDesignNode.vue';
import FormDataNode from './nodes/FormDataNode.vue';
import FormListDesignNode from './nodes/FormListDesignNode.vue';
import FormPublishNode from './nodes/FormPublishNode.vue';
import FormUIDesignNode from './nodes/FormUIDesignNode.vue';
import HttpNode from './nodes/HttpNode.vue';
import IntentNode from './nodes/IntentNode.vue';
import KnowledgeRetrievalNode from './nodes/KnowledgeRetrievalNode.vue';
@@ -184,35 +161,10 @@ const nodeTypes = {
snowflake_cortex_analyst: markRaw(SnowflakeCortexAnalystNode),
// 循环节点
loop: markRaw(LoopNode),
// 表单节点
form_basic_info: markRaw(FormBasicInfoNode),
form_database_design: markRaw(FormDatabaseDesignNode),
form_database_create: markRaw(FormDatabaseCreateNode),
form_ui_design: markRaw(FormUIDesignNode),
form_list_design: markRaw(FormListDesignNode),
form_create: markRaw(FormCreateNode),
form_publish: markRaw(FormPublishNode),
// 应用管理节点
app_create: markRaw(AppCreateNode),
app_design: markRaw(AppDesignNode),
app_settings: markRaw(AppSettingsNode),
app_update: markRaw(AppUpdateNode),
// 仪表盘节点
dashboard_basic_info: markRaw(DashboardBasicInfoNode),
dashboard_design: markRaw(DashboardDesignNode),
dashboard_create: markRaw(DashboardCreateNode),
dashboard_publish: markRaw(DashboardPublishNode),
// 系统总结节点
system_summary: markRaw(SystemSummaryNode),
// 子流程节点
subflow: markRaw(SubflowNode),
// 表单数据节点
form_data_create: markRaw(FormDataNode),
form_data_read: markRaw(FormDataNode),
form_data_update: markRaw(FormDataNode),
form_data_delete: markRaw(FormDataNode),
form_data_list: markRaw(FormDataNode),
form_schema_to_llm: markRaw(FormDataNode),
// Text-to-SQL 节点
text_to_sql: markRaw(TextToSqlNode),
// 知识库节点
@@ -390,94 +342,6 @@ const nodeCategories = computed(() => [
},
],
},
{
name: $t('ai-platform.workflow.editor.categories.formDesign'),
nodes: [
{
type: 'form_basic_info',
label: getNodeLabel('form_basic_info'),
icon: 'FileText',
color: 'purple',
},
{
type: 'form_database_design',
label: getNodeLabel('form_database_design'),
icon: 'Database',
color: 'purple',
},
{
type: 'form_database_create',
label: getNodeLabel('form_database_create'),
icon: 'DatabaseZap',
color: 'purple',
},
{
type: 'form_ui_design',
label: getNodeLabel('form_ui_design'),
icon: 'LayoutTemplate',
color: 'purple',
},
{
type: 'form_list_design',
label: getNodeLabel('form_list_design'),
icon: 'TableProperties',
color: 'purple',
},
{
type: 'form_create',
label: getNodeLabel('form_create'),
icon: 'FilePlus',
color: 'purple',
},
{
type: 'form_publish',
label: getNodeLabel('form_publish'),
icon: 'Send',
color: 'purple',
},
],
},
{
name: $t('ai-platform.workflow.editor.categories.formData'),
nodes: [
{
type: 'form_data_create',
label: getNodeLabel('form_data_create'),
icon: 'FilePlus',
color: 'green',
},
{
type: 'form_data_read',
label: getNodeLabel('form_data_read'),
icon: 'FileText',
color: 'blue',
},
{
type: 'form_data_update',
label: getNodeLabel('form_data_update'),
icon: 'FileText',
color: 'orange',
},
{
type: 'form_data_delete',
label: getNodeLabel('form_data_delete'),
icon: 'Trash2',
color: 'red',
},
{
type: 'form_data_list',
label: getNodeLabel('form_data_list'),
icon: 'Search',
color: 'blue',
},
{
type: 'form_schema_to_llm',
label: getNodeLabel('form_schema_to_llm'),
icon: 'Code',
color: 'purple',
},
],
},
{
name: $t('ai-platform.workflow.editor.categories.dialog'),
nodes: [
@@ -587,6 +451,12 @@ const nodeCategories = computed(() => [
icon: 'Workflow',
color: 'purple',
},
{
type: 'system_summary',
label: getNodeLabel('system_summary'),
icon: 'ClipboardCheck',
color: 'purple',
},
{
type: 'end',
label: getNodeLabel('end'),
@@ -612,72 +482,6 @@ const nodeCategories = computed(() => [
},
],
},
{
name: $t('ai-platform.workflow.editor.categories.appManage'),
nodes: [
{
type: 'app_create',
label: getNodeLabel('app_create'),
icon: 'LayoutTemplate',
color: 'indigo',
},
{
type: 'app_design',
label: getNodeLabel('app_design'),
icon: 'LayoutDashboard',
color: 'indigo',
},
{
type: 'app_settings',
label: getNodeLabel('app_settings'),
icon: 'Settings',
color: 'indigo',
},
{
type: 'app_update',
label: getNodeLabel('app_update'),
icon: 'Save',
color: 'indigo',
},
],
},
{
name: $t('ai-platform.workflow.editor.categories.dashboard'),
icon: 'LayoutDashboard',
color: 'cyan',
nodes: [
{
type: 'dashboard_basic_info',
label: getNodeLabel('dashboard_basic_info'),
icon: 'FileText',
color: 'cyan',
},
{
type: 'dashboard_design',
label: getNodeLabel('dashboard_design'),
icon: 'LayoutDashboard',
color: 'cyan',
},
{
type: 'dashboard_create',
label: getNodeLabel('dashboard_create'),
icon: 'FilePlus',
color: 'cyan',
},
{
type: 'dashboard_publish',
label: getNodeLabel('dashboard_publish'),
icon: 'Upload',
color: 'cyan',
},
{
type: 'system_summary',
label: getNodeLabel('system_summary'),
icon: 'ClipboardCheck',
color: 'cyan',
},
],
},
{
name: $t('ai-platform.workflow.editor.categories.knowledge'),
nodes: [
@@ -720,17 +524,6 @@ const iconComponents: Record<string, any> = {
// 循环节点图标
RefreshCw,
CornerDownLeft,
// 表单节点图标
FileText,
TableProperties,
Send,
// 应用节点图标
LayoutDashboard,
Settings,
Save,
// 仪表盘节点图标
FilePlus,
Upload,
// 系统总结节点图标
ClipboardCheck,
// 子流程节点图标
@@ -741,21 +534,7 @@ const iconComponents: Record<string, any> = {
// 过滤后的节点分类
const filteredNodeCategories = computed(() => {
// 首先根据工作流类型过滤分类
let categories = nodeCategories.value;
// 如果是通用类型,排除表单设计、应用管理、仪表盘分组
if (workflow.value?.workflow_type === 'general') {
const excludeNames = new Set([
$t('ai-platform.workflow.editor.categories.appManage'),
$t('ai-platform.workflow.editor.categories.dashboard'),
$t('ai-platform.workflow.editor.categories.formDesign'),
]);
categories = nodeCategories.value.filter(
(category: { name: string; nodes: any[] }) =>
!excludeNames.has(category.name),
);
}
const categories = nodeCategories.value;
// 再根据搜索关键词过滤
if (!nodeSearchQuery.value.trim()) {
@@ -1,32 +1,16 @@
import { markRaw } from 'vue';
import AddButtonEdge from '../editor/edges/AddButtonEdge.vue';
import AppCreateNode from '../editor/nodes/AppCreateNode.vue';
import AppDesignNode from '../editor/nodes/AppDesignNode.vue';
import AppSettingsNode from '../editor/nodes/AppSettingsNode.vue';
import AppUpdateNode from '../editor/nodes/AppUpdateNode.vue';
import ChoiceNode from '../editor/nodes/ChoiceNode.vue';
import CodeNode from '../editor/nodes/CodeNode.vue';
import ConditionNode from '../editor/nodes/ConditionNode.vue';
import ConfirmNode from '../editor/nodes/ConfirmNode.vue';
import DashboardBasicInfoNode from '../editor/nodes/DashboardBasicInfoNode.vue';
import DashboardCreateNode from '../editor/nodes/DashboardCreateNode.vue';
import DashboardDesignNode from '../editor/nodes/DashboardDesignNode.vue';
import DashboardPublishNode from '../editor/nodes/DashboardPublishNode.vue';
import DbDeleteNode from '../editor/nodes/DbDeleteNode.vue';
import DbInsertNode from '../editor/nodes/DbInsertNode.vue';
import DbQueryNode from '../editor/nodes/DbQueryNode.vue';
import DbSqlNode from '../editor/nodes/DbSqlNode.vue';
import DbUpdateNode from '../editor/nodes/DbUpdateNode.vue';
import EndNode from '../editor/nodes/EndNode.vue';
import FormBasicInfoNode from '../editor/nodes/FormBasicInfoNode.vue';
import FormCreateNode from '../editor/nodes/FormCreateNode.vue';
import FormDatabaseCreateNode from '../editor/nodes/FormDatabaseCreateNode.vue';
import FormDatabaseDesignNode from '../editor/nodes/FormDatabaseDesignNode.vue';
import FormDataNode from '../editor/nodes/FormDataNode.vue';
import FormListDesignNode from '../editor/nodes/FormListDesignNode.vue';
import FormPublishNode from '../editor/nodes/FormPublishNode.vue';
import FormUIDesignNode from '../editor/nodes/FormUIDesignNode.vue';
import HttpNode from '../editor/nodes/HttpNode.vue';
import IntentNode from '../editor/nodes/IntentNode.vue';
import KnowledgeRetrievalNode from '../editor/nodes/KnowledgeRetrievalNode.vue';
@@ -67,29 +51,8 @@ export const workflowNodeTypes = {
snowflake_cortex_llm: markRaw(SnowflakeCortexLLMNode),
snowflake_cortex_analyst: markRaw(SnowflakeCortexAnalystNode),
loop: markRaw(LoopNode),
form_basic_info: markRaw(FormBasicInfoNode),
form_database_design: markRaw(FormDatabaseDesignNode),
form_database_create: markRaw(FormDatabaseCreateNode),
form_ui_design: markRaw(FormUIDesignNode),
form_list_design: markRaw(FormListDesignNode),
form_create: markRaw(FormCreateNode),
form_publish: markRaw(FormPublishNode),
app_create: markRaw(AppCreateNode),
app_design: markRaw(AppDesignNode),
app_settings: markRaw(AppSettingsNode),
app_update: markRaw(AppUpdateNode),
dashboard_basic_info: markRaw(DashboardBasicInfoNode),
dashboard_design: markRaw(DashboardDesignNode),
dashboard_create: markRaw(DashboardCreateNode),
dashboard_publish: markRaw(DashboardPublishNode),
system_summary: markRaw(SystemSummaryNode),
subflow: markRaw(SubflowNode),
form_data_create: markRaw(FormDataNode),
form_data_read: markRaw(FormDataNode),
form_data_update: markRaw(FormDataNode),
form_data_delete: markRaw(FormDataNode),
form_data_list: markRaw(FormDataNode),
form_schema_to_llm: markRaw(FormDataNode),
text_to_sql: markRaw(TextToSqlNode),
knowledge_retrieval: markRaw(KnowledgeRetrievalNode),
};