20260408:归一化Y轴坐标,将每条曲线的数据按自身 min-max 归一化到 -100-100 范围
This commit is contained in:
parent
652caf9157
commit
e7ae333a11
@ -45,11 +45,12 @@
|
||||
<el-row>
|
||||
<el-col :span="24">
|
||||
<el-card v-loading="loading" shadow="never">
|
||||
<div slot="header">
|
||||
<div slot="header" style="display: flex; align-items: center; justify-content: space-between;">
|
||||
<span>{{ $t('dataCenter.history.384934-10') }}</span>
|
||||
<el-switch v-model="independentYAxis" active-text="比例Y轴" inactive-text="共享Y轴" @change="handleYAxisModeChange" />
|
||||
</div>
|
||||
<div class="el-table--enable-row-hover el-table--medium">
|
||||
<div v-show="devDatas.length !== 0" ref="devLineChart" style="width: 100%; height: 480px; background: #fff"></div>
|
||||
<div v-show="devDatas.length !== 0" ref="devLineChart" :style="{ width: '100%', height: devChartHeight + 'px', background: '#fff' }"></div>
|
||||
<el-empty v-if="devDatas.length === 0" style="height: 480px" :description="$t('dataCenter.history.384934-12')"></el-empty>
|
||||
<el-table v-show="devTotal > 0" style="margin-top: 50px" :data="devTableList" :border="false">
|
||||
<el-table-column :label="$t('dataCenter.history.384934-13')" prop="time" width="200" />
|
||||
@ -125,10 +126,13 @@
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<el-card v-loading="loading" shadow="never">
|
||||
<div slot="header"><span>{{ gwResultTitle }}</span></div>
|
||||
<div slot="header" style="display: flex; align-items: center; justify-content: space-between;">
|
||||
<span>{{ gwResultTitle }}</span>
|
||||
<el-switch v-model="independentYAxis" active-text="比例Y轴" inactive-text="共享Y轴" @change="handleYAxisModeChange" />
|
||||
</div>
|
||||
<!-- 计算量结果 -->
|
||||
<template v-if="gwResultMode === 'calc'">
|
||||
<div v-show="gwCalcDatas.length !== 0" ref="gwCalcLineChart" style="width: 100%; height: 480px"></div>
|
||||
<div v-show="gwCalcDatas.length !== 0" ref="gwCalcLineChart" :style="{ width: '100%', height: gwCalcChartHeight + 'px' }"></div>
|
||||
<el-empty v-if="gwCalcDatas.length === 0 && !loading" style="height: 480px" description="请选择网关、物模型(计算量)和时间范围后查询"></el-empty>
|
||||
<el-table v-show="gwCalcTotal > 0" style="margin-top: 20px" :data="gwCalcTableList" border>
|
||||
<el-table-column label="更新时间" prop="time" width="180" />
|
||||
@ -138,7 +142,7 @@
|
||||
</template>
|
||||
<!-- 多设备对比结果 -->
|
||||
<template v-else-if="gwResultMode === 'compare'">
|
||||
<div v-show="subCompareDatas.length !== 0" ref="subCompareLineChart" style="width: 100%; height: 480px"></div>
|
||||
<div v-show="subCompareDatas.length !== 0" ref="subCompareLineChart" :style="{ width: '100%', height: subCompareChartHeight + 'px' }"></div>
|
||||
<el-empty v-if="subCompareDatas.length === 0 && !loading" style="height: 480px" description="请选择网关、物模型(子设备变量)和时间范围,子设备可不选(多设备对比)"></el-empty>
|
||||
<el-table v-show="subCompareTotal > 0" style="margin-top: 20px" :data="subCompareTableList" border>
|
||||
<el-table-column v-for="col in subCompareTableColumns" :key="col.prop" :label="col.label" :prop="col.prop" :min-width="col.prop === 'time' ? 180 : 100" />
|
||||
@ -147,7 +151,7 @@
|
||||
</template>
|
||||
<!-- 单子设备历史结果 -->
|
||||
<template v-else-if="gwResultMode === 'single'">
|
||||
<div v-show="gwSingleDatas.length !== 0" ref="gwSingleLineChart" style="width: 100%; height: 480px"></div>
|
||||
<div v-show="gwSingleDatas.length !== 0" ref="gwSingleLineChart" :style="{ width: '100%', height: gwSingleChartHeight + 'px' }"></div>
|
||||
<el-empty v-if="gwSingleDatas.length === 0 && !loading" style="height: 480px" description="请选择网关、物模型、子设备和时间范围后查询"></el-empty>
|
||||
<el-table v-show="gwSingleTotal > 0" style="margin-top: 20px" :data="gwSingleTableList" border>
|
||||
<el-table-column label="更新时间" prop="time" width="180" />
|
||||
@ -209,11 +213,12 @@
|
||||
<el-row>
|
||||
<el-col :span="24">
|
||||
<el-card v-loading="loading" shadow="never">
|
||||
<div slot="header">
|
||||
<div slot="header" style="display: flex; align-items: center; justify-content: space-between;">
|
||||
<span>{{ $t('dataCenter.history.384934-10') }}</span>
|
||||
<el-switch v-model="independentYAxis" active-text="比例Y轴" inactive-text="共享Y轴" @change="handleYAxisModeChange" />
|
||||
</div>
|
||||
<div class="el-table--enable-row-hover el-table--medium">
|
||||
<div v-show="sceneDatas.length !== 0" ref="sceneLineChart" style="width: 100%; height: 480px; background: #fff"></div>
|
||||
<div v-show="sceneDatas.length !== 0" ref="sceneLineChart" :style="{ width: '100%', height: sceneChartHeight + 'px', background: '#fff' }"></div>
|
||||
<el-empty v-if="sceneDatas.length === 0" style="height: 480px" :description="$t('dataCenter.history.384934-12')"></el-empty>
|
||||
<el-table v-show="sceneTotal > 0" style="margin-top: 50px" :data="sceneTableList" :border="false">
|
||||
<el-table-column :label="$t('dataCenter.history.384934-13')" prop="time" width="200" />
|
||||
@ -378,6 +383,12 @@ export default {
|
||||
gwSingleTotal: 0,
|
||||
gwSinglePageNum: 1,
|
||||
gwSinglePageSize: 10,
|
||||
independentYAxis: true,
|
||||
devChartHeight: 480,
|
||||
gwCalcChartHeight: 480,
|
||||
gwSingleChartHeight: 480,
|
||||
subCompareChartHeight: 480,
|
||||
sceneChartHeight: 480,
|
||||
};
|
||||
},
|
||||
mounted() {
|
||||
@ -801,15 +812,86 @@ export default {
|
||||
}
|
||||
return true;
|
||||
},
|
||||
calcChartHeight(seriesCount) {
|
||||
return Math.min(1200, Math.max(480, seriesCount * 14 + 360));
|
||||
},
|
||||
applyIndependentScale(series) {
|
||||
series.forEach((s) => {
|
||||
const nums = (s.data || []).filter(v => v != null && v !== '').map(Number).filter(n => !isNaN(n));
|
||||
if (nums.length === 0) return;
|
||||
const min = Math.min(...nums);
|
||||
const max = Math.max(...nums);
|
||||
const range = max - min;
|
||||
s.data = s.data.map(v => {
|
||||
if (v == null || v === '') return null;
|
||||
const num = Number(v);
|
||||
if (isNaN(num)) return null;
|
||||
// 映射到 -100 ~ 100,range=0 时固定为 0(居中水平线)
|
||||
return {
|
||||
value: range === 0 ? 0 : ((num - min) / range) * 200 - 100,
|
||||
_original: v,
|
||||
};
|
||||
});
|
||||
});
|
||||
return {
|
||||
type: 'value',
|
||||
min: -100,
|
||||
max: 100,
|
||||
splitNumber: 4,
|
||||
axisLine: { show: true },
|
||||
axisTick: { show: true },
|
||||
axisLabel: {
|
||||
formatter: (val) => val + '%',
|
||||
},
|
||||
splitLine: {
|
||||
show: true,
|
||||
lineStyle: { type: 'dashed', opacity: 0.5 },
|
||||
},
|
||||
};
|
||||
},
|
||||
independentScaleTooltip() {
|
||||
return {
|
||||
trigger: 'axis',
|
||||
formatter: (params) => {
|
||||
if (!params || !params.length) return '';
|
||||
let html = '<div style="font-size:12px">' + params[0].axisValueLabel + '</div>';
|
||||
params.forEach((p) => {
|
||||
if (p.data == null) return;
|
||||
const val = (typeof p.data === 'object' && p.data._original != null)
|
||||
? p.data._original : p.value;
|
||||
html += p.marker + ' ' + p.seriesName + ': <b>' + val + '</b><br/>';
|
||||
});
|
||||
return html;
|
||||
},
|
||||
};
|
||||
},
|
||||
handleYAxisModeChange() {
|
||||
this.$nextTick(() => {
|
||||
if (this.activeTab === 'device') {
|
||||
if (this.devDatas.length) this.drawDevLine();
|
||||
} else if (this.activeTab === 'gateway') {
|
||||
if (this.gwResultMode === 'calc' && this.gwCalcDatas.length) this.drawGwCalcLine();
|
||||
if (this.gwResultMode === 'compare' && this.subCompareDatas.length) this.drawSubCompareLine();
|
||||
if (this.gwResultMode === 'single' && this.gwSingleDatas.length) this.drawGwSingleLine();
|
||||
} else if (this.activeTab === 'scene') {
|
||||
if (this.sceneDatas.length) this.drawSceneLine();
|
||||
}
|
||||
});
|
||||
},
|
||||
drawDevLine() {
|
||||
// 初始化折线图
|
||||
const series = this.getDevSeries();
|
||||
this.devChartHeight = this.calcChartHeight(series.length);
|
||||
this.$nextTick(() => {
|
||||
this.charts = this.$echarts.init(this.$refs.devLineChart);
|
||||
this.charts.clear();
|
||||
// 设置折线图数据和样式
|
||||
const yAxis = this.independentYAxis
|
||||
? this.applyIndependentScale(series)
|
||||
: { type: 'value', scale: true };
|
||||
const tooltip = this.independentYAxis
|
||||
? this.independentScaleTooltip()
|
||||
: { trigger: 'axis' };
|
||||
this.charts.setOption({
|
||||
tooltip: {
|
||||
trigger: 'axis',
|
||||
},
|
||||
tooltip,
|
||||
legend: {
|
||||
align: 'right',
|
||||
left: '3%',
|
||||
@ -830,33 +912,19 @@ export default {
|
||||
},
|
||||
},
|
||||
dataZoom: [
|
||||
{
|
||||
type: 'inside',
|
||||
start: 0,
|
||||
end: 100,
|
||||
},
|
||||
{
|
||||
start: 0,
|
||||
end: 100,
|
||||
},
|
||||
{ type: 'inside', start: 0, end: 100 },
|
||||
{ start: 0, end: 100 },
|
||||
],
|
||||
// 自定义:设置x轴刻度
|
||||
xAxis: {
|
||||
type: 'category',
|
||||
boundaryGap: true,
|
||||
axisTick: {
|
||||
alignWithLabel: true,
|
||||
},
|
||||
// 自定义标签
|
||||
axisTick: { alignWithLabel: true },
|
||||
data: this.devDatas.length !== 0 && this.devDatas.map((item) => Object.keys(item)[0]),
|
||||
},
|
||||
// 自定义:设置y轴刻度
|
||||
yAxis: {
|
||||
type: 'value',
|
||||
scale: true, //自适应
|
||||
},
|
||||
// 设置数据
|
||||
series: this.getDevSeries(),
|
||||
yAxis,
|
||||
series,
|
||||
});
|
||||
this.charts.resize();
|
||||
});
|
||||
},
|
||||
// 获取设备的series
|
||||
@ -1168,8 +1236,6 @@ export default {
|
||||
},
|
||||
drawGwCalcLine() {
|
||||
if (!this.$refs.gwCalcLineChart) return;
|
||||
const chart = this.$echarts.init(this.$refs.gwCalcLineChart);
|
||||
chart.clear();
|
||||
const ids = this.gwSelectedCalcIdentifiers;
|
||||
const times = this.gwCalcDatas.map((d) => Object.keys(d)[0]);
|
||||
const series = ids.map((id) => {
|
||||
@ -1185,14 +1251,37 @@ export default {
|
||||
}),
|
||||
};
|
||||
});
|
||||
this.gwCalcChartHeight = this.calcChartHeight(series.length);
|
||||
this.$nextTick(() => {
|
||||
const chart = this.$echarts.init(this.$refs.gwCalcLineChart);
|
||||
chart.clear();
|
||||
const yAxis = this.independentYAxis
|
||||
? this.applyIndependentScale(series)
|
||||
: { type: 'value', scale: true };
|
||||
const tooltip = this.independentYAxis
|
||||
? this.independentScaleTooltip()
|
||||
: { trigger: 'axis' };
|
||||
chart.setOption({
|
||||
tooltip: { trigger: 'axis' },
|
||||
legend: { data: series.map((s) => s.name) },
|
||||
grid: { left: '3%', right: '4%', bottom: '3%', containLabel: true },
|
||||
xAxis: { type: 'category', data: times },
|
||||
yAxis: { type: 'value', scale: true },
|
||||
tooltip,
|
||||
legend: { align: 'right', left: '3%', top: '3%' },
|
||||
grid: { top: '30%', left: '5%', right: '5%', bottom: '15%', containLabel: true },
|
||||
toolbox: { feature: { dataView: {}, restore: {}, saveAsImage: {} } },
|
||||
dataZoom: [
|
||||
{ type: 'inside', start: 0, end: 100 },
|
||||
{ start: 0, end: 100 },
|
||||
],
|
||||
xAxis: {
|
||||
type: 'category',
|
||||
boundaryGap: true,
|
||||
axisTick: { alignWithLabel: true },
|
||||
axisLabel: { rotate: 30 },
|
||||
data: times,
|
||||
},
|
||||
yAxis,
|
||||
series,
|
||||
});
|
||||
chart.resize();
|
||||
});
|
||||
},
|
||||
getGwSingleTableList(e) {
|
||||
if (e) {
|
||||
@ -1220,8 +1309,6 @@ export default {
|
||||
},
|
||||
drawGwSingleLine() {
|
||||
if (!this.$refs.gwSingleLineChart || !this.gwSingleDatas.length) return;
|
||||
const chart = this.$echarts.init(this.$refs.gwSingleLineChart);
|
||||
chart.clear();
|
||||
const ids = this.gwSelectedSubIdentifiers;
|
||||
const times = this.gwSingleDatas.map((d) => Object.keys(d)[0]);
|
||||
const series = ids.map((id) => {
|
||||
@ -1237,14 +1324,37 @@ export default {
|
||||
}),
|
||||
};
|
||||
});
|
||||
this.gwSingleChartHeight = this.calcChartHeight(series.length);
|
||||
this.$nextTick(() => {
|
||||
const chart = this.$echarts.init(this.$refs.gwSingleLineChart);
|
||||
chart.clear();
|
||||
const yAxis = this.independentYAxis
|
||||
? this.applyIndependentScale(series)
|
||||
: { type: 'value', scale: true };
|
||||
const tooltip = this.independentYAxis
|
||||
? this.independentScaleTooltip()
|
||||
: { trigger: 'axis' };
|
||||
chart.setOption({
|
||||
tooltip: { trigger: 'axis' },
|
||||
legend: { data: series.map((s) => s.name) },
|
||||
grid: { left: '3%', right: '4%', bottom: '3%', containLabel: true },
|
||||
xAxis: { type: 'category', data: times },
|
||||
yAxis: { type: 'value', scale: true },
|
||||
tooltip,
|
||||
legend: { align: 'right', left: '3%', top: '3%' },
|
||||
grid: { top: '30%', left: '5%', right: '5%', bottom: '15%', containLabel: true },
|
||||
toolbox: { feature: { dataView: {}, restore: {}, saveAsImage: {} } },
|
||||
dataZoom: [
|
||||
{ type: 'inside', start: 0, end: 100 },
|
||||
{ start: 0, end: 100 },
|
||||
],
|
||||
xAxis: {
|
||||
type: 'category',
|
||||
boundaryGap: true,
|
||||
axisTick: { alignWithLabel: true },
|
||||
axisLabel: { rotate: 30 },
|
||||
data: times,
|
||||
},
|
||||
yAxis,
|
||||
series,
|
||||
});
|
||||
chart.resize();
|
||||
});
|
||||
},
|
||||
getSubCompareTableList(e) {
|
||||
if (e) {
|
||||
@ -1282,8 +1392,6 @@ export default {
|
||||
},
|
||||
drawSubCompareLine() {
|
||||
if (!this.$refs.subCompareLineChart || !this.subCompareDatas.length) return;
|
||||
const chart = this.$echarts.init(this.$refs.subCompareLineChart);
|
||||
chart.clear();
|
||||
const times = this.subCompareDatas.map((d) => d.time);
|
||||
const keys = Object.keys(this.subCompareDatas[0]).filter((k) => k !== 'time');
|
||||
const series = keys.map((k) => ({
|
||||
@ -1292,14 +1400,37 @@ export default {
|
||||
smooth: true,
|
||||
data: this.subCompareDatas.map((d) => d[k]),
|
||||
}));
|
||||
this.subCompareChartHeight = this.calcChartHeight(series.length);
|
||||
this.$nextTick(() => {
|
||||
const chart = this.$echarts.init(this.$refs.subCompareLineChart);
|
||||
chart.clear();
|
||||
const yAxis = this.independentYAxis
|
||||
? this.applyIndependentScale(series)
|
||||
: { type: 'value', scale: true };
|
||||
const tooltip = this.independentYAxis
|
||||
? this.independentScaleTooltip()
|
||||
: { trigger: 'axis' };
|
||||
chart.setOption({
|
||||
tooltip: { trigger: 'axis' },
|
||||
legend: { data: series.map((s) => s.name) },
|
||||
grid: { left: '3%', right: '4%', bottom: '3%', containLabel: true },
|
||||
xAxis: { type: 'category', data: times },
|
||||
yAxis: { type: 'value', scale: true },
|
||||
tooltip,
|
||||
legend: { align: 'right', left: '3%', top: '3%' },
|
||||
grid: { top: '30%', left: '5%', right: '5%', bottom: '15%', containLabel: true },
|
||||
toolbox: { feature: { dataView: {}, restore: {}, saveAsImage: {} } },
|
||||
dataZoom: [
|
||||
{ type: 'inside', start: 0, end: 100 },
|
||||
{ start: 0, end: 100 },
|
||||
],
|
||||
xAxis: {
|
||||
type: 'category',
|
||||
boundaryGap: true,
|
||||
axisTick: { alignWithLabel: true },
|
||||
axisLabel: { rotate: 30 },
|
||||
data: times,
|
||||
},
|
||||
yAxis,
|
||||
series,
|
||||
});
|
||||
chart.resize();
|
||||
});
|
||||
},
|
||||
// 格式化设备列表数据
|
||||
formatDevTableDatas() {
|
||||
@ -1376,63 +1507,36 @@ export default {
|
||||
}
|
||||
},
|
||||
drawSceneLine() {
|
||||
// 初始化折线图
|
||||
const series = this.getSceneSeries();
|
||||
this.sceneChartHeight = this.calcChartHeight(series.length);
|
||||
this.$nextTick(() => {
|
||||
this.charts = this.$echarts.init(this.$refs.sceneLineChart);
|
||||
this.charts.clear();
|
||||
// 设置折线图数据和样式
|
||||
const yAxis = this.independentYAxis
|
||||
? this.applyIndependentScale(series)
|
||||
: { type: 'value', scale: true };
|
||||
const tooltip = this.independentYAxis
|
||||
? this.independentScaleTooltip()
|
||||
: { trigger: 'axis' };
|
||||
this.charts.setOption({
|
||||
tooltip: {
|
||||
trigger: 'axis',
|
||||
},
|
||||
legend: {
|
||||
align: 'right',
|
||||
left: '3%',
|
||||
top: '15%',
|
||||
},
|
||||
grid: {
|
||||
top: '30%',
|
||||
left: '5%',
|
||||
right: '5%',
|
||||
bottom: '5%',
|
||||
containLabel: true,
|
||||
},
|
||||
toolbox: {
|
||||
feature: {
|
||||
dataView: {},
|
||||
restore: {},
|
||||
saveAsImage: {},
|
||||
},
|
||||
},
|
||||
tooltip,
|
||||
legend: { align: 'right', left: '3%', top: '15%' },
|
||||
grid: { top: '30%', left: '5%', right: '5%', bottom: '5%', containLabel: true },
|
||||
toolbox: { feature: { dataView: {}, restore: {}, saveAsImage: {} } },
|
||||
dataZoom: [
|
||||
{
|
||||
type: 'inside',
|
||||
start: 0,
|
||||
end: 100,
|
||||
},
|
||||
{
|
||||
start: 0,
|
||||
end: 100,
|
||||
},
|
||||
{ type: 'inside', start: 0, end: 100 },
|
||||
{ start: 0, end: 100 },
|
||||
],
|
||||
// 自定义:设置x轴刻度
|
||||
xAxis: {
|
||||
type: 'category',
|
||||
boundaryGap: true,
|
||||
axisTick: {
|
||||
alignWithLabel: true,
|
||||
},
|
||||
// 自定义标签
|
||||
axisTick: { alignWithLabel: true },
|
||||
data: this.sceneDatas.length !== 0 && this.sceneDatas.map((item) => Object.keys(item)[0]),
|
||||
},
|
||||
// 自定义:设置y轴刻度
|
||||
yAxis: {
|
||||
type: 'value',
|
||||
boundaryGap: true,
|
||||
splitNumber: 4,
|
||||
interval: 250,
|
||||
},
|
||||
// 设置数据
|
||||
series: this.getSceneSeries(),
|
||||
yAxis,
|
||||
series,
|
||||
});
|
||||
this.charts.resize();
|
||||
});
|
||||
},
|
||||
// 获取设备的series
|
||||
|
||||
Loading…
Reference in New Issue
Block a user