Files
SaaS-lib/hx-ai-intelligent/src/view/monitor/energyMonitor/analysisGraph/index.vue
2024-08-23 16:47:16 +08:00

600 lines
18 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<a-row type="flex" style="height: 100%">
<a-col :span="8" style="height: 100%">
<div
style="
box-shadow: 0 0 15px rgba(69, 123, 234, 0.2);
border-radius: 10px;
width: 98%;
height: 96%;
margin: 2%;
">
<a-radio-group
v-model:value="mode"
@change="changeMode"
style="
padding-bottom: 10px;
width: 40%;
height: 20%;
padding-right: 30px;
padding-top: 10px;
display: flex;
float: right;
">
<a-radio-button value="1" style="width: 50%; text-align: center"> 同比 </a-radio-button>
<a-radio-button value="2" style="width: 50%; text-align: center"> 环比 </a-radio-button>
</a-radio-group>
<div ref="analysisGraphchart" style="width: 100%; height: 95%; padding-top: 10%"></div>
</div>
</a-col>
<a-col :span="16" style="height: 100%">
<div
ref="analysisGraphRingchart"
style="
width: 98%;
height: 38%;
box-shadow: 0 0 15px rgba(69, 123, 234, 0.2);
border-radius: 10px;
margin: 1%;
"></div>
<div
ref="analysisGraphBarchart"
style="
width: 98%;
height: 57%;
box-shadow: 0 0 15px rgba(69, 123, 234, 0.2);
border-radius: 10px;
margin: 1%;
"></div>
</a-col>
</a-row>
<div
v-show="!haveData"
style="
height: 80%;
width: 98%;
position: absolute;
border-radius: 10px;
z-index: 5;
top: 20%;
background: #ffffff;
display: flex;
justify-content: center;
align-items: center;
">
<a-empty />
</div>
</template>
<script lang="ts">
import { defineComponent, onMounted, ref, inject, watch } from 'vue';
import * as echarts from 'echarts';
import JSZip from 'jszip';
import { saveAs } from 'file-saver';
export default defineComponent({
name: 'AnalysisGraph',
setup() {
let haveData = ref(false);
const mode = ref<String>('1');
let data = ref<any[]>([]);
interface PageData {
// 图表 表格数据
graphTableList: any[];
// 图表 表格表头
graphTableColumns: any[];
// 图表 图表数据
graphGraphList: any[];
// 分析 表格数据
analysisTableList: any[];
// 分析 图表数据
analysisGraphList: any[];
}
const pageData = inject<PageData>('pageData');
if (!pageData) {
throw new Error('pageData is not provided');
}
// 监听 pageData 的变化
watch(
() => pageData as PageData,
(_newValue, _oldValue) => {
// 执行你的逻辑代码
draw();
},
{ deep: true },
);
const changeMode = () => {
// if (mode.value == '1') {
// mode.value = '2';
// } else {
// mode.value = '1';
// }
drawLeft();
};
const analysisGraphchart = ref(null);
const analysisGraphRingchart = ref(null);
const analysisGraphBarchart = ref(null);
// 左侧柱状图
let chartInstance: echarts.ECharts | null = null;
// 圆环图
let chartRight1: echarts.ECharts | null = null;
// 右下角柱状图
let chartRight2: echarts.ECharts | null = null;
const draw = () => {
// 深度拷贝
let dataList = JSON.parse(JSON.stringify(pageData.analysisGraphList));
// let dataList = pageData.analysisGraphList;
if (dataList && dataList.length > 0) {
haveData.value = true;
} else {
haveData.value = false;
return;
}
dataList.forEach((item) => {
if (item.yoyValue < 0) {
item.yoyLabel = { position: 'right' };
} else {
item.yoyLabel = { position: 'insideLeft' };
}
if (item.momValue < 0) {
item.momLabel = { position: 'right' };
} else {
item.momLabel = { position: 'insideLeft' };
}
});
data.value = dataList;
// 绘制左侧图
drawLeft();
drawRight1();
};
const drawLeft = () => {
if (data.value && data.value.length > 0) {
if (chartInstance) {
chartInstance.dispose();
}
chartInstance = echarts.init(analysisGraphchart.value);
var seriesdata = [];
var dateX = [];
// {
// name: 'AC_002暖通电表',
// value: -21,
// ringValue: 21,
// energyType: selectedValue.value,
// energyUnit: 'kWh',
// unit: 'V',
// labelRight: {
// position: 'right',
// },
// },
// id: 'HLlmTZp8_0805_0001';
// momDiff: 38.28; 环比差值
// momRate: '-171.58%'; 环比率
// momValue: -22.31; 环比值
// name: '总电表';
// value: 10.56; 值
// yoyDiff: 38.28; 同比差值
// yoyRate: '-138.10%'; 同比率
// yoyValue: -27.72; 同比值
for (let i = 0; i < data.value.length; i++) {
dateX.push(data.value[i].name);
if (mode.value == '1') {
seriesdata.push({ value: data.value[i].yoyValue, label: data.value[i].yoyLabel });
} else {
seriesdata.push({ value: data.value[i].momValue, label: data.value[i].momLabel });
}
}
var seriesList = [
{
name: data.value[0].energyType,
data: seriesdata,
type: 'bar',
label: {
show: true,
formatter: '{b}',
},
barWidth: '25%',
},
];
const option = {
grid: {
top: 80,
bottom: 20,
},
dataZoom: [
{
show: true,
type: 'slider',
zoomLock: true,
startValue: 0, // 从头开始。
endValue: 4,
showDetail: false,
width: 5,
yAxisIndex: [0, 1], // 控制y轴滚动对象
},
{
show: true,
type: 'inside',
// width: 0,
startValue: 0,
endValue: 10,
minValueSpan: 10,
yAxisIndex: [0],
zoomOnMouseWheel: false, // 关闭滚轮缩放
moveOnMouseWheel: true, // 开启滚轮平移
moveOnMouseMove: true, // 鼠标移动能触发数据窗口平移
},
],
yAxis: {
type: 'category',
axisLine: { show: false },
axisLabel: { show: false },
axisTick: { show: false },
splitLine: { show: false },
data: dateX,
},
xAxis: {
type: 'value',
position: 'top',
splitLine: {
lineStyle: {
type: 'dashed',
},
},
},
series: seriesList,
};
chartInstance = echarts.init(analysisGraphchart.value);
chartInstance.setOption(option);
}
};
const drawRight1 = () => {
if (data.value && data.value.length > 0) {
if (chartRight1) {
chartRight1.dispose();
}
chartRight1 = echarts.init(analysisGraphRingchart.value);
var seriesdata = [];
var dateX = [];
// var legendList: string | any[] = [];
let sum = 0;
for (let i = 0; i < data.value.length; i++) {
sum += data.value[i].value;
dateX.push(data.value[i].name);
seriesdata.push({
value: data.value[i].value,
name: data.value[i].name,
radius: ['70%', '90%'],
});
}
var seriesList = [
{
// name: data.value[0].energyType,
data: seriesdata,
type: 'pie',
// // 可单选
selectedMode: true,
// 选中扇区的偏移距离
clockwise: '10',
// hoverAnimation: true,
// 圆环内径和外径
radius: ['60%', '80%'],
center: ['30%', '50%'], // 调整环形图的位置,百分比表示相对于容器的宽高
// 显示折线
labelLine: {
show: true,
length: 10, // 调整第一个折线段的长度
length2: 30, // 调整第二个折线段的长度
},
label: {
show: true,
// formatter: '{b}',
position: 'outside', // 确保标签在环形图外部
// alignTo: 'edge',
formatter: '{c}' + data.value[0].unit + '\n{d}%',
alignTo: 'labelLine',
distanceToLabelLine: 5, // 调整标签和引导线之间的距离
distance: 10, // 调整标签距离图形的距离
},
itemStyle: {
normal: {
// 白色间距
borderWidth: 2,
borderColor: '#ffffff',
},
},
},
];
const option = {
tooltip: {
trigger: 'item',
},
graphic: [
{
type: 'text',
left: '28%',
top: '33%',
style: {
text: '总计',
fill: '#000',
fontSize: 14,
},
},
{
type: 'text',
left: sum > 100 ? '24%' : '30%',
top: '50%',
style: {
text: sum,
fill: '#000',
fontSize: 30,
fontWeight: 700,
},
},
],
// 图例
legend: {
top: 'center',
left: '60%',
// 排列方式 垂直
orient: 'vertical',
},
yAxis: {
type: 'category',
axisLine: { show: false },
axisLabel: { show: false },
axisTick: { show: false },
splitLine: { show: false },
data: dateX,
},
xAxis: {
type: 'value',
position: 'top',
splitLine: {
lineStyle: {
type: 'dashed',
},
},
},
series: seriesList,
};
chartRight1 = echarts.init(analysisGraphRingchart.value);
// chartRight1.setOption(option);
// 默认点击第一个
if (seriesdata.length > 0) {
seriesdata[0].radius = ['50%', '100%'];
chartRight1.setOption(option);
drawRight2(seriesdata[0]);
}
chartRight1.on('click', function (params) {
// 控制台输出数据的名称
console.log(params.name + ' 被点击了');
if (params.name && params.name != '') {
drawRight2(params);
}
});
}
};
// 右下角柱状图
// chartRight2
// analysisGraphBarchart
const drawRight2 = (auxiliary: any) => {
if (chartRight2) {
chartRight2.dispose();
}
// 辅助线
var compareData: any[] = [];
// 展示数据
var showData: any[] = [];
// X轴
var dateX: any[] = [];
data.value.forEach((item) => {
if (item.name !== auxiliary.name) {
dateX.push(item.name);
showData.push(item.value);
compareData.push(auxiliary.value);
}
});
const option = {
// 图例
// legend: {
// data: [
// { name: '对比值', icon: 'rect' }, // 对比值使用矩形图标
// {
// name: '参考线',
// icon: 'path://M234.666667 490.666667h-153.6a25.6 25.6 0 1 0 0 51.2h153.6a25.6 25.6 0 1 0 0-51.2zM473.6 490.666667h-153.6a25.6 25.6 0 1 0 0 51.2h153.6a25.6 25.6 0 1 0 0-51.2zM934.4 490.666667h-136.533333a25.6 25.6 0 1 0 0 51.2h136.533333a25.6 25.6 0 1 0 0-51.2zM712.533333 490.666667h-153.6a25.6 25.6 0 1 0 0 51.2h153.6a25.6 25.6 0 1 0 0-51.2z',
// }, // 参考线使用自定义路径图标,只显示线条
// ],
// orient: 'horizontal',
// bottom: 10,
// left: 60,
// },
tooltip: {
trigger: 'axis',
axisPointer: {
type: 'shadow',
lineStyle: {
color: 'red', // 设置 tooltip 的线颜色
},
},
},
// grid: {
// top: 20,
// bottom: 60,
// },
xAxis: {
type: 'category',
axisLine: { show: false },
axisLabel: { show: true },
axisTick: { show: false },
splitLine: { show: false },
data: dateX,
},
yAxis: {
type: 'value',
position: 'top',
splitLine: {
lineStyle: {
type: 'dashed',
},
},
},
dataZoom: [
{
show: true,
type: 'slider',
zoomLock: true,
startValue: 0, // 从头开始。
endValue: 5,
bottom: 20,
height: 10,
showDetail: false,
},
{
show: true,
type: 'inside',
xAxisIndex: 0,
zoomOnMouseWheel: false, // 滚轮是否触发缩放
moveOnMouseMove: true, // 鼠标滚轮触发滚动
moveOnMouseWheel: true,
},
],
series: [
{
name: '对比值',
type: 'bar',
stack: 'Total',
label: {
// show: true,
position: 'right',
formatter: '{b}',
},
data: showData,
barWidth: 30, // 设置柱状图的宽度
},
{
name: '参考线',
type: 'line',
data: compareData,
markLine: {
symbol: 'none',
label: {
show: false,
},
lineStyle: {
type: 'dashed',
color: 'red',
},
},
itemStyle: {
color: 'red', // 设置参考线的小圆点颜色
},
emphasis: {
itemStyle: {
opacity: 0, // 隐藏鼠标悬停时的节点
},
},
showSymbol: false, // 隐藏数据点
lineStyle: {
type: 'dashed', // 虚线样式
color: 'red',
},
},
],
};
chartRight2 = echarts.init(analysisGraphBarchart.value);
chartRight2.setOption(option);
};
onMounted(() => {
// draw();
});
// 下载图表
const downloadChart = async () => {
// if (chartInstance) {
// const base64 = chartInstance.getDataURL({
// type: 'png',
// backgroundColor: '#fff',
// });
// const link = document.createElement('a');
// link.href = base64;
// link.download = 'chart.png';
// link.click();
// }
const zip = new JSZip();
const chartInstances = [chartInstance, chartRight1, chartRight2];
const imagePromises = chartInstances.map((chart: any, index) => {
return new Promise((resolve) => {
const base64 = chart.getDataURL({
type: 'png',
backgroundColor: '#fff',
});
// 将 Base64 转换为二进制
const binary = atob(base64.split(',')[1]);
const array = [];
for (let i = 0; i < binary.length; i++) {
array.push(binary.charCodeAt(i));
}
const uint8Array = new Uint8Array(array);
resolve({ name: `chart${index + 1}.png`, data: uint8Array });
});
});
const images = await Promise.all(imagePromises);
images.forEach((image: any) => {
zip.file(image.name, image.data);
});
zip.generateAsync({ type: 'blob' }).then((content) => {
saveAs(content, 'charts.zip'); // 使用 FileSaver.js 保存 ZIP 文件
});
};
return {
analysisGraphchart,
analysisGraphRingchart,
analysisGraphBarchart,
downloadChart,
mode,
changeMode,
haveData,
};
},
});
</script>
<style lang="less" scoped>
::v-deep .ant-radio-button-wrapper-checked {
border: none !important; /* 移除选中时的边框 */
box-shadow: none !important; /* 移除选中时的阴影 */
background-color: #2778ff !important; /* 使用 !important 强制覆盖背景颜色 */
color: #ffffff !important; /* 使用 !important 强制覆盖文字颜色 */
}
/* 未选中时的颜色 */
::v-deep .ant-radio-button-wrapper {
// border: none !important; /* 移除所有边框 */
box-shadow: none !important; /* 去掉阴影(如果有的话) */
background-color: #f9f9f9;
color: #2778ff;
border-color: #2778ff;
}
</style>