能源報表初版
This commit is contained in:
parent
59bf0fa00b
commit
756cbd3b7a
5
components.d.ts
vendored
5
components.d.ts
vendored
@ -13,8 +13,12 @@ declare module 'vue' {
|
|||||||
ElCard: typeof import('element-plus/es')['ElCard']
|
ElCard: typeof import('element-plus/es')['ElCard']
|
||||||
ElCol: typeof import('element-plus/es')['ElCol']
|
ElCol: typeof import('element-plus/es')['ElCol']
|
||||||
ElContainer: typeof import('element-plus/es')['ElContainer']
|
ElContainer: typeof import('element-plus/es')['ElContainer']
|
||||||
|
ElDatePicker: typeof import('element-plus/es')['ElDatePicker']
|
||||||
ElDescriptions: typeof import('element-plus/es')['ElDescriptions']
|
ElDescriptions: typeof import('element-plus/es')['ElDescriptions']
|
||||||
ElDescriptionsItem: typeof import('element-plus/es')['ElDescriptionsItem']
|
ElDescriptionsItem: typeof import('element-plus/es')['ElDescriptionsItem']
|
||||||
|
ElDialog: typeof import('element-plus/es')['ElDialog']
|
||||||
|
ElForm: typeof import('element-plus/es')['ElForm']
|
||||||
|
ElFormItem: typeof import('element-plus/es')['ElFormItem']
|
||||||
ElHeader: typeof import('element-plus/es')['ElHeader']
|
ElHeader: typeof import('element-plus/es')['ElHeader']
|
||||||
ElInput: typeof import('element-plus/es')['ElInput']
|
ElInput: typeof import('element-plus/es')['ElInput']
|
||||||
ElMain: typeof import('element-plus/es')['ElMain']
|
ElMain: typeof import('element-plus/es')['ElMain']
|
||||||
@ -24,6 +28,7 @@ declare module 'vue' {
|
|||||||
ElStatistic: typeof import('element-plus/es')['ElStatistic']
|
ElStatistic: typeof import('element-plus/es')['ElStatistic']
|
||||||
EnergyBar: typeof import('./src/components/EnergyBar.vue')['default']
|
EnergyBar: typeof import('./src/components/EnergyBar.vue')['default']
|
||||||
EnergyLine: typeof import('./src/components/EnergyLine.vue')['default']
|
EnergyLine: typeof import('./src/components/EnergyLine.vue')['default']
|
||||||
|
EnergyModal: typeof import('./src/components/EnergyModal.vue')['default']
|
||||||
EnergyPie: typeof import('./src/components/EnergyPie.vue')['default']
|
EnergyPie: typeof import('./src/components/EnergyPie.vue')['default']
|
||||||
EnergySankey: typeof import('./src/components/EnergySankey.vue')['default']
|
EnergySankey: typeof import('./src/components/EnergySankey.vue')['default']
|
||||||
Navbar: typeof import('./src/components/Navbar.vue')['default']
|
Navbar: typeof import('./src/components/Navbar.vue')['default']
|
||||||
|
25
src/components/EnergyModal.vue
Normal file
25
src/components/EnergyModal.vue
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
<script setup>
|
||||||
|
import { defineProps, defineEmits } from "vue";
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
title: {
|
||||||
|
type: String,
|
||||||
|
default: "",
|
||||||
|
},
|
||||||
|
confirm: Function,
|
||||||
|
close: Function,
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<el-dialog :model-value="modelValue" :title="title" width="500">
|
||||||
|
<slot></slot>
|
||||||
|
<!-- 預設 slot -->
|
||||||
|
<template #footer>
|
||||||
|
<div class="dialog-footer">
|
||||||
|
<el-button @click="close()">關閉</el-button>
|
||||||
|
<el-button type="primary" @click="confirm()">確認</el-button>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</el-dialog>
|
||||||
|
</template>
|
@ -1,30 +1,18 @@
|
|||||||
<script setup>
|
<script setup>
|
||||||
import { ref, onMounted, watch, onUnmounted } from "vue";
|
import { ref, onMounted, watch, onUnmounted, defineProps } from "vue";
|
||||||
import * as echarts from "echarts";
|
import * as echarts from "echarts";
|
||||||
|
|
||||||
const chartContainer = ref(null);
|
const chartContainer = ref(null);
|
||||||
let chartInstance = null;
|
let chartInstance = null;
|
||||||
|
|
||||||
const chartData = ref({
|
const props = defineProps({
|
||||||
series: [
|
chartData: {
|
||||||
{
|
type: Object,
|
||||||
name: "電表01",
|
required: true,
|
||||||
value: 2200,
|
},
|
||||||
itemStyle: { color: "#5470c6" },
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "電表02",
|
|
||||||
value: 3600,
|
|
||||||
itemStyle: { color: "#91cc75" },
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "電表03",
|
|
||||||
value: 1600,
|
|
||||||
itemStyle: { color: "#fac858" },
|
|
||||||
},
|
|
||||||
],
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
const initChart = () => {
|
const initChart = () => {
|
||||||
chartInstance = echarts.init(chartContainer.value);
|
chartInstance = echarts.init(chartContainer.value);
|
||||||
|
|
||||||
@ -37,7 +25,7 @@ const initChart = () => {
|
|||||||
orient: "vertical",
|
orient: "vertical",
|
||||||
left: "right",
|
left: "right",
|
||||||
bottom: "0%",
|
bottom: "0%",
|
||||||
data: chartData.value.series.map((item) => item.name), // 添加图例
|
data:props.chartData.series.map((item) => item.name), // 添加图例
|
||||||
},
|
},
|
||||||
series: [
|
series: [
|
||||||
{
|
{
|
||||||
@ -45,7 +33,7 @@ const initChart = () => {
|
|||||||
type: "pie",
|
type: "pie",
|
||||||
radius: "70%", // 饼图大小
|
radius: "70%", // 饼图大小
|
||||||
center: ["50%", "50%"], // 饼图中心位置
|
center: ["50%", "50%"], // 饼图中心位置
|
||||||
data: chartData.value.series,
|
data: props.chartData.series,
|
||||||
emphasis: {
|
emphasis: {
|
||||||
itemStyle: {
|
itemStyle: {
|
||||||
shadowBlur: 10,
|
shadowBlur: 10,
|
||||||
@ -65,7 +53,7 @@ onMounted(() => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
watch(
|
watch(
|
||||||
() => chartData.value,
|
() => props.chartData,
|
||||||
(newChartData) => {
|
(newChartData) => {
|
||||||
if (chartInstance) {
|
if (chartInstance) {
|
||||||
chartInstance.setOption({
|
chartInstance.setOption({
|
||||||
|
@ -1,57 +1,131 @@
|
|||||||
<template>
|
<template>
|
||||||
<el-row :gutter="20">
|
<el-row :gutter="20">
|
||||||
<el-col :span="12">
|
<el-col :span="7">
|
||||||
<el-descriptions class="margin-top" title="報表資訊" :column="1" border>
|
<el-descriptions class="margin-top" title="報表資訊" :column="1" border>
|
||||||
<el-descriptions-item>
|
<el-descriptions-item>
|
||||||
<template #label>
|
<template #label>
|
||||||
<div class="cell-item">報表名稱</div>
|
<div class="cell-item">報表名稱</div>
|
||||||
</template>
|
</template>
|
||||||
智慧大樓電表月報表
|
{{ props.form?.name }}
|
||||||
</el-descriptions-item>
|
</el-descriptions-item>
|
||||||
<el-descriptions-item>
|
<el-descriptions-item>
|
||||||
<template #label>
|
<template #label>
|
||||||
<div class="cell-item">報表期間</div>
|
<div class="cell-item">報表期間</div>
|
||||||
</template>
|
</template>
|
||||||
2025/02/01 ~ 2025/02/29
|
{{ dayjs(props.form?.date).format("YYYY/MM") }}
|
||||||
</el-descriptions-item>
|
</el-descriptions-item>
|
||||||
<el-descriptions-item>
|
<el-descriptions-item>
|
||||||
<template #label>
|
<template #label>
|
||||||
<div class="cell-item">編製日期</div>
|
<div class="cell-item">編製日期</div>
|
||||||
</template>
|
</template>
|
||||||
2025/3/1
|
{{ dayjs().format("YYYY/MM/DD") }}
|
||||||
</el-descriptions-item>
|
</el-descriptions-item>
|
||||||
<el-descriptions-item>
|
<el-descriptions-item>
|
||||||
<template #label>
|
<template #label>
|
||||||
<div class="cell-item">編製人員</div>
|
<div class="cell-item">編製人員</div>
|
||||||
</template>
|
</template>
|
||||||
能源管理部
|
{{ props.form?.staff }}
|
||||||
</el-descriptions-item>
|
</el-descriptions-item>
|
||||||
</el-descriptions>
|
</el-descriptions>
|
||||||
</el-col>
|
</el-col>
|
||||||
<el-col :span="12">
|
<el-col :span="11">
|
||||||
<el-descriptions
|
<el-descriptions
|
||||||
class="margin-top"
|
class="margin-top"
|
||||||
title="單價(NTD/kWh)"
|
:title="priceTitle"
|
||||||
:column="1"
|
:column="2"
|
||||||
border
|
border
|
||||||
>
|
>
|
||||||
<el-descriptions-item>
|
<el-descriptions-item>
|
||||||
<template #label>
|
<template #label>
|
||||||
<div class="cell-item">尖峰單價</div>
|
<div class="cell-item">基本電費-三相</div>
|
||||||
</template>
|
</template>
|
||||||
6
|
{{ Three_Phase }}
|
||||||
|
</el-descriptions-item>
|
||||||
|
<el-descriptions-item v-if="isSummerMonth">
|
||||||
|
<template #label>
|
||||||
|
<div class="cell-item">基本電費-經常契約</div>
|
||||||
|
</template>
|
||||||
|
{{ Regular_Contract_Summer }}
|
||||||
|
</el-descriptions-item>
|
||||||
|
<el-descriptions-item v-if="!isSummerMonth">
|
||||||
|
<template #label>
|
||||||
|
<div class="cell-item">基本電費-經常契約</div>
|
||||||
|
</template>
|
||||||
|
{{ Regular_Contract_Non_Summer }}
|
||||||
|
</el-descriptions-item>
|
||||||
|
<el-descriptions-item v-if="isSummerMonth">
|
||||||
|
<template #label>
|
||||||
|
<div class="cell-item">流動電價-平日尖峰單價</div>
|
||||||
|
</template>
|
||||||
|
{{ Summer_Peak_Prices }}
|
||||||
</el-descriptions-item>
|
</el-descriptions-item>
|
||||||
<el-descriptions-item>
|
<el-descriptions-item>
|
||||||
<template #label>
|
<template #label>
|
||||||
<div class="cell-item">半尖峰單價</div>
|
<div class="cell-item">流動電價-平日半尖峰單價</div>
|
||||||
</template>
|
</template>
|
||||||
4
|
{{
|
||||||
|
isSummerMonth
|
||||||
|
? Summer_HalfPeak_Prices_Weekday
|
||||||
|
: Non_Summer_HalfPeak_Prices_Weekday
|
||||||
|
}}
|
||||||
</el-descriptions-item>
|
</el-descriptions-item>
|
||||||
<el-descriptions-item>
|
<el-descriptions-item>
|
||||||
<template #label>
|
<template #label>
|
||||||
<div class="cell-item">離峰單價</div>
|
<div class="cell-item">流動電價-平日離峰單價</div>
|
||||||
</template>
|
</template>
|
||||||
2
|
{{
|
||||||
|
isSummerMonth
|
||||||
|
? Summer_Off_Prices_Weekday
|
||||||
|
: Non_Summer_Off_Prices_Weekday
|
||||||
|
}}
|
||||||
|
</el-descriptions-item>
|
||||||
|
<el-descriptions-item>
|
||||||
|
<template #label>
|
||||||
|
<div class="cell-item">流動電價-週六半尖峰單價</div>
|
||||||
|
</template>
|
||||||
|
{{
|
||||||
|
isSummerMonth
|
||||||
|
? Summer_HalfPeak_Prices_Saturday
|
||||||
|
: Non_Summer_HalfPeak_Prices_Saturday
|
||||||
|
}}
|
||||||
|
</el-descriptions-item>
|
||||||
|
<el-descriptions-item>
|
||||||
|
<template #label>
|
||||||
|
<div class="cell-item">流動電價-週六離峰單價</div>
|
||||||
|
</template>
|
||||||
|
{{
|
||||||
|
isSummerMonth
|
||||||
|
? Summer_Off_Prices_Saturday
|
||||||
|
: Non_Summer_Off_Prices_Saturday
|
||||||
|
}}
|
||||||
|
</el-descriptions-item>
|
||||||
|
<el-descriptions-item>
|
||||||
|
<template #label>
|
||||||
|
<div class="cell-item">流動電價-週日離峰單價</div>
|
||||||
|
</template>
|
||||||
|
{{ isSummerMonth ? Summer_Off_Prices : Non_Summer_Off_Prices }}
|
||||||
|
</el-descriptions-item>
|
||||||
|
</el-descriptions>
|
||||||
|
</el-col>
|
||||||
|
<el-col :span="6">
|
||||||
|
<el-descriptions class="margin-top" title="總計" :column="1" border>
|
||||||
|
<el-descriptions-item>
|
||||||
|
<template #label>
|
||||||
|
<div class="cell-item">基本電費</div>
|
||||||
|
</template>
|
||||||
|
{{ totalStandCost.toFixed(2) }}
|
||||||
|
</el-descriptions-item>
|
||||||
|
<el-descriptions-item>
|
||||||
|
<template #label>
|
||||||
|
<div class="cell-item">流動電費</div>
|
||||||
|
</template>
|
||||||
|
{{ totalFlowCost.toFixed(2) }}
|
||||||
|
</el-descriptions-item>
|
||||||
|
<el-descriptions-item>
|
||||||
|
<template #label>
|
||||||
|
<div class="cell-item">總費用</div>
|
||||||
|
</template>
|
||||||
|
{{ totalCost.toFixed(2) }}
|
||||||
</el-descriptions-item>
|
</el-descriptions-item>
|
||||||
</el-descriptions>
|
</el-descriptions>
|
||||||
</el-col>
|
</el-col>
|
||||||
@ -79,54 +153,27 @@
|
|||||||
<th class="bg-yellow">尖峰費用</th>
|
<th class="bg-yellow">尖峰費用</th>
|
||||||
<th class="bg-yellow">半尖峰費用</th>
|
<th class="bg-yellow">半尖峰費用</th>
|
||||||
<th class="bg-yellow">離峰費用</th>
|
<th class="bg-yellow">離峰費用</th>
|
||||||
<th class="bg-yellow">總費用</th>
|
<th class="bg-yellow">小計</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
<tr>
|
<tr
|
||||||
<td>電表01</td>
|
v-for="meterData in formattedElecCostSummary"
|
||||||
<td>辦公室南區</td>
|
:key="meterData.name"
|
||||||
<td>549</td>
|
>
|
||||||
<td>51</td>
|
<td>{{ meterData.name }}</td>
|
||||||
<td>9.29</td>
|
<td>{{ meterData.area }}</td>
|
||||||
<td>600</td>
|
<td>{{ meterData.lastMonthUsage.toFixed(2) }}</td>
|
||||||
<td>150</td>
|
<td>{{ meterData.usageChange.toFixed(2) }}</td>
|
||||||
<td>200</td>
|
<td>{{ meterData.usageChangeRate.toFixed(2) }}</td>
|
||||||
<td>250</td>
|
<td>{{ meterData.totalUsage.toFixed(2) }}</td>
|
||||||
<td>900</td>
|
<td>{{ meterData.peakUsage.toFixed(2) }}</td>
|
||||||
<td>800</td>
|
<td>{{ meterData.halfPeakUsage.toFixed(2) }}</td>
|
||||||
<td>500</td>
|
<td>{{ meterData.offPeakUsage.toFixed(2) }}</td>
|
||||||
<td>2200</td>
|
<td>{{ meterData.peakCost.toFixed(2) }}</td>
|
||||||
</tr>
|
<td>{{ meterData.halfPeakCost.toFixed(2) }}</td>
|
||||||
<tr>
|
<td>{{ meterData.offPeakCost.toFixed(2) }}</td>
|
||||||
<td>電表02</td>
|
<td>{{ meterData.totalCost.toFixed(2) }}</td>
|
||||||
<td>電梯機房</td>
|
|
||||||
<td>969</td>
|
|
||||||
<td>-69</td>
|
|
||||||
<td>-7.12</td>
|
|
||||||
<td>900</td>
|
|
||||||
<td>300</td>
|
|
||||||
<td>300</td>
|
|
||||||
<td>300</td>
|
|
||||||
<td>1800</td>
|
|
||||||
<td>1200</td>
|
|
||||||
<td>600</td>
|
|
||||||
<td>3600</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td>電表03</td>
|
|
||||||
<td>會議室</td>
|
|
||||||
<td>489</td>
|
|
||||||
<td>-39</td>
|
|
||||||
<td>-7.98</td>
|
|
||||||
<td>450</td>
|
|
||||||
<td>100</td>
|
|
||||||
<td>150</td>
|
|
||||||
<td>200</td>
|
|
||||||
<td>600</td>
|
|
||||||
<td>600</td>
|
|
||||||
<td>400</td>
|
|
||||||
<td>1600</td>
|
|
||||||
</tr>
|
</tr>
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
@ -135,46 +182,222 @@
|
|||||||
<el-row :gutter="20">
|
<el-row :gutter="20">
|
||||||
<el-col :span="12">
|
<el-col :span="12">
|
||||||
<el-card style="border-radius: 8px; height: 100%">
|
<el-card style="border-radius: 8px; height: 100%">
|
||||||
<h3 class="">各電表總費用佔比</h3>
|
<h3 class="">各電表用電量比較(kWh)</h3>
|
||||||
<EnergyPie />
|
<EnergyBar
|
||||||
|
:chartData="elecUsageData"
|
||||||
|
/>
|
||||||
</el-card>
|
</el-card>
|
||||||
</el-col>
|
</el-col>
|
||||||
<el-col :span="12">
|
<el-col :span="12">
|
||||||
<el-card style="border-radius: 8px; height: 100%">
|
<el-card style="border-radius: 8px; height: 100%">
|
||||||
<h3 class="">各電表用電量比較(kWh)</h3>
|
<h3 class="">各電表總費用佔比</h3>
|
||||||
<EnergyBar :chartData="elecUsageData" />
|
<EnergyPie
|
||||||
|
:chartData="elecCostData"
|
||||||
|
/>
|
||||||
</el-card>
|
</el-card>
|
||||||
</el-col>
|
</el-col>
|
||||||
</el-row>
|
</el-row>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { ref } from "vue";
|
import { defineProps, computed } from "vue";
|
||||||
import EnergyPie from "../components/EnergyPie.vue";
|
import EnergyPie from "../components/EnergyPie.vue";
|
||||||
import EnergyBar from "../components/EnergyBar.vue";
|
import EnergyBar from "../components/EnergyBar.vue";
|
||||||
|
import useElecPriceStore from "../stores/useElecPriceStore";
|
||||||
|
import useElecReportStore from "../stores/useElecReportStore";
|
||||||
|
import getRandomColor from "../utils/getRandomColor";
|
||||||
|
import dayjs from "dayjs";
|
||||||
|
|
||||||
const elecUsageData = ref({
|
const props = defineProps({
|
||||||
categories: ["電表01", "電表02", "電表03"],
|
form: {
|
||||||
series: [
|
type: Object,
|
||||||
{
|
},
|
||||||
name: "尖峰用電",
|
});
|
||||||
type: "bar",
|
const storeElecPrice = useElecPriceStore();
|
||||||
data: [150, 300, 100],
|
const storeElecReport = useElecReportStore();
|
||||||
itemStyle: { color: "#5470c6" },
|
const elecPrices = storeElecPrice.elecData;
|
||||||
},
|
|
||||||
{
|
const isSummerMonth = computed(() => {
|
||||||
name: "半尖峰用電",
|
const month = dayjs(props.form?.date).month(); // month() 返回 0-11,代表一月到十二月
|
||||||
type: "bar",
|
return month >= 5 && month <= 8; // 月份範圍,6月是 5,9月是 8
|
||||||
data: [200, 300, 150],
|
});
|
||||||
itemStyle: { color: "#91cc75" },
|
|
||||||
},
|
const priceTitle = computed(() => {
|
||||||
{
|
return isSummerMonth.value ? "單價(NTD/kWh)-夏月" : "單價(NTD/kWh)-非夏月";
|
||||||
name: "離峰用電",
|
});
|
||||||
type: "bar",
|
|
||||||
data: [250, 300, 200],
|
const formattedElecCostSummary = computed(() => {
|
||||||
itemStyle: { color: "#fac858" },
|
const summaryThisMonth = storeElecReport.elecCostSummary.thisMonth;
|
||||||
},
|
const summaryLastMonth = storeElecReport.elecCostSummary.lastMonth;
|
||||||
],
|
|
||||||
|
if (!summaryThisMonth) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
return Object.keys(summaryThisMonth).map((key) => {
|
||||||
|
const meterDataThisMonth = summaryThisMonth[key];
|
||||||
|
const meterDataLastMonth = summaryLastMonth?.[key]; // 使用可选链
|
||||||
|
|
||||||
|
const totalUsageThisMonth = meterDataThisMonth?.totalEleCost || 0;
|
||||||
|
const totalUsageLastMonth = meterDataLastMonth?.totalEleCost || 0;
|
||||||
|
|
||||||
|
const usageChange = totalUsageThisMonth - totalUsageLastMonth;
|
||||||
|
const usageChangeRate =
|
||||||
|
totalUsageLastMonth !== 0 ? (usageChange / totalUsageLastMonth) * 100 : 0; // 避免除以 0
|
||||||
|
|
||||||
|
return {
|
||||||
|
name: meterDataThisMonth?.name,
|
||||||
|
area: meterDataThisMonth?.area,
|
||||||
|
lastMonthUsage: totalUsageLastMonth,
|
||||||
|
usageChange: usageChange,
|
||||||
|
usageChangeRate: usageChangeRate,
|
||||||
|
totalUsage: totalUsageThisMonth,
|
||||||
|
peakUsage: meterDataThisMonth?.total_peak || 0,
|
||||||
|
halfPeakUsage: meterDataThisMonth?.total_half || 0,
|
||||||
|
offPeakUsage: meterDataThisMonth?.total_Off || 0,
|
||||||
|
peakCost: meterDataThisMonth?.total_peakCost || 0,
|
||||||
|
halfPeakCost: meterDataThisMonth?.total_halfCost || 0,
|
||||||
|
offPeakCost: meterDataThisMonth?.total_OffCost || 0,
|
||||||
|
totalCost: meterDataThisMonth?.totalFlowCost || 0,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// 基本電費
|
||||||
|
const totalStandCost = computed(() => {
|
||||||
|
if (!storeElecReport.elecCostSummary.thisMonth) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
const elecCostSummaryArray = Object.values(
|
||||||
|
storeElecReport.elecCostSummary.thisMonth
|
||||||
|
);
|
||||||
|
if (elecCostSummaryArray.length === 0) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return elecCostSummaryArray[0]?.standCost || 0;
|
||||||
|
});
|
||||||
|
|
||||||
|
const totalFlowCost = computed(() => {
|
||||||
|
if (!storeElecReport.elecCostSummary.thisMonth) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
let total = 0;
|
||||||
|
Object.keys(storeElecReport.elecCostSummary.thisMonth).forEach((key) => {
|
||||||
|
// @ts-ignore
|
||||||
|
total += storeElecReport.elecCostSummary.thisMonth[key]?.totalFlowCost || 0;
|
||||||
|
});
|
||||||
|
return total;
|
||||||
|
});
|
||||||
|
|
||||||
|
const totalCost = computed(() => {
|
||||||
|
return totalStandCost.value + totalFlowCost.value;
|
||||||
|
});
|
||||||
|
|
||||||
|
const Three_Phase =
|
||||||
|
elecPrices.find((item: any) => item.displayName === "基本按戶三相")?.out || 0;
|
||||||
|
|
||||||
|
// 夏月
|
||||||
|
const Regular_Contract_Summer =
|
||||||
|
elecPrices.find((item: any) => item.displayName === "基本經常夏月")?.out || 0;
|
||||||
|
const Summer_Peak_Prices =
|
||||||
|
elecPrices.find((item: any) => item.displayName === "流動平日尖峰夏月")
|
||||||
|
?.out || 0;
|
||||||
|
const Summer_HalfPeak_Prices_Weekday =
|
||||||
|
elecPrices.find((item: any) => item.displayName === "流動平日半尖峰夏月")
|
||||||
|
?.out || 0;
|
||||||
|
const Summer_Off_Prices_Weekday =
|
||||||
|
elecPrices.find((item: any) => item.displayName === "流動平日離峰夏月")
|
||||||
|
?.out || 0;
|
||||||
|
const Summer_HalfPeak_Prices_Saturday =
|
||||||
|
elecPrices.find((item: any) => item.displayName === "流動週六半尖峰夏月")
|
||||||
|
?.out || 0;
|
||||||
|
const Summer_Off_Prices_Saturday =
|
||||||
|
elecPrices.find((item: any) => item.displayName === "流動週六離峰夏月")
|
||||||
|
?.out || 0;
|
||||||
|
const Summer_Off_Prices =
|
||||||
|
elecPrices.find((item: any) => item.displayName === "流動週日離峰夏月")
|
||||||
|
?.out || 0;
|
||||||
|
// 非夏月
|
||||||
|
const Regular_Contract_Non_Summer =
|
||||||
|
elecPrices.find((item: any) => item.displayName === "基本經常非夏月")?.out ||
|
||||||
|
0;
|
||||||
|
const Non_Summer_HalfPeak_Prices_Weekday =
|
||||||
|
elecPrices.find((item: any) => item.displayName === "流動平日半尖峰非夏月")
|
||||||
|
?.out || 0;
|
||||||
|
const Non_Summer_Off_Prices_Weekday =
|
||||||
|
elecPrices.find((item: any) => item.displayName === "流動平日離峰非夏月")
|
||||||
|
?.out || 0;
|
||||||
|
const Non_Summer_HalfPeak_Prices_Saturday =
|
||||||
|
elecPrices.find((item: any) => item.displayName === "流動週六半尖峰非夏月")
|
||||||
|
?.out || 0;
|
||||||
|
const Non_Summer_Off_Prices_Saturday =
|
||||||
|
elecPrices.find((item: any) => item.displayName === "流動週六離峰非夏月")
|
||||||
|
?.out || 0;
|
||||||
|
const Non_Summer_Off_Prices =
|
||||||
|
elecPrices.find((item: any) => item.displayName === "流動週日離峰非夏月")
|
||||||
|
?.out || 0;
|
||||||
|
|
||||||
|
const elecUsageData = computed(() => {
|
||||||
|
const summaryThisMonth = storeElecReport.elecCostSummary.thisMonth;
|
||||||
|
if (!summaryThisMonth) {
|
||||||
|
return {
|
||||||
|
categories: [],
|
||||||
|
series: [],
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
const categories = Object.keys(summaryThisMonth);
|
||||||
|
const peakData = categories.map(
|
||||||
|
(key) => summaryThisMonth[key]?.total_peak || 0
|
||||||
|
);
|
||||||
|
const halfPeakData = categories.map(
|
||||||
|
(key) => summaryThisMonth[key]?.total_half || 0
|
||||||
|
);
|
||||||
|
const offPeakData = categories.map(
|
||||||
|
(key) => summaryThisMonth[key]?.total_Off || 0
|
||||||
|
);
|
||||||
|
|
||||||
|
return {
|
||||||
|
categories: categories.map((key) => summaryThisMonth[key]?.name || key),
|
||||||
|
series: [
|
||||||
|
{
|
||||||
|
name: "尖峰用電",
|
||||||
|
type: "bar",
|
||||||
|
data: peakData,
|
||||||
|
itemStyle: { color: "#5470c6" },
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "半尖峰用電",
|
||||||
|
type: "bar",
|
||||||
|
data: halfPeakData,
|
||||||
|
itemStyle: { color: "#91cc75" },
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "離峰用電",
|
||||||
|
type: "bar",
|
||||||
|
data: offPeakData,
|
||||||
|
itemStyle: { color: "#fac858" },
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
});
|
||||||
|
const elecCostData = computed(() => {
|
||||||
|
const summaryThisMonth = storeElecReport.elecCostSummary.thisMonth;
|
||||||
|
if (!summaryThisMonth) {
|
||||||
|
return { series: [] };
|
||||||
|
}
|
||||||
|
|
||||||
|
getRandomColor.resetIndex();
|
||||||
|
const series = Object.keys(summaryThisMonth).map((key) => {
|
||||||
|
const meterData = summaryThisMonth[key];
|
||||||
|
return {
|
||||||
|
name: meterData?.name || key,
|
||||||
|
value: meterData?.totalFlowCost.toFixed(2) || 0,
|
||||||
|
itemStyle: { color: getRandomColor() },
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
return { series };
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
import { ref } from "vue";
|
import { ref } from "vue";
|
||||||
import { defineStore } from "pinia";
|
import { defineStore } from "pinia";
|
||||||
import type { NiagaraElecData } from "../utils/types";
|
import type { NiagaraElecData } from "../utils/types";
|
||||||
|
import axios from "axios";
|
||||||
|
|
||||||
const useElecPriceStore = defineStore("elecPriceData", () => {
|
const useElecPriceStore = defineStore("elecPriceData", () => {
|
||||||
const elecData = ref<NiagaraElecData[]>([]);
|
const elecData = ref<NiagaraElecData[]>([]);
|
||||||
@ -101,6 +102,30 @@ const useElecPriceStore = defineStore("elecPriceData", () => {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const updatePrice = async (slotPath: string, out: number) => {
|
||||||
|
const domain = window.location.origin;
|
||||||
|
try {
|
||||||
|
console.log("updatePrice",`${domain}/obix/config/${slotPath}/in2/value`)
|
||||||
|
const res = await axios.put(
|
||||||
|
`${domain}/obix/config/${slotPath}/in2/value`,
|
||||||
|
`<real name="in" val="${out}"/> `,
|
||||||
|
{
|
||||||
|
headers: { "Content-Type": "text/xml" },
|
||||||
|
}
|
||||||
|
);
|
||||||
|
if (res.status === 200) {
|
||||||
|
console.log(`成功更新 ${slotPath} 為 ${out}`);
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
console.error(`更新 ${slotPath} 失敗,狀態碼: ${res.status}`);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error(`更新 ${slotPath} 出錯:`, error);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
const clearAllSubscriber = () => {
|
const clearAllSubscriber = () => {
|
||||||
subscribers.value.forEach((subscriber) => {
|
subscribers.value.forEach((subscriber) => {
|
||||||
subscriber.detach("changed");
|
subscriber.detach("changed");
|
||||||
@ -112,6 +137,7 @@ const useElecPriceStore = defineStore("elecPriceData", () => {
|
|||||||
|
|
||||||
return {
|
return {
|
||||||
getElecDataFromBaja,
|
getElecDataFromBaja,
|
||||||
|
updatePrice,
|
||||||
clearAllSubscriber,
|
clearAllSubscriber,
|
||||||
elecData,
|
elecData,
|
||||||
};
|
};
|
||||||
|
202
src/stores/useElecReportStore.ts
Normal file
202
src/stores/useElecReportStore.ts
Normal file
@ -0,0 +1,202 @@
|
|||||||
|
import { ref } from "vue";
|
||||||
|
import { defineStore } from "pinia";
|
||||||
|
import dayjs from "dayjs";
|
||||||
|
import type { ElecCostSummaryMap } from "../utils/types";
|
||||||
|
import { CalcuEleCost, CalcuEleStandCost } from "../utils/CalcuEleCost";
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
const useElecReportStore = defineStore("elecReportData", () => {
|
||||||
|
const elecData = ref<any[]>([]);
|
||||||
|
// @ts-ignore
|
||||||
|
let timerId = null;
|
||||||
|
|
||||||
|
// 初始化為空物件,並定義類型
|
||||||
|
const elecCostSummary = ref<ElecCostSummaryMap>({
|
||||||
|
thisMonth: {},
|
||||||
|
lastMonth: {},
|
||||||
|
});
|
||||||
|
|
||||||
|
// 儲存每個電表的 dataMap
|
||||||
|
const elecDataMaps = ref<{
|
||||||
|
[id: string]: {
|
||||||
|
thisMonth: Map<string, number>;
|
||||||
|
lastMonth: Map<string, number>;
|
||||||
|
};
|
||||||
|
}>({});
|
||||||
|
|
||||||
|
const startTime = ref("");
|
||||||
|
const endTime = ref("");
|
||||||
|
|
||||||
|
// get data from baja
|
||||||
|
const getElecDataFromBaja = () => {
|
||||||
|
// @ts-ignore
|
||||||
|
window.require &&
|
||||||
|
// @ts-ignore
|
||||||
|
window.requirejs(["baja!"], (baja: any) => {
|
||||||
|
console.log("進入 bajaSubscriber 準備執行用電度數 BQL 訂閱");
|
||||||
|
|
||||||
|
// 定義BQL 查詢
|
||||||
|
const Total_kwhBql = `local:|foxs:4918|station:|neql:EMS:SubSys_kwh|bql:select slotPath,parent.displayName,displayName,NumericInterval.historyConfig.id,out`;
|
||||||
|
|
||||||
|
// 執行各電表的 BQL 查詢
|
||||||
|
fetchElecData(baja, Total_kwhBql);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const fetchElecData = (baja: any, bql: string) => {
|
||||||
|
let eleclist: any[] = [];
|
||||||
|
baja.Ord.make(bql).get({
|
||||||
|
cursor: {
|
||||||
|
before: () => {},
|
||||||
|
each: (record: any) => {
|
||||||
|
eleclist.push({
|
||||||
|
slotPath: record.get("slotPath"),
|
||||||
|
displayName: record.get("parent$2edisplayName"),
|
||||||
|
id: record.get("NumericInterval$2ehistoryConfig$2eid").$cEncStr,
|
||||||
|
area: record.get("out")?.get("value") ?? "",
|
||||||
|
});
|
||||||
|
},
|
||||||
|
after: () => {
|
||||||
|
const validElecList = eleclist.filter(
|
||||||
|
(item) => item.id !== undefined
|
||||||
|
);
|
||||||
|
const menoElecList = eleclist.filter((item) => item.id == undefined);
|
||||||
|
const displayNameToAreaMap = new Map<string, string>();
|
||||||
|
menoElecList.forEach((item) => {
|
||||||
|
displayNameToAreaMap.set(item.displayName, item.area);
|
||||||
|
});
|
||||||
|
validElecList.forEach((item) => {
|
||||||
|
const area = displayNameToAreaMap.get(item.displayName);
|
||||||
|
if (area) {
|
||||||
|
item.area = area; // 將 area 賦值給 validElecList 中的 item
|
||||||
|
}
|
||||||
|
});
|
||||||
|
elecData.value = [];
|
||||||
|
elecData.value.push(...validElecList);
|
||||||
|
validElecList.forEach((item) => {
|
||||||
|
const id = item.id;
|
||||||
|
elecDataMaps.value[id] = {
|
||||||
|
thisMonth: new Map<string, number>(),
|
||||||
|
lastMonth: new Map<string, number>(),
|
||||||
|
};
|
||||||
|
// 初始化每个 slotPath 的值为 undefined 或 null
|
||||||
|
elecCostSummary.value.thisMonth[id] = undefined;
|
||||||
|
elecCostSummary.value.lastMonth[id] = undefined;
|
||||||
|
getTimeToHistory(item, 'thisMonth'); // 獲取本月
|
||||||
|
getTimeToHistory(item, 'lastMonth'); // 獲取上個月
|
||||||
|
});
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const getTimeToHistory = (item: any, month: 'thisMonth' | 'lastMonth') => {
|
||||||
|
const id = item.id;
|
||||||
|
let startTimeValue = startTime.value;
|
||||||
|
let endTimeValue = endTime.value;
|
||||||
|
|
||||||
|
if (month === 'lastMonth') {
|
||||||
|
startTimeValue = dayjs(startTime.value).subtract(1, 'month').startOf('month').format("YYYY-MM-DDTHH:mm:ss.000+08:00");
|
||||||
|
endTimeValue = dayjs(endTime.value).subtract(1, 'month').endOf('month').format("YYYY-MM-DDTHH:mm:ss.000+08:00");
|
||||||
|
}
|
||||||
|
|
||||||
|
const ordString = `local:|foxs:4918|history:${id}?period=timerange;start=${startTimeValue};end=${endTimeValue}|bql:history:HistoryRollup.rollup(baja:RelTime '3600000')`; //每小时一个rollup
|
||||||
|
console.log(ordString);
|
||||||
|
// @ts-ignore
|
||||||
|
window.require &&
|
||||||
|
// @ts-ignore
|
||||||
|
window.requirejs(["baja!"], (baja: any) => {
|
||||||
|
// 使用對應電表的 dataMap
|
||||||
|
const dataMap = elecDataMaps.value[id][month];
|
||||||
|
if (!dataMap) {
|
||||||
|
console.warn(`未找到電表 ${id} (${month}) 的 dataMap`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let lastTimestamp: string | null = null;
|
||||||
|
let lastValue: number | null = null;
|
||||||
|
baja.Ord.make(ordString).get({
|
||||||
|
cursor: {
|
||||||
|
before: () => {
|
||||||
|
console.log(`開始訂閱 ${id} 的歷史資料`);
|
||||||
|
},
|
||||||
|
each: (record: any) => {
|
||||||
|
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: () => {
|
||||||
|
const elecCost = CalcuEleCost(dataMap);
|
||||||
|
const eleceStandCost = CalcuEleStandCost(dataMap);
|
||||||
|
console.log(`⏱️ 電表 ${id} 每小時差值 map:`, dataMap);
|
||||||
|
if (item.slotPath) {
|
||||||
|
// 确保 slotPath 存在
|
||||||
|
elecCostSummary.value[month][id] = {
|
||||||
|
...elecCost,
|
||||||
|
standCost: eleceStandCost.StandCost,
|
||||||
|
name: item.displayName,
|
||||||
|
area: item.area,
|
||||||
|
};
|
||||||
|
console.log(
|
||||||
|
`電表 ${item.displayName} 的電費計算結果 (${month}):`,
|
||||||
|
elecCostSummary.value[month][id]
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
console.warn(
|
||||||
|
`電表 ${id} 的 slotPath 為空,無法儲存電費計算結果 (${month})`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
limit: -1,
|
||||||
|
offset: 0,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
// 定時更新資料的函數
|
||||||
|
const updateHistoryData = () => {
|
||||||
|
console.log("重新獲取歷史資料");
|
||||||
|
if (elecData.value && elecData.value.length > 0) {
|
||||||
|
elecCostSummary.value.thisMonth = {};
|
||||||
|
elecCostSummary.value.lastMonth = {};
|
||||||
|
|
||||||
|
elecData.value.forEach((item) => {
|
||||||
|
const id = item.id;
|
||||||
|
elecDataMaps.value[id] = {
|
||||||
|
thisMonth: new Map<string, number>(),
|
||||||
|
lastMonth: new Map<string, number>(),
|
||||||
|
};
|
||||||
|
getTimeToHistory(item, 'thisMonth');
|
||||||
|
getTimeToHistory(item, 'lastMonth');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return {
|
||||||
|
getElecDataFromBaja,
|
||||||
|
elecData,
|
||||||
|
elecCostSummary,
|
||||||
|
startTime,
|
||||||
|
endTime,
|
||||||
|
updateHistoryData,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
export default useElecReportStore;
|
@ -1,14 +1,19 @@
|
|||||||
import { ref } from "vue";
|
import { ref } from "vue";
|
||||||
import { defineStore } from "pinia";
|
import { defineStore } from "pinia";
|
||||||
import dayjs from "dayjs";
|
import dayjs from "dayjs";
|
||||||
import type { NiagaraElecData, ElecCostSummary } from "../utils/types";
|
import type {
|
||||||
import { CalcuEleCost } from "../utils/CalcuEleCost";
|
NiagaraElecData,
|
||||||
|
ElecCostSummary,
|
||||||
|
ElecStandCostSummary,
|
||||||
|
} from "../utils/types";
|
||||||
|
import { CalcuEleCost, CalcuEleStandCost } from "../utils/CalcuEleCost";
|
||||||
|
|
||||||
const useElecStore = defineStore("elecData", () => {
|
const useElecStore = defineStore("elecData", () => {
|
||||||
const elecData = ref<NiagaraElecData[]>([]);
|
const elecData = ref<NiagaraElecData[]>([]);
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
let timerId = null;
|
let timerId = null;
|
||||||
const elecCostSummary = ref<ElecCostSummary | null>(null);
|
const elecFlowCostSummary = ref<ElecCostSummary | null>(null);
|
||||||
|
const elecStandCostSummary = ref<ElecStandCostSummary | null>(null);
|
||||||
|
|
||||||
// get data from baja
|
// get data from baja
|
||||||
const getElecDataFromBaja = () => {
|
const getElecDataFromBaja = () => {
|
||||||
@ -78,18 +83,27 @@ const useElecStore = defineStore("elecData", () => {
|
|||||||
each: (record: any) => {
|
each: (record: any) => {
|
||||||
const currentValue = record.get("min");
|
const currentValue = record.get("min");
|
||||||
const timestamp = record.get("timestamp").$cEncStr;
|
const timestamp = record.get("timestamp").$cEncStr;
|
||||||
if (currentValue !== null && currentValue !== 0 && timestamp !== null) {
|
if (
|
||||||
if (lastTimestamp !== null && lastValue !== null && lastValue !== 0) {
|
currentValue !== null &&
|
||||||
|
currentValue !== 0 &&
|
||||||
|
timestamp !== null
|
||||||
|
) {
|
||||||
|
if (
|
||||||
|
lastTimestamp !== null &&
|
||||||
|
lastValue !== null &&
|
||||||
|
lastValue !== 0
|
||||||
|
) {
|
||||||
const diff = currentValue - lastValue;
|
const diff = currentValue - lastValue;
|
||||||
dataMap.set(lastTimestamp, diff);
|
dataMap.set(lastTimestamp, diff);
|
||||||
}
|
}
|
||||||
lastValue = currentValue;
|
lastValue = currentValue;
|
||||||
lastTimestamp = timestamp;
|
lastTimestamp = timestamp;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
after: () => {
|
after: () => {
|
||||||
console.log("⏱️ 每小時差值 map:", dataMap);
|
console.log("⏱️ 每小時差值 map:", dataMap);
|
||||||
elecCostSummary.value = CalcuEleCost(dataMap);
|
elecFlowCostSummary.value = CalcuEleCost(dataMap);
|
||||||
|
elecStandCostSummary.value = CalcuEleStandCost(dataMap);
|
||||||
},
|
},
|
||||||
limit: -1,
|
limit: -1,
|
||||||
offset: 0,
|
offset: 0,
|
||||||
@ -129,7 +143,8 @@ const useElecStore = defineStore("elecData", () => {
|
|||||||
startTimer,
|
startTimer,
|
||||||
stopTimer,
|
stopTimer,
|
||||||
elecData,
|
elecData,
|
||||||
elecCostSummary,
|
elecFlowCostSummary,
|
||||||
|
elecStandCostSummary,
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -1,10 +1,18 @@
|
|||||||
import type { DailyResult, ElecCostSummary, DailyEntry } from "../utils/types";
|
import type {
|
||||||
|
DailyResult,
|
||||||
|
ElecCostSummary,
|
||||||
|
DailyEntry,
|
||||||
|
StandEntry,
|
||||||
|
ElecStandCostSummary,
|
||||||
|
StandResult,
|
||||||
|
} from "../utils/types";
|
||||||
import useElecPriceStore from "../stores/useElecPriceStore";
|
import useElecPriceStore from "../stores/useElecPriceStore";
|
||||||
const storeElecPrice = useElecPriceStore();
|
import useElecDemandStore from "../stores/useElecDemandStore";
|
||||||
|
|
||||||
export const CalcuEleCost = (
|
const storeElecPrice = useElecPriceStore();
|
||||||
input: Map<string, number>
|
const ContractUseValue = useElecDemandStore();
|
||||||
): ElecCostSummary => {
|
|
||||||
|
export const CalcuEleCost = (input: Map<string, number>): ElecCostSummary => {
|
||||||
const dailyData: Map<string, DailyEntry[]> = new Map();
|
const dailyData: Map<string, DailyEntry[]> = new Map();
|
||||||
let totalFlowCost = 0; // 總電價
|
let totalFlowCost = 0; // 總電價
|
||||||
let totalEleCost = 0; //總用電
|
let totalEleCost = 0; //總用電
|
||||||
@ -17,13 +25,27 @@ export const CalcuEleCost = (
|
|||||||
|
|
||||||
const dailyResults: DailyResult[] = [];
|
const dailyResults: DailyResult[] = [];
|
||||||
const elecPrices = storeElecPrice.elecData;
|
const elecPrices = storeElecPrice.elecData;
|
||||||
const Summer_Off_Prices = elecPrices.find((item:any) => item.displayName === "流動週日離峰夏月")?.out || 0;
|
const Summer_Off_Prices =
|
||||||
const Summer_HalfPeak_Prices_Saturday = elecPrices.find((item:any) => item.displayName === "流動週六半尖峰夏月")?.out || 0;
|
elecPrices.find((item: any) => item.displayName === "流動週日離峰夏月")
|
||||||
const Summer_HalfPeak_Prices_Weekday = elecPrices.find((item:any) => item.displayName === "流動平日半尖峰夏月")?.out || 0;
|
?.out || 0;
|
||||||
const Summer_Peak_Prices = elecPrices.find((item:any) => item.displayName === "流動平日尖峰夏月")?.out || 0;
|
const Summer_HalfPeak_Prices_Saturday =
|
||||||
const Non_Summer_Off_Prices = elecPrices.find((item:any) => item.displayName === "流動週日離峰非夏月")?.out || 0;
|
elecPrices.find((item: any) => item.displayName === "流動週六半尖峰夏月")
|
||||||
const Non_Summer_HalfPeak_Prices_Saturday = elecPrices.find((item:any) => item.displayName === "流動週六半尖峰非夏月")?.out || 0;
|
?.out || 0;
|
||||||
const Non_Summer_HalfPeak_Prices_Weekday = elecPrices.find((item:any) => item.displayName === "流動平日半尖峰非夏月")?.out || 0;
|
const Summer_HalfPeak_Prices_Weekday =
|
||||||
|
elecPrices.find((item: any) => item.displayName === "流動平日半尖峰夏月")
|
||||||
|
?.out || 0;
|
||||||
|
const Summer_Peak_Prices =
|
||||||
|
elecPrices.find((item: any) => item.displayName === "流動平日尖峰夏月")
|
||||||
|
?.out || 0;
|
||||||
|
const Non_Summer_Off_Prices =
|
||||||
|
elecPrices.find((item: any) => item.displayName === "流動週日離峰非夏月")
|
||||||
|
?.out || 0;
|
||||||
|
const Non_Summer_HalfPeak_Prices_Saturday =
|
||||||
|
elecPrices.find((item: any) => item.displayName === "流動週六半尖峰非夏月")
|
||||||
|
?.out || 0;
|
||||||
|
const Non_Summer_HalfPeak_Prices_Weekday =
|
||||||
|
elecPrices.find((item: any) => item.displayName === "流動平日半尖峰非夏月")
|
||||||
|
?.out || 0;
|
||||||
// 1. 將輸入資料按日期分組
|
// 1. 將輸入資料按日期分組
|
||||||
input.forEach((value, key) => {
|
input.forEach((value, key) => {
|
||||||
const dateStr = key.substring(0, 10);
|
const dateStr = key.substring(0, 10);
|
||||||
@ -117,7 +139,6 @@ export const CalcuEleCost = (
|
|||||||
dailyEleCost,
|
dailyEleCost,
|
||||||
dailyFlowCost,
|
dailyFlowCost,
|
||||||
});
|
});
|
||||||
|
|
||||||
}
|
}
|
||||||
return {
|
return {
|
||||||
dailyResults,
|
dailyResults,
|
||||||
@ -131,3 +152,72 @@ export const CalcuEleCost = (
|
|||||||
total_peakCost,
|
total_peakCost,
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const CalcuEleStandCost = (
|
||||||
|
input: Map<string, number>
|
||||||
|
): ElecStandCostSummary => {
|
||||||
|
const elecPrices = storeElecPrice.elecData;
|
||||||
|
const ContractUseData = ContractUseValue.elecData;
|
||||||
|
|
||||||
|
const Three_Phase =
|
||||||
|
elecPrices.find((item: any) => item.displayName === "基本按戶三相")?.out ||
|
||||||
|
0;
|
||||||
|
const Summer_Regular_Use =
|
||||||
|
elecPrices.find((item: any) => item.displayName === "基本經常夏月")?.out ||
|
||||||
|
0;
|
||||||
|
const Non_Summer_Regular_Use =
|
||||||
|
elecPrices.find((item: any) => item.displayName === "基本經常非夏月")
|
||||||
|
?.out || 0;
|
||||||
|
const ContractUse =
|
||||||
|
ContractUseData.find((item: any) => item.name === "Engel")?.out || 0;
|
||||||
|
|
||||||
|
const monthMap = new Map<string, StandEntry[]>(); // key: yyyy-MM
|
||||||
|
|
||||||
|
// 將資料依據年月分組
|
||||||
|
input.forEach((value, key) => {
|
||||||
|
const date = new Date(key);
|
||||||
|
const year = date.getFullYear();
|
||||||
|
const month = date.getMonth() + 1;
|
||||||
|
const ymKey = `${year}-${month.toString().padStart(2, "0")}`;
|
||||||
|
|
||||||
|
if (!monthMap.has(ymKey)) {
|
||||||
|
monthMap.set(ymKey, []);
|
||||||
|
}
|
||||||
|
monthMap.get(ymKey)?.push({ time: date, value });
|
||||||
|
});
|
||||||
|
|
||||||
|
const StandResults: StandResult[] = [];
|
||||||
|
let totalStandCost = 0;
|
||||||
|
|
||||||
|
for (const [ym, entries] of monthMap.entries()) {
|
||||||
|
if (!entries || entries.length === 0) continue;
|
||||||
|
|
||||||
|
const sampleDate = entries[0].time;
|
||||||
|
const month = sampleDate.getMonth() + 1;
|
||||||
|
const isSummer = month >= 6 && month <= 9;
|
||||||
|
|
||||||
|
let Phase = Three_Phase;
|
||||||
|
let Contract = isSummer ? Summer_Regular_Use : Non_Summer_Regular_Use;
|
||||||
|
|
||||||
|
const StandCost = Phase + Contract * ContractUse;
|
||||||
|
|
||||||
|
StandResults.push({
|
||||||
|
Phase,
|
||||||
|
Contract,
|
||||||
|
ContractUse,
|
||||||
|
StandCost,
|
||||||
|
});
|
||||||
|
|
||||||
|
totalStandCost += StandCost;
|
||||||
|
console.log(`=== ${ym} ===`);
|
||||||
|
console.log(` 按戶類別: ${Phase.toFixed(2)}`);
|
||||||
|
console.log(` 契約類型: $${Contract.toFixed(2)}`);
|
||||||
|
console.log(` 契約度數: $${ContractUse.toFixed(2)}`);
|
||||||
|
console.log(` 基本電價: $${StandCost.toFixed(2)}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
StandResults,
|
||||||
|
StandCost: totalStandCost,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
21
src/utils/getRandomColor.ts
Normal file
21
src/utils/getRandomColor.ts
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
const predefinedColors = ["#5470c6", "#91cc75", "#fac858"];
|
||||||
|
let colorIndex = 0;
|
||||||
|
|
||||||
|
const getRandomColor = () => {
|
||||||
|
if (colorIndex < predefinedColors.length) {
|
||||||
|
return predefinedColors[colorIndex++];
|
||||||
|
} else {
|
||||||
|
const letters = "0123456789ABCDEF";
|
||||||
|
let color = "#";
|
||||||
|
for (let i = 0; i < 6; i++) {
|
||||||
|
color += letters[Math.floor(Math.random() * 16)];
|
||||||
|
}
|
||||||
|
return color;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
getRandomColor.resetIndex = () => {
|
||||||
|
colorIndex = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default getRandomColor;
|
@ -3,14 +3,14 @@ export interface NiagaraElecData {
|
|||||||
slotPath: string;
|
slotPath: string;
|
||||||
displayName: string;
|
displayName: string;
|
||||||
id: string;
|
id: string;
|
||||||
out: number;
|
out: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface NiagaraElecDemandData {
|
export interface NiagaraElecDemandData {
|
||||||
slotPath: string;
|
slotPath: string;
|
||||||
displayName: string;
|
displayName: string;
|
||||||
name: string;
|
name: string;
|
||||||
out: number;
|
out: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface DailyResult {
|
export interface DailyResult {
|
||||||
@ -18,9 +18,9 @@ export interface DailyResult {
|
|||||||
off: number;
|
off: number;
|
||||||
half: number;
|
half: number;
|
||||||
peak: number;
|
peak: number;
|
||||||
offcost:number;
|
offcost: number;
|
||||||
halfcost:number;
|
halfcost: number;
|
||||||
peakcost:number;
|
peakcost: number;
|
||||||
dailyEleCost: number;
|
dailyEleCost: number;
|
||||||
dailyFlowCost: number;
|
dailyFlowCost: number;
|
||||||
}
|
}
|
||||||
@ -35,9 +35,38 @@ export interface ElecCostSummary {
|
|||||||
total_OffCost: number;
|
total_OffCost: number;
|
||||||
total_halfCost: number;
|
total_halfCost: number;
|
||||||
total_peakCost: number;
|
total_peakCost: number;
|
||||||
|
standCost?:number;
|
||||||
|
name?: string;
|
||||||
|
area?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ElecCostSummaryMap {
|
||||||
|
thisMonth: {
|
||||||
|
[slotPath: string]: ElecCostSummary | null | undefined;
|
||||||
|
};
|
||||||
|
lastMonth: {
|
||||||
|
[slotPath: string]: ElecCostSummary | null | undefined;
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface DailyEntry {
|
export interface DailyEntry {
|
||||||
time: Date;
|
time: Date;
|
||||||
value: number;
|
value: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface StandEntry {
|
||||||
|
time: Date;
|
||||||
|
value: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface StandResult {
|
||||||
|
Phase : number ;
|
||||||
|
Contract : number ;
|
||||||
|
ContractUse : number;
|
||||||
|
StandCost :number ;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ElecStandCostSummary {
|
||||||
|
StandResults: StandResult[];
|
||||||
|
StandCost :number ;
|
||||||
}
|
}
|
@ -73,11 +73,19 @@ import useElecTotalMeterStore from "../stores/useElecTotalMeterStore";
|
|||||||
import dayjs from "dayjs";
|
import dayjs from "dayjs";
|
||||||
|
|
||||||
const storeElecTotal = useElecTotalMeterStore();
|
const storeElecTotal = useElecTotalMeterStore();
|
||||||
|
const billingDateRange = ref("");
|
||||||
|
|
||||||
watch(
|
watch(
|
||||||
() => storeElecTotal.elecCostSummary,
|
() => storeElecTotal.elecFlowCostSummary,
|
||||||
(newElecData) => {
|
(newElecData) => {
|
||||||
console.log("elecCostSummary", newElecData);
|
console.log("elecFlowCostSummary", newElecData);
|
||||||
|
},
|
||||||
|
{ deep: true }
|
||||||
|
);
|
||||||
|
watch(
|
||||||
|
() => storeElecTotal.elecStandCostSummary,
|
||||||
|
(newElecData) => {
|
||||||
|
console.log("elecStandCostSummary", newElecData);
|
||||||
},
|
},
|
||||||
{ deep: true }
|
{ deep: true }
|
||||||
);
|
);
|
||||||
@ -85,6 +93,11 @@ watch(
|
|||||||
onMounted(async () => {
|
onMounted(async () => {
|
||||||
await storeElecTotal.getElecDataFromBaja();
|
await storeElecTotal.getElecDataFromBaja();
|
||||||
storeElecTotal.startTimer();
|
storeElecTotal.startTimer();
|
||||||
|
|
||||||
|
// 區間計費時間區段
|
||||||
|
billingDateRange.value = `${dayjs()
|
||||||
|
.startOf("month")
|
||||||
|
.format("YYYY/MM/DD")} - ${dayjs().format("YYYY/MM/DD")}`;
|
||||||
});
|
});
|
||||||
|
|
||||||
onUnmounted(() => {
|
onUnmounted(() => {
|
||||||
@ -97,8 +110,8 @@ const statisticData = computed(() => {
|
|||||||
let intervalFlowCost = 0;
|
let intervalFlowCost = 0;
|
||||||
let intervalEleCost = 0;
|
let intervalEleCost = 0;
|
||||||
|
|
||||||
if (storeElecTotal.elecCostSummary?.dailyResults) {
|
if (storeElecTotal.elecFlowCostSummary?.dailyResults) {
|
||||||
storeElecTotal.elecCostSummary.dailyResults.forEach((dailyResult) => {
|
storeElecTotal.elecFlowCostSummary.dailyResults.forEach((dailyResult) => {
|
||||||
if (dailyResult.dateStr.startsWith(currentMonth)) {
|
if (dailyResult.dateStr.startsWith(currentMonth)) {
|
||||||
intervalFlowCost += dailyResult.dailyFlowCost;
|
intervalFlowCost += dailyResult.dailyFlowCost;
|
||||||
intervalEleCost += dailyResult.dailyEleCost;
|
intervalEleCost += dailyResult.dailyEleCost;
|
||||||
@ -109,13 +122,13 @@ const statisticData = computed(() => {
|
|||||||
return [
|
return [
|
||||||
{
|
{
|
||||||
title: "今年電費累計",
|
title: "今年電費累計",
|
||||||
value: storeElecTotal.elecCostSummary?.totalFlowCost || 0,
|
value: storeElecTotal.elecFlowCostSummary?.totalFlowCost || 0,
|
||||||
unit: "元",
|
unit: "元",
|
||||||
},
|
},
|
||||||
{ title: "區間電費", value: intervalFlowCost, unit: "元" },
|
{ title: "區間電費", value: intervalFlowCost, unit: "元" },
|
||||||
{
|
{
|
||||||
title: "今年碳排當量累計",
|
title: "今年碳排當量累計",
|
||||||
value: storeElecTotal.elecCostSummary?.totalEleCost * 0.424,
|
value: storeElecTotal.elecFlowCostSummary?.totalEleCost * 0.424,
|
||||||
unit: "公斤 CO2e/度",
|
unit: "公斤 CO2e/度",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -125,7 +138,7 @@ const statisticData = computed(() => {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: "今年用電度數",
|
title: "今年用電度數",
|
||||||
value: storeElecTotal.elecCostSummary?.totalEleCost || 0,
|
value: storeElecTotal.elecFlowCostSummary?.totalEleCost || 0,
|
||||||
unit: "kWh",
|
unit: "kWh",
|
||||||
},
|
},
|
||||||
{ title: "區間用電度數", value: intervalEleCost, unit: "kWh" },
|
{ title: "區間用電度數", value: intervalEleCost, unit: "kWh" },
|
||||||
@ -162,22 +175,24 @@ function groupByMonth(dailyResults) {
|
|||||||
|
|
||||||
// 每月用電分析
|
// 每月用電分析
|
||||||
const monthlyElectricityData = computed(() => {
|
const monthlyElectricityData = computed(() => {
|
||||||
if (!storeElecTotal.elecCostSummary?.dailyResults) {
|
if (!storeElecTotal.elecFlowCostSummary?.dailyResults) {
|
||||||
return { categories: [], series: [] };
|
return { categories: [], series: [] };
|
||||||
}
|
}
|
||||||
|
|
||||||
const groupedData = groupByMonth(storeElecTotal.elecCostSummary.dailyResults);
|
const groupedData = groupByMonth(
|
||||||
|
storeElecTotal.elecFlowCostSummary.dailyResults
|
||||||
|
);
|
||||||
const categories = Object.keys(groupedData);
|
const categories = Object.keys(groupedData);
|
||||||
const sortedCategories = _.sortBy(categories, (month) =>
|
const sortedCategories = _.sortBy(categories, (month) =>
|
||||||
dayjs().month(month).valueOf()
|
dayjs().month(month).valueOf()
|
||||||
);
|
);
|
||||||
|
|
||||||
const baseElecData = sortedCategories.map((month) => groupedData[month].offcost);
|
const baseElecData = storeElecTotal.elecStandCostSummary.StandCost;
|
||||||
const flowElecData = sortedCategories.map(
|
const flowElecData = sortedCategories.map(
|
||||||
(month) => groupedData[month].totalCost
|
(month) => groupedData[month].totalCost
|
||||||
);
|
);
|
||||||
const totalElecData = sortedCategories.map((month, index) => {
|
const totalElecData = sortedCategories.map((month, index) => {
|
||||||
return (baseElecData[index] || 0) + (flowElecData[index] || 0);
|
return baseElecData + (flowElecData[index] || 0);
|
||||||
});
|
});
|
||||||
|
|
||||||
return {
|
return {
|
||||||
@ -186,7 +201,7 @@ const monthlyElectricityData = computed(() => {
|
|||||||
{
|
{
|
||||||
name: "基本電費",
|
name: "基本電費",
|
||||||
type: "bar",
|
type: "bar",
|
||||||
data: baseElecData,
|
data: Array(sortedCategories.length).fill(baseElecData),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "流動電費",
|
name: "流動電費",
|
||||||
@ -194,7 +209,7 @@ const monthlyElectricityData = computed(() => {
|
|||||||
data: flowElecData,
|
data: flowElecData,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "總電量",
|
name: "總電費",
|
||||||
type: "bar",
|
type: "bar",
|
||||||
data: totalElecData,
|
data: totalElecData,
|
||||||
},
|
},
|
||||||
@ -204,11 +219,13 @@ const monthlyElectricityData = computed(() => {
|
|||||||
|
|
||||||
// 每月碳排當量
|
// 每月碳排當量
|
||||||
const monthlyCarbonData = computed(() => {
|
const monthlyCarbonData = computed(() => {
|
||||||
if (!storeElecTotal.elecCostSummary?.dailyResults) {
|
if (!storeElecTotal.elecFlowCostSummary?.dailyResults) {
|
||||||
return { categories: [], series: [] };
|
return { categories: [], series: [] };
|
||||||
}
|
}
|
||||||
|
|
||||||
const groupedData = groupByMonth(storeElecTotal.elecCostSummary.dailyResults);
|
const groupedData = groupByMonth(
|
||||||
|
storeElecTotal.elecFlowCostSummary.dailyResults
|
||||||
|
);
|
||||||
const categories = Object.keys(groupedData);
|
const categories = Object.keys(groupedData);
|
||||||
const sortedCategories = _.sortBy(categories, (month) =>
|
const sortedCategories = _.sortBy(categories, (month) =>
|
||||||
dayjs().month(month).valueOf()
|
dayjs().month(month).valueOf()
|
||||||
@ -232,76 +249,82 @@ const monthlyCarbonData = computed(() => {
|
|||||||
|
|
||||||
// 每月計費度數 (kWh)
|
// 每月計費度數 (kWh)
|
||||||
const monthlyBillingData = computed(() => {
|
const monthlyBillingData = computed(() => {
|
||||||
if (!storeElecTotal.elecCostSummary?.dailyResults) {
|
if (!storeElecTotal.elecFlowCostSummary?.dailyResults) {
|
||||||
return { categories: [], series: [] };
|
return { categories: [], series: [] };
|
||||||
}
|
}
|
||||||
|
|
||||||
const billingData = groupByMonth(storeElecTotal.elecCostSummary.dailyResults);
|
const billingData = groupByMonth(
|
||||||
|
storeElecTotal.elecFlowCostSummary.dailyResults
|
||||||
|
);
|
||||||
const categories = Object.keys(billingData);
|
const categories = Object.keys(billingData);
|
||||||
const sortedCategories = _.sortBy(categories, month => dayjs().month(month).valueOf());
|
const sortedCategories = _.sortBy(categories, (month) =>
|
||||||
|
dayjs().month(month).valueOf()
|
||||||
|
);
|
||||||
|
|
||||||
const peakData = sortedCategories.map(date => billingData[date].peak);
|
const peakData = sortedCategories.map((date) => billingData[date].peak);
|
||||||
const halfData = sortedCategories.map(date => billingData[date].half);
|
const halfData = sortedCategories.map((date) => billingData[date].half);
|
||||||
const offData = sortedCategories.map(date => billingData[date].off);
|
const offData = sortedCategories.map((date) => billingData[date].off);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
categories: sortedCategories,
|
categories: sortedCategories,
|
||||||
series: [
|
series: [
|
||||||
{ name: "尖峰", type: "bar", data: peakData },
|
{ name: "尖峰", type: "bar", data: peakData },
|
||||||
{ name: "半尖峰", type: "bar", data: halfData },
|
{ name: "半尖峰", type: "bar", data: halfData },
|
||||||
{ name: "離峰", type: "bar", data: offData }
|
{ name: "離峰", type: "bar", data: offData },
|
||||||
]
|
],
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
// 區間計費度數
|
|
||||||
const billingDateRange = computed(() => {
|
|
||||||
const startOfMonth = dayjs().startOf('month').format('YYYY-MM-DD');
|
|
||||||
const today = dayjs().format('YYYY-MM-DD');
|
|
||||||
return `${startOfMonth} - ${today}`;
|
|
||||||
});
|
|
||||||
|
|
||||||
const areaBillingData = computed(() => {
|
const areaBillingData = computed(() => {
|
||||||
if (!storeElecTotal.elecCostSummary?.dailyResults) {
|
if (!storeElecTotal.elecFlowCostSummary?.dailyResults) {
|
||||||
return { categories: [], series: [] };
|
return { categories: [], series: [] };
|
||||||
}
|
}
|
||||||
|
|
||||||
const today = dayjs();
|
const today = dayjs();
|
||||||
const currentMonth = today.format('YYYY-MM');
|
const currentMonth = today.format("YYYY-MM");
|
||||||
const startDate = dayjs(`${currentMonth}-01`); // Get the first day of the current month
|
const startDate = dayjs(`${currentMonth}-01`); // Get the first day of the current month
|
||||||
const endDate = today;
|
const endDate = today;
|
||||||
|
|
||||||
// Filter daily results within the specified date range
|
// Filter daily results within the specified date range
|
||||||
const areaResults = storeElecTotal.elecCostSummary.dailyResults.filter(result => {
|
const areaResults = storeElecTotal.elecFlowCostSummary.dailyResults.filter(
|
||||||
const resultDate = dayjs(result.dateStr);
|
(result) => {
|
||||||
return resultDate.isSame(startDate, 'day') || (resultDate.isAfter(startDate, 'day') && resultDate.isBefore(endDate, 'day')) || resultDate.isSame(endDate, 'day');
|
const resultDate = dayjs(result.dateStr);
|
||||||
});
|
return (
|
||||||
|
resultDate.isSame(startDate, "day") ||
|
||||||
|
(resultDate.isAfter(startDate, "day") &&
|
||||||
|
resultDate.isBefore(endDate, "day")) ||
|
||||||
|
resultDate.isSame(endDate, "day")
|
||||||
|
);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
// Transform the filtered data for billing
|
// Transform the filtered data for billing
|
||||||
const transformedData = {};
|
const transformedData = {};
|
||||||
areaResults.forEach(result => {
|
areaResults.forEach((result) => {
|
||||||
const date = dayjs(result.dateStr).format('MM-DD'); // Format the date for the category
|
const date = dayjs(result.dateStr).format("MM-DD"); // Format the date for the category
|
||||||
transformedData[date] = {
|
transformedData[date] = {
|
||||||
peak: result.peak,
|
peak: result.peak,
|
||||||
half: result.half,
|
half: result.half,
|
||||||
off: result.off
|
off: result.off,
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
const categories = Object.keys(transformedData);
|
const categories = Object.keys(transformedData);
|
||||||
const sortedCategories = _.sortBy(categories, date => dayjs(date, 'MM-DD').valueOf());
|
const sortedCategories = _.sortBy(categories, (date) =>
|
||||||
|
dayjs(date, "MM-DD").valueOf()
|
||||||
|
);
|
||||||
|
|
||||||
const peakData = sortedCategories.map(date => transformedData[date].peak);
|
const peakData = sortedCategories.map((date) => transformedData[date].peak);
|
||||||
const halfData = sortedCategories.map(date => transformedData[date].half);
|
const halfData = sortedCategories.map((date) => transformedData[date].half);
|
||||||
const offData = sortedCategories.map(date => transformedData[date].off);
|
const offData = sortedCategories.map((date) => transformedData[date].off);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
categories: sortedCategories,
|
categories: sortedCategories,
|
||||||
series: [
|
series: [
|
||||||
{ name: "尖峰", type: "bar", data: peakData },
|
{ name: "尖峰", type: "bar", data: peakData },
|
||||||
{ name: "半尖峰", type: "bar", data: halfData },
|
{ name: "半尖峰", type: "bar", data: halfData },
|
||||||
{ name: "離峰", type: "bar", data: offData }
|
{ name: "離峰", type: "bar", data: offData },
|
||||||
]
|
],
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
@ -311,7 +311,6 @@
|
|||||||
import { ref, watch } from "vue";
|
import { ref, watch } from "vue";
|
||||||
import { Edit, CircleClose, CircleCheck } from "@element-plus/icons-vue";
|
import { Edit, CircleClose, CircleCheck } from "@element-plus/icons-vue";
|
||||||
import useElecPriceStore from "../stores/useElecPriceStore";
|
import useElecPriceStore from "../stores/useElecPriceStore";
|
||||||
import axios from "axios";
|
|
||||||
|
|
||||||
const storeElecPrice = useElecPriceStore();
|
const storeElecPrice = useElecPriceStore();
|
||||||
|
|
||||||
@ -360,30 +359,6 @@ const resetStand3Values = () => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const updatePrice = async (slotPath: string, out: number) => {
|
|
||||||
const domain = window.location.origin;
|
|
||||||
try {
|
|
||||||
console.log("updatePrice",`${domain}/obix/config/${slotPath}/in2/value`)
|
|
||||||
const res = await axios.put(
|
|
||||||
`${domain}/obix/config/${slotPath}/in2/value`,
|
|
||||||
`<real name="in" val="${out}"/> `,
|
|
||||||
{
|
|
||||||
headers: { "Content-Type": "text/xml" },
|
|
||||||
}
|
|
||||||
);
|
|
||||||
if (res.status === 200) {
|
|
||||||
console.log(`成功更新 ${slotPath} 為 ${out}`);
|
|
||||||
return true;
|
|
||||||
} else {
|
|
||||||
console.error(`更新 ${slotPath} 失敗,狀態碼: ${res.status}`);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
console.error(`更新 ${slotPath} 出錯:`, error);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const confirmChanges = async () => {
|
const confirmChanges = async () => {
|
||||||
stand3isEditing.value = false;
|
stand3isEditing.value = false;
|
||||||
|
|
||||||
@ -419,7 +394,7 @@ const confirmChanges = async () => {
|
|||||||
? item.slotPath.slice(6) // 移除 "slot:/"
|
? item.slotPath.slice(6) // 移除 "slot:/"
|
||||||
: item.slotPath; // 如果没有前綴,則保持不變
|
: item.slotPath; // 如果没有前綴,則保持不變
|
||||||
// 更新 Niagara
|
// 更新 Niagara
|
||||||
const success = await updatePrice(slotPath, stand3Value.value[i]);
|
const success = await storeElecPrice.updatePrice(slotPath, stand3Value.value[i]);
|
||||||
if (!success) {
|
if (!success) {
|
||||||
failedUpdates.push({
|
failedUpdates.push({
|
||||||
slotPath: item.slotPath,
|
slotPath: item.slotPath,
|
||||||
|
@ -1,17 +1,52 @@
|
|||||||
<template>
|
<template>
|
||||||
<el-row :gutter="20">
|
<el-row :gutter="20">
|
||||||
<el-col :span="24">
|
<el-col :span="24" style="padding-right: 30px">
|
||||||
<div style="display: flex; justify-content: flex-end">
|
<div style="display: flex; justify-content: flex-end">
|
||||||
<el-button type="primary" :icon="Printer" @click="generatePDF"
|
<el-button
|
||||||
|
plain
|
||||||
|
type="primary"
|
||||||
|
:icon="Setting"
|
||||||
|
@click="dialogVisible = true"
|
||||||
|
>
|
||||||
|
設定報表
|
||||||
|
</el-button>
|
||||||
|
<el-button plain type="success" :icon="Printer" @click="generatePDF"
|
||||||
>列印報表</el-button
|
>列印報表</el-button
|
||||||
>
|
>
|
||||||
</div>
|
</div>
|
||||||
</el-col>
|
</el-col>
|
||||||
</el-row>
|
</el-row>
|
||||||
<!-- 顯示在畫面上的內容 -->
|
<!-- 設定 Modal -->
|
||||||
<PdfContent />
|
<EnergyModal
|
||||||
|
v-model="dialogVisible"
|
||||||
<!-- 給 vue3-html2pdf 用的內容 -->
|
title="設定報表"
|
||||||
|
:confirm="handleConfirm"
|
||||||
|
:close="handleClose"
|
||||||
|
>
|
||||||
|
<template #default>
|
||||||
|
<!-- Modal 的內容 -->
|
||||||
|
<el-form :model="form" label-width="auto" style="max-width: 600px">
|
||||||
|
<el-form-item label="報表名稱">
|
||||||
|
<el-input v-model="form.name" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="報表期間">
|
||||||
|
<el-date-picker
|
||||||
|
v-model="form.date"
|
||||||
|
type="month"
|
||||||
|
placeholder="請選擇報表時間"
|
||||||
|
style="width: 100%"
|
||||||
|
value-format="YYYY-MM-DD"
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="編製人員">
|
||||||
|
<el-input v-model="form.staff" />
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
</template>
|
||||||
|
</EnergyModal>
|
||||||
|
<!-- 顯示在畫面上的內容 -->
|
||||||
|
<PdfContent :form="originalForm" />
|
||||||
|
<!-- 給 vue3-html2pdf 用的內容 -->
|
||||||
<vue3-html2pdf
|
<vue3-html2pdf
|
||||||
ref="html2Pdf"
|
ref="html2Pdf"
|
||||||
:show-layout="false"
|
:show-layout="false"
|
||||||
@ -27,30 +62,101 @@
|
|||||||
pdf-content-width="1120px"
|
pdf-content-width="1120px"
|
||||||
>
|
>
|
||||||
<template #pdf-content>
|
<template #pdf-content>
|
||||||
<PdfContent />
|
<PdfContent :form="originalForm" />
|
||||||
</template>
|
</template>
|
||||||
</vue3-html2pdf>
|
</vue3-html2pdf>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { ref } from "vue";
|
import { ref, reactive, watch, onMounted, onUnmounted } from "vue";
|
||||||
import PdfContent from "../components/PdfContent.vue";
|
import PdfContent from "../components/PdfContent.vue";
|
||||||
import { Printer } from "@element-plus/icons-vue";
|
import EnergyModal from "../components/EnergyModal.vue";
|
||||||
|
import { Setting, Printer } from "@element-plus/icons-vue";
|
||||||
import Vue3Html2pdf from "vue3-html2pdf";
|
import Vue3Html2pdf from "vue3-html2pdf";
|
||||||
|
import useElecReportStore from "../stores/useElecReportStore";
|
||||||
|
import dayjs from "dayjs";
|
||||||
|
|
||||||
|
const storeElecReport = useElecReportStore();
|
||||||
|
|
||||||
|
// 設定
|
||||||
|
const dialogVisible = ref(false);
|
||||||
|
const form = reactive({
|
||||||
|
name: "智慧大樓電表月報表",
|
||||||
|
date: dayjs()
|
||||||
|
.startOf("month")
|
||||||
|
.startOf("day")
|
||||||
|
.format("YYYY-MM-DDTHH:mm:ss.000+08:00"),
|
||||||
|
staff: "能源管理部",
|
||||||
|
});
|
||||||
|
const originalForm = reactive({ ...form });
|
||||||
|
|
||||||
// PDF 文件名稱
|
// PDF 文件名稱
|
||||||
const pdfFileName = ref("智慧大樓電表月報表");
|
const pdfFileName = ref("智慧大樓電表月報表");
|
||||||
|
|
||||||
// 引用 vue-html2pdf 實例
|
// 引用 vue-html2pdf 實例
|
||||||
const html2Pdf = ref<InstanceType<typeof Vue3Html2pdf> | null>(null);
|
const html2Pdf = ref<InstanceType<typeof Vue3Html2pdf> | null>(null);
|
||||||
|
|
||||||
|
// 處理確認邏輯
|
||||||
|
const handleConfirm = () => {
|
||||||
|
console.log("確認", form);
|
||||||
|
localStorage.setItem("elecReportForm", JSON.stringify(form));
|
||||||
|
storeElecReport.startTime = dayjs(form.date)
|
||||||
|
.startOf("month")
|
||||||
|
.startOf("day")
|
||||||
|
.format("YYYY-MM-DDTHH:mm:ss.000+08:00");
|
||||||
|
storeElecReport.endTime = dayjs(form.date)
|
||||||
|
.endOf("month")
|
||||||
|
.endOf("day")
|
||||||
|
.format("YYYY-MM-DDTHH:mm:ss.000+08:00");
|
||||||
|
storeElecReport.updateHistoryData();
|
||||||
|
Object.assign(originalForm, form);
|
||||||
|
dialogVisible.value = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
// 處理關閉邏輯
|
||||||
|
const handleClose = () => {
|
||||||
|
dialogVisible.value = false;
|
||||||
|
Object.assign(form, originalForm);
|
||||||
|
console.log("關閉");
|
||||||
|
};
|
||||||
|
|
||||||
// 觸發 PDF 生成和下載
|
// 觸發 PDF 生成和下載
|
||||||
const generatePDF = async () => {
|
const generatePDF = async () => {
|
||||||
if (html2Pdf.value) {
|
if (html2Pdf.value) {
|
||||||
await html2Pdf.value.generatePdf();
|
await html2Pdf.value.generatePdf();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => storeElecReport.elecCostSummary,
|
||||||
|
(newElecData) => {
|
||||||
|
if(newElecData){
|
||||||
|
console.log("elecCostSummary",newElecData)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{ deep: true }
|
||||||
|
);
|
||||||
|
|
||||||
|
onMounted(async () => {
|
||||||
|
const storedForm = localStorage.getItem("elecReportForm");
|
||||||
|
if (storedForm) {
|
||||||
|
Object.assign(form, JSON.parse(storedForm));
|
||||||
|
} else {
|
||||||
|
localStorage.setItem("elecReportForm", JSON.stringify(form));
|
||||||
|
}
|
||||||
|
Object.assign(originalForm, form);
|
||||||
|
storeElecReport.startTime = dayjs(form.date)
|
||||||
|
.startOf("month")
|
||||||
|
.startOf("day")
|
||||||
|
.format("YYYY-MM-DDTHH:mm:ss.000+08:00");
|
||||||
|
storeElecReport.endTime = dayjs(form.date)
|
||||||
|
.endOf("month")
|
||||||
|
.endOf("day")
|
||||||
|
.format("YYYY-MM-DDTHH:mm:ss.000+08:00");
|
||||||
|
|
||||||
|
await storeElecReport.getElecDataFromBaja();
|
||||||
|
});
|
||||||
|
|
||||||
|
onUnmounted(() => {});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped></style>
|
||||||
</style>
|
|
||||||
|
Loading…
Reference in New Issue
Block a user