You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
574 lines
17 KiB
574 lines
17 KiB
<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>
|
|
</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() {
|
|
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;
|
|
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,
|
|
};
|
|
},
|
|
});
|
|
</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>
|
|
|