CMVR-IOT-UI/src/views/device/register/components/LogicFlow/index.vue

294 lines
7.2 KiB
Vue
Raw Normal View History

2025-08-21 14:11:58 +08:00
<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>