Browse Source

feat: 联调节点,整理节点

jiaxing.liao 2 weeks ago
parent
commit
8f26b941c4

+ 9 - 0
.vscode/settings.json

@@ -17,4 +17,13 @@
   "i18n-ally.sourceLanguage": "zh",
   "i18n-ally.keystyle": "nested",
   "i18n-ally.enabledFrameworks": ["vue"],
+  "[vue]": {
+    "editor.defaultFormatter": "esbenp.prettier-vscode"
+  },
+  "[typescriptreact]": {
+    "editor.defaultFormatter": "esbenp.prettier-vscode"
+  },
+  "[javascriptreact]": {
+    "editor.defaultFormatter": "esbenp.prettier-vscode"
+  }
 }

+ 14 - 2
apps/web/src/components/Sidebar/index.vue

@@ -51,7 +51,7 @@
 						<span>{{ isDarkMode ? '浅色模式' : '暗黑模式' }}</span>
 					</template>
 					<span style="cursor: pointer" @click="toggleTheme">
-					<SvgIcon :name="isDarkMode ? 'sun' : 'moon'" />
+						<SvgIcon :name="isDarkMode ? 'sun' : 'moon'" />
 					</span>
 				</el-tooltip>
 			</div>
@@ -159,6 +159,12 @@
 		@close="showTemplateModal = false"
 		@select="handleTemplateSelect"
 	/>
+	<!-- 新建工作流弹窗 -->
+	<CreateWorkflowModal
+		:visible="createModalVisible"
+		@close="createModalVisible = false"
+		@success="handleCreateSuccess"
+	/>
 </template>
 
 <script setup lang="ts">
@@ -168,6 +174,7 @@ import { ElMessage } from 'element-plus'
 import SearchDialog from '../SearchDialog/index.vue'
 import { v4 } from 'uuid'
 import TemplateModal from '../TemplateModal/index.vue'
+import CreateWorkflowModal from '@/features/createModal/index.vue'
 import { applyTheme } from '@/theme'
 
 import logo from '@/assets/logo.svg'
@@ -181,6 +188,7 @@ const collapsed = ref(false)
 const showSearchDialog = ref(false)
 const showTemplateModal = ref(false)
 const isDarkMode = ref(false)
+const createModalVisible = ref(false)
 
 // 计算当前活跃的菜单项
 const activeMenu = computed(() => router.currentRoute.value.path)
@@ -190,7 +198,11 @@ const toggle = () => {
 }
 
 const createWorkflow = () => {
-	const id = v4()
+	createModalVisible.value = true
+}
+
+const handleCreateSuccess = (id: string) => {
+	createModalVisible.value = false
 	router.push(`/workflow/${id}`)
 }
 

+ 260 - 0
apps/web/src/features/createModal/index.vue

@@ -0,0 +1,260 @@
+<template>
+	<el-dialog
+		v-model="dialogVisible"
+		title="新建工作流"
+		width="600px"
+		:close-on-click-modal="false"
+		@close="handleClose"
+	>
+		<el-form ref="formRef" :model="form" :rules="rules" label-width="90px" class="create-form">
+			<el-form-item label="名称" prop="name">
+				<el-input
+					v-model="form.name"
+					placeholder="请输入工作流名称"
+					maxlength="50"
+					show-word-limit
+				/>
+			</el-form-item>
+
+			<el-form-item label="标签" prop="tags">
+				<div class="w-full">
+					<div class="flex gap-2 items-center">
+						<el-input-tag
+							v-model="form.tags"
+							placeholder="按回车键添加标签"
+							aria-label="按回车键添加标签"
+							:max-tags="5"
+						/>
+						<span class="tag-tip shrink-0">最多 5 个</span>
+					</div>
+				</div>
+			</el-form-item>
+
+			<el-form-item label="描述" prop="description">
+				<el-input
+					v-model="form.description"
+					type="textarea"
+					placeholder="可选,简要说明该工作流的用途"
+					:rows="3"
+					maxlength="200"
+					show-word-limit
+				/>
+			</el-form-item>
+
+			<el-form-item label="备注" prop="remark">
+				<el-input
+					v-model="form.remark"
+					type="textarea"
+					placeholder="可选,补充说明"
+					:rows="2"
+					maxlength="200"
+					show-word-limit
+				/>
+			</el-form-item>
+
+			<el-form-item label="封面" prop="profilePhoto">
+				<el-input v-model="form.profilePhoto" placeholder="可选,封面图片地址" />
+			</el-form-item>
+
+			<!-- <el-form-item label="视图位置" prop="viewPort">
+				<div class="viewport-inputs">
+					<el-input-number
+						v-model="form.viewPort.x"
+						:min="-10000"
+						:max="10000"
+						:step="10"
+						size="small"
+					/>
+					<span class="sep">x</span>
+					<el-input-number
+						v-model="form.viewPort.y"
+						:min="-10000"
+						:max="10000"
+						:step="10"
+						size="small"
+					/>
+					<span class="sep">缩放</span>
+					<el-input-number
+						v-model="form.viewPort.zoom"
+						:min="0.1"
+						:max="5"
+						:step="0.1"
+						size="small"
+					/>
+				</div>
+			</el-form-item> -->
+		</el-form>
+
+		<template #footer>
+			<div class="dialog-footer mt-2">
+				<el-button @click="handleCancel">取 消</el-button>
+				<el-button type="primary" :loading="loading" @click="handleSubmit">确 定</el-button>
+			</div>
+		</template>
+	</el-dialog>
+</template>
+
+<script setup lang="ts">
+import { ref, watch } from 'vue'
+import { ElMessage, type FormInstance, type FormRules } from 'element-plus'
+import { agent } from '@repo/api-service'
+
+interface ViewPort {
+	x: number
+	y: number
+	zoom: number
+}
+
+interface CreateWorkflowForm {
+	name: string
+	tags: string[]
+	description: string
+	remark: string
+	profilePhoto: string
+	viewPort: ViewPort
+}
+
+const props = defineProps({
+	visible: {
+		type: Boolean,
+		default: false
+	}
+})
+
+const emit = defineEmits<{
+	close: []
+	success: [id: string]
+}>()
+
+const dialogVisible = ref(false)
+const loading = ref(false)
+const formRef = ref<FormInstance>()
+
+const createDefaultForm = (): CreateWorkflowForm => ({
+	name: '',
+	tags: [],
+	description: '',
+	remark: '',
+	profilePhoto: '',
+	viewPort: {
+		x: 0,
+		y: 0,
+		zoom: 1
+	}
+})
+
+const form = ref<CreateWorkflowForm>(createDefaultForm())
+
+const rules: FormRules<CreateWorkflowForm> = {
+	name: [
+		{
+			required: true,
+			message: '请输入名称',
+			trigger: 'blur'
+		}
+	]
+}
+
+watch(
+	() => props.visible,
+	(val) => {
+		dialogVisible.value = val
+		if (val) {
+			// 打开时重置校验
+			formRef.value?.clearValidate()
+		}
+	},
+	{ immediate: true }
+)
+
+const resetForm = () => {
+	form.value = createDefaultForm()
+	formRef.value?.clearValidate()
+}
+
+const handleClose = () => {
+	resetForm()
+	emit('close')
+}
+
+const handleCancel = () => {
+	dialogVisible.value = false
+	handleClose()
+}
+
+const handleSubmit = async () => {
+	if (!formRef.value) return
+
+	try {
+		loading.value = true
+		await formRef.value.validate()
+
+		const payload = {
+			name: form.value.name,
+			tags: form.value.tags || [],
+			description: form.value.description || '',
+			remark: form.value.remark || '',
+			profilePhoto: form.value.profilePhoto || '',
+			viewPort: form.value.viewPort
+		}
+
+		const response = await agent.postAgentDoEditAgent({
+			data: payload
+		})
+
+		if (response?.isSuccess) {
+			const id = (response as any).result as string
+			ElMessage.success('创建工作流成功')
+			dialogVisible.value = false
+			resetForm()
+			if (id) {
+				emit('success', id)
+			} else {
+				emit('close')
+			}
+		} else {
+			ElMessage.error('创建工作流失败')
+		}
+	} catch (error) {
+		console.error('create workflow error', error)
+		ElMessage.error('创建工作流失败')
+	} finally {
+		loading.value = false
+	}
+}
+
+defineExpose({
+	open: () => {
+		dialogVisible.value = true
+	},
+	close: handleClose
+})
+</script>
+
+<style scoped lang="less">
+.create-form {
+	padding-top: 8px;
+}
+
+.viewport-inputs {
+	display: flex;
+	align-items: center;
+	gap: 8px;
+}
+
+.viewport-inputs .sep {
+	color: var(--text-secondary);
+	font-size: 12px;
+}
+
+.tag-tip {
+	color: var(--text-secondary);
+	font-size: 12px;
+}
+
+.dialog-footer {
+	display: flex;
+	justify-content: flex-end;
+	gap: 8px;
+}
+</style>

+ 113 - 34
apps/web/src/features/nodeLibary/index.vue

@@ -1,59 +1,133 @@
 <script lang="ts" setup>
-import { computed, inject } from 'vue'
-import { materialTools, type SourceType } from '@repo/nodes'
+import { computed, ref, watch } from 'vue'
+import { nodes, type INodeType } from '@repo/nodes'
 import { Icon } from '@repo/ui'
 
 defineOptions({
 	name: 'AddMaterialsPop'
 })
 
+interface NodeItem {
+	id: string
+	type: string
+	name: string
+	description?: string
+	icon?: string
+	iconColor?: string
+	raw: INodeType
+}
+
+interface NodeGroup {
+	id: string
+	label: string
+	nodes: NodeItem[]
+}
+
 const emit = defineEmits<{
-	'add-node': [value: SourceType]
+	'add-node': [value: { type: string }]
 }>()
 
-const vueflow = inject('vueflow')
-// const hasStart = computed(() => vueflow?.nodes?.some((node) => node.data.type === 'start'))
-// const hasEnd = computed(() => vueflow?.nodes?.some((node) => node.data.type === 'end'))
-
-const materials = computed(() => {
-	return materialTools.map((group) => {
-		return {
-			...group,
-			source: group.source.filter((item) => {
-				return item
-				// !(item.type === 'start' && !hasStart.value) && !(item.type === 'end' && !hasEnd.value)
+const normalizeType = (name: string) => {
+	if (name === 'http-request') return 'http'
+	if (name === 'if-else') return 'condition'
+	return name
+}
+
+const materials = computed<NodeGroup[]>(() => {
+	const groupMap = new Map<string, NodeGroup>()
+
+	nodes.forEach((node) => {
+		const groupName = node.group || '其他'
+
+		if (!groupMap.has(groupName)) {
+			groupMap.set(groupName, {
+				id: groupName,
+				label: groupName,
+				nodes: []
 			})
 		}
+
+		const group = groupMap.get(groupName)!
+
+		group.nodes.push({
+			id: node.name,
+			type: normalizeType(node.name),
+			name: node.displayName,
+			description: node.description,
+			icon: node.icon,
+			iconColor: node.iconColor,
+			raw: node
+		})
 	})
+
+	// 保持分组顺序稳定
+	return Array.from(groupMap.values())
 })
 
-const onAddNode = (value: SourceType) => {
-	emit('add-node', value)
+const activeGroup = ref('')
+
+watch(
+	materials,
+	(groups) => {
+		if (!groups || groups.length === 0) return
+		if (!activeGroup.value || !groups.some((g) => g.id === activeGroup.value)) {
+			activeGroup.value = groups[0]!.id
+		}
+	},
+	{ immediate: true }
+)
+
+const onAddNode = (value: NodeItem) => {
+	emit('add-node', { type: value.type })
 }
 </script>
+
 <template>
-	<div v-for="item in materials" :key="item.id">
-		<p class="mb-2 mt-1 text-[#676f83]">{{ item.label }}</p>
-		<ul>
-			<li
-				class="tool mb-3 flex items-center"
-				v-for="value in item.source"
-				:key="value.id"
-				@click="onAddNode(value)"
+	<div class="node-lib">
+		<el-tabs v-model="activeGroup" class="node-tabs" stretch>
+			<el-tab-pane
+				v-for="group in materials"
+				:key="group.id"
+				:label="group.label"
+				:name="group.id"
 			>
-				<Icon
-					:icon="value.icon"
-					height="16"
-					width="16"
-					class="mr-2 bg-[#6172f3] p-1 rounded"
-					color="#fff"
-				/>
-				<span>{{ value.name }}</span>
-			</li>
-		</ul>
+				<ul>
+					<li
+						class="tool mb-3 flex items-center"
+						v-for="item in group.nodes"
+						:key="item.id"
+						@click="onAddNode(item)"
+					>
+						<Icon
+							:icon="item.icon || 'lucide:square'"
+							height="16"
+							width="16"
+							class="mr-2 p-1 rounded"
+							:style="{ backgroundColor: item.iconColor || '#6172f3' }"
+							color="#fff"
+						/>
+						<div class="flex flex-col">
+							<span>{{ item.name }}</span>
+							<span v-if="item.description" class="desc">{{ item.description }}</span>
+						</div>
+					</li>
+				</ul>
+			</el-tab-pane>
+		</el-tabs>
 	</div>
 </template>
+
 <style lang="less" scoped>
+.node-lib {
+	min-width: 220px;
+}
+
+.node-tabs {
+	:deep(.el-tabs__header) {
+		margin-bottom: 8px;
+	}
+}
+
 ul {
 	padding: 0 0 0 10px;
 
@@ -65,6 +139,11 @@ ul {
 			color: #354052;
 		}
 
+		.desc {
+			font-size: 12px;
+			color: #9ca3af;
+		}
+
 		&:hover {
 			span {
 				color: #6172f3;

+ 1 - 1
apps/web/src/features/toolbar/index.vue

@@ -1,7 +1,7 @@
 <template>
 	<div class="absolute top-0 right-0 p-16px flex flex-col gap-12px">
 		<el-tooltip content="节点" placement="left">
-			<el-popover placement="left-start">
+			<el-popover placement="left-start" width="400px">
 				<NodeLibary @add-node="(val) => $emit('create:node', val)" />
 				<template #reference>
 					<IconButton type="primary" class="ml-12px" icon="lucide:package-plus" square />

+ 23 - 13
apps/web/src/views/Dashboard.vue

@@ -142,12 +142,7 @@
 				<el-button text @click="templateModalVisible = true">查看更多 →</el-button>
 			</div>
 			<div class="templates-grid">
-				<div
-					class="template-card"
-					v-for="item in templates"
-					:key="item.id"
-					@click="goToTemplate(item.id)"
-				>
+				<div class="template-card" v-for="item in templates" :key="item.id" @click="goToTemplate(item.id)">
 					<div class="template-icon">
 						<SvgIcon :name="item.icon" size="32" />
 					</div>
@@ -164,6 +159,13 @@
 			@close="templateModalVisible = false"
 			@select="handleTemplateSelect"
 		/>
+
+		<!-- 新建工作流弹窗 -->
+		<CreateWorkflowModal
+			:visible="createModalVisible"
+			@close="createModalVisible = false"
+			@success="handleCreateSuccess"
+		/>
 	</div>
 </template>
 
@@ -174,7 +176,7 @@ import { Delete } from '@element-plus/icons-vue'
 import { ElMessage, ElMessageBox } from 'element-plus'
 import SvgIcon from '@/components/SvgIcon/index.vue'
 import TemplateModal from '@/components/TemplateModal/index.vue'
-import { v4 } from 'uuid'
+import CreateWorkflowModal from '@/features/createModal/index.vue'
 
 const $router = useRouter()
 
@@ -187,10 +189,12 @@ const currentDate = computed(() => {
 // 模板弹窗
 const templateModalVisible = ref(false)
 
+// 新建工作流弹窗
+const createModalVisible = ref(false)
+
 // 创建工作流
 const createWorkflow = () => {
-	const id = v4()
-	$router.push(`/workflow/${id}`)
+	createModalVisible.value = true
 }
 
 // 前往编辑器
@@ -215,6 +219,12 @@ const handleTemplateSelect = (templateId: string) => {
 	templateModalVisible.value = false
 }
 
+// 新建工作流成功回调
+const handleCreateSuccess = (id: string) => {
+	createModalVisible.value = false
+	$router.push(`/workflow/${id}`)
+}
+
 // 删除工作流
 const deleteWorkflow = async (id: string) => {
 	try {
@@ -328,11 +338,11 @@ const templates = ref([
 
 <style lang="less" scoped>
 .dashboard-container {
-	height: calc(100vh - 48px);
+	//height: calc(100vh - 48px);
 	padding: 24px;
-	display: flex;
-	flex-direction: column;
-	overflow: hidden;
+	//display: flex;
+	//flex-direction: column;
+	//overflow: hidden;
 }
 
 .welcome-banner {

+ 45 - 76
apps/web/src/views/Editor.vue

@@ -94,8 +94,8 @@ import EditorFooter from '@/features/editorFooter/index.vue'
 import Toolbar from '@/features/toolbar/index.vue'
 
 import { IconButton, Input } from '@repo/ui'
+import { nodeMap } from '@repo/nodes'
 
-import type { SourceType } from '@repo/nodes'
 import { dayjs, ElMessage, ElMessageBox } from 'element-plus'
 
 const layout = inject<{ setMainStyle: (style: CSSProperties) => void }>('layout')
@@ -132,37 +132,9 @@ const saveVarsTimer = ref<number | undefined>(undefined)
 const isHydrating = ref(false)
 const notifyTimestamps = new Map<string, number>()
 
-const nodeTypeMap: Record<string, string> = {
-	'http-request': 'http-request',
-	'if-else': 'condition',
-	condition: 'condition',
-	code: 'code',
-	database: 'database',
-	start: 'start',
-	end: 'end'
-}
-
-const nodeSchemaMap: Record<string, any> = {
-	start: startNode.schema,
-	end: endNode.schema,
-	'http-request': httpNode.schema,
-	condition: conditionNode.schema,
-	code: codeNode.schema,
-	database: databaseNode.schema
-}
-
 const normalizeNodeType = (node: any) => {
 	const sourceNodeType = node?.nodeType || node?.data?.nodeType || node?.data?.type || node?.type
-	return nodeTypeMap[sourceNodeType] || sourceNodeType || 'code'
-}
-
-type AgentNodeType = 'custom' | 'start' | 'end' | 'condition' | 'task' | 'http-request'
-
-const toApiNodeType = (nodeType?: string): AgentNodeType => {
-	if (!nodeType) return 'custom'
-	return ['start', 'end', 'condition', 'http-request'].includes(nodeType)
-		? (nodeType as AgentNodeType)
-		: 'custom'
+	return sourceNodeType || 'code'
 }
 
 const toApiNodeData = (nodeData: any) => {
@@ -243,7 +215,7 @@ const buildUpdateNodePayload = (node: any) => {
 		width: node.width ?? node.data?.width ?? 96,
 		height: node.height ?? node.data?.height ?? 96,
 		selected: !!node.selected,
-		nodeType: toApiNodeType(node.data?.nodeType || node.nodeType),
+		nodeType: node.data?.nodeType || node.nodeType,
 		zIndex: node.zIndex ?? 1,
 		data: toApiNodeData(node.data)
 	}
@@ -251,10 +223,10 @@ const buildUpdateNodePayload = (node: any) => {
 
 const toWorkflowNode = (node: any) => {
 	const normalizedNodeType = normalizeNodeType(node)
-	const schema = nodeSchemaMap[normalizedNodeType] || codeNode.schema
-	const position = node?.position || schema.position || { x: 20, y: 30 }
-	const width = node?.width ?? schema.width ?? 96
-	const height = node?.height ?? schema.height ?? 96
+	const schema = nodeMap[normalizedNodeType]?.schema
+	const position = node?.position || schema?.position || { x: 20, y: 30 }
+	const width = node?.width ?? schema?.width ?? 96
+	const height = node?.height ?? schema?.height ?? 96
 
 	return {
 		...schema,
@@ -264,10 +236,10 @@ const toWorkflowNode = (node: any) => {
 		position,
 		width,
 		height,
-		zIndex: node?.zIndex ?? schema.zIndex ?? 1,
+		zIndex: node?.zIndex ?? schema?.zIndex ?? 1,
 		selected: !!node?.selected,
 		data: {
-			...(schema.data || {}),
+			...(schema?.data || {}),
 			...(node?.data || {}),
 			id: node.id,
 			position,
@@ -342,7 +314,7 @@ const loadAgentWorkflow = async (agentId: string) => {
 	isHydrating.value = true
 
 	try {
-		const response = await agent.postGetAgentInfo({ id: agentId })
+		const response = await agent.postAgentGetAgentInfo({ id: agentId })
 		const result = response?.result
 		if (!response?.isSuccess || !result) {
 			throw new Error('获取智能体信息失败')
@@ -376,7 +348,7 @@ const saveAgentMeta = async () => {
 	if (!workflow.value?.id) return
 
 	try {
-		const response = await agent.postDoEditAgent({
+		const response = await agent.postAgentDoEditAgent({
 			data: {
 				id: workflow.value.id,
 				name: workflow.value.name,
@@ -398,7 +370,7 @@ const saveAgentVariables = async () => {
 	if (!workflow.value?.id) return
 
 	try {
-		const response = await agent.postDoSaveAgentVariables({
+		const response = await agent.postAgentDoSaveAgentVariables({
 			appAgentId: workflow.value.id,
 			conversation_variables: workflow.value.conversation_variables || [],
 			env_variables: (workflow.value.env_variables || []).map((item: any) => ({
@@ -464,9 +436,9 @@ watch(
  */
 watch(
 	() => route.params?.id,
-	async (newId) => {
-		if (newId) {
-			await loadAgentWorkflow('b3a4aabb-a6b8-47f3-8a32-f45930f7d7b8' as string)
+	async (id) => {
+		if (id) {
+			await loadAgentWorkflow(id as string)
 		}
 	},
 	{ immediate: true }
@@ -508,9 +480,9 @@ const handleRunSelectedNode = async () => {
 	}
 
 	try {
-		const response = await agent.postDoTestNodeRunner({
+		const response = await agent.postAgentDoTestNodeRunner({
 			appAgentId: workflow.value.id,
-			nodeIds: [nodeID.value]
+			id: nodeID.value
 		})
 		runVisible.value = false
 		handleApiResult(response, '已提交节点测试', '节点测试失败')
@@ -527,9 +499,9 @@ const handleRunNode = async (id: string) => {
 	}
 
 	try {
-		const response = await agent.postDoTestNodeRunner({
+		const response = await agent.postAgentDoTestNodeRunner({
 			appAgentId: workflow.value.id,
-			nodeIds: [id]
+			id
 		})
 		handleApiResult(response, '已提交节点测试', '节点测试失败')
 	} catch (error) {
@@ -537,7 +509,7 @@ const handleRunNode = async (id: string) => {
 		ElMessage.error('节点测试失败')
 	}
 }
-const handleNodeCreate = (value: SourceType | string) => {
+const handleNodeCreate = (value: { type: string } | string) => {
 	const id = uuid()
 	if (typeof value === 'string') {
 		if (value === 'stickyNote') {
@@ -563,14 +535,6 @@ const handleNodeCreate = (value: SourceType | string) => {
 		return
 	}
 
-	const nodeMap: Record<string, any> = {
-		start: startNode,
-		end: endNode,
-		http: httpNode,
-		condition: conditionNode,
-		code: codeNode,
-		database: databaseNode
-	}
 	const nodeToAdd = nodeMap[value.type]?.schema
 
 	// 如果存在对应节点则添加
@@ -585,16 +549,15 @@ const handleNodeCreate = (value: SourceType | string) => {
 			__pendingCreate: true,
 			id
 		}
-		workflow.value?.nodes.push(newNode)
 
 		agent
-			.postDoNewAgentNode({
+			.postAgentDoNewAgentNode({
 				appAgentId: workflow.value.id,
 				position: newNode.position,
 				width: newNode.width,
 				height: newNode.height,
 				selected: !!newNode.selected,
-				nodeType: toApiNodeType(newNode.data?.nodeType),
+				nodeType: newNode.data?.nodeType,
 				zIndex: newNode.zIndex ?? 1,
 				parentId: newNode.parentId || ''
 			})
@@ -646,17 +609,21 @@ const handleDelete = () => {
 /**
  * 创建连线
  */
-const onCreateConnection = (connection: Connection) => {
-	const { source, target } = connection
+const onCreateConnection = async (connection: Connection) => {
+	const { source, target, sourceHandle } = connection
 
 	if (!workflow.value?.edges.some((edge) => edge.source === source && edge.target === target)) {
-		workflow.value?.edges.push({
-			id: `edge-${source}-${target}`,
+		const response = await agent.postAgentDoNewEdge({
+			appAgentId: workflow.value.id,
 			source,
 			target,
-			type: 'canvas-edge',
-			data: {}
+			sourceHandle: sourceHandle!,
+			zIndex: 1
 		})
+
+		if (handleApiResult(response, '节点已添加', '新增节点失败')) {
+			await loadAgentWorkflow(workflow.value.id)
+		}
 	}
 }
 
@@ -672,7 +639,7 @@ const handleUpdateNodesPosition = (events: { id: string; position: XYPosition }[
 			}
 			node.position = position
 			agent
-				.postDoUpdateAgentNode(buildUpdateNodePayload(node))
+				.postAgentDoUpdateAgentNode(buildUpdateNodePayload(node))
 				.then((response) => {
 					handleApiResult(response, undefined, '更新节点失败')
 				})
@@ -702,7 +669,7 @@ const hangleUpdateNodeData = (id: string, data: any) => {
 		}
 		node.data = nextData
 		agent
-			.postDoUpdateAgentNode(buildUpdateNodePayload(node))
+			.postAgentDoUpdateAgentNode(buildUpdateNodePayload(node))
 			.then((response) => {
 				handleApiResult(response, undefined, '更新节点失败')
 			})
@@ -735,24 +702,26 @@ const handleUpdateNodeProps = (id: string, attrs: Record<string, unknown>) => {
 /**
  * 删除节点
  */
-const handleDeleteNode = (id: string) => {
+const handleDeleteNode = async (id: string) => {
 	console.log('del node', id)
 	const index = workflow.value.nodes.findIndex((node) => node.id === id)
 	if (index != -1) {
-		workflow.value.nodes.splice(index, 1)
+		await agent.postAgentDoDeleteAgentNode({
+			id: id
+		})
+		await loadAgentWorkflow(workflow.value.id)
 	}
 }
 
 /**
  * 删除连线
  */
-const handleDeleteEdge = (connection: Connection) => {
-	console.log('del edge', connection)
-	const index = workflow.value.edges.findIndex(
-		(edge) => edge.id === `edge-${connection.source}-${connection.target}`
-	)
-	if (index != -1) {
-		workflow.value.edges.splice(index, 1)
+const handleDeleteEdge = async (connection: Connection) => {
+	if (connection.id) {
+		// await agent.postAgentDoDeleteEdge({
+		// 	id: connection.id
+		// })
+		await loadAgentWorkflow(workflow.value.id)
 	}
 }
 

+ 1 - 3
packages/api-client/package.json

@@ -5,9 +5,7 @@
   "exports": {
     ".": "./index.ts"
   },
-  "scripts": {
-    "genapi": "openapi2ts"
-  },
+  "scripts": {},
   "devDependencies": {
     "@repo/typescript-config": "workspace:*",
     "@types/node": "^25.1.0",

+ 1 - 1
packages/api-client/request.ts

@@ -90,7 +90,7 @@ class HttpClient {
 	): void {
 		const defaultInterceptor = (config: InternalAxiosRequestConfig): InternalAxiosRequestConfig => {
 			const search = getParams(window.location.href)
-			const enterpriseCode = search?.['enterpriseCode']
+			const enterpriseCode = search?.['enterpriseCode'] || 'a'
 			const token =
 				localStorage.getItem('token_' + enterpriseCode) ||
 				document.cookie.match(new RegExp('(^| )' + 'x-sessionId_b' + '=([^;]*)(;|$)'))?.[2]

+ 305 - 0
packages/api-service/agent.openapi.json

@@ -15,6 +15,10 @@
 		{
 			"name": "Agent",
 			"description": "智能体相关接口"
+		},
+		{
+			"name": "tools",
+			"description": "工具相关接口"
 		}
 	],
 	"paths": {
@@ -707,6 +711,307 @@
 					}
 				}
 			}
+		},
+		"/api/agent/doDeleteAgentNode": {
+			"post": {
+				"summary": "删除智能体节点",
+				"deprecated": false,
+				"description": "",
+				"tags": ["Agent"],
+				"parameters": [
+					{
+						"name": "Authorization",
+						"in": "header",
+						"description": "",
+						"example": "bpm_client_1485689273129635840",
+						"schema": {
+							"type": "string",
+							"default": "bpm_client_1485689273129635840"
+						}
+					}
+				],
+				"requestBody": {
+					"content": {
+						"application/json": {
+							"schema": {
+								"type": "object",
+								"properties": {
+									"id": {
+										"type": "string"
+									}
+								},
+								"required": ["id"]
+							},
+							"example": {
+								"id": "492048da-6f33-4a36-adc5-cff4b973b053"
+							}
+						}
+					},
+					"required": true
+				},
+				"responses": {
+					"200": {
+						"description": "",
+						"content": {
+							"application/json": {
+								"schema": {
+									"type": "object",
+									"properties": {
+										"isSuccess": {
+											"type": "boolean"
+										},
+										"code": {
+											"type": "integer"
+										},
+										"isAuthorized": {
+											"type": "boolean"
+										}
+									},
+									"required": ["isSuccess", "code", "isAuthorized"]
+								},
+								"example": {
+									"isSuccess": true,
+									"code": 1,
+									"isAuthorized": true
+								}
+							}
+						},
+						"headers": {}
+					}
+				},
+				"security": []
+			}
+		},
+		"/api/agent/doNewEdge": {
+			"post": {
+				"summary": "新增智能体边缘信息",
+				"deprecated": false,
+				"description": "",
+				"tags": ["Agent"],
+				"parameters": [
+					{
+						"name": "Authorization",
+						"in": "header",
+						"description": "",
+						"example": "bpm_client_1485689273129635840",
+						"schema": {
+							"type": "string",
+							"default": "bpm_client_1485689273129635840"
+						}
+					}
+				],
+				"requestBody": {
+					"content": {
+						"application/json": {
+							"schema": {
+								"type": "object",
+								"properties": {
+									"appAgentId": {
+										"type": "string"
+									},
+									"source": {
+										"type": "string"
+									},
+									"sourceHandle": {
+										"type": "string"
+									},
+									"target": {
+										"type": "string"
+									},
+									"zIndex": {
+										"type": "integer"
+									}
+								},
+								"required": ["appAgentId", "source", "target", "zIndex"]
+							},
+							"examples": {
+								"1": {
+									"value": {
+										"appAgentId": "b3a4aabb-a6b8-47f3-8a32-f45930f7d7b8",
+										"source": "492048da-6f33-4a36-adc5-cff4b973b053",
+										"target": "c91eb026-e86a-418f-a865-a98aa60bcf19",
+										"zIndex": 0
+									},
+									"summary": "普通节点"
+								},
+								"2": {
+									"value": {
+										"appAgentId": "b3a4aabb-a6b8-47f3-8a32-f45930f7d7b8",
+										"source": "de35b389-3782-474c-9dd9-cb1356dea54b",
+										"sourceHandle": "c2cb978e-fce8-11f0-a59d-266b7b87fca6-1",
+										"target": "6764ede2-2c3d-46bb-ae9a-fb0a892aff84",
+										"zIndex": 0
+									},
+									"summary": "条件分支节点边缘"
+								}
+							}
+						}
+					},
+					"required": true
+				},
+				"responses": {
+					"200": {
+						"description": "",
+						"content": {
+							"application/json": {
+								"schema": {
+									"type": "object",
+									"properties": {
+										"isSuccess": {
+											"type": "boolean"
+										},
+										"code": {
+											"type": "integer"
+										},
+										"isAuthorized": {
+											"type": "boolean"
+										}
+									},
+									"required": ["isSuccess", "code", "isAuthorized"]
+								},
+								"example": {
+									"isSuccess": true,
+									"code": 1,
+									"isAuthorized": true
+								}
+							}
+						},
+						"headers": {}
+					}
+				},
+				"security": []
+			}
+		},
+		"/api/agent/doSelectedEdge": {
+			"post": {
+				"summary": "选中智能体边缘",
+				"deprecated": false,
+				"description": "",
+				"tags": ["Agent"],
+				"parameters": [
+					{
+						"name": "Authorization",
+						"in": "header",
+						"description": "",
+						"example": "bpm_client_1485689273129635840",
+						"schema": {
+							"type": "string",
+							"default": "bpm_client_1485689273129635840"
+						}
+					}
+				],
+				"requestBody": {
+					"content": {
+						"application/json": {
+							"schema": {
+								"type": "object",
+								"properties": {
+									"id": {
+										"type": "string"
+									}
+								},
+								"required": ["id"]
+							},
+							"example": {
+								"id": "e518d540-2242-4def-8d42-381d9fab59ee"
+							}
+						}
+					},
+					"required": true
+				},
+				"responses": {
+					"200": {
+						"description": "",
+						"content": {
+							"application/json": {
+								"schema": {
+									"type": "object",
+									"properties": {
+										"isSuccess": {
+											"type": "boolean"
+										},
+										"code": {
+											"type": "integer"
+										},
+										"isAuthorized": {
+											"type": "boolean"
+										}
+									},
+									"required": ["isSuccess", "code", "isAuthorized"]
+								},
+								"example": {
+									"isSuccess": true,
+									"code": 1,
+									"isAuthorized": true
+								}
+							}
+						},
+						"headers": {}
+					}
+				},
+				"security": []
+			}
+		},
+		"/api/openapi/doBatchGenerateUUID": {
+			"post": {
+				"summary": "批量生成UUID",
+				"deprecated": false,
+				"description": "",
+				"tags": ["tools"],
+				"parameters": [
+					{
+						"name": "Authorization",
+						"in": "header",
+						"description": "",
+						"example": "bpm_client_1485689273129635840",
+						"schema": {
+							"type": "string",
+							"default": "bpm_client_1485689273129635840"
+						}
+					}
+				],
+				"responses": {
+					"200": {
+						"description": "",
+						"content": {
+							"application/json": {
+								"schema": {
+									"type": "object",
+									"properties": {
+										"isSuccess": {
+											"type": "boolean"
+										},
+										"code": {
+											"type": "integer"
+										},
+										"result": {
+											"type": "array",
+											"items": {
+												"type": "string"
+											}
+										},
+										"isAuthorized": {
+											"type": "boolean"
+										}
+									},
+									"required": ["isSuccess", "code", "result", "isAuthorized"]
+								},
+								"example": {
+									"isSuccess": true,
+									"code": 1,
+									"result": [
+										"a33c9f0a-bc3a-4a25-86ba-080ee6772063",
+										"efab3a9d-e5f6-4339-8d6c-6189b85d984f"
+									],
+									"isAuthorized": true
+								}
+							}
+						},
+						"headers": {}
+					}
+				},
+				"security": []
+			}
 		}
 	},
 	"components": {

+ 2 - 1
packages/api-service/index.ts

@@ -1,5 +1,6 @@
 import api from './servers/api'
 
 const agent = api.agent
+const tools = api.tools
 
-export { agent }
+export { agent, tools }

+ 71 - 7
packages/api-service/servers/api/agent.ts

@@ -2,8 +2,28 @@
 /* eslint-disable */
 import request from '@repo/api-client'
 
+/** 删除智能体节点 POST /api/agent/doDeleteAgentNode */
+export async function postAgentDoDeleteAgentNode(
+	body: {
+		id: string
+	},
+	options?: { [key: string]: any }
+) {
+	return request<{ isSuccess: boolean; code: number; isAuthorized: boolean }>(
+		'/api/agent/doDeleteAgentNode',
+		{
+			method: 'POST',
+			headers: {
+				'Content-Type': 'application/json'
+			},
+			data: body,
+			...(options || {})
+		}
+	)
+}
+
 /** 智能体编辑 POST /api/agent/doEditAgent */
-export async function postDoEditAgent(options?: { [key: string]: any }) {
+export async function postAgentDoEditAgent(options?: { [key: string]: any }) {
 	return request<{ isSuccess: boolean; code: number; result: string; isAuthorized: boolean }>(
 		'/api/agent/doEditAgent',
 		{
@@ -14,14 +34,14 @@ export async function postDoEditAgent(options?: { [key: string]: any }) {
 }
 
 /** 智能体添加节点 POST /api/agent/doNewAgentNode */
-export async function postDoNewAgentNode(
+export async function postAgentDoNewAgentNode(
 	body: {
 		appAgentId: string
 		position: { x: number; y: number }
 		width: number
 		height: number
 		selected: boolean
-		nodeType: 'custom' | 'start' | 'end' | 'condition' | 'task' | 'http-request'
+		nodeType: string
 		zIndex: number
 		parentId: string
 	},
@@ -40,8 +60,32 @@ export async function postDoNewAgentNode(
 	)
 }
 
+/** 新增智能体边缘信息 POST /api/agent/doNewEdge */
+export async function postAgentDoNewEdge(
+	body: {
+		appAgentId: string
+		source: string
+		sourceHandle?: string
+		target: string
+		zIndex: number
+	},
+	options?: { [key: string]: any }
+) {
+	return request<{ isSuccess: boolean; code: number; isAuthorized: boolean }>(
+		'/api/agent/doNewEdge',
+		{
+			method: 'POST',
+			headers: {
+				'Content-Type': 'application/json'
+			},
+			data: body,
+			...(options || {})
+		}
+	)
+}
+
 /** 保存智能体变量 POST /api/agent/doSaveAgentVariables */
-export async function postDoSaveAgentVariables(
+export async function postAgentDoSaveAgentVariables(
 	body: {
 		appAgentId: string
 		conversation_variables: string[]
@@ -66,8 +110,28 @@ export async function postDoSaveAgentVariables(
 	)
 }
 
+/** 选中智能体边缘 POST /api/agent/doSelectedEdge */
+export async function postAgentDoSelectedEdge(
+	body: {
+		id: string
+	},
+	options?: { [key: string]: any }
+) {
+	return request<{ isSuccess: boolean; code: number; isAuthorized: boolean }>(
+		'/api/agent/doSelectedEdge',
+		{
+			method: 'POST',
+			headers: {
+				'Content-Type': 'application/json'
+			},
+			data: body,
+			...(options || {})
+		}
+	)
+}
+
 /** 测试运行智能体节点 POST /api/agent/doTestNodeRunner */
-export async function postDoTestNodeRunner(
+export async function postAgentDoTestNodeRunner(
 	body: {
 		id: string
 		appAgentId: string
@@ -88,7 +152,7 @@ export async function postDoTestNodeRunner(
 }
 
 /** 更新智能体节点 POST /api/agent/doUpdateAgentNode */
-export async function postDoUpdateAgentNode(
+export async function postAgentDoUpdateAgentNode(
 	body: {
 		id: string
 		appAgentId: string
@@ -117,7 +181,7 @@ export async function postDoUpdateAgentNode(
 }
 
 /** 获取智能体信息 POST /api/agent/getAgentInfo */
-export async function postGetAgentInfo(
+export async function postAgentGetAgentInfo(
 	body: {
 		id: string
 	},

+ 3 - 1
packages/api-service/servers/api/index.ts

@@ -3,6 +3,8 @@
 // API 更新时间:
 // API 唯一标识:
 import * as agent from './agent'
+import * as tools from './tools'
 export default {
-  agent
+  agent,
+  tools
 }

+ 14 - 0
packages/api-service/servers/api/tools.ts

@@ -0,0 +1,14 @@
+// @ts-ignore
+/* eslint-disable */
+import request from '@repo/api-client'
+
+/** 批量生成UUID POST /api/openapi/doBatchGenerateUUID */
+export async function postOpenapiDoBatchGenerateUuid(options?: { [key: string]: any }) {
+  return request<{ isSuccess: boolean; code: number; result: string[]; isAuthorized: boolean }>(
+    '/api/openapi/doBatchGenerateUUID',
+    {
+      method: 'POST',
+      ...(options || {})
+    }
+  )
+}

+ 4 - 0
packages/nodes/Interface.ts

@@ -83,6 +83,10 @@ export interface INodeType {
 	 * 版本信息
 	 */
 	version: string[]
+	/**
+	 * 所属分组
+	 */
+	group?: string
 	/**
 	 * 展示名称
 	 */

+ 1 - 0
packages/nodes/materials/code.tsx

@@ -5,6 +5,7 @@ export const codeNode: INodeType = {
 	displayName: '代码',
 	name: 'code',
 	description: '通过代码处理数据',
+	group: '业务逻辑',
 	icon: 'lucide:code',
 	iconColor: '#f9c74f',
 	inputs: [NodeConnectionTypes.main],

+ 3 - 2
packages/nodes/materials/condition.ts

@@ -3,8 +3,9 @@ import { NodeConnectionTypes, type INodeType } from '../Interface'
 export const conditionNode: INodeType = {
 	version: ['1'],
 	displayName: '条件判断',
-	name: 'condition',
+	name: 'if-else',
 	description: '根据条件判断',
+	group: '业务逻辑',
 	icon: 'lucide:trending-up-down',
 	iconColor: '#b33be6',
 	inputs: [NodeConnectionTypes.main],
@@ -22,7 +23,7 @@ export const conditionNode: INodeType = {
 		width: 96,
 		height: 96,
 		selected: false,
-		nodeType: 'condition',
+		nodeType: 'if-else',
 		zIndex: 1,
 		data: {}
 	}

+ 1 - 0
packages/nodes/materials/database.ts

@@ -5,6 +5,7 @@ export const databaseNode: INodeType = {
 	displayName: '数据查询',
 	name: 'database',
 	description: '通过数据库查询数据',
+	group: '数据处理',
 	icon: 'lucide:database-zap',
 	iconColor: '#64dc34',
 	inputs: [NodeConnectionTypes.main],

+ 1 - 0
packages/nodes/materials/end.ts

@@ -5,6 +5,7 @@ export const endNode: INodeType = {
 	displayName: '结束',
 	name: 'end',
 	description: '流程结束节点',
+	group: '业务逻辑',
 	icon: 'lucide:unplug',
 	iconColor: '#c49600',
 	inputs: [NodeConnectionTypes.main],

+ 38 - 53
packages/nodes/materials/http.ts

@@ -1,71 +1,55 @@
 import { NodeConnectionTypes, type INodeType } from '../Interface'
 
-type HttpData = {
-	outputs: [
-		{
-			name: 'body'
-			describe: '响应内容'
-			type: 'string'
-		},
-		{
-			name: 'status_code'
-			describe: '响应状态码'
-			type: 'number'
-		},
-		{
-			name: 'headers'
-			describe: '响应头列表 JSON'
-			type: 'object'
-		}
-	]
-	output_can_alter: false
-	variables: []
+export type HttpData = {
+	outputs: Array<{
+		name: 'body' | 'status_code' | 'headers'
+		describe: string
+		type: 'string' | 'number' | 'object'
+	}>
+	output_can_alter: boolean
+	variables: any[]
 	method: 'post'
-	ssl_verify: false
-	isInIteration: false
-	default_value: []
+	ssl_verify: boolean
+	isInIteration: boolean
+	default_value: any[]
 	body: {
 		type: 'json'
-		data: [
-			{
-				key: ''
-				type: 'text'
-				value: '{"id":"b3a4aabb-a6b8-47f3-8a32-f45930f7d7b8"}'
-			}
-		]
+		data: Array<{
+			key: string
+			type: 'text'
+			value: string
+		}>
 	}
-	params: []
-	title: 'HTTP 请求'
+	params: any[]
+	title: string
 	type: 'http-request'
-	error_strategy: 'none'
+	error_strategy: 'none' | string
 	retry_config: {
-		max_retries: 3
-		retry_enabled: false
-		retry_interval: 100
+		max_retries: number
+		retry_enabled: boolean
+		retry_interval: number
 	}
-	url: '#{env.api_address}/api/agent/getAgentInfo'
+	url: string
 	authorization: {
-		type: 'none'
+		type: string
 		config: {
-			api_key: ''
-			header: ''
-			type: ''
+			api_key: string
+			header: string
+			type: string
 		}
 	}
 	timeout_config: {
-		max_write_timeout: 0
-		max_read_timeout: 0
-		max_connect_timeout: 0
+		max_write_timeout: number
+		max_read_timeout: number
+		max_connect_timeout: number
 	}
-	heads: [
-		{
-			name: 'Authorization'
-			value: ''
-		}
-	]
-	selected: true
-	desc: ''
-	isInLoop: false
+	heads: Array<{
+		name: string
+		value: string
+	}>
+	selected: boolean
+	desc: string
+	isInLoop: boolean
 }
 
 export const httpNode: INodeType = {
@@ -73,6 +57,7 @@ export const httpNode: INodeType = {
 	displayName: 'HTTP 请求',
 	name: 'http-request',
 	description: '通过HTTP请求获取数据',
+	group: '数据处理',
 	icon: 'lucide:link',
 	iconColor: '#9373ee',
 	inputs: [NodeConnectionTypes.main],

+ 17 - 5
packages/nodes/materials/index.ts

@@ -11,11 +11,22 @@ import { httpNode } from './http'
 import { conditionNode } from './condition'
 import { databaseNode } from './database'
 import { codeNode } from './code'
-
-import { materialTools, type SourceType } from './toolbar'
+import { iterationNode } from './iteration'
+import { loopNode } from './loop'
+import { listNode } from './list'
 import type { INodeType } from '../Interface'
 
-const nodes = [startNode, endNode, httpNode, conditionNode, databaseNode, codeNode]
+const nodes = [
+	startNode,
+	endNode,
+	httpNode,
+	conditionNode,
+	databaseNode,
+	codeNode,
+	iterationNode,
+	loopNode,
+	listNode
+]
 
 const nodeMap = nodes.reduce(
 	(acc, cur) => {
@@ -32,8 +43,9 @@ export {
 	conditionNode,
 	databaseNode,
 	codeNode,
-	materialTools,
 	nodes,
 	nodeMap,
-	type SourceType
+	iterationNode,
+	loopNode,
+	listNode
 }

+ 30 - 0
packages/nodes/materials/iteration.ts

@@ -0,0 +1,30 @@
+import { NodeConnectionTypes, type INodeType } from '../Interface'
+
+export type IterationData = {}
+
+export const iterationNode: INodeType = {
+	version: ['1'],
+	displayName: '迭代',
+	name: 'iteration',
+	description: '迭代节点',
+	group: '业务逻辑',
+	icon: 'lucide:repeat-2',
+	iconColor: '#9373ee',
+	inputs: [NodeConnectionTypes.main],
+	outputs: [NodeConnectionTypes.main],
+	// 业务数据
+	schema: {
+		appAgentId: '',
+		parentId: '',
+		position: {
+			x: 20,
+			y: 30
+		},
+		width: 96,
+		height: 96,
+		selected: false,
+		nodeType: 'iteration',
+		zIndex: 1,
+		data: {}
+	}
+}

+ 30 - 0
packages/nodes/materials/list.ts

@@ -0,0 +1,30 @@
+import { NodeConnectionTypes, type INodeType } from '../Interface'
+
+export type ListData = {}
+
+export const listNode: INodeType = {
+	version: ['1'],
+	displayName: '列表操作',
+	name: 'list',
+	description: '列表操作节点',
+	group: '数据处理',
+	icon: 'lucide:list',
+	iconColor: '#9373ee',
+	inputs: [NodeConnectionTypes.main],
+	outputs: [NodeConnectionTypes.main],
+	// 业务数据
+	schema: {
+		appAgentId: '',
+		parentId: '',
+		position: {
+			x: 20,
+			y: 30
+		},
+		width: 96,
+		height: 96,
+		selected: false,
+		nodeType: 'list',
+		zIndex: 1,
+		data: {}
+	}
+}

+ 30 - 0
packages/nodes/materials/loop.ts

@@ -0,0 +1,30 @@
+import { NodeConnectionTypes, type INodeType } from '../Interface'
+
+export type LoopData = {}
+
+export const loopNode: INodeType = {
+	version: ['1'],
+	displayName: '循环',
+	name: 'loop',
+	description: '循环节点',
+	group: '业务逻辑',
+	icon: 'lucide:loop',
+	iconColor: '#9373ee',
+	inputs: [NodeConnectionTypes.main],
+	outputs: [NodeConnectionTypes.main],
+	// 业务数据
+	schema: {
+		appAgentId: '',
+		parentId: '',
+		position: {
+			x: 20,
+			y: 30
+		},
+		width: 96,
+		height: 96,
+		selected: false,
+		nodeType: 'loop',
+		zIndex: 1,
+		data: {}
+	}
+}

+ 1 - 0
packages/nodes/materials/start.ts

@@ -5,6 +5,7 @@ export const startNode: INodeType = {
 	displayName: '开始',
 	name: 'start',
 	description: '流程开始节点',
+	group: '业务逻辑',
 	icon: 'lucide:play',
 	iconColor: '#409eff',
 	inputs: [],

+ 0 - 94
packages/nodes/materials/toolbar.ts

@@ -1,94 +0,0 @@
-/*
- * @Author: liuJie
- * @Date: 2026-01-25 00:06:41
- * @LastEditors: liuJie
- * @LastEditTime: 2026-01-25 21:52:47
- * @Describe: 工具栏配置
- */
-export interface MaterialToolType {
-	label: string
-	id: string
-	description: string
-	source: Array<SourceType>
-}
-
-export interface SourceType {
-	name: string
-	type: string
-	icon: string
-	component: string
-	id: string
-	data?: any
-	active: boolean
-	isEdit: boolean
-}
-
-export const materialTools: MaterialToolType[] = [
-	{
-		label: '业务逻辑',
-		id: 'basic-nodes',
-		description: '业务节点',
-		source: [
-			{
-				name: '开始',
-				type: 'start',
-				icon: 'lucide:play',
-				component: 'Start',
-				id: 'start-node',
-				data: {},
-				active: false,
-				isEdit: false
-			},
-			{
-				name: '结束',
-				type: 'end',
-				icon: 'lucide:unplug',
-				component: 'End',
-				id: 'end-node-id',
-				data: {},
-				active: false,
-				isEdit: false
-			},
-			{
-				name: 'HTTP请求',
-				type: 'http',
-				icon: 'lucide:link',
-				component: 'Http',
-				id: 'http-node-id',
-				data: {},
-				active: false,
-				isEdit: false
-			},
-			{
-				name: '条件分支',
-				type: 'condition',
-				icon: 'lucide:trending-up-down',
-				component: 'Condition',
-				id: 'condition-node-id',
-				data: {},
-				active: false,
-				isEdit: false
-			},
-			{
-				name: '代码执行',
-				type: 'code',
-				icon: 'lucide:code',
-				component: 'Code',
-				id: 'code-node-id',
-				data: {},
-				active: false,
-				isEdit: false
-			},
-			{
-				name: '数据查询',
-				type: 'database',
-				icon: 'lucide:database-zap',
-				component: 'Database',
-				id: 'data-query-node-id',
-				data: {},
-				active: false,
-				isEdit: false
-			}
-		]
-	}
-]

+ 2 - 3
packages/workflow/src/components/Canvas.vue

@@ -6,13 +6,12 @@ import type {
 	CanvasNodeMoveEvent,
 	IWorkflowNode
 } from '../Interface'
-import type { SourceType } from '@repo/nodes'
 import type { NodeMouseEvent, Connection, NodeDragEvent } from '@vue-flow/core'
 
 import { ref, onMounted, computed, provide } from 'vue'
 import { VueFlow, useVueFlow, MarkerType } from '@vue-flow/core'
 import { MiniMap } from '@vue-flow/minimap'
-import { onKeyDown, onKeyUp, useThrottleFn } from '@vueuse/core'
+import { useThrottleFn } from '@vueuse/core'
 
 import CanvasNode from './elements/nodes/CanvasNode.vue'
 import CanvasEdge from './elements/edges/CanvasEdge.vue'
@@ -215,7 +214,7 @@ const onZoomToFit = () => {
 const onResetZoom = () => {
 	zoomTo(1)
 }
-const onAddNode = (value: SourceType | string) => {
+const onAddNode = (value: { type: string } | string) => {
 	emit('create:node', value)
 }
 

+ 2 - 1
packages/workflow/src/components/elements/edges/CanvasEdge.vue

@@ -35,7 +35,8 @@ const connection = computed<Connection>(() => ({
 	source: props.source,
 	target: props.target,
 	sourceHandle: props.sourceHandleId,
-	targetHandle: props.targetHandleId
+	targetHandle: props.targetHandleId,
+	id: props.id
 }))
 
 watch(