diff --git a/src/apis/system/api.js b/src/apis/system/api.js new file mode 100644 index 0000000..f2e5715 --- /dev/null +++ b/src/apis/system/api.js @@ -0,0 +1,2 @@ +export const GET_SYSTEM_FLOOR_LIST_API = `/api/Device/GetFloor`; +export const GET_SYSTEM_DEVICE_LIST_API = `/api/Device/GetDeviceList`; \ No newline at end of file diff --git a/src/apis/system/index.js b/src/apis/system/index.js new file mode 100644 index 0000000..4054f3f --- /dev/null +++ b/src/apis/system/index.js @@ -0,0 +1,32 @@ +import { GET_SYSTEM_FLOOR_LIST_API, GET_SYSTEM_DEVICE_LIST_API } from "./api"; +import instance from "@/util/request"; +import apihandler from "@/util/apihandler"; + +export const getSystemFloors = async (building_tag, sub_system_tag) => { + const res = await instance.post(GET_SYSTEM_FLOOR_LIST_API, { + building_tag, + sub_system_tag, + }); + + return apihandler(res.code, res.data, { + msg: res.msg, + code: res.code, + }); +}; + +export const getSystemDevices = async ({ + sub_system_tag, + building_tag, + floor_tag, +}) => { + const res = await instance.post(GET_SYSTEM_DEVICE_LIST_API, { + sub_system_tag, + building_tag, + floor_tag, + }); + + return apihandler(res.code, res.data, { + msg: res.msg, + code: res.code, + }); +}; diff --git a/src/assets/img/equipment/replay01.svg b/src/assets/img/equipment/replay01.svg new file mode 100644 index 0000000..639e6eb --- /dev/null +++ b/src/assets/img/equipment/replay01.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/img/equipment/replay02.svg b/src/assets/img/equipment/replay02.svg new file mode 100644 index 0000000..62e067b --- /dev/null +++ b/src/assets/img/equipment/replay02.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/img/equipment/state-background.svg b/src/assets/img/equipment/state-background.svg new file mode 100644 index 0000000..8334698 --- /dev/null +++ b/src/assets/img/equipment/state-background.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/img/equipment/state-title.svg b/src/assets/img/equipment/state-title.svg new file mode 100644 index 0000000..6202385 --- /dev/null +++ b/src/assets/img/equipment/state-title.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/img/equipment/table-item-w.svg b/src/assets/img/equipment/table-item-w.svg new file mode 100644 index 0000000..74bbc0c --- /dev/null +++ b/src/assets/img/equipment/table-item-w.svg @@ -0,0 +1,7 @@ + + + + + + diff --git a/src/components/chart/EffectScatter.vue b/src/components/chart/EffectScatter.vue index 0be1380..3ff8e1c 100644 --- a/src/components/chart/EffectScatter.vue +++ b/src/components/chart/EffectScatter.vue @@ -8,7 +8,10 @@ const props = defineProps({ className: String, id: String, svg: Object, - getCoordinate: Function, + getCoordinate: { + type: Function, + default: null + }, }); let chart = ref(null); @@ -22,33 +25,23 @@ async function updateSvg(svg, option) { axios.get(svg.path).then(({ data }) => { echarts.registerMap(svg.full_name, { svg: data }); chart.value.setOption(option); - chart.value.getZr().on("click", function (params) { - var pixelPoint = [params.offsetX, params.offsetY]; - var dataPoint = chart.value.convertFromPixel({ geoIndex: 0 }, pixelPoint); - currentClickPosition.value = dataPoint; - props.getCoordinate(dataPoint); - chart.value.setOption({ - series: { - data: [dataPoint], - }, + if (props.getCoordinate) { + chart.value.getZr().on("click", function (params) { + var pixelPoint = [params.offsetX, params.offsetY]; + var dataPoint = chart.value.convertFromPixel({ geoIndex: 0 }, pixelPoint); + currentClickPosition.value = dataPoint; + props.getCoordinate(dataPoint); + chart.value.setOption({ + series: { + data: [dataPoint], + }, + }); + }); - console.log(chart.value.getOption()); - }); + } + }); console.log("updateSvg", svg.path); - // fetch(svg.path) - // .then((res) => console.log(res)) - // .then(function (svg) { - // console.log(svg); - // // echarts.registerMap(svg.full_name, { svg }); - // // chart.setOption(option); - // // chart.getZr().on("click", function (params) { - // // var pixelPoint = [params.offsetX, params.offsetY]; - // // var dataPoint = curChart.convertFromPixel({ geoIndex: 0 }, pixelPoint); - // // console.log(dataPoint); - // // currentClickPosition.value = dataPoint; - // // }); - // }); } function init() { @@ -69,7 +62,7 @@ defineExpose({ }); - + diff --git a/src/components/customUI/ButtonGroup.vue b/src/components/customUI/ButtonGroup.vue index 3c4621d..4a90fd6 100644 --- a/src/components/customUI/ButtonGroup.vue +++ b/src/components/customUI/ButtonGroup.vue @@ -7,28 +7,22 @@ const props = defineProps({ withLine: Boolean, // this is for change active button onclick: Function, + className: String }); - { + { item.onClick ? item.onClick(e, item) : onclick(e, item); } - " - > + "> {{ item.title }} diff --git a/src/components/forge/ForgeForSystem.vue b/src/components/forge/ForgeForSystem.vue new file mode 100644 index 0000000..4e8d7ce --- /dev/null +++ b/src/components/forge/ForgeForSystem.vue @@ -0,0 +1,257 @@ + + + + + + + + 10 °C + 20 °C + 30 °C + 40 °C + + + + + + + + + getCurrentInfoModalData( + e, + { left: e.clientX, top: e.clientY }, + value + ) + "> + {{ value.full_name }} + {{ value.alarmMsg }} + {{ value.show_value }} + + + + + + diff --git a/src/components/navbar/NavbarItem.vue b/src/components/navbar/NavbarItem.vue index 6324db8..5dac885 100644 --- a/src/components/navbar/NavbarItem.vue +++ b/src/components/navbar/NavbarItem.vue @@ -17,9 +17,9 @@ const iniFroList = async () => { res.data.map((d) => AUTHPAGES.find(({ authCode }) => authCode === d.authCode) ? { - ...d, - ...AUTHPAGES.find(({ authCode }) => authCode === d.authCode), - } + ...d, + ...AUTHPAGES.find(({ authCode }) => authCode === d.authCode), + } : d ) ); @@ -43,6 +43,8 @@ const onClose = () => { }; const navigateToSub = (sub) => { + // buildingStore.selectedSystem = sub; + onClose() // console.log("navigateToSub", sub); // let pageAct = JSON.parse(sessionStorage.getItem("pageAct")); // pageAct = { @@ -119,24 +121,20 @@ onMounted(() => { - + - navigateToSub(sub)" - class="text-xl text-center py-3 hover:bg-black hover:text-info" - > - {{ sub.full_name }} + navigateToSub(sub)"> + + + + + {{ sub.full_name }} + + diff --git a/src/constant/authPage.js b/src/constant/authPage.js index 4b5ae86..cd22406 100644 --- a/src/constant/authPage.js +++ b/src/constant/authPage.js @@ -7,7 +7,7 @@ export const AUTHPAGES = [ { authCode: "PF1", icon: "tv", - navigate: "/", + navigate: "/system", }, { authCode: "PF2", diff --git a/src/hooks/baja/useAlarmData.js b/src/hooks/baja/useAlarmData.js index 4895518..8562df3 100644 --- a/src/hooks/baja/useAlarmData.js +++ b/src/hooks/baja/useAlarmData.js @@ -31,7 +31,7 @@ export default function useAlarmData() { timer = null; }, each: (record) => { - const alarmDisplayName = record.get("alarmData").get("sourceName"); + let alarmDisplayName = record.get("alarmData").get("sourceName"); alarmDisplayName = alarmDisplayName.replace(/\$2d/g, "_"); // 檢查並替換 $2d 為 _ const sourceTmp = alarmDisplayName.split("_"); const bfName = sourceTmp[1] + "-" + sourceTmp[4]; diff --git a/src/hooks/baja/useSystemStatusByBaja.js b/src/hooks/baja/useSystemStatusByBaja.js index 35ef0ea..e8ba4cf 100644 --- a/src/hooks/baja/useSystemStatusByBaja.js +++ b/src/hooks/baja/useSystemStatusByBaja.js @@ -291,6 +291,7 @@ export default function useSystemStatusByBaja(updateHeatBarIsShow) { }; const fitToView = () => { + if(!searchParams.value.camera_position) return const { x, y, z } = JSON.parse(searchParams.value.camera_position); const newPosition = new THREE.Vector3(x, y, z); //!<<< 相机的新位置 diff --git a/src/hooks/forge/useForgeHeatmap.js b/src/hooks/forge/useForgeHeatmap.js new file mode 100644 index 0000000..e31c3c2 --- /dev/null +++ b/src/hooks/forge/useForgeHeatmap.js @@ -0,0 +1,47 @@ +export default function useForgeHeatmap(){ + + const createHeatMap = async (heatMapName) => { + const { + SurfaceShadingData, + SurfaceShadingPoint, + SurfaceShadingNode, + SurfaceShadingGroup, + } = Autodesk.DataVisualization.Core; + const shadingGroup = new SurfaceShadingGroup(`iot_heatmap_${heatMapName}`); + const rooms = new Map(); + + for (const { id, roomDbId, position, sensorTypes } of deviceList.value) { + if (!id || roomDbId == -1 || !roomDbId) { + continue; + } + + if (!rooms.has(roomDbId)) { + const room = new SurfaceShadingNode(id, roomDbId); + shadingGroup.addChild(room); + rooms.set(roomDbId, room); + } + const room = rooms.get(roomDbId); + room.addPoint(new SurfaceShadingPoint(id, position, sensorTypes)); + } + + const shadingData = new SurfaceShadingData(); + shadingData.addChild(shadingGroup); + shadingData.initialize(forgeViewer.value?.model); + + await dataVizExtension.value.setupSurfaceShading( + forgeViewer.value.model, + shadingData + ); + dataVizExtension.value.registerSurfaceShadingColors( + "temperature", + [0x0000ff, 0x00ff00, 0xffff00, 0xff0000] + ); + dataVizExtension.value.renderSurfaceShading( + `iot_heatmap_${heatMapName}`, + "temperature", + getSensorValue + ); + + console.log(dataVizExtension.value); + }; +} \ No newline at end of file diff --git a/src/hooks/forge/useForgeSprite.js b/src/hooks/forge/useForgeSprite.js new file mode 100644 index 0000000..e764aea --- /dev/null +++ b/src/hooks/forge/useForgeSprite.js @@ -0,0 +1,101 @@ +import { watch, inject, markRaw, ref } from "vue"; +import useAlarmStore from "@/stores/useAlarmStore"; +import hexToRgb from "@/util/hexToRgb"; + +export default function useForgeSprite() { + const store = useAlarmStore(); + const { subscribeData } = inject("system_deviceList"); + const forgeViewer = ref(null); + const dataVizExtn = ref(null); + const updateDataVisualization = async (viewer) => { + if (!forgeViewer.value) { + forgeViewer.value = markRaw(viewer); + } + + const dataVisualization = await NOP_VIEWER.loadExtension( + "Autodesk.DataVisualization" + ); + dataVizExtn.value = markRaw(dataVisualization); + }; + + const onSpriteClicked = (event) => { + event.hasStopped = true; + const data = deviceList.value.find((d) => d.spriteDbId === event.dbId); + modalContent.value = data; + store.getDbIdStore(data.forge_dbid); + toggleModal(event.originalEvent); + }; + // 創建 sprites + const createSprites = async () => { + const DataVizCore = Autodesk.DataVisualization.Core; + const viewableType = DataVizCore.ViewableType.SPRITE; + let spriteColor = new THREE.Color(0xffffff); + const BASEURL = import.meta.env.VITE_FORGE_BASEURL; + const spriteIconUrl = `${BASEURL}/hotspot.svg`; + const style = new DataVizCore.ViewableStyle( + viewableType, + spriteColor, + spriteIconUrl + ); + const viewableData = new DataVizCore.ViewableData(); + viewableData.spriteSize = 24; // Sprites as points of size 24 x 24 pixels + subscribeData.value?.forEach((d, index) => { + const position = d.device_coordinate_3d; + style.color = new THREE.Color(hexToRgb(d.device_normal_color)); + const viewable = new DataVizCore.SpriteViewable( + position, + style, + d.spriteDbId + ); + viewableData.addViewable(viewable); + }); + await viewableData.finish(); + dataVizExtn.value.addViewables(viewableData); + + NOP_VIEWER.addEventListener(DataVizCore.MOUSE_CLICK, onSpriteClicked); + NOP_VIEWER.addEventListener( + Autodesk.Viewing.SELECTION_CHANGED_EVENT, + onSpriteClicked + ); + }; + + const fitToView = () => { + const { x, y, z } = JSON.parse(searchParams.value.camera_position); + const newPosition = new THREE.Vector3(x, y, z); //!<<< 相机的新位置 + + const { + x: x1, + y: y1, + z: z1, + } = JSON.parse(searchParams.value.target_position); //!<<< 计算新焦点位置 + const newTarget = new THREE.Vector3(x1, y1, z1); //!<<< 焦點的新位置 + + NOP_VIEWER.navigation.getCamera().setView({ + position: newPosition.clone(), + target: newTarget.clone(), + }); + setTimeout(() => { + updateDbidPosition(NOP_VIEWER, subscribeData.value); + }, 700); + }; + + const hideAllObjects = () => { + const tree = NOP_VIEWER.model?.getData().instanceTree; + const allDbIdsStr = Object.keys(tree.nodeAccess.dbIdToIndex); + for (var i = 0; i < allDbIdsStr.length; i++) { + NOP_VIEWER.hide(parseInt(allDbIdsStr[i])); + } + + subscribeData.value.forEach((value, index) => { + NOP_VIEWER.show(value.forge_dbid); + }); + + NOP_VIEWER.impl.invalidate(true); + }; + + return { + createSprites, + updateDataVisualization, + hideAllObjects, + }; +} diff --git a/src/router/index.js b/src/router/index.js index 9f1f5ac..a272662 100644 --- a/src/router/index.js +++ b/src/router/index.js @@ -11,8 +11,11 @@ import EnergyManagement from "@/views/energyManagement/EnergyManagement.vue"; import Login from "@/views/login/Login.vue"; import useUserInfoStore from "@/stores/useUserInfoStore"; import useGetCookie from "@/hooks/useGetCookie"; +import System from "@/views/system/System.vue"; +import SystemFloor from "@/views/system/SystemFloor.vue"; import Test from "@/views/Test.vue"; +import SystemMain from "@/views/system/SystemMain.vue"; const router = createRouter({ history: createWebHashHistory(import.meta.env.BASE_URL), @@ -29,6 +32,23 @@ const router = createRouter({ name: "dashboard", component: Dashboard, }, + { + path: "/system/:main_system_id/:sub_system_id", + name: "system", + component: System, + children: [ + { + path: "", + name: "sub_system", + component: SystemMain, + }, + { + path: "floor/:floor_id", + name: "floor", + component: SystemFloor, + }, + ], + }, { path: "/historyData", name: "history", diff --git a/src/stores/useBuildingStore.js b/src/stores/useBuildingStore.js index c46c264..5a6987c 100644 --- a/src/stores/useBuildingStore.js +++ b/src/stores/useBuildingStore.js @@ -1,5 +1,6 @@ import { defineStore } from "pinia"; import { ref, computed } from "vue"; +import { useRoute } from "vue-router"; const useBuildingStore = defineStore("buildingInfo", () => { // 所有棟別 @@ -35,12 +36,21 @@ const useBuildingStore = defineStore("buildingInfo", () => { return subPages; }); + const route = useRoute(); + const selectedSystem = computed(() => { + if (route.params.sub_system_id && subSys.value.length > 0) { + return subSys.value.find((s) => s.key === route.params.sub_system_id); + } + return null; + }); + return { buildings, selectedBuilding, mainSubSys, mainSys, subSys, + selectedSystem, }; }); diff --git a/src/views/system/System.vue b/src/views/system/System.vue new file mode 100644 index 0000000..56f617b --- /dev/null +++ b/src/views/system/System.vue @@ -0,0 +1,151 @@ + + + + + + + + + + + {{ statusList.device_normal_text }} + + + + {{ statusList.device_close_text }} + + + + {{ statusList.device_error_text }} + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/views/system/SystemFloor.vue b/src/views/system/SystemFloor.vue new file mode 100644 index 0000000..e84b1a0 --- /dev/null +++ b/src/views/system/SystemFloor.vue @@ -0,0 +1,62 @@ + + + + + + + + diff --git a/src/views/system/SystemMain.vue b/src/views/system/SystemMain.vue new file mode 100644 index 0000000..4c5cb6f --- /dev/null +++ b/src/views/system/SystemMain.vue @@ -0,0 +1,11 @@ + + + + + + + \ No newline at end of file diff --git a/src/views/system/components/SystemCard.vue b/src/views/system/components/SystemCard.vue new file mode 100644 index 0000000..bd5c1aa --- /dev/null +++ b/src/views/system/components/SystemCard.vue @@ -0,0 +1,122 @@ + + + + + + + + + {{ d.full_name }} + + + + + + + + {{ device.full_name }} + + + + 狀態: + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/views/system/components/SystemFloorBar.vue b/src/views/system/components/SystemFloorBar.vue new file mode 100644 index 0000000..c5f2f2b --- /dev/null +++ b/src/views/system/components/SystemFloorBar.vue @@ -0,0 +1,86 @@ + + + + + {{ store.selectedSystem?.full_name }} + + + + + \ No newline at end of file diff --git a/src/views/system/components/SystemSubBar.vue b/src/views/system/components/SystemSubBar.vue new file mode 100644 index 0000000..4471ef5 --- /dev/null +++ b/src/views/system/components/SystemSubBar.vue @@ -0,0 +1,34 @@ + + + + + + + + + + \ No newline at end of file
{{ d.full_name }}