尝试完善图表更新 bug 图表无法显示
This commit is contained in:
parent
30ffdd1782
commit
0d801118cc
|
@ -52,50 +52,34 @@ public class ServerController(
|
|||
[FromQuery] List<string?>? dataTypes = null, [FromQuery] int? startIndex = 0)
|
||||
{
|
||||
var userId = HttpContext.User.Claims.FirstOrDefault(x => x.Type == ClaimTypes.NameIdentifier)!.Value;
|
||||
//创建一个缓存
|
||||
Dictionary<string, ServerMonitoringData?> temp = new();
|
||||
//创建一个时间缓存
|
||||
//创建查询 流式
|
||||
var query = dbContext.ServerMonitoringData
|
||||
.AsNoTracking() // 不追踪实体状态
|
||||
.Where(x => x.ServerId == serverId) // 筛选服务器ID
|
||||
.Where(x => dataTypes == null || dataTypes.Contains(x.DataType)) // 如果dataTypes不为空,则进一步筛选
|
||||
.OrderBy(x => x.Time)
|
||||
.Skip(Math.Max(startIndex ?? 0, 0)) // 跳过指定数量的记录
|
||||
.Take(1000)
|
||||
.AsAsyncEnumerable(); // 启用流式查询
|
||||
Dictionary<string, List<List<object>>> temp = new();
|
||||
|
||||
await foreach (var data in query)
|
||||
{
|
||||
//计算两者时间差 按秒分割
|
||||
if (!temp.TryGetValue(data.DataType, out var tempData)) tempData = data;
|
||||
temp[data.DataType] = data;
|
||||
var time = tempData?.Time;
|
||||
if (time == null || data.Time == null) continue;
|
||||
var timeDifferenceInSeconds = (data.Time - time)?.TotalSeconds;
|
||||
if (timeDifferenceInSeconds <= 0) continue;
|
||||
var steps = (int)timeDifferenceInSeconds!;
|
||||
var value = double.Parse(data.Data ?? "0");
|
||||
var lastValue = double.Parse(tempData?.Data ?? "0");
|
||||
//计算中间值 贝塞尔曲线
|
||||
var controlPoint = (value + lastValue) / 2;
|
||||
List<List<object>> values = [];
|
||||
for (var i = 0; i < steps; i++)
|
||||
if (!temp.TryGetValue(data.DataType, out var value))
|
||||
{
|
||||
var t = (double)i / (steps - 1);
|
||||
var bezierValue = (1 - t) * (1 - t) * value + 2 * (1 - t) * t * controlPoint + t * t * lastValue;
|
||||
var bezierTime = time.Value.AddSeconds(i);
|
||||
var utcTime = bezierTime.ToUniversalTime();
|
||||
var unixTimestamp = ((DateTimeOffset)utcTime).ToUnixTimeMilliseconds();
|
||||
List<object> innerList =
|
||||
[
|
||||
unixTimestamp,
|
||||
((int)bezierValue).ToString(CultureInfo.InvariantCulture)
|
||||
];
|
||||
values.Add(innerList);
|
||||
value = new List<List<object>>();
|
||||
temp[data.DataType] = value;
|
||||
}
|
||||
|
||||
await hubContext.Clients.Groups(userId).SendAsync("ReceiveDataHistory", data.DataType, values);
|
||||
var unixTimestamp = ((DateTimeOffset)data.Time?.ToUniversalTime()!).ToUnixTimeMilliseconds();
|
||||
value.Add([unixTimestamp!, data.Data!]);
|
||||
}
|
||||
|
||||
foreach (var entry in temp)
|
||||
{
|
||||
await hubContext.Clients.Groups(userId).SendAsync("ReceiveDataHistory", entry.Key, entry.Value, true);
|
||||
}
|
||||
|
||||
return Ok();
|
||||
}
|
||||
|
||||
|
|
|
@ -95,13 +95,14 @@ const items = [
|
|||
<template>
|
||||
<div :id="`card_${id}`" ref="cardRef" class="card-layout ">
|
||||
<div :id="'item' + id" class="card-title vue-draggable-handle">
|
||||
<div></div>
|
||||
<h3>{{ title ?? "默认标题" }}</h3>
|
||||
</div>
|
||||
<component :is="charts.find(x => x.id === chart)?.component" v-if="targetIsVisible&&!isSwitched"
|
||||
:rangeNum="rangeNum"
|
||||
:valueIds="valueIds"
|
||||
:valueNames="valueNames"/>
|
||||
<div class="card-content">
|
||||
<component :is="charts.find(x => x.id === chart)?.component" v-if="targetIsVisible&&!isSwitched"
|
||||
:rangeNum="rangeNum"
|
||||
:valueIds="valueIds"
|
||||
:valueNames="valueNames"/>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
@ -109,46 +110,31 @@ const items = [
|
|||
@import "base";
|
||||
|
||||
.card-layout {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
background: $light-bg-color;
|
||||
border-radius: $radius;
|
||||
box-shadow: 0 10px 30px 0 rgba(17, 38, 146, 0.05);
|
||||
padding: $padding*1.5;
|
||||
position: relative;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: $gap;
|
||||
will-change: scroll-position, contents;
|
||||
border: $border;
|
||||
display: grid;
|
||||
grid-template-rows: 40px 1fr;
|
||||
|
||||
.dark-mode & {
|
||||
background: $dark-bg-color;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
.card-title {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
gap: $gap;
|
||||
color: $light-text-color;
|
||||
position: absolute;
|
||||
z-index: 10;
|
||||
&,
|
||||
h3:hover {
|
||||
cursor: move;
|
||||
}
|
||||
|
||||
.dark-mode & {
|
||||
color: $dark-text-color;
|
||||
}
|
||||
|
||||
div {
|
||||
width: 6px;
|
||||
height: 20px;
|
||||
border-radius: $radius;
|
||||
background: $primary-color;
|
||||
padding: 0 20px;
|
||||
|
||||
h3 {
|
||||
font-size: 14px;
|
||||
line-height: 24px;
|
||||
text-align: left;
|
||||
color: #333;
|
||||
}
|
||||
}
|
||||
.card-content{
|
||||
padding: 0 20px 20px;
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -4,17 +4,13 @@ import {useMainLayoutStore} from "~/strores/UseMainLayoutStore";
|
|||
|
||||
import type {PropType} from "vue";
|
||||
import {ref} from 'vue';
|
||||
import dayjs from "dayjs";
|
||||
import VChart from 'vue-echarts';
|
||||
import _ from "lodash";
|
||||
import {useSessionSignalRStore} from "~/strores/HubStore";
|
||||
|
||||
const dataStore = useDataStore()
|
||||
const hubStore=useSessionSignalRStore()
|
||||
const hubStore = useSessionSignalRStore()
|
||||
const mainLayoutStore = useMainLayoutStore()
|
||||
type ArbitraryKeyValuePairs = {
|
||||
[key: string]: (number | string)[];
|
||||
};
|
||||
const chartRef = ref<any>(null)
|
||||
const isLoading = ref<boolean>(true)
|
||||
const props = defineProps({
|
||||
|
@ -33,7 +29,7 @@ const props = defineProps({
|
|||
})
|
||||
const option = computed(() => {
|
||||
return {
|
||||
backgroundColor:'',
|
||||
backgroundColor: '',
|
||||
//关闭动画
|
||||
// animation: false,
|
||||
tooltip: {
|
||||
|
@ -46,18 +42,19 @@ const option = computed(() => {
|
|||
}
|
||||
},
|
||||
grid: {
|
||||
left: '2%',
|
||||
left: '0',
|
||||
right: '50',
|
||||
bottom: '10%',
|
||||
bottom: '38',
|
||||
top: '15',
|
||||
containLabel: true
|
||||
},
|
||||
xAxis: [
|
||||
{
|
||||
type: 'time',
|
||||
// boundaryGap: false
|
||||
axisLabel:{
|
||||
showMinLabel:true,
|
||||
showMaxLabel:true,
|
||||
boundaryGap: false,
|
||||
axisLabel: {
|
||||
showMinLabel: true,
|
||||
showMaxLabel: true,
|
||||
}
|
||||
},
|
||||
],
|
||||
|
@ -66,15 +63,6 @@ const option = computed(() => {
|
|||
type: 'value',
|
||||
}
|
||||
],
|
||||
toolbox: {
|
||||
feature: {
|
||||
dataZoom: {
|
||||
yAxisIndex: 'none'
|
||||
},
|
||||
|
||||
saveAsImage: {}
|
||||
}
|
||||
},
|
||||
dataZoom: [
|
||||
{
|
||||
type: 'inside',
|
||||
|
@ -100,18 +88,18 @@ const option = computed(() => {
|
|||
large: true,
|
||||
smooth: true,
|
||||
largeThreshold: 10000,
|
||||
sampling:'lttb',
|
||||
sampling: 'lttb',
|
||||
emphasis: {
|
||||
focus: 'series'
|
||||
},
|
||||
markPoint: {
|
||||
data: [
|
||||
{ type: 'max', name: 'Max' },
|
||||
{ type: 'min', name: 'Min' }
|
||||
{type: 'max', name: 'Max'},
|
||||
{type: 'min', name: 'Min'}
|
||||
]
|
||||
},
|
||||
markLine: {
|
||||
data: [{ type: 'average', name: 'Avg' }]
|
||||
data: [{type: 'average', name: 'Avg'}]
|
||||
},
|
||||
data: [],
|
||||
}
|
||||
|
@ -122,19 +110,34 @@ let interval: NodeJS.Timeout;
|
|||
onUnmounted(() => {
|
||||
clearInterval(interval);
|
||||
})
|
||||
let maxValue = 100;
|
||||
onMounted(() => {
|
||||
setTimeout(()=>{
|
||||
hubStore.connection?.on("ReceiveDataHistory",(type:string,values:string[][])=>{
|
||||
isLoading.value = true;
|
||||
chartRef.value.appendData({
|
||||
seriesIndex: props.valueIds?.indexOf(type),
|
||||
data:values
|
||||
})
|
||||
|
||||
isLoading.value = false;
|
||||
if (!chartRef.value) {
|
||||
console.error('ECharts instance is not initialized');
|
||||
return;
|
||||
}
|
||||
hubStore.connection?.on("ReceiveDataHistory", (type: string, values: [number,string][], done: boolean) => {
|
||||
chartRef.value.appendData({
|
||||
seriesIndex: props.valueIds?.indexOf(type),
|
||||
data: values
|
||||
})
|
||||
},1000)
|
||||
setTimeout(()=>{
|
||||
if (done) {
|
||||
//设置y轴最大值为数据最大值的140%
|
||||
// const currentOption = chartRef.value.getOption();
|
||||
// const maxData = Math.max(...values.map(item =>item[1]));
|
||||
|
||||
// if (maxData > maxValue) {
|
||||
// maxValue = maxData
|
||||
// currentOption.yAxis[0].max = maxData * 1.4;
|
||||
// chartRef.value.setOption(currentOption);
|
||||
// }
|
||||
|
||||
}
|
||||
isLoading.value = false;
|
||||
})
|
||||
|
||||
|
||||
setTimeout(() => {
|
||||
$fetch('/Api/Server/GetServerHistoryDate', {
|
||||
method: 'GET',
|
||||
headers: {
|
||||
|
@ -147,53 +150,8 @@ onMounted(() => {
|
|||
},
|
||||
baseURL: useRuntimeConfig().public.baseUrl,
|
||||
})
|
||||
setTimeout(()=>{
|
||||
interval = setInterval(() => {
|
||||
const data = dataStore.data
|
||||
const time=Date.now()
|
||||
props.valueIds?.forEach((id, index) => {
|
||||
const newData = data[id] ?? 0
|
||||
chartRef.value.appendData({
|
||||
seriesIndex: index,
|
||||
data: [[time,newData]]
|
||||
})
|
||||
const currentOption = chartRef.value.getOption();
|
||||
chartRef.value.setOption(currentOption)
|
||||
})},1000)
|
||||
})
|
||||
},1000)
|
||||
// let history = dataStore.dataHistory
|
||||
// setTimeout(() => {
|
||||
// isLoading.value = false;
|
||||
// }, 5000)
|
||||
// nextTick(() => {
|
||||
// props.valueIds?.forEach((id, index) => {
|
||||
// chartRet.value.appendData({
|
||||
// seriesIndex: index,
|
||||
// data: history.data[id]
|
||||
// })
|
||||
// })
|
||||
// const currentOption = chartRet.value.getOption();
|
||||
// currentOption.xAxis[0].data = history.times
|
||||
// chartRet.value.setOption(currentOption)
|
||||
// isLoading.value = false
|
||||
// interval = setInterval(() => {
|
||||
// const data = dataStore.data
|
||||
// props.valueIds?.forEach((id, index) => {
|
||||
// const newData = data[id] ?? 0
|
||||
// if (!values.value[id]) {
|
||||
// values.value[id] = []
|
||||
// }
|
||||
// chartRet.value.appendData({
|
||||
// seriesIndex: index,
|
||||
// data: [newData]
|
||||
// })
|
||||
// })
|
||||
// const currentOption = chartRet.value.getOption();
|
||||
// currentOption.xAxis[0].data.push(dayjs().format('MM-DD HH:mm:ss'))
|
||||
// chartRet.value.setOption(currentOption)
|
||||
// }, 4000 + 1000 * Math.random())
|
||||
// })
|
||||
}, 2000)
|
||||
|
||||
})
|
||||
// let endIndex = dataStore.dataHistory.times ? dataStore.dataHistory.times.length : 0
|
||||
// let startTemp = 0;
|
||||
|
@ -256,7 +214,7 @@ onMounted(() => {
|
|||
autoresize
|
||||
:theme="$colorMode.value"
|
||||
class="chart"
|
||||
/>
|
||||
/>
|
||||
</template>
|
||||
|
||||
<style>
|
||||
|
|
Loading…
Reference in New Issue