CviLux_fe/src/views/system/SystemFloor.vue
2025-07-28 16:49:44 +08:00

229 lines
5.9 KiB
Vue

<script setup>
import { useRoute } from "vue-router";
import EffectScatter from "@/components/chart/EffectScatter.vue";
import { computed, inject, nextTick, ref, watch } from "vue";
import { twMerge } from "tailwind-merge";
import useSelectedFloor from "@/hooks/useSelectedFloor";
const route = useRoute();
const { currentFloor, subscribeData, realtimeData } = inject("system_deviceList");
const { getCurrentInfoModalData, selected_dbid } = inject(
"system_selectedDevice"
);
const FILE_BASEURL = import.meta.env.VITE_FILE_API_BASEURL;
const asset_floor_chart = ref(null);
const sameOption = {
type: "effectScatter",
coordinateSystem: "geo",
geoIndex: 0,
encode: {
tooltip: 2,
},
};
// 根據設備取得即時狀態顏色
const getDeviceRealtimeColor = (device) => {
if (device.full_name === 'SmartSocket-AA001') return 'red';
if (device.full_name === 'SmartSocket-AA003' || device.full_name === 'SmartSocket-AA004') return 'gray';
const realtimeDevice = realtimeData.value?.find(
(item) => item.device_number === device.device_number
);
const state = realtimeDevice?.state || '';
if (state === 'offnormal' || state === '') return device.device_close_color || '#999';
return device.device_normal_color || '#009100';
};
const defaultOption = (map, data = []) => {
return {
animation: false,
tooltip: {},
geo: {
tooltip: {
show: false,
},
map,
roam: true, // 一定要关闭拖拽
},
series: [
{
...sameOption,
symbolSize: 10,
itemStyle: {
color: (params) => getDeviceRealtimeColor(params.data[2]),
},
data,
},
{
...sameOption,
symbolSize: 20,
itemStyle: {
color: (params) => getDeviceRealtimeColor(params.data[2]),
},
data: [],
},
],
};
};
// 監聽 realtimeData 變化,刷新地圖顏色
watch(
realtimeData,
() => {
setTimeout(() => {
if (
selectedFloor.value &&
asset_floor_chart.value &&
asset_floor_chart.value.chart &&
asset_floor_chart.value.chart.isDisposed() === false
) {
asset_floor_chart.value.chart.setOption(
defaultOption(selectedFloor.value?.title, selectedData.value),
true
);
}
}, 0);
},
{ deep: true }
);
const { selectedFloor } = useSelectedFloor();
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]) ||
[];
asset_floor_chart.value.updateSvg(
{
full_name: newValue?.title,
path: `${FILE_BASEURL}/${newValue.map_url}`,
},
defaultOption(newValue?.title, selectedData.value)
);
newChart.value.chart.on("click", function (params) {
getCurrentInfoModalData(
params.event,
{
left: params.event.offsetX,
top: params.event.offsetY,
},
params.data[2]
);
selected_dbid.value[1] = params.data[2].spriteDbId;
});
}
},
{
immediate: true,
deep: true,
}
);
watch(
subscribeData,
(newData) => {
let values = newData
?.filter(
(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 &&
asset_floor_chart.value.chart.isDisposed() === false
) {
const selected = allData.value.filter((d) => d[2].is2DActive);
const unSelected = allData.value.filter((d) => !d[2].is2DActive);
setTimeout(() => {
if (asset_floor_chart.value?.chart && !asset_floor_chart.value.chart.isDisposed()) {
asset_floor_chart.value.chart.setOption({
series: [
{ data: unSelected },
{
data: selected,
},
],
});
}
}, 0);
}
},
{
deep: true,
}
);
watch(
selected_dbid,
(newSelectedDbid) => {
if (
asset_floor_chart.value &&
asset_floor_chart.value.chart &&
allData.value
) {
selectedData.value = selectedData.value.map((item) => {
if (item[2].spriteDbId === newSelectedDbid[1]) {
return [...item.slice(0, 2), { ...item[2], is2DActive: true }];
}
return [...item.slice(0, 2), { ...item[2], is2DActive: false }];
});
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);
setTimeout(() => {
if (asset_floor_chart.value?.chart && !asset_floor_chart.value.chart.isDisposed()) {
asset_floor_chart.value.chart.setOption({
series: [
{ data: unSelected },
{
data: selected,
},
],
});
}
}, 0);
}
},
{
deep: true,
}
);
</script>
<template>
<!-- <Loading class="absolute" /> -->
<EffectScatter
id="system_floor_chart"
ref="asset_floor_chart"
class="min-h-full bg-white"
/>
<!-- <div class="text-lg" v-if="!currentFloor?.key">尚未上傳樓層平面圖</div> -->
</template>
<style lang="scss" scoped></style>