feat: improve workflow observability and lighten admin frontend
This commit is contained in:
@@ -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 {
|
||||
|
||||
@@ -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';
|
||||
|
||||
@@ -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$/,
|
||||
|
||||
@@ -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',
|
||||
|
||||
@@ -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),
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user