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