862 lines
23 KiB
Vue
862 lines
23 KiB
Vue
<template>
|
||
<div class="layer-switcher">
|
||
<div class="layer-icon" @click="toggleLayerPanel"></div>
|
||
|
||
<!-- 图层面板 -->
|
||
<div class="layer-panel" v-if="showPanel">
|
||
<!-- 标签页切换 -->
|
||
<div class="panel-tabs">
|
||
<div
|
||
class="tab"
|
||
:class="{ active: activeTab === 'icon' }"
|
||
@click="activeTab = 'icon'"
|
||
>
|
||
图标
|
||
</div>
|
||
<div
|
||
class="tab"
|
||
:class="{ active: activeTab === 'text' }"
|
||
@click="activeTab = 'text'"
|
||
>
|
||
文本
|
||
</div>
|
||
<div
|
||
class="tab"
|
||
:class="{ active: activeTab === 'road' }"
|
||
@click="activeTab = 'road'"
|
||
>
|
||
道路
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 图标tab页 -->
|
||
<div class="panel-content" v-if="activeTab === 'icon'">
|
||
<div class="layer-group">
|
||
<!-- <div class="group-title">图标显示控制</div> -->
|
||
<div class="layer-grid">
|
||
<div class="layer-item" v-for="(cat, type) in categories" :key="type">
|
||
<label class="checkbox-container">
|
||
<input type="checkbox" v-model="cat.visible" @change="emitSet(type)">
|
||
<span class="checkmark"></span>
|
||
<span class="layer-name">{{ cat.name }}</span>
|
||
<!-- <img :src="cat.icon" class="layer-icon-preview" /> -->
|
||
</label>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 文本tab页 -->
|
||
<div class="panel-content" v-else-if="activeTab === 'text'">
|
||
<div class="layer-group">
|
||
<!-- <div class="group-title">标签显示控制</div> -->
|
||
<div class="layer-grid">
|
||
<div class="layer-item" v-for="(cat, type) in categories" :key="type">
|
||
<label class="checkbox-container">
|
||
<input type="checkbox" v-model="cat.showLabel" @change="emitSet(type)">
|
||
<span class="checkmark"></span>
|
||
<span class="layer-name">{{ cat.name }}</span>
|
||
</label>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="panel-content" v-else>
|
||
<div class="layer-group">
|
||
<!-- <div class="group-title">道路图层</div> -->
|
||
<div class="layer-grid-full">
|
||
<div class="layer-item">
|
||
<label class="checkbox-container">
|
||
<input type="checkbox" :checked="!hideRoadLayer" @change="toggleHideRoadLayer" />
|
||
<span class="checkmark"></span>
|
||
<span class="layer-name">电子围栏</span>
|
||
<!-- <svg class="layer-icon-preview" width="24" height="24" viewBox="0 0 1024 1024"><path d="M356.246145 681.56286c-68.156286-41.949414-107.246583-103.84102-107.246583-169.805384 0-65.966411 39.090297-127.860063 107.246583-169.809477 12.046361-7.414877 15.800871-23.190165 8.385994-35.236526-7.413853-12.046361-23.191188-15.801894-35.236526-8.387018-39.640836 24.399713-72.539106 56.044434-95.137801 91.515297-23.86657 37.461193-36.481889 79.620385-36.481889 121.917724 0 42.297338 12.615319 84.454484 36.481889 121.914654 22.598694 35.469839 55.496965 67.11456 95.137801 91.51325 4.185322 2.576685 8.821923 3.804652 13.400195 3.804652 8.598842 0 16.998139-4.329609 21.836331-12.190647C372.047016 704.752002 368.291482 688.976714 356.246145 681.56286z" fill="#409eff"/></svg> -->
|
||
</label>
|
||
</div>
|
||
<!-- 添加自定义路线图层选项 -->
|
||
<div class="layer-item">
|
||
<label class="checkbox-container">
|
||
<input type="checkbox" v-model="showCustomRoadLayer" />
|
||
<span class="checkmark"></span>
|
||
<span class="layer-name">路线图</span>
|
||
<!-- <svg class="layer-icon-preview" width="24" height="24" viewBox="0 0 1024 1024"><path d="M356.246145 681.56286c-68.156286-41.949414-107.246583-103.84102-107.246583-169.805384 0-65.966411 39.090297-127.860063 107.246583-169.809477 12.046361-7.414877 15.800871-23.190165 8.385994-35.236526-7.413853-12.046361-23.191188-15.801894-35.236526-8.387018-39.640836 24.399713-72.539106 56.044434-95.137801 91.515297-23.86657 37.461193-36.481889 79.620385-36.481889 121.917724 0 42.297338 12.615319 84.454484 36.481889 121.914654 22.598694 35.469839 55.496965 67.11456 95.137801 91.51325 4.185322 2.576685 8.821923 3.804652 13.400195 3.804652 8.598842 0 16.998139-4.329609 21.836331-12.190647C372.047016 704.752002 368.291482 688.976714 356.246145 681.56286z" fill="#FF5722"/></svg> -->
|
||
</label>
|
||
</div>
|
||
<!-- 添加区域1图层选项 -->
|
||
<div class="layer-item">
|
||
<label class="checkbox-container">
|
||
<input type="checkbox" v-model="showArea1Layer" />
|
||
<span class="checkmark"></span>
|
||
<span class="layer-name">测试区域1</span>
|
||
</label>
|
||
</div>
|
||
<!-- 添加区域2图层选项 -->
|
||
<div class="layer-item">
|
||
<label class="checkbox-container">
|
||
<input type="checkbox" v-model="showArea2Layer" />
|
||
<span class="checkmark"></span>
|
||
<span class="layer-name">测试区域2</span>
|
||
</label>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</template>
|
||
|
||
<script setup>
|
||
import { ref, onMounted, watch, computed, onBeforeMount, onUnmounted } from 'vue';
|
||
import { Vector as VectorSource } from 'ol/source';
|
||
import { Vector as VectorLayer } from 'ol/layer';
|
||
import { Style, Icon, Stroke, Fill } from 'ol/style';
|
||
import Feature from 'ol/Feature';
|
||
import Point from 'ol/geom/Point';
|
||
import GeoJSON from 'ol/format/GeoJSON';
|
||
import Overlay from 'ol/Overlay';
|
||
import labelBg from '../../../assets/images/label_bg.png';
|
||
|
||
// 导入车辆图标
|
||
import car1Icon from '../../../assets/images/Aircraft.png'; //滑入航空器
|
||
import car1Icon1 from '../../../assets/images/Aircraft1.png'; //滑出航空器
|
||
import car2Icon from '../../../assets/images/noPeopleCar.png';
|
||
import noPeopleCarIcon from '../../../assets/images/noPeopleCar.png';
|
||
import airportBg from '../../../assets/images/airport_bg.png'; // 蓝色背景
|
||
import airportOutBg from '../../../assets/images/airport_out.png'; // 黄色背景
|
||
|
||
// 定义props接收地图实例
|
||
const props = defineProps({
|
||
map: Object,
|
||
categories: Object
|
||
});
|
||
|
||
// 响应式状态
|
||
const showPanel = ref(false);
|
||
const activeTab = ref('icon'); // 默认选中图标标签页
|
||
const selectedTextStyle = ref('white'); // 默认选中白色样式
|
||
|
||
// 新增:道路tab相关状态
|
||
const hideRoadLayer = ref(false); // 默认不隐藏(即显示)
|
||
const showRoadLayer = computed(() => !hideRoadLayer.value); // 计算属性,与hideRoadLayer相反
|
||
let roadVectorLayer = null;
|
||
|
||
// 新增:自定义路线图层状态
|
||
const showCustomRoadLayer = ref(true); // 默认显示
|
||
let customRoadVectorLayer = null;
|
||
|
||
// 新增:区域1图层状态
|
||
const showArea1Layer = ref(true); // 默认显示
|
||
let area1VectorLayer = null;
|
||
|
||
// 新增:区域2图层状态
|
||
const showArea2Layer = ref(true); // 默认显示
|
||
let area2VectorLayer = null;
|
||
|
||
// 向父组件发出事件
|
||
const emit = defineEmits(['layerChange', 'setCategoryVisibility']);
|
||
|
||
// 切换图层面板显示
|
||
function toggleLayerPanel() {
|
||
showPanel.value = !showPanel.value;
|
||
}
|
||
|
||
// 切换电子围栏显示状态
|
||
function toggleHideRoadLayer() {
|
||
hideRoadLayer.value = !hideRoadLayer.value;
|
||
console.log('toggleHideRoadLayer: hideRoadLayer =', hideRoadLayer.value, 'showRoadLayer =', showRoadLayer.value);
|
||
|
||
if (showRoadLayer.value) {
|
||
addRoadLayer();
|
||
} else {
|
||
removeRoadLayer();
|
||
}
|
||
}
|
||
|
||
// 组件挂载时初始化
|
||
onMounted(() => {
|
||
if (props.map) {
|
||
// 初始化自定义路线图层
|
||
loadCustomRoadLayer();
|
||
|
||
// 初始化电子围栏图层 - 默认显示
|
||
if (showRoadLayer.value) {
|
||
addRoadLayer();
|
||
}
|
||
|
||
// 初始化区域1图层
|
||
loadArea1Layer();
|
||
|
||
// 初始化区域2图层
|
||
loadArea2Layer();
|
||
}
|
||
});
|
||
|
||
// 监听地图实例变化
|
||
watch(() => props.map, (newMap) => {
|
||
if (newMap) {
|
||
// 初始化自定义路线图层
|
||
loadCustomRoadLayer();
|
||
|
||
// 初始化电子围栏图层
|
||
if (showRoadLayer.value) {
|
||
addRoadLayer();
|
||
}
|
||
|
||
// 初始化区域1图层
|
||
loadArea1Layer();
|
||
|
||
// 初始化区域2图层
|
||
loadArea2Layer();
|
||
}
|
||
});
|
||
|
||
// 向外暴露方法
|
||
defineExpose({
|
||
setLayerVisibility(typeKey, visible) {
|
||
emit('setCategoryVisibility', typeKey, {
|
||
visible,
|
||
showLabel: props.categories[typeKey]?.showLabel || true
|
||
});
|
||
}
|
||
});
|
||
|
||
async function loadCustomRoadLayer() {
|
||
// 先移除已存在的自定义路线图层,避免重复
|
||
removeCustomRoadLayer();
|
||
if (!props.map) return;
|
||
try {
|
||
// 使用相对路径加载自定义路线文件
|
||
const res = await fetch('./roadTest.json');
|
||
const geojson = await res.json();
|
||
const source = new VectorSource({
|
||
features: new GeoJSON().readFeatures(geojson, {
|
||
dataProjection: 'EPSG:4326',
|
||
featureProjection: props.map.getView().getProjection()
|
||
})
|
||
});
|
||
|
||
customRoadVectorLayer = new VectorLayer({
|
||
source,
|
||
style: new Style({
|
||
stroke: new Stroke({ color: '#C9C9C9', width: 1 })
|
||
}),
|
||
zIndex: 2, // 确保在标准道路图层之上
|
||
visible: showCustomRoadLayer.value
|
||
});
|
||
|
||
props.map.addLayer(customRoadVectorLayer);
|
||
console.log('loadCustomRoadLayer: 已添加自定义路线图层', customRoadVectorLayer);
|
||
} catch (e) {
|
||
console.error('loadCustomRoadLayer: 加载或添加自定义路线图层失败', e);
|
||
}
|
||
}
|
||
|
||
function removeCustomRoadLayer() {
|
||
if (!props.map) return;
|
||
let removed = false;
|
||
// 获取所有图层,查找类型为 VectorLayer 且 zIndex 为 2 的(即自定义路线图层)
|
||
const layers = props.map.getLayers().getArray();
|
||
for (let i = layers.length - 1; i >= 0; i--) {
|
||
const lyr = layers[i];
|
||
if (lyr instanceof VectorLayer && lyr.getZIndex && lyr.getZIndex() === 2) {
|
||
props.map.removeLayer(lyr);
|
||
removed = true;
|
||
console.log('removeCustomRoadLayer: 已移除自定义路线图层', lyr);
|
||
}
|
||
}
|
||
customRoadVectorLayer = null;
|
||
if (!removed) {
|
||
console.log('removeCustomRoadLayer: 没有找到可移除的自定义路线图层');
|
||
}
|
||
}
|
||
|
||
// 监听自定义路线显示状态变化
|
||
watch(showCustomRoadLayer, (val) => {
|
||
console.log('showCustomRoadLayer变化:', val);
|
||
if (val) {
|
||
if (customRoadVectorLayer) {
|
||
customRoadVectorLayer.setVisible(true);
|
||
} else {
|
||
loadCustomRoadLayer();
|
||
}
|
||
} else if (customRoadVectorLayer) {
|
||
customRoadVectorLayer.setVisible(false);
|
||
}
|
||
});
|
||
|
||
async function addRoadLayer() {
|
||
// 先移除已存在的道路图层,避免重复
|
||
removeRoadLayer();
|
||
if (!props.map) return;
|
||
try {
|
||
// 使用相对路径,确保在任何部署环境下都能正确加载
|
||
const res = await fetch('./dianziweilan.json');
|
||
const geojson = await res.json();
|
||
const source = new VectorSource({
|
||
features: new GeoJSON().readFeatures(geojson, {
|
||
dataProjection: 'EPSG:4326',
|
||
featureProjection: props.map.getView().getProjection()
|
||
})
|
||
});
|
||
|
||
// 创建两个图层样式 - 一个用于普通状态,一个用于闪烁状态
|
||
const normalStyle = new Style({
|
||
stroke: new Stroke({ color: 'rgba(0, 0, 0, 0.4)', width: 1 }),
|
||
fill: new Fill({ color: 'rgba(0, 0, 0, 0.2)' })
|
||
});
|
||
|
||
const flashStyle = new Style({
|
||
stroke: new Stroke({ color: 'rgba(255, 0, 0, 0.8)', width: 1 }),
|
||
fill: new Fill({ color: 'rgba(255, 0, 0, 0.3)' })
|
||
});
|
||
|
||
roadVectorLayer = new VectorLayer({
|
||
source,
|
||
style: normalStyle,
|
||
zIndex: 1,
|
||
className: 'fence-layer'
|
||
});
|
||
|
||
props.map.addLayer(roadVectorLayer);
|
||
console.log('addRoadLayer: 已添加电子围栏图层', roadVectorLayer);
|
||
|
||
// 开始闪烁效果
|
||
startFenceFlashing(roadVectorLayer, normalStyle, flashStyle);
|
||
|
||
} catch (e) {
|
||
console.error('addRoadLayer: 加载或添加电子围栏图层失败', e);
|
||
}
|
||
}
|
||
|
||
let flashingInterval = null;
|
||
|
||
// 启动闪烁效果
|
||
function startFenceFlashing(layer, normalStyle, flashStyle) {
|
||
// 清除可能存在的旧定时器
|
||
if (flashingInterval) {
|
||
clearInterval(flashingInterval);
|
||
}
|
||
|
||
// 闪烁状态标志
|
||
let isFlashing = false;
|
||
|
||
// 定时切换样式
|
||
flashingInterval = setInterval(() => {
|
||
if (!layer) return;
|
||
|
||
layer.setStyle(isFlashing ? normalStyle : flashStyle);
|
||
isFlashing = !isFlashing;
|
||
}, 800); // 每800毫秒切换一次样式
|
||
}
|
||
|
||
// 停止闪烁效果
|
||
function stopFenceFlashing() {
|
||
if (flashingInterval) {
|
||
clearInterval(flashingInterval);
|
||
flashingInterval = null;
|
||
}
|
||
}
|
||
|
||
// 移除电子围栏图层时也停止闪烁
|
||
function removeRoadLayer() {
|
||
if (!props.map) return;
|
||
stopFenceFlashing();
|
||
let removed = false;
|
||
// 获取所有图层,查找类型为 VectorLayer 且 zIndex 为 1 的(即道路图层)
|
||
const layers = props.map.getLayers().getArray();
|
||
for (let i = layers.length - 1; i >= 0; i--) {
|
||
const lyr = layers[i];
|
||
if (lyr instanceof VectorLayer && lyr.getZIndex && lyr.getZIndex() === 1) {
|
||
props.map.removeLayer(lyr);
|
||
removed = true;
|
||
console.log('removeRoadLayer: 已移除道路图层', lyr);
|
||
}
|
||
}
|
||
roadVectorLayer = null;
|
||
if (!removed) {
|
||
console.log('removeRoadLayer: 没有找到可移除的道路图层');
|
||
}
|
||
}
|
||
|
||
watch(showRoadLayer, (val) => {
|
||
console.log('showRoadLayer变化:', val);
|
||
if (val) addRoadLayer();
|
||
else removeRoadLayer();
|
||
});
|
||
|
||
// 加载区域1图层
|
||
async function loadArea1Layer() {
|
||
// 先移除已存在的区域1图层,避免重复
|
||
removeArea1Layer();
|
||
if (!props.map) return;
|
||
try {
|
||
// 使用相对路径加载区域1文件
|
||
const res = await fetch('./quyu1.json');
|
||
const geojson = await res.json();
|
||
const source = new VectorSource({
|
||
features: new GeoJSON().readFeatures(geojson, {
|
||
dataProjection: 'EPSG:4326',
|
||
featureProjection: props.map.getView().getProjection()
|
||
})
|
||
});
|
||
|
||
area1VectorLayer = new VectorLayer({
|
||
source,
|
||
style: new Style({
|
||
stroke: new Stroke({ color: '#FF5733', width: 2 }),
|
||
fill: new Fill({ color: 'rgba(255, 87, 51, 0.3)' })
|
||
}),
|
||
zIndex: 3, // 确保在其他图层之上
|
||
visible: showArea1Layer.value
|
||
});
|
||
|
||
props.map.addLayer(area1VectorLayer);
|
||
console.log('loadArea1Layer: 已添加区域1图层', area1VectorLayer);
|
||
} catch (e) {
|
||
console.error('loadArea1Layer: 加载或添加区域1图层失败', e);
|
||
}
|
||
}
|
||
|
||
// 移除区域1图层
|
||
function removeArea1Layer() {
|
||
if (!props.map) return;
|
||
let removed = false;
|
||
// 获取所有图层,查找类型为 VectorLayer 且 zIndex 为 3 的(即区域1图层)
|
||
const layers = props.map.getLayers().getArray();
|
||
for (let i = layers.length - 1; i >= 0; i--) {
|
||
const lyr = layers[i];
|
||
if (lyr instanceof VectorLayer && lyr.getZIndex && lyr.getZIndex() === 3) {
|
||
props.map.removeLayer(lyr);
|
||
removed = true;
|
||
console.log('removeArea1Layer: 已移除区域1图层', lyr);
|
||
}
|
||
}
|
||
area1VectorLayer = null;
|
||
if (!removed) {
|
||
console.log('removeArea1Layer: 没有找到可移除的区域1图层');
|
||
}
|
||
}
|
||
|
||
// 加载区域2图层
|
||
async function loadArea2Layer() {
|
||
// 先移除已存在的区域2图层,避免重复
|
||
removeArea2Layer();
|
||
if (!props.map) return;
|
||
try {
|
||
// 使用相对路径加载区域2文件
|
||
const res = await fetch('./quyu2.json');
|
||
const geojson = await res.json();
|
||
const source = new VectorSource({
|
||
features: new GeoJSON().readFeatures(geojson, {
|
||
dataProjection: 'EPSG:4326',
|
||
featureProjection: props.map.getView().getProjection()
|
||
})
|
||
});
|
||
|
||
area2VectorLayer = new VectorLayer({
|
||
source,
|
||
style: new Style({
|
||
stroke: new Stroke({ color: '#3374FF', width: 2 }),
|
||
fill: new Fill({ color: 'rgba(51, 116, 255, 0.3)' })
|
||
}),
|
||
zIndex: 4, // 确保在区域1图层之上
|
||
visible: showArea2Layer.value
|
||
});
|
||
|
||
props.map.addLayer(area2VectorLayer);
|
||
console.log('loadArea2Layer: 已添加区域2图层', area2VectorLayer);
|
||
} catch (e) {
|
||
console.error('loadArea2Layer: 加载或添加区域2图层失败', e);
|
||
}
|
||
}
|
||
|
||
// 移除区域2图层
|
||
function removeArea2Layer() {
|
||
if (!props.map) return;
|
||
let removed = false;
|
||
// 获取所有图层,查找类型为 VectorLayer 且 zIndex 为 4 的(即区域2图层)
|
||
const layers = props.map.getLayers().getArray();
|
||
for (let i = layers.length - 1; i >= 0; i--) {
|
||
const lyr = layers[i];
|
||
if (lyr instanceof VectorLayer && lyr.getZIndex && lyr.getZIndex() === 4) {
|
||
props.map.removeLayer(lyr);
|
||
removed = true;
|
||
console.log('removeArea2Layer: 已移除区域2图层', lyr);
|
||
}
|
||
}
|
||
area2VectorLayer = null;
|
||
if (!removed) {
|
||
console.log('removeArea2Layer: 没有找到可移除的区域2图层');
|
||
}
|
||
}
|
||
|
||
// 监听区域1显示状态变化
|
||
watch(showArea1Layer, (val) => {
|
||
console.log('showArea1Layer变化:', val);
|
||
if (val) {
|
||
if (area1VectorLayer) {
|
||
area1VectorLayer.setVisible(true);
|
||
} else {
|
||
loadArea1Layer();
|
||
}
|
||
} else if (area1VectorLayer) {
|
||
area1VectorLayer.setVisible(false);
|
||
}
|
||
});
|
||
|
||
// 监听区域2显示状态变化
|
||
watch(showArea2Layer, (val) => {
|
||
console.log('showArea2Layer变化:', val);
|
||
if (val) {
|
||
if (area2VectorLayer) {
|
||
area2VectorLayer.setVisible(true);
|
||
} else {
|
||
loadArea2Layer();
|
||
}
|
||
} else if (area2VectorLayer) {
|
||
area2VectorLayer.setVisible(false);
|
||
}
|
||
});
|
||
|
||
onUnmounted(() => {
|
||
removeRoadLayer();
|
||
removeCustomRoadLayer();
|
||
removeArea1Layer();
|
||
removeArea2Layer();
|
||
stopFenceFlashing();
|
||
});
|
||
|
||
// 发送分类可见性设置到父组件
|
||
function emitSet(type) {
|
||
emit('setCategoryVisibility', type, {
|
||
visible: props.categories[type].visible,
|
||
showLabel: props.categories[type].showLabel
|
||
});
|
||
}
|
||
</script>
|
||
|
||
<style scoped>
|
||
.layer-switcher {
|
||
position: relative;
|
||
}
|
||
|
||
.layer-icon {
|
||
width: 32px;
|
||
height: 32px;
|
||
cursor: pointer;
|
||
}
|
||
|
||
.layer-panel {
|
||
position: absolute;
|
||
top: 10px;
|
||
left: 50px;
|
||
width:397px;
|
||
background-color: #424851;
|
||
border-radius: 4px;
|
||
box-shadow: 0 2px 8px rgba(0,0,0,0.4);
|
||
z-index: 3100;
|
||
overflow: hidden;
|
||
color: #ffffff;
|
||
}
|
||
|
||
|
||
.panel-tabs {
|
||
display: flex;
|
||
border-bottom: 1px solid #303850;
|
||
|
||
}
|
||
|
||
.tab {
|
||
padding: 0 20px;
|
||
height: 40px;
|
||
line-height: 40px;
|
||
font-size: 14px;
|
||
text-align: center;
|
||
cursor: pointer;
|
||
transition: all 0.2s ease;
|
||
color: #F0F0F0;
|
||
position: relative;
|
||
}
|
||
|
||
.tab:hover {
|
||
color: #409eff;
|
||
}
|
||
|
||
.tab.active {
|
||
color: #409eff;
|
||
}
|
||
|
||
.tab.active::after {
|
||
content: "";
|
||
position: absolute;
|
||
bottom: 0;
|
||
left: 50%;
|
||
transform: translateX(-50%);
|
||
width: 100%;
|
||
height: 2px;
|
||
background-color: #409eff;
|
||
}
|
||
|
||
.panel-content {
|
||
padding: 10px;
|
||
max-height: 400px;
|
||
overflow-y: auto;
|
||
}
|
||
|
||
.layer-group {
|
||
margin-bottom: 15px;
|
||
}
|
||
|
||
.group-title {
|
||
font-weight: bold;
|
||
margin-bottom: 8px;
|
||
padding-bottom: 5px;
|
||
border-bottom: 1px solid #999999;
|
||
color: #ffffff;
|
||
}
|
||
|
||
.layer-grid {
|
||
display: grid;
|
||
grid-template-columns: auto auto auto;
|
||
gap: 5px;
|
||
}
|
||
|
||
.layer-grid-full {
|
||
display: grid;
|
||
grid-template-columns: 1fr;
|
||
gap: 5px;
|
||
}
|
||
|
||
.layer-item {
|
||
padding: 6px 0;
|
||
}
|
||
|
||
/* 自定义复选框样式 */
|
||
.checkbox-container {
|
||
display: flex;
|
||
align-items: center;
|
||
position: relative;
|
||
padding-left: 30px;
|
||
cursor: pointer;
|
||
font-size: 14px;
|
||
user-select: none;
|
||
}
|
||
|
||
.checkbox-container input {
|
||
position: absolute;
|
||
opacity: 0;
|
||
cursor: pointer;
|
||
height: 0;
|
||
width: 0;
|
||
}
|
||
|
||
.checkmark {
|
||
position: absolute;
|
||
left: 0;
|
||
height: 18px;
|
||
width: 18px;
|
||
background-color: transparent;
|
||
border: 1px solid #999999;
|
||
border-radius: 3px;
|
||
}
|
||
|
||
.checkbox-container:hover input ~ .checkmark {
|
||
background-color: #666666;
|
||
}
|
||
|
||
.checkbox-container input:checked ~ .checkmark {
|
||
background-color: #0096ff;
|
||
border-color: #0078cc;
|
||
}
|
||
|
||
.checkmark:after {
|
||
content: "";
|
||
position: absolute;
|
||
display: none;
|
||
}
|
||
|
||
.checkbox-container input:checked ~ .checkmark:after {
|
||
display: block;
|
||
}
|
||
|
||
.checkbox-container .checkmark:after {
|
||
left: 6px;
|
||
top: 2px;
|
||
width: 5px;
|
||
height: 10px;
|
||
border: solid white;
|
||
border-width: 0 2px 2px 0;
|
||
transform: rotate(45deg);
|
||
}
|
||
|
||
.layer-name {
|
||
margin-left: 5px;
|
||
color: #ffffff;
|
||
}
|
||
|
||
/* 图层图标预览 */
|
||
.layer-icon-preview {
|
||
width: 20px;
|
||
height: 20px;
|
||
margin-left: 8px;
|
||
object-fit: contain;
|
||
}
|
||
|
||
/* 滚动条样式 */
|
||
.panel-content::-webkit-scrollbar {
|
||
width: 6px;
|
||
}
|
||
|
||
.panel-content::-webkit-scrollbar-track {
|
||
background: #555555;
|
||
}
|
||
|
||
.panel-content::-webkit-scrollbar-thumb {
|
||
background: #999999;
|
||
border-radius: 3px;
|
||
}
|
||
|
||
.panel-content::-webkit-scrollbar-thumb:hover {
|
||
background: #bbbbbb;
|
||
}
|
||
|
||
/* 文本样式选择器 */
|
||
.style-selector {
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: 15px;
|
||
padding: 10px 0;
|
||
}
|
||
|
||
.style-item {
|
||
display: flex;
|
||
justify-content: space-between;
|
||
align-items: center;
|
||
padding: 5px 10px;
|
||
}
|
||
|
||
.style-label {
|
||
font-size: 14px;
|
||
color: #ffffff;
|
||
}
|
||
|
||
.radio-box {
|
||
width: 20px;
|
||
height: 20px;
|
||
border: 2px solid #999999;
|
||
border-radius: 3px;
|
||
cursor: pointer;
|
||
position: relative;
|
||
}
|
||
|
||
.radio-box:hover {
|
||
border-color: #bbbbbb;
|
||
}
|
||
|
||
.radio-box.active {
|
||
border-color: #0096ff;
|
||
}
|
||
|
||
.radio-box.active::after {
|
||
content: "";
|
||
position: absolute;
|
||
top: 50%;
|
||
left: 50%;
|
||
transform: translate(-50%, -50%);
|
||
width: 12px;
|
||
height: 12px;
|
||
background-color: #0096ff;
|
||
border-radius: 1px;
|
||
}
|
||
|
||
.radio-box.blue {
|
||
border-color: #0096ff;
|
||
}
|
||
|
||
.radio-box.blue.active::after {
|
||
background-color: #0096ff;
|
||
}
|
||
|
||
.radio-box.white {
|
||
border-color: #ffffff;
|
||
}
|
||
|
||
.radio-box.white.active::after {
|
||
background-color: #ffffff;
|
||
}
|
||
|
||
/* 道路json样式 */
|
||
.road-json {
|
||
padding: 10px;
|
||
background-color: #333;
|
||
border-radius: 4px;
|
||
margin-top: 10px;
|
||
}
|
||
|
||
.road-json pre {
|
||
margin: 0;
|
||
padding: 0;
|
||
white-space: pre-wrap;
|
||
word-break: break-all;
|
||
}
|
||
|
||
/* 自定义标签样式 */
|
||
.custom-label {
|
||
position: absolute;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
min-width: 120px;
|
||
height: 28px;
|
||
padding: 0 10px;
|
||
font-size: 12px;
|
||
font-weight: bold;
|
||
border-radius: 4px;
|
||
border: 1px solid;
|
||
color: #fff;
|
||
box-sizing: border-box;
|
||
white-space: nowrap;
|
||
z-index: 1000;
|
||
pointer-events: none;
|
||
transform: translateX(-50%);
|
||
}
|
||
|
||
/* 滑入航空器 - 黄色背景,黑色文字 */
|
||
.label-aircraft-in {
|
||
background-color: rgba(245, 231, 79, 0.7);
|
||
border-color: #E4CB0D;
|
||
color: #333;
|
||
}
|
||
|
||
/* 滑出航空器 - 蓝色背景,白色文字 */
|
||
.label-aircraft-out {
|
||
background-color: rgba(52, 122, 226, 0.7);
|
||
border-color: #347AE2;
|
||
color: #fff;
|
||
}
|
||
|
||
/* 无人车 */
|
||
.label-car {
|
||
background-color: rgba(37, 37, 37, 0.7);
|
||
border-color: #484848;
|
||
color: #fff;
|
||
}
|
||
|
||
/* 文本样式类 */
|
||
.custom-label.style-default {
|
||
/* 默认样式在各个类型中已定义 */
|
||
}
|
||
|
||
.custom-label.style-blue {
|
||
background-color: rgba(52, 122, 226, 0.7) !important;
|
||
border-color: #347AE2 !important;
|
||
color: #fff !important;
|
||
}
|
||
|
||
.custom-label.style-white {
|
||
background-color: rgba(255, 255, 255, 0.7) !important;
|
||
border-color: #ffffff !important;
|
||
color: #333 !important;
|
||
}
|
||
</style> |