CviLux_fe/src/views/headquarters/components/ElecCompare.vue

361 lines
8.6 KiB
Vue

<script setup>
import { ref, onMounted, watch, computed } from "vue";
import * as echarts from "echarts";
import BarChart from "@/components/chart/BarChart.vue";
import { useI18n } from "vue-i18n";
const { locale, t } = useI18n();
// 假資料
const fakeEnergyData = ref({
buildings: [
{
name: "A棟",
today: 100,
yesterday: 90,
week: 500,
lastWeek: 450,
month: 2000,
lastMonth: 1800,
year: 24000,
lastYear: 22000,
},
{
name: "B棟",
today: 120,
yesterday: 110,
week: 600,
lastWeek: 550,
month: 2400,
lastMonth: 2200,
year: 28000,
lastYear: 26000,
},
{
name: "C棟",
today: 80,
yesterday: 70,
week: 400,
lastWeek: 350,
month: 1600,
lastMonth: 1400,
year: 19000,
lastYear: 17000,
},
{
name: "D棟",
today: 110,
yesterday: 100,
week: 550,
lastWeek: 500,
month: 2200,
lastMonth: 2000,
year: 26000,
lastYear: 24000,
},
],
});
const chartData = ref([]);
const currentType = ref({
name: "today",
});
const energyTypeList = ref([
{
title: t("dashboard.daily_relative_change"),
key: "today",
},
{
title: t("dashboard.weekly_relative_change"),
key: "week",
},
{
title: t("dashboard.monthly_relative_change"),
key: "month",
},
{
title: t("dashboard.yearly_relative_change"),
key: "year",
},
]);
const labels = computed(() => {
switch (currentType.value.name) {
case "today":
return [t("dashboard.today"), t("dashboard.yesterday")];
case "week":
return [t("dashboard.this_week"), t("dashboard.last_week")];
case "month":
return [t("dashboard.this_month"), t("dashboard.last_month")];
case "year":
return [t("dashboard.this_year"), t("dashboard.last_year")];
default:
return [t("dashboard.today"), t("dashboard.yesterday")];
}
});
const barWidth = 30; // Set barWidth
const barChartOptions = computed(() => ({
xAxis: {
type: "category",
data: chartData.value.map((item) => item.name),
axisLine: { lineStyle: { color: "#fff" } },
},
yAxis: { type: "value", show: false },
grid: {
left: "-10%",
right: "1%",
bottom: "3%",
top: "4%",
containLabel: true,
},
series: [
{
name: "當前",
data: chartData.value.map((item) => item.current),
type: "bar",
barWidth: barWidth,
barGap: "-10%",
itemStyle: {
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
{ offset: 0, color: "#186B80" },
{ offset: 1, color: "#50C3E3" },
]),
shadowBlur: 5,
shadowColor: "rgba(0, 0, 0, 0.3)",
shadowOffsetY: 2,
shadowOffsetX: 5,
},
z: 3,
},
{
name: "對比",
data: chartData.value.map((item) => item.last),
type: "bar",
barWidth: barWidth,
itemStyle: {
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
{ offset: 0, color: "#988F2C" },
{ offset: 1, color: "#FFF26D" },
]),
shadowBlur: 5,
shadowColor: "rgba(0, 0, 0, 0.3)",
shadowOffsetY: 2,
shadowOffsetX: 5,
},
},
{
// this top
z: 6,
type: "pictorialBar",
symbolPosition: "end",
data: chartData.value.map((item) => item.current),
symbol: "diamond",
symbolOffset: ["-45%", "-50%"],
symbolSize: [barWidth, barWidth * 0.5],
itemStyle: {
borderWidth: 0,
color: "#50C3E3",
},
},
{
// this bot
z: 6,
type: "pictorialBar",
symbolPosition: "start",
data: chartData.value.map((item) => item.current),
symbol: "diamond",
symbolOffset: ["-45%", "50%"],
symbolSize: [barWidth, barWidth * 0.5],
itemStyle: {
borderWidth: 0,
color: "#50C3E3",
},
},
{
// last top
z: 3,
type: "pictorialBar",
symbolPosition: "end",
data: chartData.value.map((item) => item.last),
symbol: "diamond",
symbolOffset: ["45%", "-50%"],
symbolSize: [barWidth, barWidth * 0.5],
itemStyle: {
borderWidth: 0,
color: "#FFF26D",
},
},
{
// last bot
z: 3,
type: "pictorialBar",
symbolPosition: "start",
data: chartData.value.map((item) => item.last),
symbol: "diamond",
symbolOffset: ["45%", "50%"],
symbolSize: [barWidth, barWidth * 0.5],
itemStyle: {
borderWidth: 0,
color: "#FFF26D",
},
},
],
tooltip: {
trigger: "axis",
axisPointer: { type: "shadow" },
formatter: function (params) {
let tooltipText = `<div>${params[0].axisValueLabel}</div>`;
const filteredParams = params.filter((item) => item.seriesType === "bar");
filteredParams.forEach((item) => {
tooltipText += `<div>${item.marker} ${
item.value ? item.value : "-"
}</div>`;
});
return tooltipText;
},
},
}));
function updateChartData() {
// 從 fakeEnergyData 提取資料
chartData.value = fakeEnergyData.value.buildings.map((building) => {
let currentKey = currentType.value.name;
let lastKey;
switch (currentType.value.name) {
case "today":
lastKey = "yesterday";
break;
case "week":
lastKey = "lastWeek";
break;
case "month":
lastKey = "lastMonth";
break;
case "year":
lastKey = "lastYear";
break;
default:
lastKey = "yesterday";
}
return {
name: building.name,
current: building[currentKey],
last: building[lastKey],
difference: building[currentKey] - building[lastKey],
};
});
}
// 使用 watch 監聽 fakeEnergyData 的變化
watch(
() => [fakeEnergyData.value, currentType.value],
() => {
updateChartData();
},
{ deep: true, immediate: true } // 立即執行一次,確保初始資料載入
);
// 监听 currentType 的变化
watch(currentType, () => {
updateChartData();
});
watch(locale, () => {
updateChartData();
});
</script>
<template>
<div class="flex flex-wrap">
<div class="w-full chart-data relative px-3 mb-3">
<div class="flex flex-wrap items-center justify-between">
<h2 class="font-light pt-1 px-1">
{{ $t("dashboard.relative_energy_consumption") }}
</h2>
<Select
:value="currentType"
class="!w-24"
selectClass="border-info focus-within:border-info btn-xs text-xs"
name="name"
Attribute="title"
:options="energyTypeList"
:isTopLabelExist="false"
:isBottomLabelExist="false"
>
</Select>
</div>
<div class="h-[100px]">
<BarChart
id="dashboard_chart_compare"
class="h-full"
:option="barChartOptions"
/>
</div>
<!-- 表格數據展示 -->
<div class="flex justify-between">
<div
v-for="(data, index) in chartData"
:key="index"
class="w-1/4 text-center mx-1"
>
<div
class="text-xs bg-cyan-900 p-1 border border-cyan-100 border-opacity-20"
>
{{ labels[0] }}
</div>
<div
class="text-sm bg-cyan-900 p-1 border border-cyan-100 border-opacity-20"
>
{{ data.current ?? "-" }}
</div>
<div
class="text-xs bg-cyan-900 p-1 border border-cyan-100 border-opacity-20"
>
{{ labels[1] }}
</div>
<div
class="text-sm bg-cyan-900 p-1 border border-cyan-100 border-opacity-20"
>
{{ data.last ?? "-" }}
</div>
<div
class="text-sm bg-cyan-900 p-1 border border-cyan-100 border-opacity-20"
>
<span
:class="{
'text-red-500': data.difference > 0,
'text-green-500': data.difference < 0,
}"
>
{{
data.difference
? (data.difference > 0 ? "+" : "") + data.difference
: "-"
}}
</span>
</div>
</div>
</div>
</div>
</div>
</template>
<style lang="scss" scoped>
.chart-data:before {
@apply absolute -left-0 -top-2 h-10 w-10 bg-no-repeat z-10;
content: "";
background: url(@ASSET/img/chart-data-background01.svg) center center;
}
.chart-data::after {
@apply absolute -right-1 -bottom-3 h-10 w-10 bg-no-repeat z-10;
content: "";
background: url(@ASSET/img/chart-data-background02.svg) center center;
}
</style>