框架UI修改
This commit is contained in:
parent
48dd8d62bb
commit
704af2eb18
@ -1,3 +1,4 @@
|
||||
{
|
||||
"systemName": "關渡醫院中央監控"
|
||||
"systemName": "關渡醫院中央監控",
|
||||
"is3D": true
|
||||
}
|
||||
|
19
src/App.vue
19
src/App.vue
@ -8,7 +8,7 @@ import useNiagaraDataStore from "@/stores/useNiagaraDataStore";
|
||||
import useNavDataStore from "@/stores/useNavDataStore";
|
||||
|
||||
const showSidebar = ref(false);
|
||||
const systemName = ref("");
|
||||
const is3D = ref(false);
|
||||
const userStore = useUserStore();
|
||||
const niagaraStore = useNiagaraDataStore();
|
||||
const navStore = useNavDataStore();
|
||||
@ -20,29 +20,24 @@ const toggleSidebar = () => {
|
||||
onMounted(async () => {
|
||||
const json = await fetch("./config.json");
|
||||
const res = await json.json();
|
||||
systemName.value = res.systemName;
|
||||
is3D.value = res.is3D;
|
||||
|
||||
await userStore.loadUserInfo();
|
||||
await niagaraStore.getSystemData();
|
||||
await navStore.getNavData();
|
||||
});
|
||||
|
||||
provide("app_config", { is3D });
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<a-layout class="w-full" style="min-height: 100vh">
|
||||
<LeftSidebar
|
||||
:showSidebar="showSidebar"
|
||||
:toggleSidebar="toggleSidebar"
|
||||
/>
|
||||
<Navbar
|
||||
:systemName="systemName"
|
||||
:open="toggleSidebar"
|
||||
:userName="userStore.userName"
|
||||
/>
|
||||
<LeftSidebar :showSidebar="showSidebar" :toggleSidebar="toggleSidebar" />
|
||||
<Navbar :open="toggleSidebar" :userName="userStore.userName" />
|
||||
<a-layout-content
|
||||
class="overflow-x-hidden"
|
||||
:style="{
|
||||
margin: '5px',
|
||||
margin: '5px',
|
||||
padding: '0px',
|
||||
background: '#fafafa',
|
||||
minHeight: '280px',
|
||||
|
@ -1,14 +1,21 @@
|
||||
<script setup>
|
||||
import { ref } from "vue";
|
||||
import { ref, inject } from "vue";
|
||||
import Forge from "@/components/forge/Forge.vue";
|
||||
import { twMerge } from "tailwind-merge";
|
||||
const FILE_BASEURL = import.meta.env.VITE_FILE_API_BASEURL;
|
||||
const type = ref("3d");
|
||||
const type = ref("2d");
|
||||
|
||||
const { is3D } = inject("app_config");
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<a-card class="card">
|
||||
<div class="w-full relative">
|
||||
<a-card
|
||||
class="card h-full"
|
||||
:bodyStyle="{
|
||||
height: '100%',
|
||||
}"
|
||||
>
|
||||
<div :class="twMerge('w-full relative', is3D ? '' : 'h-full')">
|
||||
<img
|
||||
alt="build"
|
||||
:src="`${FILE_BASEURL}/file/UI_images/build/2D/build.jpg`"
|
||||
@ -18,7 +25,7 @@ const type = ref("3d");
|
||||
type == '2d' ? 'opacity-100 z-10' : 'opacity-0 z-0'
|
||||
)
|
||||
"
|
||||
style="width: 400px; height: 400px; vertical-align: middle"
|
||||
style="height: 100%; vertical-align: middle"
|
||||
/>
|
||||
<Forge
|
||||
:class="
|
||||
@ -29,32 +36,18 @@ const type = ref("3d");
|
||||
"
|
||||
/>
|
||||
</div>
|
||||
<div class="flex items-center justify-between mt-2">
|
||||
<div>
|
||||
<h5>Building</h5>
|
||||
<p class="text-gray-400">
|
||||
掌握建築用電、系統健康狀態,打造智慧節能醫院
|
||||
</p>
|
||||
<div class="mt-2" v-if="is3D">
|
||||
<div class="flex items-center justify-between">
|
||||
<h5>智慧建築</h5>
|
||||
<a-radio-group v-model:value="type">
|
||||
<a-radio-button value="2d">2D</a-radio-button>
|
||||
<a-radio-button value="3d">3D</a-radio-button>
|
||||
</a-radio-group>
|
||||
</div>
|
||||
<a-radio-group v-model:value="type">
|
||||
<a-radio-button value="2d">2D</a-radio-button>
|
||||
<a-radio-button value="3d">3D</a-radio-button>
|
||||
</a-radio-group>
|
||||
<p class="intro text-gray-400">
|
||||
掌握建築用電、系統健康狀態,打造智慧節能醫院
|
||||
</p>
|
||||
</div>
|
||||
<!-- <a-row>
|
||||
<a-col :span="6">
|
||||
<a-statistic title="用戶數" :value="3.6" suffix="萬" />
|
||||
</a-col>
|
||||
<a-col :span="6">
|
||||
<a-statistic title="智慧電錶" :value="82" suffix="個" />
|
||||
</a-col>
|
||||
<a-col :span="6">
|
||||
<a-statistic title="電費支出" :value="-7.2" suffix="萬" />
|
||||
</a-col>
|
||||
<a-col :span="6">
|
||||
<a-statistic title="警示次數" :value="15" suffix="次" />
|
||||
</a-col>
|
||||
</a-row> -->
|
||||
</a-card>
|
||||
</template>
|
||||
|
||||
@ -66,7 +59,11 @@ const type = ref("3d");
|
||||
h5 {
|
||||
margin: 0;
|
||||
font-weight: 700;
|
||||
font-size: 18px;
|
||||
font-size: 33px;
|
||||
color: #141414;
|
||||
}
|
||||
|
||||
.intro {
|
||||
font-size: 1.125rem;
|
||||
}
|
||||
</style>
|
||||
|
@ -93,9 +93,9 @@ const generateCylinderChartOption = (data) => {
|
||||
x2: 1,
|
||||
y2: 0,
|
||||
colorStops: [
|
||||
{ offset: 0, color: "#1890ff" }, // 左側顏色
|
||||
{ offset: 0, color: "#69B0CF" }, // 左側顏色
|
||||
{ offset: 0.5, color: "#acd7e4" }, // 中間顏色
|
||||
{ offset: 1, color: "#1890ff" }, // 右側顏色
|
||||
{ offset: 1, color: "#69B0CF" }, // 右側顏色
|
||||
],
|
||||
};
|
||||
const color2 = {
|
||||
@ -106,9 +106,9 @@ const generateCylinderChartOption = (data) => {
|
||||
x2: 1,
|
||||
y2: 0,
|
||||
colorStops: [
|
||||
{ offset: 0, color: "#91cc75" }, // 左側顏色
|
||||
{ offset: 0.5, color: "#d2f0c2" }, // 中間顏色
|
||||
{ offset: 1, color: "#91cc75" }, // 右側顏色
|
||||
{ offset: 0, color: "#E7F5A7" }, // 左側顏色
|
||||
{ offset: 0.5, color: "#ebffe0" }, // 中間顏色
|
||||
{ offset: 1, color: "#E7F5A7" }, // 右側顏色
|
||||
],
|
||||
};
|
||||
|
||||
@ -190,9 +190,9 @@ const generateCylinderChartOption = (data) => {
|
||||
symbolPosition: "end",
|
||||
data: data.values[1].value,
|
||||
itemStyle: {
|
||||
color: "#d2f0c2",
|
||||
color: "#ebffe0",
|
||||
borderWidth: 1,
|
||||
borderColor: "#91cc75",
|
||||
borderColor: "#E7F5A7",
|
||||
borderType: "solid",
|
||||
},
|
||||
z: 12,
|
||||
@ -207,7 +207,7 @@ const generateCylinderChartOption = (data) => {
|
||||
data: data.values[0].value,
|
||||
z: 12,
|
||||
itemStyle: {
|
||||
color: "#1890ff",
|
||||
color: "#69B0CF",
|
||||
},
|
||||
},
|
||||
{
|
||||
@ -218,7 +218,7 @@ const generateCylinderChartOption = (data) => {
|
||||
symbolPosition: "start",
|
||||
data: data.values[1].value,
|
||||
itemStyle: {
|
||||
color: "#91cc75",
|
||||
color: "#E7F5A7",
|
||||
},
|
||||
z: 12,
|
||||
},
|
||||
|
@ -1,5 +1,6 @@
|
||||
<script setup>
|
||||
import { defineProps } from "vue";
|
||||
const FILE_BASEURL = import.meta.env.VITE_FILE_API_BASEURL;
|
||||
const props = defineProps({
|
||||
elecData: {
|
||||
type: Object,
|
||||
@ -20,7 +21,7 @@ const props = defineProps({
|
||||
</a-col>
|
||||
<a-col :span="6" class="pl-0">
|
||||
<div class="icon-box">
|
||||
<font-awesome-icon :icon="['fas', item.icon]" size="2x"/>
|
||||
<img :src="`${FILE_BASEURL}/file/UI_images/stat/${item.icon}.svg`" alt="icon">
|
||||
</div>
|
||||
</a-col>
|
||||
</a-row>
|
||||
@ -52,11 +53,5 @@ const props = defineProps({
|
||||
.icon-box {
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
text-align: center;
|
||||
background: #1890ff;
|
||||
color: #fff;
|
||||
border-radius: 0.5rem;
|
||||
margin-left: auto;
|
||||
line-height: 55px;
|
||||
}
|
||||
</style>
|
||||
|
@ -46,9 +46,10 @@ const handleClick = (ord) => {
|
||||
if (ord) {
|
||||
router.push({
|
||||
name: "baja",
|
||||
query: {
|
||||
pagename: 'system',
|
||||
ord: encodeURIComponent(ord) },
|
||||
query: {
|
||||
pagename: "system",
|
||||
ord: encodeURIComponent(ord),
|
||||
},
|
||||
});
|
||||
}
|
||||
};
|
||||
@ -65,7 +66,7 @@ const handleClick = (ord) => {
|
||||
<a-col :span="6" v-for="item in flattenedItems" :key="item.key">
|
||||
<a-card
|
||||
@click="handleClick(item.ord)"
|
||||
class="shadow"
|
||||
class="shadow inner-card"
|
||||
:bodyStyle="{
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
@ -79,7 +80,7 @@ const handleClick = (ord) => {
|
||||
alt="Icon"
|
||||
style="width: 30px; height: 30px; vertical-align: middle"
|
||||
/>
|
||||
<span class="text-lg">{{ item.label }}</span>
|
||||
<span class="text-lg text-white">{{ item.label }}</span>
|
||||
</a-card>
|
||||
</a-col>
|
||||
</a-row>
|
||||
@ -92,6 +93,9 @@ const handleClick = (ord) => {
|
||||
.card {
|
||||
box-shadow: 0 20px 27px rgb(0 0 0 / 5%);
|
||||
}
|
||||
.inner-card {
|
||||
background-color: #69b0cf;
|
||||
}
|
||||
h5 {
|
||||
margin: 0;
|
||||
font-weight: 700;
|
||||
|
@ -18,12 +18,11 @@ const preOpenKeys = ref([]); // 用於儲存之前展開的 submenu
|
||||
|
||||
const filteredItems = computed(() => {
|
||||
if (navStore.menuList && navStore.menuList.length > 0) {
|
||||
if(navStore.menuList[0].children.length > 1){
|
||||
if (navStore.menuList[0].children.length > 1) {
|
||||
return navStore.menuList[0].children;
|
||||
}else{
|
||||
} else {
|
||||
return navStore.menuList[0].children[0].children;
|
||||
}
|
||||
|
||||
}
|
||||
return [];
|
||||
});
|
||||
@ -32,8 +31,7 @@ const handleClick = (ord) => {
|
||||
if (ord) {
|
||||
router.push({
|
||||
name: "baja",
|
||||
query: { pagename: 'system',
|
||||
ord: encodeURIComponent(ord) },
|
||||
query: { pagename: "system", ord: encodeURIComponent(ord) },
|
||||
});
|
||||
}
|
||||
};
|
||||
@ -55,9 +53,12 @@ watch(
|
||||
:closable="false"
|
||||
placement="left"
|
||||
width="300"
|
||||
bodyStyle="padding: 0"
|
||||
:bodyStyle="{
|
||||
padding: 0,
|
||||
backgroundColor: '#69b0cf',
|
||||
}"
|
||||
>
|
||||
<a-menu mode="inline" theme="light">
|
||||
<a-menu mode="inline" theme="light" class="sidebarMenu">
|
||||
<template v-for="(item, index) in filteredItems" :key="index">
|
||||
<a-menu-item
|
||||
v-if="!item.children"
|
||||
@ -94,3 +95,20 @@ watch(
|
||||
</a-menu>
|
||||
</a-drawer>
|
||||
</template>
|
||||
|
||||
<style>
|
||||
.sidebarMenu {
|
||||
background-color: transparent;
|
||||
color: #fff;
|
||||
}
|
||||
.sidebarMenu .ant-menu-submenu-selected > .ant-menu-submenu-title {
|
||||
color: #fff;
|
||||
}
|
||||
.sidebarMenu .ant-menu-item:hover {
|
||||
background-color: #e7f5a7 !important;
|
||||
}
|
||||
.sidebarMenu .ant-menu-item-selected {
|
||||
background-color: #e7f5a7 !important;
|
||||
color: #333 !important;
|
||||
}
|
||||
</style>
|
||||
|
@ -3,7 +3,7 @@ import { ref, computed, onBeforeUnmount, watch } from "vue";
|
||||
import { useRouter } from "vue-router";
|
||||
import useNiagaraDataStore from "@/stores/useNiagaraDataStore";
|
||||
import useAlarmDataStore from "@/stores/useAlarmDataStore";
|
||||
|
||||
const FILE_BASEURL = import.meta.env.VITE_FILE_API_BASEURL;
|
||||
const router = useRouter();
|
||||
const niagaraStore = useNiagaraDataStore();
|
||||
const alarmDataStore = useAlarmDataStore();
|
||||
@ -56,7 +56,7 @@ onBeforeUnmount(() => {
|
||||
</template>
|
||||
<a-badge :count="totalAlarmCount" :overflow-count="999" class="!mt-3">
|
||||
<a class="flex flex-col items-center">
|
||||
<font-awesome-icon :icon="['fas', 'bell']" size="2x" />
|
||||
<img :src="`${FILE_BASEURL}/file/UI_images/icon/notify.svg`" alt="notify_icon" class="icon" />
|
||||
<span class="text-sm">告警</span>
|
||||
</a>
|
||||
</a-badge>
|
||||
|
@ -1,138 +1,22 @@
|
||||
<script setup>
|
||||
import { ref, onMounted, onUnmounted } from "vue";
|
||||
import {
|
||||
imagesWeatherDay,
|
||||
imagesWeatherNight,
|
||||
orderWeather,
|
||||
} from "@/constants";
|
||||
|
||||
const weatherStateText = ref("N/A");
|
||||
const weatherStateImage = ref(null);
|
||||
const actualWeather = ref("Clear");
|
||||
const temperature = ref("N/A");
|
||||
const humidity = ref("N/A");
|
||||
const actualNighttime = ref(false);
|
||||
const dateTime = ref("N/A");
|
||||
import { nextTick, onMounted, onUnmounted } from "vue";
|
||||
import useWeatherDataStore from "@/stores/useWeatherDataStore";
|
||||
|
||||
const weatherStore = useWeatherDataStore();
|
||||
let intervalId = null;
|
||||
let subscriber = null; // 宣告 subscriber 變數
|
||||
|
||||
// 初始化資料與訂閱更新
|
||||
const initializeData = () => {
|
||||
if (window.require && window.requirejs) {
|
||||
window.requirejs(["baja!"], (baja) => {
|
||||
subscriber = new baja.Subscriber(); // 初始化 subscriber
|
||||
subscriber.attach("changed", (prop) => {
|
||||
console.log(
|
||||
"weather",
|
||||
prop,
|
||||
prop.$getDisplayName(),
|
||||
prop.$getValue().getValueDisplay()
|
||||
);
|
||||
|
||||
// 根據 slotName 判斷資料類型
|
||||
if (prop.$slotName === "temp") {
|
||||
temperature.value = prop.$getValue().getValueDisplay();
|
||||
} else if (prop.$slotName === "humidity") {
|
||||
humidity.value = prop.$getValue().getValueDisplay();
|
||||
} else if (prop.$slotName === "state") {
|
||||
actualWeather.value = prop.$getValue().getValueDisplay();
|
||||
loadWeather(actualWeather.value); // 載入天氣資料
|
||||
} else if (prop.$slotName === "sunDown") {
|
||||
actualNighttime.value = !prop.$getValue().getValueDisplay();
|
||||
loadWeather(actualWeather.value); // 重新載入天氣資料
|
||||
}
|
||||
});
|
||||
|
||||
// 訂閱 Niagara 資料
|
||||
baja.Ord.make(
|
||||
"station:|slot:/Services/WeatherService/WeatherService/WeatherService/current"
|
||||
)
|
||||
.get({ subscriber })
|
||||
.then((result) => {
|
||||
console.log("Successfuly subscribed to weather data", result);
|
||||
})
|
||||
.catch((err) => {
|
||||
console.error(`訂閱 weather 失敗: ${err.message}`);
|
||||
subscriber.detach("changed"); // 發生錯誤時取消訂閱
|
||||
});
|
||||
});
|
||||
} else {
|
||||
console.error("未能載入 Baja 環境,請確認依賴已正確加載。");
|
||||
}
|
||||
};
|
||||
|
||||
// 載入天氣資料函數
|
||||
const loadWeather = (actualWeather) => {
|
||||
console.log(
|
||||
"actualWeather",
|
||||
actualWeather.replace(/\s/g, ""),
|
||||
actualNighttime.value
|
||||
);
|
||||
let weatherStateTextTemp = "Unknown";
|
||||
let weatherStateImageTemp = null;
|
||||
|
||||
// 找到 actualWeather 在 orderWeather 中的索引
|
||||
const actualWeatherIndex = orderWeather.indexOf(
|
||||
actualWeather.replace(/\s/g, "")
|
||||
);
|
||||
|
||||
if (actualWeatherIndex === -1) {
|
||||
// 如果找不到,表示 weather 狀態不在 orderWeather 中
|
||||
console.error("天氣狀態不在 orderWeather 陣列中!實際值:" + actualWeather);
|
||||
weatherStateTextTemp = "Unknown";
|
||||
weatherStateImageTemp = imagesWeatherDay[weatherStateTextTemp];
|
||||
} else if (actualWeather === "none") {
|
||||
console.warn("未設定天氣狀態或點不存在");
|
||||
weatherStateTextTemp = "Unknown";
|
||||
weatherStateImageTemp = imagesWeatherDay[weatherStateTextTemp];
|
||||
} else {
|
||||
if (actualNighttime.value) {
|
||||
console.log("Nighttime activ");
|
||||
weatherStateTextTemp = orderWeather[actualWeatherIndex];
|
||||
weatherStateImageTemp = imagesWeatherNight[weatherStateTextTemp];
|
||||
} else {
|
||||
console.log("Daytime activ");
|
||||
weatherStateTextTemp = orderWeather[actualWeatherIndex];
|
||||
weatherStateImageTemp = imagesWeatherDay[weatherStateTextTemp];
|
||||
}
|
||||
}
|
||||
|
||||
weatherStateImage.value = weatherStateImageTemp;
|
||||
weatherStateText.value = weatherStateTextTemp; // 更新天氣狀態
|
||||
};
|
||||
|
||||
// 時間
|
||||
const updateTime = () => {
|
||||
const date = new Date();
|
||||
if (window.require && window.requirejs) {
|
||||
window.requirejs(["baja!"], (baja) => {
|
||||
const bAbsTime = baja.AbsTime.make({ jsDate: date });
|
||||
bAbsTime
|
||||
.toDateTimeString()
|
||||
.then((dateTimeStr) => {
|
||||
dateTime.value = dateTimeStr;
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error("轉換時間字串失敗:", error);
|
||||
});
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
updateTime(); // 更新時間
|
||||
intervalId = setInterval(updateTime, 1000);
|
||||
setTimeout(() => {
|
||||
initializeData();
|
||||
}, 1000);
|
||||
onMounted(async () => {
|
||||
console.log('window.require',window.require);
|
||||
|
||||
await weatherStore.subscribeToWeather();
|
||||
// 更新時間
|
||||
weatherStore.updateTime();
|
||||
intervalId = setInterval(weatherStore.updateTime, 1000);
|
||||
});
|
||||
|
||||
onUnmounted(() => {
|
||||
clearInterval(intervalId);
|
||||
if (subscriber) {
|
||||
subscriber.detachAll(); // 取消所有訂閱
|
||||
}
|
||||
weatherStore.clearAllSubscriber();
|
||||
});
|
||||
</script>
|
||||
|
||||
@ -140,15 +24,15 @@ onUnmounted(() => {
|
||||
<div class="leading-none pt-2">
|
||||
<p class="flex items-center">
|
||||
<img
|
||||
v-if="weatherStateImage"
|
||||
:src="weatherStateImage"
|
||||
v-if="weatherStore.weatherStateImage"
|
||||
:src="weatherStore.weatherStateImage"
|
||||
alt="Weather Icon"
|
||||
style="width: 20px; height: 20px; vertical-align: middle"
|
||||
/>
|
||||
- {{ weatherStateText }}
|
||||
- {{ weatherStore.weatherStateText }}
|
||||
</p>
|
||||
<p>{{ temperature }} | {{ humidity }}</p>
|
||||
<p>{{ dateTime }}</p>
|
||||
<p>{{ weatherStore.temperature }} | {{ weatherStore.humidity }}</p>
|
||||
<p>{{ weatherStore.dateTime }}</p>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
@ -6,7 +6,7 @@ import NavWeather from "./NavWeather.vue";
|
||||
import NavAlarm from "./NavAlarm.vue";
|
||||
import useNiagaraDataStore from "@/stores/useNiagaraDataStore";
|
||||
import useNavDataStore from "@/stores/useNavDataStore";
|
||||
|
||||
const FILE_BASEURL = import.meta.env.VITE_FILE_API_BASEURL;
|
||||
const router = useRouter();
|
||||
const route = useRoute();
|
||||
const niagaraStore = useNiagaraDataStore();
|
||||
@ -163,7 +163,7 @@ watch(
|
||||
</a-menu>
|
||||
</template>
|
||||
<a class="flex flex-col items-center pt-3">
|
||||
<font-awesome-icon :icon="['fas', 'user']" size="2x" />
|
||||
<img :src="`${FILE_BASEURL}/file/UI_images/icon/user.svg`" alt="user_icon" class="icon" />
|
||||
<span class="text-sm">{{ props.userName }}</span>
|
||||
</a>
|
||||
</a-dropdown>
|
||||
@ -217,11 +217,13 @@ watch(
|
||||
width: 30px;
|
||||
height: 30px;
|
||||
vertical-align: middle;
|
||||
margin-left: 20px;
|
||||
}
|
||||
|
||||
.icon {
|
||||
width: 30px;
|
||||
height: 30px;
|
||||
vertical-align: middle;
|
||||
fill: #69B0CF;
|
||||
}
|
||||
</style>
|
||||
|
@ -56,7 +56,7 @@ const useElecStore = defineStore("elecData", () => {
|
||||
eleclist.push({
|
||||
slotPath: record.get("slotPath"),
|
||||
displayName: record.get("parent$2edisplayName"),
|
||||
id: record.get("NumericInterval$2ehistoryConfig$2eid").$cEncStr,
|
||||
id: record.get("NumericInterval$2ehistoryConfig$2eid"),
|
||||
out: record.get("out")?.get("value") ?? null,
|
||||
});
|
||||
},
|
||||
@ -77,15 +77,15 @@ const useElecStore = defineStore("elecData", () => {
|
||||
|
||||
const gettimeToHistory = (item) => {
|
||||
const id = item.id;
|
||||
const startTime = dayjs()
|
||||
const startTime = dayjs("2025-05-08T16:30:00.000+08:00")
|
||||
.subtract(13, "day")
|
||||
.startOf("day")
|
||||
.format("YYYY-MM-DDTHH:mm:ss.000+08:00");
|
||||
const endTime = dayjs()
|
||||
const endTime = dayjs("2025-05-08T16:30:00.000+08:00")
|
||||
.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
|
||||
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 &&
|
||||
@ -105,6 +105,7 @@ const useElecStore = defineStore("elecData", () => {
|
||||
const timestamp = record.get("timestamp").$cEncStr;
|
||||
if (
|
||||
currentValue !== null &&
|
||||
!isNaN(currentValue) &&
|
||||
currentValue !== 0 &&
|
||||
timestamp !== null
|
||||
) {
|
||||
@ -129,8 +130,8 @@ const useElecStore = defineStore("elecData", () => {
|
||||
// 提取今天和昨天的数据
|
||||
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");
|
||||
const today = dayjs("2025-05-08T16:30:00.000+08:00").format("YYYY-MM-DD");
|
||||
const yesterday = dayjs("2025-05-08T16:30:00.000+08:00").subtract(1, "day").format("YYYY-MM-DD");
|
||||
|
||||
if (date === today) {
|
||||
todayelecdata.value.set(timestamp, value);
|
||||
|
128
src/stores/useWeatherDataStore.js
Normal file
128
src/stores/useWeatherDataStore.js
Normal file
@ -0,0 +1,128 @@
|
||||
import { defineStore } from "pinia";
|
||||
import { ref } from "vue";
|
||||
import {
|
||||
imagesWeatherDay,
|
||||
imagesWeatherNight,
|
||||
orderWeather,
|
||||
} from "@/constants";
|
||||
|
||||
const useWeatherDataStore = defineStore("weatherData", () => {
|
||||
const weatherStateText = ref("N/A");
|
||||
const weatherStateImage = ref(null);
|
||||
const actualWeather = ref("Clear");
|
||||
const temperature = ref("N/A");
|
||||
const humidity = ref("N/A");
|
||||
const actualNighttime = ref(false);
|
||||
const dateTime = ref("N/A");
|
||||
|
||||
const subscribers = ref([]);
|
||||
const subscribeToWeather = () => {
|
||||
window.require &&
|
||||
window.requirejs(["baja!"], (baja) => {
|
||||
const subscriber = new baja.Subscriber(); // 初始化 subscriber
|
||||
subscriber.attach("changed", (prop) => {
|
||||
// 根據 slotName 判斷資料類型
|
||||
if (prop.$slotName === "temp") {
|
||||
temperature.value = prop.$getValue().getValueDisplay();
|
||||
} else if (prop.$slotName === "humidity") {
|
||||
humidity.value = prop.$getValue().getValueDisplay();
|
||||
} else if (prop.$slotName === "state") {
|
||||
actualWeather.value = prop.$getValue().getValueDisplay();
|
||||
loadWeather(actualWeather.value); // 載入天氣資料
|
||||
} else if (prop.$slotName === "sunDown") {
|
||||
actualNighttime.value = !prop.$getValue().getValueDisplay();
|
||||
loadWeather(actualWeather.value); // 重新載入天氣資料
|
||||
}
|
||||
});
|
||||
|
||||
// 訂閱 Niagara 資料
|
||||
baja.Ord.make(
|
||||
"station:|slot:/Services/WeatherService/WeatherService/WeatherService/current"
|
||||
)
|
||||
.get({ subscriber })
|
||||
.then((result) => {
|
||||
console.log("Successfuly subscribed to weather data", result);
|
||||
})
|
||||
.catch((err) => {
|
||||
console.error(`訂閱 weather 失敗: ${err.message}`);
|
||||
subscriber.detach("changed"); // 發生錯誤時取消訂閱
|
||||
});
|
||||
subscribers.value.push(subscriber);
|
||||
});
|
||||
};
|
||||
|
||||
// 載入天氣資料函數
|
||||
const loadWeather = (actualWeather) => {
|
||||
let weatherStateTextTemp = "Unknown";
|
||||
let weatherStateImageTemp = null;
|
||||
|
||||
// 找到 actualWeather 在 orderWeather 中的索引
|
||||
const actualWeatherIndex = orderWeather.indexOf(
|
||||
actualWeather.replace(/\s/g, "")
|
||||
);
|
||||
|
||||
if (actualWeatherIndex === -1) {
|
||||
// 如果找不到,表示 weather 狀態不在 orderWeather 中
|
||||
console.error(
|
||||
"天氣狀態不在 orderWeather 陣列中!實際值:" + actualWeather
|
||||
);
|
||||
weatherStateTextTemp = "Unknown";
|
||||
weatherStateImageTemp = imagesWeatherDay[weatherStateTextTemp];
|
||||
} else if (actualWeather === "none") {
|
||||
console.warn("未設定天氣狀態或點不存在");
|
||||
weatherStateTextTemp = "Unknown";
|
||||
weatherStateImageTemp = imagesWeatherDay[weatherStateTextTemp];
|
||||
} else {
|
||||
if (actualNighttime.value) {
|
||||
console.log("Nighttime activ");
|
||||
weatherStateTextTemp = orderWeather[actualWeatherIndex];
|
||||
weatherStateImageTemp = imagesWeatherNight[weatherStateTextTemp];
|
||||
} else {
|
||||
console.log("Daytime activ");
|
||||
weatherStateTextTemp = orderWeather[actualWeatherIndex];
|
||||
weatherStateImageTemp = imagesWeatherDay[weatherStateTextTemp];
|
||||
}
|
||||
}
|
||||
|
||||
weatherStateImage.value = weatherStateImageTemp;
|
||||
weatherStateText.value = weatherStateTextTemp; // 更新天氣狀態
|
||||
};
|
||||
|
||||
const updateTime = () => {
|
||||
const date = new Date();
|
||||
window.require &&
|
||||
window.requirejs(["baja!"], (baja) => {
|
||||
const bAbsTime = baja.AbsTime.make({ jsDate: date });
|
||||
bAbsTime
|
||||
.toDateTimeString()
|
||||
.then((dateTimeStr) => {
|
||||
dateTime.value = dateTimeStr;
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error("轉換時間字串失敗:", error);
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
const clearAllSubscriber = () => {
|
||||
subscribers.value.forEach((subscriber) => {
|
||||
subscriber.detach("changed");
|
||||
});
|
||||
subscribers.value = [];
|
||||
};
|
||||
|
||||
return {
|
||||
weatherStateText,
|
||||
weatherStateImage,
|
||||
actualWeather,
|
||||
temperature,
|
||||
humidity,
|
||||
actualNighttime,
|
||||
dateTime,
|
||||
subscribeToWeather,
|
||||
updateTime,
|
||||
clearAllSubscriber,
|
||||
};
|
||||
});
|
||||
|
||||
export default useWeatherDataStore;
|
@ -1,4 +1,9 @@
|
||||
@import "tailwindcss";
|
||||
:root {
|
||||
font-family:"Microsoft JhengHei", "Noto Sans CJK TC", STHeiti, sans-serif, serif;
|
||||
font-family: "Microsoft JhengHei", "Noto Sans CJK TC", STHeiti, sans-serif,
|
||||
serif;
|
||||
}
|
||||
|
||||
a {
|
||||
color: #69b0cf;
|
||||
}
|
||||
|
@ -16,25 +16,25 @@ const elecStat = ref([
|
||||
value: 0,
|
||||
label: "今日用電量",
|
||||
unit: "kWH",
|
||||
icon: "leaf",
|
||||
icon: "elec",
|
||||
},
|
||||
{
|
||||
value: 0,
|
||||
label: "昨日用電量",
|
||||
unit: "kWH",
|
||||
icon: "leaf",
|
||||
icon: "elec",
|
||||
},
|
||||
{
|
||||
value: 0,
|
||||
label: "即時功率",
|
||||
unit: "kW",
|
||||
icon: "bolt",
|
||||
icon: "power",
|
||||
},
|
||||
{
|
||||
value: 0,
|
||||
label: "契約容量佔比",
|
||||
unit: "%",
|
||||
icon: "charging-station",
|
||||
icon: "pie",
|
||||
},
|
||||
]);
|
||||
const elecDemand_P = ref(0); // 即時趨勢
|
||||
@ -43,11 +43,11 @@ const yesterdayTodayData = ref({
|
||||
categories: [],
|
||||
values: [
|
||||
{
|
||||
name: `${dayjs().format("YYYY-MM-DD")} 用電量`,
|
||||
name: `${dayjs("2025-05-08T16:30:00.000+08:00").format("YYYY-MM-DD")} 用電量`,
|
||||
value: [],
|
||||
},
|
||||
{
|
||||
name: `${dayjs().subtract(1, "day").format("YYYY-MM-DD")} 用電量`,
|
||||
name: `${dayjs("2025-05-08T16:30:00.000+08:00").subtract(1, "day").format("YYYY-MM-DD")} 用電量`,
|
||||
value: [],
|
||||
},
|
||||
],
|
||||
@ -68,7 +68,7 @@ const weekComparisonData = ref({
|
||||
});
|
||||
|
||||
const generateWeekCategories = () => {
|
||||
const today = dayjs();
|
||||
const today = dayjs("2025-05-08T16:30:00.000+08:00");
|
||||
const currentDay = today.day();
|
||||
const daysOfWeek = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"];
|
||||
const dynamicCategories = [];
|
||||
@ -91,8 +91,8 @@ watch(
|
||||
(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 today = dayjs("2025-05-08T16:30:00.000+08:00").format("YYYY-MM-DD");
|
||||
const yesterday = dayjs("2025-05-08T16:30:00.000+08:00").subtract(1, "day").format("YYYY-MM-DD");
|
||||
|
||||
const todayData = newElecData.dailyResults.find(
|
||||
(item) => item.dateStr === today
|
||||
@ -107,11 +107,11 @@ watch(
|
||||
elecStat.value = [
|
||||
{
|
||||
...elecStat.value[0],
|
||||
value: todayElecCost,
|
||||
value: todayElecCost.toFixed(2),
|
||||
},
|
||||
{
|
||||
...elecStat.value[1],
|
||||
value: yesterdayElecCost,
|
||||
value: yesterdayElecCost.toFixed(2),
|
||||
},
|
||||
{
|
||||
...elecStat.value[2],
|
||||
@ -151,8 +151,8 @@ watch(
|
||||
([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 todayDate = dayjs("2025-05-08T16:30:00.000+08:00").format("YYYY-MM-DD");
|
||||
const yesterdayDate = dayjs("2025-05-08T16:30:00.000+08:00").subtract(1, "day").format("YYYY-MM-DD");
|
||||
|
||||
const categories = [];
|
||||
const todayValues = [];
|
||||
|
Loading…
Reference in New Issue
Block a user