系統監控: 2D圓點bug修正 | 能源管理: 日報表功能

This commit is contained in:
koko 2025-01-24 16:01:17 +08:00
parent 8e2b5e1e2c
commit 5b1ff9749d
7 changed files with 235 additions and 94 deletions

View File

@ -5,3 +5,5 @@ export const GET_TAI_POWER_API = `/api/Energe/GetTaipower`;
export const GET_SIDEBAR_API = `/api/Energe/GetEnergySideBar`; export const GET_SIDEBAR_API = `/api/Energe/GetEnergySideBar`;
export const GET_SEARCH_API = `/api/Energe/GetFilter`; export const GET_SEARCH_API = `/api/Energe/GetFilter`;
export const GET_REPORT_API = `/api/Energe/GetReport`;

View File

@ -4,6 +4,7 @@ import {
GET_TAI_POWER_API, GET_TAI_POWER_API,
GET_SIDEBAR_API, GET_SIDEBAR_API,
GET_SEARCH_API, GET_SEARCH_API,
GET_REPORT_API,
} from "./api"; } from "./api";
import instance from "@/util/request"; import instance from "@/util/request";
import apihandler from "@/util/apihandler"; import apihandler from "@/util/apihandler";
@ -52,3 +53,26 @@ export const getEnergySearch = async (type) => {
code: res.code, code: res.code,
}); });
}; };
export const getReport = async ({
department,
elecType,
floor,
start_time,
end_time,
type,
}) => {
const res = await instance.post(GET_REPORT_API, {
department,
elecType,
floor,
start_time,
end_time,
type,
});
return apihandler(res.code, res.data, {
msg: res.msg,
code: res.code,
});
};

View File

@ -8,7 +8,7 @@ import {
getAssetFloorList, getAssetFloorList,
getElecTypeList, getElecTypeList,
} from "@/apis/asset"; } from "@/apis/asset";
import { getAccountUserList } from "@/apis/account"; import { getReport } from "@/apis/energy";
import { useI18n } from "vue-i18n"; import { useI18n } from "vue-i18n";
const { t } = useI18n(); const { t } = useI18n();
const route = useRoute(); const route = useRoute();
@ -78,7 +78,7 @@ const getFloor = async () => {
const onSearch = async () => { const onSearch = async () => {
loading.value = true; loading.value = true;
const res = await getAccountUserList(formState.value); const res = await getReport(formState.value);
tableData.value = res.data; tableData.value = res.data;
loading.value = false; loading.value = false;
}; };
@ -114,7 +114,7 @@ watch(
watch( watch(
() => route.params.type, () => route.params.type,
(newVal) => { (newVal) => {
formState.value.type = newVal; formState.value.type = Number(newVal);
}, },
{ immediate: true } { immediate: true }
); );

View File

@ -1,33 +1,91 @@
<script setup> <script setup>
import Table from "@/components/customUI/Table.vue"; import Table from "@/components/customUI/Table.vue";
import { inject, computed } from "vue"; import { inject, computed, ref } from "vue";
import { useI18n } from "vue-i18n"; import { useI18n } from "vue-i18n";
import dayjs from "dayjs";
const { t } = useI18n(); const { t } = useI18n();
const { tableData, loading } = inject("energy_table_data"); const { tableData, loading } = inject("energy_table_data");
const columns = computed(() => [
//
const defaultColumns = ref([
{ {
title: t("history.building_name"), title: t("assetManagement.department"),
key: "building_name", key: "department_name",
}, },
{ {
title: t("energy.floor"), title: t("energy.floor"),
key: "building_name", key: "floor_name",
},
{
title: t("energy.electricity_classification"),
key: "elecType",
}, },
{ {
title: t("history.device_name"), title: t("history.device_name"),
key: "device_name", key: "device_name",
filter: true, filter: true,
}, },
{
title: t("energy.subtotal"),
key: "subtotal",
}
]); ]);
//
const columns = computed(() => {
if (!tableData.value || tableData.value.length === 0) {
return [...defaultColumns.value, {
title: t("energy.subtotal"),
key: "subtotal",
}];
}
//
const dynamicColumns = [...defaultColumns.value];
// data
const firstDataItem = tableData.value[0];
if (firstDataItem && firstDataItem.data) {
// data
firstDataItem.data.forEach((item, index) => {
// 使 dayjs format
const formattedTime = dayjs(item.time).format("MM-DD");
dynamicColumns.push({
title: formattedTime,
key: `data[${index}].value`,
});
});
}
dynamicColumns.push({
title: t("energy.subtotal"),
key: "subtotal",
});
return dynamicColumns;
});
// 調 subtotal
const dataSource = computed(() => {
if(!tableData.value || tableData.value.length === 0) return [];
return tableData.value.map(item => {
let subtotalValue = 0;
if(item.data && item.data.length > 0) {
item.data.forEach((dataItem) => {
subtotalValue += parseFloat(dataItem.value || 0);
});
}
return {
...item,
subtotal: subtotalValue.toFixed(2)
}
})
})
</script> </script>
<template> <template>
<Table :columns="columns" :dataSource="tableData" :loading="loading"></Table> <Table :columns="columns" :dataSource="dataSource" :loading="loading"></Table>
</template> </template>
<style lang="scss" scoped></style> <style lang="scss" scoped></style>

View File

@ -157,12 +157,17 @@ const updateCurrentFloor = (floor) => {
const realtimeData = ref([]) const realtimeData = ref([])
const timeId = ref(null) const timeId = ref(null)
const getAllDeviceRealtime = () => { const getAllDeviceRealtime = async () => {
timeId.value = setInterval(async () => { //
const res = await getSystemRealTime(subscribeData.value.map(d => d.device_number)) const fetchData = async () => {
console.log(res.data) const res = await getSystemRealTime(subscribeData.value.map(d => d.device_number));
realtimeData.value = res.data console.log(res.data);
}, 10000) realtimeData.value = res.data;
};
await fetchData(); //
// 10
timeId.value = setInterval(fetchData, 10000);
} }
watch(subscribeData, (newValue) => { watch(subscribeData, (newValue) => {
@ -206,9 +211,7 @@ const getCurrentInfoModalData = async (e, position, value) => {
: "", : "",
}; };
} }
const immediateRes = await getSystemRealTime([value.device_number]);
realtimeData.value = immediateRes.data;
} }
const mobile = isMobile(e); const mobile = isMobile(e);
selectedDevice.value = { selectedDevice.value = {

View File

@ -1,14 +1,16 @@
<script setup> <script setup>
import { useRoute } from 'vue-router'; import { useRoute } from "vue-router";
import EffectScatter from "@/components/chart/EffectScatter.vue"; import EffectScatter from "@/components/chart/EffectScatter.vue";
import { computed, inject, ref, watch } from 'vue'; import { computed, inject, ref, watch } from "vue";
import { twMerge } from 'tailwind-merge'; import { twMerge } from "tailwind-merge";
import useSelectedFloor from "@/hooks/useSelectedFloor" import useSelectedFloor from "@/hooks/useSelectedFloor";
const route = useRoute() const route = useRoute();
const { currentFloor, subscribeData } = inject("system_deviceList") const { currentFloor, subscribeData } = inject("system_deviceList");
const { getCurrentInfoModalData } = inject("system_selectedDevice") const { getCurrentInfoModalData, selected_dbid } = inject(
"system_selectedDevice"
);
const FILE_BASEURL = import.meta.env.VITE_FILE_API_BASEURL; const FILE_BASEURL = import.meta.env.VITE_FILE_API_BASEURL;
const asset_floor_chart = ref(null); const asset_floor_chart = ref(null);
@ -19,7 +21,7 @@ const sameOption = {
encode: { encode: {
tooltip: 2, tooltip: 2,
}, },
} };
const defaultOption = (map, data = []) => { const defaultOption = (map, data = []) => {
return { return {
animation: false, animation: false,
@ -36,7 +38,7 @@ const defaultOption = (map, data = []) => {
...sameOption, ...sameOption,
symbolSize: 10, symbolSize: 10,
itemStyle: { itemStyle: {
color: data?.[0]?.[3]?.device_normal_color || "#009100", color: data?.[0]?.[2]?.device_normal_color || "#009100",
}, },
data, data,
}, },
@ -44,85 +46,141 @@ const defaultOption = (map, data = []) => {
...sameOption, ...sameOption,
symbolSize: 20, symbolSize: 20,
itemStyle: { itemStyle: {
color: data?.[0]?.[3]?.device_normal_color || "#009100", color: data?.[0]?.[2]?.device_normal_color || "#009100",
}, },
data: [], data: [],
} },
], ],
} };
}; };
const { selectedFloor } = useSelectedFloor() const { selectedFloor } = useSelectedFloor();
const allData = ref([]) const allData = ref([]);
const selectedData = ref([]);
watch(
[selectedFloor, () => asset_floor_chart],
([newValue, newChart], [oldValue]) => {
if (
newValue &&
newChart.value &&
oldValue?.key !== newValue.key &&
newValue.map_url
) {
selectedData.value =
subscribeData.value
?.filter(
(d) => d.device_coordinate && d.floor_guid === route.params.floor_id
)
.map((d) => [...d.device_coordinate.slice(1, -1).split(","), d]) ||
[];
watch([selectedFloor, () => asset_floor_chart,], ([newValue, newChart], [oldValue]) => { asset_floor_chart.value.updateSvg(
if (newValue && newChart.value && oldValue?.key !== newValue.key && newValue.map_url) { {
asset_floor_chart.value.updateSvg( full_name: newValue?.title,
{ path: `${FILE_BASEURL}/${newValue.map_url}`,
full_name: newValue?.title, },
path: `${FILE_BASEURL}/${newValue.map_url}`,
},
defaultOption(newValue?.title, subscribeData.value?.filter(d => d.device_coordinate && d.floor_guid === route.params.floor_id).map(d => [...d.device_coordinate.split(","), d]) || []) defaultOption(newValue?.title, selectedData.value)
); );
newChart.value.chart.on("click", function (params) { newChart.value.chart.on("click", function (params) {
console.log(params, params.data[2]) getCurrentInfoModalData(
getCurrentInfoModalData(params.event, { params.event,
left: params.event.offsetX {
, top: params.event.offsetY left: params.event.offsetX,
}, params.data[2]) top: params.event.offsetY,
},
params.data[2]
);
selected_dbid.value[0] = params.data[2].forge_dbid;
});
}
},
{
immediate: true,
deep: true,
}
);
let values = [...subscribeData.value].map(d => ({ ...d, is2DActive: d.forge_dbid === params.data[2].forge_dbid })) watch(
subscribeData.value = values; subscribeData,
(newData) => {
const selected = allData.value.filter((d => d[2].device_number === params.data[2].device_number)) let values = newData
const unSelected = allData.value.filter((d => d[2].device_number !== params.data[2].device_number)) ?.filter(
newChart.value.chart.setOption({ (d) => d.device_coordinate && d.floor_guid === route.params.floor_id
)
.map((d) => [...d.device_coordinate.slice(1, -1).split(","), d]);
allData.value = values;
selectedData.value = newData
?.filter((d) => d.device_coordinate)
.map((d) => [...d.device_coordinate.slice(1, -1).split(","), d]);
if (
selectedFloor.value &&
asset_floor_chart.value &&
asset_floor_chart.value.chart
) {
const selected = allData.value.filter((d) => d[2].is2DActive);
const unSelected = allData.value.filter((d) => !d[2].is2DActive);
asset_floor_chart.value.chart.setOption({
series: [ series: [
{ data: unSelected }, { { data: unSelected },
{
data: selected, data: selected,
} },
] ],
}) });
}
}); },
{
deep: true,
} }
}, { );
immediate: true,
deep: true,
})
watch(subscribeData, (newData) => { watch(
let values = newData?.filter(d => d.device_coordinate && d.floor_guid === route.params.floor_id).map(d => [...d.device_coordinate.split(","), d]) selected_dbid,
allData.value = values; (newSelectedDbid) => {
if (selectedFloor.value && asset_floor_chart.value && asset_floor_chart.value.chart) { if (
const selected = allData.value.filter((d => d[2].is2DActive)) asset_floor_chart.value &&
const unSelected = allData.value.filter((d => !d[2].is2DActive)) asset_floor_chart.value.chart &&
asset_floor_chart.value.chart.setOption({ allData.value
series: [ ) {
{ data: unSelected }, { selectedData.value = selectedData.value.map((item) => {
data: selected, if (item[2].forge_dbid === newSelectedDbid[0]) {
return [...item.slice(0, 2), { ...item[2], is2DActive: true }];
} }
] return [...item.slice(0, 2), { ...item[2], is2DActive: false }];
}) });
console.log(asset_floor_chart.value.chart.getOption())
const selected = selectedData.value.filter((d) => d[2].is2DActive);
const unSelected = selectedData.value.filter((d) => !d[2].is2DActive);
console.log("allData.value", allData.value, selected, unSelected);
asset_floor_chart.value.chart.setOption({
series: [
{ data: unSelected },
{
data: selected,
},
],
});
}
},
{
deep: true,
} }
}, { );
immediate: true,
deep: true,
})
</script> </script>
<template> <template>
<!-- <Loading class="absolute" /> --> <!-- <Loading class="absolute" /> -->
<EffectScatter id="system_floor_chart" ref="asset_floor_chart" class="min-h-full bg-white" /> <EffectScatter
id="system_floor_chart"
ref="asset_floor_chart"
class="min-h-full bg-white"
/>
<!-- <div class="text-lg" v-if="!currentFloor?.key">尚未上傳樓層平面圖</div> --> <!-- <div class="text-lg" v-if="!currentFloor?.key">尚未上傳樓層平面圖</div> -->
</template> </template>
<style lang='scss' scoped></style> <style lang="scss" scoped></style>

View File

@ -10,10 +10,6 @@ const FILE_BASEURL = import.meta.env.VITE_FILE_API_BASEURL;
const fitToView = (forge_dbid, spriteDbId) => { const fitToView = (forge_dbid, spriteDbId) => {
selected_dbid.value = [forge_dbid, spriteDbId]; selected_dbid.value = [forge_dbid, spriteDbId];
console.log(subscribeData.value)
let allData = [...subscribeData.value].map(d => ({ ...d, is2DActive: d.forge_dbid === forge_dbid }))
subscribeData.value = allData;
}; };
</script> </script>