首頁能源圖表串接
This commit is contained in:
parent
be68eb3568
commit
7599b7fa3a
@ -1,67 +1,73 @@
|
|||||||
<script setup>
|
<script setup>
|
||||||
import { ref, onMounted } from "vue";
|
import { ref, onMounted, onUnmounted, defineProps, watch } from "vue";
|
||||||
import * as echarts from "echarts";
|
import * as echarts from "echarts";
|
||||||
|
const props = defineProps({
|
||||||
// 定義用電比較資料
|
yesterdayTodayData: {
|
||||||
const yesterdayTodayData = {
|
type: Object,
|
||||||
categories: [
|
required: true,
|
||||||
"00:00",
|
},
|
||||||
"01:00",
|
weekComparisonData: {
|
||||||
"02:00",
|
type: Object,
|
||||||
"03:00",
|
required: true,
|
||||||
"04:00",
|
},
|
||||||
"05:00",
|
});
|
||||||
"06:00",
|
|
||||||
"07:00",
|
|
||||||
"08:00",
|
|
||||||
"09:00",
|
|
||||||
"10:00",
|
|
||||||
"11:00",
|
|
||||||
],
|
|
||||||
values: [
|
|
||||||
{
|
|
||||||
name: `2025.01.8 用電量`,
|
|
||||||
value: [8, 8, 8, 8, 8, 15, 25, 65, 75, 60, 70, 65],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: `2025.01.7 用電量`,
|
|
||||||
value: [10, 10, 10, 10, 10, 20, 30, 80, 90, 70, 80, 85],
|
|
||||||
},
|
|
||||||
],
|
|
||||||
};
|
|
||||||
|
|
||||||
// 本週與上週用電比較資料
|
|
||||||
const weekComparisonData = {
|
|
||||||
categories: ["Fri", "Sat", "Sun", "Mon", "Tue", "Wed", "Thu"],
|
|
||||||
values: [
|
|
||||||
{
|
|
||||||
name: "本週用電量",
|
|
||||||
value: [850, 200, 350, 850, 950, 950, 900],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "上週用電量",
|
|
||||||
value: [800, 150, 300, 750, 900, 900, 800],
|
|
||||||
},
|
|
||||||
],
|
|
||||||
};
|
|
||||||
|
|
||||||
const yesterdayTodayChart = ref(null);
|
const yesterdayTodayChart = ref(null);
|
||||||
const weekComparisonChart = ref(null);
|
const weekComparisonChart = ref(null);
|
||||||
|
let yesterdayTodayEChart = null;
|
||||||
|
let weekComparisonEChart = null;
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => props.yesterdayTodayData,
|
||||||
|
(newYesterdayTodayData) => {
|
||||||
|
// 更新圖表
|
||||||
|
if (yesterdayTodayEChart) {
|
||||||
|
yesterdayTodayEChart.setOption(
|
||||||
|
generateCylinderChartOption(newYesterdayTodayData)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{ deep: true }
|
||||||
|
);
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => props.weekComparisonData,
|
||||||
|
(newWeekComparisonData) => {
|
||||||
|
// 更新圖表
|
||||||
|
if (weekComparisonEChart) {
|
||||||
|
weekComparisonEChart.setOption(
|
||||||
|
generateCylinderChartOption(newWeekComparisonData)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{ deep: true }
|
||||||
|
);
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
// 初始化昨天和今天用電比較圖表
|
// 初始化昨天和今天用電比較圖表
|
||||||
const yesterdayTodayEChart = echarts.init(yesterdayTodayChart.value);
|
yesterdayTodayEChart = echarts.init(yesterdayTodayChart.value);
|
||||||
yesterdayTodayEChart.setOption(
|
yesterdayTodayEChart.setOption(
|
||||||
generateCylinderChartOption(yesterdayTodayData)
|
generateCylinderChartOption(props.yesterdayTodayData)
|
||||||
);
|
);
|
||||||
|
|
||||||
// 初始化本週和上週用電比較圖表
|
// 初始化本週和上週用電比較圖表
|
||||||
const weekComparisonEChart = echarts.init(weekComparisonChart.value);
|
weekComparisonEChart = echarts.init(weekComparisonChart.value);
|
||||||
weekComparisonEChart.setOption(
|
weekComparisonEChart.setOption(
|
||||||
generateCylinderChartOption(weekComparisonData)
|
generateCylinderChartOption(props.weekComparisonData)
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
onUnmounted(() => {
|
||||||
|
if (yesterdayTodayEChart) {
|
||||||
|
yesterdayTodayEChart.dispose();
|
||||||
|
yesterdayTodayEChart = null;
|
||||||
|
}
|
||||||
|
if (weekComparisonEChart) {
|
||||||
|
weekComparisonEChart.dispose();
|
||||||
|
weekComparisonEChart = null;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
// 生成圓柱圖表的 option
|
// 生成圓柱圖表的 option
|
||||||
const generateCylinderChartOption = (data) => {
|
const generateCylinderChartOption = (data) => {
|
||||||
const barWidth = 15;
|
const barWidth = 15;
|
||||||
@ -101,9 +107,12 @@ const generateCylinderChartOption = (data) => {
|
|||||||
formatter: function (params) {
|
formatter: function (params) {
|
||||||
// 僅顯示 bar 系列的資料
|
// 僅顯示 bar 系列的資料
|
||||||
const barData = params.filter((item) => item.seriesType === "bar");
|
const barData = params.filter((item) => item.seriesType === "bar");
|
||||||
return barData
|
const category = data.categories[params[0].dataIndex];
|
||||||
.map((item) => `${item.seriesName}: ${item.data}`)
|
let tooltipContent = `${category}<br/>`;
|
||||||
.join("<br/>");
|
barData.forEach((item) => {
|
||||||
|
tooltipContent += `${item.seriesName}: ${item.data}<br/>`;
|
||||||
|
});
|
||||||
|
return tooltipContent;
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
legend: {
|
legend: {
|
||||||
|
@ -1,35 +1,15 @@
|
|||||||
<script setup>
|
<script setup>
|
||||||
const mockData = [
|
import { defineProps } from "vue";
|
||||||
{
|
const props = defineProps({
|
||||||
value: 305.5,
|
elecData: {
|
||||||
label: "今日用電量",
|
type: Object,
|
||||||
unit: "kWH",
|
|
||||||
icon: "leaf",
|
|
||||||
},
|
},
|
||||||
{
|
});
|
||||||
value: 886.75,
|
|
||||||
label: "昨日用電量",
|
|
||||||
unit: "kWH",
|
|
||||||
icon: "leaf",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
value: 7.84,
|
|
||||||
label: "即時功率",
|
|
||||||
unit: "kW",
|
|
||||||
icon: "bolt",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
value: 20.96,
|
|
||||||
label: "容積占比",
|
|
||||||
unit: "%",
|
|
||||||
icon: "charging-station",
|
|
||||||
},
|
|
||||||
];
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<a-row :gutter="24">
|
<a-row :gutter="24">
|
||||||
<a-col v-for="(item, index) in mockData" :key="index" :span="6" class="mb-5">
|
<a-col v-for="(item, index) in elecData" :key="index" :span="6" class="mb-5">
|
||||||
<a-card class="number">
|
<a-card class="number">
|
||||||
<a-row :gutter="24" align="middle" justify="space-between">
|
<a-row :gutter="24" align="middle" justify="space-between">
|
||||||
<a-col :span="18">
|
<a-col :span="18">
|
||||||
|
106
src/stores/useElecDemandStore.js
Normal file
106
src/stores/useElecDemandStore.js
Normal file
@ -0,0 +1,106 @@
|
|||||||
|
import { ref } from "vue";
|
||||||
|
import { defineStore } from "pinia";
|
||||||
|
|
||||||
|
const useElecDemandStore = defineStore("elecDemand", () => {
|
||||||
|
const elecData = ref([]);
|
||||||
|
const subscribers = ref([]);
|
||||||
|
|
||||||
|
// get data from baja
|
||||||
|
const getElecDemandFromBaja = () => {
|
||||||
|
// @ts-ignore
|
||||||
|
window.require &&
|
||||||
|
// @ts-ignore
|
||||||
|
window.requirejs(["baja!"], (baja) => {
|
||||||
|
let eleclist = [];
|
||||||
|
baja.Ord.make(
|
||||||
|
`local:|foxs:4918|station:|neql:EMS:kw|bql:select slotPath,parent.displayName,name,out`
|
||||||
|
).get({
|
||||||
|
cursor: {
|
||||||
|
before: () => {},
|
||||||
|
each: (record) => {
|
||||||
|
const newItem = {
|
||||||
|
slotPath: record.get("slotPath"),
|
||||||
|
displayName: record.get("parent$2edisplayName"),
|
||||||
|
name: record.get("name"),
|
||||||
|
out: record.get("out")?.get("value") ?? 0,
|
||||||
|
};
|
||||||
|
eleclist.push(newItem);
|
||||||
|
},
|
||||||
|
after: () => {
|
||||||
|
elecData.value = [];
|
||||||
|
elecData.value.push(...eleclist);
|
||||||
|
eleclist.forEach((item, index) => {
|
||||||
|
subscribeToHistory(item, index);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const subscribeToHistory = (item, index) => {
|
||||||
|
const slotPath = item.slotPath;
|
||||||
|
const ordString = `local:|foxs:4918|station:|${slotPath}`;
|
||||||
|
|
||||||
|
// @ts-ignore
|
||||||
|
window.require &&
|
||||||
|
// @ts-ignore
|
||||||
|
window.requirejs(["baja!"], (baja) => {
|
||||||
|
// 建立訂閱器
|
||||||
|
const subscriber = new baja.Subscriber();
|
||||||
|
|
||||||
|
// 定義 changed 事件的處理函數
|
||||||
|
subscriber.attach("changed", (prop) => {
|
||||||
|
try {
|
||||||
|
if (prop && prop.getName() === "out") {
|
||||||
|
// 取得 out 的新值
|
||||||
|
const match = prop.$display.match(/^(\d+(\.\d+)?)/);
|
||||||
|
const newValue = match ? parseFloat(match[0]) : 0;
|
||||||
|
// 更新 elecData 中對應的 out 值
|
||||||
|
const updatedIndex = elecData.value.findIndex(
|
||||||
|
(data) => data.slotPath === item.slotPath
|
||||||
|
);
|
||||||
|
|
||||||
|
if (updatedIndex !== -1) {
|
||||||
|
elecData.value[updatedIndex].out = Number(newValue);
|
||||||
|
console.log(`Niagara 用電需求 ${item.name} 更新:`, newValue);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error(
|
||||||
|
`處理 ${item.name || index} 告警變化失敗: ${error.message}`,
|
||||||
|
error
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
baja.Ord.make(ordString)
|
||||||
|
.get({ subscriber })
|
||||||
|
.then(() => {
|
||||||
|
console.log(`Successfuly subscribed to ${item.name}`);
|
||||||
|
})
|
||||||
|
.catch((err) => {
|
||||||
|
console.error(`訂閱 ${item.name || index} 失敗: ${err.message}`);
|
||||||
|
subscriber.detach("changed"); // 移除事件監聽器
|
||||||
|
});
|
||||||
|
subscribers.value.push(subscriber);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const clearAllSubscriber = () => {
|
||||||
|
subscribers.value.forEach((subscriber) => {
|
||||||
|
subscriber.detach("changed");
|
||||||
|
subscriber.unsubscribeAll(); // 移除所有訂閱
|
||||||
|
});
|
||||||
|
subscribers.value = [];
|
||||||
|
console.log("所有訂閱已清除");
|
||||||
|
};
|
||||||
|
|
||||||
|
return {
|
||||||
|
getElecDemandFromBaja,
|
||||||
|
elecData,
|
||||||
|
clearAllSubscriber,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
export default useElecDemandStore;
|
194
src/stores/useElecTotalMeterStore.js
Normal file
194
src/stores/useElecTotalMeterStore.js
Normal file
@ -0,0 +1,194 @@
|
|||||||
|
import { ref } from "vue";
|
||||||
|
import { defineStore } from "pinia";
|
||||||
|
import dayjs from "dayjs";
|
||||||
|
|
||||||
|
import { calcuEleCost } from "@/utils";
|
||||||
|
|
||||||
|
const useElecStore = defineStore("elecData", () => {
|
||||||
|
const elecData = ref([]);
|
||||||
|
const elecPriceData = ref([]);
|
||||||
|
let timerId = null;
|
||||||
|
const todayelecdata = ref(new Map());
|
||||||
|
const yesterdayelecdata = ref(new Map());
|
||||||
|
const elecFlowCostSummary = ref(null);
|
||||||
|
|
||||||
|
const getElecDataFromBaja = () => {
|
||||||
|
window.require &&
|
||||||
|
window.requirejs(["baja!"], (baja) => {
|
||||||
|
console.log("進入 bajaSubscriber 準備執行用電價 BQL 訂閱");
|
||||||
|
|
||||||
|
// 定義BQL 查詢
|
||||||
|
const price_kwhBql = `local:|foxs:4918|station:|neql:EMS:parameter|bql:select slotPath,parent.displayName,displayName,NumericInterval.historyConfig.id,out`;
|
||||||
|
|
||||||
|
// 執行查詢
|
||||||
|
let pricelist = [];
|
||||||
|
baja.Ord.make(price_kwhBql).get({
|
||||||
|
cursor: {
|
||||||
|
before: () => {},
|
||||||
|
each: (record) => {
|
||||||
|
pricelist.push({
|
||||||
|
slotPath: record.get("slotPath"),
|
||||||
|
displayName: record.get("displayName"),
|
||||||
|
id: record.get("NumericInterval$2ehistoryConfig$2eid").$cEncStr,
|
||||||
|
out: record.get("out")?.get("value") ?? 0,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
after: () => {
|
||||||
|
elecPriceData.value = [];
|
||||||
|
elecPriceData.value.push(...pricelist);
|
||||||
|
},
|
||||||
|
limit: -1,
|
||||||
|
offset: 0,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
console.log("進入 bajaSubscriber 準備執行用電度數 BQL 訂閱");
|
||||||
|
|
||||||
|
// 定義BQL 查詢
|
||||||
|
const Total_kwhBql = `local:|foxs:4918|station:|neql:EMS:Total_kwh|bql:select slotPath,parent.displayName,displayName,NumericInterval.historyConfig.id,out`;
|
||||||
|
|
||||||
|
// 執行各電表的 BQL 查詢
|
||||||
|
let eleclist = [];
|
||||||
|
baja.Ord.make(Total_kwhBql).get({
|
||||||
|
cursor: {
|
||||||
|
before: () => {},
|
||||||
|
each: (record) => {
|
||||||
|
eleclist.push({
|
||||||
|
slotPath: record.get("slotPath"),
|
||||||
|
displayName: record.get("parent$2edisplayName"),
|
||||||
|
id: record.get("NumericInterval$2ehistoryConfig$2eid").$cEncStr,
|
||||||
|
out: record.get("out")?.get("value") ?? null,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
after: () => {
|
||||||
|
const validElecList = eleclist.filter(
|
||||||
|
(item) => item.id !== undefined
|
||||||
|
);
|
||||||
|
elecData.value = [];
|
||||||
|
elecData.value.push(...validElecList);
|
||||||
|
validElecList.forEach((item) => {
|
||||||
|
gettimeToHistory(item);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const gettimeToHistory = (item) => {
|
||||||
|
const id = item.id;
|
||||||
|
const startTime = dayjs()
|
||||||
|
.subtract(13, "day")
|
||||||
|
.startOf("day")
|
||||||
|
.format("YYYY-MM-DDTHH:mm:ss.000+08:00");
|
||||||
|
const endTime = dayjs()
|
||||||
|
.endOf("day")
|
||||||
|
.format("YYYY-MM-DDTHH:mm:ss.000+08:00");
|
||||||
|
|
||||||
|
const ordString = `local:|foxs:4918|history:${id}?period=timerange;;start=${startTime};end=${endTime}|bql:history:HistoryRollup.rollup(baja:RelTime '3600000')`; //每小时一个rollup
|
||||||
|
console.log(ordString);
|
||||||
|
// @ts-ignore
|
||||||
|
window.require &&
|
||||||
|
// @ts-ignore
|
||||||
|
window.requirejs(["baja!"], (baja) => {
|
||||||
|
console.log("進入 bajaSubscriber 準備執行 BQL 訂閱");
|
||||||
|
const dataMap = new Map();
|
||||||
|
let lastTimestamp = null;
|
||||||
|
let lastValue = null;
|
||||||
|
baja.Ord.make(ordString).get({
|
||||||
|
cursor: {
|
||||||
|
before: () => {
|
||||||
|
console.log(`開始訂閱 ${id} 的歷史資料`);
|
||||||
|
},
|
||||||
|
each: (record) => {
|
||||||
|
const currentValue = record.get("min");
|
||||||
|
const timestamp = record.get("timestamp").$cEncStr;
|
||||||
|
if (
|
||||||
|
currentValue !== null &&
|
||||||
|
currentValue !== 0 &&
|
||||||
|
timestamp !== null
|
||||||
|
) {
|
||||||
|
if (
|
||||||
|
lastTimestamp !== null &&
|
||||||
|
lastValue !== null &&
|
||||||
|
lastValue !== 0
|
||||||
|
) {
|
||||||
|
const diff = currentValue - lastValue;
|
||||||
|
dataMap.set(lastTimestamp, diff);
|
||||||
|
}
|
||||||
|
lastValue = currentValue;
|
||||||
|
lastTimestamp = timestamp;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
after: () => {
|
||||||
|
console.log("⏱️ 每小時差值 map:", dataMap);
|
||||||
|
// 清空之前的數據,避免累積
|
||||||
|
todayelecdata.value = new Map();
|
||||||
|
yesterdayelecdata.value = new Map();
|
||||||
|
|
||||||
|
// 提取今天和昨天的数据
|
||||||
|
for (const [timestamp, value] of dataMap) {
|
||||||
|
const date = dayjs(timestamp).format("YYYY-MM-DD");
|
||||||
|
const today = dayjs().format("YYYY-MM-DD");
|
||||||
|
const yesterday = dayjs().subtract(1, "day").format("YYYY-MM-DD");
|
||||||
|
|
||||||
|
if (date === today) {
|
||||||
|
todayelecdata.value.set(timestamp, value);
|
||||||
|
} else if (date === yesterday) {
|
||||||
|
yesterdayelecdata.value.set(timestamp, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log("今天每小時差值 map:", todayelecdata.value);
|
||||||
|
console.log("昨天每小時差值 map:", yesterdayelecdata.value);
|
||||||
|
elecFlowCostSummary.value = calcuEleCost(
|
||||||
|
dataMap,
|
||||||
|
elecPriceData.value
|
||||||
|
);
|
||||||
|
},
|
||||||
|
limit: -1,
|
||||||
|
offset: 0,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
// 定時更新資料的函數
|
||||||
|
const updateHistoryData = () => {
|
||||||
|
console.log("定時器觸發,重新獲取歷史資料");
|
||||||
|
if (elecData.value && elecData.value.length > 0) {
|
||||||
|
elecData.value.forEach((item) => {
|
||||||
|
gettimeToHistory(item);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// 啟動定時器
|
||||||
|
const startTimer = () => {
|
||||||
|
timerId = setInterval(() => {
|
||||||
|
updateHistoryData();
|
||||||
|
}, 60 * 60 * 1000); // 每小時執行一次
|
||||||
|
};
|
||||||
|
|
||||||
|
// 停止定時器
|
||||||
|
const stopTimer = () => {
|
||||||
|
// @ts-ignore
|
||||||
|
if (timerId) {
|
||||||
|
clearInterval(timerId);
|
||||||
|
timerId = null;
|
||||||
|
console.log("計時器已停止");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
return {
|
||||||
|
getElecDataFromBaja,
|
||||||
|
startTimer,
|
||||||
|
stopTimer,
|
||||||
|
elecData,
|
||||||
|
elecPriceData,
|
||||||
|
elecFlowCostSummary,
|
||||||
|
todayelecdata,
|
||||||
|
yesterdayelecdata,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
export default useElecStore;
|
140
src/utils/calcuEleCost.js
Normal file
140
src/utils/calcuEleCost.js
Normal file
@ -0,0 +1,140 @@
|
|||||||
|
export const calcuEleCost = (input, elecPriceData) => {
|
||||||
|
const dailyData = new Map();
|
||||||
|
let totalFlowCost = 0; // 總電價
|
||||||
|
let totalEleCost = 0; //總用電
|
||||||
|
let total_Off = 0; //總離峰用電
|
||||||
|
let total_half = 0; //總半尖峰用電
|
||||||
|
let total_peak = 0; //總尖峰用電
|
||||||
|
let total_OffCost = 0; //總離峰電價
|
||||||
|
let total_halfCost = 0; //總半尖峰電價
|
||||||
|
let total_peakCost = 0; //總尖峰電價
|
||||||
|
|
||||||
|
const dailyResults = [];
|
||||||
|
const elecPrices = elecPriceData;
|
||||||
|
const Summer_Off_Prices =
|
||||||
|
elecPrices.find((item) => item.displayName === "流動週日離峰夏月")
|
||||||
|
?.out || 0;
|
||||||
|
const Summer_HalfPeak_Prices_Saturday =
|
||||||
|
elecPrices.find((item) => item.displayName === "流動週六半尖峰夏月")
|
||||||
|
?.out || 0;
|
||||||
|
const Summer_HalfPeak_Prices_Weekday =
|
||||||
|
elecPrices.find((item) => item.displayName === "流動平日半尖峰夏月")
|
||||||
|
?.out || 0;
|
||||||
|
const Summer_Peak_Prices =
|
||||||
|
elecPrices.find((item) => item.displayName === "流動平日尖峰夏月")
|
||||||
|
?.out || 0;
|
||||||
|
const Non_Summer_Off_Prices =
|
||||||
|
elecPrices.find((item) => item.displayName === "流動週日離峰非夏月")
|
||||||
|
?.out || 0;
|
||||||
|
const Non_Summer_HalfPeak_Prices_Saturday =
|
||||||
|
elecPrices.find((item) => item.displayName === "流動週六半尖峰非夏月")
|
||||||
|
?.out || 0;
|
||||||
|
const Non_Summer_HalfPeak_Prices_Weekday =
|
||||||
|
elecPrices.find((item) => item.displayName === "流動平日半尖峰非夏月")
|
||||||
|
?.out || 0;
|
||||||
|
// 1. 將輸入資料按日期分組
|
||||||
|
input.forEach((value, key) => {
|
||||||
|
const dateStr = key.substring(0, 10);
|
||||||
|
if (!dailyData.has(dateStr)) {
|
||||||
|
dailyData.set(dateStr, []);
|
||||||
|
}
|
||||||
|
dailyData.get(dateStr)?.push({ time: new Date(key), value });
|
||||||
|
});
|
||||||
|
|
||||||
|
for (const [dateStr, entries] of dailyData.entries()) {
|
||||||
|
if (!entries || entries.length === 0) continue;
|
||||||
|
|
||||||
|
const sampleDate = entries[0].time;
|
||||||
|
const dayOfWeek = sampleDate.getDay();
|
||||||
|
const month = sampleDate.getMonth() + 1;
|
||||||
|
|
||||||
|
let off = 0; //離峰用電
|
||||||
|
let half = 0; //半尖峰用電
|
||||||
|
let peak = 0; //尖峰用電
|
||||||
|
let offcost = 0; //離峰電價
|
||||||
|
let halfcost = 0; //半尖峰電價
|
||||||
|
let peakcost = 0; //尖峰電價
|
||||||
|
let dailyFlowCost = 0; //當日電價
|
||||||
|
let dailyEleCost = 0; //當日用電
|
||||||
|
|
||||||
|
const isSummer = month >= 6 && month <= 9;
|
||||||
|
|
||||||
|
entries.forEach(({ time, value }) => {
|
||||||
|
const hour = time.getHours();
|
||||||
|
|
||||||
|
if (isSummer) {
|
||||||
|
if (dayOfWeek === 0) {
|
||||||
|
off += value;
|
||||||
|
} else if (dayOfWeek === 6) {
|
||||||
|
if (hour < 9) off += value;
|
||||||
|
else half += value;
|
||||||
|
} else {
|
||||||
|
if (hour >= 16 && hour < 22) peak += value;
|
||||||
|
else if ((hour >= 9 && hour < 16) || hour >= 22) half += value;
|
||||||
|
else off += value;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (dayOfWeek === 0) {
|
||||||
|
off += value;
|
||||||
|
} else {
|
||||||
|
if (hour < 6 || (hour >= 11 && hour < 14)) off += value;
|
||||||
|
else half += value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (isSummer) {
|
||||||
|
console.log("夏月計費");
|
||||||
|
const summerHalfPeakRate =
|
||||||
|
dayOfWeek === 6
|
||||||
|
? Summer_HalfPeak_Prices_Saturday
|
||||||
|
: Summer_HalfPeak_Prices_Weekday;
|
||||||
|
offcost = off * Summer_Off_Prices;
|
||||||
|
halfcost = half * summerHalfPeakRate;
|
||||||
|
peakcost = peak * Summer_Peak_Prices;
|
||||||
|
dailyFlowCost = offcost + halfcost + peakcost;
|
||||||
|
dailyEleCost = off + half + peak;
|
||||||
|
} else {
|
||||||
|
console.log("非夏月計費");
|
||||||
|
const nonSummerHalfPeakRate =
|
||||||
|
dayOfWeek === 6
|
||||||
|
? Non_Summer_HalfPeak_Prices_Saturday
|
||||||
|
: Non_Summer_HalfPeak_Prices_Weekday;
|
||||||
|
offcost = off * Non_Summer_Off_Prices;
|
||||||
|
halfcost = half * nonSummerHalfPeakRate;
|
||||||
|
dailyFlowCost = offcost + halfcost;
|
||||||
|
dailyEleCost = off + half;
|
||||||
|
}
|
||||||
|
|
||||||
|
totalFlowCost += dailyFlowCost;
|
||||||
|
totalEleCost += dailyEleCost;
|
||||||
|
total_Off += off;
|
||||||
|
total_half += half;
|
||||||
|
total_peak += peak;
|
||||||
|
total_OffCost += offcost;
|
||||||
|
total_halfCost += halfcost;
|
||||||
|
total_peakCost += peakcost;
|
||||||
|
dailyResults.push({
|
||||||
|
dateStr,
|
||||||
|
off,
|
||||||
|
half,
|
||||||
|
peak,
|
||||||
|
offcost,
|
||||||
|
halfcost,
|
||||||
|
peakcost,
|
||||||
|
dailyEleCost,
|
||||||
|
dailyFlowCost,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
dailyResults,
|
||||||
|
totalEleCost,
|
||||||
|
totalFlowCost,
|
||||||
|
total_Off,
|
||||||
|
total_half,
|
||||||
|
total_peak,
|
||||||
|
total_OffCost,
|
||||||
|
total_halfCost,
|
||||||
|
total_peakCost,
|
||||||
|
};
|
||||||
|
};
|
1
src/utils/index.js
Normal file
1
src/utils/index.js
Normal file
@ -0,0 +1 @@
|
|||||||
|
export * from './calcuEleCost';
|
@ -1,10 +1,237 @@
|
|||||||
<script setup>
|
<script setup>
|
||||||
import { ref, onMounted, watch } from "vue";
|
import { ref, onMounted, onUnmounted, watch } from "vue";
|
||||||
import DashboardStat from "@/components/dashboard/dashboardStat.vue";
|
import DashboardStat from "@/components/dashboard/dashboardStat.vue";
|
||||||
import DashboardBuild from "@/components/dashboard/dashboardBuild.vue";
|
import DashboardBuild from "@/components/dashboard/dashboardBuild.vue";
|
||||||
import DashboardElecChart from "@/components/dashboard/dashboardElecChart.vue";
|
import DashboardElecChart from "@/components/dashboard/dashboardElecChart.vue";
|
||||||
import DashboardTag from "@/components/dashboard/dashboardTag.vue";
|
import DashboardTag from "@/components/dashboard/dashboardTag.vue";
|
||||||
import DashboardAlert from "@/components/dashboard/dashboardAlert.vue";
|
import DashboardAlert from "@/components/dashboard/dashboardAlert.vue";
|
||||||
|
import useElecTotalMeterStore from "@/stores/useElecTotalMeterStore";
|
||||||
|
import useElecDemandStore from "@/stores/useElecDemandStore";
|
||||||
|
import dayjs from "dayjs";
|
||||||
|
|
||||||
|
const elecDataStore = useElecTotalMeterStore();
|
||||||
|
const elecDemandStore = useElecDemandStore();
|
||||||
|
const elecStat = ref([
|
||||||
|
{
|
||||||
|
value: 0,
|
||||||
|
label: "今日用電量",
|
||||||
|
unit: "kWH",
|
||||||
|
icon: "leaf",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: 0,
|
||||||
|
label: "昨日用電量",
|
||||||
|
unit: "kWH",
|
||||||
|
icon: "leaf",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: 0,
|
||||||
|
label: "即時功率",
|
||||||
|
unit: "kW",
|
||||||
|
icon: "bolt",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: 0,
|
||||||
|
label: "容積占比",
|
||||||
|
unit: "%",
|
||||||
|
icon: "charging-station",
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
const elecDemand_P = ref(0); // 即時趨勢
|
||||||
|
const elecDemand_Engel = ref(0); // 契約容量
|
||||||
|
const yesterdayTodayData = ref({
|
||||||
|
categories: [],
|
||||||
|
values: [
|
||||||
|
{
|
||||||
|
name: `${dayjs().format("YYYY-MM-DD")} 用電量`,
|
||||||
|
value: [],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: `${dayjs().subtract(1, "day").format("YYYY-MM-DD")} 用電量`,
|
||||||
|
value: [],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
const weekComparisonData = ref({
|
||||||
|
categories: [],
|
||||||
|
values: [
|
||||||
|
{
|
||||||
|
name: "本週用電量",
|
||||||
|
value: [0, 0, 0, 0, 0, 0, 0],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "上週用電量",
|
||||||
|
value: [0, 0, 0, 0, 0, 0, 0],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
const generateWeekCategories = () => {
|
||||||
|
const today = dayjs();
|
||||||
|
const currentDay = today.day();
|
||||||
|
const daysOfWeek = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"];
|
||||||
|
const dynamicCategories = [];
|
||||||
|
|
||||||
|
for (let i = 0; i < 7; i++) {
|
||||||
|
const dayIndex = (currentDay - i + 7) % 7;
|
||||||
|
dynamicCategories.unshift(daysOfWeek[dayIndex]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return dynamicCategories;
|
||||||
|
};
|
||||||
|
|
||||||
|
weekComparisonData.value = {
|
||||||
|
...weekComparisonData.value,
|
||||||
|
categories: generateWeekCategories(),
|
||||||
|
};
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => elecDataStore.elecFlowCostSummary,
|
||||||
|
(newElecData) => {
|
||||||
|
console.log("elecStandCostSummary", newElecData);
|
||||||
|
if (newElecData && newElecData.dailyResults) {
|
||||||
|
const today = dayjs().format("YYYY-MM-DD");
|
||||||
|
const yesterday = dayjs().subtract(1, "day").format("YYYY-MM-DD");
|
||||||
|
|
||||||
|
const todayData = newElecData.dailyResults.find(
|
||||||
|
(item) => item.dateStr === today
|
||||||
|
);
|
||||||
|
const yesterdayData = newElecData.dailyResults.find(
|
||||||
|
(item) => item.dateStr === yesterday
|
||||||
|
);
|
||||||
|
|
||||||
|
const todayElecCost = todayData ? todayData.dailyEleCost : 0;
|
||||||
|
const yesterdayElecCost = yesterdayData ? yesterdayData.dailyEleCost : 0;
|
||||||
|
|
||||||
|
elecStat.value = [
|
||||||
|
{
|
||||||
|
...elecStat.value[0],
|
||||||
|
value: todayElecCost,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
...elecStat.value[1],
|
||||||
|
value: yesterdayElecCost,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
...elecStat.value[2],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
...elecStat.value[3],
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
const thisWeekData = newElecData.dailyResults
|
||||||
|
.slice(-7)
|
||||||
|
.map((item) => item.dailyEleCost || 0);
|
||||||
|
const lastWeekData = newElecData.dailyResults
|
||||||
|
.slice(-14, -7)
|
||||||
|
.map((item) => item.dailyEleCost || 0);
|
||||||
|
|
||||||
|
weekComparisonData.value = {
|
||||||
|
...weekComparisonData.value,
|
||||||
|
values: [
|
||||||
|
{
|
||||||
|
name: "本週用電量",
|
||||||
|
value: thisWeekData,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "上週用電量",
|
||||||
|
value: lastWeekData,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{ deep: true }
|
||||||
|
);
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => [elecDataStore.todayelecdata, elecDataStore.yesterdayelecdata],
|
||||||
|
([newTodayData, newYesterdayData]) => {
|
||||||
|
console.log("todayyesterday", newTodayData, newYesterdayData);
|
||||||
|
|
||||||
|
const todayDate = dayjs().format("YYYY-MM-DD");
|
||||||
|
const yesterdayDate = dayjs().subtract(1, "day").format("YYYY-MM-DD");
|
||||||
|
|
||||||
|
const categories = [];
|
||||||
|
const todayValues = [];
|
||||||
|
|
||||||
|
for (const [timestamp, value] of newTodayData) {
|
||||||
|
const hour = dayjs(timestamp).format("HH:00");
|
||||||
|
categories.push(hour);
|
||||||
|
todayValues.push(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
categories.sort();
|
||||||
|
|
||||||
|
const filledYesterdayValues = categories.map((hour) => {
|
||||||
|
const timestamp = dayjs(
|
||||||
|
`${yesterdayDate} ${hour}`,
|
||||||
|
"YYYY-MM-DD HH:00"
|
||||||
|
).format("YYYY-MM-DDTHH:mm:ss.000+08:00");
|
||||||
|
return newYesterdayData.get(timestamp) || 0;
|
||||||
|
});
|
||||||
|
|
||||||
|
console.log("todayValues", todayValues, filledYesterdayValues);
|
||||||
|
yesterdayTodayData.value = {
|
||||||
|
categories: categories,
|
||||||
|
values: [
|
||||||
|
{
|
||||||
|
name: `${todayDate} 用電量`,
|
||||||
|
value: todayValues,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: `${yesterdayDate} 用電量`,
|
||||||
|
value: filledYesterdayValues,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
},
|
||||||
|
{ deep: true }
|
||||||
|
);
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => elecDemandStore.elecData,
|
||||||
|
(newElecData) => {
|
||||||
|
console.log("elecDemandStore.elecData updated:", newElecData);
|
||||||
|
|
||||||
|
const newElecDemand_P =
|
||||||
|
newElecData.find((item) => item.name === "P")?.out || 0;
|
||||||
|
const newElecDemand_Engel =
|
||||||
|
newElecData.find((item) => item.name === "Engel")?.out || 0;
|
||||||
|
|
||||||
|
elecDemand_P.value = newElecDemand_P;
|
||||||
|
elecDemand_Engel.value = newElecDemand_Engel;
|
||||||
|
|
||||||
|
elecStat.value = [
|
||||||
|
...elecStat.value.slice(0, 2),
|
||||||
|
{
|
||||||
|
...elecStat.value[2],
|
||||||
|
value: newElecDemand_P,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
...elecStat.value[3],
|
||||||
|
value: newElecDemand_Engel
|
||||||
|
? ((newElecDemand_P / newElecDemand_Engel) * 100).toFixed(2)
|
||||||
|
: 0,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
},
|
||||||
|
{ deep: true }
|
||||||
|
);
|
||||||
|
|
||||||
|
onMounted(async () => {
|
||||||
|
await elecDataStore.getElecDataFromBaja();
|
||||||
|
elecDataStore.startTimer();
|
||||||
|
|
||||||
|
await elecDemandStore.getElecDemandFromBaja();
|
||||||
|
});
|
||||||
|
|
||||||
|
onUnmounted(() => {
|
||||||
|
elecDataStore.stopTimer();
|
||||||
|
elecDemandStore.clearAllSubscriber();
|
||||||
|
});
|
||||||
</script>
|
</script>
|
||||||
<template>
|
<template>
|
||||||
<a-row :gutter="24" class="px-5 py-2">
|
<a-row :gutter="24" class="px-5 py-2">
|
||||||
@ -14,9 +241,12 @@ import DashboardAlert from "@/components/dashboard/dashboardAlert.vue";
|
|||||||
</a-col>
|
</a-col>
|
||||||
<a-col :span="16" class="">
|
<a-col :span="16" class="">
|
||||||
<!-- 用電數據 -->
|
<!-- 用電數據 -->
|
||||||
<DashboardStat />
|
<DashboardStat :elecData="elecStat" />
|
||||||
<!-- 用電圖表 -->
|
<!-- 用電圖表 -->
|
||||||
<DashboardElecChart />
|
<DashboardElecChart
|
||||||
|
:yesterdayTodayData="yesterdayTodayData"
|
||||||
|
:weekComparisonData="weekComparisonData"
|
||||||
|
/>
|
||||||
</a-col>
|
</a-col>
|
||||||
</a-row>
|
</a-row>
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user