lxl-dev #3

Merged
zhanghao merged 122 commits from lxl-dev into dev 2026-08-24 13:55:18 +08:00
3 changed files with 60 additions and 6 deletions
Showing only changes of commit 8e2afcbaa6 - Show all commits

View File

@ -159,7 +159,8 @@ const execute = () => {
document.dispatchEvent(new CustomEvent("singleNodeExecution", { document.dispatchEvent(new CustomEvent("singleNodeExecution", {
bubbles: false, bubbles: false,
cancelable: 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 ruleFormRef = ref()
const rules = ref({}); const rules = ref({});
const tableData = ref([]) const tableData = ref([])
// 按流程缓存整套试运行参数,关闭或执行后再次打开时继续使用。
const trialRunCache = new Map()
const bindingLoading = ref(false) const bindingLoading = ref(false)
const emptyBindingSummary = (robotId = '') => ({ robotId, matched: 0, missing: 0, untouched: 0 }) const emptyBindingSummary = (robotId = '') => ({ robotId, matched: 0, missing: 0, untouched: 0 })
const bindingSummary = ref(emptyBindingSummary()) const bindingSummary = ref(emptyBindingSummary())
const emits = defineEmits(['close', 'changeState']) 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 = () => { const handleClose = () => {
ruleFormRef.value?.resetFields() rememberTrialRunParams()
ruleFormRef.value?.clearValidate()
emits('close') emits('close')
} }
@ -105,14 +133,17 @@ watch(() => props.drawer,
(newVal) => { (newVal) => {
if (newVal) { if (newVal) {
const data = getStartNodeFormData() const data = getStartNodeFormData()
tableData.value = data const cached = trialRunCache.get(cacheKey())
bindingSummary.value = emptyBindingSummary() tableData.value = mergeCachedValues(data, cached)
const robotId = tableData.value.find(item => item.name === 'robotId')?.value || ''
bindingSummary.value = emptyBindingSummary(robotId)
loadRobotOptions() loadRobotOptions()
} }
} }
) )
const confirm = () => { const confirm = () => {
rememberTrialRunParams()
lf.fitView() lf.fitView()
ruleFormRef.value.validate().then(async () => { ruleFormRef.value.validate().then(async () => {
const formData = formatTableData(tableData.value) const formData = formatTableData(tableData.value)

View File

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