feat: 流程添加注释

This commit is contained in:
zhanghao 2026-08-07 14:38:16 +08:00
parent 8e2afcbaa6
commit 6c9ead2603
37 changed files with 1107 additions and 7 deletions

View File

@ -1,3 +1,7 @@
<!--
* 文件说明节点组件库侧栏负责分类搜索和拖拽创建
* 作用范围仅服务于流程设计器模块
-->
<template>
<div class="component-library">
<div class="library-header">
@ -75,6 +79,11 @@ watch(keyword, (value) => {
if (value.trim()) activeNames.value = filteredGroups.value.map((group) => group.collapseTitle);
});
/**
* 处理 handleDragCustom 对应的数据或交互作用范围仅限当前组件或模块
* @param {*} event 触发操作的浏览器事件
* @param {*} node 当前流程节点
*/
const handleDragCustom = (event, node) => {
event.dataTransfer.effectAllowed = "copy";
event.dataTransfer.setData("flowNode", JSON.stringify(node));

View File

@ -1,3 +1,7 @@
<!--
* 文件说明递归参数表单负责嵌套字段的增删和类型切换
* 作用范围仅服务于流程设计器模块
-->
<!-- components/FormItemRecursive.vue -->
<template>
<div class="form-item-recursive">
@ -147,6 +151,10 @@ const { formType, currentList, propPath, depth, isFirstLevel, endDepth, parentPa
const emit = defineEmits(['add-item', 'delete-item'])
//
/**
* 处理 handleTypeChange 对应的数据或交互作用范围仅限当前组件或模块
* @param {*} item 调用方传入的 item 参数
*/
const handleTypeChange = (item) => {
if (!["object", "array<object>"].includes(item.type)) {
item.children = []; //
@ -156,6 +164,10 @@ const handleTypeChange = (item) => {
};
//
/**
* 处理 handleAddChild 对应的数据或交互作用范围仅限当前组件或模块
* @param {*} parentItem 调用方传入的 parentItem 参数
*/
const handleAddChild = (parentItem) => {
parentItem.children.push({
name: "",
@ -168,6 +180,10 @@ const handleAddChild = (parentItem) => {
};
//
/**
* 处理 handleDelete 对应的数据或交互作用范围仅限当前组件或模块
* @param {*} index 目标项索引
*/
const handleDelete = (index) => {
// = + parentPathundefined
const fullPath = parentPath ? [...parentPath, index] : [index];
@ -175,6 +191,10 @@ const handleDelete = (index) => {
}
//
/**
* 处理 handleChildDelete 对应的数据或交互作用范围仅限当前组件或模块
* @param {*} childFullPath 调用方传入的 childFullPath 参数
*/
const handleChildDelete = (childFullPath) => {
emit("delete-item", childFullPath);
}

View File

@ -1,3 +1,7 @@
<!--
* 文件说明节点运行状态面板展示耗时输入输出和错误
* 作用范围仅服务于流程设计器模块
-->
<template>
<div v-if="showState !== 'NORMAL'" class="node-state" :class="`node-state--${showState.toLowerCase()}`">
<div class="node-state__summary">
@ -66,6 +70,9 @@ watch(() => props.state, (value) => {
showState.value = value;
}, { immediate: true });
/**
* 关闭 closePopover 对应的数据或交互作用范围仅限当前组件或模块
*/
const closePopover = () => {
showState.value = "NORMAL";
popoverVisible.value = false;

View File

@ -1,3 +1,7 @@
<!--
* 文件说明节点标题栏负责重命名删除和单节点执行
* 作用范围仅服务于流程设计器模块
-->
<template>
<div class="node-title">
<div class="node-title__main">
@ -108,12 +112,18 @@ const typeLabel = computed(() => {
return { EDGE: "设备", LLM: "模型", AE: "评估" }[props.nodeType] || props.nodeType;
});
/**
* 处理 sourceName 对应的数据或交互作用范围仅限当前组件或模块
*/
const sourceName = () => props.nodeProperties?.name || props.nodeName || "未命名节点";
watch(sourceName, (value) => {
if (!isEditing.value) localName.value = value;
});
/**
* 处理 startEditing 对应的数据或交互作用范围仅限当前组件或模块
*/
const startEditing = async () => {
isEditing.value = true;
await nextTick();
@ -121,6 +131,9 @@ const startEditing = async () => {
nameInputRef.value?.select?.();
};
/**
* 确认 confirmNodeName 对应的数据或交互作用范围仅限当前组件或模块
*/
const confirmNodeName = () => {
const name = localName.value.trim();
if (!name) return;
@ -129,11 +142,17 @@ const confirmNodeName = () => {
isEditing.value = false;
};
/**
* 处理 cancelNodeName 对应的数据或交互作用范围仅限当前组件或模块
*/
const cancelNodeName = () => {
localName.value = sourceName();
isEditing.value = false;
};
/**
* 删除 deleteNode 对应的数据或交互作用范围仅限当前组件或模块
*/
const deleteNode = async () => {
try {
await ElMessageBox.confirm(
@ -155,6 +174,9 @@ const deleteNode = async () => {
}
};
/**
* 执行 execute 对应的数据或交互作用范围仅限当前组件或模块
*/
const execute = () => {
document.dispatchEvent(new CustomEvent("singleNodeExecution", {
bubbles: false,

View File

@ -1,3 +1,7 @@
<!--
* 文件说明动态节点包装器挂载具体节点并同步内容变化
* 作用范围仅服务于流程设计器模块
-->
<template>
<div class="node__box">
<component :is="componentId" :model="props.model" :properties="props.properties" ref="nodeRef" @contentChange="contentChange"></component>
@ -18,6 +22,9 @@ const emits = defineEmits(['contentChange', 'bindRef'])
const componentId = ref(props.component)
/**
* 处理 contentChange 对应的数据或交互作用范围仅限当前组件或模块
*/
const contentChange = () => {
emits('contentChange')
}
@ -40,4 +47,4 @@ onMounted(() => {
box-shadow: 0 5px 15px 0#00000008;
position: relative;
}
</style>
</style>

View File

@ -1,3 +1,7 @@
<!--
* 文件说明节点参数抽屉统一触发参数校验和保存
* 作用范围仅服务于流程设计器模块
-->
<template>
<el-drawer
:model-value="props.drawer"
@ -55,6 +59,9 @@ const currentComponent = computed(() => {
})
//
/**
* 确认 confirmSave 对应的数据或交互作用范围仅限当前组件或模块
*/
const confirmSave = async () => {
if (paramsComponentRef.value && typeof paramsComponentRef.value.validateAndSave === 'function') {
await paramsComponentRef.value.validateAndSave()
@ -67,6 +74,9 @@ const confirmSave = async () => {
defineExpose({ confirmSave })
// /
/**
* 处理 handleSaveSuccess 对应的数据或交互作用范围仅限当前组件或模块
*/
const handleSaveSuccess = () => {
emitter.emit('setProperties', { id: props.data.id })
emitter.emit("throwAnError", { id: props.data.id, hasError: false, type: props.data.type });
@ -74,6 +84,9 @@ const handleSaveSuccess = () => {
emits('close')
}
/**
* 处理 handleSaveError 对应的数据或交互作用范围仅限当前组件或模块
*/
const handleSaveError = () => {
emitter.emit('setProperties', { id: props.data.id })
emitter.emit("throwAnError", { id: props.data.id, hasError: true, type: props.data.type });

View File

@ -1,3 +1,7 @@
<!--
* 文件说明流程试运行抽屉收集运行参数并绑定机器人设备
* 作用范围仅服务于流程设计器模块
-->
<template>
<el-drawer
:model-value="props.drawer"
@ -53,7 +57,7 @@
/>
</el-select>
</el-form-item>
<el-form-item v-else-if="row.type !== 'object' && !row.type.includes('array')" label="" :prop="`tableData${row.propPath}value`" :rules="row.required ? [{ required: true, message: '请输入变量名', trigger: 'blur' }] : []">
<el-input v-if="row.type === 'string'" v-model="row.value" />
<el-input-number v-if="row.type === 'number'" :controls="false" v-model="row.value" />
@ -93,14 +97,27 @@ const tableData = ref([])
// 使
const trialRunCache = new Map()
const bindingLoading = ref(false)
/**
* 处理 emptyBindingSummary 对应的数据或交互作用范围仅限当前组件或模块
* @param {*} robotId 机器人 ID
*/
const emptyBindingSummary = (robotId = '') => ({ robotId, matched: 0, missing: 0, untouched: 0 })
const bindingSummary = ref(emptyBindingSummary())
const emits = defineEmits(['close', 'changeState'])
/**
* 克隆 cloneData 对应的数据或交互作用范围仅限当前组件或模块
* @param {*} data 待处理的数据
*/
const cloneData = (data) => JSON.parse(JSON.stringify(data || []))
//
/**
* 合并 mergeCachedValues 对应的数据或交互作用范围仅限当前组件或模块
* @param {*} source 源数据
* @param {*} cached 缓存数据
*/
const mergeCachedValues = (source, cached) => {
const cachedByName = new Map((cached || []).map(item => [item.name, item]))
return (source || []).map((item, index) => {
@ -115,14 +132,23 @@ const mergeCachedValues = (source, cached) => {
})
}
/**
* 处理 cacheKey 对应的数据或交互作用范围仅限当前组件或模块
*/
const cacheKey = () => props.flowId || '__new_flow__'
//
/**
* 缓存 rememberTrialRunParams 对应的数据或交互作用范围仅限当前组件或模块
*/
const rememberTrialRunParams = () => {
if (!tableData.value.length) return
trialRunCache.set(cacheKey(), cloneData(tableData.value))
}
/**
* 处理 handleClose 对应的数据或交互作用范围仅限当前组件或模块
*/
const handleClose = () => {
rememberTrialRunParams()
ruleFormRef.value?.clearValidate()
@ -142,6 +168,9 @@ watch(() => props.drawer,
}
)
/**
* 确认 confirm 对应的数据或交互作用范围仅限当前组件或模块
*/
const confirm = () => {
rememberTrialRunParams()
lf.fitView()
@ -174,7 +203,13 @@ const confirm = () => {
const robotIdOptions = ref([])
/**
* 处理 handleRobotChange 对应的数据或交互作用范围仅限当前组件或模块
* @param {*} robotId 机器人 ID
*/
const handleRobotChange = async (robotId) => {
//
//
bindingSummary.value = emptyBindingSummary(robotId || '')
if (!robotId) return
bindingLoading.value = true

View File

@ -1,3 +1,7 @@
<!--
* 文件说明通用节点参数面板配置输入输出及机器人动作调试
* 作用范围仅服务于流程设计器模块
-->
<template>
<el-collapse v-model="activeNames">
<!-- 输入参数 -->
@ -142,6 +146,11 @@ const activeNames = ref(['1', '2'])
const { quoteOptions, cascaderRefs, handleTypeChange, cascaderChange, visibleChange } = useQuote(props.data.id)
//
/**
* 新增 addFormItem 对应的数据或交互作用范围仅限当前组件或模块
* @param {*} e 触发操作的浏览器事件
* @param {*} type 目标类型
*/
const addFormItem = (e, type) => {
e.stopPropagation()
const obj = {
@ -156,11 +165,20 @@ const addFormItem = (e, type) => {
}
//
/**
* 处理 handleDelete 对应的数据或交互作用范围仅限当前组件或模块
* @param {*} index 目标项索引
*/
const handleDelete = (index) => {
formData.nodeParams.splice(index, 1)
}
// FormItemRecursive emit
/**
* 删除 deleteTopLevelItem 对应的数据或交互作用范围仅限当前组件或模块
* @param {*} fullPath 字段完整路径
* @param {*} propPath 字段校验路径
*/
const deleteTopLevelItem = (fullPath, propPath) => {
let currentLevel = formData[propPath]
for (let i = 0; i < fullPath.length - 1; i++) {
@ -171,6 +189,9 @@ const deleteTopLevelItem = (fullPath, propPath) => {
}
//
/**
* 初始化 initData 对应的数据或交互作用范围仅限当前组件或模块
*/
const initData = () => {
const nodeParams = JSON.parse(JSON.stringify(props.data.properties.nodeParams || []))
const outputParams = JSON.parse(JSON.stringify(props.data.properties.outputParams || []))
@ -187,6 +208,9 @@ watch(
)
//
/**
* 校验 validateAndSave 对应的数据或交互作用范围仅限当前组件或模块
*/
const validateAndSave = async () => {
let inputValid = true
let outputValid = true
@ -224,6 +248,9 @@ const robotOptions = ref([])
const deviceOptions = ref([])
const deviceLoading = ref(false)
/**
* 加载 loadRobotOptions 对应的数据或交互作用范围仅限当前组件或模块
*/
const loadRobotOptions = async () => {
const response = await getRobotList({ pageNum: 1, pageSize: 1000, connectStatus: '1' })
robotOptions.value = (response.rows || []).map(robot => ({
@ -232,6 +259,10 @@ const loadRobotOptions = async () => {
}))
}
/**
* 加载 loadRobotDevices 对应的数据或交互作用范围仅限当前组件或模块
* @param {*} robotId 机器人 ID
*/
const loadRobotDevices = async (robotId) => {
robotForm.deviceId = ''
deviceOptions.value = []
@ -250,6 +281,10 @@ const loadRobotDevices = async (robotId) => {
}
}
/**
* 测试 testRule 对应的数据或交互作用范围仅限当前组件或模块
* @param {*} property 参数属性
*/
const testRule = (property) => {
if (property?.name === 'stationId' && props.data.properties.action === 'AGV_MOVE_TO_STATION') {
return [
@ -311,6 +346,9 @@ const robotDialogTitle = computed(() => {
return '选择机器人设备'
})
/**
* 处理 submitRobotForm 对应的数据或交互作用范围仅限当前组件或模块
*/
const submitRobotForm = () => {
if (robotFormAction.value === 'AGV_MOVE_TO_POINT') {
robotFormRef.value.validate(async (valid) => {
@ -440,6 +478,10 @@ const submitRobotForm = () => {
}
}
/**
* 处理 toStationOptions 对应的数据或交互作用范围仅限当前组件或模块
* @param {*} stations 站点集合
*/
const toStationOptions = (stations) => (Array.isArray(stations) ? stations : [])
.filter(station => station?.id !== undefined && station?.id !== null && String(station.id).trim())
.map(station => {
@ -451,6 +493,10 @@ const toStationOptions = (stations) => (Array.isArray(stations) ? stations : [])
}
})
/**
* 打开 openRobotForm 对应的数据或交互作用范围仅限当前组件或模块
* @param {*} action 节点动作类型
*/
const openRobotForm = async (action) => {
dialogVisible.value = true
robotFormAction.value = action

View File

@ -1,3 +1,7 @@
<!--
* 文件说明代码节点参数面板编辑并校验脚本和输入输出字段
* 作用范围仅服务于流程设计器模块
-->
<template>
<el-collapse v-model="activeNames">
<el-collapse v-model="activeNames">
@ -211,6 +215,9 @@ const formData = reactive({
const codeRef = ref()
let editorInstance
/**
* 判断 hasErrors 对应的数据或交互作用范围仅限当前组件或模块
*/
function hasErrors() {
const model = editorInstance.getModel();
if (!model) return false;
@ -220,6 +227,10 @@ function hasErrors() {
//
return markers.some(marker => marker.severity === monaco.MarkerSeverity.Error);
}
/**
* 初始化 initEditor 对应的数据或交互作用范围仅限当前组件或模块
* @param {*} type 目标类型
*/
const initEditor = (type) => {
monaco.languages.typescript.javascriptDefaults.setCompilerOptions({
target: monaco.languages.typescript.ScriptTarget.ES2020,
@ -260,6 +271,10 @@ const initEditor = (type) => {
}
//
/**
* 设置 setValue 对应的数据或交互作用范围仅限当前组件或模块
* @param {*} value 待处理的新值
*/
const setValue = (value) => {
if (editorInstance) {
editorInstance.setValue(value)
@ -267,6 +282,9 @@ const setValue = (value) => {
}
//
/**
* 销毁 disposeEditor 对应的数据或交互作用范围仅限当前组件或模块
*/
const disposeEditor = () => {
if (editorInstance) {
editorInstance.dispose()
@ -284,6 +302,11 @@ const activeNames = ref(['1', '2'])
const { quoteOptions, cascaderRefs, handleTypeChange, cascaderChange, visibleChange } = useQuote(props.data.id)
//
/**
* 新增 addFormItem 对应的数据或交互作用范围仅限当前组件或模块
* @param {*} e 触发操作的浏览器事件
* @param {*} type 目标类型
*/
const addFormItem = (e, type) => {
e.stopPropagation()
const obj = {
@ -298,11 +321,20 @@ const addFormItem = (e, type) => {
}
//
/**
* 处理 handleDelete 对应的数据或交互作用范围仅限当前组件或模块
* @param {*} index 目标项索引
*/
const handleDelete = (index) => {
formData.nodeParams.splice(index, 1)
}
// FormItemRecursive emit
/**
* 删除 deleteTopLevelItem 对应的数据或交互作用范围仅限当前组件或模块
* @param {*} fullPath 字段完整路径
* @param {*} propPath 字段校验路径
*/
const deleteTopLevelItem = (fullPath, propPath) => {
let currentLevel = formData[propPath]
for (let i = 0; i < fullPath.length - 1; i++) {
@ -313,6 +345,9 @@ const deleteTopLevelItem = (fullPath, propPath) => {
}
//
/**
* 初始化 initData 对应的数据或交互作用范围仅限当前组件或模块
*/
const initData = () => {
const nodeParams = JSON.parse(JSON.stringify(props.data.properties.nodeParams || []))
const outputParams = JSON.parse(JSON.stringify(props.data.properties.outputParams || []))
@ -329,6 +364,9 @@ const initData = () => {
}
//
/**
* 校验 validateAndSave 对应的数据或交互作用范围仅限当前组件或模块
*/
const validateAndSave = async () => {
const targetObj = formData.nodeParams.find(item => item.name === 'code');
if (targetObj) {
@ -460,4 +498,4 @@ onBeforeUnmount(() => {
height: 300px;
margin: 12px 0;
}
</style>
</style>

View File

@ -1,3 +1,7 @@
<!--
* 文件说明通用设备参数面板选择设备并配置脚本和参数
* 作用范围仅服务于流程设计器模块
-->
<template>
<el-collapse v-model="activeNames">
<el-collapse v-model="activeNames">
@ -260,6 +264,9 @@ const formData = reactive({
const codeRef = ref()
let editorInstance
/**
* 判断 hasErrors 对应的数据或交互作用范围仅限当前组件或模块
*/
function hasErrors() {
const model = editorInstance.getModel();
if (!model) return false;
@ -269,6 +276,10 @@ function hasErrors() {
//
return markers.some(marker => marker.severity === monaco.MarkerSeverity.Error);
}
/**
* 初始化 initEditor 对应的数据或交互作用范围仅限当前组件或模块
* @param {*} type 目标类型
*/
const initEditor = (type) => {
monaco.languages.typescript.javascriptDefaults.setCompilerOptions({
target: monaco.languages.typescript.ScriptTarget.ES2020,
@ -309,6 +320,10 @@ const initEditor = (type) => {
}
//
/**
* 设置 setValue 对应的数据或交互作用范围仅限当前组件或模块
* @param {*} value 待处理的新值
*/
const setValue = (value) => {
if (editorInstance) {
editorInstance.setValue(value)
@ -316,6 +331,9 @@ const setValue = (value) => {
}
//
/**
* 销毁 disposeEditor 对应的数据或交互作用范围仅限当前组件或模块
*/
const disposeEditor = () => {
if (editorInstance) {
editorInstance.dispose()
@ -341,6 +359,9 @@ const deviceFormRules = {
deviceId: [{ required: true, message: '请选择执行设备', trigger: 'change' }]
}
/**
* 加载 loadRobotOptions 对应的数据或交互作用范围仅限当前组件或模块
*/
const loadRobotOptions = async () => {
robotLoading.value = true
try {
@ -354,6 +375,10 @@ const loadRobotOptions = async () => {
}
}
/**
* 加载 loadRobotDevices 对应的数据或交互作用范围仅限当前组件或模块
* @param {*} robotId 机器人 ID
*/
const loadRobotDevices = async (robotId) => {
deviceForm.deviceId = ''
deviceOptions.value = []
@ -370,6 +395,9 @@ const loadRobotDevices = async (robotId) => {
}
}
/**
* 打开 openDeviceDialog 对应的数据或交互作用范围仅限当前组件或模块
*/
const openDeviceDialog = async () => {
deviceDialogVisible.value = true
deviceForm.robotId = ''
@ -378,6 +406,9 @@ const openDeviceDialog = async () => {
await loadRobotOptions()
}
/**
* 确认 confirmDevice 对应的数据或交互作用范围仅限当前组件或模块
*/
const confirmDevice = async () => {
const valid = await deviceFormRef.value?.validate().catch(() => false)
if (!valid) return
@ -397,6 +428,11 @@ const confirmDevice = async () => {
const { quoteOptions, cascaderRefs, handleTypeChange, cascaderChange, visibleChange } = useQuote(props.data.id)
//
/**
* 新增 addFormItem 对应的数据或交互作用范围仅限当前组件或模块
* @param {*} e 触发操作的浏览器事件
* @param {*} type 目标类型
*/
const addFormItem = (e, type) => {
e.stopPropagation()
const obj = {
@ -411,11 +447,20 @@ const addFormItem = (e, type) => {
}
//
/**
* 处理 handleDelete 对应的数据或交互作用范围仅限当前组件或模块
* @param {*} index 目标项索引
*/
const handleDelete = (index) => {
formData.nodeParams.splice(index, 1)
}
// FormItemRecursive emit
/**
* 删除 deleteTopLevelItem 对应的数据或交互作用范围仅限当前组件或模块
* @param {*} fullPath 字段完整路径
* @param {*} propPath 字段校验路径
*/
const deleteTopLevelItem = (fullPath, propPath) => {
let currentLevel = formData[propPath]
for (let i = 0; i < fullPath.length - 1; i++) {
@ -426,6 +471,9 @@ const deleteTopLevelItem = (fullPath, propPath) => {
}
//
/**
* 初始化 initData 对应的数据或交互作用范围仅限当前组件或模块
*/
const initData = () => {
const nodeParams = JSON.parse(JSON.stringify(props.data.properties.nodeParams || []))
const outputParams = JSON.parse(JSON.stringify(props.data.properties.outputParams || []))
@ -442,6 +490,9 @@ const initData = () => {
}
//
/**
* 校验 validateAndSave 对应的数据或交互作用范围仅限当前组件或模块
*/
const validateAndSave = async () => {
const targetObj = formData.nodeParams.find(item => item.name === 'requestJson');
if (targetObj) {

View File

@ -1,3 +1,7 @@
<!--
* 文件说明HTTP 节点参数面板配置请求及输出字段
* 作用范围仅服务于流程设计器模块
-->
<template>
<el-form :inline="true" :model="httpNodeData" :rules="rules" class="http-form" ref="dynamicFormRef"
label-position="top" label-width="auto">
@ -352,6 +356,9 @@ const formData = reactive({
const codeRef = ref()
let editorInstance
/**
* 判断 hasErrors 对应的数据或交互作用范围仅限当前组件或模块
*/
function hasErrors() {
const model = editorInstance.getModel();
if (!model) return false;
@ -362,6 +369,10 @@ function hasErrors() {
return markers.some(marker => marker.severity === monaco.MarkerSeverity.Error);
}
/**
* 初始化 initEditor 对应的数据或交互作用范围仅限当前组件或模块
* @param {*} type 目标类型
*/
const initEditor = (type) => {
monaco.languages.typescript.javascriptDefaults.setCompilerOptions({
target: monaco.languages.typescript.ScriptTarget.ES2020,
@ -402,6 +413,10 @@ const initEditor = (type) => {
}
//
/**
* 设置 setValue 对应的数据或交互作用范围仅限当前组件或模块
* @param {*} value 待处理的新值
*/
const setValue = (value) => {
if (editorInstance) {
editorInstance.setValue(value)
@ -409,6 +424,9 @@ const setValue = (value) => {
}
//
/**
* 销毁 disposeEditor 对应的数据或交互作用范围仅限当前组件或模块
*/
const disposeEditor = () => {
if (editorInstance) {
editorInstance.dispose()
@ -424,6 +442,11 @@ const activeNames = ref(['1', '2', '3', '4'])
const { quoteOptions, cascaderRefs, handleTypeChange, cascaderChange, visibleChange } = useQuote(props.data.id)
//
/**
* 新增 addFormItem 对应的数据或交互作用范围仅限当前组件或模块
* @param {*} e 触发操作的浏览器事件
* @param {*} type 目标类型
*/
const addFormItem = (e, type) => {
e.stopPropagation()
const obj = {
@ -438,11 +461,20 @@ const addFormItem = (e, type) => {
}
//
/**
* 处理 handleDelete 对应的数据或交互作用范围仅限当前组件或模块
* @param {*} index 目标项索引
*/
const handleDelete = (index) => {
formData.nodeParams.splice(index, 1)
}
// FormItemRecursive emit
/**
* 删除 deleteTopLevelItem 对应的数据或交互作用范围仅限当前组件或模块
* @param {*} fullPath 字段完整路径
* @param {*} propPath 字段校验路径
*/
const deleteTopLevelItem = (fullPath, propPath) => {
let currentLevel = formData[propPath]
for (let i = 0; i < fullPath.length - 1; i++) {
@ -461,6 +493,11 @@ const httpNodeData = ref({
}
});
/**
* 新增 addHttpNodeFormItem 对应的数据或交互作用范围仅限当前组件或模块
* @param {*} e 触发操作的浏览器事件
* @param {*} type 目标类型
*/
const addHttpNodeFormItem = (e, type) => {
e.stopPropagation();
if (type === 'body') {
@ -476,6 +513,11 @@ const addHttpNodeFormItem = (e, type) => {
}
};
/**
* 处理 handleHttpNodeDelete 对应的数据或交互作用范围仅限当前组件或模块
* @param {*} type 目标类型
* @param {*} index 目标项索引
*/
const handleHttpNodeDelete = (type, index) => {
if (type === 'body') {
httpNodeData.value.body.formData.splice(index, 1);
@ -485,6 +527,9 @@ const handleHttpNodeDelete = (type, index) => {
};
//
/**
* 初始化 initData 对应的数据或交互作用范围仅限当前组件或模块
*/
const initData = () => {
const nodeParams = JSON.parse(JSON.stringify(props.data.properties.nodeParams || []))
const transformedData = transformHttpNodeData(nodeParams);
@ -503,7 +548,12 @@ const initData = () => {
}
//
/**
* 校验 validateAndSave 对应的数据或交互作用范围仅限当前组件或模块
*/
const validateAndSave = async () => {
// JSON
//
let inputValid = true
if (dynamicFormRef.value) {
await dynamicFormRef.value.validate((valid) => { if (!valid) inputValid = false })
@ -651,4 +701,4 @@ onBeforeUnmount(() => {
height: 300px;
margin: 12px 0;
}
</style>
</style>

View File

@ -1,3 +1,7 @@
<!--
* 文件说明识别节点参数面板配置识别条件和告警规则
* 作用范围仅服务于流程设计器模块
-->
<template>
<el-form :inline="true" :model="RecognizeNodeData" :rules="rules" ref="dynamicFormRef" class="http-form"
label-position="top" label-width="auto">
@ -134,6 +138,11 @@ const activeNames = ref(['1', '2'])
const { quoteOptions, cascaderRefs, handleTypeChange, cascaderChange, visibleChange } = useQuote(props.data.id)
//
/**
* 新增 addFormItem 对应的数据或交互作用范围仅限当前组件或模块
* @param {*} e 触发操作的浏览器事件
* @param {*} type 目标类型
*/
const addFormItem = (e, type) => {
e.stopPropagation()
const obj = {
@ -159,6 +168,10 @@ const alarmOperators = [
{ label: '不等于NE', value: 'NE' }
]
/**
* 新增 addAlarmRule 对应的数据或交互作用范围仅限当前组件或模块
* @param {*} e 触发操作的浏览器事件
*/
const addAlarmRule = (e) => {
e.stopPropagation()
RecognizeNodeData.value.alarmRules.push({
@ -169,15 +182,28 @@ const addAlarmRule = (e) => {
})
}
/**
* 删除 deleteAlarmRule 对应的数据或交互作用范围仅限当前组件或模块
* @param {*} index 目标项索引
*/
const deleteAlarmRule = (index) => {
RecognizeNodeData.value.alarmRules.splice(index, 1)
}
/**
* 新增 addRecognizeItem 对应的数据或交互作用范围仅限当前组件或模块
* @param {*} e 触发操作的浏览器事件
*/
const addRecognizeItem = (e) => {
e.stopPropagation();
RecognizeNodeData.value.outputParams.push({ name: "", type: "string", input: "" });
}
/**
* 删除 deleteRecognizeItem 对应的数据或交互作用范围仅限当前组件或模块
* @param {*} fullPath 字段完整路径
* @param {*} propPath 字段校验路径
*/
const deleteRecognizeItem = (fullPath, propPath) => {
//
let currentLevel = RecognizeNodeData.value[propPath];
@ -195,6 +221,9 @@ const deleteRecognizeItem = (fullPath, propPath) => {
}
//
/**
* 初始化 initData 对应的数据或交互作用范围仅限当前组件或模块
*/
const initData = () => {
const nodeParams = JSON.parse(JSON.stringify(props.data.properties.nodeParams || []))
const outputParams = JSON.parse(JSON.stringify(props.data.properties.outputParams || []))
@ -214,6 +243,9 @@ watch(
)
//
/**
* 校验 validateAndSave 对应的数据或交互作用范围仅限当前组件或模块
*/
const validateAndSave = async () => {
let inputValid = true
let outputValid = true

View File

@ -1,3 +1,7 @@
<!--
* 文件说明智能体参数面板配置智能体调用和语音合成
* 作用范围仅服务于流程设计器模块
-->
<template>
<el-form :inline="true" :model="sdAgentNodeData" :rules="rules" ref="dynamicFormRef" class="http-form"
label-position="top" label-width="auto">
@ -162,6 +166,11 @@ const activeNames = ref(['1', '2'])
const { quoteOptions, cascaderRefs, handleTypeChange, cascaderChange, visibleChange } = useQuote(props.data.id)
//
/**
* 新增 addFormItem 对应的数据或交互作用范围仅限当前组件或模块
* @param {*} e 触发操作的浏览器事件
* @param {*} type 目标类型
*/
const addFormItem = (e, type) => {
e.stopPropagation()
const obj = {
@ -178,6 +187,9 @@ const addFormItem = (e, type) => {
//
const sdAgentNodeData = ref({})
/**
* 处理 voiceOptions 对应的数据或交互作用范围仅限当前组件或模块
*/
const voiceOptions = () => {
return [{
value: 'x4_xiaoyan',
@ -185,6 +197,10 @@ const voiceOptions = () => {
}]
}
/**
* 处理 handleTtsChange 对应的数据或交互作用范围仅限当前组件或模块
* @param {*} val 变更值
*/
const handleTtsChange = (val) => {
if (val) {
sdAgentNodeData.value.tts = []
@ -200,11 +216,20 @@ const handleTtsChange = (val) => {
}
}
/**
* 新增 addSdAgentItem 对应的数据或交互作用范围仅限当前组件或模块
* @param {*} e 触发操作的浏览器事件
*/
const addSdAgentItem = (e) => {
e.stopPropagation();
sdAgentNodeData.value.outputParams.push({ name: "", type: "string", input: "" });
}
/**
* 删除 deleteSdAgentItem 对应的数据或交互作用范围仅限当前组件或模块
* @param {*} fullPath 字段完整路径
* @param {*} propPath 字段校验路径
*/
const deleteSdAgentItem = (fullPath, propPath) => {
//
let currentLevel = sdAgentNodeData.value[propPath];
@ -222,6 +247,9 @@ const deleteSdAgentItem = (fullPath, propPath) => {
}
//
/**
* 初始化 initData 对应的数据或交互作用范围仅限当前组件或模块
*/
const initData = () => {
const nodeParams = JSON.parse(JSON.stringify(props.data.properties.nodeParams || []))
const outputParams = JSON.parse(JSON.stringify(props.data.properties.outputParams || []))
@ -241,6 +269,9 @@ watch(
)
//
/**
* 校验 validateAndSave 对应的数据或交互作用范围仅限当前组件或模块
*/
const validateAndSave = async () => {
let inputValid = true
let outputValid = true

View File

@ -1,3 +1,7 @@
<!--
* 文件说明开始节点参数面板配置流程入口参数
* 作用范围仅服务于流程设计器模块
-->
<template>
<el-collapse v-model="activeNames">
<el-collapse-item name="1" icon-position="left">
@ -42,6 +46,11 @@ const dynamicFormRef = ref()
const activeNames = ref(['1', '2'])
//
/**
* 新增 addFormItem 对应的数据或交互作用范围仅限当前组件或模块
* @param {*} e 触发操作的浏览器事件
* @param {*} type 目标类型
*/
const addFormItem = (e, type) => {
e.stopPropagation()
const obj = {
@ -56,11 +65,20 @@ const addFormItem = (e, type) => {
}
//
/**
* 处理 handleDelete 对应的数据或交互作用范围仅限当前组件或模块
* @param {*} index 目标项索引
*/
const handleDelete = (index) => {
formData.nodeParams.splice(index, 1)
}
// FormItemRecursive emit
/**
* 删除 deleteTopLevelItem 对应的数据或交互作用范围仅限当前组件或模块
* @param {*} fullPath 字段完整路径
* @param {*} propPath 字段校验路径
*/
const deleteTopLevelItem = (fullPath, propPath) => {
let currentLevel = formData[propPath]
for (let i = 0; i < fullPath.length - 1; i++) {
@ -71,6 +89,9 @@ const deleteTopLevelItem = (fullPath, propPath) => {
}
//
/**
* 初始化 initData 对应的数据或交互作用范围仅限当前组件或模块
*/
const initData = () => {
const inputParams = JSON.parse(JSON.stringify(props.data.properties.inputParams || []))
formData.inputParams = inputParams;
@ -85,6 +106,9 @@ watch(
)
//
/**
* 校验 validateAndSave 对应的数据或交互作用范围仅限当前组件或模块
*/
const validateAndSave = async () => {
let inputValid = true
if (dynamicFormRef.value) {
@ -180,4 +204,4 @@ defineExpose({ validateAndSave })
cursor: pointer;
}
}
</style>
</style>

View File

@ -1,10 +1,24 @@
/**
* 文件说明参数引用组合逻辑维护可引用字段和级联选择状态
* 作用范围仅服务于流程设计器模块
*/
import { ref } from 'vue'
import { getInput } from '@/utils/flow'
/**
* 处理 useQuote 对应的数据或交互作用范围仅限当前组件或模块
* @param {*} nodeId 流程节点 ID
*/
export function useQuote(nodeId) {
const quoteOptions = ref([])
const cascaderRefs = ref([])
/**
* 处理 handleTypeChange 对应的数据或交互作用范围仅限当前组件或模块
* @param {*} index 目标项索引
* @param {*} formData 参数表单数据
* @param {*} type 目标类型
*/
const handleTypeChange = (index, formData, type = 'default') => {
// 根据节点类型处理不同数据源
// 这里简化为通用逻辑,具体实现需根据实际数据结构调整
@ -17,6 +31,13 @@ export function useQuote(nodeId) {
}
}
/**
* 处理 cascaderChange 对应的数据或交互作用范围仅限当前组件或模块
* @param {*} value 待处理的新值
* @param {*} index 目标项索引
* @param {*} formData 参数表单数据
* @param {*} field 调用方传入的 field 参数
*/
const cascaderChange = (value, index, formData, field = 'nodeParams') => {
const selectedOptions = cascaderRefs.value[index]?.getCheckedNodes(true)
if (selectedOptions && selectedOptions.length) {
@ -25,6 +46,14 @@ export function useQuote(nodeId) {
}
}
/**
* 处理 visibleChange 对应的数据或交互作用范围仅限当前组件或模块
* @param {*} visible 是否可见
* @param {*} index 目标项索引
* @param {*} currentQuote 调用方传入的 currentQuote 参数
* @param {*} formData 参数表单数据
* @param {*} field 调用方传入的 field 参数
*/
const visibleChange = (visible, index, currentQuote, formData, field = 'nodeParams') => {
if (visible) {
const option = getInput(nodeId)
@ -50,4 +79,4 @@ export function useQuote(nodeId) {
cascaderChange,
visibleChange
}
}
}

View File

@ -1,9 +1,18 @@
/**
* 文件说明流程事件订阅组合逻辑并在卸载时释放监听器
* 作用范围仅服务于流程设计器模块
*/
import { onUnmounted } from "vue";
import { emitter } from "@/utils/eventBus";
export const useFlowEvents = () => {
const listeners = [];
/**
* 订阅 onFlowEvent 对应的数据或交互作用范围仅限当前组件或模块
* @param {*} type 目标类型
* @param {*} handler 事件处理函数
*/
const onFlowEvent = (type, handler) => {
emitter.on(type, handler);
listeners.push([type, handler]);

View File

@ -1,3 +1,7 @@
/**
* 文件说明定义流程设计器可用的节点表单字段及下拉选项等静态配置
* 作用范围仅服务于流程设计器模块
*/
import cameraSvg from './icon/camera.svg'
import videoSvg from './icon/video.svg'
import loopSvg from './icon/loop.svg'
@ -12,6 +16,9 @@ import audioSvg from './icon/audio.svg'
import touchSvg from './icon/touch.svg'
import expressionSvg from './icon/expression.svg'
/**
* 处理与 http method options 相关的状态或数据该影响范围仅限当前模块
*/
const httpMethodOptions = () => {
return [{
value: 'POST',
@ -28,6 +35,9 @@ const httpMethodOptions = () => {
}]
}
/**
* 处理与 express options 相关的状态或数据该影响范围仅限当前模块
*/
const expressOptions = () => {
return [{
value: 1,
@ -50,6 +60,9 @@ const expressOptions = () => {
}]
}
/**
* 处理与 task type options 相关的状态或数据该影响范围仅限当前模块
*/
const taskTypeOptions = () => {
return [{
value: 'voice',
@ -60,6 +73,9 @@ const taskTypeOptions = () => {
}]
}
/**
* 处理与 voice options 相关的状态或数据该影响范围仅限当前模块
*/
const voiceOptions = () => {
return [{
value: 'x4_xiaoyan',
@ -67,6 +83,9 @@ const voiceOptions = () => {
}]
}
/**
* 处理与 inspection event types 相关的状态或数据该影响范围仅限当前模块
*/
const inspectionEventTypes = () => {
return [{
value: 'No-Glove',
@ -245,6 +264,10 @@ export const collapseList = [
{ name: "input", type: "input", input: "", required: true },
{ name: "code", type: "input", input: `
// 方法定义不能修改
/**
* 处理与 r 相关的状态或数据该影响范围仅限当前模块
* @param {*} params 待处理的参数集合
*/
function handler(params) {
// 返回值是一个可序列化成 json 的 dict 或 object
const result ={

View File

@ -1,3 +1,7 @@
<!--
* 文件说明流程设计器主页面负责编辑校验试运行执行跟踪和快捷操作
* 作用范围仅服务于流程设计器模块
-->
<template>
<div class="page">
<el-container class="page__container">
@ -314,10 +318,18 @@ const defaultEdgeOptions = {
style: { stroke: "#ff6b35", strokeWidth: 2 },
};
/**
* 处理 dragover 对应的数据或交互作用范围仅限当前组件或模块
* @param {*} e 触发操作的浏览器事件
*/
const dragover = (e) => {
e.preventDefault();
};
/**
* 处理 drop 对应的数据或交互作用范围仅限当前组件或模块
* @param {*} e 触发操作的浏览器事件
*/
const drop = (e) => {
let node = e.dataTransfer.getData("flowNode");
if (!node) return;
@ -349,6 +361,10 @@ const drop = (e) => {
}
};
/**
* 处理 handleConnect 对应的数据或交互作用范围仅限当前组件或模块
* @param {*} connection 连接信息
*/
const handleConnect = (connection) => {
if (!connection.source || !connection.target) return;
const edge = lf.addEdge({
@ -361,6 +377,10 @@ const handleConnect = (connection) => {
lf.emit("edge:add", { data: edge });
};
/**
* 处理 handleNodeDoubleClick 对应的数据或交互作用范围仅限当前组件或模块
* @param {*} { node } 调用方传入的 { node } 参数
*/
const handleNodeDoubleClick = ({ node }) => {
const data = lf.getGraphData().nodes.find((item) => item.id === node.id);
if (!data || ["selectArea", "branch", "stopLoop", "subStart", "subEnd", "end"].includes(data.type)) return;
@ -368,6 +388,10 @@ const handleNodeDoubleClick = ({ node }) => {
paramsDrawerData.value = data;
};
/**
* 查找 findLoopAtPoint 对应的数据或交互作用范围仅限当前组件或模块
* @param {*} point 画布坐标
*/
const findLoopAtPoint = (point) => {
return (vueFlow.getNodes.value || []).find((candidate) => {
if (candidate.data?.flowType !== "loop") return false;
@ -381,6 +405,10 @@ const findLoopAtPoint = (point) => {
});
};
/**
* 查找 findContainingLoop 对应的数据或交互作用范围仅限当前组件或模块
* @param {*} node 当前流程节点
*/
const findContainingLoop = (node) => {
const position = node.computedPosition || node.position;
const width = Number(node.dimensions?.width || node.data?.size?.width || 0);
@ -388,12 +416,21 @@ const findContainingLoop = (node) => {
return findLoopAtPoint({ x: position.x + width / 2, y: position.y + height / 2 });
};
/**
* 处理 eventClientPoint 对应的数据或交互作用范围仅限当前组件或模块
* @param {*} event 触发操作的浏览器事件
*/
const eventClientPoint = (event) => {
const pointer = event?.touches?.[0] || event?.changedTouches?.[0] || event;
if (!Number.isFinite(pointer?.clientX) || !Number.isFinite(pointer?.clientY)) return null;
return { x: pointer.clientX, y: pointer.clientY };
};
/**
* 处理 handleNodeDragStart 对应的数据或交互作用范围仅限当前组件或模块
* @param {*} { node 调用方传入的 { node 参数
* @param {*} event } 调用方传入的 event } 参数
*/
const handleNodeDragStart = ({ node, event }) => {
const drag = lf.prepareLoopChildDrag(node.id);
const clientPoint = eventClientPoint(event);
@ -405,6 +442,11 @@ const handleNodeDragStart = ({ node, event }) => {
});
};
/**
* 更新 updateLoopChildDrag 对应的数据或交互作用范围仅限当前组件或模块
* @param {*} { node 调用方传入的 { node 参数
* @param {*} event } 调用方传入的 event } 参数
*/
const updateLoopChildDrag = ({ node, event }) => {
const drag = loopChildDragContexts.get(node.id);
const clientPoint = eventClientPoint(event);
@ -416,8 +458,16 @@ const updateLoopChildDrag = ({ node, event }) => {
});
};
/**
* 处理 handleNodeDrag 对应的数据或交互作用范围仅限当前组件或模块
* @param {*} payload 事件数据
*/
const handleNodeDrag = (payload) => updateLoopChildDrag(payload);
/**
* 处理 handleNodeDragStop 对应的数据或交互作用范围仅限当前组件或模块
* @param {*} payload 事件数据
*/
const handleNodeDragStop = (payload) => {
const { node } = payload;
updateLoopChildDrag(payload);
@ -435,21 +485,37 @@ const handleNodeDragStop = (payload) => {
lf.markChanged();
};
/**
* 处理 handleNodesChange 对应的数据或交互作用范围仅限当前组件或模块
* @param {*} changes 变更集合
*/
const handleNodesChange = (changes) => {
if (changes.some((change) => ["add", "remove"].includes(change.type) || (change.type === "position" && change.dragging === false))) {
lf.markChanged();
}
};
/**
* 处理 handleEdgesChange 对应的数据或交互作用范围仅限当前组件或模块
* @param {*} changes 变更集合
*/
const handleEdgesChange = (changes) => {
if (changes.some((change) => ["add", "remove"].includes(change.type))) lf.markChanged();
};
/**
* 处理 handleEdgeClick 对应的数据或交互作用范围仅限当前组件或模块
* @param {*} { event 调用方传入的 { event 参数
* @param {*} edge } 调用方传入的 edge } 参数
*/
const handleEdgeClick = ({ event, edge }) => {
event?.stopPropagation?.();
lf.selectElementById(edge.id);
};
/**
* 处理 handlePaneClick 对应的数据或交互作用范围仅限当前组件或模块
*/
const handlePaneClick = () => lf.clearSelectElements();
const flowStore = useFlowStore();
@ -507,6 +573,9 @@ const testRun = async () => {
};
//
/**
* 处理 flowPauseFn 对应的数据或交互作用范围仅限当前组件或模块
*/
const flowPauseFn = async () => {
const res = await flowPause(instId.value);
if (res.code === 200) {
@ -520,6 +589,9 @@ const hasRunResult = ref(false);
let flowPollTimer = null;
let flowDisposed = false;
/**
* 清理 clearFlowPollTimer 对应的数据或交互作用范围仅限当前组件或模块
*/
const clearFlowPollTimer = () => {
if (flowPollTimer !== null) {
window.clearTimeout(flowPollTimer);
@ -527,6 +599,12 @@ const clearFlowPollTimer = () => {
}
};
/**
* 调度 scheduleFlowView 对应的数据或交互作用范围仅限当前组件或模块
* @param {*} currentInstId 当前流程实例 ID
* @param {*} taskId 任务 ID
* @param {*} delay 延迟毫秒数
*/
const scheduleFlowView = (currentInstId, taskId = null, delay = 2000) => {
clearFlowPollTimer();
flowPollTimer = window.setTimeout(() => {
@ -535,6 +613,9 @@ const scheduleFlowView = (currentInstId, taskId = null, delay = 2000) => {
}, delay);
};
/**
* 处理 flowResumeFn 对应的数据或交互作用范围仅限当前组件或模块
*/
const flowResumeFn = async () => {
const res = await flowResume(instId.value);
if (res.code === 200) {
@ -545,6 +626,9 @@ const flowResumeFn = async () => {
}
};
/**
* 处理 flowStopFn 对应的数据或交互作用范围仅限当前组件或模块
*/
const flowStopFn = async () => {
const res = await flowStop(instId.value);
if (res.code === 200) {
@ -593,6 +677,8 @@ const hasSameEdge = (edge, ignoredId) => lf.getGraphData().edges.some((item) =>
));
const validEdge = (data) => {
// 线
// 线
const sourceModel = lf.getNodeModelById(data.sourceNodeId);
const targetModel = lf.getNodeModelById(data.targetNodeId);
if (!sourceModel || !targetModel) {
@ -664,6 +750,8 @@ const changeState = (value) => {
};
const loopFlowView = async (instId, taskId = null) => {
// 使 ID
//
const params = {
itemId: flowInfoData.value.itemId,
instId,
@ -1009,6 +1097,10 @@ const handleSingleNodeRobotChange = async (robotId) => {
}
};
/**
* 处理 stationOptions 对应的数据或交互作用范围仅限当前组件或模块
* @param {*} stations 站点集合
*/
const stationOptions = (stations) => (Array.isArray(stations) ? stations : [])
.filter((station) => station?.id !== undefined && station?.id !== null && String(station.id).trim())
.map((station) => {
@ -1017,6 +1109,9 @@ const stationOptions = (stations) => (Array.isArray(stations) ? stations : [])
return { value: id, label: description ? `${id} - ${description}` : id };
});
/**
* 加载 loadSingleNodeStations 对应的数据或交互作用范围仅限当前组件或模块
*/
const loadSingleNodeStations = async () => {
stationLoading.value = true;
try {
@ -1040,6 +1135,9 @@ const loadSingleNodeStations = async () => {
}
};
/**
* 加载 loadSingleNodeRobots 对应的数据或交互作用范围仅限当前组件或模块
*/
const loadSingleNodeRobots = async () => {
const response = await getRobotList({ pageNum: 1, pageSize: 1000, connectStatus: '1' });
singleNodeRobotOptions.value = (response.rows || []).map(item => ({
@ -1048,6 +1146,10 @@ const loadSingleNodeRobots = async () => {
}));
};
/**
* 处理 singleNodeExecution 对应的数据或交互作用范围仅限当前组件或模块
* @param {*} e 触发操作的浏览器事件
*/
const singleNodeExecution = async (e) => {
rememberSingleNodeRobot();
const { name, nodeType, nodeParams, action, nodeId } = e.detail;
@ -1090,6 +1192,9 @@ const singleNodeExecution = async (e) => {
}
};
/**
* 执行 execute 对应的数据或交互作用范围仅限当前组件或模块
*/
const execute = async () => {
if (nodeAction.value === "AGV_MOVE_TO_STATION") {
const stationParam = formData.nodeParams.find((item) => item.name === "stationId");
@ -1124,6 +1229,9 @@ const execute = async () => {
const showParamsDrawer = ref(false);
const paramsDrawerData = ref({});
/**
* 清理 clearRun 对应的数据或交互作用范围仅限当前组件或模块
*/
const clearRun = () => {
const { nodes } = lf.getGraphData();
nodes.forEach((item) => {
@ -1155,10 +1263,18 @@ const EDITABLE_SELECTOR = [
"[role='dialog']",
].join(",");
/**
* 判断 isEditableElement 对应的数据或交互作用范围仅限当前组件或模块
* @param {*} element 调用方传入的 element 参数
*/
const isEditableElement = (element) => (
element instanceof Element && Boolean(element.closest(EDITABLE_SELECTOR))
);
/**
* 判断 isTextEditing 对应的数据或交互作用范围仅限当前组件或模块
* @param {*} event 触发操作的浏览器事件
*/
const isTextEditing = (event) => {
const eventPath = typeof event.composedPath === "function" ? event.composedPath() : [];
if (eventPath.some(isEditableElement)) return true;
@ -1166,13 +1282,23 @@ const isTextEditing = (event) => {
return Boolean(window.getSelection()?.toString());
};
/**
* 复制 copySelectedNodes 对应的数据或交互作用范围仅限当前组件或模块
* @param {*} event 触发操作的浏览器事件
*/
const copySelectedNodes = (event) => {
// 线
// ID
const selectedNodes = lf.getSelectElements().nodes;
if (!selectedNodes.length) return;
const graph = lf.getGraphData();
const nodeById = new Map(graph.nodes.map((node) => [node.id, node]));
const selectedIds = new Set(selectedNodes.map((node) => node.id));
/**
* 新增 addChildren 对应的数据或交互作用范围仅限当前组件或模块
* @param {*} nodeId 流程节点 ID
*/
const addChildren = (nodeId) => {
const node = nodeById.get(nodeId);
(node?.children || []).forEach((childId) => {
@ -1200,11 +1326,23 @@ const copySelectedNodes = (event) => {
pasteCount = 0;
};
/**
* 重映射 remapNodeAnchorId 对应的数据或交互作用范围仅限当前组件或模块
* @param {*} anchorId 锚点 ID
* @param {*} oldNodeId 原节点 ID
* @param {*} newNodeId 新节点 ID
*/
const remapNodeAnchorId = (anchorId, oldNodeId, newNodeId) => {
if (!anchorId || !String(anchorId).startsWith(`${oldNodeId}_`)) return anchorId;
return `${newNodeId}${String(anchorId).slice(oldNodeId.length)}`;
};
/**
* 重映射 remapCopiedNodeReferences 对应的数据或交互作用范围仅限当前组件或模块
* @param {*} value 待处理的新值
* @param {*} idMap 节点 ID 映射
* @param {*} parentKey 递归父键名
*/
const remapCopiedNodeReferences = (value, idMap, parentKey = "") => {
if (Array.isArray(value)) {
const result = value.map((item) => remapCopiedNodeReferences(item, idMap));
@ -1219,7 +1357,13 @@ const remapCopiedNodeReferences = (value, idMap, parentKey = "") => {
)));
};
/**
* 粘贴 pasteCopiedNodes 对应的数据或交互作用范围仅限当前组件或模块
* @param {*} event 触发操作的浏览器事件
*/
const pasteCopiedNodes = (event) => {
// ID ID 线
// ID
if (!copiedGraph.nodes.length || flowStore.disableForm) return;
event.preventDefault();
@ -1297,6 +1441,10 @@ const pasteCopiedNodes = (event) => {
});
};
/**
* 处理 handleKeyboardShortcut 对应的数据或交互作用范围仅限当前组件或模块
* @param {*} event 触发操作的浏览器事件
*/
const handleKeyboardShortcut = (event) => {
if (isTextEditing(event)) return;

View File

@ -1,3 +1,7 @@
<!--
* 文件说明流程正常结束节点
* 作用范围仅服务于流程设计器模块
-->
<template>
<div class="node__container">
<div class="terminal-node">

View File

@ -1,3 +1,7 @@
<!--
* 文件说明节点分组容器并展示成员数量
* 作用范围仅服务于流程设计器模块
-->
<template>
<div class="group__container">
<NodeTitle
@ -33,6 +37,10 @@ const props = defineProps({
const emits = defineEmits(["contentChange"]);
/**
* 设置 setNodeName 对应的数据或交互作用范围仅限当前组件或模块
* @param {*} name 名称或事件名称
*/
const setNodeName = (name) => {
const properties = lf.getProperties(props.model.id);
lf.setProperties(props.model.id, { ...properties, name });

View File

@ -1,3 +1,7 @@
<!--
* 文件说明通用服务节点并响应运行状态
* 作用范围仅服务于流程设计器模块
-->
<template>
<div class="node__container" :class="props.model.id">
<NodeState :state="nodeOperatingStatus" :runtimes="runtimes" ref="nodeStateRef">
@ -63,6 +67,10 @@ const props = defineProps({
const emits = defineEmits(["contentChange"]);
/**
* 设置 setNodeName 对应的数据或交互作用范围仅限当前组件或模块
* @param {*} name 名称或事件名称
*/
const setNodeName = (name) => {
const properties = lf.getProperties(props.model.id);
lf.setProperties(props.model.id, {
@ -80,6 +88,9 @@ const errorInfoData = ref('');
const nodeStateRef = ref(null);
/**
* 关闭 closePopover 对应的数据或交互作用范围仅限当前组件或模块
*/
const closePopover = () => {
if (nodeStateRef.value) {
nodeStateRef.value.closePopover();

View File

@ -1,3 +1,7 @@
<!--
* 文件说明流程入口节点并响应运行状态
* 作用范围仅服务于流程设计器模块
-->
<template>
<div class="node__container" :class="props.model.id">
<keep-alive>
@ -42,6 +46,9 @@ const outputJsonData = ref({});
const nodeStateRef = ref(null);
/**
* 关闭 closePopover 对应的数据或交互作用范围仅限当前组件或模块
*/
const closePopover = () => {
if (nodeStateRef.value) {
nodeStateRef.value.closePopover();

View File

@ -1,3 +1,7 @@
<!--
* 文件说明代码执行节点并展示运行状态
* 作用范围仅服务于流程设计器模块
-->
<template>
<div class="node__container" :class="props.model.id">
<NodeState :state="nodeOperatingStatus" :runtimes="runtimes" ref="nodeStateRef">
@ -61,6 +65,10 @@ const props = defineProps({
const emits = defineEmits(["contentChange"]);
/**
* 设置 setNodeName 对应的数据或交互作用范围仅限当前组件或模块
* @param {*} name 名称或事件名称
*/
const setNodeName = (name) => {
const properties = lf.getProperties(props.model.id);
lf.setProperties(props.model.id, {
@ -78,6 +86,9 @@ const errorInfoData = ref('');
const nodeStateRef = ref(null);
/**
* 关闭 closePopover 对应的数据或交互作用范围仅限当前组件或模块
*/
const closePopover = () => {
if (nodeStateRef.value) {
nodeStateRef.value.closePopover();

View File

@ -1,3 +1,7 @@
<!--
* 文件说明当前循环上下文节点并展示运行状态
* 作用范围仅服务于流程设计器模块
-->
<template>
<div class="node__container" :class="props.model.id">
<NodeState :state="nodeOperatingStatus" :runtimes="runtimes" ref="nodeStateRef">
@ -47,6 +51,10 @@ const props = defineProps({
const emits = defineEmits(["contentChange"]);
/**
* 设置 setNodeName 对应的数据或交互作用范围仅限当前组件或模块
* @param {*} name 名称或事件名称
*/
const setNodeName = (name) => {
const properties = lf.getProperties(props.model.id);
lf.setProperties(props.model.id, {
@ -63,6 +71,9 @@ const outputJsonData = ref({});
const nodeStateRef = ref(null);
/**
* 关闭 closePopover 对应的数据或交互作用范围仅限当前组件或模块
*/
const closePopover = () => {
if (nodeStateRef.value) {
nodeStateRef.value.closePopover();

View File

@ -1,3 +1,7 @@
<!--
* 文件说明通用设备执行节点并展示运行状态
* 作用范围仅服务于流程设计器模块
-->
<template>
<div class="node__container" :class="props.model.id">
<NodeState :state="nodeOperatingStatus" :runtimes="runtimes" ref="nodeStateRef">
@ -61,6 +65,10 @@ const props = defineProps({
const emits = defineEmits(["contentChange"]);
/**
* 设置 setNodeName 对应的数据或交互作用范围仅限当前组件或模块
* @param {*} name 名称或事件名称
*/
const setNodeName = (name) => {
const properties = lf.getProperties(props.model.id);
lf.setProperties(props.model.id, {
@ -78,6 +86,9 @@ const errorInfoData = ref('');
const nodeStateRef = ref(null);
/**
* 关闭 closePopover 对应的数据或交互作用范围仅限当前组件或模块
*/
const closePopover = () => {
if (nodeStateRef.value) {
nodeStateRef.value.closePopover();

View File

@ -1,3 +1,7 @@
<!--
* 文件说明HTTP 请求执行节点并展示运行状态
* 作用范围仅服务于流程设计器模块
-->
<template>
<div class="node__container" :class="props.model.id">
<NodeState :state="nodeOperatingStatus" :runtimes="runtimes" ref="nodeStateRef">
@ -62,6 +66,10 @@ const props = defineProps({
const emits = defineEmits(["contentChange"]);
/**
* 设置 setNodeName 对应的数据或交互作用范围仅限当前组件或模块
* @param {*} name 名称或事件名称
*/
const setNodeName = (name) => {
const properties = lf.getProperties(props.model.id);
lf.setProperties(props.model.id, {
@ -79,6 +87,9 @@ const errorInfoData = ref('');
const nodeStateRef = ref(null);
/**
* 关闭 closePopover 对应的数据或交互作用范围仅限当前组件或模块
*/
const closePopover = () => {
if (nodeStateRef.value) {
nodeStateRef.value.closePopover();

View File

@ -1,3 +1,7 @@
<!--
* 文件说明循环容器节点管理循环体节点拖入和布局
* 作用范围仅服务于流程设计器模块
-->
<template>
<div
class="group__container"
@ -46,15 +50,27 @@ const emits = defineEmits(["bindRef", "addToGroup", "contentChange"]);
const isDragOver = ref(false);
const actualChildCount = computed(() => props.childCount ?? props.model?.children?.size ?? 0);
/**
* 处理 handleDragover 对应的数据或交互作用范围仅限当前组件或模块
* @param {*} e 触发操作的浏览器事件
*/
const handleDragover = (e) => {
e.preventDefault();
isDragOver.value = true;
};
/**
* 处理 handleDragleave 对应的数据或交互作用范围仅限当前组件或模块
* @param {*} e 触发操作的浏览器事件
*/
const handleDragleave = (e) => {
if (!e.currentTarget.contains(e.relatedTarget)) isDragOver.value = false;
};
/**
* 处理 handleDrop 对应的数据或交互作用范围仅限当前组件或模块
* @param {*} e 触发操作的浏览器事件
*/
const handleDrop = (e) => {
e.preventDefault();
e.stopPropagation();
@ -84,6 +100,10 @@ const handleDrop = (e) => {
lf.addToGroup(props.model.id, node.id, point.canvasOverlayPosition);
};
/**
* 设置 setNodeName 对应的数据或交互作用范围仅限当前组件或模块
* @param {*} name 名称或事件名称
*/
const setNodeName = (name) => {
const properties = lf.getProperties(props.model.id);
lf.setProperties(props.model.id, {

View File

@ -1,3 +1,7 @@
<!--
* 文件说明识别执行节点并展示运行状态
* 作用范围仅服务于流程设计器模块
-->
<template>
<div class="node__container" :class="props.model.id">
<NodeState :state="nodeOperatingStatus" :runtimes="runtimes" ref="nodeStateRef">
@ -48,6 +52,10 @@ const props = defineProps({
const emits = defineEmits(["contentChange"]);
/**
* 设置 setNodeName 对应的数据或交互作用范围仅限当前组件或模块
* @param {*} name 名称或事件名称
*/
const setNodeName = (name) => {
const properties = lf.getProperties(props.model.id);
lf.setProperties(props.model.id, {
@ -64,6 +72,9 @@ const outputJsonData = ref({});
const nodeStateRef = ref(null);
/**
* 关闭 closePopover 对应的数据或交互作用范围仅限当前组件或模块
*/
const closePopover = () => {
if (nodeStateRef.value) {
nodeStateRef.value.closePopover();

View File

@ -1,3 +1,7 @@
<!--
* 文件说明智能体执行节点并展示运行状态
* 作用范围仅服务于流程设计器模块
-->
<template>
<div class="node__container" :class="props.model.id">
<NodeState :state="nodeOperatingStatus" :runtimes="runtimes" ref="nodeStateRef">
@ -48,6 +52,10 @@ const props = defineProps({
const emits = defineEmits(["contentChange"]);
/**
* 设置 setNodeName 对应的数据或交互作用范围仅限当前组件或模块
* @param {*} name 名称或事件名称
*/
const setNodeName = (name) => {
const properties = lf.getProperties(props.model.id);
lf.setProperties(props.model.id, {
@ -64,6 +72,9 @@ const outputJsonData = ref({});
const nodeStateRef = ref(null);
/**
* 关闭 closePopover 对应的数据或交互作用范围仅限当前组件或模块
*/
const closePopover = () => {
if (nodeStateRef.value) {
nodeStateRef.value.closePopover();

View File

@ -1,3 +1,7 @@
<!--
* 文件说明延时等待节点并展示运行状态
* 作用范围仅服务于流程设计器模块
-->
<template>
<div class="node__container" :class="props.model.id">
<NodeState :state="nodeOperatingStatus" :runtimes="runtimes" ref="nodeStateRef">
@ -47,6 +51,10 @@ const props = defineProps({
const emits = defineEmits(["contentChange"]);
/**
* 设置 setNodeName 对应的数据或交互作用范围仅限当前组件或模块
* @param {*} name 名称或事件名称
*/
const setNodeName = (name) => {
const properties = lf.getProperties(props.model.id);
lf.setProperties(props.model.id, {
@ -63,6 +71,9 @@ const outputJsonData = ref({});
const nodeStateRef = ref(null);
/**
* 关闭 closePopover 对应的数据或交互作用范围仅限当前组件或模块
*/
const closePopover = () => {
if (nodeStateRef.value) {
nodeStateRef.value.closePopover();

View File

@ -1,3 +1,7 @@
<!--
* 文件说明终止循环节点用于结束当前循环
* 作用范围仅服务于流程设计器模块
-->
<template>
<div class="node__container" >
<!-- 状态栏 -->
@ -16,6 +20,10 @@ const props = defineProps({
text: String,
});
/**
* 设置 setNodeName 对应的数据或交互作用范围仅限当前组件或模块
* @param {*} name 名称或事件名称
*/
const setNodeName = (name) => {
const properties = lf.getProperties(props.model.id);
lf.setProperties(props.model.id, {
@ -194,4 +202,4 @@ const setNodeName = (name) => {
}
}
}
</style>
</style>

View File

@ -1,3 +1,7 @@
<!--
* 文件说明循环体单次执行出口节点
* 作用范围仅服务于流程设计器模块
-->
<template>
<div class="node__container">
<div class="terminal-node">

View File

@ -1,3 +1,7 @@
<!--
* 文件说明循环体单次执行入口节点
* 作用范围仅服务于流程设计器模块
-->
<template>
<div class="node__container">
<div class="terminal-node">

View File

@ -1,3 +1,7 @@
<!--
* 文件说明条件分支节点配置条件组和动态分支锚点
* 作用范围仅服务于流程设计器模块
-->
<template>
<div class="node__container" ref="switchRef" @mouseleave="setNodeProperties">
<NodeState :state="nodeOperatingStatus" :runtimes="runtimes" ref="nodeStateRef">
@ -299,10 +303,18 @@ const formData = reactive({
nodeParams: props.properties?.conditions || [],
});
/**
* 新增 addFormItem 对应的数据或交互作用范围仅限当前组件或模块
*/
const addFormItem = () => {
addCondition(0);
};
/**
* 删除 deleteFormItem 对应的数据或交互作用范围仅限当前组件或模块
* @param {*} id 目标元素 ID
* @param {*} index 目标项索引
*/
const deleteFormItem = (id, index) => {
if (index === 0) {
return;
@ -316,6 +328,11 @@ const deleteFormItem = (id, index) => {
};
const quoteOptions = ref([]);
/**
* 处理 handleTypeChange 对应的数据或交互作用范围仅限当前组件或模块
* @param {*} pIndex 条件组索引
* @param {*} index 目标项索引
*/
const handleTypeChange = (pIndex, index) => {
if (formData.nodeParams[pIndex].list[index].type === "input") {
formData.nodeParams[pIndex].list[index].quote = "";
@ -328,6 +345,10 @@ const handleTypeChange = (pIndex, index) => {
}
};
/**
* 新增 addCondition 对应的数据或交互作用范围仅限当前组件或模块
* @param {*} height 锚点纵向位置
*/
const addCondition = (height) => {
const id = randomUUID().replace(/-/g, "");
formData.nodeParams.push({
@ -351,6 +372,11 @@ const addCondition = (height) => {
});
};
/**
* 新增 addListItem 对应的数据或交互作用范围仅限当前组件或模块
* @param {*} pIndex 条件组索引
* @param {*} cIndex 条件项索引
*/
const addListItem = (pIndex, cIndex) => {
formData.nodeParams[pIndex].list.push({
name: "",
@ -365,6 +391,11 @@ const addListItem = (pIndex, cIndex) => {
});
};
/**
* 移除 removeListItem 对应的数据或交互作用范围仅限当前组件或模块
* @param {*} pIndex 条件组索引
* @param {*} cIndex 条件项索引
*/
const removeListItem = (pIndex, cIndex) => {
if (cIndex === 0) {
return;
@ -378,7 +409,12 @@ const removeListItem = (pIndex, cIndex) => {
let anchorSyncFrame = null;
let resizeObserver = null;
/**
* 同步 syncAnchorPositions 对应的数据或交互作用范围仅限当前组件或模块
*/
const syncAnchorPositions = () => {
//
//
const root = switchRef.value;
if (!root) return;
const rootRect = root.getBoundingClientRect();
@ -393,6 +429,9 @@ const syncAnchorPositions = () => {
emits("syncAnchors", positions);
};
/**
* 调度 scheduleAnchorSync 对应的数据或交互作用范围仅限当前组件或模块
*/
const scheduleAnchorSync = () => {
if (anchorSyncFrame !== null) cancelAnimationFrame(anchorSyncFrame);
anchorSyncFrame = requestAnimationFrame(() => {
@ -401,6 +440,9 @@ const scheduleAnchorSync = () => {
});
};
/**
* 设置 setNodeProperties 对应的数据或交互作用范围仅限当前组件或模块
*/
const setNodeProperties = async () => {
try {
const valid = await dynamicForm.value.validate();
@ -418,6 +460,10 @@ const setNodeProperties = async () => {
}
};
/**
* 设置 setNodeName 对应的数据或交互作用范围仅限当前组件或模块
* @param {*} name 名称或事件名称
*/
const setNodeName = (name) => {
const properties = lf.getProperties(props.model.id);
lf.setProperties(props.model.id, {
@ -427,6 +473,13 @@ const setNodeName = (name) => {
emits("contentChange");
}
/**
* 处理 visibleChange 对应的数据或交互作用范围仅限当前组件或模块
* @param {*} value 待处理的新值
* @param {*} type 目标类型
* @param {*} index 目标项索引
* @param {*} pIndex 条件组索引
*/
const visibleChange = async (value, type, index, pIndex) => {
if (value) {
const option = getInput(props.model.id);
@ -468,6 +521,9 @@ watch(
const nodeStateRef = ref(null);
/**
* 关闭 closePopover 对应的数据或交互作用范围仅限当前组件或模块
*/
const closePopover = () => {
if (nodeStateRef.value) {
nodeStateRef.value.closePopover();
@ -475,6 +531,10 @@ const closePopover = () => {
}
const isEditing = ref(false)
/**
* 处理 handleInputFocus 对应的数据或交互作用范围仅限当前组件或模块
* @param {*} e 触发操作的浏览器事件
*/
const handleInputFocus = (e) => {
//
e.stopPropagation()
@ -482,6 +542,10 @@ const handleInputFocus = (e) => {
}
/**
* 处理 handleInputKeydown 对应的数据或交互作用范围仅限当前组件或模块
* @param {*} e 触发操作的浏览器事件
*/
const handleInputKeydown = (e) => {
//
e.stopPropagation();

View File

@ -1,3 +1,7 @@
<!--
* 文件说明Vue Flow 连线渲染组件负责路径展示及选中状态下的删除操作
* 作用范围仅服务于流程设计器模块
-->
<template>
<BaseEdge
:id="id"
@ -54,6 +58,9 @@ const deleteActionStyle = computed(() => ({
transform: `translate(-50%, -50%) translate(${pathData.value[1]}px, ${pathData.value[2]}px)`,
}));
/**
* 删除与 edge 相关的状态或数据该影响范围仅限当前组件
*/
const deleteEdge = () => {
window.lf?.deleteEdge(props.id);
window.lf?.clearSelectElements();

View File

@ -1,3 +1,7 @@
<!--
* 文件说明Vue Flow 节点壳组件负责锚点连接规则分支位置及分组交互适配
* 作用范围仅服务于流程设计器模块
-->
<template>
<div
class="flow-node-shell"
@ -130,22 +134,39 @@ const branchAnchors = computed(() => {
});
const model = computed(() => window.lf?.getNodeModelById(props.id));
/**
* 获取与 loop scope 相关的状态或数据该影响范围仅限当前组件
* @param {*} nodeId 流程节点 ID
*/
const getLoopScope = (nodeId) => {
const node = vueFlow.findNode(nodeId);
const parent = node?.parentNode ? vueFlow.findNode(node.parentNode) : null;
return parent?.data?.flowType === "loop" ? parent.id : null;
};
/**
* 判断与 valid loop connection 相关的状态或数据该影响范围仅限当前组件
* @param {*} connection 待校验或创建的连接信息
*/
const isValidLoopConnection = (connection) => {
if (!connection.source || !connection.target || connection.source === connection.target) return false;
return getLoopScope(connection.source) === getLoopScope(connection.target);
};
/**
* 同步与 anchors 相关的状态或数据该影响范围仅限当前组件
* @param {*} anchors 需要同步的锚点集合
*/
const syncAnchors = (anchors) => {
window.lf?.setProperties(props.id, { ...window.lf.getProperties(props.id), anchor: anchors });
vueFlow.updateNodeInternals([props.id]);
};
/**
* 新增与 anchor 相关的状态或数据该影响范围仅限当前组件
* @param {*} height 锚点纵向位置
* @param {*} anchorId 分支锚点 ID
*/
const addAnchor = (height, anchorId) => {
const anchors = [...branchAnchors.value];
if (!anchors.some((anchor) => anchor.id === anchorId)) {
@ -154,8 +175,21 @@ const addAnchor = (height, anchorId) => {
}
};
/**
* 移除与 anchor 相关的状态或数据该影响范围仅限当前组件
* @param {*} anchorId 分支锚点 ID
*/
const removeAnchor = (anchorId) => syncAnchors(branchAnchors.value.filter((anchor) => anchor.id !== anchorId));
/**
* 更新与 anchor 相关的状态或数据该影响范围仅限当前组件
* @param {*} height 锚点纵向位置
* @param {*} anchorId 分支锚点 ID
*/
const changeAnchor = (height, anchorId) => syncAnchors(branchAnchors.value.map((anchor) => anchor.id === anchorId ? { ...anchor, height } : anchor));
/**
* 同步与 branch anchors 相关的状态或数据该影响范围仅限当前组件
* @param {*} positions 分支锚点位置集合
*/
const syncBranchAnchors = (positions = []) => {
if (!positions.length) return;
const positionMap = new Map(positions.map((item) => [item.id, Number(item.height || 0)]));
@ -176,11 +210,23 @@ const syncBranchAnchors = (positions = []) => {
if (changed) syncAnchors(anchors);
else vueFlow.updateNodeInternals([props.id]);
};
/**
* 处理与 content changed 相关的状态或数据该影响范围仅限当前组件
*/
const contentChanged = () => {
vueFlow.updateNodeInternals([props.id]);
window.lf?.markChanged();
};
/**
* 绑定与 component 相关的状态或数据该影响范围仅限当前组件
* @param {*} instance 节点组件实例
*/
const bindComponent = (instance) => window.lf?.registerComponentInstance(props.id, instance);
/**
* 新增与 to group 相关的状态或数据该影响范围仅限当前组件
* @param {*} childId 子节点 ID
* @param {*} dropCenter 拖放中心点坐标
*/
const addToGroup = (childId, dropCenter) => window.lf?.addToGroup(props.id, childId, dropCenter);
onMounted(() => bindComponent(componentRef.value));

View File

@ -1,7 +1,15 @@
/**
* 文件说明旧流程数据与 Vue Flow 状态操作 API 之间的兼容适配层
* 作用范围仅服务于流程设计器模块
*/
import { nextTick } from "vue";
import { v4 as uuid } from "uuid";
import { MarkerType } from "@vue-flow/core";
/**
* 克隆 clone 对应的数据或交互作用范围仅限画布适配层
* @param {*} value 待处理的新值
*/
const clone = (value) => JSON.parse(JSON.stringify(value ?? {}));
const GROUP_TYPES = new Set(["loop", "selectArea"]);
@ -11,6 +19,9 @@ const LOOP_BODY_INSET = { left: 24, top: 112, right: 24, bottom: 24 };
const GROUP_BODY_INSET = { left: 24, top: 120, right: 24, bottom: 24 };
const LOOP_MIN_SIZE = { width: 520, height: 360 };
/**
* 处理 loopChildExtent 对应的数据或交互作用范围仅限画布适配层
*/
const loopChildExtent = () => ({
range: "parent",
padding: [
@ -21,6 +32,9 @@ const loopChildExtent = () => ({
],
});
/**
* 编组 groupChildExtent 对应的数据或交互作用范围仅限画布适配层
*/
const groupChildExtent = () => ({
range: "parent",
padding: [
@ -31,6 +45,10 @@ const groupChildExtent = () => ({
],
});
/**
* 处理 nodeSize 对应的数据或交互作用范围仅限画布适配层
* @param {*} node 当前流程节点
*/
const nodeSize = (node) => {
const width = Number(node.properties?.width || node.width);
const height = Number(node.properties?.height || node.height);
@ -45,9 +63,21 @@ const nodeSize = (node) => {
return { width: 372, height: 180 };
};
/**
* 处理 edgeId 对应的数据或交互作用范围仅限画布适配层
*/
const edgeId = () => `edge_${uuid().replace(/-/g, "")}`;
/**
* 处理 nodeId 对应的数据或交互作用范围仅限画布适配层
*/
const nodeId = () => uuid().replace(/-/g, "");
/**
* 处理 toVueNode 对应的数据或交互作用范围仅限画布适配层
* @param {*} node 当前流程节点
* @param {*} sizeOverride 调用方传入的 sizeOverride 参数
* @param {*} positionOverride 调用方传入的 positionOverride 参数
*/
const toVueNode = (node, sizeOverride, positionOverride) => {
const normalized = clone(node);
normalized.id ||= nodeId();
@ -98,6 +128,10 @@ const toVueNode = (node, sizeOverride, positionOverride) => {
};
};
/**
* 处理 toVueEdge 对应的数据或交互作用范围仅限画布适配层
* @param {*} edge 调用方传入的 edge 参数
*/
const toVueEdge = (edge) => {
const normalized = clone(edge);
normalized.id ||= edgeId();
@ -124,7 +158,13 @@ const toVueEdge = (edge) => {
};
};
/**
* 关联 attachChildrenToGroups 对应的数据或交互作用范围仅限画布适配层
* @param {*} nodes 调用方传入的 nodes 参数
*/
const attachChildrenToGroups = (nodes) => {
// 先建立节点索引和显式父子关系,再用旧数据中的 children 补齐缺失关系。
// 最后统一换算相对坐标,避免父节点尚未就绪时提前计算造成位置漂移。
const nodeLookup = new Map(nodes.map((node) => [node.id, node]));
const declaredParents = new Map();
@ -200,9 +240,19 @@ export const createFlowFacade = (store) => {
let history = [];
let historyIndex = -1;
/**
* 获取 getNodes 对应的数据或交互作用范围仅限画布适配层
*/
const getNodes = () => store.getNodes.value || [];
/**
* 获取 getEdges 对应的数据或交互作用范围仅限画布适配层
*/
const getEdges = () => store.getEdges.value || [];
/**
* 处理 backendNode 对应的数据或交互作用范围仅限画布适配层
* @param {*} node 当前流程节点
*/
const backendNode = (node) => {
const result = clone(node.data.backend);
const size = {
@ -242,6 +292,10 @@ export const createFlowFacade = (store) => {
return result;
};
/**
* 处理 backendEdge 对应的数据或交互作用范围仅限画布适配层
* @param {*} edge 调用方传入的 edge 参数
*/
const backendEdge = (edge) => ({
...clone(edge.data?.backend),
id: edge.id,
@ -252,10 +306,18 @@ export const createFlowFacade = (store) => {
targetAnchorId: edge.targetHandle || `${edge.target}_3`,
});
/**
* 处理 emit 对应的数据或交互作用范围仅限画布适配层
* @param {*} name 名称或事件名称
* @param {*} payload 事件数据
*/
const emit = (name, payload) => {
(handlers.get(name) || []).forEach((handler) => handler(payload));
};
/**
* 处理 recordHistory 对应的数据或交互作用范围仅限画布适配层
*/
const recordHistory = () => {
if (historySuspended) return;
const graph = facade.getGraphData();
@ -267,12 +329,19 @@ export const createFlowFacade = (store) => {
historyIndex = history.length - 1;
};
/**
* 更新 changed 对应的数据或交互作用范围仅限画布适配层
*/
const changed = () => {
if (historySuspended) return;
recordHistory();
emit("history:change", { data: facade.getGraphData() });
};
/**
* 获取 getNodeRect 对应的数据或交互作用范围仅限画布适配层
* @param {*} node 当前流程节点
*/
const getNodeRect = (node) => {
const parent = node.parentNode ? store.findNode(node.parentNode) : null;
const parentPosition = parent?.position || parent?.computedPosition || { x: 0, y: 0 };
@ -287,9 +356,17 @@ export const createFlowFacade = (store) => {
};
};
/**
* 获取 getLoopLayoutState 对应的数据或交互作用范围仅限画布适配层
* @param {*} group 调用方传入的 group 参数
*/
const getLoopLayoutState = (group) => {
const current = getNodeRect(group);
const existing = loopLayoutStates.get(group.id);
/**
* 处理 if 对应的数据或交互作用范围仅限画布适配层
* @param {*} !existing 调用方传入的 !existing 参数
*/
if (!existing) {
const state = { base: { ...current }, last: { ...current } };
loopLayoutStates.set(group.id, state);
@ -298,6 +375,10 @@ export const createFlowFacade = (store) => {
const sizeChanged = Math.abs(current.width - existing.last.width) > 1
|| Math.abs(current.height - existing.last.height) > 1;
/**
* 处理 if 对应的数据或交互作用范围仅限画布适配层
* @param {*} sizeChanged 调用方传入的 sizeChanged 参数
*/
if (sizeChanged) {
existing.base = { ...current };
} else {
@ -310,6 +391,12 @@ export const createFlowFacade = (store) => {
return existing;
};
/**
* 布局 layoutLoop 对应的数据或交互作用范围仅限画布适配层
* @param {*} group 调用方传入的 group 参数
* @param {*} activeChildId 调用方传入的 activeChildId 参数
* @param {*} activePosition 调用方传入的 activePosition 参数
*/
const layoutLoop = (group, activeChildId, activePosition) => {
const state = getLoopLayoutState(group);
const children = getNodes().filter((node) => node.parentNode === group.id);
@ -365,8 +452,16 @@ export const createFlowFacade = (store) => {
return true;
};
/**
* 获取 getAnchors 对应的数据或交互作用范围仅限画布适配层
* @param {*} node 当前流程节点
*/
const getAnchors = (node) => {
const type = node.data.flowType;
/**
* 处理 if 对应的数据或交互作用范围仅限画布适配层
* @param {*} type 目标类型
*/
if (type === "branch") {
return [
{ id: `${node.id}_entry`, name: "left" },
@ -382,6 +477,10 @@ export const createFlowFacade = (store) => {
];
};
/**
* 处理 modelFor 对应的数据或交互作用范围仅限画布适配层
* @param {*} id 目标元素 ID
*/
const modelFor = (id) => {
const node = store.findNode(id);
if (!node) return null;
@ -415,31 +514,58 @@ export const createFlowFacade = (store) => {
};
const facade = {
/**
* 订阅 on 对应的数据或交互作用范围仅限画布适配层
* @param {*} name 名称或事件名称
* @param {*} handler 事件处理函数
*/
on(name, handler) {
const list = handlers.get(name) || [];
list.push(handler);
handlers.set(name, list);
return () => facade.off(name, handler);
},
/**
* 订阅 once 对应的数据或交互作用范围仅限画布适配层
* @param {*} name 名称或事件名称
* @param {*} handler 事件处理函数
*/
once(name, handler) {
/**
* 处理 wrapped 对应的数据或交互作用范围仅限画布适配层
* @param {*} payload 事件数据
*/
const wrapped = (payload) => {
facade.off(name, wrapped);
handler(payload);
};
facade.on(name, wrapped);
},
/**
* 取消订阅 off 对应的数据或交互作用范围仅限画布适配层
* @param {*} name 名称或事件名称
* @param {*} handler 事件处理函数
*/
off(name, handler) {
if (!handler) handlers.delete(name);
else handlers.set(name, (handlers.get(name) || []).filter((item) => item !== handler));
},
emit,
markChanged: changed,
/**
* 获取 getGraphData 对应的数据或交互作用范围仅限画布适配层
*/
getGraphData() {
return {
nodes: getNodes().map(backendNode),
edges: getEdges().map(backendEdge),
};
},
/**
* 渲染 render 对应的数据或交互作用范围仅限画布适配层
* @param {*} graph 调用方传入的 graph 参数
* @param {*} options 行为配置
*/
render(graph, options = {}) {
historySuspended = true;
loopLayoutStates.clear();
@ -456,6 +582,9 @@ export const createFlowFacade = (store) => {
}))
: options.nodePositions;
const data = toVueFlowData(graph, { nodeSizes, nodePositions });
/**
* 处理 finishRender 对应的数据或交互作用范围仅限画布适配层
*/
const finishRender = () => {
historySuspended = false;
if (options.resetHistory !== false) {
@ -465,6 +594,9 @@ export const createFlowFacade = (store) => {
}
nextTick(() => store.updateNodeInternals());
};
/**
* 处理 applyData 对应的数据或交互作用范围仅限画布适配层
*/
const applyData = () => {
store.setNodes(data.nodes);
store.setEdges(data.edges);
@ -482,12 +614,20 @@ export const createFlowFacade = (store) => {
applyData();
}
},
/**
* 新增 addNode 对应的数据或交互作用范围仅限画布适配层
* @param {*} node 当前流程节点
*/
addNode(node) {
const vueNode = toVueNode(node);
store.addNodes(vueNode);
changed();
return backendNode(vueNode);
},
/**
* 删除 deleteNode 对应的数据或交互作用范围仅限画布适配层
* @param {*} id 目标元素 ID
*/
deleteNode(id) {
const target = store.findNode(id);
if (target?.data?.flowType === "selectArea") {
@ -513,6 +653,10 @@ export const createFlowFacade = (store) => {
changed();
if (parentId) nextTick(() => facade.resizeGroupToChildren(parentId));
},
/**
* 新增 addEdge 对应的数据或交互作用范围仅限画布适配层
* @param {*} edge 调用方传入的 edge 参数
*/
addEdge(edge) {
const vueEdge = toVueEdge(edge);
const source = store.findNode(vueEdge.source);
@ -522,17 +666,34 @@ export const createFlowFacade = (store) => {
changed();
return backendEdge(vueEdge);
},
/**
* 删除 deleteEdge 对应的数据或交互作用范围仅限画布适配层
* @param {*} id 目标元素 ID
*/
deleteEdge(id) {
store.removeEdges(id);
changed();
},
/**
* 删除 deleteEdgeByNodeId 对应的数据或交互作用范围仅限画布适配层
* @param {*} id 目标元素 ID
*/
deleteEdgeByNodeId(id) {
store.removeEdges(getEdges().filter((edge) => edge.source === id || edge.target === id));
changed();
},
/**
* 获取 getProperties 对应的数据或交互作用范围仅限画布适配层
* @param {*} id 目标元素 ID
*/
getProperties(id) {
return clone(store.findNode(id)?.data?.properties || {});
},
/**
* 设置 setProperties 对应的数据或交互作用范围仅限画布适配层
* @param {*} id 目标元素 ID
* @param {*} properties 调用方传入的 properties 参数
*/
setProperties(id, properties) {
const node = store.findNode(id);
if (!node) return;
@ -541,15 +702,25 @@ export const createFlowFacade = (store) => {
changed();
},
getNodeModelById: modelFor,
/**
* 获取 getSelectElements 对应的数据或交互作用范围仅限画布适配层
*/
getSelectElements() {
return {
nodes: (store.getSelectedNodes.value || []).map(backendNode),
edges: (store.getSelectedEdges.value || []).map(backendEdge),
};
},
/**
* 清理 clearSelectElements 对应的数据或交互作用范围仅限画布适配层
*/
clearSelectElements() {
store.removeSelectedElements();
},
/**
* 处理 selectElementById 对应的数据或交互作用范围仅限画布适配层
* @param {*} id 目标元素 ID
*/
selectElementById(id) {
store.removeSelectedElements();
if (!id) return;
@ -558,33 +729,63 @@ export const createFlowFacade = (store) => {
const edge = store.findEdge(id);
if (edge) store.addSelectedEdges([edge]);
},
/**
* 处理 selectElementsByIds 对应的数据或交互作用范围仅限画布适配层
* @param {*} ids 调用方传入的 ids 参数
*/
selectElementsByIds(ids = []) {
store.removeSelectedElements();
const nodes = ids.map((id) => store.findNode(id)).filter(Boolean);
if (nodes.length) store.addSelectedNodes(nodes);
},
/**
* 处理 focusOn 对应的数据或交互作用范围仅限画布适配层
* @param {*} { id } 调用方传入的 { id } 参数
*/
focusOn({ id }) {
const node = store.findNode(id);
if (!node) return;
const center = backendNode(node);
store.setCenter(center.x, center.y, { zoom: Math.max(store.viewport.value?.zoom || 1, 0.7), duration: 300 });
},
/**
* 处理 fitView 对应的数据或交互作用范围仅限画布适配层
*/
fitView() {
return store.fitView({ padding: 0.18, duration: 300 });
},
/**
* 处理 translateCenter 对应的数据或交互作用范围仅限画布适配层
*/
translateCenter() {
return facade.fitView();
},
/**
* 缩放 zoom 对应的数据或交互作用范围仅限画布适配层
* @param {*} scale 缩放比例
* @param {*} center 缩放中心
*/
zoom(scale, center) {
if (Array.isArray(center) && center.length === 2) {
return store.setCenter(center[0], center[1], { zoom: scale, duration: 250 });
}
return store.zoomTo(scale, { duration: 250 });
},
/**
* 获取 getPointByClient 对应的数据或交互作用范围仅限画布适配层
* @param {*} x 客户端横坐标
* @param {*} y 客户端纵坐标
*/
getPointByClient(x, y) {
const point = store.screenToFlowCoordinate({ x, y });
return { canvasOverlayPosition: point, domOverlayPosition: point };
},
/**
* 新增 addToGroup 对应的数据或交互作用范围仅限画布适配层
* @param {*} groupId 分组节点 ID
* @param {*} childId 子节点 ID
* @param {*} dropCenter 拖放中心坐标
*/
addToGroup(groupId, childId, dropCenter) {
const group = store.findNode(groupId);
const child = store.findNode(childId);
@ -610,7 +811,14 @@ export const createFlowFacade = (store) => {
changed();
});
},
/**
* 编组 groupNodes 对应的数据或交互作用范围仅限画布适配层
* @param {*} groupNode 调用方传入的 groupNode 参数
* @param {*} childIds 子节点 ID 集合
*/
groupNodes(groupNode, childIds = []) {
// 先计算选中节点的外接矩形确定容器尺寸,再把子节点坐标转换为容器内相对坐标。
// 与分组外节点相连的边保持不变,此处仅更新节点层级和几何信息。
const graph = facade.getGraphData();
const selectedIds = new Set(childIds);
const children = graph.nodes.filter((node) => (
@ -641,6 +849,10 @@ export const createFlowFacade = (store) => {
});
return clone(group);
},
/**
* 处理 ungroup 对应的数据或交互作用范围仅限画布适配层
* @param {*} groupId 分组节点 ID
*/
ungroup(groupId) {
const graph = facade.getGraphData();
const group = graph.nodes.find((node) => node.id === groupId && node.type === "selectArea");
@ -668,6 +880,10 @@ export const createFlowFacade = (store) => {
});
return true;
},
/**
* 准备 prepareLoopChildDrag 对应的数据或交互作用范围仅限画布适配层
* @param {*} childId 子节点 ID
*/
prepareLoopChildDrag(childId) {
const child = store.findNode(childId);
const group = child?.parentNode ? store.findNode(child.parentNode) : null;
@ -680,16 +896,35 @@ export const createFlowFacade = (store) => {
const rect = getNodeRect(child);
return { groupId: group.id, position: { x: rect.x, y: rect.y } };
},
/**
* 布局 layoutLoopDuringDrag 对应的数据或交互作用范围仅限画布适配层
* @param {*} groupId 分组节点 ID
* @param {*} childId 子节点 ID
* @param {*} position 调用方传入的 position 参数
*/
layoutLoopDuringDrag(groupId, childId, position) {
const group = store.findNode(groupId);
if (!group || group.data.flowType !== "loop" || !position) return;
layoutLoop(group, childId, position);
},
/**
* 调整 resizeGroupToChildren 对应的数据或交互作用范围仅限画布适配层
* @param {*} groupId 分组节点 ID
* @param {*} options 行为配置
*/
resizeGroupToChildren(groupId, options = {}) {
// 循环容器沿用统一的内边距和最小尺寸约束,并按配置决定是否记录变更。
// 拖拽过程可关闭变更标记,避免高频移动产生多余历史记录。
const group = store.findNode(groupId);
if (!group || group.data.flowType !== "loop") return;
if (layoutLoop(group) && options.markChanged !== false) changed();
},
/**
* 移动 moveNodeBy 对应的数据或交互作用范围仅限画布适配层
* @param {*} id 目标元素 ID
* @param {*} dx 横向位移
* @param {*} dy 纵向位移
*/
moveNodeBy(id, dx, dy) {
const node = store.findNode(id);
if (!node) return;
@ -701,6 +936,9 @@ export const createFlowFacade = (store) => {
});
changed();
},
/**
* 撤销 undo 对应的数据或交互作用范围仅限画布适配层
*/
undo() {
if (historyIndex <= 0) return false;
historyIndex -= 1;
@ -709,6 +947,9 @@ export const createFlowFacade = (store) => {
nextTick(() => emit("history:change", { data: facade.getGraphData() }));
return true;
},
/**
* 重做 redo 对应的数据或交互作用范围仅限画布适配层
*/
redo() {
if (historyIndex >= history.length - 1) return false;
historyIndex += 1;
@ -717,6 +958,11 @@ export const createFlowFacade = (store) => {
nextTick(() => emit("history:change", { data: facade.getGraphData() }));
return true;
},
/**
* 注册 registerComponentInstance 对应的数据或交互作用范围仅限画布适配层
* @param {*} id 目标元素 ID
* @param {*} instance 组件实例
*/
registerComponentInstance(id, instance) {
if (instance) componentInstances.set(id, instance);
},