# 🔄 节点功能与目录分离重构 ## 📋 需求分析 ### 原有问题 `category` 字段承担双重职责: 1. 表示节点功能(Loader/Transform/Visualizer)→ 用于节点颜色、图标、Handle 显示 2. 表示目录分类 → 用于 NodePalette 树状展示 **冲突**:无法实现 "Loader/CSV"、"Transform/Filter" 这样的二级目录分类 ### 解决方案 将 `category` 和节点功能分离: - **`function`**: 节点功能(Loader/Transform/Visualizer) - **`category`**: 树状目录路径(Loader/CSV, Transform/Filter) 同时弃用前端的 `node.type` 属性,统一使用 `function` 字段。 --- ## ✅ 后端修改(main.py) ### 所有节点添加 `function` 字段 ```python # 修改前 "CSVLoader": { "display_name": "CSV 数据加载器", "category": "Loader", # 既表示功能又表示目录 "node_logic": "standard", # ... } # 修改后 "CSVLoader": { "display_name": "CSV 数据加载器", "function": "Loader", # ✅ 节点功能:加载器 "category": "Loader/CSV", # ✅ 树状目录:加载器/CSV "node_logic": "standard", # ... } ``` ### 元数据结构完整示例 ```python { "CSVLoader": { "display_name": "CSV 数据加载器", "function": "Loader", # 节点功能 "category": "Loader/CSV", # 树状目录 "node_logic": "standard", # 节点逻辑 "supports_preview": True, "inputs": [], "outputs": [{"name": "table", "type": "DataTable"}], "param_schema": {...} }, "UTraceLoader": { "display_name": "UTrace 文件加载器", "function": "Loader", "category": "Loader/Trace", # 二级目录:Trace # ... }, "FilterRows": { "display_name": "行过滤器", "function": "Transform", "category": "Transform/Filter", # 二级目录:Filter # ... }, "SelectColumns": { "display_name": "列选择器", "function": "Transform", "category": "Transform/Select", # 二级目录:Select # ... }, "Aggregator": { "display_name": "数据聚合", "function": "Transform", "category": "Transform/Aggregate", "node_logic": "aggregate", "allow_multiple_inputs": True, # ✅ 多输入支持 # ... }, "TimeRangeFilter": { "display_name": "时间范围过滤", "function": "Transform", "category": "Transform/Filter", # 与 FilterRows 共享目录 # ... }, "ChartVisualizer": { "display_name": "图表可视化", "function": "Visualizer", "category": "Visualizer/Chart", # ... }, "TableOutput": { "display_name": "表格输出", "function": "Visualizer", "category": "Visualizer/Table", # ... } } ``` ### 完整节点清单 | 节点类型 | function | category | node_logic | allow_multiple_inputs | |----------|----------|----------|------------|----------------------| | CSVLoader | Loader | Loader/CSV | standard | false | | UTraceLoader | Loader | Loader/Trace | standard | false | | FilterRows | Transform | Transform/Filter | standard | false | | SelectColumns | Transform | Transform/Select | standard | false | | Aggregator | Transform | Transform/Aggregate | aggregate | ✅ true | | TimeRangeFilter | Transform | Transform/Filter | standard | false | | ChartVisualizer | Visualizer | Visualizer/Chart | standard | false | | TableOutput | Visualizer | Visualizer/Table | standard | false | --- ## ✅ 前端修改 ### 1. UniversalNode.tsx #### 接口重构 ```typescript // 修改前 interface UniversalNodeProps { data: { meta?: { category?: 'Loader' | 'Transform' | 'Visualizer' // ❌ 混用 } } } // 修改后 interface UniversalNodeProps { data: { meta?: { function?: 'Loader' | 'Transform' | 'Visualizer' // ✅ 节点功能 category?: string // ✅ 树状目录路径(如 "Loader/CSV") } } } ``` #### 常量重命名 ```typescript // 修改前 const CATEGORY_COLORS: Record = { Loader: '#3b82f6', Transform: '#8b5cf6', Visualizer: '#22c55e', } // 修改后 const FUNCTION_COLORS: Record = { Loader: '#3b82f6', Transform: '#8b5cf6', Visualizer: '#22c55e', } ``` #### 组件实现 ```typescript // 修改前 const category = data.meta?.category || 'Transform' const headerColor = CATEGORY_COLORS[category] || '#6b7280' const showInput = category === 'Transform' || category === 'Visualizer' const showOutput = category === 'Loader' || category === 'Transform' // 修改后 const nodeFunction = data.meta?.function || 'Transform' const headerColor = FUNCTION_COLORS[nodeFunction] || '#6b7280' const showInput = nodeFunction === 'Transform' || nodeFunction === 'Visualizer' const showOutput = nodeFunction === 'Loader' || nodeFunction === 'Transform' ``` #### Header 显示 ```typescript // 修改前
{categoryIcon[category] || '🔹'}
{category}
// 修改后
{functionIcon[nodeFunction] || '🔹'}
{nodeFunction}
{/* 显示 Loader/Transform/Visualizer */} ``` --- ### 2. Inspector.tsx #### 图标映射 ```typescript // 修改前 {node.type === 'csv' ? '📂' : node.type === 'transform' ? '⚙️' : node.type === 'output' ? '📊' : '📦'} // 修改后 {node.data?.meta?.function === 'Loader' ? '📥' : node.data?.meta?.function === 'Transform' ? '⚙️' : node.data?.meta?.function === 'Visualizer' ? '📊' : '📦'} ``` #### 标题显示 ```typescript // 修改前

{node.type.toUpperCase()}

{/* CSV, TRANSFORM, OUTPUT */} // 修改后

{node.data?.meta?.display_name || 'Node'}

{/* CSV 数据加载器 */} ``` #### 系统属性 ```typescript // 修改前
{/* csv, transform, output */}
// 修改后
{/* Loader */}
{/* Loader/CSV */}
``` --- ### 3. Workspace.tsx #### MiniMap 颜色映射 ```typescript // 修改前 { const category = node.data?.meta?.category if (category === 'Loader') return '#3b82f6' if (category === 'Transform') return '#8b5cf6' if (category === 'Visualizer') return '#22c55e' return '#64748b' }} /> // 修改后 { const nodeFunction = node.data?.meta?.function if (nodeFunction === 'Loader') return '#3b82f6' if (nodeFunction === 'Transform') return '#8b5cf6' if (nodeFunction === 'Visualizer') return '#22c55e' return '#64748b' }} /> ``` --- ### 4. NodePalette.tsx #### 接口更新 ```typescript // 修改前 interface PluginMeta { display_name?: string category?: string // 混用 } // 修改后 interface PluginMeta { display_name?: string function?: string // 节点功能 category?: string // 树状目录 } ``` #### 二级目录解析 ```typescript // 修改前 const groups: Record }> = {} for (const [k, v] of Object.entries(plugins)) { const cat = (v.category || 'Other').split('/')[0] if (!groups[cat]) groups[cat] = { key: cat, items: [] } groups[cat].items.push({ type: k, meta: v }) } // 修改后 const groups: Record }> = {} for (const [k, v] of Object.entries(plugins)) { const categoryPath = v.category || 'Other' const [mainCat, subCat] = categoryPath.split('/') if (!groups[mainCat]) { groups[mainCat] = { key: mainCat, items: [] } } groups[mainCat].items.push({ type: k, meta: v, subCategory: subCat // 保存二级分类 }) } ``` #### 算子卡片显示 ```typescript // 修改前
{group.key === 'Loaders' ? '📂' : group.key === 'Transforms' ? '⚙️' : '📦'} {item.meta.display_name}
// 修改后
{/* 主行:图标 + 名称 */}
{item.meta.function === 'Loader' ? '📥' : item.meta.function === 'Transform' ? '⚙️' : item.meta.function === 'Visualizer' ? '📊' : '📦'} {item.meta.display_name}
{/* 二级分类标签 */} {item.subCategory && (
{item.subCategory} {/* CSV, Trace, Filter, Select */}
)}
``` --- ## 📊 视觉效果对比 ### NodePalette 显示 **修改前**: ``` ▸ Loaders (2) 📂 CSV 数据加载器 📂 UTrace 文件加载器 ▸ Transforms (4) ⚙️ 行过滤器 ⚙️ 列选择器 ⚙️ 数据聚合 ⚙️ 时间范围过滤 ``` **修改后**: ``` ▸ Loader (2) 📥 CSV 数据加载器 CSV ← 二级分类标签 📥 UTrace 文件加载器 Trace ← 二级分类标签 ▸ Transform (4) ⚙️ 行过滤器 Filter ← 二级分类标签 ⚙️ 列选择器 Select ⚙️ 数据聚合 Aggregate ⚙️ 时间范围过滤 Filter ← 相同二级目录 ``` ### Inspector 面板 **修改前**: ``` ┌─ 系统信息 ────────┐ │ 节点 ID: n_xxx │ │ 节点类型: csv │ ← 前端类型(混乱) │ 分类: Loader │ ← 混用 └──────────────────┘ ``` **修改后**: ``` ┌─ 系统信息 ────────────┐ │ 节点 ID: n_xxx │ │ 节点功能: Loader │ ← 清晰的功能标识 │ 目录分类: Loader/CSV │ ← 完整的目录路径 └──────────────────────┘ ``` ### 节点本体显示 **修改前**: ``` ┌─ CSV 数据加载器 ──┐ │ Loader │ ← 显示 category │ file_path: ... │ └──────────────────┘ ``` **修改后**: ``` ┌─ CSV 数据加载器 ──┐ │ LOADER │ ← 显示 function(功能) │ file_path: ... │ └──────────────────┘ ``` --- ## 🎯 数据流追踪 ### 从后端到前端的完整流程 ``` 1️⃣ 后端定义(main.py) ↓ GET /plugins ↓ { "CSVLoader": { "function": "Loader", ← 节点功能 "category": "Loader/CSV" ← 树状目录 } } ↓ 2️⃣ NodePalette 接收 ↓ 解析 category: "Loader/CSV" ├─ mainCat: "Loader" ← 一级目录 └─ subCat: "CSV" ← 二级目录 ↓ 显示: Loader (2) 📥 CSV 数据加载器 CSV ← subCategory 标签 ↓ 3️⃣ 拖拽到画布 ↓ 创建节点: { id: "n_xxx", type: "universal", ← 所有节点统一类型 data: { meta: { function: "Loader", ← 用于功能判断 category: "Loader/CSV" ← 用于显示目录 } } } ↓ 4️⃣ UniversalNode 渲染 ↓ 读取 function: "Loader" ├─ 决定颜色:#3b82f6(蓝色) ├─ 决定图标:📥 ├─ 决定 Handle:只显示 Output └─ 显示文字:LOADER ↓ 5️⃣ Inspector 显示 ↓ 系统信息: - 节点功能: Loader ← function - 目录分类: Loader/CSV ← category ``` --- ## 🔍 关键概念对比 ### function vs category | 字段 | 用途 | 值域 | 示例 | 前端使用位置 | |------|------|------|------|-------------| | **function** | 节点功能 | Loader, Transform, Visualizer | `"Loader"` | UniversalNode 颜色、图标、Handle 逻辑 | | **category** | 树状目录 | 任意路径(支持 / 分隔) | `"Loader/CSV"` | NodePalette 分组、Inspector 显示 | ### 节点类型演变 | 阶段 | 前端类型 | 后端类型 | 功能标识 | 目录标识 | |------|---------|---------|---------|---------| | **Phase 1** | node.type = 'csv' | N/A | ❌ 混乱 | ❌ 无 | | **Phase 2** | node.type = 'universal' | meta.category | ⚠️ category 混用 | ⚠️ category 混用 | | **Phase 3 (现在)** | node.type = 'universal' | meta.function | ✅ function | ✅ category | --- ## 🧪 测试验证 ### 测试 1:后端 API 验证 ```bash # 访问后端 curl http://127.0.0.1:8000/plugins # 验证响应包含 { "plugins": { "CSVLoader": { "function": "Loader", ✅ "category": "Loader/CSV", ✅ "allow_multiple_inputs": false }, "Aggregator": { "function": "Transform", "category": "Transform/Aggregate", "allow_multiple_inputs": true ✅ } } } ``` ### 测试 2:NodePalette 二级目录 ``` 操作:打开应用 预期:左侧面板显示 ✅ Loader (2) 📥 CSV 数据加载器 CSV ← 二级分类标签 📥 UTrace 文件加载器 Trace ✅ Transform (4) ⚙️ 行过滤器 Filter ⚙️ 列选择器 Select ⚙️ 数据聚合 Aggregate ⚙️ 时间范围过滤 Filter ← 相同目录共享 ``` ### 测试 3:节点功能识别 ``` 操作:拖拽 CSV Loader 到画布 预期: 1. 节点颜色:蓝色(#3b82f6) ✅ 2. 节点图标:📥 ✅ 3. Handle 显示:只有 Output ✅ 4. 节点 Header:显示 "LOADER" ✅ 操作:拖拽 FilterRows 到画布 预期: 1. 节点颜色:紫色(#8b5cf6) ✅ 2. 节点图标:⚙️ ✅ 3. Handle 显示:Input + Output ✅ 4. 节点 Header:显示 "TRANSFORM" ✅ ``` ### 测试 4:Inspector 显示 ``` 操作:选中 CSV Loader 节点 预期 Inspector 显示: ┌─ 系统信息 ─────────────┐ │ 节点 ID: n_xxx │ ✅ │ 节点功能: Loader │ ✅ (原来是 node.type) │ 目录分类: Loader/CSV │ ✅ (新增) └───────────────────────┘ ┌─ 参数配置 ─────────────┐ │ file_path: [输入框] │ │ delimiter: [输入框] │ └───────────────────────┘ ``` ### 测试 5:MiniMap 颜色 ``` 操作:拖拽多种节点到画布 预期 MiniMap 显示: - Loader 节点:蓝色 ✅ - Transform 节点:紫色 ✅ - Visualizer 节点:绿色 ✅ ``` --- ## 📁 修改文件清单 ### 后端 1. **server/main.py** - ✅ 所有节点添加 `"function"` 字段 - ✅ 修改 `"category"` 为树状路径(支持 `/` 分隔) - ✅ Aggregator 添加 `"allow_multiple_inputs": True` ### 前端 2. **web/src/components/nodes/UniversalNode.tsx** - ✅ `CATEGORY_COLORS` → `FUNCTION_COLORS` - ✅ `meta.category` → `meta.function` - ✅ `categoryIcon` → `functionIcon` - ✅ Header 显示 `nodeFunction` 代替 `category` 3. **web/src/components/Inspector.tsx** - ✅ 图标映射:`node.type` → `node.data.meta.function` - ✅ 标题显示:`node.type.toUpperCase()` → `node.data.meta.display_name` - ✅ 系统属性:删除 `node.type`,添加 `function` 和 `category` 显示 4. **web/src/components/Workspace.tsx** - ✅ MiniMap 颜色:`meta.category` → `meta.function` 5. **web/src/components/NodePalette.tsx** - ✅ 接口添加 `function` 字段 - ✅ 二级目录解析:`category.split('/')` → `[mainCat, subCat]` - ✅ 算子卡片:显示 `function` 图标 + `subCategory` 标签 --- ## 🎨 设计亮点 ### 1. 职责分离 - **function**: 节点功能逻辑(颜色、图标、Handle) - **category**: UI 组织结构(树状目录、分组) ### 2. 可扩展性 ```python # 轻松添加三级目录 "category": "Transform/Filter/Advanced" # 前端自动支持 categoryPath.split('/') → ['Transform', 'Filter', 'Advanced'] ``` ### 3. 语义化 ```typescript // 修改前(混乱) if (node.type === 'csv') { ... } // 前端类型 if (meta.category === 'Loader') { ... } // 既是功能又是目录 // 修改后(清晰) if (meta.function === 'Loader') { ... } // 节点功能 if (meta.category.startsWith('Loader/')) { ... } // 目录位置 ``` ### 4. 向后兼容 - 前端 `node.type` 统一为 `'universal'`,不再依赖具体类型 - 所有节点使用相同的 UniversalNode 组件 - 元数据驱动,易于扩展 --- ## 🚀 后续增强建议 ### 1. 动态树状目录 ```typescript // 支持折叠二级目录 Loader (2) ▸ CSV (1) - CSV 数据加载器 ▸ Trace (1) - UTrace 文件加载器 Transform (4) ▸ Filter (2) - 行过滤器 - 时间范围过滤 ▸ Select (1) ▸ Aggregate (1) ``` ### 2. 功能徽章 ```typescript // 节点卡片显示功能标签 ┌─ CSV 数据加载器 ────────┐ │ [Loader] [CSV] [⚡Preview] │ │ file_path: ... │ └─────────────────────────┘ ``` ### 3. 搜索增强 ```typescript // 支持按 function 和 category 过滤 搜索:Loader → 显示所有 Loader 功能节点 搜索:Filter → 显示所有 Filter 目录节点 搜索:Loader/CSV → 显示特定路径节点 ``` --- **完成时间**:2026-01-07 **版本**:Function & Category Separation v1.0 **状态**:✅ 重构完成,功能与目录完全分离