pccv_front/src/components/forge/Forge.vue
2025-06-17 17:50:29 +08:00

283 lines
7.3 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<script setup>
import {
ref,
onMounted,
defineProps,
computed,
onUnmounted,
watch,
watchEffect,
provide,
inject,
} from "vue";
import { getUrn, getAccessToken } from "@/apis/forge";
import { twMerge } from "tailwind-merge";
import useSystemStatusByBaja from "@/hooks/baja/useSystemStatusByBaja";
import ForgeInfoModal from "./ForgeInfoModal.vue";
import useAlarmStore from "@/stores/useAlarmStore";
const FILE_BASEURL = import.meta.env.VITE_FILE_API_BASEURL;
const { forgeLock } = inject("app_toggle");
const props = defineProps({
fullScreen: Boolean,
initialData: Object,
cubeStyle: {
type: Object,
default: {
right: 25,
top: 2,
},
},
});
const heat_bar_isShow = ref(false);
const updateHeatBarIsShow = (isShow) => {
heat_bar_isShow.value = isShow;
};
const {
subscribeData,
visibleDbid,
updateDbidPosition,
hideAllObjects,
updateForgeViewer,
forgeViewer,
urn,
loadModel,
updateInitialData,
subComponents,
} = useSystemStatusByBaja(updateHeatBarIsShow);
watch(
() => props.initialData,
(newValue) => {
newValue && updateInitialData(newValue);
},
{
deep: true,
}
);
const store = useAlarmStore();
const subscribeDataWithErrorMsg = computed(() => {
let data = { ...subscribeData.value };
for (let [key, value] of Object.entries(subscribeData.value)) {
const alarm = store.alarmData.find(
({ device_number }) => device_number === key
);
data[key].alarmMsg = alarm ? alarm.msg : "";
}
console.log("baja update data: ", data);
return data;
});
const forgeDom = ref(null);
const initViewer = (container) => {
return new Promise(function (resolve, reject) {
Autodesk.Viewing.Initializer(
{
env: "Local",
language: "en",
},
function () {
const config = {
extensions: [
"Autodesk.DataVisualization",
"Autodesk.DocumentBrowser",
],
};
let viewer = new Autodesk.Viewing.GuiViewer3D(container, config);
Autodesk.Viewing.Private.InitParametersSetting.alpha = true;
viewer.start();
resolve(viewer);
}
);
});
};
const initForge = () => {
initViewer(forgeDom.value).then((viewer) => {
const localFilePath =
import.meta.env.MODE === "production"
? `${FILE_BASEURL}/upload/forge/0.svf`
: "/forge/0.svf";
loadModel(viewer, localFilePath).then(() => {
viewer.addEventListener(
Autodesk.Viewing.GEOMETRY_LOADED_EVENT,
async function () {
console.log(
"Autodesk.Viewing.GEOMETRY_LOADED_EVENT",
viewer.isLoadDone()
);
updateForgeViewer(viewer);
const tree = viewer.model.getData().instanceTree;
hideAllObjects(tree, visibleDbid.value);
// 印出被點選物件的 dbid
// viewer.addEventListener(
// Autodesk.Viewing.SELECTION_CHANGED_EVENT,
// function (event) {
// console.log("你選取的 forge_dbid", event.dbIdArray);
// }
// );
}
);
viewer.addEventListener(
Autodesk.Viewing.CAMERA_CHANGE_EVENT,
function (e) {
viewer.isLoadDone() && updateDbidPosition(this, subscribeData.value);
console.log(
"camera position changed: ",
NOP_VIEWER.navigation.getTarget(),
e.camera.position
);
}
);
});
});
};
onMounted(() => {
console.log("Forge 加載");
initForge();
});
// 傳遞目前點擊資訊
const currentInfoModalData = ref(null);
const isMobile = (pointerType) => {
return pointerType !== "mouse"; // is desktop
};
const getCurrentInfoModalData = (e, position, value) => {
const mobile = isMobile(e.pointerType);
currentInfoModalData.value = {
initPos: mobile
? { left: `50%`, top: `50%` }
: { left: `${position.left}px`, top: `${position.top}px` },
value,
isMobile: mobile,
};
forge_info_modal.showModal();
};
watch([forgeViewer, forgeLock], ([newViewer, newLock]) => {
if (newViewer && newLock !== undefined) {
newViewer.setNavigationLock(newLock); // 鎖定視角
newViewer.navigation.setZoomTowardsPivot(!newLock); // 滾輪縮放
newViewer.navigation.setReverseZoomDirection(newLock); // 滑動變更視角
}
});
onUnmounted(() => {
console.log("Forge 銷毀");
console.log("sub", subComponents);
subComponents.value?.unsubscribeAll();
subComponents.value?.detach();
updateForgeViewer(null);
NOP_VIEWER.tearDown();
});
</script>
<template>
<ForgeInfoModal :data="currentInfoModalData" />
<div
:class="
twMerge(
fullScreen
? 'absolute top-0 left-0 w-screen h-full z-0'
: 'w-full relative'
)
"
>
<div
id="forge-preview"
ref="forgeDom"
:class="
twMerge(
'relative w-full h-full',
fullScreen ? 'min-h-screen ' : 'min-h-[600px]'
)
"
>
<div v-show="heat_bar_isShow" class="absolute z-10 heatbar">
<div class="w-40 flex justify-between text-[10px] mb-1">
<span class="text-gradient-1">-20°C</span>
<span class="text-gradient-2">0°C</span>
<span class="text-gradient-3">20°C</span>
<span class="text-gradient-4">40°C</span>
</div>
<div
class="w-40 h-3"
style="
background: linear-gradient(
to right,
#0000ff 0%,
#00ff00 33%,
#ffff00 66%,
#ff0000 100%
);
"
></div>
</div>
<label
v-for="(value, key) in subscribeDataWithErrorMsg"
:key="key"
:data-dbid="value.forge_dbid"
:class="
twMerge(
`after:border-t-[${value.currentColor}]`,
'flex items-center justify-center h-12 -translate-x-1/2 -translate-y-1/5 absolute z-50 px-5 py-4 text-center rounded-md text-lg border-2 border-white',
'after:absolute after:border-t-[10px] after:border-x-[12px] after:border-x-transparent after:-bottom-[8px] after:left-1/2 after:-translate-x-1/2 ',
'before:absolute before:border-t-[12px] before:border-x-[14px] before:border-x-transparent before:-bottom-[12px] before:left-1/2 before:-translate-x-1/2 before:border-white'
)
"
:style="{
left: `${Math.floor(value.device_coordinate_3d.x)}px`,
top: `${Math.floor(value.device_coordinate_3d.y) - 100}px`,
display: value.is_show,
backgroundColor: value.currentColor,
}"
@click.prevent="
(e) =>
getCurrentInfoModalData(
e,
{ left: e.clientX, top: e.clientY },
value
)
"
>
<span class="mr-2">{{ value.full_name }}</span>
<span v-if="value.alarmMsg">{{ value.alarmMsg }}</span>
<span v-else>{{ value.show_value }}</span>
</label>
</div>
</div>
</template>
<style lang="css">
.adsk-viewing-viewer {
background-color: transparent !important;
}
#guiviewer3d-toolbar {
display: none;
bottom: 200px;
}
.viewcubeWrapper {
right: v-bind("`${props.cubeStyle.right}%`") !important;
top: v-bind("`${props.cubeStyle.top}%`") !important;
}
.homeViewWrapper {
transform: scale(1.5) !important;
}
.heatbar {
right: v-bind("`${props.cubeStyle.right + 2}%`") !important;
top: 0% !important;
}
</style>