实时数据页面提交

This commit is contained in:
stt
2025-12-04 16:29:46 +08:00
parent ce9caa8729
commit 1b23355134
2 changed files with 303 additions and 190 deletions

View File

@@ -1,27 +1,45 @@
<template> <template>
<div class="container"> <div class="container">
<!-- 使用 v-for 遍历四个角落 --> <!-- 使用 v-for 遍历四个角落 -->
<div v-for="corner in corners" v-show="corner.show" :key="corner.id" :class="['corner', corner.className]"> <div v-for="corner in corners" :key="corner.id" :class="['corner', corner.className]">
<div class="content"> <div class="content">
<div class="title" style="display: flex"> <div class="title">{{ corner.title }}</div>
<img src="@/assets/img/lightning.png" class="title_img" /> <el-descriptions :column="1" size="small" label-width="70px" border>
<span>{{ corner.title }}</span> <el-descriptions-item v-for="(item, index) in corner.data" :key="index" :label="item.label">
</div> <div v-html="item.value" v-if="item.label !== '暂降次数'"></div>
<vxe-table <!-- 跑马灯 -->
:data="corner.data" <!-- <div v-else style="display: flex">
size="small" <div style="width: 30px">{{ corner.raceLists.length }}</div>
border <div class="simple-marquee">
show-header <div class="marquee-content">
:header-cell-style="{ textAlign: 'center' }" <span
:cell-style="{ textAlign: 'center' }" style="margin-right: 15px"
height="200" v-for="(event, index) in corner.raceLists"
style="padding: 5px;" :key="index"
> @click="goToRace(event)"
<vxe-column field="name" title="名称"></vxe-column> >
<vxe-column field="a" title="A" width="40"></vxe-column> {{
<vxe-column field="b" title="B" width="40"></vxe-column> index +
<vxe-column field="c" title="C" width="40"></vxe-column> 1 +
</vxe-table> '、' +
event.startTime +
'发生' +
event.eventType +
',' +
'残余电压:' +
(event.featureAmplitude * 100).toFixed(2) +
'%' +
',' +
'持续时间:' +
event.duration +
'S'
}}
</span>
</div>
</div>
</div> -->
</el-descriptions-item>
</el-descriptions>
</div> </div>
<span class="close-btn" @click="closeCorner(corner.id)"> <span class="close-btn" @click="closeCorner(corner.id)">
<Close /> <Close />
@@ -32,10 +50,14 @@
<script setup lang="ts"> <script setup lang="ts">
import { ref, onMounted, onBeforeUnmount, nextTick, reactive } from 'vue' import { ref, onMounted, onBeforeUnmount, nextTick, reactive } from 'vue'
// import { clickImage } from "@/api/manage_wx"; import { clickImage } from '@/api/manage_wx'
import { Close } from '@element-plus/icons-vue' import { Close } from '@element-plus/icons-vue'
import socketClient from '@/utils/webSocketClient' import socketClient from '@/utils/webSocketClient'
// import { useStore } from 'vuex'
// 定义接收的 props
const props = defineProps<{
eventList?: []
}>()
//开始创建webSocket客户端 //开始创建webSocket客户端
const dataSocket = reactive({ const dataSocket = reactive({
socketServe: socketClient.Instance socketServe: socketClient.Instance
@@ -47,14 +69,7 @@ const corners = ref([
title: '左上', title: '左上',
className: 'top-left', className: 'top-left',
show: false, show: false,
data: [ data: [] as { label: string; value: string }[],
{ name: '电压', a: 1098, b: 2080, c: 3006 },
{ name: '电流', a: 15, b: 25, c: 35 },
{ name: '电压畸变率', a: 12, b: 22, c: 32 },
{ name: '电流畸变率', a: 18, b: 28, c: 38 },
{ name: '电压偏值', a: 12, b: 22, c: 32 },
{ name: '电压不平衡', a: 18, b: 28, c: 38 }
] as any[],
elementId: '', // 记录该角落对应的元素ID elementId: '', // 记录该角落对应的元素ID
raceLists: [] as any[] // 为每个角落添加独立的跑马灯数据存储 raceLists: [] as any[] // 为每个角落添加独立的跑马灯数据存储
}, },
@@ -63,7 +78,7 @@ const corners = ref([
title: '右上', title: '右上',
className: 'top-right', className: 'top-right',
show: false, show: false,
data: [] as any[], data: [] as { label: string; value: string }[],
elementId: '', elementId: '',
raceLists: [] as any[] // 为每个角落添加独立的跑马灯数据存储 raceLists: [] as any[] // 为每个角落添加独立的跑马灯数据存储
} }
@@ -84,69 +99,94 @@ const corners = ref([
// elementId: "", // elementId: "",
// }, // },
]) ])
interface DataTableItem {
name: string
a: number
b: number
c: number
}
const tableData = ref<DataTableItem[]>([
{ name: '电压', a: 1098, b: 2080, c: 3006 },
{ name: '电流', a: 15, b: 25, c: 35 },
{ name: '电压畸变率', a: 12, b: 22, c: 32 },
{ name: '电流畸变率', a: 18, b: 28, c: 38 },
{ name: '电压偏值', a: 12, b: 22, c: 32 },
{ name: '电压不平衡', a: 18, b: 28, c: 38 }
])
const steadyStateList = ref([]) const steadyStateList = ref([])
// const store = useStore()
const selectedId = ref('') const selectedId = ref('')
// 内部响应式数据 // 内部响应式数据
const eventList = ref<any[]>([]) const eventList = ref([])
// 点击跑马灯展示弹框
const dipDetail = ref(null)
// const handleClickImage = async (elementId: string) => { // const handleClickImage = async (elementId: string) => {
// // 检查 elementId 是否有值,没有值则直接返回空数组 // // 检查 elementId 是否有值,没有值则直接返回空数组
// if (!elementId) { // if (!elementId) {
// eventList.value = []; // eventList.value = []
// return; // return
// }
// try {
// // 发送点击图片请求
// const res = await clickImage({ lineId: elementId });
// // 确保返回的数据是数组格式,并且过滤掉 null/undefined 元素
// let dataToStore: any[] = [];
// if (Array.isArray(res.data)) {
// dataToStore = res.data.filter(
// (item:any) => item !== null && item !== undefined
// );
// } else if (res.data && Array.isArray(res.data.records)) {
// dataToStore = res.data.records.filter(
// (item:any) => item !== null && item !== undefined
// );
// } else if (res.data) {
// // 如果是单个对象且不为 null
// if (res.data !== null && res.data !== undefined) {
// dataToStore = [res.data];
// }
// } // }
// try {
// // 发送点击图片请求
// const res = await clickImage({
// lineId: elementId,
// searchBeginTime: store.state.timeValue[0],
// searchEndTime: store.state.timeValue[1]
// })
// eventList.value = dataToStore; // // 确保返回的数据是数组格式,并且过滤掉 null/undefined 元素
// } catch (error) { // let dataToStore: any[] = []
// console.error("调用 clickImage 接口出错:", error);
// // 出错时设置为空数组,避免后续处理出错 // if (Array.isArray(res.data)) {
// eventList.value = []; // dataToStore = res.data.filter(item => item !== null && item !== undefined)
// } // } else if (res.data && Array.isArray(res.data.records)) {
// }; // dataToStore = res.data.records.filter(item => item !== null && item !== undefined)
// } else if (res.data) {
// // 如果是单个对象且不为 null
// if (res.data !== null && res.data !== undefined) {
// dataToStore = [res.data]
// }
// }
// eventList.value = dataToStore
// } catch (error) {
// console.error('调用 clickImage 接口出错:', error)
// // 出错时设置为空数组,避免后续处理出错
// eventList.value = []
// }
// }
// 监听 props 变化
// watch(
// () => props.eventList,
// (newVal) => {
// if (newVal && Array.isArray(newVal)) {
// eventList.value = [...newVal];
// dataLoaded.value = true;
// } else {
// dataLoaded.value = false;
// }
// },
// { immediate: true, deep: true }
// );
// 记录显示顺序,用于循环替换 // 记录显示顺序,用于循环替换
const displayOrder = ref<number[]>([]) const displayOrder = ref<number[]>([])
// 计算跑马灯动画时长(毫秒)
const calculateMarqueeDuration = corner => {
if (!corner.raceLists || corner.raceLists.length === 0) {
return 25000 // 默认25秒
}
// 计算所有事件文本的总长度
let totalTextLength = 0
corner.raceLists.forEach((event, index) => {
const text = `${index + 1}${event.startTime}发生${event.eventType},残余电压:${(
event.featureAmplitude * 100
).toFixed(2)}%,持续时间:${event.duration}S`
totalTextLength += text.length
})
// 添加分隔符长度和边距
const separatorLength = corner.raceLists.length > 1 ? (corner.raceLists.length - 1) * 15 : 0 // margin-right: 15px
totalTextLength += separatorLength
// 根据文本长度计算时长(减慢速度)
// 将每字符需要的时间从50ms增加到80ms最少8秒最多90秒
const duration = Math.min(Math.max(totalTextLength * 80, 8000), 90000)
return duration
}
// 更新指定角落数据的函数 // 更新指定角落数据的函数
const updateCornerData = (cornerIndex: number, dataItem: any, elementId: string) => { const updateCornerData = (cornerIndex: number, dataItem: any, elementId: string) => {
// 更新标题为 objName // 更新标题为 objName
@@ -177,6 +217,15 @@ const updateCornerData = (cornerIndex: number, dataItem: any, elementId: string)
// 记录该角落对应的元素ID // 记录该角落对应的元素ID
corners.value[cornerIndex].elementId = elementId corners.value[cornerIndex].elementId = elementId
corners.value[cornerIndex].show = true corners.value[cornerIndex].show = true
// 设置动画时长
nextTick(() => {
const duration = calculateMarqueeDuration(corners.value[cornerIndex])
const marqueeElement = document.querySelector(`.${corners.value[cornerIndex].className} .marquee-content`)
if (marqueeElement) {
;(marqueeElement as HTMLElement).style.setProperty('--marquee-duration', `${duration}ms`)
}
})
} }
// 显示下一个角落的函数 // 显示下一个角落的函数
@@ -276,8 +325,8 @@ const init = () => {
corners.value.forEach((corner, index) => { corners.value.forEach((corner, index) => {
let str = `` let str = ``
steadyState steadyState
.filter((item: any) => item.lineId == corner.elementId) .filter(item => item.lineId == corner.elementId)
.forEach((item: any) => { .forEach(item => {
if (item.value == 3.1415926) { if (item.value == 3.1415926) {
str += `<div>${item.statisticalName}/</div>` str += `<div>${item.statisticalName}/</div>`
} else { } else {
@@ -288,6 +337,15 @@ const init = () => {
corner.data.length > 0 corner.data.length > 0
? (corner.data[2].value = `<div style="max-height: 100px;overflow-y: auto;">${str} </div>`) ? (corner.data[2].value = `<div style="max-height: 100px;overflow-y: auto;">${str} </div>`)
: '' : ''
// 更新跑马灯动画时长
nextTick(() => {
const duration = calculateMarqueeDuration(corner)
const marqueeElement = document.querySelector(`.${corner.className} .marquee-content`)
if (marqueeElement) {
;(marqueeElement as HTMLElement).style.setProperty('--marquee-duration', `${duration}ms`)
}
})
}) })
} }
}) })
@@ -312,6 +370,10 @@ const send = () => {
}, 1000 * 60) }, 1000 * 60)
} }
const goToRace = row => {
dipDetail.value.open(row.measurementPointId)
}
// 监听来自 iframe 的消息 // 监听来自 iframe 的消息
window.addEventListener('message', async function (event) { window.addEventListener('message', async function (event) {
// 安全起见可以验证消息来源origin // 安全起见可以验证消息来源origin
@@ -323,7 +385,7 @@ window.addEventListener('message', async function (event) {
selectedId.value = event.data.selectedId selectedId.value = event.data.selectedId
// 调用接口获取最新数据 // 调用接口获取最新数据
// await handleClickImage(clickedElementId); // await handleClickImage(clickedElementId)
// 根据接收到的元素LineId显示对应数据 // 根据接收到的元素LineId显示对应数据
await showNextCorner(clickedElementId) await showNextCorner(clickedElementId)
@@ -339,6 +401,41 @@ onBeforeUnmount(() => {
}) })
</script> </script>
<style>
/* 走马灯样式 - 支持逐条显示 */
.simple-marquee {
width: 230px;
height: 24px;
overflow: hidden;
position: relative;
white-space: nowrap;
}
.marquee-content {
display: inline-block;
padding-left: 100%;
/* animation: marquee-single 15s linear infinite; */
animation: marquee-single var(--marquee-duration, 25s) linear infinite;
line-height: 24px;
text-decoration: underline;
text-decoration-color: #4877f6;
}
@keyframes marquee-single {
0% {
transform: translateX(0);
}
100% {
transform: translateX(-100%);
}
}
/* 鼠标悬停时暂停动画 */
.simple-marquee:hover .marquee-content {
animation-play-state: paused;
cursor: pointer;
}
</style>
<style scoped lang="scss"> <style scoped lang="scss">
.container { .container {
@@ -348,9 +445,9 @@ onBeforeUnmount(() => {
.corner { .corner {
width: 240px; width: 240px;
/* height: 135px; */ /* height: 135px; */
background-color: #fff; background-color: #2b2d3a90;
position: absolute; position: absolute;
color: #000; color: 000;
/* font-weight: bold; */ /* font-weight: bold; */
/* 添加弹出动画 */ /* 添加弹出动画 */
opacity: 0; opacity: 0;
@@ -396,20 +493,15 @@ onBeforeUnmount(() => {
.title { .title {
font-size: 16px; font-size: 16px;
// text-align: center; /* text-align: center; */
padding: 5px; /* margin-bottom: 8px; */
color: #fff; padding: 8px;
border-bottom: 1px solid #444; /* color: #409eff; */
background-color: var(--el-color-primary); border-bottom: 1px solid #fff;
background-color: #fff;
border-radius: 8px 8px 0 0; border-radius: 8px 8px 0 0;
} }
.title_img {
width: 25px;
height: 25px;
margin-right: 10px;
}
.data-item { .data-item {
display: flex; display: flex;
margin-bottom: 4px; margin-bottom: 4px;
@@ -444,16 +536,4 @@ onBeforeUnmount(() => {
.indicator { .indicator {
display: flex; display: flex;
} }
.table {
width: 100%;
border-collapse: collapse;
}
.table th,
.table td {
border: 1px solid #000;
text-align: center;
font-size: 12px;
}
</style> </style>

View File

@@ -2,13 +2,13 @@
<div class="default-main" :style="{ padding: prop.height ? '0px !important' : '10px' }"> <div class="default-main" :style="{ padding: prop.height ? '0px !important' : '10px' }">
<!-- 实时数据 --> <!-- 实时数据 -->
<!-- 添加加载事件监听 --> <!-- 添加加载事件监听 -->
<div class="dataBox" :style="{ height: prop.height || pageHeight.height }" > <div class="dataBox" :style="{ height: prop.height || pageHeight.height }">
<div <div
class="iframe-container" class="iframe-container"
:style="{ :style="{
boxShadow: `var(--el-box-shadow-light)` boxShadow: `var(--el-box-shadow-light)`
}" }"
style="position: relative;" style="position: relative"
> >
<iframe <iframe
:src="iframeSrc" :src="iframeSrc"
@@ -19,17 +19,21 @@
id="iframeLeft" id="iframeLeft"
@load="onIframeLoad" @load="onIframeLoad"
></iframe> ></iframe>
<IframeDia <IframeDia style="position: absolute; top: 0px; right: 0px; left: 0px" />
style="position: absolute; top: 0px; right: 0px; left: 0px"
/>
</div> </div>
<el-card class="bottom-container " style="min-height: 230px;"> <el-card class="bottom-container" style="min-height: 230px">
<div class="buttonBox"> <div class="buttonBox">
<el-button type="primary" icon="el-icon-Aim" @click="reset">复位</el-button> <el-button type="primary" icon="el-icon-Aim" @click="reset">复位</el-button>
</div> </div>
<div class="tableBox"> <div class="tableBox">
<Table ref="tableRef" height="100%"></Table> <!-- <Table ref="tableRef" height="100%"></Table> -->
<vxe-table border auto-resize height="100%" :data="tableData" ref="tableRef">
<vxe-column type="seq" title="序号" align="center" width="80px"></vxe-column>
<vxe-column field="date" align="center" title="时间" width="200px"></vxe-column>
<vxe-column field="name" align="center" title="监测点名" width="200px"></vxe-column>
<vxe-column field="address" align="center" title="事件描述"></vxe-column>
</vxe-table>
</div> </div>
</el-card> </el-card>
</div> </div>
@@ -48,85 +52,115 @@ import IframeDia from './iframeDia.vue'
// }>() // }>()
const prop = defineProps({ const prop = defineProps({
width: { type: [String, Number]}, width: { type: [String, Number] },
height: { type: [String, Number]}, height: { type: [String, Number] },
timeKey: { type: [String, Number]}, timeKey: { type: [String, Number] },
timeValue: { type: Object } timeValue: { type: Object }
}) })
const tableStore: any = new TableStore({ const tableData = ref()
url: '/user-boot/role/selectRoleDetail?id=0',
method: 'POST',
showPage: false, // 在父页面中添加事件监听器
window.addEventListener('message', function (event) {
const { action, data } = event.data
column: [ if (action == 'securityDetailData') {
{ console.log('tableArray:', data)
field: 'index', // 处理接收到的 tableArray 数据
title: '序号', tableData.value = data
width: '80',
formatter: (row: any) => {
return (tableStore.table.params.pageNum - 1) * tableStore.table.params.pageSize + row.rowIndex + 1
}
},
{
title: '时间',
field: 'whetherToGovern',
minWidth: '70'
},
{
title: '监测点名',
field: 'name',
minWidth: '90'
// render: 'customTemplate',
// customTemplate: (row: any) => {
// return `<span style='cursor: pointer;text-decoration: underline;'>${row.name}</span>`
// }
},
{ title: '事件描述', field: 'question', minWidth: '200' }
],
beforeSearchFun: () => {
// tableStore.table.params.searchBeginTime = prop.timeValue?.[0] || getTimeOfTheMonth(prop.timeKey)[0]
// tableStore.table.params.searchEndTime = prop.timeValue?.[1] || getTimeOfTheMonth(prop.timeKey)[1]
},
loadCallback: () => {
tableStore.table.data = [
{
name: '10kV1#电动机',
type: '电动机',
whetherToGovern: '2025-01-01 15:00:00',
question: '3次谐波电压、5次谐波电流、电压不平衡度超标'
},
{
name: '10kV2#(治理后)',
type: '电焊机',
whetherToGovern: '2025-05-01 16:00:00',
question: '所有指标均合格'
},
{
name: '380V电焊机(治理前)',
type: '电焊机',
whetherToGovern: '2025-06-01 15:00:00',
question: '5次谐波电流、电压不平衡度超标'
},
{
name: '380V水泵机',
type: '电动机',
whetherToGovern: '2025-08-01 15:00:00',
question: '所有指标均合格'
}
]
} }
}) })
// const tableStore: any = new TableStore({
// url: '/user-boot/role/selectRoleDetail?id=0',
// method: 'POST',
// showPage: false,
// column: [
// {
// field: 'index',
// title: '序号',
// width: '80',
// formatter: (row: any) => {
// return (tableStore.table.params.pageNum - 1) * tableStore.table.params.pageSize + row.rowIndex + 1
// }
// },
// {
// title: '时间',
// field: 'whetherToGovern',
// minWidth: '70'
// },
// {
// title: '监测点名',
// field: 'name',
// minWidth: '90'
// // render: 'customTemplate',
// // customTemplate: (row: any) => {
// // return `<span style='cursor: pointer;text-decoration: underline;'>${row.name}</span>`
// // }
// },
// { title: '事件描述', field: 'question', minWidth: '200' }
// ],
// beforeSearchFun: () => {
// // tableStore.table.params.searchBeginTime = prop.timeValue?.[0] || getTimeOfTheMonth(prop.timeKey)[0]
// // tableStore.table.params.searchEndTime = prop.timeValue?.[1] || getTimeOfTheMonth(prop.timeKey)[1]
// },
// loadCallback: () => {
// tableStore.table.data = [
// {
// name: '10kV1#电动机',
// type: '电动机',
// whetherToGovern: '2025-01-01 15:00:00',
// question: '3次谐波电压、5次谐波电流、电压不平衡度超标'
// },
// {
// name: '10kV2#(治理后)',
// type: '电焊机',
// whetherToGovern: '2025-05-01 16:00:00',
// question: '所有指标均合格'
// },
// {
// name: '380V电焊机(治理前)',
// type: '电焊机',
// whetherToGovern: '2025-06-01 15:00:00',
// question: '5次谐波电流、电压不平衡度超标'
// },
// {
// name: '380V水泵机',
// type: '电动机',
// whetherToGovern: '2025-08-01 15:00:00',
// question: '所有指标均合格'
// }
// ]
// }
// })
const tableRef = ref() const tableRef = ref()
provide('tableRef', tableRef) // provide('tableRef', tableRef)
const pageHeight = mainHeight(40) // const pageHeight = mainHeight(40)
provide('tableStore', tableStore) // provide('tableStore', tableStore)
const reset = () => { const reset = () => {
tableRef.value.reset() // 向 iframe 发送复位事件
sendResetToIframe();
// 清空表格数据
// tableData.value = [];
}
// 向 iframe 发送复位消息的函数
const sendResetToIframe = () => {
const iframe = document.getElementById('iframeLeft') as HTMLIFrameElement;
if (iframe && iframe.contentWindow) {
iframe.contentWindow.postMessage(
{
type: 'RESET_EVENT',
payload: true
},
'*' // 生产环境中应替换为具体的域名
);
}
} }
const iframeSrc = ref('') const iframeSrc = ref('')
@@ -154,10 +188,9 @@ const iframeSrc = ref('')
onMounted(() => { onMounted(() => {
iframeSrc.value = iframeSrc.value =
'http://192.168.1.179:4001' + 'http://192.168.1.179:4001' + `/zutai/?id=4b4f7f4260198776594f5f9d93a532e8&&name=stt&&preview=true#/preview_YPT`
`/zutai/?id=4b4f7f4260198776594f5f9d93a532e8&&name=stt&&preview=true#/preview_YPT`
tableStore.index() // tableStore.index()
// 监听来自 eventStatistics 组件的消息 // 监听来自 eventStatistics 组件的消息
window.addEventListener('message', handleMessage) window.addEventListener('message', handleMessage)