<template>
|
<div class="device">
|
<div class="left-top">
|
<span>监测设备</span>
|
</div>
|
<div class="left-content device-content">
|
<div style="margin-left: 5px; margin-bottom: 5px">
|
<span style="color: white">北京市:</span>
|
<el-select
|
@change="handleChange1"
|
v-model="selectValue1"
|
placeholder="请选择行政区"
|
size="mini"
|
style="width: 240px"
|
>
|
<el-option
|
v-for="item in BJoptions"
|
:key="item.value"
|
:label="item.label"
|
:value="item.value"
|
/>
|
</el-select>
|
</div>
|
<div style="margin-left: 5px">
|
<span style="color: white">重点沟:</span>
|
<el-select
|
@change="handleChange"
|
v-model="selectValue"
|
placeholder="请选择重点沟"
|
size="mini"
|
style="width: 240px"
|
>
|
<el-option
|
v-for="item in options"
|
:key="item.value"
|
:label="item.label"
|
:value="item.value"
|
/>
|
</el-select>
|
</div>
|
<!-- 加载遮罩层和内容容器 -->
|
<div class="tree-container">
|
<div v-if="isLoading" class="loading-overlay">
|
<div class="loading-content">
|
<el-icon class="loading-icon"><Loading /></el-icon>
|
<span class="loading-text">数据加载中...</span>
|
</div>
|
</div>
|
|
<el-tree
|
v-show="!isLoading"
|
:data="deviceTree"
|
default-expand-all
|
node-key="deviceId"
|
:props="treeProps"
|
@node-click="handleTreeNodeClick"
|
class="device-tree"
|
>
|
<template #default="{ node, data }">
|
<span v-if="!data.children" class="device-tree-item">
|
<span class="device-item-text" :title="node.label">{{
|
node.label
|
}}</span>
|
</span>
|
<span v-else class="device-tree-category">
|
{{ node.label }} ({{ data.children.length }})
|
</span>
|
</template>
|
</el-tree>
|
|
<!-- <el-tree
|
v-show="!isLoading"
|
:data="deviceTree"
|
default-expand-all
|
node-key="deviceId"
|
:props="treeProps"
|
@node-click="handleTreeNodeClick"
|
class="device-tree"
|
>
|
<template #default="{ node, data }">
|
<span v-if="!data.children" class="device-tree-item">
|
<el-tooltip :content="node.label" placement="top" effect="dark">
|
<span class="device-item-text" :title="node.label">{{
|
node.label
|
}}</span>
|
</el-tooltip>
|
</span>
|
<span v-else class="device-tree-category">
|
<el-tooltip
|
:content="`${node.label} (${data.children.length})`"
|
placement="top"
|
effect="dark"
|
>
|
<span :title="`${node.label} (${data.children.length})`"
|
>{{ node.label }} ({{ data.children.length }})</span
|
>
|
</el-tooltip>
|
</span>
|
</template>
|
</el-tree> -->
|
</div>
|
</div>
|
</div>
|
</template>
|
|
<script setup>
|
import { ref, computed, onMounted, watch, onBeforeUnmount } from "vue";
|
import { useRoute, onBeforeRouteUpdate } from "vue-router";
|
import { createPoint, clearAllPoints } from "@/utils/map";
|
import { deviceDictList, getDictName } from "@/constant/dict.js";
|
import { initeWaterPrimitiveView } from "@/utils/water"; //相机flyTo函数,后续options列表中有对应经纬度后弃用
|
import { useSimStore } from "@/store/simulation";
|
import { Loading } from "@element-plus/icons-vue";
|
import { getDeviceInfoByPage } from "@/api/hpApi";
|
|
const simStore = useSimStore();
|
|
const isLoading = ref(true); // 初始设置为加载状态
|
|
onMounted(async () => {
|
try {
|
if (simStore.devices && simStore.devices.length > 0) {
|
await loadDeviceList(selectValue.value);
|
}
|
// initeWaterPrimitiveView();
|
} finally {
|
isLoading.value = false;
|
}
|
});
|
|
const route = useRoute();
|
|
onBeforeUnmount(() => {
|
if (route.path !== "/zhjc") {
|
clearAllPoints();
|
}
|
});
|
|
const selectValue1 = ref("");
|
|
const BJoptions = ref([
|
{ label: "密云区", value: "110118000000" },
|
{ label: "房山区", value: "110111000000" },
|
{ label: "门头沟区", value: "110109000000" },
|
{ label: "延庆区", value: "110119000000" },
|
{ label: "怀柔区", value: "110116000000" },
|
{ label: "昌平区", value: "110114000000" },
|
{ label: "平谷区", value: "110117000000" },
|
{ label: "海淀区", value: "110108000000" },
|
{ label: "石景山区", value: "110107000000" },
|
{ label: "丰台区", value: "110106000000" },
|
]);
|
|
const selectValue = ref("孙胡沟");
|
|
const options = ref([
|
{ value: "孙胡沟", label: "孙胡沟" },
|
{ value: "鱼水洞后沟", label: "鱼水洞后沟" },
|
{ value: "于家西沟", label: "于家西沟" },
|
{ value: "北河沟", label: "北河沟" },
|
{ value: "龙泉峪村", label: "龙泉峪村" },
|
]);
|
|
const deviceListAll = ref([]);
|
|
const initializeDevicePoints = () => {
|
deviceListAll.value.forEach((item, index) => {
|
// 根据需求可增删
|
item.type = getDictName(deviceDictList, item.dictDeviceType);
|
item.name = item.deviceName.split(selectValue.value)[1] || item.deviceName;
|
item.id = item.deviceId;
|
item.className = "device";
|
item.showLabel = true;
|
// 打印每个设备的名称和设备类型
|
// console.log(`设备名称: ${item.id}, 设备类型: ${item.name}`);
|
createPoint(item);
|
});
|
};
|
|
// 单个点渲染
|
const DevicePoints = async (item) => {
|
// 根据需求可增删
|
item.type = getDictName(deviceDictList, item.dictDeviceType);
|
item.name = item.deviceName;
|
// item.name = item.deviceName.split(selectValue.value)[1] || item.deviceName;
|
item.id = item.deviceId;
|
item.className = "device";
|
item.showLabel = true;
|
// 打印每个设备的名称和设备类型
|
// console.log(`设备名称: ${item.id}, 设备类型: ${item.name}`);
|
createPoint(item);
|
};
|
|
// 根据区域名称加载设备列表
|
const loadDeviceList = async (areaName) => {
|
try {
|
clearAllPoints();
|
isLoading.value = true;
|
|
deviceListAll.value = simStore.devices.filter((item) =>
|
item.deviceName?.includes(areaName)
|
);
|
|
await initializeDevicePoints();
|
} catch (error) {
|
console.error("加载设备信息失败", error);
|
} finally {
|
isLoading.value = false;
|
}
|
};
|
|
// 监听 simStore.devices 变化
|
watch(
|
() => simStore.devices,
|
(newVal) => {
|
if (newVal && newVal.length > 0) {
|
loadDeviceList(selectValue.value);
|
} else {
|
clearAllPoints();
|
deviceListAll.value = [];
|
}
|
}
|
);
|
|
// 处理区域变化事件
|
const handleChange = (item) => {
|
if (!item) {
|
ElMessage("请选择一个区域");
|
return;
|
}
|
selectValue1.value = "";
|
selectValue.value = item;
|
// 根据新区域名重新加载设备列表
|
loadDeviceList(item);
|
initializeDevicePoints();
|
};
|
|
// 处理区域变化事件
|
const handleChange1 = async (item) => {
|
if (!item) {
|
ElMessage("请选择一个区域");
|
return;
|
}
|
selectValue1.value = item;
|
selectValue.value = "";
|
deviceListAll.value = [];
|
let pageNum = 1;
|
let hasMore = true;
|
|
try {
|
while (hasMore) {
|
const res = await getDeviceInfoByPage(item, pageNum);
|
|
// 将新数据追加到列表中
|
deviceListAll.value = [...deviceListAll.value, ...res.data.pageData];
|
|
// 判断是否还有下一页
|
if (pageNum < res.data.pageCount) {
|
pageNum++;
|
await new Promise((resolve) => setTimeout(resolve, 300));
|
} else {
|
hasMore = false;
|
}
|
}
|
} catch (err) {
|
console.error("获取监测设备失败", err);
|
deviceListAll.value = []; // 可选:清空或保留已加载部分
|
} finally {
|
// 暂时先关闭加载状态,因为此处为分页请求数据,然后一点一点的渲染,最终会加载完成
|
// 如果开启加载状态,最后还是会等所有数据都请求回来才会渲染
|
// loading.value = false; // 关闭加载状态
|
}
|
};
|
|
const treeProps = {
|
label: "deviceName",
|
children: "children",
|
};
|
|
// 计算属性:将设备列表转换为树形结构
|
const deviceTree = computed(() => {
|
const typeMap = {};
|
|
// 先按设备类型分组
|
deviceListAll.value.forEach((device) => {
|
const typeName = getDictName(deviceDictList, device.dictDeviceType);
|
if (!typeName) {
|
console.warn("未找到设备类型:", device);
|
return;
|
}
|
if (!typeMap[typeName]) {
|
typeMap[typeName] = [];
|
}
|
// 直接使用原始的设备名称,不进行任何替换操作
|
typeMap[typeName].push({
|
...device,
|
deviceName: device.deviceName.trim(), // 只去除首尾空格
|
});
|
});
|
|
// 转换为树形结构
|
return Object.keys(typeMap).map((typeName) => ({
|
deviceName: typeName,
|
children: typeMap[typeName],
|
}));
|
});
|
|
function handleTreeNodeClick(data, node) {
|
if (data.children) {
|
return; // 一级节点,不执行点击逻辑
|
}
|
if (selectValue.value) {
|
// 此处调用是因为GisView页面会在点击下一乡镇之前把上一个选择的区域的隐患点清除掉(如果刚好选择了孙胡沟,那么下一个点击将会清空孙胡沟的隐患点)
|
initializeDevicePoints();
|
} else {
|
// 行政区划的点位太多了,只能选中哪个渲染哪个
|
DevicePoints(data);
|
}
|
// 只有设备节点才处理点击事件
|
if (!data.children) {
|
const entity = viewer.entities.getById(data.deviceId);
|
if (entity) {
|
viewer.flyTo(entity, {
|
offset: {
|
heading: Cesium.Math.toRadians(0),
|
pitch: Cesium.Math.toRadians(-45),
|
range: 5000,
|
},
|
});
|
}
|
}
|
}
|
</script>
|
<style lang="less" scoped>
|
.device {
|
position: absolute;
|
width: 345px;
|
top: 100px;
|
left: 30px;
|
bottom: 55px;
|
z-index: 99;
|
}
|
|
.left-top {
|
height: 40px;
|
line-height: 40px;
|
color: white;
|
font-size: 16px;
|
font-weight: bold;
|
border-bottom: 1px solid rgba(255, 255, 255, 0.1);
|
}
|
|
.device-content {
|
padding: 10px;
|
box-sizing: border-box;
|
height: calc(100% - 70px);
|
position: relative;
|
}
|
|
.tree-container {
|
position: relative;
|
height: calc(100% - 22px);
|
// margin-top: 10px;
|
overflow-y: auto;
|
}
|
|
.loading-overlay {
|
position: absolute;
|
top: 0;
|
left: 0;
|
right: 0;
|
bottom: 0;
|
// background-color: rgba(0, 0, 0, 0.7);
|
display: flex;
|
justify-content: center;
|
align-items: center;
|
z-index: 100;
|
// border-radius: 4px;
|
}
|
|
.loading-content {
|
display: flex;
|
flex-direction: column;
|
align-items: center;
|
color: #fff;
|
}
|
|
.loading-icon {
|
font-size: 24px;
|
margin-bottom: 8px;
|
animation: rotating 2s linear infinite;
|
}
|
|
.loading-text {
|
font-size: 14px;
|
}
|
|
@keyframes rotating {
|
0% {
|
transform: rotate(0deg);
|
}
|
100% {
|
transform: rotate(360deg);
|
}
|
}
|
|
.device-tree {
|
height: calc(100% - 50px);
|
background: transparent;
|
color: white;
|
margin-top: 10px;
|
width: 100%;
|
overflow-x: auto;
|
|
:deep(.el-tree-node__content) {
|
height: 30px;
|
margin: 5px 0;
|
}
|
|
:deep(.el-tree-node__expand-icon) {
|
color: white;
|
}
|
|
:deep(.el-tree-node:focus > .el-tree-node__content) {
|
background-color: rgba(38, 124, 124, 0.3);
|
}
|
}
|
|
.device-tree-item {
|
display: flex;
|
align-items: center;
|
cursor: pointer;
|
}
|
|
.device-tree-category {
|
font-weight: bold;
|
color: #fff;
|
}
|
|
.device-item-text {
|
overflow: hidden;
|
text-overflow: ellipsis;
|
white-space: nowrap;
|
display: inline-block;
|
}
|
|
:deep(.el-select__placeholder) {
|
color: white;
|
}
|
|
:deep(.el-select-dropdown__item.hover),
|
:deep(.el-select-dropdown__item:hover) {
|
color: white !important;
|
background-color: rgba(38, 124, 124, 0.5);
|
}
|
|
:deep(.el-tree-node__content) {
|
padding-left: 0px !important;
|
}
|
</style>
|