143 lines
3.9 KiB
Vue
143 lines
3.9 KiB
Vue
<script setup>
|
||
import { useRoute } from "vue-router";
|
||
import EffectScatter from "@/components/chart/EffectScatter.vue";
|
||
import useSearchParam from "@/hooks/useSearchParam";
|
||
import { computed, inject, ref, watch } from "vue";
|
||
import { twMerge } from "tailwind-merge";
|
||
import { useI18n } from "vue-i18n";
|
||
|
||
const { t } = useI18n();
|
||
const route = useRoute();
|
||
const { searchParams, changeParams } = useSearchParam();
|
||
const FILE_BASEURL = import.meta.env.VITE_FILE_API_BASEURL;
|
||
|
||
const props = defineProps({
|
||
data: {
|
||
type: Object,
|
||
},
|
||
});
|
||
|
||
const asset_floor_chart = ref(null);
|
||
const currentFloorId = ref(null);
|
||
|
||
const defaultOption = (map, data = []) => {
|
||
return {
|
||
animation: false,
|
||
tooltip: {},
|
||
geo: {
|
||
tooltip: {
|
||
show: true,
|
||
confine: true,
|
||
formatter: function (params) {
|
||
return params.data[2]?.full_name
|
||
? `
|
||
<div class="p-2 w-auto min-w-24">
|
||
<div class="text-lg">${params.data[2].full_name}</div>
|
||
<div class="text-sm text-gray-500">${t("operation.status")} : ${
|
||
params.data[2].state || ""
|
||
}</div>`
|
||
: "";
|
||
},
|
||
},
|
||
map,
|
||
roam: true, // 允許縮放和平移
|
||
layoutSize: window.innerWidth <= 768 ? "110%" : "100%",
|
||
layoutCenter: ["50%", "50%"],
|
||
scaleLimit: { min: 1, max: 2 },
|
||
},
|
||
series: [
|
||
// 背景圓形
|
||
{
|
||
type: "scatter",
|
||
coordinateSystem: "geo",
|
||
geoIndex: 0,
|
||
symbolSize: (value) => value[2]?.bgSize || 50,
|
||
symbol: "range",
|
||
itemStyle: {
|
||
color: (params) => params.data[2]?.bgColor || "rgba(0,0,0,0.8)",
|
||
},
|
||
data,
|
||
z: 1,
|
||
},
|
||
{
|
||
type: "scatter",
|
||
coordinateSystem: "geo",
|
||
geoIndex: 0,
|
||
encode: {
|
||
tooltip: 2,
|
||
},
|
||
symbolSize: 30,
|
||
itemStyle: {
|
||
color: "rgba(225,225,225,0.8)",
|
||
},
|
||
symbol: (value, params) => {
|
||
// 若有 icon 屬性就用 image,否則用圓點
|
||
return params.data[2]?.icon
|
||
? `image://${params.data[2].icon}`
|
||
: "roundRect";
|
||
},
|
||
data,
|
||
label: {
|
||
show: true,
|
||
position: "bottom",
|
||
formatter: (params) => params.data[2]?.full_name || "",
|
||
color: "#333",
|
||
fontSize: 14,
|
||
backgroundColor: "rgba(255,255,255)",
|
||
padding: [4, 4],
|
||
borderRadius: 3,
|
||
},
|
||
z: 2,
|
||
},
|
||
],
|
||
};
|
||
};
|
||
|
||
const currentIconData = computed(() => {
|
||
const data = props.data?.[searchParams.value.floor_id] || [];
|
||
return data;
|
||
});
|
||
|
||
watch(
|
||
[searchParams, () => asset_floor_chart.value, () => props.data],
|
||
([newValue, newChart, newData], [oldValue]) => {
|
||
if (newValue.floor_id && newChart && Object.keys(newData || {}).length > 0) {
|
||
const isFloorChanged = currentFloorId.value !== newValue.floor_id;
|
||
|
||
if (isFloorChanged) {
|
||
// 樓層切換時才重新載入 SVG
|
||
console.log("Floor changed, updating chart with new SVG", newValue.floor_id);
|
||
currentFloorId.value = newValue.floor_id;
|
||
newChart.updateSvg(
|
||
{
|
||
full_name: newValue.floor_id,
|
||
path: `${FILE_BASEURL}/upload/floor_map/${newValue.floor_id}.svg`,
|
||
},
|
||
defaultOption(newValue.floor_id, currentIconData.value)
|
||
);
|
||
} else if (currentFloorId.value === newValue.floor_id && newChart.chart) {
|
||
// 只是資料更新時,只更新圖表資料,不重新載入 SVG
|
||
console.log("Data updated, refreshing chart data only");
|
||
newChart.chart.setOption({
|
||
series: defaultOption(newValue.floor_id, currentIconData.value).series
|
||
}, false, true);
|
||
}
|
||
}
|
||
},
|
||
{
|
||
immediate: true,
|
||
deep: true,
|
||
}
|
||
);
|
||
</script>
|
||
|
||
<template>
|
||
<EffectScatter
|
||
id="system_floor_chart"
|
||
ref="asset_floor_chart"
|
||
class="bg-slate-300 absolute top-0 left-0 min-h-full z-0"
|
||
/>
|
||
</template>
|
||
|
||
<style lang="scss" scoped></style>
|