框架UI修改
This commit is contained in:
parent
48dd8d62bb
commit
704af2eb18
@ -1,3 +1,4 @@
|
|||||||
{
|
{
|
||||||
"systemName": "關渡醫院中央監控"
|
"systemName": "關渡醫院中央監控",
|
||||||
|
"is3D": true
|
||||||
}
|
}
|
||||||
|
17
src/App.vue
17
src/App.vue
@ -8,7 +8,7 @@ import useNiagaraDataStore from "@/stores/useNiagaraDataStore";
|
|||||||
import useNavDataStore from "@/stores/useNavDataStore";
|
import useNavDataStore from "@/stores/useNavDataStore";
|
||||||
|
|
||||||
const showSidebar = ref(false);
|
const showSidebar = ref(false);
|
||||||
const systemName = ref("");
|
const is3D = ref(false);
|
||||||
const userStore = useUserStore();
|
const userStore = useUserStore();
|
||||||
const niagaraStore = useNiagaraDataStore();
|
const niagaraStore = useNiagaraDataStore();
|
||||||
const navStore = useNavDataStore();
|
const navStore = useNavDataStore();
|
||||||
@ -20,25 +20,20 @@ const toggleSidebar = () => {
|
|||||||
onMounted(async () => {
|
onMounted(async () => {
|
||||||
const json = await fetch("./config.json");
|
const json = await fetch("./config.json");
|
||||||
const res = await json.json();
|
const res = await json.json();
|
||||||
systemName.value = res.systemName;
|
is3D.value = res.is3D;
|
||||||
|
|
||||||
await userStore.loadUserInfo();
|
await userStore.loadUserInfo();
|
||||||
await niagaraStore.getSystemData();
|
await niagaraStore.getSystemData();
|
||||||
await navStore.getNavData();
|
await navStore.getNavData();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
provide("app_config", { is3D });
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<a-layout class="w-full" style="min-height: 100vh">
|
<a-layout class="w-full" style="min-height: 100vh">
|
||||||
<LeftSidebar
|
<LeftSidebar :showSidebar="showSidebar" :toggleSidebar="toggleSidebar" />
|
||||||
:showSidebar="showSidebar"
|
<Navbar :open="toggleSidebar" :userName="userStore.userName" />
|
||||||
:toggleSidebar="toggleSidebar"
|
|
||||||
/>
|
|
||||||
<Navbar
|
|
||||||
:systemName="systemName"
|
|
||||||
:open="toggleSidebar"
|
|
||||||
:userName="userStore.userName"
|
|
||||||
/>
|
|
||||||
<a-layout-content
|
<a-layout-content
|
||||||
class="overflow-x-hidden"
|
class="overflow-x-hidden"
|
||||||
:style="{
|
:style="{
|
||||||
|
@ -1,14 +1,21 @@
|
|||||||
<script setup>
|
<script setup>
|
||||||
import { ref } from "vue";
|
import { ref, inject } from "vue";
|
||||||
import Forge from "@/components/forge/Forge.vue";
|
import Forge from "@/components/forge/Forge.vue";
|
||||||
import { twMerge } from "tailwind-merge";
|
import { twMerge } from "tailwind-merge";
|
||||||
const FILE_BASEURL = import.meta.env.VITE_FILE_API_BASEURL;
|
const FILE_BASEURL = import.meta.env.VITE_FILE_API_BASEURL;
|
||||||
const type = ref("3d");
|
const type = ref("2d");
|
||||||
|
|
||||||
|
const { is3D } = inject("app_config");
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<a-card class="card">
|
<a-card
|
||||||
<div class="w-full relative">
|
class="card h-full"
|
||||||
|
:bodyStyle="{
|
||||||
|
height: '100%',
|
||||||
|
}"
|
||||||
|
>
|
||||||
|
<div :class="twMerge('w-full relative', is3D ? '' : 'h-full')">
|
||||||
<img
|
<img
|
||||||
alt="build"
|
alt="build"
|
||||||
:src="`${FILE_BASEURL}/file/UI_images/build/2D/build.jpg`"
|
: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'
|
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
|
<Forge
|
||||||
:class="
|
:class="
|
||||||
@ -29,32 +36,18 @@ const type = ref("3d");
|
|||||||
"
|
"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex items-center justify-between mt-2">
|
<div class="mt-2" v-if="is3D">
|
||||||
<div>
|
<div class="flex items-center justify-between">
|
||||||
<h5>Building</h5>
|
<h5>智慧建築</h5>
|
||||||
<p class="text-gray-400">
|
|
||||||
掌握建築用電、系統健康狀態,打造智慧節能醫院
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
<a-radio-group v-model:value="type">
|
<a-radio-group v-model:value="type">
|
||||||
<a-radio-button value="2d">2D</a-radio-button>
|
<a-radio-button value="2d">2D</a-radio-button>
|
||||||
<a-radio-button value="3d">3D</a-radio-button>
|
<a-radio-button value="3d">3D</a-radio-button>
|
||||||
</a-radio-group>
|
</a-radio-group>
|
||||||
</div>
|
</div>
|
||||||
<!-- <a-row>
|
<p class="intro text-gray-400">
|
||||||
<a-col :span="6">
|
掌握建築用電、系統健康狀態,打造智慧節能醫院
|
||||||
<a-statistic title="用戶數" :value="3.6" suffix="萬" />
|
</p>
|
||||||
</a-col>
|
</div>
|
||||||
<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>
|
</a-card>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@ -66,7 +59,11 @@ const type = ref("3d");
|
|||||||
h5 {
|
h5 {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
font-weight: 700;
|
font-weight: 700;
|
||||||
font-size: 18px;
|
font-size: 33px;
|
||||||
color: #141414;
|
color: #141414;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.intro {
|
||||||
|
font-size: 1.125rem;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
@ -93,9 +93,9 @@ const generateCylinderChartOption = (data) => {
|
|||||||
x2: 1,
|
x2: 1,
|
||||||
y2: 0,
|
y2: 0,
|
||||||
colorStops: [
|
colorStops: [
|
||||||
{ offset: 0, color: "#1890ff" }, // 左側顏色
|
{ offset: 0, color: "#69B0CF" }, // 左側顏色
|
||||||
{ offset: 0.5, color: "#acd7e4" }, // 中間顏色
|
{ offset: 0.5, color: "#acd7e4" }, // 中間顏色
|
||||||
{ offset: 1, color: "#1890ff" }, // 右側顏色
|
{ offset: 1, color: "#69B0CF" }, // 右側顏色
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
const color2 = {
|
const color2 = {
|
||||||
@ -106,9 +106,9 @@ const generateCylinderChartOption = (data) => {
|
|||||||
x2: 1,
|
x2: 1,
|
||||||
y2: 0,
|
y2: 0,
|
||||||
colorStops: [
|
colorStops: [
|
||||||
{ offset: 0, color: "#91cc75" }, // 左側顏色
|
{ offset: 0, color: "#E7F5A7" }, // 左側顏色
|
||||||
{ offset: 0.5, color: "#d2f0c2" }, // 中間顏色
|
{ offset: 0.5, color: "#ebffe0" }, // 中間顏色
|
||||||
{ offset: 1, color: "#91cc75" }, // 右側顏色
|
{ offset: 1, color: "#E7F5A7" }, // 右側顏色
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -190,9 +190,9 @@ const generateCylinderChartOption = (data) => {
|
|||||||
symbolPosition: "end",
|
symbolPosition: "end",
|
||||||
data: data.values[1].value,
|
data: data.values[1].value,
|
||||||
itemStyle: {
|
itemStyle: {
|
||||||
color: "#d2f0c2",
|
color: "#ebffe0",
|
||||||
borderWidth: 1,
|
borderWidth: 1,
|
||||||
borderColor: "#91cc75",
|
borderColor: "#E7F5A7",
|
||||||
borderType: "solid",
|
borderType: "solid",
|
||||||
},
|
},
|
||||||
z: 12,
|
z: 12,
|
||||||
@ -207,7 +207,7 @@ const generateCylinderChartOption = (data) => {
|
|||||||
data: data.values[0].value,
|
data: data.values[0].value,
|
||||||
z: 12,
|
z: 12,
|
||||||
itemStyle: {
|
itemStyle: {
|
||||||
color: "#1890ff",
|
color: "#69B0CF",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -218,7 +218,7 @@ const generateCylinderChartOption = (data) => {
|
|||||||
symbolPosition: "start",
|
symbolPosition: "start",
|
||||||
data: data.values[1].value,
|
data: data.values[1].value,
|
||||||
itemStyle: {
|
itemStyle: {
|
||||||
color: "#91cc75",
|
color: "#E7F5A7",
|
||||||
},
|
},
|
||||||
z: 12,
|
z: 12,
|
||||||
},
|
},
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
<script setup>
|
<script setup>
|
||||||
import { defineProps } from "vue";
|
import { defineProps } from "vue";
|
||||||
|
const FILE_BASEURL = import.meta.env.VITE_FILE_API_BASEURL;
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
elecData: {
|
elecData: {
|
||||||
type: Object,
|
type: Object,
|
||||||
@ -20,7 +21,7 @@ const props = defineProps({
|
|||||||
</a-col>
|
</a-col>
|
||||||
<a-col :span="6" class="pl-0">
|
<a-col :span="6" class="pl-0">
|
||||||
<div class="icon-box">
|
<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>
|
</div>
|
||||||
</a-col>
|
</a-col>
|
||||||
</a-row>
|
</a-row>
|
||||||
@ -52,11 +53,5 @@ const props = defineProps({
|
|||||||
.icon-box {
|
.icon-box {
|
||||||
width: 48px;
|
width: 48px;
|
||||||
height: 48px;
|
height: 48px;
|
||||||
text-align: center;
|
|
||||||
background: #1890ff;
|
|
||||||
color: #fff;
|
|
||||||
border-radius: 0.5rem;
|
|
||||||
margin-left: auto;
|
|
||||||
line-height: 55px;
|
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
@ -47,8 +47,9 @@ const handleClick = (ord) => {
|
|||||||
router.push({
|
router.push({
|
||||||
name: "baja",
|
name: "baja",
|
||||||
query: {
|
query: {
|
||||||
pagename: 'system',
|
pagename: "system",
|
||||||
ord: encodeURIComponent(ord) },
|
ord: encodeURIComponent(ord),
|
||||||
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -65,7 +66,7 @@ const handleClick = (ord) => {
|
|||||||
<a-col :span="6" v-for="item in flattenedItems" :key="item.key">
|
<a-col :span="6" v-for="item in flattenedItems" :key="item.key">
|
||||||
<a-card
|
<a-card
|
||||||
@click="handleClick(item.ord)"
|
@click="handleClick(item.ord)"
|
||||||
class="shadow"
|
class="shadow inner-card"
|
||||||
:bodyStyle="{
|
:bodyStyle="{
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
@ -79,7 +80,7 @@ const handleClick = (ord) => {
|
|||||||
alt="Icon"
|
alt="Icon"
|
||||||
style="width: 30px; height: 30px; vertical-align: middle"
|
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-card>
|
||||||
</a-col>
|
</a-col>
|
||||||
</a-row>
|
</a-row>
|
||||||
@ -92,6 +93,9 @@ const handleClick = (ord) => {
|
|||||||
.card {
|
.card {
|
||||||
box-shadow: 0 20px 27px rgb(0 0 0 / 5%);
|
box-shadow: 0 20px 27px rgb(0 0 0 / 5%);
|
||||||
}
|
}
|
||||||
|
.inner-card {
|
||||||
|
background-color: #69b0cf;
|
||||||
|
}
|
||||||
h5 {
|
h5 {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
font-weight: 700;
|
font-weight: 700;
|
||||||
|
@ -18,12 +18,11 @@ const preOpenKeys = ref([]); // 用於儲存之前展開的 submenu
|
|||||||
|
|
||||||
const filteredItems = computed(() => {
|
const filteredItems = computed(() => {
|
||||||
if (navStore.menuList && navStore.menuList.length > 0) {
|
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;
|
return navStore.menuList[0].children;
|
||||||
}else{
|
} else {
|
||||||
return navStore.menuList[0].children[0].children;
|
return navStore.menuList[0].children[0].children;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
return [];
|
return [];
|
||||||
});
|
});
|
||||||
@ -32,8 +31,7 @@ const handleClick = (ord) => {
|
|||||||
if (ord) {
|
if (ord) {
|
||||||
router.push({
|
router.push({
|
||||||
name: "baja",
|
name: "baja",
|
||||||
query: { pagename: 'system',
|
query: { pagename: "system", ord: encodeURIComponent(ord) },
|
||||||
ord: encodeURIComponent(ord) },
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -55,9 +53,12 @@ watch(
|
|||||||
:closable="false"
|
:closable="false"
|
||||||
placement="left"
|
placement="left"
|
||||||
width="300"
|
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">
|
<template v-for="(item, index) in filteredItems" :key="index">
|
||||||
<a-menu-item
|
<a-menu-item
|
||||||
v-if="!item.children"
|
v-if="!item.children"
|
||||||
@ -94,3 +95,20 @@ watch(
|
|||||||
</a-menu>
|
</a-menu>
|
||||||
</a-drawer>
|
</a-drawer>
|
||||||
</template>
|
</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 { useRouter } from "vue-router";
|
||||||
import useNiagaraDataStore from "@/stores/useNiagaraDataStore";
|
import useNiagaraDataStore from "@/stores/useNiagaraDataStore";
|
||||||
import useAlarmDataStore from "@/stores/useAlarmDataStore";
|
import useAlarmDataStore from "@/stores/useAlarmDataStore";
|
||||||
|
const FILE_BASEURL = import.meta.env.VITE_FILE_API_BASEURL;
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const niagaraStore = useNiagaraDataStore();
|
const niagaraStore = useNiagaraDataStore();
|
||||||
const alarmDataStore = useAlarmDataStore();
|
const alarmDataStore = useAlarmDataStore();
|
||||||
@ -56,7 +56,7 @@ onBeforeUnmount(() => {
|
|||||||
</template>
|
</template>
|
||||||
<a-badge :count="totalAlarmCount" :overflow-count="999" class="!mt-3">
|
<a-badge :count="totalAlarmCount" :overflow-count="999" class="!mt-3">
|
||||||
<a class="flex flex-col items-center">
|
<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>
|
<span class="text-sm">告警</span>
|
||||||
</a>
|
</a>
|
||||||
</a-badge>
|
</a-badge>
|
||||||
|
@ -1,138 +1,22 @@
|
|||||||
<script setup>
|
<script setup>
|
||||||
import { ref, onMounted, onUnmounted } from "vue";
|
import { nextTick, onMounted, onUnmounted } from "vue";
|
||||||
import {
|
import useWeatherDataStore from "@/stores/useWeatherDataStore";
|
||||||
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");
|
|
||||||
|
|
||||||
|
const weatherStore = useWeatherDataStore();
|
||||||
let intervalId = null;
|
let intervalId = null;
|
||||||
let subscriber = null; // 宣告 subscriber 變數
|
|
||||||
|
|
||||||
// 初始化資料與訂閱更新
|
onMounted(async () => {
|
||||||
const initializeData = () => {
|
console.log('window.require',window.require);
|
||||||
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 判斷資料類型
|
await weatherStore.subscribeToWeather();
|
||||||
if (prop.$slotName === "temp") {
|
// 更新時間
|
||||||
temperature.value = prop.$getValue().getValueDisplay();
|
weatherStore.updateTime();
|
||||||
} else if (prop.$slotName === "humidity") {
|
intervalId = setInterval(weatherStore.updateTime, 1000);
|
||||||
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);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
onUnmounted(() => {
|
onUnmounted(() => {
|
||||||
clearInterval(intervalId);
|
clearInterval(intervalId);
|
||||||
if (subscriber) {
|
weatherStore.clearAllSubscriber();
|
||||||
subscriber.detachAll(); // 取消所有訂閱
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
@ -140,15 +24,15 @@ onUnmounted(() => {
|
|||||||
<div class="leading-none pt-2">
|
<div class="leading-none pt-2">
|
||||||
<p class="flex items-center">
|
<p class="flex items-center">
|
||||||
<img
|
<img
|
||||||
v-if="weatherStateImage"
|
v-if="weatherStore.weatherStateImage"
|
||||||
:src="weatherStateImage"
|
:src="weatherStore.weatherStateImage"
|
||||||
alt="Weather Icon"
|
alt="Weather Icon"
|
||||||
style="width: 20px; height: 20px; vertical-align: middle"
|
style="width: 20px; height: 20px; vertical-align: middle"
|
||||||
/>
|
/>
|
||||||
- {{ weatherStateText }}
|
- {{ weatherStore.weatherStateText }}
|
||||||
</p>
|
</p>
|
||||||
<p>{{ temperature }} | {{ humidity }}</p>
|
<p>{{ weatherStore.temperature }} | {{ weatherStore.humidity }}</p>
|
||||||
<p>{{ dateTime }}</p>
|
<p>{{ weatherStore.dateTime }}</p>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
@ -6,7 +6,7 @@ import NavWeather from "./NavWeather.vue";
|
|||||||
import NavAlarm from "./NavAlarm.vue";
|
import NavAlarm from "./NavAlarm.vue";
|
||||||
import useNiagaraDataStore from "@/stores/useNiagaraDataStore";
|
import useNiagaraDataStore from "@/stores/useNiagaraDataStore";
|
||||||
import useNavDataStore from "@/stores/useNavDataStore";
|
import useNavDataStore from "@/stores/useNavDataStore";
|
||||||
|
const FILE_BASEURL = import.meta.env.VITE_FILE_API_BASEURL;
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const route = useRoute();
|
const route = useRoute();
|
||||||
const niagaraStore = useNiagaraDataStore();
|
const niagaraStore = useNiagaraDataStore();
|
||||||
@ -163,7 +163,7 @@ watch(
|
|||||||
</a-menu>
|
</a-menu>
|
||||||
</template>
|
</template>
|
||||||
<a class="flex flex-col items-center pt-3">
|
<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>
|
<span class="text-sm">{{ props.userName }}</span>
|
||||||
</a>
|
</a>
|
||||||
</a-dropdown>
|
</a-dropdown>
|
||||||
@ -217,11 +217,13 @@ watch(
|
|||||||
width: 30px;
|
width: 30px;
|
||||||
height: 30px;
|
height: 30px;
|
||||||
vertical-align: middle;
|
vertical-align: middle;
|
||||||
|
margin-left: 20px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.icon {
|
.icon {
|
||||||
width: 30px;
|
width: 30px;
|
||||||
height: 30px;
|
height: 30px;
|
||||||
vertical-align: middle;
|
vertical-align: middle;
|
||||||
|
fill: #69B0CF;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
@ -56,7 +56,7 @@ const useElecStore = defineStore("elecData", () => {
|
|||||||
eleclist.push({
|
eleclist.push({
|
||||||
slotPath: record.get("slotPath"),
|
slotPath: record.get("slotPath"),
|
||||||
displayName: record.get("parent$2edisplayName"),
|
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,
|
out: record.get("out")?.get("value") ?? null,
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
@ -77,15 +77,15 @@ const useElecStore = defineStore("elecData", () => {
|
|||||||
|
|
||||||
const gettimeToHistory = (item) => {
|
const gettimeToHistory = (item) => {
|
||||||
const id = item.id;
|
const id = item.id;
|
||||||
const startTime = dayjs()
|
const startTime = dayjs("2025-05-08T16:30:00.000+08:00")
|
||||||
.subtract(13, "day")
|
.subtract(13, "day")
|
||||||
.startOf("day")
|
.startOf("day")
|
||||||
.format("YYYY-MM-DDTHH:mm:ss.000+08:00");
|
.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")
|
.endOf("day")
|
||||||
.format("YYYY-MM-DDTHH:mm:ss.000+08:00");
|
.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);
|
console.log(ordString);
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
window.require &&
|
window.require &&
|
||||||
@ -105,6 +105,7 @@ const useElecStore = defineStore("elecData", () => {
|
|||||||
const timestamp = record.get("timestamp").$cEncStr;
|
const timestamp = record.get("timestamp").$cEncStr;
|
||||||
if (
|
if (
|
||||||
currentValue !== null &&
|
currentValue !== null &&
|
||||||
|
!isNaN(currentValue) &&
|
||||||
currentValue !== 0 &&
|
currentValue !== 0 &&
|
||||||
timestamp !== null
|
timestamp !== null
|
||||||
) {
|
) {
|
||||||
@ -129,8 +130,8 @@ const useElecStore = defineStore("elecData", () => {
|
|||||||
// 提取今天和昨天的数据
|
// 提取今天和昨天的数据
|
||||||
for (const [timestamp, value] of dataMap) {
|
for (const [timestamp, value] of dataMap) {
|
||||||
const date = dayjs(timestamp).format("YYYY-MM-DD");
|
const date = dayjs(timestamp).format("YYYY-MM-DD");
|
||||||
const today = dayjs().format("YYYY-MM-DD");
|
const today = dayjs("2025-05-08T16:30:00.000+08:00").format("YYYY-MM-DD");
|
||||||
const yesterday = dayjs().subtract(1, "day").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) {
|
if (date === today) {
|
||||||
todayelecdata.value.set(timestamp, value);
|
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";
|
@import "tailwindcss";
|
||||||
:root {
|
: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,
|
value: 0,
|
||||||
label: "今日用電量",
|
label: "今日用電量",
|
||||||
unit: "kWH",
|
unit: "kWH",
|
||||||
icon: "leaf",
|
icon: "elec",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
value: 0,
|
value: 0,
|
||||||
label: "昨日用電量",
|
label: "昨日用電量",
|
||||||
unit: "kWH",
|
unit: "kWH",
|
||||||
icon: "leaf",
|
icon: "elec",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
value: 0,
|
value: 0,
|
||||||
label: "即時功率",
|
label: "即時功率",
|
||||||
unit: "kW",
|
unit: "kW",
|
||||||
icon: "bolt",
|
icon: "power",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
value: 0,
|
value: 0,
|
||||||
label: "契約容量佔比",
|
label: "契約容量佔比",
|
||||||
unit: "%",
|
unit: "%",
|
||||||
icon: "charging-station",
|
icon: "pie",
|
||||||
},
|
},
|
||||||
]);
|
]);
|
||||||
const elecDemand_P = ref(0); // 即時趨勢
|
const elecDemand_P = ref(0); // 即時趨勢
|
||||||
@ -43,11 +43,11 @@ const yesterdayTodayData = ref({
|
|||||||
categories: [],
|
categories: [],
|
||||||
values: [
|
values: [
|
||||||
{
|
{
|
||||||
name: `${dayjs().format("YYYY-MM-DD")} 用電量`,
|
name: `${dayjs("2025-05-08T16:30:00.000+08:00").format("YYYY-MM-DD")} 用電量`,
|
||||||
value: [],
|
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: [],
|
value: [],
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
@ -68,7 +68,7 @@ const weekComparisonData = ref({
|
|||||||
});
|
});
|
||||||
|
|
||||||
const generateWeekCategories = () => {
|
const generateWeekCategories = () => {
|
||||||
const today = dayjs();
|
const today = dayjs("2025-05-08T16:30:00.000+08:00");
|
||||||
const currentDay = today.day();
|
const currentDay = today.day();
|
||||||
const daysOfWeek = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"];
|
const daysOfWeek = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"];
|
||||||
const dynamicCategories = [];
|
const dynamicCategories = [];
|
||||||
@ -91,8 +91,8 @@ watch(
|
|||||||
(newElecData) => {
|
(newElecData) => {
|
||||||
console.log("elecStandCostSummary", newElecData);
|
console.log("elecStandCostSummary", newElecData);
|
||||||
if (newElecData && newElecData.dailyResults) {
|
if (newElecData && newElecData.dailyResults) {
|
||||||
const today = dayjs().format("YYYY-MM-DD");
|
const today = dayjs("2025-05-08T16:30:00.000+08:00").format("YYYY-MM-DD");
|
||||||
const yesterday = dayjs().subtract(1, "day").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(
|
const todayData = newElecData.dailyResults.find(
|
||||||
(item) => item.dateStr === today
|
(item) => item.dateStr === today
|
||||||
@ -107,11 +107,11 @@ watch(
|
|||||||
elecStat.value = [
|
elecStat.value = [
|
||||||
{
|
{
|
||||||
...elecStat.value[0],
|
...elecStat.value[0],
|
||||||
value: todayElecCost,
|
value: todayElecCost.toFixed(2),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
...elecStat.value[1],
|
...elecStat.value[1],
|
||||||
value: yesterdayElecCost,
|
value: yesterdayElecCost.toFixed(2),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
...elecStat.value[2],
|
...elecStat.value[2],
|
||||||
@ -151,8 +151,8 @@ watch(
|
|||||||
([newTodayData, newYesterdayData]) => {
|
([newTodayData, newYesterdayData]) => {
|
||||||
console.log("todayyesterday", newTodayData, newYesterdayData);
|
console.log("todayyesterday", newTodayData, newYesterdayData);
|
||||||
|
|
||||||
const todayDate = dayjs().format("YYYY-MM-DD");
|
const todayDate = dayjs("2025-05-08T16:30:00.000+08:00").format("YYYY-MM-DD");
|
||||||
const yesterdayDate = dayjs().subtract(1, "day").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 categories = [];
|
||||||
const todayValues = [];
|
const todayValues = [];
|
||||||
|
Loading…
Reference in New Issue
Block a user