feat: 工作流适配数据中枢查询节点

- 新增查询数据与写入数据节点并移除旧数据中心节点入口

- 将查询数据节点切换为连接服务加 SQL 的执行模型

- 同步更新工作流校验、提示词上下文与设计器交互
This commit is contained in:
2026-04-02 18:56:34 +08:00
parent 798effbd5b
commit 1ecc28e498
40 changed files with 1973 additions and 692 deletions

View File

@@ -20,7 +20,6 @@ import WorkflowForm from '#/views/ai/workflow/components/WorkflowForm.vue';
import WorkflowSteps from '#/views/ai/workflow/components/WorkflowSteps.vue';
import {getCustomNode} from './customNode/index';
import nodeNames from './customNode/nodeNames';
import '@tinyflow-ai/vue/dist/index.css';
@@ -59,17 +58,35 @@ const codeEngineList = ref<any[]>([
available: true,
},
]);
function escapeHtmlAttr(value?: string) {
return String(value || '')
.replaceAll('&', '&amp;')
.replaceAll('"', '&quot;')
.replaceAll('<', '&lt;')
.replaceAll('>', '&gt;');
}
function buildModelIconMarkup(icon?: string, providerType?: string) {
const normalized = String(icon || '').trim();
if (normalized) {
if (normalized.startsWith('<svg') || normalized.startsWith('<img')) {
return normalized;
}
return `<img src="${escapeHtmlAttr(normalized)}" alt="" style="width:100%; height:100%; object-fit:contain;" />`;
}
if (!providerType) {
return undefined;
}
return getIconByValue(providerType) || undefined;
}
const provider = computed(() => ({
llm: () => llmList.value.map((item: any) => {
let iconStr = undefined;
if (item.modelProvider?.icon) {
iconStr = `<img src="${item.modelProvider.icon}" style="width:100%; height:100%; object-fit:contain;" />`;
} else if (item.modelProvider?.providerType) {
const svgStr = getIconByValue(item.modelProvider.providerType);
if (svgStr) {
iconStr = svgStr;
}
}
const iconStr = buildModelIconMarkup(
item.modelProvider?.icon,
item.modelProvider?.providerType,
);
// Extract brand and model name directly from the title if it contains '/'
let displayTitle = item.title || '';
@@ -330,15 +347,15 @@ async function runCheck(stage: WorkflowCheckStage, silentPass: boolean = false)
stage,
});
checkResult.value = res.data;
const issues = Array.isArray(res.data?.issues) ? res.data.issues : [];
checkIssuesVisible.value = issues.length > 0;
if (!res.data?.passed) {
checkIssuesVisible.value = true;
ElMessage.error($t('aiWorkflow.checkFailed'));
return false;
}
checkIssuesVisible.value = false;
focusedIssueKey.value = '';
issueFocusActive.value = false;
if (!silentPass) {
if (!silentPass && issues.length === 0) {
ElMessage.success($t('aiWorkflow.checkPassed'));
}
return true;