feat(flow): 添加单节点执行机器人选择记忆功能

- 引入 watch 监听器和相关变量用于缓存单节点机器人选择
- 实现按节点ID缓存机器人选择,新节点默认沿用最近一次选择
- 在单节点执行前后记录当前机器人选择到缓存中
- 修改NodeTitle组件传递节点ID用于按节点恢复机器人选择
- 添加试运行参数缓存功能,支持按流程缓存整套参数
- 实现参数层级缓存值恢复,兼容参数结构变化
- 优化试运行关闭和确认时的数据缓存处理逻辑
This commit is contained in:
lixiaolong 2026-08-07 11:28:01 +08:00
parent e6d379f571
commit 8e2afcbaa6
3 changed files with 60 additions and 6 deletions

View File

@ -159,7 +159,8 @@ const execute = () => {
document.dispatchEvent(new CustomEvent("singleNodeExecution", {
bubbles: false,
cancelable: false,
detail: { ...props.nodeProperties },
// ID
detail: { ...props.nodeProperties, nodeId: props.nodeId },
}));
};

View File

@ -90,14 +90,42 @@ const props = defineProps({
const ruleFormRef = ref()
const rules = ref({});
const tableData = ref([])
// 使
const trialRunCache = new Map()
const bindingLoading = ref(false)
const emptyBindingSummary = (robotId = '') => ({ robotId, matched: 0, missing: 0, untouched: 0 })
const bindingSummary = ref(emptyBindingSummary())
const emits = defineEmits(['close', 'changeState'])
const cloneData = (data) => JSON.parse(JSON.stringify(data || []))
//
const mergeCachedValues = (source, cached) => {
const cachedByName = new Map((cached || []).map(item => [item.name, item]))
return (source || []).map((item, index) => {
const cachedAtIndex = cached?.[index]
const cachedItem = cachedAtIndex?.name === item.name ? cachedAtIndex : cachedByName.get(item.name)
if (!cachedItem) return item
const merged = { ...item, value: cachedItem.value }
if (Array.isArray(item.children)) {
merged.children = mergeCachedValues(item.children, cachedItem.children)
}
return merged
})
}
const cacheKey = () => props.flowId || '__new_flow__'
//
const rememberTrialRunParams = () => {
if (!tableData.value.length) return
trialRunCache.set(cacheKey(), cloneData(tableData.value))
}
const handleClose = () => {
ruleFormRef.value?.resetFields()
rememberTrialRunParams()
ruleFormRef.value?.clearValidate()
emits('close')
}
@ -105,14 +133,17 @@ watch(() => props.drawer,
(newVal) => {
if (newVal) {
const data = getStartNodeFormData()
tableData.value = data
bindingSummary.value = emptyBindingSummary()
const cached = trialRunCache.get(cacheKey())
tableData.value = mergeCachedValues(data, cached)
const robotId = tableData.value.find(item => item.name === 'robotId')?.value || ''
bindingSummary.value = emptyBindingSummary(robotId)
loadRobotOptions()
}
}
)
const confirm = () => {
rememberTrialRunParams()
lf.fitView()
ruleFormRef.value.validate().then(async () => {
const formData = formatTableData(tableData.value)

View File

@ -245,7 +245,7 @@
</el-dialog>
</template>
<script setup>
import { computed, reactive, ref, onMounted, onUnmounted, nextTick } from "vue";
import { computed, reactive, ref, watch, onMounted, onUnmounted, nextTick } from "vue";
import { MarkerType, VueFlow, useVueFlow } from "@vue-flow/core";
import { v4 as uuid } from "uuid";
import { Background } from "@vue-flow/background";
@ -899,6 +899,10 @@ const rules = reactive({});
const executeForm = ref();
const nodeAction = ref("");
const singleNodeExecuting = ref(false);
const activeSingleNodeId = ref("");
// 沿
const singleNodeRobotCache = new Map();
let lastSingleNodeRobotId = "";
const stationLoading = ref(false);
const singleNodeRobotOptions = ref([]);
const flattenSingleNodeParams = (params, parentPath = [], fields = []) => {
@ -945,6 +949,16 @@ const singleNodeRobotId = computed(() =>
const singleNodeDeviceId = computed(() =>
formData.nodeParams.find((item) => item.name === "deviceId")?.input
);
//
const rememberSingleNodeRobot = () => {
if (!activeSingleNodeId.value || !singleNodeRobotId.value) return;
lastSingleNodeRobotId = singleNodeRobotId.value;
singleNodeRobotCache.set(activeSingleNodeId.value, singleNodeRobotId.value);
};
watch(visible, (value) => {
if (!value) rememberSingleNodeRobot();
});
const handleSingleNodeRobotChange = async (robotId) => {
const deviceParam = formData.nodeParams.find((item) => item.name === "deviceId");
@ -1035,10 +1049,12 @@ const loadSingleNodeRobots = async () => {
};
const singleNodeExecution = async (e) => {
const { name, nodeType, nodeParams, action } = e.detail;
rememberSingleNodeRobot();
const { name, nodeType, nodeParams, action, nodeId } = e.detail;
const executionParams = JSON.parse(JSON.stringify(nodeParams || []));
const normalizedAction = String(action || "").toUpperCase();
const effectiveNodeType = nodeType || (getDeviceKindForAction(normalizedAction) ? "EDGE" : "");
activeSingleNodeId.value = nodeId || `${normalizedAction}:${name || ""}`;
nodeName.value = name;
visible.value = true;
nodeAction.value = normalizedAction;
@ -1057,6 +1073,11 @@ const singleNodeExecution = async (e) => {
},
...executionParams,
];
const cachedRobotId = singleNodeRobotCache.get(activeSingleNodeId.value) || lastSingleNodeRobotId;
if (cachedRobotId) {
formData.nodeParams[0].input = cachedRobotId;
await handleSingleNodeRobotChange(cachedRobotId);
}
} else {
formData.nodeParams = executionParams;
}
@ -1081,6 +1102,7 @@ const execute = async () => {
}
}
if (!validateSingleNodeParams()) return;
rememberSingleNodeRobot();
singleNodeExecuting.value = true;
try {
const res = await flowAction({