feat: 适配设备运行状态实时图表与日志列表大数据量展示
- 实时图表数据排序方式由升序改为降序,提升最新数据的展示优先级 - 增加动态优化逻辑,根据数据量自动调整采样、动画和符号显示策略 - 优化时间轴标签显示,避免重叠并提升可读性 - 日志列表从 Ant Design Table 迁移至 VxeTable,提升大数据量下的渲染性能 - 调整日志接口分页参数,确保一次性加载最多9999条数据 - 监听日志数据变化并动态更新表格内容 - 优化 Tab 组件行为,销毁非活跃面板以减少内存占用 - 统一多个模拟组件中的提示文本表述
This commit is contained in:
parent
b012582f68
commit
019029f3ce
|
@ -28,7 +28,7 @@ const renderChart = () => {
|
|||
if (props.data.length === 0) return;
|
||||
|
||||
// 按时间升序排序
|
||||
const sortedData = [...props.data].sort((a, b) => a.timestamp - b.timestamp);
|
||||
const sortedData = [...props.data].reverse();
|
||||
|
||||
// ECharts时间序列数据格式:[时间, 数值]
|
||||
const seriesData = sortedData.map((item) => [
|
||||
|
@ -36,30 +36,35 @@ const renderChart = () => {
|
|||
Number.parseFloat(item.value),
|
||||
]);
|
||||
|
||||
// 动态优化参数
|
||||
const dataCount = seriesData.length;
|
||||
let sampling = 'false';
|
||||
let showSymbol = true;
|
||||
let animation = true;
|
||||
let dataZoomStart = 0;
|
||||
|
||||
if (dataCount > 2000) {
|
||||
sampling = 'lttb';
|
||||
showSymbol = false;
|
||||
animation = false;
|
||||
dataZoomStart = 90; // 只显示最后10%
|
||||
} else if (dataCount > 500) {
|
||||
sampling = 'average';
|
||||
showSymbol = false;
|
||||
animation = false;
|
||||
dataZoomStart = 80;
|
||||
} else {
|
||||
sampling = 'false';
|
||||
showSymbol = true;
|
||||
animation = true;
|
||||
dataZoomStart = 0;
|
||||
}
|
||||
|
||||
renderEcharts({
|
||||
animation,
|
||||
tooltip: {
|
||||
// 提示框组件
|
||||
trigger: 'axis',
|
||||
formatter(params) {
|
||||
const item = params[0];
|
||||
const date = new Date(item.value[0]);
|
||||
const timeStr = `${date.getFullYear()}-${(date.getMonth() + 1)
|
||||
.toString()
|
||||
.padStart(
|
||||
2,
|
||||
'0',
|
||||
)}-${date.getDate().toString().padStart(2, '0')} ${date
|
||||
.getHours()
|
||||
.toString()
|
||||
.padStart(
|
||||
2,
|
||||
'0',
|
||||
)}:${date.getMinutes().toString().padStart(2, '0')}:${date
|
||||
.getSeconds()
|
||||
.toString()
|
||||
.padStart(2, '0')}`;
|
||||
return `时间: ${timeStr}<br/>值: ${item.value[1]}`;
|
||||
},
|
||||
},
|
||||
grid: {
|
||||
top: 30,
|
||||
|
@ -70,7 +75,11 @@ const renderChart = () => {
|
|||
name: '时间',
|
||||
axisLabel: {
|
||||
rotate: 30,
|
||||
formatter(value: number) {
|
||||
hideOverlap: true, // 自动隐藏重叠标签
|
||||
interval: 'auto', // 自动间隔显示
|
||||
align: 'center', // 让多行内容居中
|
||||
margin: 32,
|
||||
formatter(value) {
|
||||
const date = new Date(value);
|
||||
return `${date.getFullYear()}-${(date.getMonth() + 1)
|
||||
.toString()
|
||||
|
@ -100,7 +109,7 @@ const renderChart = () => {
|
|||
show: true, // 显示组件
|
||||
xAxisIndex: [0], // 控制第一个x轴
|
||||
top: '90%', // 放置在底部
|
||||
start: 0, // 初始起始范围比例为0%
|
||||
start: dataZoomStart, // 初始起始范围比例为0%
|
||||
end: 100, // 初始结束范围比例为100%
|
||||
left: '17%',
|
||||
width: '65%',
|
||||
|
@ -108,16 +117,18 @@ const renderChart = () => {
|
|||
{
|
||||
type: 'inside', // 内置型
|
||||
xAxisIndex: [0], // 控制第一个x轴
|
||||
start: 0,
|
||||
start: dataZoomStart,
|
||||
end: 100,
|
||||
},
|
||||
],
|
||||
series: [
|
||||
{
|
||||
name: '数据系列',
|
||||
name: '值',
|
||||
type: 'line',
|
||||
data: seriesData,
|
||||
// 可选:配置线条样式
|
||||
sampling,
|
||||
showSymbol,
|
||||
lineStyle: {
|
||||
color: '#5470C6',
|
||||
},
|
||||
|
|
|
@ -1,4 +1,6 @@
|
|||
<script setup lang="ts">
|
||||
import type { VxeGridProps } from '#/adapter/vxe-table';
|
||||
|
||||
import { computed, onMounted, onUnmounted, ref, watch } from 'vue';
|
||||
|
||||
import { EllipsisText } from '@vben/common-ui';
|
||||
|
@ -17,11 +19,11 @@ import {
|
|||
Row,
|
||||
Select,
|
||||
Space,
|
||||
Table,
|
||||
Tabs,
|
||||
} from 'ant-design-vue';
|
||||
import dayjs from 'dayjs';
|
||||
|
||||
import { useVbenVxeGrid } from '#/adapter/vxe-table';
|
||||
import { deviceLogList } from '#/api/device/device';
|
||||
import { getWebSocket } from '#/utils/websocket';
|
||||
|
||||
|
@ -33,8 +35,41 @@ interface Props {
|
|||
deviceInfo: any;
|
||||
}
|
||||
|
||||
interface LogType {
|
||||
id: number;
|
||||
name: string;
|
||||
role: string;
|
||||
sex: string;
|
||||
}
|
||||
|
||||
const props = defineProps<Props>();
|
||||
|
||||
const gridOptions: VxeGridProps<LogType> = {
|
||||
round: false,
|
||||
border: false,
|
||||
toolbarConfig: {
|
||||
enabled: false,
|
||||
},
|
||||
columns: [
|
||||
{ type: 'seq', width: 70 },
|
||||
{ field: 'timestamp', title: '时间' },
|
||||
{ field: 'value', title: '值' },
|
||||
],
|
||||
data: [],
|
||||
minHeight: 300,
|
||||
maxHeight: 500,
|
||||
pagerConfig: {
|
||||
enabled: false,
|
||||
},
|
||||
scrollY: {
|
||||
enabled: true,
|
||||
gt: 0,
|
||||
},
|
||||
showOverflow: true,
|
||||
};
|
||||
|
||||
const [Grid, gridApi] = useVbenVxeGrid({ gridOptions });
|
||||
|
||||
const loading = ref(false);
|
||||
const selectedGroup = ref('all');
|
||||
const selectedTypes = ref(['R', 'RW']);
|
||||
|
@ -157,18 +192,6 @@ const onCalendarChange = (val: any) => {
|
|||
dates.value = val;
|
||||
};
|
||||
|
||||
const logColumns = [
|
||||
{ title: '时间', dataIndex: 'timestamp', key: 'timestamp', width: 200 },
|
||||
{
|
||||
title: '值',
|
||||
dataIndex: 'value',
|
||||
key: 'value',
|
||||
width: 150,
|
||||
align: 'center',
|
||||
},
|
||||
// { title: '操作', key: 'action', width: 100 },
|
||||
];
|
||||
|
||||
const openPropertyLog = (property: any) => {
|
||||
currentProperty.value = property;
|
||||
logTabActiveKey.value = 'list';
|
||||
|
@ -190,6 +213,8 @@ const loadPropertyLog = async (field, startTime, endTime) => {
|
|||
fields: field,
|
||||
startTime,
|
||||
endTime,
|
||||
current: 1,
|
||||
size: 9999,
|
||||
orderType: 2,
|
||||
metaDataType: 'property',
|
||||
});
|
||||
|
@ -286,6 +311,11 @@ const closeLogModal = () => {
|
|||
logData.value = [];
|
||||
};
|
||||
|
||||
watch(logData, (data) => {
|
||||
console.log('日志数据变化', data);
|
||||
gridApi.setGridOptions({ data });
|
||||
});
|
||||
|
||||
onMounted(() => {
|
||||
initRuntime();
|
||||
subscribeRealtimeData();
|
||||
|
@ -405,23 +435,13 @@ watch(
|
|||
</Space>
|
||||
</div>
|
||||
|
||||
<Tabs v-model:active-key="logTabActiveKey" class="log-tabs">
|
||||
<Tabs
|
||||
v-model:active-key="logTabActiveKey"
|
||||
class="log-tabs"
|
||||
destroy-inactive-tab-pane
|
||||
>
|
||||
<Tabs.TabPane key="list" tab="列表">
|
||||
<Table
|
||||
:columns="logColumns"
|
||||
:data-source="logData"
|
||||
:loading="logLoading"
|
||||
:pagination="false"
|
||||
size="small"
|
||||
row-key="timestamp"
|
||||
:scroll="{ y: 500 }"
|
||||
>
|
||||
<template #bodyCell="{ column }">
|
||||
<template v-if="column.key === 'action'">
|
||||
<Button type="link" size="small">查看</Button>
|
||||
</template>
|
||||
</template>
|
||||
</Table>
|
||||
<Grid class="log-grid" />
|
||||
</Tabs.TabPane>
|
||||
|
||||
<Tabs.TabPane
|
||||
|
@ -537,6 +557,10 @@ watch(
|
|||
}
|
||||
}
|
||||
|
||||
.log-grid :deep(.vxe-grid) {
|
||||
padding: 0 !important;
|
||||
}
|
||||
|
||||
:deep(.ant-card-body) {
|
||||
padding: 12px;
|
||||
}
|
||||
|
|
|
@ -576,7 +576,7 @@ onMounted(() => {
|
|||
<div class="parameter-box">
|
||||
<div class="parameter-header">参数:</div>
|
||||
<div class="parameter-content">
|
||||
<pre>{{ parameterContent || '点击触发后显示参数' }}</pre>
|
||||
<pre>{{ parameterContent || '点击执行后显示参数' }}</pre>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -752,7 +752,7 @@ onMounted(() => {
|
|||
<div class="parameter-box">
|
||||
<div class="parameter-header">参数:</div>
|
||||
<div class="parameter-content">
|
||||
<pre>{{ parameterContent || '点击执行后显示执行参数' }}</pre>
|
||||
<pre>{{ parameterContent || '点击执行后显示参数' }}</pre>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -812,7 +812,7 @@ onMounted(() => {
|
|||
<div class="parameter-box">
|
||||
<div class="parameter-header">参数:</div>
|
||||
<div class="parameter-content">
|
||||
<pre>{{ parameterContent || '点击执行后显示执行参数' }}</pre>
|
||||
<pre>{{ parameterContent || '点击执行后显示参数' }}</pre>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
Loading…
Reference in New Issue