通道配对,预检测界面

This commit is contained in:
sjl
2025-08-05 10:37:40 +08:00
parent 0079f7415e
commit 9c5e54507b
14 changed files with 1550 additions and 343 deletions

View File

@@ -1,105 +1,98 @@
<template>
<el-dialog title="设备通道配对" v-model='dialogVisible' v-bind="dialogBig">
<div class="flow-container" style="overflow: hidden; position: relative;">
<!-- <el-form ref='dialogFormRef' :disabled="false" label-width="auto" class="form-two">
<el-form-item label='被检设备' prop='type'>
<el-select v-model="value" clearable placeholder="被检设备" >
<el-option label="CN88888" :value="2"></el-option>
<el-option label="CN250985" :value="1"></el-option>
<el-option label="PQS240224008" :value="0"></el-option>
</el-select>
</el-form-item>
<el-form-item label='标准设备' prop='type' >
<el-select v-model="value" clearable placeholder="被检设备" >
<el-option label="CN88888" :value="2"></el-option>
<el-option label="CN250985" :value="1"></el-option>
<el-option label="PQS240224008" :value="0"></el-option>
</el-select>
</el-form-item>
</el-form> -->
<el-button @click="logConnections">打印当前配对</el-button>
<VueFlow
:nodes="nodes"
:edges="edges"
:connection-radius="30"
:nodes-draggable="false"
:dragging = "false"
:zoom-on-scroll="false"
:pan-on-drag="false"
:disable-zoom-pan-on-connect="true"
auto-connect
@connect="handleConnect"
/>
</div>
<div class="flow-container" style="overflow: hidden; position: relative;">
<el-button @click="logConnections">打印当前配对</el-button>
<VueFlow
:nodes="nodes"
:edges="edges"
:connection-radius="30"
:nodes-draggable="false"
:dragging="false"
:zoom-on-scroll="false"
:pan-on-drag="false"
:disable-zoom-pan-on-connect="true"
:prevent-scrolling="true"
:fit-view="true"
:min-zoom="1"
:max-zoom="1"
:elements-selectable="false"
auto-connect
@connect="handleConnect"
@connect-start="handleConnectStart"
@connect-end="handleConnectEnd"
@pane-ready="onPaneReady"
v-on:pane-mouse-move="false"
>
</VueFlow>
</div>
<!-- 底部操作按钮 -->
<template #footer>
<div class="dialog-footer">
<el-button @click="dialogVisible = false">取消</el-button>
<el-button type="primary" @click="handleNext">下一步</el-button>
</div>
</template>
</el-dialog>
<!-- 手动检测-勾选检测项弹窗 -->
<SelectTestItemPopup ref="selectTestItemPopupRef" @openTestDialog="openTestDialog"></SelectTestItemPopup>
<CompareTestPopup ref='testPopup'></CompareTestPopup>
</template>
<script lang="ts" setup>
import { h, ref} from 'vue'
import { VueFlow, useVueFlow} from '@vue-flow/core'
import {dialogBig} from "@/utils/elementBind";
import {Platform,Promotion,Flag} from '@element-plus/icons-vue'
import { c } from 'vite/dist/node/types.d-aGj9QkWt';
import { de, el } from 'element-plus/es/locale';
import { h, ref } from 'vue'
import { VueFlow, useVueFlow } from '@vue-flow/core'
import { dialogBig } from "@/utils/elementBind";
import { Platform, Flag } from '@element-plus/icons-vue'
import { Device } from '@/api/device/interface/device';
import { Plan } from '@/api/plan/interface';
import { StandardDevice } from '@/api/device/interface/standardDevice';
const dialogVisible = ref(false)
// 初始化 VueFlow
const { edges} = useVueFlow()
import SelectTestItemPopup from "@/views/home/components/selectTestItemPopup.vue";
import CompareTestPopup from './compareTestPopup.vue'
import { ElMessage } from 'element-plus';
import CustomEdge from './RemoveableEdge.vue' // 导入自定义连接线组件
const dialogVisible = ref(false)
const selectTestItemPopupRef = ref<InstanceType<typeof SelectTestItemPopup>>()
const testPopup = ref()
const dialogTitle = ref('手动检测')
// 初始化 VueFlow注册自定义连线类型
const { edges, setViewport } = useVueFlow({
edgeTypes: {
default: CustomEdge
}
})
// 初始化时锁定画布位置
const onPaneReady = () => {
setViewport({ x: 0, y: 0, zoom: 1 })
}
// 提取公共的label渲染函数
const createLabel = ( text: string,type :string) => {
const createLabel = (text: string, type: string) => {
return h('div', {
style: {
display: 'flex',
flexDirection: 'column', // 图标和文字上下排列
flexDirection: 'column',
alignItems: 'center',
fontSize: '15px',
textAlign: 'center',
border: '1px solid #ccc', // 添加边框
borderRadius: '8px', // 圆角
padding: '8px', // 内边距
backgroundColor: '#f9f9f9' // 可选:背景色
border: '1px solid #ccc',
borderRadius: '8px',
padding: '8px',
backgroundColor: '#f9f9f9'
}
}, [
h(Platform, {
style: {
width: '20px',
marginBottom: '4px', // 图标与文字间距
marginBottom: '4px',
color: '#526ade'
}
}),
h('div', null, '设备名称:'+ text),
h('div', null, '设备类型:'+ type)
]) as any
}
const createLabel2 = (text: string) => {
return h('div', {
style: {
display: 'flex',
alignItems: 'center',
fontSize: '15px',
textAlign: 'center',
border: '1px solid #ccc', // 添加边框
borderRadius: '8px', // 圆角
padding: '8px', // 内边距
backgroundColor: '#f9f9f9' // 可选:背景色
}
}, [
h(Promotion, {
style: {
width: '20px',
marginRight: '4px',
color: '#526ade'
}
}),
text
h('div', null, '设备名称:' + text),
h('div', null, '设备类型:' + type)
]) as any
}
@@ -110,10 +103,10 @@ const createLabel3 = (text: string) => {
alignItems: 'center',
fontSize: '15px',
textAlign: 'center',
border: '1px solid #ccc', // 添加边框
borderRadius: '8px', // 圆角
padding: '8px', // 内边距
backgroundColor: '#f9f9f9' // 可选:背景色
border: '1px solid #ccc',
borderRadius: '8px',
padding: '8px',
backgroundColor: '#f9f9f9'
}
}, [
h(Flag, {
@@ -127,194 +120,40 @@ const createLabel3 = (text: string) => {
]) as any
}
// 节点数据
const nodes2 = ref([
{
id: '1',
data: { label: createLabel('被检设备1') },
position: { x: 0, y: 50 },
class: 'no-handle-node', // 👈 添加自定义 class
style: { width: '200px', border: 'none', boxShadow: 'none' }
},
{
id: '2',
data: { label: createLabel('标准设备1') },
position: { x: 950, y: 0 },
class: 'no-handle-node', // 👈 添加自定义 class
style: { width: '200px', border: 'none', boxShadow: 'none' }
},
{
id: '3',
data: { label: createLabel('被检设备2') },
position: { x: 0, y: 250 },
class: 'no-handle-node', // 👈 添加自定义 class
style: { width: '200px', border: 'none', boxShadow: 'none' }
},
{
id: '4',
data: { label: createLabel('标准设备2') },
position: { x: 950, y: 200 },
class: 'no-handle-node', // 👈 添加自定义 class
style: { width: '200px', border: 'none', boxShadow: 'none' }
},
const handleConnectStart = (params: any) => {
onPaneReady()
}
{
id: '5',
data: { label: createLabel('被检设备3') },
position: { x: 0, y: 425 },
class: 'no-handle-node', // 👈 添加自定义 class
style: { width: '200px', border: 'none', boxShadow: 'none' }
},
{
id: '被检通道1',
type: 'input',
data: { label: createLabel3('被检通道1') },
position: { x: 200, y: 0 },
sourcePosition: 'right',
style: { width: '150px', border: 'none', boxShadow: 'none' }
},
{
id: '被检通道2',
type: 'input',
data: { label: createLabel3('被检通道2') },
position: { x: 200, y:50 },
sourcePosition: 'right',
style: { width: '150px', border: 'none', boxShadow: 'none' }
},
{
id: '被检通道3',
type: 'input',
data: { label: createLabel3('被检通道3') },
position: { x: 200, y: 100 },
sourcePosition: 'right',
style: { width: '150px', border: 'none', boxShadow: 'none' }
},
{
id: '被检通道4',
type: 'input',
data: { label: createLabel3('被检通道4') },
position: { x: 200, y: 150 },
sourcePosition: 'right',
style: { width: '150px', border: 'none', boxShadow: 'none' }
},
{
id: '被检通道5',
type: 'input',
data: { label: createLabel3('被检通道1') },
position: { x: 200, y: 250 },
sourcePosition: 'right',
style: { width: '150px', border: 'none', boxShadow: 'none' }
},
{
id: '被检通道6',
type: 'input',
data: { label: createLabel3('被检通道2') },
position: { x: 200, y:300 },
sourcePosition: 'right',
style: { width: '150px', border: 'none', boxShadow: 'none' }
},
{
id: '被检通道7',
type: 'input',
data: { label: createLabel3('被检通道1') },
position: { x: 200, y:400 },
sourcePosition: 'right',
style: { width: '150px', border: 'none', boxShadow: 'none' }
},
{
id: '被检通道8',
type: 'input',
data: { label: createLabel3('被检通道2') },
position: { x: 200, y:450 },
sourcePosition: 'right',
style: { width: '150px', border: 'none', boxShadow: 'none' }
},
{
id: '被检通道9',
type: 'input',
data: { label: createLabel3('被检通道3') },
position: { x: 200, y:500 },
sourcePosition: 'right',
style: { width: '150px', border: 'none', boxShadow: 'none' }
},
{
id: '标准通道1',
type: 'output',
data: { label: createLabel2('标准通道1') },
position: { x: 800, y: 0 },
targetPosition: 'left',
style: { width: '150px', border: 'none', boxShadow: 'none' }
},
{
id: '标准通道2',
type: 'output',
data: { label: createLabel2('标准通道2') },
position: { x: 800, y: 50 },
targetPosition: 'left',
style: { width: '150px', border: 'none', boxShadow: 'none' }
},
{
id: '标准通道3',
type: 'output',
data: { label: createLabel2('标准通道1') },
position: { x: 800, y: 150 },
targetPosition: 'left',
style: { width: '150px', border: 'none', boxShadow: 'none' }
},
{
id: '标准通道4',
type: 'output',
data: { label: createLabel2('标准通道2') },
position: { x: 800, y: 200 },
targetPosition: 'left',
style: { width: '150px', border: 'none', boxShadow: 'none' }
},
{
id: '标准通道5',
type: 'output',
data: { label: createLabel2('标准通道3') },
position: { x: 800, y: 250 },
targetPosition: 'left',
style: { width: '150px', border: 'none', boxShadow: 'none' }
},
{
id: '标准通道6',
type: 'output',
data: { label: createLabel2('标准通道4') },
position: { x: 800, y: 300 },
targetPosition: 'left',
style: { width: '150px', border: 'none', boxShadow: 'none' }
},
]);
const handleConnectEnd = (params: any) => {
onPaneReady()
}
const handleConnect = (params: any) => {
console.log('连接信息:', params);
const sourceNode = nodes.value.find(node => node.id === params.source)
const targetNode = nodes.value.find(node => node.id === params.target)
// 连接规则验证
const isValidConnection =
sourceNode?.type === 'input' &&
targetNode?.type === 'output'
if (!isValidConnection) {
// 删除当前连接
removeEdge(params)
}
ElMessage.warning('只能从被检通道连接到标准通道')
return
}
}
// 删除不合法连接的函数
// 删除不合法连接
const removeEdge = (params: any) => {
const edgeIndex = edges.value.findIndex(edge => edge.source === params.source && edge.target === params.target)
const edgeIndex = edges.value.findIndex(edge =>
edge.source === params.source && edge.target === params.target
)
if (edgeIndex !== -1) {
edges.value.splice(edgeIndex, 1) // 删除该连接
edges.value.splice(edgeIndex, 1)
}
}
@@ -334,45 +173,34 @@ function logConnections() {
}
const nodes = ref([])
const open = async (device:Device.ResPqDev[],standardDev:StandardDevice.ResPqStandardDevice[]) => {
nodes.value = createNodes(device,standardDev)
const sourceIdArray = ref<string[]>()
const open = async (device: Device.ResPqDev[], standardDev: StandardDevice.ResPqStandardDevice[],sourceIds: string[]) => {
edges.value = []
sourceIdArray.value = sourceIds
nodes.value = createNodes(device, standardDev)
dialogVisible.value = true
onPaneReady()
}
// // 每台设备的通道数量
// const channelCounts = {
// '1': 1, // 被检设备1 → 4个通道
// '3': 3, // 被检设备2 → 2个通道
// '5': 2, // 被检设备3 → 2个通道
// '7': 4 // 被检设备4 → 4个通道
// }
const handleNext = () => {
if (edges.value.length === 0) {
ElMessage.warning('请先完成通道配对')
return
}
dialogVisible.value = false
selectTestItemPopupRef.value?.open(sourceIdArray.value)
}
// 每台设备的通道数量
// const channelCounts2 = {
// '2': 2, // 标准设备1 → 2个通道
// '4': 1, // 标准设备2 → 1个通道
// }
// const inspectionDevices = [
// { id: '1', name: '被检设备1', type: 'normal', deviceType: 'PQS-882B4' },
// { id: '3', name: '被检设备2', type: 'normal', deviceType: 'PQS-883A2' },
// { id: '5', name: '被检设备3', type: 'normal', deviceType: 'PQS-882B2' },
// { id: '7', name: '被检设备4', type: 'normal', deviceType: 'PQS_883B' }
// ]
// const standardDevices = [
// { id: '2', name: '标准设备1', type: 'normal', deviceType: 'PQS-882A' },
// { id: '4', name: '标准设备2', type: 'normal', deviceType: 'PQS-882B2' },
// ]
const openTestDialog = () => {
testPopup.value?.open(dialogTitle.value,sourceIdArray.value)
}
const createNodes = (device: Device.ResPqDev[], standardDev: StandardDevice.ResPqStandardDevice[]) => {
const channelCounts: Record<string, number> = {}
// 每台被检设备的通道数量
device.forEach(device => {
channelCounts[device.id] = device.devChns || 0
})
// 每台被检设备的信息
const inspectionDevices = device.map(d => ({
id: d.id,
name: d.name,
@@ -381,23 +209,19 @@ const createNodes = (device: Device.ResPqDev[], standardDev: StandardDevice.ResP
}))
const channelCounts2: Record<string, number> = {}
// 每台标准设备的通道数量
standardDev.forEach(dev => {
standardDev.forEach(dev => {
const channelList = dev.inspectChannel ? dev.inspectChannel.split(',') : []
channelCounts2[dev.id] = channelList.length
})
console.log(standardDev)
// 每台标准设备的信息
const standardDevices = standardDev.map(d => ({
const standardDevices = standardDev.map(d => ({
id: d.id,
name: d.name,
type: 'normal',
deviceType: d.devType
}))
const newNodes: any[] = []
// 存储每组被检/标准通道的垂直范围
const deviceChannelGroups: { deviceId: string; centerY: number; }[] = []
const standardChannelGroups: any[] = []
@@ -421,6 +245,8 @@ const createNodes = (device: Device.ResPqDev[], standardDev: StandardDevice.ResP
sourcePosition: 'right',
style: { width: '150px', border: 'none', boxShadow: 'none' }
})
// 计算设备节点Y坐标居中显示
if(i == 1 && count == 1){
deviceChannelGroups.push({
deviceId,
@@ -442,10 +268,12 @@ const createNodes = (device: Device.ResPqDev[], standardDev: StandardDevice.ResP
centerY: yPosition.value - 100
})
}
yPosition.value += 50
}
yPosition.value += 50
})
// 添加标准通道
Object.entries(channelCounts2).forEach(([deviceId, count]) => {
for (let i = 1; i <= count; i++) {
@@ -458,7 +286,9 @@ const createNodes = (device: Device.ResPqDev[], standardDev: StandardDevice.ResP
targetPosition: 'left',
style: { width: '150px', border: 'none', boxShadow: 'none' }
})
if(i == 1 && count == 1){
// 计算设备节点Y坐标居中显示
if(i == 1 && count == 1){
standardChannelGroups.push({
deviceId,
centerY: yPosition2.value - 25
@@ -479,51 +309,51 @@ const createNodes = (device: Device.ResPqDev[], standardDev: StandardDevice.ResP
centerY: yPosition2.value - 100
})
}
yPosition2.value += 50
yPosition2.value += 50
}
yPosition2.value += 50
yPosition2.value += 50
})
// 添加被检设备
deviceChannelGroups.forEach(({ deviceId, centerY }) => {
const device = inspectionDevices.find(d => d.id === deviceId)
if (!device) return
// 添加被检设备
deviceChannelGroups.forEach(({ deviceId, centerY }) => {
const device = inspectionDevices.find(d => d.id === deviceId)
if (device) {
newNodes.push({
id: device.id,
data: { label: createLabel(device.name,device.deviceType) },
data: { label: createLabel(device.name, device.deviceType) },
position: { x: deviceWidth, y: centerY },
class: 'no-handle-node',
style: { width: '200px', border: 'none', boxShadow: 'none' }
})
})
}
})
// 添加标准设备
standardChannelGroups.forEach(({ deviceId, centerY }) => {
const device = standardDevices.find(d => d.id === deviceId)
if (!device) return
// 添加标准设备
standardChannelGroups.forEach(({ deviceId, centerY }) => {
const device = standardDevices.find(d => d.id === deviceId)
if (device) {
newNodes.push({
id: device.id,
data: { label: createLabel(device.name,device.deviceType) },
data: { label: createLabel(device.name, device.deviceType) },
position: { x: standardWidth, y: centerY },
class: 'no-handle-node',
style: { width: '200px', border: 'none', boxShadow: 'none' }
})
})
}
})
return newNodes
}
defineExpose({open})
defineExpose({ open })
</script>
<style>
.flow-container {
display: flex;
flex-direction: column;
height: 800px;
height: 600px;
width: 100%;
overflow: hidden;
}
.vue-flow__node.no-handle-node .vue-flow__handle {