156 lines
4.5 KiB
TypeScript
156 lines
4.5 KiB
TypeScript
|
|
/**
|
||
|
|
* 旧版数据格式适配层
|
||
|
|
* 将旧版前端数据格式转换为后端期望的协议格式
|
||
|
|
*/
|
||
|
|
|
||
|
|
/**
|
||
|
|
* 将旧版 nodes/edges 转换为后端执行器期望的格式
|
||
|
|
*/
|
||
|
|
export function toExecutorPayload(data: {
|
||
|
|
nodes: any[]
|
||
|
|
edges: any[]
|
||
|
|
settings?: any
|
||
|
|
globalContext?: any
|
||
|
|
}) {
|
||
|
|
const { nodes, edges, settings, globalContext } = data
|
||
|
|
|
||
|
|
// 转换节点
|
||
|
|
const buildParams = (node: any) => {
|
||
|
|
if (node.params && typeof node.params === 'object') return node.params
|
||
|
|
|
||
|
|
const meta = node.meta || node.data?.meta || {}
|
||
|
|
const schema = meta.param_schema
|
||
|
|
if (schema) {
|
||
|
|
let names: string[] = []
|
||
|
|
if (Array.isArray(schema)) {
|
||
|
|
names = schema.map((s: any) => s && s.name).filter(Boolean)
|
||
|
|
} else if (schema && typeof schema === 'object') {
|
||
|
|
names = Object.keys(schema)
|
||
|
|
}
|
||
|
|
const out: Record<string, any> = {}
|
||
|
|
names.forEach((k) => {
|
||
|
|
if (node.data && Object.prototype.hasOwnProperty.call(node.data, k)) out[k] = node.data[k]
|
||
|
|
else if (node.params && Object.prototype.hasOwnProperty.call(node.params, k)) out[k] = node.params[k]
|
||
|
|
else out[k] = null
|
||
|
|
})
|
||
|
|
return out
|
||
|
|
}
|
||
|
|
|
||
|
|
const source = (node.params && typeof node.params === 'object') ? node.params : (node.data && typeof node.data === 'object' ? node.data : {})
|
||
|
|
const reserved = new Set(['meta', 'label', 'position', 'updim', 'file_path', 'file', 'dirty'])
|
||
|
|
const out: Record<string, any> = {}
|
||
|
|
Object.entries(source).forEach(([k, v]) => {
|
||
|
|
if (!reserved.has(k)) out[k] = v
|
||
|
|
})
|
||
|
|
return out
|
||
|
|
}
|
||
|
|
|
||
|
|
const nodesOut = nodes.map((n: any) => {
|
||
|
|
// Support both React-view nodes and exported workflow nodes
|
||
|
|
const className = n.class_name || n.data?.meta?.class_name || n.schemaName || (n.meta && n.meta.class_name) || n.type || 'UnknownNode'
|
||
|
|
|
||
|
|
const nodeTypeRaw = n.data?.meta?.node_type || n.meta?.node_type || n.node_type || 'normal'
|
||
|
|
const nodeType = nodeTypeRaw ? nodeTypeRaw.toString().toLowerCase() : 'normal'
|
||
|
|
|
||
|
|
return {
|
||
|
|
id: n.id,
|
||
|
|
node_type: nodeType,
|
||
|
|
class_name: className,
|
||
|
|
params: buildParams(n),
|
||
|
|
sub_workflow_nodes: n.sub_workflow_nodes || n.sub_workflow?.nodes || null,
|
||
|
|
sub_workflow_edges: n.sub_workflow_edges || n.sub_workflow?.edges || null
|
||
|
|
}
|
||
|
|
})
|
||
|
|
|
||
|
|
// 转换边
|
||
|
|
const edgesOut = edges.map((e: any) => ({
|
||
|
|
source: e.source,
|
||
|
|
source_port: e.source_port || e.sourceHandle?.replace(/^output-/, '') || 'output',
|
||
|
|
target: e.target,
|
||
|
|
target_port: e.target_port || e.targetHandle?.replace(/^input-/, '') || 'input',
|
||
|
|
dimension_mode: e.dimension_mode || 'none'
|
||
|
|
}))
|
||
|
|
|
||
|
|
const payload: any = {
|
||
|
|
nodes: nodesOut,
|
||
|
|
edges: edgesOut
|
||
|
|
}
|
||
|
|
|
||
|
|
if (settings) payload.settings = settings
|
||
|
|
if (globalContext) payload.global_context = globalContext
|
||
|
|
|
||
|
|
return payload
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* 将后端节点 meta 转换为前端期望的格式
|
||
|
|
*/
|
||
|
|
export function transformNodeMeta(meta: any) {
|
||
|
|
return {
|
||
|
|
...meta,
|
||
|
|
display_name: meta.display_name || meta.name || 'Unknown',
|
||
|
|
category: meta.category || 'Other',
|
||
|
|
param_schema: normalizeParamSchema(meta.param_schema),
|
||
|
|
inputs: normalizeInputs(meta.inputs),
|
||
|
|
outputs: normalizeOutputs(meta.outputs)
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* 标准化参数模式
|
||
|
|
*/
|
||
|
|
function normalizeParamSchema(raw: any): Array<any> {
|
||
|
|
if (Array.isArray(raw)) return raw
|
||
|
|
if (raw && typeof raw === 'object') {
|
||
|
|
return Object.entries(raw).map(([name, spec]: [string, any]) => ({
|
||
|
|
name,
|
||
|
|
...(spec || {})
|
||
|
|
}))
|
||
|
|
}
|
||
|
|
return []
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* 标准化输入端口
|
||
|
|
*/
|
||
|
|
function normalizeInputs(raw: any): Array<any> {
|
||
|
|
if (Array.isArray(raw)) return raw
|
||
|
|
if (raw && typeof raw === 'object') {
|
||
|
|
return Object.entries(raw).map(([name, spec]: [string, any]) => ({
|
||
|
|
name,
|
||
|
|
...(spec || {})
|
||
|
|
}))
|
||
|
|
}
|
||
|
|
return []
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* 标准化输出端口
|
||
|
|
*/
|
||
|
|
function normalizeOutputs(raw: any): Array<any> {
|
||
|
|
if (Array.isArray(raw)) return raw
|
||
|
|
if (raw && typeof raw === 'object') {
|
||
|
|
return Object.entries(raw).map(([name, spec]: [string, any]) => ({
|
||
|
|
name,
|
||
|
|
...(spec || {})
|
||
|
|
}))
|
||
|
|
}
|
||
|
|
return []
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* 获取类型信息
|
||
|
|
*/
|
||
|
|
export function getTypeInfo(type: string = 'any') {
|
||
|
|
const typeMap: Record<string, { dim: number; color: string }> = {
|
||
|
|
'any': { dim: 1, color: '#8b5cf6' },
|
||
|
|
'number': { dim: 1, color: '#3b82f6' },
|
||
|
|
'string': { dim: 1, color: '#10b981' },
|
||
|
|
'array': { dim: 2, color: '#f59e0b' },
|
||
|
|
'dataframe': { dim: 2, color: '#f59e0b' },
|
||
|
|
'image': { dim: 2, color: '#ec4899' },
|
||
|
|
'tensor': { dim: 3, color: '#ef4444' },
|
||
|
|
}
|
||
|
|
return typeMap[type] || { dim: 1, color: '#6b7280' }
|
||
|
|
}
|