調整近30天能耗趨勢、環比能耗(假資料)

This commit is contained in:
koko 2025-07-28 14:53:40 +08:00
parent 8d23b695c6
commit 1812ce2495
6 changed files with 308 additions and 231 deletions

BIN
public/CviLux_globalmap.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 42 KiB

View File

@ -80,10 +80,16 @@ onUnmounted(() => {
未來瀚荃會持續精進提供更快更好以及高附加價值的產品與服務來滿足您的需求
</p>
</div>
<!--狀態進度-->
<!--在線狀態-->
<SysProgress />
</div>
<div class="w-full xl:w-2/4 mt-2"></div>
<div class="w-full xl:w-2/4 max-h-[calc(100vh-90px)] mt-2 border border-cyan-400 shadow-md shadow-cyan-500/40">
<img
src="/CviLux_globalmap.jpg"
alt=""
class="w-full h-full invert object-cover "
/>
</div>
<div class="w-full xl:w-1/4 mt-2">
<ElecRank :energyCostData="energyCostData" />
<ElecTrends
@ -98,6 +104,6 @@ onUnmounted(() => {
<style lang="scss" scoped>
.area-img-box {
@apply border border-light-info bg-gray-900/80 backdrop-blur-lg relative overflow-hidden shadow-md shadow-blue-300 mb-3;
@apply border border-light-info bg-gray-900/80 backdrop-blur-lg relative overflow-hidden shadow-md shadow-blue-300 mb-4;
}
</style>

View File

@ -5,35 +5,202 @@ import BarChart from "@/components/chart/BarChart.vue";
import { useI18n } from "vue-i18n";
const { locale, t } = useI18n();
const props = defineProps({
energyCostData: {
type: Object,
required: true,
},
//
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 labels = computed(() => [
t("dashboard.today"),
t("dashboard.yesterday"),
t("dashboard.this_week"),
t("dashboard.last_week"),
t("dashboard.this_month"),
t("dashboard.last_month"),
t("dashboard.this_year"),
t("dashboard.last_year"),
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 = ref({
const barChartOptions = computed(() => ({
xAxis: {
type: "category",
data: chartData.value.map((item) => item.category),
data: chartData.value.map((item) => item.name),
axisLine: { lineStyle: { color: "#fff" } },
},
yAxis: { type: "value", show: false },
series: [], //
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" },
@ -49,179 +216,56 @@ const barChartOptions = ref({
return tooltipText;
},
},
});
}));
function updateChartData(newEnergyCostData) {
if (newEnergyCostData && newEnergyCostData.compare) {
// props.energyCostData.compare
const compareData = newEnergyCostData.compare;
function updateChartData() {
// fakeEnergyData
chartData.value = fakeEnergyData.value.buildings.map((building) => {
let currentKey = currentType.value.name;
let lastKey;
//
chartData.value = [
{
category: t("dashboard.daily_relative_change"),
this: compareData.day.current,
last: compareData.day.last,
change: compareData.day.percentage,
},
{
category: t("dashboard.weekly_relative_change"),
this: compareData.week.current,
last: compareData.week.last,
change: compareData.week.percentage,
},
{
category: t("dashboard.monthly_relative_change"),
this: compareData.month.current,
last: compareData.month.last,
change: compareData.month.percentage,
},
{
category: t("dashboard.yearly_relative_change"),
this: compareData.year.current,
last: compareData.year.last,
change: compareData.year.percentage,
},
];
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";
}
// barChartOptions
barChartOptions.value = {
xAxis: {
type: "category",
data: chartData.value.map((item) => item.category),
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.this),
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.this),
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.this),
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;
},
},
return {
name: building.name,
current: building[currentKey],
last: building[lastKey],
difference: building[currentKey] - building[lastKey],
};
}
});
}
// 使 watch energyCostData
// 使 watch fakeEnergyData
watch(
() => props.energyCostData,
(newEnergyCostData) => {
updateChartData(newEnergyCostData);
() => [fakeEnergyData.value, currentType.value],
() => {
updateChartData();
},
{ immediate: true } //
{ deep: true, immediate: true } //
);
// currentType
watch(currentType, () => {
updateChartData();
});
watch(locale, () => {
updateChartData(props.energyCostData);
updateChartData();
});
</script>
@ -229,9 +273,20 @@ watch(locale, () => {
<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">
<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
@ -249,19 +304,19 @@ watch(locale, () => {
class="w-1/4 text-center mx-1"
>
<div
class="text-sm bg-cyan-900 p-1 border border-cyan-100 border-opacity-20"
class="text-xs bg-cyan-900 p-1 border border-cyan-100 border-opacity-20"
>
{{ labels[index * 2] }}
{{ labels[0] }}
</div>
<div
class="text-sm bg-cyan-900 p-1 border border-cyan-100 border-opacity-20"
>
{{ data.this ?? "-" }}
{{ data.current ?? "-" }}
</div>
<div
class="text-sm bg-cyan-900 p-1 border border-cyan-100 border-opacity-20"
class="text-xs bg-cyan-900 p-1 border border-cyan-100 border-opacity-20"
>
{{ labels[index * 2 + 1] }}
{{ labels[1] }}
</div>
<div
class="text-sm bg-cyan-900 p-1 border border-cyan-100 border-opacity-20"
@ -273,13 +328,13 @@ watch(locale, () => {
>
<span
:class="{
'text-red-500': data.change > 0,
'text-green-500': data.change < 0,
'text-red-500': data.difference > 0,
'text-green-500': data.difference < 0,
}"
>
{{
data.change
? (data.change > 0 ? "+" : "") + data.change + "%"
data.difference
? (data.difference > 0 ? "+" : "") + data.difference
: "-"
}}
</span>

View File

@ -37,7 +37,7 @@ const getCurrentEnergyData = () => {
</script>
<template>
<div class="state-box-col relative h-full max-h-[200px] mb-3">
<div class="state-box-col relative h-full max-h-[190px] mb-3">
<div class="state-box h-full">
<!-- 標題和切換按鈕 -->
<div class="flex justify-between items-center mb-2">
@ -58,7 +58,7 @@ const getCurrentEnergyData = () => {
</div>
<!-- 能耗排名列表 -->
<div class="max-h-[150px] overflow-y-auto">
<div class="max-h-[140px] overflow-y-auto">
<table class="table table-sm text-center">
<tbody>
<tr

View File

@ -11,25 +11,27 @@ const { t } = useI18n();
const props = defineProps({
formState: {
type: Object,
type: Object,
required: true,
},
energyCostData: {
type: Object,
type: Object,
required: true,
},
getEnergyCostData: {
type: Function,
required: true,
}
},
});
const chartData = ref([]);
const buildingList = ref([]);
const floorList = ref([]);
const deptList = ref([]);
const weekComparisonOption = ref({});
const currentType = ref({
name: "all",
});
// option
const generateCylinderChartOption = (data) => {
const barWidth = 15;
@ -98,7 +100,7 @@ const generateCylinderChartOption = (data) => {
left: "0%",
right: "0%",
bottom: "3%",
top: "10%",
top: "17%",
containLabel: true,
},
tooltip: {
@ -113,7 +115,7 @@ const generateCylinderChartOption = (data) => {
const processEnergyData = () => {
if (!props.energyCostData || !props.energyCostData.trend) {
chartData.value = [];
chartData.value = [];
weekComparisonOption.value = generateCylinderChartOption(chartData.value);
return;
}
@ -123,8 +125,8 @@ const processEnergyData = () => {
);
chartData.value = dailyData.map((item) => ({
date: dayjs(item.time).format("MM/DD"),
energy: item.value,
date: dayjs(item.time).format("MM/DD"),
energy: item.value,
}));
weekComparisonOption.value = generateCylinderChartOption(chartData.value);
@ -135,15 +137,35 @@ watch(
(newEnergyCostData) => {
processEnergyData();
},
{ deep: true, immediate: true }
{ deep: true, immediate: true }
);
watch(
() => storeBuild.buildings,
(newValue) => {
if (newValue) {
buildingList.value = [
{
title: "All",
key: "all",
},
...newValue.map((building) => ({
title: building.full_name,
key: building.building_guid,
})),
];
}
},
{
immediate: true,
}
);
watch(
() => storeBuild.floorList,
(newValue) => {
if (newValue) {
console.log('newValue',newValue);
floorList.value = [
{
title: "All",
@ -157,25 +179,6 @@ watch(
immediate: true,
}
);
watch(
() => storeBuild.floorList,
(newValue) => {
if (newValue) {
floorList.value = [
{
title: "All",
key: "all",
},
...storeBuild.floorList,
];
}
},
{
immediate: true,
}
);
watch(
() => storeBuild.deptList,
(newValue) => {
@ -199,7 +202,20 @@ watch(
<template>
<div class="w-full chart-data relative px-3 mb-3">
<div class="flex flex-wrap items-center justify-between">
<h2 class="font-light">{{ $t("dashboard.last_30_days_energy_trend") }}</h2>
<h2 class="font-light pt-1 px-1">
{{ $t("dashboard.last_30_days_energy_trend") }}
</h2>
<Select
:value="currentType"
class="w-auto my-2"
selectClass="border-info focus-within:border-info btn-xs text-xs"
name="name"
Attribute="title"
:options="buildingList"
:isTopLabelExist="false"
:isBottomLabelExist="false"
>
</Select>
</div>
<div class="h-[200px]">
<BarChart
@ -223,4 +239,4 @@ watch(
content: "";
background: url(@ASSET/img/chart-data-background02.svg) center center;
}
</style>
</style>

View File

@ -114,7 +114,7 @@ onUnmounted(() => {
}
.state-box {
@apply h-[21rem] border border-light-info shadow-md shadow-blue-300 rounded-sm py-2 px-6 mb-5 text-white relative;
@apply h-[21rem] border border-light-info shadow-md shadow-blue-300 rounded-sm py-2 px-6 mb-2 text-white relative;
}
.state-box:after {