navbar、告警設定、資產管理、運維管理、mqtt 加上building_guid | 資產管理小類能上傳圖片 | 首頁顯示小類icon | 能源管理 : 即時需量串接api 、 圖表初始化設定、新增日期與時間區間 | 系統監控加上部門
This commit is contained in:
parent
8b85e2d67c
commit
c8e00421b2
@ -65,8 +65,12 @@ export const postOperationRecord = async (formData) => {
|
||||
});
|
||||
};
|
||||
|
||||
export const getAlertSubList = async () => {
|
||||
const res = await instance.post(GET_ALERT_SUB_LIST_API, {});
|
||||
export const getAlertSubList = async (
|
||||
building_guid
|
||||
) => {
|
||||
const res = await instance.post(GET_ALERT_SUB_LIST_API, {
|
||||
building_guid
|
||||
});
|
||||
|
||||
return apihandler(res.code, res.data, {
|
||||
msg: res.msg,
|
||||
|
@ -31,8 +31,8 @@ import instance from "@/util/request";
|
||||
import apihandler from "@/util/apihandler";
|
||||
import { object } from "yup";
|
||||
|
||||
export const getAssetMainList = async () => {
|
||||
const res = await instance.post(GET_ASSET_MAIN_LIST_API);
|
||||
export const getAssetMainList = async (building_guid) => {
|
||||
const res = await instance.post(GET_ASSET_MAIN_LIST_API,{building_guid});
|
||||
|
||||
return apihandler(res.code, res.data, {
|
||||
msg: res.msg,
|
||||
@ -49,11 +49,12 @@ export const deleteAssetMainItem = async (id) => {
|
||||
});
|
||||
};
|
||||
|
||||
export const postAssetMainList = async ({ id, system_key, system_value }) => {
|
||||
export const postAssetMainList = async ({ id, system_key, system_value, building_guid }) => {
|
||||
const res = await instance.post(POST_ASSET_MAIN_LIST_API, {
|
||||
id,
|
||||
system_key,
|
||||
system_value,
|
||||
building_guid
|
||||
});
|
||||
|
||||
return apihandler(res.code, res.data, {
|
||||
@ -71,18 +72,8 @@ export const getAssetSubList = async (id) => {
|
||||
});
|
||||
};
|
||||
|
||||
export const postAssetSubList = async ({
|
||||
system_key,
|
||||
system_value,
|
||||
system_parent_id,
|
||||
id,
|
||||
}) => {
|
||||
const res = await instance.post(POST_ASSET_SUB_LIST_API, {
|
||||
system_key,
|
||||
system_value,
|
||||
system_parent_id,
|
||||
id,
|
||||
});
|
||||
export const postAssetSubList = async (formData) => {
|
||||
const res = await instance.post(POST_ASSET_SUB_LIST_API, formData);
|
||||
|
||||
return apihandler(res.code, res.data, {
|
||||
msg: res.msg,
|
||||
|
@ -49,10 +49,8 @@ export const getAuth = async (lang) => {
|
||||
});
|
||||
};
|
||||
|
||||
export const getAllSysSidebar = async () => {
|
||||
const res = await instance.post(GET_SUBAUTHPAGE_API, {
|
||||
building_tag: "",
|
||||
});
|
||||
export const getAllSysSidebar = async (building_guid) => {
|
||||
const res = await instance.post(GET_SUBAUTHPAGE_API, {building_guid});
|
||||
return apihandler(res.code, res.data, {
|
||||
msg: res.msg,
|
||||
code: res.code,
|
||||
|
@ -12,6 +12,7 @@ export const GET_Excel_API = `/api/Energe/GetReportExcel`;
|
||||
export const GET_DEMAND_API = `/api/Energe/SearchDemandValue`;
|
||||
export const POST_ADD_DEMAND_API = `/api/Energe/AddDemandValue`;
|
||||
export const POST_EDIT_DEMAND_API = `/api/Energe/UpdateDemandValue`;
|
||||
export const GET_REALTIME_DEMAND_API = `/api/Energe/GetRealTimeDemand`;
|
||||
|
||||
// 碳排係數
|
||||
export const GET_CARBON_API = `/api/Energe/SearchCarbonValue`;
|
||||
|
@ -8,6 +8,7 @@ import {
|
||||
GET_Excel_API,
|
||||
GET_DEMAND_API,
|
||||
POST_EDIT_DEMAND_API,
|
||||
GET_REALTIME_DEMAND_API,
|
||||
GET_CARBON_API,
|
||||
POST_EDIT_CARBON_API,
|
||||
GET_TIME_ELEC_API,
|
||||
@ -199,6 +200,15 @@ export const postEditCarbonValue = async ({
|
||||
});
|
||||
};
|
||||
|
||||
export const getRealTimeDemand = async (building_guid) => {
|
||||
const res = await instance.post(GET_REALTIME_DEMAND_API, { building_guid });
|
||||
|
||||
return apihandler(res.code, res.data, {
|
||||
msg: res.msg,
|
||||
code: res.code,
|
||||
});
|
||||
};
|
||||
|
||||
export const getTimeElec = async (building_guid) => {
|
||||
const res = await instance.post(GET_TIME_ELEC_API, { building_guid });
|
||||
|
||||
|
@ -20,13 +20,13 @@ export const getSystemFloors = async (building_tag, sub_system_tag) => {
|
||||
|
||||
export const getSystemDevices = async ({
|
||||
sub_system_tag,
|
||||
building_tag,
|
||||
floor_tag,
|
||||
building_guid,
|
||||
department_id_list,
|
||||
}) => {
|
||||
const res = await instance.post(GET_SYSTEM_DEVICE_LIST_API, {
|
||||
sub_system_tag,
|
||||
building_tag,
|
||||
floor_tag,
|
||||
building_guid,
|
||||
department_id_list,
|
||||
});
|
||||
|
||||
return apihandler(res.code, res.data, {
|
||||
|
@ -37,8 +37,8 @@ const authPages = computed(() =>
|
||||
);
|
||||
|
||||
const open = ref(false);
|
||||
const getSubMonitorPage = async (building) => {
|
||||
const res = await getAllSysSidebar();
|
||||
const getSubMonitorPage = async (building_guid) => {
|
||||
const res = await getAllSysSidebar(building_guid);
|
||||
buildingStore.mainSubSys = res.data.history_Main_Systems;
|
||||
menu_array.value = res.data.history_Main_Systems;
|
||||
};
|
||||
@ -48,7 +48,7 @@ const getSubPage = async (system_type) => {
|
||||
};
|
||||
const showDrawer = async (authCode) => {
|
||||
if (authCode === "PF1") {
|
||||
await getSubMonitorPage();
|
||||
await getSubMonitorPage(buildingStore.selectedBuilding.building_guid);
|
||||
} else if (authCode === "PF2" || authCode === "PF11") {
|
||||
await getSubPage(authCode === "PF2" ? "Energy" : "Setting");
|
||||
}
|
||||
@ -71,7 +71,7 @@ watch(
|
||||
() => buildingStore.selectedBuilding,
|
||||
(newVal) => {
|
||||
if (newVal !== null) {
|
||||
getSubMonitorPage(newVal.building_tag);
|
||||
getSubMonitorPage(newVal.building_guid);
|
||||
}
|
||||
}
|
||||
);
|
||||
|
@ -5,6 +5,8 @@ import { ref, onMounted, watch, inject } from "vue";
|
||||
import useSearchParam from "@/hooks/useSearchParam";
|
||||
import useActiveBtn from "@/hooks/useActiveBtn";
|
||||
import { useI18n } from "vue-i18n";
|
||||
import useBuildingStore from "@/stores/useBuildingStore";
|
||||
const store = useBuildingStore();
|
||||
const { t } = useI18n();
|
||||
const { searchParams, changeParams } = useSearchParam();
|
||||
const { items, changeActiveBtn, setItems, selectedBtn } = useActiveBtn();
|
||||
@ -17,7 +19,7 @@ const formState = ref({
|
||||
});
|
||||
|
||||
const getMainSystems = async () => {
|
||||
const res = await getAssetMainList();
|
||||
const res = await getAssetMainList(store.selectedBuilding.building_guid);
|
||||
const cate = res.data.map((d, index) => ({
|
||||
...d,
|
||||
title: d.system_key,
|
||||
@ -60,9 +62,15 @@ const deleteItem = async (id) => {
|
||||
});
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
watch(
|
||||
() => store.selectedBuilding,
|
||||
(newBuilding) => {
|
||||
if (newBuilding) {
|
||||
getMainSystems();
|
||||
});
|
||||
}
|
||||
},
|
||||
{ immediate: true }
|
||||
);
|
||||
|
||||
watch(selectedBtn, (newValue) => {
|
||||
changeParams({
|
||||
|
@ -3,8 +3,11 @@ import { ref, defineProps, inject } from "vue";
|
||||
import * as yup from "yup";
|
||||
import useFormErrorMessage from "@/hooks/useFormErrorMessage";
|
||||
import { postAssetMainList } from "@/apis/asset";
|
||||
import useBuildingStore from "@/stores/useBuildingStore";
|
||||
import useSearchParam from "@/hooks/useSearchParam";
|
||||
import { useI18n } from "vue-i18n";
|
||||
|
||||
const storeBuild = useBuildingStore();
|
||||
const { t } = useI18n();
|
||||
const { openToast } = inject("app_toast");
|
||||
const { searchParams, changeParams } = useSearchParam();
|
||||
@ -30,6 +33,7 @@ const onOk = async () => {
|
||||
const res = await postAssetMainList({
|
||||
...props.formState,
|
||||
id: props.formState ? props.formState.id : 0,
|
||||
building_guid:storeBuild.selectedBuilding?.building_guid || null,
|
||||
});
|
||||
|
||||
if (res.isSuccess) {
|
||||
|
@ -45,21 +45,41 @@ watch(
|
||||
deep: true,
|
||||
}
|
||||
);
|
||||
const formState = ref({
|
||||
id: 0,
|
||||
system_key: "",
|
||||
system_value: "",
|
||||
system_parent_id: 0,
|
||||
file: [],
|
||||
});
|
||||
|
||||
const editRecord = ref(null);
|
||||
// 編輯 modal
|
||||
const openModal = () => {
|
||||
|
||||
|
||||
const openModal = (item) => {
|
||||
if (item.id) {
|
||||
formState.value = { ...item };
|
||||
|
||||
if (item.device_image) {
|
||||
const subFile = item
|
||||
? {
|
||||
name: item.device_image,
|
||||
src: item.device_image,
|
||||
ext: item.device_image?.split(".")[1],
|
||||
}
|
||||
: {};
|
||||
formState.value.file = [subFile];
|
||||
}
|
||||
} else {
|
||||
formState.value = {
|
||||
id: 0,
|
||||
system_key: "",
|
||||
system_value: "",
|
||||
system_parent_id: 0,
|
||||
file: [],
|
||||
};
|
||||
}
|
||||
asset_add_sub_item.showModal();
|
||||
};
|
||||
const onCancel = () => {
|
||||
editRecord.value = null;
|
||||
asset_add_sub_item.close();
|
||||
};
|
||||
|
||||
const edit = (item) => {
|
||||
editRecord.value = item;
|
||||
openModal();
|
||||
};
|
||||
|
||||
const deleteItem = async (id) => {
|
||||
openToast("warning", t("msg.sure_to_delete"), "body", async () => {
|
||||
@ -83,9 +103,8 @@ const deleteItem = async (id) => {
|
||||
</h2>
|
||||
<AssetSubListAddModal
|
||||
:openModal="openModal"
|
||||
:onCancel="onCancel"
|
||||
:getData="getSubSystems"
|
||||
:editRecord="editRecord"
|
||||
:formState="formState"
|
||||
/>
|
||||
<button
|
||||
@click.stop.prevent="isEditMode = !isEditMode"
|
||||
@ -112,7 +131,7 @@ const deleteItem = async (id) => {
|
||||
<template v-if="isEditMode">
|
||||
<span
|
||||
class="ml-2 text-base text-warning"
|
||||
@click.stop.prevent="() => edit(item)"
|
||||
@click.stop.prevent="() => openModal(item)"
|
||||
>
|
||||
<FontAwesomeIcon :icon="['fas', 'pencil-alt']"></FontAwesomeIcon>
|
||||
</span>
|
||||
|
@ -5,21 +5,25 @@ import useFormErrorMessage from "@/hooks/useFormErrorMessage";
|
||||
import useSearchParam from "@/hooks/useSearchParam";
|
||||
import { getAssetMainList, postAssetSubList } from "@/apis/asset";
|
||||
import { useI18n } from "vue-i18n";
|
||||
import useBuildingStore from "@/stores/useBuildingStore";
|
||||
const store = useBuildingStore();
|
||||
const { t } = useI18n();
|
||||
const { searchParams, changeParams } = useSearchParam();
|
||||
const props = defineProps({
|
||||
openModal: Function,
|
||||
onCancel: Function,
|
||||
getData: Function,
|
||||
editRecord: Object,
|
||||
formState: Object,
|
||||
});
|
||||
|
||||
const form = ref(null);
|
||||
const mainSystem = ref([]);
|
||||
|
||||
const updateFileList = (files) => {
|
||||
formState.value.icon = files;
|
||||
console.log("file", files);
|
||||
props.formState.file = files;
|
||||
};
|
||||
|
||||
const getMainSystems = async () => {
|
||||
const res = await getAssetMainList();
|
||||
const res = await getAssetMainList(store.selectedBuilding.building_guid);
|
||||
mainSystem.value = res.data.map((d) => ({ ...d, key: d.id }));
|
||||
};
|
||||
|
||||
@ -27,53 +31,55 @@ let subSysSchema = yup.object({
|
||||
system_key: yup.string().required(t("button.required")), // 名稱
|
||||
system_value: yup.string().required(t("button.required")), // 代稱
|
||||
system_parent_id: yup.number().required(t("button.required")), // 大類id
|
||||
file: yup.array(),
|
||||
});
|
||||
|
||||
const { formErrorMsg, handleSubmit, handleErrorReset } =
|
||||
useFormErrorMessage(subSysSchema);
|
||||
|
||||
const formState = ref({
|
||||
const onCancel = () => {
|
||||
props.formState = {
|
||||
id: 0,
|
||||
system_key: "",
|
||||
system_value: "",
|
||||
system_parent_id: 0,
|
||||
icon: [],
|
||||
});
|
||||
|
||||
const resetForm = () => {
|
||||
formState.value = {
|
||||
system_key: "",
|
||||
system_value: "",
|
||||
system_parent_id: 0,
|
||||
icon: [],
|
||||
file: [],
|
||||
};
|
||||
asset_add_sub_item.close();
|
||||
updateFileList([]);
|
||||
handleErrorReset();
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
getMainSystems();
|
||||
});
|
||||
|
||||
watch(
|
||||
() => props.editRecord,
|
||||
(newValue) => {
|
||||
if (newValue) {
|
||||
formState.value = newValue;
|
||||
} else {
|
||||
resetForm();
|
||||
}
|
||||
() => store.selectedBuilding,
|
||||
(newBuilding) => {
|
||||
if (newBuilding) {
|
||||
getMainSystems();
|
||||
}
|
||||
},
|
||||
{ immediate: true }
|
||||
);
|
||||
|
||||
const onOk = async () => {
|
||||
// 編輯
|
||||
const value = await handleSubmit(subSysSchema, formState.value);
|
||||
const res = await postAssetSubList({
|
||||
...formState.value,
|
||||
id: props.editRecord ? props.editRecord.id : 0,
|
||||
});
|
||||
const value = await handleSubmit(subSysSchema, props.formState);
|
||||
console.log("props.formState", props.formState);
|
||||
|
||||
const formData = new FormData(form.value);
|
||||
formData.delete("file");
|
||||
formData.append("id", props.formState.id);
|
||||
|
||||
if (props.formState.file[0]) {
|
||||
formData.append("file", props.formState.file[0]);
|
||||
}
|
||||
if (props.formState.Device_image) {
|
||||
formData.append("Device_image", props.formState.Device_image);
|
||||
}
|
||||
|
||||
const res = await postAssetSubList(formData);
|
||||
if (res.isSuccess) {
|
||||
props.getData(parseInt(searchParams.value.mainSys_id));
|
||||
props.onCancel();
|
||||
onCancel();
|
||||
}
|
||||
};
|
||||
</script>
|
||||
@ -84,7 +90,11 @@ const onOk = async () => {
|
||||
</button>
|
||||
<Modal
|
||||
id="asset_add_sub_item"
|
||||
:title="props.editRecord?.id ? t('assetManagement.edit_device_category') : t('assetManagement.add_device_category')"
|
||||
:title="
|
||||
props.formState?.id
|
||||
? t('assetManagement.edit_device_category')
|
||||
: t('assetManagement.add_device_category')
|
||||
"
|
||||
:open="open"
|
||||
:onCancel="onCancel"
|
||||
width="300"
|
||||
@ -99,7 +109,11 @@ const onOk = async () => {
|
||||
</span></template
|
||||
>
|
||||
</Input>
|
||||
<Input name="system_value" :value="formState" :readonly="props.editRecord?.id">
|
||||
<Input
|
||||
name="system_value"
|
||||
:value="formState"
|
||||
:readonly="props.formState?.id"
|
||||
>
|
||||
<template #topLeft>{{ $t("assetManagement.system_value") }}</template>
|
||||
<template #bottomLeft
|
||||
><span class="text-error text-base">
|
||||
@ -114,7 +128,7 @@ const onOk = async () => {
|
||||
name="system_parent_id"
|
||||
:value="formState"
|
||||
selectClass="border-info focus-within:border-info"
|
||||
:disabled="props.editRecord?.id"
|
||||
:disabled="props.formState?.id"
|
||||
>
|
||||
<template #topLeft>{{
|
||||
$t("assetManagement.system_parent")
|
||||
@ -126,11 +140,10 @@ const onOk = async () => {
|
||||
>
|
||||
</Select>
|
||||
<Upload
|
||||
name="icon"
|
||||
:fileList="formState.icon"
|
||||
name="file"
|
||||
:fileList="formState.file || []"
|
||||
:getFileList="updateFileList"
|
||||
:multiple="false"
|
||||
:baseUrl="`${BASEURL}/upload/graph_manage`"
|
||||
>
|
||||
<template #topLeft>{{ $t("operation.upload_file") }}</template>
|
||||
</Upload>
|
||||
|
@ -3,12 +3,13 @@ import { ref, onMounted, watch } from "vue";
|
||||
import useSearchParam from "@/hooks/useSearchParam";
|
||||
import useActiveBtn from "@/hooks/useActiveBtn";
|
||||
import { getAlertSubList } from "@/apis/alert";
|
||||
|
||||
import useBuildingStore from "@/stores/useBuildingStore";
|
||||
const store = useBuildingStore();
|
||||
const { searchParams, changeParams } = useSearchParam();
|
||||
const { items, changeActiveBtn, setItems, selectedBtn } = useActiveBtn();
|
||||
|
||||
const getSubSystems = async () => {
|
||||
const res = await getAlertSubList();
|
||||
const res = await getAlertSubList(store.selectedBuilding.building_guid);
|
||||
const history_Sub_systems = res.data.history_Main_Systems.flatMap(
|
||||
(mainSystem) => {
|
||||
return mainSystem.history_Sub_systems;
|
||||
@ -24,9 +25,15 @@ const getSubSystems = async () => {
|
||||
setItems(subSystems);
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
watch(
|
||||
() => store.selectedBuilding,
|
||||
(newBuilding) => {
|
||||
if (newBuilding) {
|
||||
getSubSystems();
|
||||
});
|
||||
}
|
||||
},
|
||||
{ immediate: true }
|
||||
);
|
||||
|
||||
watch(selectedBtn,
|
||||
(newValue) => {
|
||||
|
@ -5,7 +5,7 @@ import { twMerge } from "tailwind-merge";
|
||||
import useBuildingStore from "@/stores/useBuildingStore";
|
||||
const store = useBuildingStore();
|
||||
const router = useRouter();
|
||||
|
||||
const FILE_BASEURL = import.meta.env.VITE_FILE_API_BASEURL;
|
||||
const navigateToSubSystem = (mainSystemId, subSystemId) => {
|
||||
router.push({
|
||||
name: "sub_system",
|
||||
@ -17,7 +17,7 @@ const navigateToSubSystem = (mainSystemId, subSystemId) => {
|
||||
});
|
||||
};
|
||||
|
||||
console.log("subSys",store.subSys)
|
||||
// console.log("subSys", store.subSys);
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@ -43,11 +43,16 @@ console.log("subSys",store.subSys)
|
||||
|
||||
<div class="equipment-item">
|
||||
<div class="w-16">
|
||||
<!-- <FontAwesomeIcon
|
||||
<img
|
||||
v-if="item.device_image_url"
|
||||
class="w-7 m-auto"
|
||||
:src="`${FILE_BASEURL}/${item.device_image_url}`"
|
||||
/>
|
||||
<FontAwesomeIcon
|
||||
v-else
|
||||
class="text-2xl mt-1 m-auto"
|
||||
:icon="['fas', 'tv']"
|
||||
></FontAwesomeIcon> -->
|
||||
<img class="w-7 m-auto" src="https://cgems.cvilux-group.com:8088/upload/device_icon/3454f5e0-3afa-4ace-ae54-a68bf7183e7d.png">
|
||||
></FontAwesomeIcon>
|
||||
</div>
|
||||
<div class="icon-text">
|
||||
<div class="">
|
||||
|
@ -1,92 +1,29 @@
|
||||
<script setup>
|
||||
import LineChart from "@/components/chart/LineChart.vue";
|
||||
import { ref, onMounted,watch } from "vue";
|
||||
import { ref, onMounted, watch, onUnmounted } from "vue";
|
||||
import ImmediateDemandModal from "./ImmediateDemandModal.vue";
|
||||
import { getDemand } from "@/apis/energy";
|
||||
import { getDemand, getRealTimeDemand } from "@/apis/energy";
|
||||
import { useI18n } from "vue-i18n";
|
||||
import useBuildingStore from "@/stores/useBuildingStore";
|
||||
import dayjs from "dayjs";
|
||||
|
||||
const store = useBuildingStore();
|
||||
const { t } = useI18n();
|
||||
|
||||
const demandData = ref(null);
|
||||
// 假資料
|
||||
const data = {
|
||||
categories: [
|
||||
"16:22:29",
|
||||
"16:22:37",
|
||||
"16:22:47",
|
||||
"16:23:00",
|
||||
"16:23:08",
|
||||
"16:23:18",
|
||||
],
|
||||
series: [
|
||||
{
|
||||
name: t("energy.real_time_Trend"), // 即時趨勢
|
||||
type: "line",
|
||||
data: [320, 310, 300, 305, 310, 300],
|
||||
smooth: true,
|
||||
lineStyle: {
|
||||
width: 3,
|
||||
},
|
||||
itemStyle: {
|
||||
color: "#17CEE3",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: t("energy.contract_capacity"), // 契約容量
|
||||
type: "line",
|
||||
data: [400, 400, 400, 400, 400, 400],
|
||||
smooth: true,
|
||||
lineStyle: {
|
||||
width: 3,
|
||||
},
|
||||
itemStyle: {
|
||||
color: "#E4EA00",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: t("energy.alert_capacity"), // 警戒容量
|
||||
type: "line",
|
||||
data: [350, 350, 350, 350, 350, 350],
|
||||
smooth: true,
|
||||
lineStyle: {
|
||||
width: 3,
|
||||
},
|
||||
itemStyle: {
|
||||
color: "#62E39A",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: t("energy.reset_value"), // 偵測值
|
||||
type: "line",
|
||||
data: [280, 300, 290, 295, 300, 290],
|
||||
smooth: true,
|
||||
lineStyle: {
|
||||
width: 3,
|
||||
},
|
||||
itemStyle: {
|
||||
color: "#E9971F",
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
const realTimeDemand = ref([]);
|
||||
const demand_chart = ref(null);
|
||||
const intervalId = ref(null);
|
||||
// 預設為空,避免 `null` 造成 `ECharts` 失敗
|
||||
const defaultChartOption = ref({
|
||||
tooltip: {
|
||||
trigger: "axis",
|
||||
},
|
||||
tooltip: { trigger: "axis" },
|
||||
legend: {
|
||||
data: data.series.map((s) => s.name),
|
||||
textStyle: {
|
||||
color: "#ffffff",
|
||||
fontSize: 16,
|
||||
},
|
||||
orient: "horizontal",
|
||||
data: [],
|
||||
textStyle: { color: "#ffffff", fontSize: 16 },
|
||||
bottom: "0%",
|
||||
},
|
||||
grid: {
|
||||
top: "10%",
|
||||
top: "3%",
|
||||
left: "0%",
|
||||
right: "0%",
|
||||
bottom: "15%",
|
||||
@ -94,42 +31,126 @@ const defaultChartOption = ref({
|
||||
},
|
||||
xAxis: {
|
||||
type: "category",
|
||||
splitLine: {
|
||||
show: false,
|
||||
},
|
||||
axisLabel: {
|
||||
color: "#ffffff",
|
||||
},
|
||||
data: data.categories,
|
||||
splitLine: { show: false },
|
||||
axisLabel: { color: "#ffffff" },
|
||||
data: [], // 預設空值,避免 undefined
|
||||
},
|
||||
yAxis: {
|
||||
type: "value",
|
||||
splitLine: {
|
||||
show: false,
|
||||
splitLine: { show: false },
|
||||
axisLabel: { color: "#ffffff" },
|
||||
},
|
||||
axisLabel: {
|
||||
color: "#ffffff",
|
||||
},
|
||||
},
|
||||
series: data.series,
|
||||
series: [],
|
||||
});
|
||||
|
||||
// 取得契約數據
|
||||
const getData = async () => {
|
||||
if (store.selectedBuilding.building_guid) {
|
||||
const res = await getDemand(store.selectedBuilding.building_guid);
|
||||
demandData.value = res.data[0];
|
||||
updateChart();
|
||||
}
|
||||
};
|
||||
|
||||
// 取得即時數據
|
||||
const getRealTime = async () => {
|
||||
if (store.selectedBuilding.building_guid) {
|
||||
const res = await getRealTimeDemand(store.selectedBuilding.building_guid);
|
||||
realTimeDemand.value = res.data.reverse();
|
||||
updateChart();
|
||||
}
|
||||
};
|
||||
|
||||
// 更新圖表資料
|
||||
const updateChart = () => {
|
||||
if (!demandData.value || !realTimeDemand.value.length) return;
|
||||
|
||||
defaultChartOption.value = {
|
||||
...defaultChartOption.value,
|
||||
legend: {
|
||||
...defaultChartOption.value.legend,
|
||||
data: [
|
||||
t("energy.real_time_Trend"),
|
||||
t("energy.contract_capacity"),
|
||||
t("energy.alert_capacity"),
|
||||
t("energy.reset_value"),
|
||||
],
|
||||
},
|
||||
xAxis: {
|
||||
...defaultChartOption.value.xAxis,
|
||||
data: realTimeDemand.value.map(({ time }) =>
|
||||
dayjs(time).format("HH:mm:ss")
|
||||
),
|
||||
},
|
||||
series: [
|
||||
{
|
||||
name: t("energy.real_time_Trend"),
|
||||
type: "line",
|
||||
data: realTimeDemand.value.map((d) => d.value),
|
||||
smooth: true,
|
||||
lineStyle: { width: 3 },
|
||||
itemStyle: { color: "#17CEE3" },
|
||||
},
|
||||
{
|
||||
name: t("energy.contract_capacity"),
|
||||
type: "line",
|
||||
data: Array(realTimeDemand.value.length).fill(
|
||||
demandData.value.contract
|
||||
),
|
||||
smooth: true,
|
||||
lineStyle: { width: 3 },
|
||||
itemStyle: { color: "#E4EA00" },
|
||||
},
|
||||
{
|
||||
name: t("energy.alert_capacity"),
|
||||
type: "line",
|
||||
data: Array(realTimeDemand.value.length).fill(demandData.value.alert),
|
||||
smooth: true,
|
||||
lineStyle: { width: 3 },
|
||||
itemStyle: { color: "#62E39A" },
|
||||
},
|
||||
{
|
||||
name: t("energy.reset_value"),
|
||||
type: "line",
|
||||
data: Array(realTimeDemand.value.length).fill(demandData.value.reset),
|
||||
smooth: true,
|
||||
lineStyle: { width: 3 },
|
||||
itemStyle: { color: "#E9971F" },
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
// 確保 `chart` 有更新
|
||||
if (demand_chart.value?.chart) {
|
||||
// demand_chart.value.chart.clear();
|
||||
demand_chart.value.chart.setOption(defaultChartOption.value);
|
||||
}
|
||||
};
|
||||
|
||||
// 監聽建築變更時重新抓取數據
|
||||
watch(
|
||||
() => store.selectedBuilding,
|
||||
(newBuilding) => {
|
||||
if (newBuilding) {
|
||||
getData();
|
||||
getRealTime();
|
||||
// 每 30 秒重新取得即時數據
|
||||
if (intervalId.value) {
|
||||
clearInterval(intervalId.value);
|
||||
}
|
||||
// 設置新的定時器
|
||||
intervalId.value = setInterval(getRealTime, 30000);
|
||||
}
|
||||
},
|
||||
{ immediate: true }
|
||||
);
|
||||
|
||||
onUnmounted(() => {
|
||||
// 清除定時器
|
||||
clearInterval(intervalId.value); // 使用 intervalId.value
|
||||
intervalId.value = null; // 清空 intervalId
|
||||
console.log("Interval cleared!");
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@ -137,11 +158,14 @@ watch(
|
||||
<div class="flex items-center text-white mb-5">
|
||||
<div class="flex items-end text-base relative text mr-32">
|
||||
{{ $t("energy.immediate_demand") }}
|
||||
<span class="text-2xl px-2.5">245.48 kw</span>
|
||||
</div>
|
||||
<div class="flex items-end text-base">
|
||||
{{ $t("energy.average_demand") }}
|
||||
<span class="text-2xl px-2.5">230.8 kw</span>
|
||||
<span class="text-2xl px-2.5"
|
||||
>{{
|
||||
realTimeDemand.length > 0
|
||||
? realTimeDemand[realTimeDemand.length - 1].value
|
||||
: "---"
|
||||
}}
|
||||
kw</span
|
||||
>
|
||||
</div>
|
||||
<ImmediateDemandModal :demandData="demandData" :getData="getData" />
|
||||
</div>
|
||||
|
@ -109,7 +109,7 @@ const chartOption = computed(() => {
|
||||
|
||||
const loadData = async (value) => {
|
||||
const res = await getElecUseDay(value);
|
||||
if (res.isSuccess) {
|
||||
if (res.isSuccess && res.data) {
|
||||
dataSource.value = res.data
|
||||
.sort((a, b) => a.time.localeCompare(b.time))
|
||||
.map((d) => ({ ...d, key: d.id }));
|
||||
@ -119,6 +119,10 @@ const loadData = async (value) => {
|
||||
min: dates[0],
|
||||
max: dates[dates.length - 1],
|
||||
};
|
||||
} else {
|
||||
// 初始化圖表
|
||||
dataSource.value = [];
|
||||
dateRange.value = { min: null, max: null };
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -3,8 +3,10 @@ import { inject, computed, ref, watch } from "vue";
|
||||
import { useI18n } from "vue-i18n";
|
||||
import LineChart from "@/components/chart/LineChart.vue";
|
||||
import { SECOND_CHART_COLOR } from "@/constant";
|
||||
import useSearchParam from "@/hooks/useSearchParam";
|
||||
import dayjs from "dayjs";
|
||||
|
||||
const { searchParams } = useSearchParam();
|
||||
const { t } = useI18n();
|
||||
const { tableData } = inject("energy_table_data");
|
||||
const history_chart = ref(null);
|
||||
@ -46,7 +48,10 @@ const defaultChartOption = {
|
||||
splitLine: { show: false },
|
||||
axisLabel: {
|
||||
color: "#ffffff",
|
||||
formatter: (value) => dayjs(value).format("HH:mm"), // 格式化為時間
|
||||
formatter: (value) =>
|
||||
searchParams.value.Type == 2
|
||||
? dayjs(value).format("HH:mm")
|
||||
: dayjs(value).format("MM-DD"), // 格式化為時間
|
||||
},
|
||||
data: [],
|
||||
},
|
||||
@ -54,6 +59,10 @@ const defaultChartOption = {
|
||||
type: "value",
|
||||
splitLine: { show: false },
|
||||
axisLabel: { color: "#ffffff" },
|
||||
// interval: 100, //Y 軸的刻度間隔
|
||||
min: "dataMin",
|
||||
max: "dataMax",
|
||||
// splitArea: { show: false },
|
||||
},
|
||||
series: [],
|
||||
};
|
||||
|
@ -1,11 +1,13 @@
|
||||
<script setup>
|
||||
import Checkbox from "@/components/customUI/Checkbox.vue";
|
||||
import { computed, ref, watch, inject } from "vue";
|
||||
import { useRoute } from "vue-router";
|
||||
import useBuildingStore from "@/stores/useBuildingStore";
|
||||
import useSearchParam from "@/hooks/useSearchParam";
|
||||
import { getHistorySideBar } from "@/apis/history";
|
||||
import { useI18n } from "vue-i18n";
|
||||
const { t } = useI18n();
|
||||
const route = useRoute();
|
||||
const storeBuild = useBuildingStore();
|
||||
const buildingGuid = computed(() => storeBuild.selectedBuilding?.building_guid);
|
||||
const { searchParams, changeParams } = useSearchParam();
|
||||
@ -22,7 +24,7 @@ const getDeviceData = async ({
|
||||
const res = await getHistorySideBar({
|
||||
sub_system_tag,
|
||||
department_id,
|
||||
elec_type_id,
|
||||
elec_type_id: route.params.type == 3 ? elec_type_id : [],
|
||||
building_guid: buildingGuid.value,
|
||||
});
|
||||
deviceData.value = (res.data || []).map((building) => ({
|
||||
|
@ -22,12 +22,12 @@ const itemsForEndTime = ref();
|
||||
const initializeItems = () => {
|
||||
setItems([
|
||||
{
|
||||
title: t("history.date_range"),
|
||||
title: t("history.time_range"),
|
||||
key: "dateRange",
|
||||
active: searchParams.value.Type
|
||||
? parseInt(searchParams.value.Type) === 1
|
||||
? parseInt(searchParams.value.Type) === 2
|
||||
: true,
|
||||
Type: 1,
|
||||
Type: 2,
|
||||
},
|
||||
// {
|
||||
// title: t("history.time_range"),
|
||||
|
@ -4,6 +4,8 @@ import { computed, onMounted, ref, watch,inject } from "vue";
|
||||
import useSearchParam from "@/hooks/useSearchParam";
|
||||
import useActiveBtn from "@/hooks/useActiveBtn";
|
||||
import { useI18n } from "vue-i18n";
|
||||
import useBuildingStore from "@/stores/useBuildingStore";
|
||||
const store = useBuildingStore();
|
||||
const { t } = useI18n();
|
||||
const { searchParams, changeParams } = useSearchParam();
|
||||
const { search } = inject("operation_table");
|
||||
@ -23,7 +25,7 @@ const {
|
||||
} = useActiveBtn("multiple");
|
||||
|
||||
const getMainSystems = async () => {
|
||||
const res = await getAssetMainList();
|
||||
const res = await getAssetMainList(store.selectedBuilding.building_guid);
|
||||
const cate = res.data.map((d, index) => ({
|
||||
...d,
|
||||
title: d.system_key,
|
||||
@ -48,9 +50,15 @@ const getSubSystems = async (id) => {
|
||||
setSysItems(sub);
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
watch(
|
||||
() => store.selectedBuilding,
|
||||
(newBuilding) => {
|
||||
if (newBuilding) {
|
||||
getMainSystems();
|
||||
});
|
||||
}
|
||||
},
|
||||
{ immediate: true }
|
||||
);
|
||||
|
||||
watch(selectedMainSysItems, (newValue) => {
|
||||
changeParams({
|
||||
|
@ -11,6 +11,8 @@ import useActiveBtn from "@/hooks/useActiveBtn";
|
||||
import MQTTListAddModal from "./MQTTListAddModal.vue";
|
||||
import PointListAddModal from "./PointListAddModal.vue";
|
||||
import { useI18n } from "vue-i18n";
|
||||
import useBuildingStore from "@/stores/useBuildingStore";
|
||||
const store = useBuildingStore();
|
||||
const { t } = useI18n();
|
||||
const { openToast, cancelToastOpen } = inject("app_toast");
|
||||
const MQTTDataSource = ref([]);
|
||||
@ -35,7 +37,7 @@ const {
|
||||
} = useActiveBtn();
|
||||
|
||||
const getMainSystems = async () => {
|
||||
const res = await getAssetMainList();
|
||||
const res = await getAssetMainList(store.selectedBuilding.building_guid);
|
||||
const cate = res.data.map((d, index) => ({
|
||||
...d,
|
||||
title: d.system_key,
|
||||
@ -179,9 +181,15 @@ watch(
|
||||
}
|
||||
);
|
||||
|
||||
onMounted(() => {
|
||||
watch(
|
||||
() => store.selectedBuilding,
|
||||
(newBuilding) => {
|
||||
if (newBuilding) {
|
||||
getMainSystems();
|
||||
});
|
||||
}
|
||||
},
|
||||
{ immediate: true }
|
||||
);
|
||||
</script>
|
||||
|
||||
<template>
|
||||
|
@ -38,6 +38,7 @@ const raw_data = ref([]);
|
||||
const data = ref([]); // filter data
|
||||
const route = useRoute();
|
||||
const floors = ref([]);
|
||||
const deptData = ref([]);
|
||||
const companyOptions = ref([]);
|
||||
const selected_dbid = ref([]);
|
||||
|
||||
@ -55,7 +56,8 @@ const getData = async () => {
|
||||
if (!route.params.sub_system_id) return
|
||||
const res = await getSystemDevices({
|
||||
sub_system_tag: route.params.sub_system_id,
|
||||
building_tag: buildingStore.selectedBuilding?.building_tag,
|
||||
building_guid: buildingStore.selectedBuilding?.building_guid,
|
||||
department_id_list:deptData.value,
|
||||
})
|
||||
const devices = res.data.map(d => ({
|
||||
...d,
|
||||
@ -136,6 +138,19 @@ watch(
|
||||
}
|
||||
);
|
||||
|
||||
watch(
|
||||
() => deptData.value,
|
||||
(newVal) => {
|
||||
if (newVal) {
|
||||
getData();
|
||||
}
|
||||
},
|
||||
{
|
||||
immediate: true,
|
||||
deep: true,
|
||||
}
|
||||
);
|
||||
|
||||
watch(
|
||||
() => route.params.sub_system_id,
|
||||
(newRoute, oldRoute) => {
|
||||
@ -230,7 +245,7 @@ const clearSelectedDeviceInfo = () => {
|
||||
selectedDevice.value.value = null;
|
||||
}
|
||||
|
||||
provide("system_selectedDevice", { selectedDeviceRealtime, selectedDevice, getCurrentInfoModalData, clearSelectedDeviceInfo, selectedDeviceCog, selected_dbid })
|
||||
provide("system_selectedDevice", { selectedDeviceRealtime, selectedDevice, getCurrentInfoModalData, clearSelectedDeviceInfo, selectedDeviceCog, selected_dbid, deptData })
|
||||
|
||||
onBeforeUnmount(() => {
|
||||
clearInterval(timeId.value);
|
||||
|
@ -4,7 +4,7 @@ import { getDepartmentList } from "@/apis/asset";
|
||||
import { onMounted, ref, watch, inject } from "vue";
|
||||
import useActiveBtn from "@/hooks/useActiveBtn";
|
||||
import useBuildingStore from "@/stores/useBuildingStore";
|
||||
|
||||
const { deptData } = inject("system_selectedDevice");
|
||||
const storeBuild = useBuildingStore();
|
||||
// 選部門
|
||||
const {
|
||||
@ -23,6 +23,7 @@ watch(
|
||||
active: true,
|
||||
}));
|
||||
setDeptItems(deptList);
|
||||
deptData.value = deptList.map((d) => d.key);
|
||||
}
|
||||
},
|
||||
{
|
||||
@ -30,6 +31,10 @@ watch(
|
||||
immediate: true,
|
||||
}
|
||||
);
|
||||
|
||||
watch(selectedDeptItems, (newVal) => {
|
||||
deptData.value = newVal.map((d) => d.key);
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
|
Loading…
Reference in New Issue
Block a user