告警遮罩改 | 設備管理UI | 2D / 3D 顯示設定頁面初切版 | MQTT switch 顯示 初切版

This commit is contained in:
koko 2025-07-03 11:37:35 +08:00
parent 68d834aec9
commit 14e3f9ea4a
12 changed files with 134 additions and 17 deletions

29
.github/prompts/exportCSV.prompt.md vendored Normal file
View File

@ -0,0 +1,29 @@
---
mode: agent
---
# API 路徑整理與引用檢查規則
## 目標
- 針對 apis 目錄下所有子資料夾(如 account、alert、asset、building、dashboard、energy、forge、graph、history、login、operation、productSetting、system的 api.js 與 index.js 檔案,完整追蹤 API 路徑的實際引用情形。
- 追蹤流程:
1. 先在 api.js 找出所有 API 路徑常數(如 `export const GET_XXX_API = '/path'`)。
2. 在 index.js 檔案確認這些常數有被 import 並包裝成 API function`getXXX`)。
3. 再全專案搜尋這些 API function 是否有被其他檔案 import 並呼叫。
- 產生一份 csv 報表,格式如下:
| API 路徑 | 定義常數 | API function | 是否有被引用 |
|----------|----------|--------------|-------------|
| /user | GET_USER_API | getUser | Y |
| /admin | GET_ADMIN_API | getAdmin | N |
## 詳細規則
- 處理 apis 目錄下所有子資料夾的 api.js 與 index.js 檔案。
- API 路徑的定義需涵蓋 get/post/put/delete 等(如 `export const API = '/path'`)。
- 只統計有被 index.js import 並包裝成 function 的 API 路徑。
- 檢查 function 是否有被其他檔案 import 並呼叫(排除 apis 目錄本身)。
- 匹配到的檔案需記錄完整路徑,可多個檔案以分號分隔。
- 統計結果輸出為 csv 檔案。
## 輸出
- 檔名api_usage_report.csv
- 欄位API 路徑, 定義常數, API function, 是否有被引用,

View File

@ -58,6 +58,7 @@
box-sizing: border-box; box-sizing: border-box;
margin: 0; margin: 0;
font-weight: normal; font-weight: normal;
scrollbar-color: auto !important;
} }
body { body {

View File

@ -36,7 +36,7 @@ const toggleErrIcon = () => {
</div> </div>
<div <div
v-if="showErr" v-if="showErr"
class="drawer-side translate-y-20 max-h-[90vh] overflow-x-hidden overflow-y-scroll" class="drawer-side translate-y-20 max-h-[90vh] overflow-x-hidden overflow-y-scroll w-[300px] left-auto right-0"
> >
<AlarmCards /> <AlarmCards />
</div> </div>

View File

@ -15,7 +15,7 @@ const props = defineProps({
<span class="label-text-alt"> <slot name="topRight"></slot></span> <span class="label-text-alt"> <slot name="topRight"></slot></span>
</div> </div>
<textarea <textarea
class="textarea text-lg rounded-md border-info focus-within:border-info h-24" class="textarea text-lg rounded-md border-info focus-within:border-info h-40"
:placeholder="placeholder" :placeholder="placeholder"
:name="name" :name="name"
v-model="value[name]" v-model="value[name]"

View File

@ -65,7 +65,7 @@ import {
faClock, faClock,
faCheckCircle faCheckCircle
} from "@fortawesome/free-solid-svg-icons"; } from "@fortawesome/free-solid-svg-icons";
import { faCircle } from "@fortawesome/free-regular-svg-icons"; import { faCircle,faPaperPlane } from "@fortawesome/free-regular-svg-icons";
/* add icons to the library */ /* add icons to the library */
library.add( library.add(
@ -130,7 +130,8 @@ library.add(
faCrown, faCrown,
faClock, faClock,
faCheckCircle, faCheckCircle,
faCircle faCircle,
faPaperPlane
); );
export default library; export default library;

View File

@ -3,8 +3,6 @@ import { ref, inject, onBeforeMount, onMounted, watch } from "vue";
import * as yup from "yup"; import * as yup from "yup";
import "yup-phone-lite"; import "yup-phone-lite";
import useSearchParam from "@/hooks/useSearchParam"; import useSearchParam from "@/hooks/useSearchParam";
import AssetTableModalLeftInfoIoT from "./AssetTableModalLeftInfoIoT.vue";
import AssetTableModalLeftInfoGraph from "./AssetTableModalLeftInfoGraph.vue";
import AssetTableModalLeftInfoMQTT from "./AssetTableModalLeftInfoMQTT.vue"; import AssetTableModalLeftInfoMQTT from "./AssetTableModalLeftInfoMQTT.vue";
import useUserInfoStore from "@/stores/useUserInfoStore"; import useUserInfoStore from "@/stores/useUserInfoStore";
import dayjs from "dayjs"; import dayjs from "dayjs";
@ -209,8 +207,6 @@ watch(
</Select> </Select>
</div> </div>
<AssetTableModalLeftInfoMQTT /> <AssetTableModalLeftInfoMQTT />
<AssetTableModalLeftInfoGraph />
<AssetTableModalLeftInfoIoT />
</template> </template>
<style lang="scss" scoped></style> <style lang="scss" scoped></style>

View File

@ -96,9 +96,9 @@ const onCancel = () => {
</script> </script>
<template> <template>
<div class="flex w-72"> <div class="flex col-span-2 pb-5">
<Input :value="formState" name="topic"> <Input :value="formState" name="topic">
<template #topLeft>MQTT Topic</template> <template #topLeft>MQTT subscribe topic</template>
</Input> </Input>
<button type="button" class="btn btn-add mt-11 ms-1" @click="openModal"> <button type="button" class="btn btn-add mt-11 ms-1" @click="openModal">
<font-awesome-icon :icon="['fas', 'cog']" /> <font-awesome-icon :icon="['fas', 'cog']" />
@ -106,6 +106,19 @@ const onCancel = () => {
</button> </button>
</div> </div>
<div class="flex flex-col col-span-2 border-t-gray-400 border-t py-5">
<Input :value="formState" name="topic">
<template #topLeft>MQTT publish topic</template>
</Input>
<Textarea :value="formState" name="notice" class="">
<template #topLeft>MQTT 傳送的訊息</template>
</Textarea>
<button type="button" class="btn btn-add mt-6 w-24" @click="openModal">
<font-awesome-icon :icon="['far', 'paper-plane']" />
Send
</button>
</div>
<Modal id="mqtt_test" title="MQTT Topic" :onCancel="onCancel" :width="400"> <Modal id="mqtt_test" title="MQTT Topic" :onCancel="onCancel" :width="400">
<template #modalContent> <template #modalContent>
<!-- 顯示接收到的訊息 --> <!-- 顯示接收到的訊息 -->

View File

@ -2,6 +2,8 @@
import { onMounted, ref, inject, onBeforeMount, watch, computed } from "vue"; import { onMounted, ref, inject, onBeforeMount, watch, computed } from "vue";
import EffectScatter from "@/components/chart/EffectScatter.vue"; import EffectScatter from "@/components/chart/EffectScatter.vue";
import useBuildingStore from "@/stores/useBuildingStore"; import useBuildingStore from "@/stores/useBuildingStore";
import AssetTableModalLeftInfoIoT from "./AssetTableModalLeftInfoIoT.vue";
import AssetTableModalLeftInfoGraph from "./AssetTableModalLeftInfoGraph.vue";
import * as yup from "yup"; import * as yup from "yup";
import { twMerge } from "tailwind-merge"; import { twMerge } from "tailwind-merge";
import { useI18n } from "vue-i18n"; import { useI18n } from "vue-i18n";
@ -115,7 +117,7 @@ const getCoordinate = (position) => {
<template> <template>
<!-- 平面圖 --> <!-- 平面圖 -->
<div class="flex gap-4 mb-5"> <div class="flex gap-4 mb-5">
<Select <Select
:value="formState" :value="formState"
@ -143,7 +145,7 @@ const getCoordinate = (position) => {
></Input ></Input
> >
</div> </div>
<div class="relative"> <div class="relative min-h-[70vh]">
<EffectScatter <EffectScatter
id="asset_floor_chart" id="asset_floor_chart"
ref="asset_floor_chart" ref="asset_floor_chart"
@ -157,11 +159,13 @@ const getCoordinate = (position) => {
/> />
<div <div
v-if="!currentFloor?.floor_map_url" v-if="!currentFloor?.floor_map_url"
class="absolute top-0 left-0 flex justify-center items-center min-h-[500px] w-full border border-stone-900 shadow-lg bg-sub-success bg-opacity-25 rounded-md" class="absolute top-0 left-0 flex justify-center items-center min-h-[70vh] w-full border border-stone-900 shadow-lg bg-sub-success bg-opacity-25 rounded-md"
> >
<p class="text-2xl">{{ $t("assetManagement.add_floor_text") }}</p> <p class="text-2xl">{{ $t("assetManagement.add_floor_text") }}</p>
</div> </div>
</div> </div>
<AssetTableModalLeftInfoGraph />
<AssetTableModalLeftInfoIoT />
</template> </template>
<style lang="scss" scoped></style> <style lang="scss" scoped></style>

View File

@ -10,6 +10,7 @@ import Building from "./components/Building.vue";
import ElecPriceManagement from "./components/ElecPriceManagement.vue"; import ElecPriceManagement from "./components/ElecPriceManagement.vue";
import MQTTList from "./components/MQTTList.vue"; import MQTTList from "./components/MQTTList.vue";
import Demand from "./components/Demand.vue"; import Demand from "./components/Demand.vue";
import ViewModeSetting from "./components/ViewModeSetting.vue";
// import PointList from "./components/PointList.vue"; // import PointList from "./components/PointList.vue";
const route = useRoute(); const route = useRoute();
@ -33,7 +34,9 @@ const updateComponent = () => {
} else if (sub_system_id === "MQTT_Result") { } else if (sub_system_id === "MQTT_Result") {
currentComponent.value = markRaw(MQTTList); currentComponent.value = markRaw(MQTTList);
} else if (sub_system_id === "Demand") { } else if (sub_system_id === "Demand") {
currentComponent.value = markRaw(Demand); currentComponent.value = markRaw(Demand);
} else if (sub_system_id === "ViewModeSetting") {
currentComponent.value = markRaw(ViewModeSetting);
} }
}; };

View File

@ -151,6 +151,34 @@ watch(
> >
<template #topLeft>{{ $t("setting.hide_point") }}</template> <template #topLeft>{{ $t("setting.hide_point") }}</template>
</RadioGroup> </RadioGroup>
<RadioGroup
class="my-2"
name="is_link"
:value="formState"
:items="[
{
key: 1,
value: 1,
title: '開啟',
},
{
key: 0,
value: 0,
title: '關閉',
},
]"
:required="true"
>
<template #topLeft>switch 顯示</template>
</RadioGroup>
<Textarea :value="formState" name="notice" class="w-full my-2">
<template #topLeft>switch 開啟時傳送的訊息</template>
</Textarea>
<Textarea :value="formState" name="notice" class="w-full my-2">
<template #topLeft>switch 關閉時傳送的訊息</template>
</Textarea>
</form> </form>
</template> </template>
<template #modalAction> <template #modalAction>

View File

@ -0,0 +1,41 @@
<script setup>
import { onMounted, ref, inject, computed } from "vue";
import { useI18n } from "vue-i18n";
const { t } = useI18n();
const FILE_BASEURL = import.meta.env.VITE_FILE_API_BASEURL;
const formState = ref({
lorf: [],
});
const updateFileList = (files) => {
formState.value.lorf = files;
};
</script>
<template>
<div class="flex justify-start items-center mt-10 mb-5">
<h3 class="text-xl mr-5">2D / 3D 顯示設定 :</h3>
</div>
<div class="flex gap-5">
<span class="text-lg">3D 模型顯示 : </span
><input
type="checkbox"
className="toggle toggle-lg toggle-success"
defaultChecked
/>
</div>
<Upload
class="my-2 max-w-[600px] w-full"
name="oriFile"
:fileList="formState?.lorf"
:getFileList="updateFileList"
:multiple="true"
:baseUrl="`${FILE_BASEURL}/upload/operation`"
formats="png、jpg"
>
<template #topLeft>首頁2D圖上傳 :</template>
</Upload>
</template>
<style lang="css" scoped></style>

View File

@ -36,8 +36,9 @@ const groupedData = computed(() => {
return grouped; return grouped;
}); });
const togglePowerSwitch = () => { const togglePowerSwitch = (e, val) => {
console.log("Power Switch"); const isChecked = e.target.checked;
console.log("Power Switch", e, val, "狀態:", isChecked);
}; };
</script> </script>
@ -73,7 +74,7 @@ const togglePowerSwitch = () => {
type="checkbox" type="checkbox"
class="toggle toggle-success" class="toggle toggle-success"
:checked="group.value" :checked="group.value"
@change="togglePowerSwitch()" @change="togglePowerSwitch($event, group.value)"
/> />
</template> </template>
<template v-else> <template v-else>