123 lines
3.7 KiB
Vue
123 lines
3.7 KiB
Vue
<template>
|
|
<div class="row" v-if="chartData.labels.length > 1">
|
|
<div class="col-12 col-md-6 mb-3">
|
|
<Doughnut :data="chartData" :options="options" />
|
|
</div>
|
|
<div class="col-12 col-md-6 d-flex align-items-center">
|
|
<LegendList :legends="legends" grid />
|
|
</div>
|
|
</div>
|
|
</template>
|
|
|
|
<script>
|
|
import { Doughnut } from "vue-chartjs";
|
|
import { DoughnutController, ArcElement, LinearScale, Legend, Tooltip } from "chart.js";
|
|
import LegendList from "./LegendList.vue";
|
|
import { registerChartComponents, commonOptions, tooltipLabelColor } from "./chartConfig";
|
|
import formatter, { POWER_UNIT } from "../../mixins/formatter";
|
|
import colors from "../../colors";
|
|
import { GROUPS } from "./types";
|
|
|
|
registerChartComponents([DoughnutController, ArcElement, LinearScale, Legend, Tooltip]);
|
|
|
|
export default {
|
|
name: "EnergyGroupedChart",
|
|
components: { Doughnut, LegendList },
|
|
props: {
|
|
sessions: { type: Array, default: () => [] },
|
|
groupBy: { type: String, default: GROUPS.NONE },
|
|
colorMappings: { type: Object, default: () => ({ loadpoint: {}, vehicle: {}, solar: {} }) },
|
|
},
|
|
mixins: [formatter],
|
|
computed: {
|
|
chartData() {
|
|
console.log("update energy aggregate data");
|
|
const aggregatedData = {};
|
|
|
|
if (this.groupBy === GROUPS.NONE) {
|
|
const total = this.sessions.reduce((acc, s) => acc + s.chargedEnergy, 0);
|
|
const self = this.sessions.reduce(
|
|
(acc, s) => acc + (s.chargedEnergy / 100) * s.solarPercentage,
|
|
0
|
|
);
|
|
aggregatedData.self = self;
|
|
aggregatedData.grid = total - self;
|
|
} else {
|
|
this.sessions.forEach((session) => {
|
|
const groupKey = session[this.groupBy];
|
|
if (!aggregatedData[groupKey]) {
|
|
aggregatedData[groupKey] = 0;
|
|
}
|
|
aggregatedData[groupKey] += session.chargedEnergy;
|
|
});
|
|
}
|
|
|
|
// Sort the data by energy in descending order
|
|
const sortedEntries = Object.entries(aggregatedData); //.sort((a, b) => b[1] - a[1]);
|
|
|
|
const labels = sortedEntries.map(([label]) =>
|
|
this.groupBy === GROUPS.NONE ? this.$t(`sessions.group.${label}`) : label
|
|
);
|
|
const data = sortedEntries.map(([, value]) => value);
|
|
const colorGroup = this.groupBy === GROUPS.NONE ? "solar" : this.groupBy;
|
|
const backgroundColor = sortedEntries.map(
|
|
([label]) => this.colorMappings[colorGroup][label]
|
|
);
|
|
|
|
return {
|
|
labels: labels,
|
|
datasets: [{ data, backgroundColor }],
|
|
};
|
|
},
|
|
legends() {
|
|
const total = this.chartData.datasets[0].data.reduce((acc, curr) => acc + curr, 0);
|
|
const maxEnergy = Math.max(...this.chartData.datasets[0].data);
|
|
// sync energy units for label grid view
|
|
const unit =
|
|
maxEnergy < 1 ? POWER_UNIT.W : maxEnergy > 1e4 ? POWER_UNIT.MW : POWER_UNIT.KW;
|
|
const fmtShare = (value) => this.fmtPercentage((100 / total) * value, 1);
|
|
const fmtValue = (value) => this.fmtWh(value * 1e3, unit);
|
|
return this.chartData.labels.map((label, index) => ({
|
|
label: label,
|
|
color: this.chartData.datasets[0].backgroundColor[index],
|
|
value: [
|
|
fmtValue(this.chartData.datasets[0].data[index]),
|
|
fmtShare(this.chartData.datasets[0].data[index]),
|
|
],
|
|
}));
|
|
},
|
|
options() {
|
|
return {
|
|
...commonOptions,
|
|
locale: this.$i18n?.locale,
|
|
aspectRatio: 1,
|
|
borderRadius: 10,
|
|
color: colors.text,
|
|
borderWidth: 3,
|
|
borderColor: colors.background,
|
|
cutout: "70%",
|
|
radius: "95%",
|
|
animation: { duration: 250 },
|
|
plugins: {
|
|
...commonOptions.plugins,
|
|
tooltip: {
|
|
...commonOptions.plugins.tooltip,
|
|
mode: "index",
|
|
position: "center",
|
|
intersect: false,
|
|
callbacks: {
|
|
label: (tooltipItem) => this.formatValue(tooltipItem.raw || 0),
|
|
labelColor: tooltipLabelColor(false),
|
|
},
|
|
},
|
|
},
|
|
};
|
|
},
|
|
},
|
|
methods: {
|
|
formatValue(value) {
|
|
return this.fmtWh(value * 1e3, POWER_UNIT.AUTO);
|
|
},
|
|
},
|
|
};
|
|
</script>
|