diff --git a/src/views/flow/components/NodeTitle.vue b/src/views/flow/components/NodeTitle.vue
index 61adc85..2f0a7f3 100644
--- a/src/views/flow/components/NodeTitle.vue
+++ b/src/views/flow/components/NodeTitle.vue
@@ -87,7 +87,22 @@ const isEditing = ref(false);
const localName = ref("");
const nameInputRef = ref();
const isSelectArea = computed(() => props.nodeType === "selectArea");
-const canExecute = computed(() => Boolean(props.nodeType) && !["NONE", "selectArea"].includes(props.nodeType));
+const flowContextActions = new Set([
+ "NONE",
+ "START",
+ "END",
+ "SUB_START",
+ "SUB_END",
+ "START_LOOP",
+ "STOP_LOOP",
+ "BRANCH",
+ "GET_CURRENT_OBJECT",
+]);
+const canExecute = computed(() => {
+ if (isSelectArea.value) return false;
+ const action = String(props.nodeProperties?.action || "").toUpperCase();
+ return Boolean(action) && !flowContextActions.has(action);
+});
const typeLabel = computed(() => {
if (!props.nodeType || ["NONE", "selectArea"].includes(props.nodeType)) return "";
return { EDGE: "设备", LLM: "模型", AE: "评估" }[props.nodeType] || props.nodeType;
diff --git a/src/views/flow/config.js b/src/views/flow/config.js
index e18b761..59f385b 100644
--- a/src/views/flow/config.js
+++ b/src/views/flow/config.js
@@ -3,8 +3,6 @@ import videoSvg from './icon/video.svg'
import loopSvg from './icon/loop.svg'
import stopLoopSvg from './icon/stopLoop.svg'
import switchSvg from './icon/switch.svg'
-import startSvg from './icon/start.svg'
-import endSvg from './icon/end.svg'
import microphoneSvg from './icon/microphone.svg'
import sleepSvg from './icon/sleep.svg'
import planSvg from './icon/plan.svg'
@@ -14,11 +12,6 @@ import audioSvg from './icon/audio.svg'
import touchSvg from './icon/touch.svg'
import expressionSvg from './icon/expression.svg'
-const booleanOptions = () => [
- { value: false, label: '否' },
- { value: true, label: '是' }
-]
-
const httpMethodOptions = () => {
return [{
value: 'POST',
@@ -464,8 +457,10 @@ function handler(params) {
type: "serviceNode",
desc: "用于播放音频的节点",
action: 'SPEAKER_PLAYAUDIO',
+ nodeType: 'EDGE',
nodeParams: [
- { name: "deviceId", type: "input", input: "", componentType: 'select', selectOptions: [], disabled: true }
+ { name: "deviceId", type: "input", input: "", componentType: 'select', selectOptions: [], disabled: true },
+ { name: "audioPath", type: "input", input: "", disabled: true }
],
outputType: 'json'
},
diff --git a/src/views/flow/index.vue b/src/views/flow/index.vue
index 1955613..d66ea0c 100644
--- a/src/views/flow/index.vue
+++ b/src/views/flow/index.vue
@@ -180,17 +180,15 @@
label-width="auto"
:disabled="flowStore.disableForm"
>
-
+
@@ -198,27 +196,25 @@
-
+
已自动匹配当前机器人设备
@@ -243,7 +239,7 @@
@@ -628,7 +624,9 @@ const validEdge = (data) => {
}
if (sourceName === "right" && targetName === "left") {
- if (hasSameEdge(data, data.id)) lf.deleteEdge(data.id);
+ if (hasSameEdge(data, data.id)) {
+ lf.deleteEdge(data.id);
+ }
return;
} else if (
(sourceName === "right" && targetName === "right") ||
@@ -654,8 +652,7 @@ const validEdge = (data) => {
const instId = ref(null);
const changeState = (value) => {
- const flowData = JSON.parse(JSON.stringify(lf.getGraphData()));
- oldFlowData = flowData;
+ oldFlowData = JSON.parse(JSON.stringify(lf.getGraphData()));
isOpen.value = false;
flowStore.updateDisableForm(true);
flowState.value = value.type;
@@ -763,7 +760,7 @@ const registerBeforeUnload = () => {
window.addEventListener("beforeunload", handleBeforeUnload);
};
-const handleBeforeUnload = (event) => {
+const handleBeforeUnload = (_event) => {
if (flowState.value === "pause" && !flowInfoData.value.isLog) {
flowStopFn();
}
@@ -879,8 +876,7 @@ const defaultProps = {
};
const showNodeRelationship = () => {
const { nodes } = lf.getGraphData();
- const outputData = convertToTree(nodes);
- treeData.value = outputData;
+ treeData.value = convertToTree(nodes);
};
const handleTreeNodeClick = (node) => {
@@ -902,8 +898,47 @@ const formData = reactive({
const rules = reactive({});
const executeForm = ref();
const nodeAction = ref("");
+const singleNodeExecuting = ref(false);
const stationLoading = ref(false);
const singleNodeRobotOptions = ref([]);
+const flattenSingleNodeParams = (params, parentPath = [], fields = []) => {
+ (params || []).forEach((param, index) => {
+ const name = String(param?.name || "").trim();
+ const path = [...parentPath, name || `param-${index}`];
+ if (Array.isArray(param?.children)) {
+ flattenSingleNodeParams(param.children, path, fields);
+ return;
+ }
+ if (!name && (param?.input === "" || param?.input === null || param?.input === undefined)) return;
+ fields.push({ key: `${path.join(".")}-${index}`, param });
+ });
+ return fields;
+};
+const singleNodeFields = computed(() => flattenSingleNodeParams(formData.nodeParams));
+const buildSingleNodePayload = (params) => {
+ const payload = {};
+ (params || []).forEach((param) => {
+ const name = String(param?.name || "").trim();
+ if (!name) return;
+ payload[name] = Array.isArray(param.children)
+ ? buildSingleNodePayload(param.children)
+ : param.input;
+ });
+ return payload;
+};
+const validateSingleNodeParams = () => {
+ for (const { param } of singleNodeFields.value) {
+ if (!String(param?.name || "").trim()) {
+ ElMessage.warning("请输入参数名");
+ return false;
+ }
+ if (param.input === "" || param.input === null || param.input === undefined) {
+ ElMessage.warning(`请输入参数 ${param.name} 的值`);
+ return false;
+ }
+ }
+ return true;
+};
const singleNodeRobotId = computed(() =>
formData.nodeParams.find((item) => item.name === "robotId")?.input
);
@@ -1002,11 +1037,14 @@ const loadSingleNodeRobots = async () => {
const singleNodeExecution = async (e) => {
const { name, nodeType, nodeParams, action } = e.detail;
const executionParams = JSON.parse(JSON.stringify(nodeParams || []));
+ const normalizedAction = String(action || "").toUpperCase();
+ const effectiveNodeType = nodeType || (getDeviceKindForAction(normalizedAction) ? "EDGE" : "");
nodeName.value = name;
visible.value = true;
- nodeAction.value = action;
+ nodeAction.value = normalizedAction;
+ singleNodeExecuting.value = false;
stationLoading.value = false;
- if (nodeType === "EDGE") {
+ if (effectiveNodeType === "EDGE") {
await loadSingleNodeRobots();
formData.nodeParams = [
{
@@ -1022,7 +1060,7 @@ const singleNodeExecution = async (e) => {
} else {
formData.nodeParams = executionParams;
}
- if (action === "AGV_MOVE_TO_STATION") {
+ if (nodeAction.value === "AGV_MOVE_TO_STATION") {
const stationParam = formData.nodeParams.find((item) => item.name === "stationId");
if (stationParam) {
stationParam.input = "";
@@ -1042,21 +1080,22 @@ const execute = async () => {
return;
}
}
- const result = await executeForm.value.validate();
- if (result) {
- const obj = {};
- formData.nodeParams.forEach((item) => {
- obj[item.name] = item.input;
- });
+ if (!validateSingleNodeParams()) return;
+ singleNodeExecuting.value = true;
+ try {
const res = await flowAction({
action: nodeAction.value,
- payload: obj,
+ payload: buildSingleNodePayload(formData.nodeParams),
});
if (res.code === 200) {
ElMessage.success("执行成功");
} else {
ElMessage.error(res.msg);
}
+ } catch (error) {
+ console.error("单节点执行失败:", error);
+ } finally {
+ singleNodeExecuting.value = false;
}
};
@@ -1347,7 +1386,6 @@ onUnmounted(() => {
.single-node-param-row {
display: flex;
flex-wrap: nowrap;
- align-items: center;
justify-content: space-between;
gap: 24px;
align-items: start;