修改ws数据处理

main
suixy 3 months ago
parent e7454f4c88
commit 6f2432466d

@ -10,6 +10,8 @@
</div> </div>
</div> </div>
<div class="title">RFID中间件监控平台</div> <div class="title">RFID中间件监控平台</div>
<div class="title1">成功率</div>
<div class="title2">告警统计</div>
<div class="topTitle" style="left: 13%">设备数量:</div> <div class="topTitle" style="left: 13%">设备数量:</div>
<div class="topTitle" style="left: 37.5%">在线数量:</div> <div class="topTitle" style="left: 37.5%">在线数量:</div>
<div class="topTitle" style="left: 62%">离线数量:</div> <div class="topTitle" style="left: 62%">离线数量:</div>
@ -57,12 +59,12 @@
</div> </div>
</vue3ScrollSeamless> </vue3ScrollSeamless>
</div> </div>
<div class="center" ref="scrollNodeRef" @mouseenter="hover = true" @mouseleave="mouseleave"> <div class="center" ref="scrollNodeRef" @mouseenter="mouseenter" @mouseleave="mouseleave">
<div v-masonry style="width: 100%; height: 100%" ref="masonryRef"> <div v-masonry style="width: 100%; height: 100%" ref="masonryRef">
<TreeItem v-masonry-tile :data="i" v-for="i in centerData" :key="i.id" :wsData="wsData" /> <TreeItem v-masonry-tile :data="i" v-for="i in centerData" :key="i.id" :wsData="wsData" :copy="false" />
</div> </div>
<div v-masonry style="width: 100%; height: 100%"> <div v-masonry style="width: 100%; height: 100%">
<TreeItem v-masonry-tile :data="i" v-for="i in centerData" :key="i.id" :wsData="wsData" /> <TreeItem v-masonry-tile :data="i" v-for="i in centerData" :key="i.id" :wsData="wsData" :copy="true" />
</div> </div>
</div> </div>
</div> </div>
@ -73,9 +75,12 @@ import thbg from '@/assets/chart/thbg.png';
import trbg from '@/assets/chart/trbg.png'; import trbg from '@/assets/chart/trbg.png';
import TreeItem from './treeItem.vue'; import TreeItem from './treeItem.vue';
import { vue3ScrollSeamless } from 'vue3-scroll-seamless'; import { vue3ScrollSeamless } from 'vue3-scroll-seamless';
import { parseTime } from '@/utils/ruoyi.js';
import { getRealtimeStats, getLocationTree, getSuccessRateTrends } from '@/api/rfid/dashboard'; import { getRealtimeStats, getLocationTree, getSuccessRateTrends } from '@/api/rfid/dashboard';
let timer = null;
function findParentsWithLocationType3(arr) { function findParentsWithLocationType3(arr) {
const result = []; const result = [];
@ -122,6 +127,13 @@ const timeFun = () => {
}; };
}; };
const mouseenter = () => {
hover.value = true;
if (timer) {
clearTimeout(timer);
}
timer = null;
};
const mouseleave = () => { const mouseleave = () => {
setTimeout(() => { setTimeout(() => {
hover.value = false; hover.value = false;
@ -172,7 +184,9 @@ const MenuItem = defineComponent({
})} })}
</ElSubMenu> </ElSubMenu>
) : ( ) : (
<el-menu-item index={`${data.id}`}>{data.locationAlias}</el-menu-item> <el-menu-item onClick={() => scrollToId(data.id)} index={`${data.id}`}>
{data.locationAlias}
</el-menu-item>
); );
} }
}); });
@ -195,6 +209,31 @@ function scrollToBottom(scrollNode, speed = 1) {
requestAnimationFrame(step); requestAnimationFrame(step);
} }
const scrollToId = (id) => {
const container = scrollNodeRef.value;
if (!container) return;
const target = container.querySelector(`#tree${id}`);
if (target) {
const containerTop = container.getBoundingClientRect().top;
const targetTop = target.getBoundingClientRect().top;
const scrollOffset = targetTop - containerTop + container.scrollTop;
container.scrollTo({
top: scrollOffset,
behavior: 'smooth' //
});
hover.value = true;
if (timer) {
clearTimeout(timer);
}
timer = null;
timer = setTimeout(() => {
hover.value = false;
}, 5000);
}
};
const getData = () => { const getData = () => {
getSuccessRateTrends().then((res) => { getSuccessRateTrends().then((res) => {
successRateRef.value.setData({ successRateRef.value.setData({
@ -233,7 +272,14 @@ const getData = () => {
}, },
xAxis: { xAxis: {
type: 'category', type: 'category',
data: res.data.map((item) => item.timePoint) data: res.data.map((item) => item.timePoint),
axisLabel: {
show: true,
color: '#7ae7f8'
},
axisLine: {
show: false
}
}, },
grid: { grid: {
top: 30, top: 30,
@ -242,10 +288,18 @@ const getData = () => {
bottom: 30 bottom: 30
}, },
yAxis: { yAxis: {
name: '%',
nameTextStyle: {
color: '#7ae7f8'
},
splitLine: { splitLine: {
show: false show: false
}, },
type: 'value' type: 'value',
axisLabel: {
show: true,
color: '#7ae7f8'
}
}, },
series: [ series: [
{ {
@ -284,6 +338,7 @@ const getData = () => {
getLocationTree().then((res) => { getLocationTree().then((res) => {
treeData.value = res.data; treeData.value = res.data;
centerData.value = findParentsWithLocationType3(res.data); centerData.value = findParentsWithLocationType3(res.data);
console.log(centerData.value);
nextTick(() => { nextTick(() => {
scrollToBottom(scrollNodeRef.value, 2); scrollToBottom(scrollNodeRef.value, 2);
}); });
@ -333,11 +388,37 @@ const getSocket = () => {
const socket = new WebSocket('ws://192.168.0.16:7181/ws'); const socket = new WebSocket('ws://192.168.0.16:7181/ws');
socket.addEventListener('open', () => { socket.addEventListener('open', () => {
// console.log(' WebSocket '); console.log('✅ WebSocket 连接成功');
}); });
socket.addEventListener('message', (event) => { socket.addEventListener('message', (event) => {
// console.log(JSON.parse(event.data)); let data = JSON.parse(event.data);
console.log(data);
if (data?.deviceStatus === '1') {
wsData.value[data.deviceId] = data;
}
if (data?.deviceStatus === '2' && data?.alarmFlag === '1') {
tableData.value.push({
alarmTime: parseTime(data.recordTime),
deviceName: wsData.value[data.deviceId]?.epcStr.trimStart() || '',
location: centerData.value.flatMap((item) => item.children || []).find((child) => child.deviceId === targetDeviceId)?.locationAlias || '',
alarmAction: '未获取到标签'
});
if (data.dataType === '2') {
wsData.value[data.deviceId] = data;
}
}
if (data?.deviceStatus === '3' && data?.alarmFlag === '1') {
tableData.value.push({
alarmTime: parseTime(data.recordTime),
deviceName: wsData.value[data.deviceId]?.epcStr.trimStart() || '',
location: centerData.value.flatMap((item) => item.children || []).find((child) => child.deviceId === targetDeviceId)?.locationAlias || '',
alarmAction: '链接断开'
});
if (data.dataType === '2') {
wsData.value[data.deviceId] = data;
}
}
}); });
socket.addEventListener('close', () => { socket.addEventListener('close', () => {
@ -353,7 +434,6 @@ const getSocket = () => {
onMounted(() => { onMounted(() => {
getData(); getData();
getSocket(); getSocket();
wsData.value[data.deviceId] = data;
}); });
</script> </script>
<style scoped lang="less"> <style scoped lang="less">
@ -434,6 +514,28 @@ onMounted(() => {
color: transparent; color: transparent;
} }
.title1 {
position: absolute;
top: 27.9%;
left: 76.5%;
transform: translateY(-50%);
font-size: 1vw;
font-weight: 700;
letter-spacing: 0.2vw;
color: #7ae7f8;
}
.title2 {
position: absolute;
top: 61.5%;
left: 76.5%;
transform: translateY(-50%);
font-size: 1vw;
font-weight: 700;
letter-spacing: 0.2vw;
color: #7ae7f8;
}
.topTitle { .topTitle {
position: absolute; position: absolute;
top: 16.7%; top: 16.7%;

@ -2,19 +2,25 @@
<div class="node" ref="container"> <div class="node" ref="container">
<!-- 主节点 --> <!-- 主节点 -->
<div class="node-main" ref="nodeMain"> <div class="node-main" ref="nodeMain">
<div class="node-icon" ref="nodeIcon"></div> <div class="node-icon" ref="nodeIcon" :id="copy ? '' : `tree${data.id}`"></div>
<div class="node-title">{{ data.locationAlias }}</div> <div class="node-title">{{ data.locationAlias }}</div>
</div> </div>
<!-- 子节点 --> <!-- 子节点 -->
<div class="children"> <div class="children">
<div v-for="(child, index) in data.children" :key="index" class="child-item" :ref="(el) => (childRefs[index] = el)"> <div
<div class="child-icon"></div> :id="copy ? '' : `tree${child.id}`"
v-for="(child, index) in data.children"
:key="index"
:class="wsData[data.deviceId]?.epcStr ? 'child-item' : 'child-item1'"
:ref="(el) => (childRefs[index] = el)"
>
<div :class="wsData[data.deviceId]?.epcStr ? 'child-icon' : 'child-icon1'"></div>
<div class="child-info"> <div class="child-info">
<div class="child-name"> <div class="child-name">
工位名称: <span style="font-size: 12px">{{ child.locationAlias }}</span> 工位名称: <span style="font-size: 12px">{{ child.locationAlias }}</span>
</div> </div>
<div class="child-code">编码{{ wsData[data.deviceId]?.epcStr?.trimStart()}}</div> <div class="child-code">编码{{ wsData[data.deviceId]?.epcStr?.trimStart() }}</div>
<div class="child-time">时间{{ parseTime(wsData[data.deviceId]?.recordTime) }}</div> <div class="child-time">时间{{ parseTime(wsData[data.deviceId]?.recordTime) }}</div>
</div> </div>
</div> </div>
@ -29,7 +35,7 @@
<script setup> <script setup>
import { ref, reactive, onMounted, nextTick, watchEffect } from 'vue'; import { ref, reactive, onMounted, nextTick, watchEffect } from 'vue';
import {parseTime} from '@/utils/ruoyi.js' import { parseTime } from '@/utils/ruoyi.js';
const props = defineProps({ const props = defineProps({
data: { data: {
@ -40,6 +46,10 @@ const props = defineProps({
type: Object, type: Object,
required: true required: true
}, },
copy: {
type: Boolean,
default: false
}
}); });
const container = ref(null); const container = ref(null);
@ -152,7 +162,21 @@ watchEffect(() => {
overflow: hidden; overflow: hidden;
} }
.child-item:last-child { .child-item1 {
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/430786e7de542172c80b3882e4ea8dbd.png');
background-size: 100% 100%;
background-repeat: no-repeat;
overflow: hidden;
}
.child-item:last-child,
.child-item1:last-child {
margin-bottom: 0; margin-bottom: 0;
} }
@ -161,7 +185,21 @@ watchEffect(() => {
height: 50px; height: 50px;
border-radius: 4px; border-radius: 4px;
margin-right: 8px; margin-right: 8px;
background-image: url('@/assets/chart/e06ce08b1a179471f9600f1d53ec3781.png'); background-image: url('@/assets/chart/device1.png');
background-size: 100% 100%;
background-repeat: no-repeat;
position: absolute;
top: 50%;
left: 12px;
transform: translateY(-50%);
}
.child-icon1 {
width: 50px;
height: 50px;
border-radius: 4px;
margin-right: 8px;
background-image: url('@/assets/chart/device2.png');
background-size: 100% 100%; background-size: 100% 100%;
background-repeat: no-repeat; background-repeat: no-repeat;
position: absolute; position: absolute;

@ -37,7 +37,7 @@
</el-card> </el-card>
</el-col> </el-col>
<el-col :xs="12" :sm="12" :md="6"> <el-col :xs="12" :sm="12" :md="6">
<el-card shadow="hover" class="shortcut-card" @click="$router.push('/rfid/dashboard')"> <el-card shadow="hover" class="shortcut-card" @click="$router.push('/chart1')">
<div class="shortcut-inner"> <div class="shortcut-inner">
<el-icon :size="36" color="#f56c6c"><DataAnalysis /></el-icon> <el-icon :size="36" color="#f56c6c"><DataAnalysis /></el-icon>
<span>监控看板</span> <span>监控看板</span>
@ -90,7 +90,7 @@
<el-table-column prop="locationAlias" label="位置名称" min-width="120" /> <el-table-column prop="locationAlias" label="位置名称" min-width="120" />
<el-table-column prop="locationType" label="位置类型" width="100" align="center"> <el-table-column prop="locationType" label="位置类型" width="100" align="center">
<template #default="{ row }"> <template #default="{ row }">
<dict-tag :options="location_type" :value="row.locationType"/> <dict-tag :options="location_type" :value="row.locationType" />
</template> </template>
</el-table-column> </el-table-column>
<el-table-column prop="childCount" label="子位置数" width="100" align="center" /> <el-table-column prop="childCount" label="子位置数" width="100" align="center" />
@ -124,10 +124,10 @@
<script setup name="Index" lang="ts"> <script setup name="Index" lang="ts">
import { onMounted, ref, getCurrentInstance, toRefs, type ComponentInternalInstance, onUnmounted } from 'vue'; import { onMounted, ref, getCurrentInstance, toRefs, type ComponentInternalInstance, onUnmounted } from 'vue';
import { getHomeStats, type HomeStatsVO } from "@/api/rfid/statistics"; import { getHomeStats, type HomeStatsVO } from '@/api/rfid/statistics';
const { proxy } = getCurrentInstance() as ComponentInternalInstance; const { proxy } = getCurrentInstance() as ComponentInternalInstance;
const { location_type } = toRefs<any>(proxy?.useDict("location_type")); const { location_type } = toRefs<any>(proxy?.useDict('location_type'));
const stats = ref<Partial<HomeStatsVO>>({}); const stats = ref<Partial<HomeStatsVO>>({});
const currentTime = ref(''); const currentTime = ref('');
@ -136,8 +136,12 @@ let timer: number | null = null;
const updateTime = () => { const updateTime = () => {
const now = new Date(); const now = new Date();
currentTime.value = now.toLocaleString('zh-CN', { currentTime.value = now.toLocaleString('zh-CN', {
year: 'numeric', month: '2-digit', day: '2-digit', year: 'numeric',
hour: '2-digit', minute: '2-digit', second: '2-digit' month: '2-digit',
day: '2-digit',
hour: '2-digit',
minute: '2-digit',
second: '2-digit'
}); });
}; };
@ -146,7 +150,7 @@ const loadData = async () => {
const res = await getHomeStats(); const res = await getHomeStats();
stats.value = res.data; stats.value = res.data;
} catch (e) { } catch (e) {
console.error("获取首页数据失败", e); console.error('获取首页数据失败', e);
} }
}; };
@ -184,7 +188,7 @@ onUnmounted(() => {
margin: 0 0 8px 0; margin: 0 0 8px 0;
} }
p { p {
color: rgba(255,255,255,0.85); color: rgba(255, 255, 255, 0.85);
font-size: 14px; font-size: 14px;
margin: 0; margin: 0;
} }
@ -197,7 +201,7 @@ onUnmounted(() => {
transition: all 0.3s; transition: all 0.3s;
&:hover { &:hover {
transform: translateY(-4px); transform: translateY(-4px);
box-shadow: 0 4px 12px rgba(0,0,0,0.15); box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
} }
:deep(.el-card__body) { :deep(.el-card__body) {
padding: 24px 16px; padding: 24px 16px;
@ -230,9 +234,18 @@ onUnmounted(() => {
font-size: 20px; font-size: 20px;
font-weight: 600; font-weight: 600;
color: #303133; color: #303133;
&.text-primary { color: #409eff; }
&.text-success { color: #67c23a; } &.text-primary {
&.text-warning { color: #e6a23c; } color: #409eff;
}
&.text-success {
color: #67c23a;
}
&.text-warning {
color: #e6a23c;
}
} }
} }
:deep(.el-divider) { :deep(.el-divider) {

Loading…
Cancel
Save