152 lines
3.8 KiB
Vue
152 lines
3.8 KiB
Vue
<template>
|
|
<div>
|
|
<LegendList
|
|
:legends="[{ label: entry.title, color: entry.color, value: '', type: 'area' }]"
|
|
/>
|
|
<div ref="chartEl" class="soc-chart" :class="{ 'soc-chart--labels': showXAxis }"></div>
|
|
</div>
|
|
</template>
|
|
|
|
<script lang="ts">
|
|
import { defineComponent, type PropType } from "vue";
|
|
import {
|
|
FONT_FAMILY,
|
|
forecastXAxes,
|
|
forecastYAxis,
|
|
tooltipStyle,
|
|
tooltipTable,
|
|
} from "../Forecast/echarts";
|
|
import colors, { dimColor } from "@/colors";
|
|
import formatter from "@/mixins/formatter";
|
|
import echartsChart from "@/mixins/echartsChart";
|
|
import { is12hFormat } from "@/units";
|
|
import type { EvoptData } from "./TimeSeriesDataTable.vue";
|
|
import LegendList from "../Sessions/LegendList.vue";
|
|
import { slotTimes } from "./chart";
|
|
|
|
export interface SocChartEntry {
|
|
index: number; // index into evopt.res.batteries
|
|
title: string;
|
|
capacity: number; // kWh
|
|
color: string;
|
|
}
|
|
|
|
export default defineComponent({
|
|
name: "SocChart",
|
|
components: { LegendList },
|
|
mixins: [formatter, echartsChart],
|
|
props: {
|
|
evopt: {
|
|
type: Object as PropType<EvoptData>,
|
|
required: true,
|
|
},
|
|
entry: {
|
|
type: Object as PropType<SocChartEntry>,
|
|
required: true,
|
|
},
|
|
timestamp: {
|
|
type: String,
|
|
default: "",
|
|
},
|
|
showXAxis: { type: Boolean, default: false },
|
|
},
|
|
computed: {
|
|
times(): number[] {
|
|
return slotTimes(this.timestamp, this.evopt.req.time_series.dt);
|
|
},
|
|
endTime(): number {
|
|
const dt = this.evopt.req.time_series.dt;
|
|
const last = this.times[this.times.length - 1];
|
|
return last === undefined ? 0 : last + (dt[dt.length - 1] || 0) * 1000;
|
|
},
|
|
socSeries(): [number, number][] {
|
|
const soc = this.evopt.res.batteries[this.entry.index]?.state_of_charge || [];
|
|
const capacityWh = this.entry.capacity * 1000;
|
|
return this.times.map((t, i) => {
|
|
const wh = soc[i] ?? 0;
|
|
return [t, capacityWh > 0 ? (wh / capacityWh) * 100 : 0] as [number, number];
|
|
});
|
|
},
|
|
xAxes(): Record<string, unknown>[] {
|
|
const stepHours = is12hFormat() ? 6 : 4;
|
|
const [hourAxis, dayAxis] = forecastXAxes(
|
|
this.times[0] ?? 0,
|
|
this.endTime,
|
|
this.hourShort,
|
|
this.weekdayShort,
|
|
stepHours
|
|
);
|
|
if (!this.showXAxis) {
|
|
(hourAxis as Record<string, unknown>)["axisLabel"] = { show: false };
|
|
}
|
|
return [hourAxis, dayAxis];
|
|
},
|
|
chartOption(): Record<string, unknown> {
|
|
return {
|
|
animation: false,
|
|
textStyle: { fontFamily: FONT_FAMILY },
|
|
grid: {
|
|
top: 10,
|
|
right: 36,
|
|
bottom: this.showXAxis ? 34 : 8,
|
|
left: 0,
|
|
borderWidth: 0,
|
|
},
|
|
tooltip: {
|
|
trigger: "axis",
|
|
axisPointer: {
|
|
type: "line",
|
|
lineStyle: { color: colors.muted || "", opacity: 0.4 },
|
|
},
|
|
...tooltipStyle(colors.text || "", () => this.chart),
|
|
formatter: this.tooltipFormatter,
|
|
},
|
|
xAxis: this.xAxes,
|
|
yAxis: forecastYAxis({
|
|
min: 0,
|
|
max: 100,
|
|
position: "right",
|
|
interval: 50,
|
|
axisLabel: {
|
|
color: colors.muted || "",
|
|
formatter: (v: number) => this.fmtNumber(v, 0),
|
|
},
|
|
}),
|
|
series: [
|
|
{
|
|
type: "line",
|
|
step: "start",
|
|
z: 3,
|
|
data: this.socSeries,
|
|
showSymbol: false,
|
|
lineStyle: { color: this.entry.color, width: 2 },
|
|
itemStyle: { color: this.entry.color },
|
|
areaStyle: { color: dimColor(this.entry.color) },
|
|
emphasis: { disabled: true },
|
|
},
|
|
],
|
|
};
|
|
},
|
|
},
|
|
methods: {
|
|
tooltipFormatter(params: { axisValue: number; value?: [number, number] }[]): string {
|
|
const arr = Array.isArray(params) ? params : [params];
|
|
const v = arr[0]?.value?.[1];
|
|
if (v == null || Number.isNaN(v)) return "";
|
|
const t = new Date(arr[0]!.axisValue);
|
|
const head = `${this.weekdayShort(t)} ${this.fmtHourMinute(t)}`;
|
|
return tooltipTable(head, [{ values: [this.fmtPercentage(v)] }]);
|
|
},
|
|
},
|
|
});
|
|
</script>
|
|
|
|
<style scoped>
|
|
.soc-chart {
|
|
height: 54px;
|
|
width: 100%;
|
|
}
|
|
.soc-chart--labels {
|
|
height: 80px;
|
|
}
|
|
</style>
|