|
@@ -0,0 +1,836 @@
|
|
|
+<script setup lang="ts">
|
|
|
+import { computed, onMounted, ref, watch } from 'vue';
|
|
|
+import VChart from 'vue-echarts';
|
|
|
+import { BarChart, LineChart, PieChart } from 'echarts/charts';
|
|
|
+import { DatasetComponent, GridComponent, LegendComponent, TitleComponent, TooltipComponent } from 'echarts/components';
|
|
|
+import { use } from 'echarts/core';
|
|
|
+import { CanvasRenderer } from 'echarts/renderers';
|
|
|
+
|
|
|
+import SvgIcon from '@/components/SvgIcon.vue';
|
|
|
+import { useRequest } from '@/hooks/request';
|
|
|
+import { useViewVisible } from '@/hooks/view-visible';
|
|
|
+import { t } from '@/i18n';
|
|
|
+import { getElectricityDataStatistics } from '@/api';
|
|
|
+import type { IconfontIcon } from '@/icons/fonts/iconfont';
|
|
|
+
|
|
|
+import cumulativeElectricityBill from '@/assets/img/cumulative-electricity-bill.png';
|
|
|
+import cumulativeElectricityConsumption from '@/assets/img/cumulative-electricity-consumption.png';
|
|
|
+import electricityDailyConsumption from '@/assets/img/electricity-daily-consumption.png';
|
|
|
+import electricitySequentialGrowth from '@/assets/img/electricity-sequential-growth.png';
|
|
|
+
|
|
|
+import { DeviceType } from '../device-work-status/device-card';
|
|
|
+
|
|
|
+import type { SegmentedBaseOption } from 'ant-design-vue/es/segmented/src/segmented';
|
|
|
+import type { Dayjs } from 'dayjs';
|
|
|
+import type { BarSeriesOption, LineSeriesOption, PieSeriesOption } from 'echarts/charts';
|
|
|
+import type {
|
|
|
+ DatasetComponentOption,
|
|
|
+ GridComponentOption,
|
|
|
+ LegendComponentOption,
|
|
|
+ TitleComponentOption,
|
|
|
+ TooltipComponentOption,
|
|
|
+} from 'echarts/components';
|
|
|
+import type { ComposeOption } from 'echarts/core';
|
|
|
+import type { ElectricityStatisticsResult, EnergyCardItem } from '@/types';
|
|
|
+
|
|
|
+type StatisticsValue = 'electricityConsumption' | 'electricityCost';
|
|
|
+
|
|
|
+interface StatisticsTypeItem extends SegmentedBaseOption {
|
|
|
+ value: StatisticsValue;
|
|
|
+ payload: {
|
|
|
+ title: string;
|
|
|
+ icon: IconfontIcon;
|
|
|
+ };
|
|
|
+}
|
|
|
+
|
|
|
+interface EquipmentData {
|
|
|
+ key: string;
|
|
|
+ name: string;
|
|
|
+ type: string;
|
|
|
+ electricity: number;
|
|
|
+ cost: number;
|
|
|
+ dailyElectricity: number;
|
|
|
+ ratio: number;
|
|
|
+ children?: EquipmentData[];
|
|
|
+}
|
|
|
+
|
|
|
+use([
|
|
|
+ CanvasRenderer,
|
|
|
+ BarChart,
|
|
|
+ PieChart,
|
|
|
+ LineChart,
|
|
|
+ GridComponent,
|
|
|
+ TooltipComponent,
|
|
|
+ DatasetComponent,
|
|
|
+ LegendComponent,
|
|
|
+ TitleComponent,
|
|
|
+]);
|
|
|
+
|
|
|
+type EChartsOption = ComposeOption<
|
|
|
+ | TitleComponentOption
|
|
|
+ | LegendComponentOption
|
|
|
+ | TooltipComponentOption
|
|
|
+ | DatasetComponentOption
|
|
|
+ | GridComponentOption
|
|
|
+ | BarSeriesOption
|
|
|
+ | LineSeriesOption
|
|
|
+ | PieSeriesOption
|
|
|
+>;
|
|
|
+
|
|
|
+const { handleRequest } = useRequest();
|
|
|
+const energyConsumeResult = ref<ElectricityStatisticsResult>();
|
|
|
+
|
|
|
+const getEnergyConsumeResult = () => {
|
|
|
+ handleRequest(async () => {
|
|
|
+ energyConsumeResult.value = await getElectricityDataStatistics({
|
|
|
+ deviceGroupId: 7,
|
|
|
+ deviceTypes: [DeviceType.冷水主机, DeviceType.冷却塔, DeviceType.冷却泵, DeviceType.冷冻泵],
|
|
|
+ startTime: '2025-04-01 00:00:00',
|
|
|
+ endTime: '2025-05-01 23:00:00',
|
|
|
+ });
|
|
|
+
|
|
|
+ energyConsumeResult.value.groupQueryVos.forEach((item) => {
|
|
|
+ item.groupName = item.deviceTypeName + t('common.group');
|
|
|
+ });
|
|
|
+
|
|
|
+ energyConsumeResult.value.hisQueryVos.forEach((item) => {
|
|
|
+ item.groupName = item.deviceTypeName + t('common.group');
|
|
|
+ });
|
|
|
+ });
|
|
|
+};
|
|
|
+
|
|
|
+onMounted(() => {
|
|
|
+ getEnergyConsumeResult();
|
|
|
+});
|
|
|
+
|
|
|
+const time = ref<Dayjs>();
|
|
|
+
|
|
|
+const energyCardList = computed<EnergyCardItem[]>(() => {
|
|
|
+ return [
|
|
|
+ {
|
|
|
+ value: energyConsumeResult.value?.cumulativeEnergy ?? '-',
|
|
|
+ unit: t('energyAnalysis.tenThousandKwh'),
|
|
|
+ description: t('energyAnalysis.cumulativeElectricityConsumption'),
|
|
|
+ icon: cumulativeElectricityConsumption,
|
|
|
+ bgColor: '#FFF6E6',
|
|
|
+ },
|
|
|
+ {
|
|
|
+ value: energyConsumeResult.value?.cumulativeBill ?? '-',
|
|
|
+ unit: t('energyAnalysis.tenThousandYuan'),
|
|
|
+ description: t('energyAnalysis.cumulativeElectricityCost'),
|
|
|
+ icon: cumulativeElectricityBill,
|
|
|
+ bgColor: '#EFF1FE',
|
|
|
+ },
|
|
|
+ {
|
|
|
+ value: energyConsumeResult.value?.ringGrowth ?? '-',
|
|
|
+ unit: '%',
|
|
|
+ description: t('energyAnalysis.electricityConsumptionGrowthRate'),
|
|
|
+ icon: electricitySequentialGrowth,
|
|
|
+ bgColor: 'var(--antd-color-primary-opacity-15)',
|
|
|
+ },
|
|
|
+ {
|
|
|
+ value: energyConsumeResult.value?.cumulativeDailyUse ?? '-',
|
|
|
+ unit: t('energyAnalysis.tenThousandKwh'),
|
|
|
+ description: t('energyAnalysis.averageDailyElectricityConsumption'),
|
|
|
+ icon: electricityDailyConsumption,
|
|
|
+ bgColor: 'rgba(104, 194, 58, 0.12)',
|
|
|
+ },
|
|
|
+ ];
|
|
|
+});
|
|
|
+
|
|
|
+const statisticsTypes = ref<StatisticsTypeItem[]>([
|
|
|
+ {
|
|
|
+ value: 'electricityConsumption',
|
|
|
+ payload: {
|
|
|
+ title: t('energyAnalysis.electricityConsumption'),
|
|
|
+ icon: 'electricity-quantity',
|
|
|
+ },
|
|
|
+ },
|
|
|
+ {
|
|
|
+ value: 'electricityCost',
|
|
|
+ payload: {
|
|
|
+ title: t('energyAnalysis.electricityCost'),
|
|
|
+ icon: 'electricity-bill',
|
|
|
+ },
|
|
|
+ },
|
|
|
+]);
|
|
|
+
|
|
|
+const currentStatisticsType = ref<StatisticsValue>('electricityConsumption');
|
|
|
+
|
|
|
+const isElectricityConsume = computed(() => {
|
|
|
+ return currentStatisticsType.value === 'electricityConsumption';
|
|
|
+});
|
|
|
+
|
|
|
+const pieOption = computed<EChartsOption>(() => {
|
|
|
+ const centerValue = isElectricityConsume.value
|
|
|
+ ? energyConsumeResult.value?.cumulativeEnergy
|
|
|
+ : energyConsumeResult.value?.cumulativeBill;
|
|
|
+ const unitText = isElectricityConsume.value
|
|
|
+ ? t('energyAnalysis.tenThousandKwh')
|
|
|
+ : t('energyAnalysis.tenThousandYuan');
|
|
|
+ const legendData = energyConsumeResult.value?.groupQueryVos.map((item) => item.groupName as string);
|
|
|
+ const seriesData = energyConsumeResult.value?.groupQueryVos.map((item) => ({
|
|
|
+ name: item.groupName as string,
|
|
|
+ value: isElectricityConsume.value ? item.totalEnergyW : item.totalBillW,
|
|
|
+ }));
|
|
|
+
|
|
|
+ return {
|
|
|
+ tooltip: {
|
|
|
+ trigger: 'item',
|
|
|
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
|
+ formatter(params: any) {
|
|
|
+ return `${params.marker}${params.name}: ${params.value}${unitText}`;
|
|
|
+ },
|
|
|
+ },
|
|
|
+ legend: {
|
|
|
+ orient: 'horizontal',
|
|
|
+ bottom: 0,
|
|
|
+ textStyle: {
|
|
|
+ color: '#000',
|
|
|
+ fontSize: 12,
|
|
|
+ },
|
|
|
+ itemWidth: 8,
|
|
|
+ itemHeight: 8,
|
|
|
+ itemGap: 24,
|
|
|
+ data: legendData,
|
|
|
+ },
|
|
|
+ series: [
|
|
|
+ {
|
|
|
+ type: 'pie',
|
|
|
+ radius: ['40%', '65%'],
|
|
|
+ avoidLabelOverlap: true,
|
|
|
+ label: {
|
|
|
+ show: true,
|
|
|
+ position: 'outside',
|
|
|
+ formatter: '{d}%',
|
|
|
+ },
|
|
|
+ labelLine: {
|
|
|
+ show: true,
|
|
|
+ },
|
|
|
+ data: seriesData,
|
|
|
+ center: ['50%', '50%'],
|
|
|
+ },
|
|
|
+ {
|
|
|
+ type: 'pie',
|
|
|
+ radius: ['0', '30%'],
|
|
|
+ itemStyle: {
|
|
|
+ color: 'transparent',
|
|
|
+ },
|
|
|
+ tooltip: {
|
|
|
+ show: false,
|
|
|
+ },
|
|
|
+ label: {
|
|
|
+ position: 'center',
|
|
|
+ formatter: `{value|${centerValue ?? '-'}}\n{title|${unitText}}`,
|
|
|
+ rich: {
|
|
|
+ value: {
|
|
|
+ fontSize: 30,
|
|
|
+ fontWeight: 'bold',
|
|
|
+ color: '#000',
|
|
|
+ lineHeight: 36,
|
|
|
+ },
|
|
|
+ title: {
|
|
|
+ fontSize: 14,
|
|
|
+ color: '#666',
|
|
|
+ lineHeight: 22,
|
|
|
+ },
|
|
|
+ },
|
|
|
+ },
|
|
|
+ data: [{ value: 1, name: '' }],
|
|
|
+ },
|
|
|
+ ],
|
|
|
+ color: ['#4ECDC4', '#5470C6', '#36A2EB', '#FFC107'],
|
|
|
+ };
|
|
|
+});
|
|
|
+
|
|
|
+const lineOption = computed<EChartsOption>(() => {
|
|
|
+ const unitText = isElectricityConsume.value ? 'kwh' : t('energyAnalysis.yuan');
|
|
|
+ const tooltipTitle = isElectricityConsume.value
|
|
|
+ ? t('energyAnalysis.totalElectricityConsumption')
|
|
|
+ : t('energyAnalysis.totalElectricityCost');
|
|
|
+ const hisQueryVos = energyConsumeResult.value?.hisQueryVos || [];
|
|
|
+ const deviceTypes = hisQueryVos.map((item) => item.groupName as string) || [];
|
|
|
+ const dataset = {
|
|
|
+ source: [['time', ...deviceTypes]],
|
|
|
+ };
|
|
|
+
|
|
|
+ const timeSet = new Set();
|
|
|
+
|
|
|
+ hisQueryVos.forEach((item) => {
|
|
|
+ item.valueList.forEach((valueItem) => {
|
|
|
+ timeSet.add(valueItem.time);
|
|
|
+ });
|
|
|
+ });
|
|
|
+
|
|
|
+ const times = Array.from(timeSet);
|
|
|
+
|
|
|
+ times.forEach((time) => {
|
|
|
+ const row = [time];
|
|
|
+
|
|
|
+ deviceTypes.forEach((deviceType) => {
|
|
|
+ const deviceData = hisQueryVos.find((item) => item.groupName === deviceType);
|
|
|
+ const valueItem = deviceData?.valueList.find((item) => item.time === time);
|
|
|
+ row.push(isElectricityConsume.value ? valueItem?.energy : valueItem?.bill);
|
|
|
+ });
|
|
|
+
|
|
|
+ dataset.source.push(row as string[]);
|
|
|
+ });
|
|
|
+
|
|
|
+ return {
|
|
|
+ title: {
|
|
|
+ text: `${t('common.unit')}: ${unitText}`,
|
|
|
+ textStyle: {
|
|
|
+ color: '#999',
|
|
|
+ fontSize: 10,
|
|
|
+ fontWeight: 400,
|
|
|
+ lineHeight: 14,
|
|
|
+ },
|
|
|
+ left: '7%',
|
|
|
+ top: '9%',
|
|
|
+ },
|
|
|
+ tooltip: {
|
|
|
+ trigger: 'axis',
|
|
|
+ formatter(params) {
|
|
|
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
|
+ const tempParms = params as any[];
|
|
|
+ const time = tempParms[0].axisValue;
|
|
|
+ let totalValue = 0;
|
|
|
+
|
|
|
+ tempParms.forEach((param) => {
|
|
|
+ totalValue += param.value[param.encode.y[0]];
|
|
|
+ });
|
|
|
+
|
|
|
+ let tableHtml = '<table class="echarts-tooltip-electricity">';
|
|
|
+ tableHtml += `<tr><th>${time}</th></tr>`;
|
|
|
+ tableHtml += `<tr><td>${tooltipTitle}</td><td>${totalValue}${unitText}</td><td>100.00%</td></tr>`;
|
|
|
+
|
|
|
+ tempParms.forEach((param) => {
|
|
|
+ const value = param.value[param.encode.y[0]];
|
|
|
+ const percent = ((value / totalValue) * 100).toFixed(2) + '%';
|
|
|
+ tableHtml += `<tr><td>${param.marker}${param.seriesName}</td><td>${value}${unitText}</td><td>${percent}</td></tr>`;
|
|
|
+ });
|
|
|
+
|
|
|
+ tableHtml += '</table>';
|
|
|
+ return tableHtml;
|
|
|
+ },
|
|
|
+ },
|
|
|
+ legend: {
|
|
|
+ orient: 'horizontal',
|
|
|
+ bottom: 0,
|
|
|
+ textStyle: {
|
|
|
+ color: '#000',
|
|
|
+ fontSize: 12,
|
|
|
+ },
|
|
|
+ itemWidth: 22,
|
|
|
+ itemHeight: 8,
|
|
|
+ itemGap: 24,
|
|
|
+ data: deviceTypes,
|
|
|
+ },
|
|
|
+ dataset,
|
|
|
+ xAxis: {
|
|
|
+ type: 'category',
|
|
|
+ boundaryGap: true,
|
|
|
+ axisLine: {
|
|
|
+ lineStyle: {
|
|
|
+ type: 'dashed',
|
|
|
+ color: '#EBEBEB',
|
|
|
+ },
|
|
|
+ },
|
|
|
+ axisLabel: {
|
|
|
+ color: '#999',
|
|
|
+ },
|
|
|
+ axisTick: {
|
|
|
+ show: false,
|
|
|
+ },
|
|
|
+ },
|
|
|
+ yAxis: {
|
|
|
+ type: 'value',
|
|
|
+ axisLabel: {
|
|
|
+ color: '#999',
|
|
|
+ },
|
|
|
+ splitLine: {
|
|
|
+ lineStyle: {
|
|
|
+ type: 'dashed',
|
|
|
+ color: '#EBEBEB',
|
|
|
+ },
|
|
|
+ },
|
|
|
+ },
|
|
|
+ series: deviceTypes.map((type) => ({
|
|
|
+ name: type,
|
|
|
+ type: 'line',
|
|
|
+ stack: 'Total',
|
|
|
+ areaStyle: {
|
|
|
+ opacity: 0.3,
|
|
|
+ },
|
|
|
+ symbolSize: 6,
|
|
|
+ encode: {
|
|
|
+ x: 'time',
|
|
|
+ y: type,
|
|
|
+ },
|
|
|
+ })),
|
|
|
+ grid: {
|
|
|
+ left: '4%',
|
|
|
+ right: 0,
|
|
|
+ bottom: 46,
|
|
|
+ top: '17.5%',
|
|
|
+ containLabel: true,
|
|
|
+ },
|
|
|
+ color: ['#4ECDC4', '#5470C6', '#36A2EB', '#FFC107'],
|
|
|
+ };
|
|
|
+});
|
|
|
+
|
|
|
+const deviceAndGroups = computed<EquipmentData[]>(() => {
|
|
|
+ const groupQueryVos = energyConsumeResult.value?.groupQueryVos || [];
|
|
|
+
|
|
|
+ return groupQueryVos.map((item) => ({
|
|
|
+ key: item.deviceType,
|
|
|
+ name: item.groupName as string,
|
|
|
+ type: item.deviceTypeName,
|
|
|
+ electricity: item.totalEnergy,
|
|
|
+ cost: item.totalBill,
|
|
|
+ dailyElectricity: item.totalDailyUse,
|
|
|
+ ratio: item.totalRatio,
|
|
|
+ children: item.valueList?.map((child) => ({
|
|
|
+ key: child.deviceId.toString(),
|
|
|
+ name: child.deviceName,
|
|
|
+ type: item.deviceTypeName,
|
|
|
+ electricity: child.energy,
|
|
|
+ cost: child.bill,
|
|
|
+ dailyElectricity: child.dailyUse,
|
|
|
+ ratio: child.ratio,
|
|
|
+ })),
|
|
|
+ }));
|
|
|
+});
|
|
|
+
|
|
|
+const exportData = () => {
|
|
|
+ return;
|
|
|
+};
|
|
|
+
|
|
|
+const { handleRequest: handleDevElectricRequest } = useRequest();
|
|
|
+const { visible, showView } = useViewVisible();
|
|
|
+const selectedDevice = ref<EquipmentData>();
|
|
|
+const devEnergyConsumeResult = ref<ElectricityStatisticsResult>();
|
|
|
+
|
|
|
+watch(visible, () => {
|
|
|
+ if (visible.value) {
|
|
|
+ getDevElectricityData();
|
|
|
+ }
|
|
|
+});
|
|
|
+
|
|
|
+const viewDevElectricityData = (record: EquipmentData) => {
|
|
|
+ selectedDevice.value = record;
|
|
|
+ showView();
|
|
|
+};
|
|
|
+
|
|
|
+const getDevElectricityData = () => {
|
|
|
+ handleDevElectricRequest(async () => {
|
|
|
+ devEnergyConsumeResult.value = await getElectricityDataStatistics({
|
|
|
+ deviceGroupId: 7,
|
|
|
+ deviceId: selectedDevice.value?.key,
|
|
|
+ startTime: '2025-04-01 00:00:00',
|
|
|
+ endTime: '2025-05-01 23:00:00',
|
|
|
+ });
|
|
|
+ });
|
|
|
+};
|
|
|
+
|
|
|
+const devElectricityOption = computed<EChartsOption>(() => {
|
|
|
+ const hisQueryVos = devEnergyConsumeResult.value?.hisQueryVos[0]?.valueList || [];
|
|
|
+ const times: string[] = [];
|
|
|
+ const energy: number[] = [];
|
|
|
+ const bill: number[] = [];
|
|
|
+
|
|
|
+ hisQueryVos.forEach((item) => {
|
|
|
+ times.push(item.time);
|
|
|
+ energy.push(item.energy);
|
|
|
+ bill.push(item.bill);
|
|
|
+ });
|
|
|
+
|
|
|
+ // 计算 Y 轴的分割段数
|
|
|
+ const yAxisSegments = 5;
|
|
|
+ const energyMax = Math.ceil(Math.max(...energy) / yAxisSegments) * yAxisSegments;
|
|
|
+ const billMax = Math.ceil(Math.max(...bill) / yAxisSegments) * yAxisSegments;
|
|
|
+
|
|
|
+ return {
|
|
|
+ title: [
|
|
|
+ {
|
|
|
+ text: `${t('common.unit')}(kwh)`,
|
|
|
+ textStyle: {
|
|
|
+ color: '#999',
|
|
|
+ fontSize: 12,
|
|
|
+ fontWeight: 400,
|
|
|
+ lineHeight: 17,
|
|
|
+ },
|
|
|
+ left: '6%',
|
|
|
+ top: 0,
|
|
|
+ },
|
|
|
+ {
|
|
|
+ text: `${t('common.unit')}(${t('energyAnalysis.yuan')})`,
|
|
|
+ textStyle: {
|
|
|
+ color: '#999',
|
|
|
+ fontSize: 12,
|
|
|
+ fontWeight: 400,
|
|
|
+ lineHeight: 17,
|
|
|
+ },
|
|
|
+ right: '2%',
|
|
|
+ top: 0,
|
|
|
+ },
|
|
|
+ ],
|
|
|
+ tooltip: {
|
|
|
+ trigger: 'axis',
|
|
|
+ formatter(params) {
|
|
|
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
|
+ const tempParms = params as any[];
|
|
|
+ let tooltipStr = `<div>${tempParms[0].name}</div>`;
|
|
|
+
|
|
|
+ tempParms.forEach((param) => {
|
|
|
+ const unit =
|
|
|
+ param.seriesName === t('energyAnalysis.electricityConsumption') ? 'kwh' : t('energyAnalysis.yuan');
|
|
|
+ tooltipStr += `<div>${param.marker} ${param.seriesName}: ${param.value}${unit}</div>`;
|
|
|
+ });
|
|
|
+
|
|
|
+ return tooltipStr;
|
|
|
+ },
|
|
|
+ },
|
|
|
+ legend: [
|
|
|
+ {
|
|
|
+ orient: 'horizontal',
|
|
|
+ bottom: 0,
|
|
|
+ left: 263,
|
|
|
+ textStyle: {
|
|
|
+ color: '#000',
|
|
|
+ fontSize: 12,
|
|
|
+ },
|
|
|
+ itemWidth: 8,
|
|
|
+ itemHeight: 8,
|
|
|
+ itemGap: 24,
|
|
|
+ data: [t('energyAnalysis.electricityConsumption')],
|
|
|
+ },
|
|
|
+ {
|
|
|
+ orient: 'horizontal',
|
|
|
+ bottom: 0,
|
|
|
+ left: 331,
|
|
|
+ textStyle: {
|
|
|
+ color: '#000',
|
|
|
+ fontSize: 12,
|
|
|
+ },
|
|
|
+ itemWidth: 22,
|
|
|
+ itemHeight: 8,
|
|
|
+ itemGap: 24,
|
|
|
+ data: [t('energyAnalysis.electricityCost')],
|
|
|
+ },
|
|
|
+ ],
|
|
|
+ xAxis: {
|
|
|
+ type: 'category',
|
|
|
+ boundaryGap: true,
|
|
|
+ axisLine: {
|
|
|
+ lineStyle: {
|
|
|
+ type: 'dashed',
|
|
|
+ color: '#EBEBEB',
|
|
|
+ },
|
|
|
+ },
|
|
|
+ axisLabel: {
|
|
|
+ color: '#999',
|
|
|
+ fontSize: 10,
|
|
|
+ },
|
|
|
+ axisTick: {
|
|
|
+ show: false,
|
|
|
+ },
|
|
|
+ data: times,
|
|
|
+ },
|
|
|
+ yAxis: [
|
|
|
+ {
|
|
|
+ type: 'value',
|
|
|
+ position: 'left',
|
|
|
+ min: 0,
|
|
|
+ max: energyMax,
|
|
|
+ interval: energyMax / yAxisSegments,
|
|
|
+ axisLabel: {
|
|
|
+ color: '#999',
|
|
|
+ fontSize: 10,
|
|
|
+ },
|
|
|
+ splitLine: {
|
|
|
+ lineStyle: {
|
|
|
+ type: 'dashed',
|
|
|
+ color: '#EBEBEB',
|
|
|
+ },
|
|
|
+ },
|
|
|
+ },
|
|
|
+ {
|
|
|
+ type: 'value',
|
|
|
+ position: 'right',
|
|
|
+ min: 0,
|
|
|
+ max: billMax,
|
|
|
+ interval: billMax / yAxisSegments,
|
|
|
+ axisLabel: {
|
|
|
+ color: '#999',
|
|
|
+ fontSize: 10,
|
|
|
+ },
|
|
|
+ splitLine: {
|
|
|
+ lineStyle: {
|
|
|
+ type: 'dashed',
|
|
|
+ color: '#EBEBEB',
|
|
|
+ },
|
|
|
+ },
|
|
|
+ },
|
|
|
+ ],
|
|
|
+ series: [
|
|
|
+ {
|
|
|
+ name: t('energyAnalysis.electricityConsumption'),
|
|
|
+ type: 'bar',
|
|
|
+ data: energy,
|
|
|
+ barWidth: 24,
|
|
|
+ },
|
|
|
+ {
|
|
|
+ name: t('energyAnalysis.electricityCost'),
|
|
|
+ type: 'line',
|
|
|
+ yAxisIndex: 1,
|
|
|
+ data: bill,
|
|
|
+ symbolSize: 6,
|
|
|
+ },
|
|
|
+ ],
|
|
|
+ grid: {
|
|
|
+ left: '4%',
|
|
|
+ right: 0,
|
|
|
+ bottom: 38,
|
|
|
+ top: 25,
|
|
|
+ containLabel: true,
|
|
|
+ },
|
|
|
+ color: ['#32BAC0', '#5B8FF9'],
|
|
|
+ };
|
|
|
+});
|
|
|
+</script>
|
|
|
+
|
|
|
+<template>
|
|
|
+ <ACard :title="$t('energyAnalysis.energyConsumptionAnalysis')" :bordered="false">
|
|
|
+ <template #extra>
|
|
|
+ <span class="">{{ $t('common.selectTime') }}</span>
|
|
|
+ <ADatePicker v-model:value="time" picker="year" />
|
|
|
+ <AButton @click="exportData">{{ $t('common.export') }}</AButton>
|
|
|
+ </template>
|
|
|
+ <ARow class="energy-card-list" :gutter="[24, 24]">
|
|
|
+ <ACol v-for="(item, index) in energyCardList" :key="index" :xs="24" :sm="24" :md="12" :lg="12" :xl="6" :xxl="6">
|
|
|
+ <div
|
|
|
+ class="energy-card-container"
|
|
|
+ :style="{
|
|
|
+ backgroundColor: item.bgColor,
|
|
|
+ }"
|
|
|
+ >
|
|
|
+ <img class="energy-card-icon" :src="item.icon" />
|
|
|
+ <div>
|
|
|
+ <div class="energy-card-title">
|
|
|
+ <span class="energy-card-value">{{ item.value }}</span>
|
|
|
+ <span class="energy-card-unit">{{ item.unit }}</span>
|
|
|
+ </div>
|
|
|
+ <div class="energy-card-description">{{ item.description }}</div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </ACol>
|
|
|
+ </ARow>
|
|
|
+ <ASegmented v-model:value="currentStatisticsType" :options="statisticsTypes">
|
|
|
+ <template #label="{ payload }">
|
|
|
+ <SvgIcon :name="payload.icon" />
|
|
|
+ <span>{{ payload.title }}</span>
|
|
|
+ </template>
|
|
|
+ </ASegmented>
|
|
|
+ <div class="chart-container">
|
|
|
+ <div class="chart-wrapper chart-pie">
|
|
|
+ <VChart class="chart" :option="pieOption" autoresize />
|
|
|
+ </div>
|
|
|
+ <div class="chart-wrapper chart-line">
|
|
|
+ <VChart class="chart" :option="lineOption" autoresize />
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ <ATable :data-source="deviceAndGroups" :pagination="false">
|
|
|
+ <ATableColumn :title="$t('energyAnalysis.deviceGroupName')" data-index="name" key="name">
|
|
|
+ <template #default="{ record }">
|
|
|
+ <span v-if="record.children">{{ record.name }}</span>
|
|
|
+ <a v-else class="device-name" @click="viewDevElectricityData(record)">
|
|
|
+ {{ record.name }}
|
|
|
+ </a>
|
|
|
+ </template>
|
|
|
+ </ATableColumn>
|
|
|
+ <ATableColumn :title="$t('energyAnalysis.deviceType')" data-index="type" key="type" />
|
|
|
+ <ATableColumn
|
|
|
+ :title="$t('energyAnalysis.electricityConsumptionUnit')"
|
|
|
+ data-index="electricity"
|
|
|
+ key="electricity"
|
|
|
+ />
|
|
|
+ <ATableColumn :title="$t('energyAnalysis.electricityCostUnit')" data-index="cost" key="cost" />
|
|
|
+ <ATableColumn
|
|
|
+ :title="$t('energyAnalysis.dailyElectricityUnit')"
|
|
|
+ data-index="dailyElectricity"
|
|
|
+ key="dailyElectricity"
|
|
|
+ />
|
|
|
+ <ATableColumn :title="$t('energyAnalysis.electricityRatioUnit')" data-index="ratio" key="ratio" />
|
|
|
+ </ATable>
|
|
|
+ <AModal v-model:visible="visible" :title="selectedDevice?.name" :width="920" :footer="null">
|
|
|
+ <!-- <TimeRangeSelect class="device-electricity-select" is-last /> -->
|
|
|
+ <div class="device-electricity-detail">
|
|
|
+ <div class="device-electricity-summary">
|
|
|
+ <div class="device-electricity-item">
|
|
|
+ <span class="device-electricity-value">{{ devEnergyConsumeResult?.cumulativeEnergy ?? '-' }}</span>
|
|
|
+ <span class="device-electricity-label">{{ $t('energyAnalysis.tenThousandKwh') }}</span>
|
|
|
+ <div class="device-electricity-title">
|
|
|
+ <SvgIcon name="electricity-quantity" />
|
|
|
+ <span class="device-electricity-label">{{ $t('energyAnalysis.totalElectricityConsumption') }}</span>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ <div class="device-electricity-item">
|
|
|
+ <span class="device-electricity-value">{{ devEnergyConsumeResult?.cumulativeBill ?? '-' }}</span>
|
|
|
+ <span class="device-electricity-label">{{ $t('energyAnalysis.tenThousandYuan') }}</span>
|
|
|
+ <div class="device-electricity-title">
|
|
|
+ <SvgIcon name="electricity-bill" />
|
|
|
+ <span class="device-electricity-label">{{ $t('energyAnalysis.totalElectricityCost') }}</span>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ <VChart class="device-electricity-chart" :option="devElectricityOption" autoresize />
|
|
|
+ </div>
|
|
|
+ </AModal>
|
|
|
+ </ACard>
|
|
|
+</template>
|
|
|
+
|
|
|
+<style lang="scss" scoped>
|
|
|
+.energy-card-list {
|
|
|
+ margin-bottom: 24px;
|
|
|
+}
|
|
|
+
|
|
|
+:deep(.ant-card-body) {
|
|
|
+ .ant-segmented {
|
|
|
+ padding: 4px;
|
|
|
+ color: var(--antd-color-primary);
|
|
|
+ background-color: var(--antd-color-primary-opacity-15);
|
|
|
+ border-radius: 4px;
|
|
|
+
|
|
|
+ .ant-segmented-item-label {
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ padding-inline: 12px;
|
|
|
+
|
|
|
+ i {
|
|
|
+ margin-right: 4px;
|
|
|
+ font-size: 16px;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ .ant-segmented-item-selected {
|
|
|
+ color: var(--antd-color-primary);
|
|
|
+ box-shadow: none;
|
|
|
+ }
|
|
|
+
|
|
|
+ .ant-segmented-item {
|
|
|
+ + .ant-segmented-item {
|
|
|
+ margin-left: 8px;
|
|
|
+ }
|
|
|
+
|
|
|
+ &:hover:not(.ant-segmented-item-selected, .ant-segmented-item-disabled) {
|
|
|
+ color: var(--antd-color-primary);
|
|
|
+
|
|
|
+ &::after {
|
|
|
+ background-color: var(--antd-color-primary-opacity-15);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+.chart-container {
|
|
|
+ display: flex;
|
|
|
+ margin-bottom: 40px;
|
|
|
+}
|
|
|
+
|
|
|
+.chart-wrapper {
|
|
|
+ height: 318px;
|
|
|
+}
|
|
|
+
|
|
|
+.chart-pie {
|
|
|
+ width: 360px;
|
|
|
+}
|
|
|
+
|
|
|
+.chart-line {
|
|
|
+ width: calc(100% - 360px);
|
|
|
+}
|
|
|
+
|
|
|
+.chart {
|
|
|
+ height: 100%;
|
|
|
+}
|
|
|
+
|
|
|
+.device-name {
|
|
|
+ color: var(--antd-color-primary);
|
|
|
+}
|
|
|
+
|
|
|
+.device-electricity-select {
|
|
|
+ margin-top: 16px;
|
|
|
+}
|
|
|
+
|
|
|
+.device-electricity-detail {
|
|
|
+ display: flex;
|
|
|
+ margin-top: 14px;
|
|
|
+}
|
|
|
+
|
|
|
+.device-electricity-summary {
|
|
|
+ width: 250px;
|
|
|
+ height: 258px;
|
|
|
+ padding: 40px 24px;
|
|
|
+ margin-top: 10px;
|
|
|
+ background: #f5f7fa;
|
|
|
+ border-radius: 8px;
|
|
|
+}
|
|
|
+
|
|
|
+.device-electricity-item {
|
|
|
+ &:first-child {
|
|
|
+ padding-bottom: 25px;
|
|
|
+ border-bottom: 1px dashed #e4e7ed;
|
|
|
+ }
|
|
|
+
|
|
|
+ & + & {
|
|
|
+ margin-top: 24px;
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+.device-electricity-value {
|
|
|
+ margin-right: 4px;
|
|
|
+ font-family: DINAlternate;
|
|
|
+ font-size: 30px;
|
|
|
+ font-weight: bold;
|
|
|
+ line-height: 36px;
|
|
|
+ color: #000;
|
|
|
+}
|
|
|
+
|
|
|
+.device-electricity-label {
|
|
|
+ font-size: 14px;
|
|
|
+ font-weight: 500;
|
|
|
+ line-height: 22px;
|
|
|
+ color: #000;
|
|
|
+}
|
|
|
+
|
|
|
+.device-electricity-title {
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ margin-top: 4px;
|
|
|
+
|
|
|
+ i {
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ justify-content: center;
|
|
|
+ width: 24px;
|
|
|
+ height: 24px;
|
|
|
+ margin-right: 8px;
|
|
|
+ font-size: 16px;
|
|
|
+ color: #fff;
|
|
|
+ border-radius: 2px;
|
|
|
+ }
|
|
|
+
|
|
|
+ .svg-icon-electricity-quantity {
|
|
|
+ background-color: var(--antd-color-primary);
|
|
|
+ }
|
|
|
+
|
|
|
+ .svg-icon-electricity-bill {
|
|
|
+ background-color: #1f8ffb;
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+.device-electricity-chart {
|
|
|
+ height: 314px;
|
|
|
+}
|
|
|
+</style>
|