CMVR-IOT-UI/src/views/device/register/components/LogicFlow/index.vue
2025-08-21 14:11:58 +08:00

294 lines
7.2 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<div class="flow_container">
<div class="btn_container">
<div class="instState">实例状态<span :class="instStatus">{{ stateMap[instStatus]}}</span></div>
<el-button key=" translateCenter" type="primary" @click="() => lfRef.translateCenter()">居中</el-button>
<el-button key="fitView" type="primary" @click="() => lfRef.fitView()">适应屏幕</el-button>
<el-button v-if="['RUNNING','PAUSED'].includes(instStatus)" type="primary" @click="stopFlow">停止</el-button>
<el-button v-if="instStatus === 'RUNNING'" type="primary" @click="pauseFlow">暂停</el-button>
<el-button v-if="instStatus === 'PAUSED'" type="primary" @click="restartFlow">恢复</el-button>
</div>
<div ref="containerRef" class="containerRef"></div>
</div>
</template>
<script setup>
import { ref, onMounted } from "vue";
import LogicFlow from "@logicflow/core";
import { Group } from "@logicflow/extension";
import "@logicflow/core/es/index.css";
import { convertNodeRedToLogicFlow, recursiveFilter, changeOtherNodeState } from '@/utils/convertNodeRedToLogicFlow'
import { registerCustomNode } from "./customFlowNode";
import { registerCustomGroup } from "./CustomGroup";
import { getLogicFlow } from '@/api/test/log'
import { pauseInst, resumeInst, stopInst } from '@/api/device/terminal'
import { ElMessage } from 'element-plus'
const containerRef = ref(null);
const lfRef = ref(null);
// 在新窗口的脚本中
const queryParams = new URLSearchParams(window.location.search);
const instId = queryParams.get('instId') || '';
const taskId = queryParams.get('taskId') || '';
const itemId = queryParams.get('itemId') || '03e0842c2fd7ab7575636a473aeefc89';
const data = {
nodes: [
{
id: "custom-node-1",
text: "node-1",
type: "CustomNode",
x: 100,
y: 100,
},
{
id: 'custom-node-2',
type: 'CustomNode',
x: 300,
y: 300,
text: 'node-2',
},
],
// 边
edges: [
{
type: 'polyline',
sourceNodeId: 'custom-node-1',
targetNodeId: 'custom-node-2',
},
],
};
const dataNodes = ref([])
const loadNodeRedData = async () => {
const {nodes, edges} = await convertNodeRedToLogicFlow(itemId)
lfRef.value.render({
nodes,
edges
})
dataNodes.value = nodes
setTimeout(() => {
lfRef.value.fitView()
}, 20)
getLog()
}
const getLog = async () => {
const res = await getLogicFlow({
instId,
taskId,
itemId
})
if (res.code === 200) {
const arr = res.data.filter(item => {
return item.nodeType
})
const map = new Map()
arr.forEach(item => {
if (['inject', 'start'].includes(item.nodeType)) { // , 'end'
map.set(item.nodeId, {
state: 'success',
nodeType: item.nodeType,
input: item.params
})
} else {
let obj
if (map.has(item.nodeId)) {
obj = map.get(item.nodeId)
} else {
obj = {
nodeType: item.nodeType,
}
}
if (item.paramsType === 'INPUT') {
obj.state = item.instStatus.toLowerCase(),
obj.input = item.instStatus === 'FAILED' ? item.params : item.message
} else {
obj.state = item.instStatus.toLowerCase() === 'running' ? 'success' : item.instStatus.toLowerCase(),
obj.output = item.instStatus === 'FAILED' ? item.message : item.params
}
map.set(item.nodeId, obj)
}
});
for (const [key, value] of map) {
lfRef.value.setProperties(key, value)
}
changeOtherNodeState(dataNodes.value, lfRef.value)
if (arr[arr.length - 1].nodeType !== 'end' && arr[arr.length - 1].instStatus !== 'FAILED') {
setTimeout(() => {
getLog()
}, 2000)
}
}
}
const stopFlow = async () => {
const res = await stopInst(instId)
if (res.code === 200) {
ElMessage.success('停止成功')
getInstDetail()
} else {
ElMessage.error(res.msg)
}
}
const pauseFlow = async () => {
const res = await pauseInst(instId)
if (res.code === 200) {
ElMessage.success('暂停成功')
getInstDetail()
} else {
ElMessage.error(res.msg)
}
}
const restartFlow = async () => {
const res = await resumeInst(instId)
if (res.code === 200) {
ElMessage.success('恢复成功')
getInstDetail()
} else {
ElMessage.error(res.msg)
}
}
const instStatus = ref('RUNNING')
const stateMap = {
SUCCESS: "已完成",
RUNNING: "执行中",
FAILED: "失败",
PAUSED: "已暂停",
STOPPED: "已终止"
};
const getInstDetail = async () => {
const res = await getLogicFlow({
instId,
taskId,
itemId
})
if (res.code === 200) {
const item = res.data[res.data.length - 1]
instStatus.value = item.instStatus
}
}
onMounted(() => {
if (containerRef.value) {
const lf = new LogicFlow({
container: containerRef.value,
isSilentMode: true,
adjustNodePosition: true,
stopZoomGraph: false,
stopScrollGraph: false,
stopMoveGraph: false,
autoExpand: true,
adjustEdgeStartAndEnd: true,
allowRotate: false,
edgeTextEdit: false,
keyboard: {
enabled: true,
},
partial: true,
background: {
color: "#FFFFFF",
},
grid: true,
edgeTextDraggable: false,
nodeTextEdit: false, //节点是否可编辑。false不可编辑
textEdit: false, //是否开启文本编辑
style: {
inputText: {
background: "black",
color: "white",
},
},
idGenerator(type) {
return type + "_" + Math.random();
},
plugins: [Group],
group: {
foldable: true, // 启用折叠功能
foldSize: 30, // 折叠后显示的图标尺寸
},
});
// 注册自定义节点
registerCustomNode(lf)
registerCustomGroup(lf)
// 渲染数据 data
lf.render(data);
lf.on('node:dragstart', (data, e) => {
lfRef.value.setProperties(data.data.id, {
nowTime: new Date().getTime()
})
})
// 监听子节点拖动事件
lf.on("node:mousemove", ({ data, e }) => {
const { nodes } = lf.getGraphData();
const arr = recursiveFilter(nodes, data.id)
arr.forEach(item => {
lf.getNodeModelById(item.id).updateSize()
})
});
// 监听子节点拖动事件
lf.on("blank:drop", () => {
getLog()
});
lfRef.value = lf;
}
loadNodeRedData()
getInstDetail()
});
</script>
<style lang="scss" scoped>
.flow_container {
width: 100%;
height: 100%;
position: relative;
.btn_container {
height: 60px;
display: flex;
align-items: center;
justify-content: end;
position: absolute;
right: 10px;
top: 0;
z-index: 2;
.instState {
margin-right: 12px;
font-size: 16px;
font-weight: bold;
.RUNNING, .PAUSED, .STOPPED {
color: #8bbf86;
}
.SUCCESS {
color: green;
}
.FAILED {
color: rgb(128, 41, 0);
}
}
}
:deep(.containerRef) {
width: 100%;
height: 100%;
.lf-graph {
width: 100% !important;
height: 100% !important;
}
}
}
</style>