You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

209 lines
4.6 KiB
Vue

<template>
<div class="node" ref="container">
<!-- 主节点 -->
<div class="node-main" ref="nodeMain">
<div class="node-icon" ref="nodeIcon"></div>
<div class="node-title">{{ data.locationAlias }}</div>
</div>
<!-- 子节点 -->
<div class="children">
<div v-for="(child, index) in data.children" :key="index" class="child-item" :ref="(el) => (childRefs[index] = el)">
<div class="child-icon"></div>
<div class="child-info">
<div class="child-name">
工位名称: <span style="font-size: 12px">{{ child.locationAlias }}</span>
</div>
<div class="child-code">编码{{ wsData[data.deviceId]?.epcStr?.trimStart()}}</div>
3 weeks ago
<div class="child-time">时间{{ parseTime(wsData[data.deviceId]?.recordTime) }}</div>
</div>
</div>
</div>
<!-- SVG 折线 -->
<svg class="connector" ref="svg">
<polyline v-for="(line, index) in lines" :key="index" :points="line" stroke="#0156A5" fill="none" stroke-width="1" />
</svg>
</div>
</template>
<script setup>
import { ref, reactive, onMounted, nextTick, watchEffect } from 'vue';
3 weeks ago
import {parseTime} from '@/utils/ruoyi.js'
const props = defineProps({
data: {
type: Object,
required: true
3 weeks ago
},
wsData: {
type: Object,
required: true
},
});
const container = ref(null);
const nodeIcon = ref(null);
const childRefs = ref([]);
const lines = reactive(Array(props.data.children.length).fill(''));
const svg = ref(null);
const updateLines = () => {
if (!container.value || !nodeIcon.value) return;
const containerRect = container.value.getBoundingClientRect();
childRefs.value.forEach((childEl, index) => {
if (!childEl) return;
const nodeRect = nodeIcon.value.getBoundingClientRect();
const childRect = childEl.getBoundingClientRect();
const startX = nodeRect.right - containerRect.left;
const startY = nodeRect.top + nodeRect.height / 2 - containerRect.top;
const endX = childRect.left - containerRect.left;
const endY = childRect.top + childRect.height / 2 - containerRect.top;
const midX = startX + (endX - startX) / 2;
lines[index] = `${startX},${startY} ${midX},${startY} ${midX},${endY} ${endX},${endY}`;
});
// 更新 SVG 大小
const rect = container.value.getBoundingClientRect();
svg.value.setAttribute('width', rect.width);
svg.value.setAttribute('height', rect.height);
};
onMounted(() => {
nextTick(() => {
updateLines();
});
});
// 如果子节点数量或容器大小动态变化,可以监听更新折线
watchEffect(() => {
nextTick(() => {
updateLines();
});
});
</script>
<style scoped>
.node {
color: #fff;
font-size: 12px;
width: 360px;
position: relative;
vertical-align: top;
display: flex;
align-items: stretch;
break-inside: avoid;
margin-bottom: 12px;
margin-left: 12px;
}
/* 主节点 */
.node-main {
width: 100px;
display: flex;
position: relative;
justify-content: center;
align-items: center;
}
.node-icon {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
width: 56px;
height: 56px;
background-image: url('@/assets/chart/906cbf773b3a37fc27e380375606fb27.png');
background-size: 100% 100%;
background-repeat: no-repeat;
border-radius: 4px;
}
.node-title {
position: absolute;
top: calc(50% + 30px);
left: 50%;
transform: translateX(-50%);
font-size: 1vw;
font-weight: bold;
}
/* 子节点列表 */
.children {
display: inline-block;
width: 260px;
}
.child-item {
background: rgba(0, 255, 180, 0.15);
border-radius: 6px;
margin-bottom: 8px;
position: relative;
height: 75px;
margin-left: 20px;
background-image: url('@/assets/chart/efc42f5a4ed9491a16f6817d7fbe2336.png');
background-size: 100% 100%;
background-repeat: no-repeat;
overflow: hidden;
}
.child-item:last-child {
margin-bottom: 0;
}
.child-icon {
width: 50px;
height: 50px;
border-radius: 4px;
margin-right: 8px;
background-image: url('@/assets/chart/e06ce08b1a179471f9600f1d53ec3781.png');
background-size: 100% 100%;
background-repeat: no-repeat;
position: absolute;
top: 50%;
left: 12px;
transform: translateY(-50%);
}
.child-name {
position: absolute;
top: 10px;
left: 74px;
font-size: 14px;
white-space: nowrap;
}
.child-code,
.child-time {
opacity: 0.9;
white-space: nowrap;
}
.child-code {
position: absolute;
top: 27px;
left: 74px;
font-size: 14px;
}
.child-time {
position: absolute;
top: 46px;
left: 74px;
font-size: 14px;
}
/* SVG 折线 */
.connector {
position: absolute;
top: 0;
left: 0;
pointer-events: none;
}
</style>