2091 lines
64 KiB
Vue
2091 lines
64 KiB
Vue
<template>
|
||
<!-- 检查URL参数中是否有Map=dev,如果有,则使用本地地图数据,否则使用阿里云地图数据 -->
|
||
<div class="chongqing-map-container">
|
||
<div ref="mapContainer" class="map-container"></div>
|
||
<div v-if="loading" class="loading-overlay">
|
||
<div class="loading-spinner"></div>
|
||
<span class="loading-text">地图加载中...</span>
|
||
</div>
|
||
<div v-if="error" class="error-overlay">
|
||
<span class="error-text">{{ error }}</span>
|
||
<button class="retry-btn" @click="loadMapData">重试</button>
|
||
</div>
|
||
<mapInfoDialog v-model:visible="mapInfoDialogVisible" :type="mapInfoDialogType" :data="mapInfoDialogData" />
|
||
<centerInfoCard
|
||
:visible="centerCardVisible"
|
||
:title="centerCardTitle"
|
||
:dataList="centerCardDataList"
|
||
@close="closeCenterCard"
|
||
@itemClick="handleCenterCardItemClick"
|
||
/>
|
||
|
||
<hazardPointSituationDialog :data="{}" />
|
||
</div>
|
||
</template>
|
||
|
||
<script setup>
|
||
import { ref, onMounted, onUnmounted, watch, defineExpose, h, render } from 'vue'
|
||
import axios from 'axios'
|
||
import { request } from '@/utils/request'
|
||
|
||
import projectIcon from '../../../assets/MaMap_img/项目@2x.png'
|
||
import bridgeIcon from '../../../assets/MaMap_img/桥梁icon@2x.png'
|
||
import tunnelIcon from '../../../assets/MaMap_img/蓝色@2x1.png'
|
||
import tunnelIcon2 from '../../../assets/MaMap_img/隧洞icon@2x.png'
|
||
import rescueTeamIcon from '../../../assets/MaMap_img/队伍icon@2x.png'
|
||
import engineeringIconIcon from '../../../assets/MaMap_img/危大工程icon@2x.png'
|
||
|
||
import hazardIconIcon1 from '../../../assets/MaMap_img/一般路内隐患点@2x.png'
|
||
import hazardIconIcon2 from '../../../assets/MaMap_img/一般路外隐患点@2x.png'
|
||
import hazardIconIcon3 from '../../../assets/MaMap_img/较大路内隐患点@2x.png'
|
||
import hazardIconIcon4 from '../../../assets/MaMap_img/较大路外隐患点@2x.png'
|
||
import hazardIconIcon5 from '../../../assets/MaMap_img/重大路内隐患点@2x.png'
|
||
import hazardIconIcon6 from '../../../assets/MaMap_img/重大路外隐患点@2x.png'
|
||
|
||
import tunnelLineIcon3 from '../../../assets/MaMap_img/高风险路段@2x.png'
|
||
import tunnelLineIcon2 from '../../../assets/MaMap_img/较高风险路段@2x.png'
|
||
import tunnelLineIcon1 from '../../../assets/MaMap_img/中风险路段@2x.png'
|
||
import tunnelLineIcon from '../../../assets/MaMap_img/线路icon定位@2x.png'
|
||
|
||
import mapInfoDialog from '../Dialog/mapInfoDialog.vue'
|
||
import centerInfoCard from '../Dialog/centerInfoCard.vue'
|
||
import hazardPointSituationDialog from '../Dialog/hazardPointSituationDialog.vue'
|
||
import I from '../../../../dist/cesium/Workers/upsampleVerticesFromCesium3DTilesTerrain'
|
||
|
||
const mapContainer = ref(null)
|
||
const loading = ref(false)
|
||
const error = ref(null)
|
||
let mapInstance = null
|
||
let geoJsonLayer = null
|
||
|
||
const props = defineProps({
|
||
activeitem: {
|
||
type: Object,
|
||
default: () => {},
|
||
},
|
||
dateRange: {
|
||
type: Array,
|
||
default: () => [],
|
||
},
|
||
roadItem: {
|
||
// 路段项 数据 主要是选中的路段等级
|
||
type: Object,
|
||
default: () => {
|
||
return {
|
||
label: '高风险',
|
||
}
|
||
},
|
||
},
|
||
})
|
||
|
||
watch(
|
||
() => props.roadItem,
|
||
async (newVal, oldVal) => {
|
||
console.log('newVal', newVal)
|
||
getAffectedRoadSectionData(false)
|
||
},
|
||
{ immediate: true },
|
||
)
|
||
// 定义 emits
|
||
const emit = defineEmits([
|
||
'districtClick',
|
||
'openTongnanTeam',
|
||
'openResponseSituation',
|
||
'openTongnanResponsible',
|
||
'riskPointStatsChange',
|
||
'update:roadvalArr',
|
||
'openHazardPointSituation',
|
||
'openRoadSectionSituation',
|
||
])
|
||
|
||
// 当前选中的区县
|
||
const selectedDistrict = ref(null)
|
||
let selectedLayer = null
|
||
|
||
// 地图信息弹窗
|
||
const mapInfoDialogVisible = ref(false)
|
||
const mapInfoDialogType = ref('project')
|
||
const mapInfoDialogData = ref({})
|
||
|
||
// 中心信息卡片弹窗
|
||
const centerCardVisible = ref(false)
|
||
const centerCardTitle = ref('调度统计')
|
||
const centerCardDataList = ref([])
|
||
|
||
// 地图上显示的区县卡片标记
|
||
let countyCardMarkers = []
|
||
|
||
// 打开地图信息弹窗
|
||
const openMapInfoDialog = (type, data) => {
|
||
mapInfoDialogType.value = type
|
||
mapInfoDialogData.value = data
|
||
mapInfoDialogVisible.value = true
|
||
}
|
||
|
||
// 清除地图上的区县卡片标记
|
||
const clearCountyCardMarkers = () => {
|
||
countyCardMarkers.forEach((marker) => {
|
||
// 清理 Vue 组件
|
||
if (marker._vueContainer) {
|
||
render(null, marker._vueContainer)
|
||
}
|
||
if (mapInstance) {
|
||
mapInstance.removeLayer(marker)
|
||
}
|
||
})
|
||
countyCardMarkers = []
|
||
}
|
||
|
||
// 在地图上显示区县卡片
|
||
const showCountyCardsOnMap = (dataList) => {
|
||
if (!mapInstance || !geoJsonLayer) {
|
||
console.warn('地图未初始化,无法显示区县卡片')
|
||
return
|
||
}
|
||
|
||
// 清除之前的卡片标记
|
||
clearCountyCardMarkers()
|
||
|
||
if (!dataList || dataList.length === 0) {
|
||
return
|
||
}
|
||
|
||
// 简化区县名称
|
||
const simplifyName = (name) => {
|
||
return name.replace('土家族苗族自治县', '').replace('苗族土家族自治县', '').replace('自治县', '').replace('区', '').replace('县', '')
|
||
}
|
||
|
||
// 遍历数据列表,为每个区县创建卡片
|
||
dataList.forEach((item) => {
|
||
// if (j.countyName === item.countyName) {
|
||
// item = j;
|
||
// }
|
||
const countyName = item.countyName || item.name
|
||
if (!countyName) return
|
||
|
||
const targetName = simplifyName(countyName)
|
||
|
||
// 查找对应的区县图层
|
||
let targetLayer = null
|
||
geoJsonLayer.eachLayer((layer) => {
|
||
const layerName = layer.feature?.properties?.name || ''
|
||
if (simplifyName(layerName) === targetName) {
|
||
targetLayer = layer
|
||
}
|
||
})
|
||
|
||
if (targetLayer) {
|
||
// 获取区县的中心点
|
||
const bounds = targetLayer.getBounds()
|
||
const center = bounds.getCenter()
|
||
|
||
// 创建一个容器元素用于挂载 Vue 组件
|
||
const container = document.createElement('div')
|
||
container.className = 'county-card-wrapper'
|
||
// 使用 Vue 的 h 函数创建组件虚拟节点
|
||
const vnode = h(centerInfoCard, {
|
||
visible: true,
|
||
title: countyName,
|
||
dataList: [item],
|
||
item: item,
|
||
onClose: () => {
|
||
closeCenterCard()
|
||
},
|
||
onItemClick: (clickedItem) => {
|
||
handleCenterCardItemClick(clickedItem)
|
||
},
|
||
})
|
||
|
||
// 渲染组件到容器
|
||
render(vnode, container)
|
||
|
||
// 创建自定义图标,使用渲染后的 HTML
|
||
const customIcon = window.L.divIcon({
|
||
className: 'county-card-icon',
|
||
html: container.innerHTML,
|
||
iconSize: [140, 40],
|
||
iconAnchor: [110, 60],
|
||
})
|
||
|
||
// 创建标记
|
||
const marker = window.L.marker(center, {
|
||
icon: customIcon,
|
||
interactive: true,
|
||
zIndexOffset: 2000, // 提升区县卡片层级,确保在最上层
|
||
})
|
||
|
||
// 添加点击事件到 marker
|
||
marker.on('click', () => {
|
||
// 调用处理函数
|
||
handleCenterCardItemClick(item)
|
||
// 移动地图到该位置
|
||
// mapInstance.setView(center, 10);
|
||
// 触发点击事件
|
||
emit('districtClick', {
|
||
name: countyName,
|
||
data: item,
|
||
type: item.type,
|
||
})
|
||
})
|
||
|
||
marker.addTo(mapInstance)
|
||
countyCardMarkers.push(marker)
|
||
|
||
// 保存容器引用以便后续清理
|
||
marker._vueContainer = container
|
||
}
|
||
})
|
||
|
||
// 调整地图视角以显示所有卡片
|
||
// if (countyCardMarkers.length > 0) {
|
||
// const group = new window.L.featureGroup(countyCardMarkers);
|
||
// mapInstance.fitBounds(group.getBounds().pad(0.2));
|
||
// }
|
||
}
|
||
|
||
// 获取公路类型文本
|
||
const getRoadTypeText = (roadType) => {
|
||
const roadTypeMap = {
|
||
national: '国省道',
|
||
rural: '农村公路',
|
||
}
|
||
return roadTypeMap[roadType] || roadType
|
||
}
|
||
|
||
// 打开中心信息卡片弹窗
|
||
const openCenterCard = (data) => {
|
||
centerCardDataList.value = data.dataList || []
|
||
centerCardTitle.value = data.title || '调度统计'
|
||
centerCardVisible.value = true
|
||
let affectedItems = []
|
||
// 遍历数据列表,只有影响区域的区县才添加到受影响列表中
|
||
data.dataList.forEach((item) => {
|
||
if (item.countyName && (item.countyName.includes('渝北') || item.countyName.includes('江北'))) {
|
||
item.countyName = '两江新区'
|
||
}
|
||
affectedCountyData.value.sortedList.forEach((j) => {
|
||
if (item.countyName.includes(j.name)) {
|
||
affectedItems.push(item)
|
||
}
|
||
})
|
||
})
|
||
|
||
// 在地图上显示区县卡片
|
||
showCountyCardsOnMap(affectedItems)
|
||
}
|
||
|
||
// 关闭中心信息卡片弹窗
|
||
const closeCenterCard = () => {
|
||
centerCardVisible.value = false
|
||
clearCountyCardMarkers()
|
||
}
|
||
|
||
// 处理中心卡片项点击
|
||
const handleCenterCardItemClick = (item) => {
|
||
console.log('点击了卡片项:', item)
|
||
// 根据区县名称定位地图
|
||
if (item.countyName || item.name) {
|
||
const countyName = item.countyName || item.name
|
||
locateToDistrict(countyName)
|
||
}
|
||
}
|
||
|
||
// 打开隧道信息弹窗(兼容旧代码)
|
||
const openTunnelDialog = (data) => {
|
||
openMapInfoDialog('tunnel', data)
|
||
}
|
||
|
||
// 格式化日期时间为接口所需格式
|
||
const formatDateTime = (date) => {
|
||
if (!date) return ''
|
||
const d = new Date(date)
|
||
const year = d.getFullYear()
|
||
const month = String(d.getMonth() + 1).padStart(2, '0')
|
||
const day = String(d.getDate()).padStart(2, '0')
|
||
const hours = String(d.getHours()).padStart(2, '0')
|
||
const minutes = String(d.getMinutes()).padStart(2, '0')
|
||
const seconds = String(d.getSeconds()).padStart(2, '0')
|
||
return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`
|
||
}
|
||
|
||
// 获取时间参数
|
||
const getTimeParams = (type) => {
|
||
let start = ''
|
||
let end = ''
|
||
if (props.dateRange && props.dateRange.length === 2) {
|
||
start = formatDateTime(props.dateRange[0]) || ''
|
||
end = formatDateTime(props.dateRange[1]) || ''
|
||
}
|
||
return {
|
||
start: start,
|
||
end: end,
|
||
}
|
||
}
|
||
|
||
// 受影响对象数据
|
||
const affectedCountyData = ref({
|
||
byName: {},
|
||
sortedList: [],
|
||
})
|
||
|
||
// 获取受影响对象数据
|
||
const getAffectedCountyData = async () => {
|
||
try {
|
||
const timeParams = getTimeParams()
|
||
const res = await request({
|
||
url: '/snow-ops-platform/weather-warning/affected-county',
|
||
method: 'GET',
|
||
params: timeParams,
|
||
})
|
||
console.log('受影响对象数据:', res)
|
||
clearProjectMarkers()
|
||
if (res.code === '00000' && res.data) {
|
||
// 统计各区县预警数量
|
||
const warningStats = countWarningsByCounty(res.data)
|
||
console.log('区县预警统计:', warningStats)
|
||
affectedCountyData.value = warningStats
|
||
|
||
getAffectedProjectData(true) // 获取受影响项目数据
|
||
getAffectedTunnelData(true) // 获取受影响隧道数据
|
||
getAffectedBridgeData(true) // 获取受影响桥梁数据
|
||
getDangerProjectData(true) // 获取危大工程数据
|
||
|
||
let item = { isWithinRedLine: '', label: '' }
|
||
for (let index = 0; index < 6; index++) {
|
||
if (index == 0) {
|
||
item = {
|
||
isWithinRedLine: '是',
|
||
label: '重大路内隐患点',
|
||
}
|
||
} else if (index == 1) {
|
||
item = {
|
||
isWithinRedLine: '否',
|
||
label: '重大路外隐患点',
|
||
}
|
||
} else if (index == 2) {
|
||
item = {
|
||
isWithinRedLine: '是',
|
||
label: '较大路内隐患点',
|
||
}
|
||
} else if (index == 3) {
|
||
item = {
|
||
isWithinRedLine: '否',
|
||
label: '较大路外隐患点',
|
||
}
|
||
} else if (index == 4) {
|
||
item = {
|
||
isWithinRedLine: '是',
|
||
label: '一般路内隐患点',
|
||
}
|
||
} else if (index == 5) {
|
||
item = {
|
||
isWithinRedLine: '是',
|
||
label: '一般路外隐患点',
|
||
}
|
||
} else if (index == 6) {
|
||
item = {
|
||
isWithinRedLine: '',
|
||
label: '',
|
||
}
|
||
}
|
||
if (item.label) {
|
||
handleHazardItemClick(item, true) // 获取风险点数据,第一次加载传入 true
|
||
}
|
||
}
|
||
|
||
// 获取受影响路段数据
|
||
for (let index = 0; index < 5; index++) {
|
||
console.log(index)
|
||
if (index == 0) {
|
||
props.roadItem.label = '高风险路段'
|
||
} else if (index == 1) {
|
||
props.roadItem.label = '较高风险路段'
|
||
} else if (index == 2) {
|
||
props.roadItem.label = '中风险路段'
|
||
} else if (index == 3) {
|
||
props.roadItem.label = '低风险路段'
|
||
} else if (index == 4) {
|
||
props.roadItem.label = ''
|
||
}
|
||
if (props.roadItem.label) {
|
||
getAffectedRoadSectionData(true)
|
||
}
|
||
}
|
||
|
||
// getEmergencyForceData();
|
||
|
||
loadMapData() // 加载地图数据
|
||
}
|
||
} catch (error) {
|
||
console.error('获取受影响对象数据失败:', error)
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 通用数据过滤方法 - 根据区县筛选数据并设置坐标
|
||
* @param {Array} dataList - 原始数据列表
|
||
* @param {boolean} flag - 是否只筛选受影响区县的数据
|
||
* @param {Object} options - 配置选项
|
||
* @param {string} options.countyField - 区县字段名
|
||
* @param {string} options.countyMatchField - 匹配区县时使用的字段名(如与countyField不同)
|
||
* @param {Function} options.coordinateParser - 坐标解析函数,接收item返回[lng, lat]
|
||
* @returns {Array} 过滤后的数据
|
||
*/
|
||
const filterDataByCounty = (dataList, flag, options) => {
|
||
const { countyField = 'COUNTY', countyMatchField, coordinateParser } = options
|
||
const matchField = countyMatchField || countyField
|
||
const filteredData = []
|
||
|
||
// 提取区县名称(去掉前缀和后缀)
|
||
const extractCountyName = (value) => {
|
||
if (!value) return ''
|
||
// 去掉"重庆市"前缀
|
||
let name = value.replace(/^重庆市/, '')
|
||
// 去掉"区"、"县"后缀
|
||
name = name.replace(/(区|县)$/, '')
|
||
return name
|
||
}
|
||
|
||
dataList.forEach((item) => {
|
||
// 区县名称处理:江北/渝北 -> 两江新区
|
||
const countyValue = item[countyField]
|
||
if (countyValue && (countyValue.includes('江北') || countyValue.includes('渝北'))) {
|
||
item[countyField] = '两江新区'
|
||
}
|
||
|
||
const matchValue = item[matchField]
|
||
// 提取纯净的区县名称用于匹配
|
||
const extractedCountyName = extractCountyName(matchValue)
|
||
|
||
if (!flag) {
|
||
// 不过滤,直接添加所有数据
|
||
item.COORDINATE_POINT = coordinateParser(item)
|
||
filteredData.push(item)
|
||
} else {
|
||
// 根据受影响区县列表进行过滤
|
||
const isMatched = affectedCountyData.value.sortedList.some((j) => {
|
||
// 同时支持原始匹配和提取后的区县名称匹配
|
||
return matchValue && j.name && (matchValue.includes(j.name) || extractedCountyName === j.name)
|
||
})
|
||
if (isMatched) {
|
||
item.COORDINATE_POINT = coordinateParser(item)
|
||
filteredData.push(item)
|
||
}
|
||
}
|
||
})
|
||
|
||
return filteredData
|
||
}
|
||
|
||
const affectedBridgeData = ref([])
|
||
// 获取受影响桥梁数据
|
||
const getAffectedBridgeData = async (flag) => {
|
||
try {
|
||
// 检查桥梁标记是否已存在,如果存在则不加载接口
|
||
if (hasProjectMarkersByType('bridge')) {
|
||
console.log('类型为 bridge 的标记已存在,跳过接口调用')
|
||
return []
|
||
}
|
||
|
||
const timeParams = getTimeParams()
|
||
const res = await request({
|
||
url: '/snow-ops-platform/map/bridge',
|
||
method: 'GET',
|
||
params: timeParams,
|
||
})
|
||
if (res.data) {
|
||
// 使用通用过滤方法处理数据
|
||
const filteredData = filterDataByCounty(res.data, flag, {
|
||
countyField: 'COUNTY',
|
||
countyMatchField: 'GL1_QXMC',
|
||
coordinateParser: (item) => [item.GL1_QLJD, item.GL1_QLWD],
|
||
})
|
||
affectedBridgeData.value = filteredData
|
||
console.log('受影响桥梁数据:', affectedBridgeData.value)
|
||
addProjectMarkers(affectedBridgeData.value, bridgeIcon, 'bridge')
|
||
}
|
||
} catch (error) {
|
||
console.error('获取受影响桥梁数据失败:', error)
|
||
return []
|
||
}
|
||
}
|
||
|
||
const tunnelInfoDialogRef = ref([])
|
||
// 获取受影响隧道数据
|
||
const getAffectedTunnelData = async (flag) => {
|
||
try {
|
||
// 检查隧道标记是否已存在,如果存在则不加载接口
|
||
if (hasProjectMarkersByType('tunnel')) {
|
||
console.log('类型为 tunnel 的标记已存在,跳过接口调用')
|
||
return []
|
||
}
|
||
|
||
const timeParams = getTimeParams()
|
||
const res = await request({
|
||
url: '/snow-ops-platform/map/tunnel',
|
||
method: 'GET',
|
||
params: timeParams,
|
||
})
|
||
if (res.code === '00000' && res.data) {
|
||
// 使用通用过滤方法处理数据
|
||
const filteredData = filterDataByCounty(res.data, flag, {
|
||
countyField: 'COUNTY',
|
||
countyMatchField: 'GL1_QXMC',
|
||
coordinateParser: (item) => [Number(item.GL1_SDJD2), Number(item.GL1_SDWD2)],
|
||
})
|
||
tunnelInfoDialogRef.value = filteredData
|
||
console.log('受影响隧道数据:', tunnelInfoDialogRef.value)
|
||
addProjectMarkers(tunnelInfoDialogRef.value, tunnelIcon2, 'tunnel')
|
||
}
|
||
return []
|
||
} catch (error) {
|
||
console.error('获取受影响隧道数据失败:', error)
|
||
return []
|
||
}
|
||
}
|
||
|
||
// 获取受影响项目数据
|
||
const affectedProjectData = ref([])
|
||
const getAffectedProjectData = async (flag) => {
|
||
try {
|
||
// 检查项目标记是否已存在,如果存在则不加载接口
|
||
if (hasProjectMarkersByType('project')) {
|
||
console.log('类型为 project 的标记已存在,跳过接口调用')
|
||
return []
|
||
}
|
||
|
||
const timeParams = getTimeParams()
|
||
const res = await request({
|
||
url: '/snow-ops-platform/map/project',
|
||
method: 'GET',
|
||
params: timeParams,
|
||
})
|
||
console.log('受影响项目数据:', res)
|
||
if (res.code === '00000' && res.data) {
|
||
console.log('项目数据条数:', res.data.length)
|
||
// 预处理:解析坐标格式
|
||
const preprocessedData = res.data.map((item) => {
|
||
const newItem = { ...item }
|
||
if (item.COORDINATE_POINT) {
|
||
console.log('原始坐标:', item.COORDINATE_POINT)
|
||
// 解析格式: "106.44E,29.62N" -> [经度, 纬度]
|
||
const coordStr = item.COORDINATE_POINT.trim()
|
||
// 按逗号分割
|
||
const parts = coordStr.split(',')
|
||
if (parts.length === 2) {
|
||
// 去掉 E/N 后缀并转换为数字
|
||
const lng = parseFloat(parts[0].replace(/E$/i, '').trim())
|
||
const lat = parseFloat(parts[1].replace(/N$/i, '').trim())
|
||
if (!isNaN(lng) && !isNaN(lat)) {
|
||
newItem.COORDINATE_POINT_PARSED = [lng, lat]
|
||
}
|
||
}
|
||
console.log('解析后坐标:', newItem.COORDINATE_POINT_PARSED)
|
||
}
|
||
return newItem
|
||
})
|
||
// 使用通用过滤方法处理数据
|
||
const filteredData = filterDataByCounty(preprocessedData, flag, {
|
||
countyField: 'ADMINISTRATIVE_REGION',
|
||
coordinateParser: (item) => item.COORDINATE_POINT_PARSED || [],
|
||
})
|
||
affectedProjectData.value = filteredData
|
||
// 在地图上添加项目标记
|
||
console.log('开始添加项目标记...')
|
||
addProjectMarkers(filteredData, projectIcon, 'project')
|
||
} else {
|
||
console.warn('没有获取到项目数据或返回码错误:', res)
|
||
}
|
||
return res.data || []
|
||
} catch (error) {
|
||
console.error('获取受影响项目数据失败:', error)
|
||
return []
|
||
}
|
||
}
|
||
|
||
// 获取受影响路段数据
|
||
const affectedRoadSectionData = ref([])
|
||
const roadvalArr = ref([
|
||
{ label: '高风险', value: 0, show: false },
|
||
{ label: '较高风险', value: 0, show: false },
|
||
{ label: '中风险', value: 0, show: false },
|
||
{ label: '低风险', value: 0, show: false },
|
||
])
|
||
const getAffectedRoadSectionData = async (flag) => {
|
||
try {
|
||
console.log('受影响路段数据:', props.roadItem)
|
||
const { start, end } = getTimeParams()
|
||
let riskLevel = ''
|
||
if (props.roadItem.label == '高风险路段') {
|
||
riskLevel = '高风险'
|
||
} else if (props.roadItem.label == '较高风险路段') {
|
||
riskLevel = '较高风险'
|
||
} else if (props.roadItem.label == '中风险路段') {
|
||
riskLevel = '中风险'
|
||
} else if (props.roadItem.label == '低风险路段') {
|
||
riskLevel = '低风险'
|
||
}
|
||
|
||
// 检查该等级的路段标记是否已存在,如果存在则不加载接口
|
||
const typeKey = `road-${riskLevel}`
|
||
if (hasProjectMarkersByType(typeKey)) {
|
||
console.log(`类型为 ${typeKey} 的标记已存在,跳过接口调用`)
|
||
return []
|
||
}
|
||
|
||
const res = await request({
|
||
// url: '/snow-ops-platform/weather-warning/affected-object/road-section',
|
||
url: '/snow-ops-platform/risk-point/road-section',
|
||
method: 'GET',
|
||
params: {
|
||
riskLevel: riskLevel,
|
||
isWithinRedLine: '是',
|
||
start: start,
|
||
end: end,
|
||
},
|
||
})
|
||
console.log('受影响路段数据:', res)
|
||
|
||
if (res.code === '00000' && res.data) {
|
||
// 使用通用过滤方法处理数据
|
||
const filteredData = filterDataByCounty(res.data, flag, {
|
||
countyField: 'COUNTY',
|
||
countyMatchField: 'GL1_QXMC',
|
||
coordinateParser: (item) => [item.GL1_LON, item.GL1_LAT],
|
||
})
|
||
affectedRoadSectionData.value = filteredData
|
||
// 在地图上添加项目标记
|
||
console.log('受影响路段数据:', affectedRoadSectionData.value)
|
||
|
||
let img = tunnelLineIcon
|
||
if (riskLevel == '高风险') {
|
||
img = tunnelLineIcon3
|
||
roadvalArr.value[0] = {
|
||
label: '高风险',
|
||
value: affectedRoadSectionData.value.length,
|
||
show: true,
|
||
}
|
||
} else if (riskLevel == '较高风险') {
|
||
img = tunnelLineIcon2
|
||
roadvalArr.value[1] = {
|
||
label: '较高风险',
|
||
value: affectedRoadSectionData.value.length,
|
||
show: true,
|
||
}
|
||
} else if (riskLevel == '中风险') {
|
||
img = tunnelLineIcon1
|
||
roadvalArr.value[2] = {
|
||
label: '中风险',
|
||
value: affectedRoadSectionData.value.length,
|
||
show: true,
|
||
}
|
||
} else if (riskLevel == '低风险') {
|
||
img = tunnelLineIcon
|
||
roadvalArr.value[3] = {
|
||
label: '低风险',
|
||
value: affectedRoadSectionData.value.length,
|
||
show: true,
|
||
}
|
||
}
|
||
emit('update:roadvalArr ==== 风险点总数', roadvalArr.value)
|
||
|
||
// 给数据添加风险等级字段,确保 addProjectMarkers 能正确识别类型键
|
||
const dataWithRiskLevel = affectedRoadSectionData.value.map((item) => ({
|
||
...item,
|
||
riskLevel: riskLevel,
|
||
}))
|
||
addProjectMarkers(dataWithRiskLevel, img, 'road')
|
||
}
|
||
return []
|
||
} catch (error) {
|
||
console.error('获取受影响路段数据失败:', error)
|
||
return []
|
||
}
|
||
}
|
||
|
||
const emergencyForceData = ref([])
|
||
// 获取应急力量数据
|
||
const getEmergencyForceData = async () => {
|
||
try {
|
||
// 检查应急力量标记是否已存在,如果存在则不加载接口
|
||
if (hasProjectMarkersByType('emergency')) {
|
||
console.log('类型为 emergency 的标记已存在,跳过接口调用')
|
||
return []
|
||
}
|
||
|
||
const timeParams = getTimeParams()
|
||
const res = await request({
|
||
url: '/snow-ops-platform/yhYjll/listForcesAndMaterials',
|
||
method: 'GET',
|
||
params: timeParams,
|
||
})
|
||
console.log('应急力量数据:', res)
|
||
if (res.code === '00000' && res.data) {
|
||
// 解析坐标数据
|
||
res.data.forEach((item) => {
|
||
item.COORDINATE_POINT = [item.lng, item.lat]
|
||
console.log('解析后坐标:', item.COORDINATE_POINT)
|
||
})
|
||
emergencyForceData.value = res.data
|
||
|
||
// 在地图上添加应急力量标记
|
||
console.log('开始添加应急力量标记...', res.data)
|
||
addProjectMarkers(res.data, rescueTeamIcon, 'emergency')
|
||
} else {
|
||
console.warn('没有获取到应急力量数据或返回码错误:', res)
|
||
}
|
||
return res.data || []
|
||
} catch (error) {
|
||
console.error('获取应急力量数据失败:', error)
|
||
return []
|
||
}
|
||
}
|
||
|
||
const dangerProjectData = ref([])
|
||
// 获取危大工程数据
|
||
const getDangerProjectData = async (flag) => {
|
||
try {
|
||
// 检查危大工程标记是否已存在,如果存在则不加载接口
|
||
if (hasProjectMarkersByType('engineering')) {
|
||
console.log('类型为 engineering 的标记已存在,跳过接口调用')
|
||
return []
|
||
}
|
||
|
||
const res = await request({
|
||
url: '/snow-ops-platform/dangerProjectInfo/listAll',
|
||
method: 'GET',
|
||
})
|
||
console.log('危大工程数据:', res)
|
||
if (res.code === '00000' && res.data) {
|
||
// 使用通用过滤方法处理数据
|
||
const filteredData = filterDataByCounty(res.data, flag, {
|
||
countyField: 'districtName',
|
||
coordinateParser: (item) => [item.longitude, item.latitude],
|
||
})
|
||
dangerProjectData.value = filteredData
|
||
console.log('过滤后危大工程数据:', filteredData)
|
||
|
||
// 在地图上添加危大工程标记
|
||
console.log('开始添加危大工程标记...', filteredData)
|
||
addProjectMarkers(filteredData, engineeringIconIcon, 'engineering')
|
||
} else {
|
||
console.warn('没有获取到危大工程数据或返回码错误:', res)
|
||
}
|
||
return res.data || []
|
||
} catch (error) {
|
||
console.error('获取危大工程数据失败:', error)
|
||
return []
|
||
}
|
||
}
|
||
|
||
// 风险点数据
|
||
const riskPointData = ref([])
|
||
// 获取风险点数据
|
||
const getRiskPointData = async (riskLevel, item, flag) => {
|
||
try {
|
||
// 根据风险等级和在红线内外确定类型键
|
||
let riskTypeKey = 'riskPoint'
|
||
if (riskLevel) {
|
||
if (riskLevel.includes('重大') && item.isWithinRedLine === '是') {
|
||
riskTypeKey = 'riskPoint-重大路内'
|
||
} else if (riskLevel.includes('重大') && item.isWithinRedLine === '否') {
|
||
riskTypeKey = 'riskPoint-重大路外'
|
||
} else if (riskLevel.includes('较大') && item.isWithinRedLine === '是') {
|
||
riskTypeKey = 'riskPoint-较大路内'
|
||
} else if (riskLevel.includes('较大') && item.isWithinRedLine === '否') {
|
||
riskTypeKey = 'riskPoint-较大路外'
|
||
} else if (riskLevel.includes('一般') && item.isWithinRedLine === '是') {
|
||
riskTypeKey = 'riskPoint-一般路内'
|
||
} else if (riskLevel.includes('一般') && item.isWithinRedLine === '否') {
|
||
riskTypeKey = 'riskPoint-一般路外'
|
||
}
|
||
}
|
||
|
||
// 检查该类型的风险点标记是否已存在,如果存在则删除旧图标,加载最新图标
|
||
if (hasProjectMarkersByType(riskTypeKey)) {
|
||
console.log(`类型为 ${riskTypeKey} 的标记已存在,删除旧图标并重新加载`)
|
||
clearProjectMarkersByType(riskTypeKey)
|
||
}
|
||
|
||
const res = await request({
|
||
url: '/snow-ops-platform/risk-point',
|
||
method: 'GET',
|
||
params: {
|
||
riskLevel: riskLevel || '',
|
||
isWithinRedLine: item.isWithinRedLine,
|
||
},
|
||
})
|
||
console.log('风险点数据:', res)
|
||
if (res.code === '00000' && res.data) {
|
||
// 解析坐标数据
|
||
const filteredData = filterDataByCounty(res.data, flag, {
|
||
countyField: 'DistrictRICT_NAME',
|
||
countyMatchField: 'GL1_QXMC',
|
||
coordinateParser: (item) => [item.GL1_LON, item.GL1_LAT],
|
||
})
|
||
filteredData.forEach((item) => {
|
||
item.COORDINATE_POINT = [item.GL1_LON, item.GL1_LAT]
|
||
})
|
||
riskPointData.value = filteredData
|
||
// 在地图上添加风险点标记
|
||
console.log('开始添加风险点标记...', filteredData)
|
||
// 根据风险等级选择不同的图标
|
||
let iconUrl = hazardIconIcon1 // 默认使用一般路内隐患点图标
|
||
if (riskLevel) {
|
||
if (riskLevel.includes('重大') && item.isWithinRedLine === '是') {
|
||
iconUrl = hazardIconIcon5 // 重大路内隐患点
|
||
} else if (riskLevel.includes('重大') && item.isWithinRedLine === '否') {
|
||
iconUrl = hazardIconIcon6 // 重大路外隐患点
|
||
} else if (riskLevel.includes('较大') && item.isWithinRedLine === '是') {
|
||
iconUrl = hazardIconIcon3 // 较大路内隐患点
|
||
} else if (riskLevel.includes('较大') && item.isWithinRedLine === '否') {
|
||
iconUrl = hazardIconIcon4 // 较大路外隐患点
|
||
} else if (riskLevel.includes('一般') && item.isWithinRedLine === '是') {
|
||
iconUrl = hazardIconIcon1 // 一般路内隐患点
|
||
} else if (riskLevel.includes('一般') && item.isWithinRedLine === '否') {
|
||
iconUrl = hazardIconIcon2 // 一般路外隐患点
|
||
}
|
||
}
|
||
|
||
emit('riskPointStatsChange', {
|
||
riskLevel: riskLevel.substring(0, 2),
|
||
isWithinRedLine: item.isWithinRedLine,
|
||
value: riskPointData.value.length,
|
||
})
|
||
// 给数据添加风险等级和在红线内外字段,确保 addProjectMarkers 能正确识别类型键
|
||
const dataWithRiskInfo = riskPointData.value.map((riskItem) => ({
|
||
...riskItem,
|
||
riskLevel: riskLevel,
|
||
isWithinRedLine: item.isWithinRedLine,
|
||
}))
|
||
addProjectMarkers(dataWithRiskInfo, iconUrl, 'riskPoint')
|
||
// 获取风险点统计数据
|
||
} else {
|
||
console.warn('没有获取到风险点数据或返回码错误:', res)
|
||
}
|
||
return res.data || []
|
||
} catch (error) {
|
||
console.error('获取风险点数据失败:', error)
|
||
return []
|
||
}
|
||
}
|
||
|
||
// 按类型存储项目标记,避免同类型重复刷新
|
||
// 格式: { 'road-高风险': [...], 'road-较高风险': [...], 'riskPoint-高风险': [...], ... }
|
||
let projectMarkersMap = new Map()
|
||
|
||
// 清除所有项目标记
|
||
const clearProjectMarkers = () => {
|
||
projectMarkersMap.forEach((markers, type) => {
|
||
markers.forEach((marker) => {
|
||
if (mapInstance) {
|
||
mapInstance.removeLayer(marker)
|
||
}
|
||
})
|
||
})
|
||
projectMarkersMap.clear()
|
||
clearCountyCardMarkers()
|
||
|
||
// 关闭所有弹窗
|
||
mapInfoDialogVisible.value = false
|
||
centerCardVisible.value = false
|
||
}
|
||
|
||
// 清除指定类型的项目标记
|
||
const clearProjectMarkersByType = (typeKey) => {
|
||
const markers = projectMarkersMap.get(typeKey)
|
||
if (markers) {
|
||
markers.forEach((marker) => {
|
||
if (mapInstance) {
|
||
mapInstance.removeLayer(marker)
|
||
}
|
||
})
|
||
projectMarkersMap.delete(typeKey)
|
||
console.log(`已清除类型为 ${typeKey} 的标记,共 ${markers.length} 个`)
|
||
}
|
||
}
|
||
|
||
// 检查指定类型的项目标记是否已存在
|
||
const hasProjectMarkersByType = (typeKey) => {
|
||
const markers = projectMarkersMap.get(typeKey)
|
||
return markers && markers.length > 0
|
||
}
|
||
|
||
// 根据数据类型获取对应的图标
|
||
const getIconByType = (item, type) => {
|
||
// 如果是风险点类型,根据风险等级和是否在红线内返回对应图标
|
||
if (type === 'riskPoint' || item.riskLevel) {
|
||
const riskLevel = item.riskLevel || ''
|
||
const isWithinRedLine = item.isWithinRedLine || item.iswithinredline || ''
|
||
|
||
if (riskLevel.includes('重大')) {
|
||
return isWithinRedLine === '是' ? hazardIconIcon5 : hazardIconIcon6
|
||
} else if (riskLevel.includes('较大')) {
|
||
return isWithinRedLine === '是' ? hazardIconIcon3 : hazardIconIcon4
|
||
} else if (riskLevel.includes('一般')) {
|
||
return isWithinRedLine === '是' ? hazardIconIcon1 : hazardIconIcon2
|
||
}
|
||
}
|
||
|
||
// 如果是路段类型,根据风险等级返回对应图标
|
||
if (type === 'road' || item.riskLevel) {
|
||
const riskLevel = item.riskLevel || ''
|
||
if (riskLevel.includes('高风险')) {
|
||
return tunnelLineIcon3
|
||
} else if (riskLevel.includes('较高风险')) {
|
||
return tunnelLineIcon2
|
||
} else if (riskLevel.includes('中风险')) {
|
||
return tunnelLineIcon1
|
||
} else if (riskLevel.includes('低风险')) {
|
||
return tunnelLineIcon
|
||
}
|
||
}
|
||
|
||
// 默认返回传入的图标或项目图标
|
||
return projectIcon
|
||
}
|
||
|
||
// 在地图上添加项目标记
|
||
const addProjectMarkers = (data, iconUrl, type = 'project') => {
|
||
if (!mapInstance) {
|
||
console.warn('mapInstance 未初始化,无法添加标记')
|
||
return
|
||
}
|
||
if (!data || data.length === 0) {
|
||
console.warn('没有数据,无法添加标记')
|
||
return
|
||
}
|
||
|
||
// 根据类型和等级生成类型键,用于区分不同类型的标记
|
||
// 例如: 'road-高风险', 'road-较高风险', 'riskPoint-高风险', 'project', 'tunnel', 'bridge'
|
||
let typeKey = type
|
||
|
||
// 对于路段类型,从数据中解析风险等级
|
||
if (type === 'road' && data.length > 0) {
|
||
const riskLevel = data[0].GL1_FXDJ || data[0].riskLevel || ''
|
||
if (riskLevel) {
|
||
typeKey = `road-${riskLevel}`
|
||
}
|
||
}
|
||
|
||
// 对于风险点类型,从数据中解析风险等级和在红线内外
|
||
if (type === 'riskPoint' && data.length > 0) {
|
||
const riskLevel = data[0].GL1_FXDJ || data[0].riskLevel || ''
|
||
const isWithinRedLine = data[0].GL1_SFHXN || data[0].isWithinRedLine || ''
|
||
if (riskLevel) {
|
||
// 根据风险等级和在红线内外确定类型键
|
||
if (riskLevel.includes('重大') && isWithinRedLine === '是') {
|
||
typeKey = 'riskPoint-重大路内'
|
||
} else if (riskLevel.includes('重大') && isWithinRedLine === '否') {
|
||
typeKey = 'riskPoint-重大路外'
|
||
} else if (riskLevel.includes('较大') && isWithinRedLine === '是') {
|
||
typeKey = 'riskPoint-较大路内'
|
||
} else if (riskLevel.includes('较大') && isWithinRedLine === '否') {
|
||
typeKey = 'riskPoint-较大路外'
|
||
} else if (riskLevel.includes('一般') && isWithinRedLine === '是') {
|
||
typeKey = 'riskPoint-一般路内'
|
||
} else if (riskLevel.includes('一般') && isWithinRedLine === '否') {
|
||
typeKey = 'riskPoint-一般路外'
|
||
} else {
|
||
typeKey = `riskPoint-${riskLevel}`
|
||
}
|
||
}
|
||
}
|
||
|
||
// 如果该类型的标记已存在,直接返回,避免重复渲染
|
||
if (hasProjectMarkersByType(typeKey)) {
|
||
console.log(`类型为 ${typeKey} 的标记已存在,跳过添加`)
|
||
return
|
||
}
|
||
|
||
// 存储新添加的标记
|
||
const newMarkers = []
|
||
|
||
// 遍历数据添加标记
|
||
data.forEach((item) => {
|
||
if (item && item.COORDINATE_POINT && item.COORDINATE_POINT.length == 2) {
|
||
// COORDINATE_POINT 格式: [经度, 纬度]
|
||
// Leaflet 需要: [纬度, 经度]
|
||
const lng = item.COORDINATE_POINT[0]
|
||
const lat = item.COORDINATE_POINT[1]
|
||
|
||
const latNum = parseFloat(lat)
|
||
const lngNum = parseFloat(lng)
|
||
|
||
if (!isNaN(latNum) && !isNaN(lngNum)) {
|
||
// 根据数据类型获取对应的图标
|
||
const currentIconUrl = iconUrl || getIconByType(item, type)
|
||
|
||
// 创建自定义图标
|
||
const projectIconObj = window.L.icon({
|
||
iconUrl: currentIconUrl,
|
||
iconSize: [35, 35],
|
||
iconAnchor: [10, 10],
|
||
popupAnchor: [0, -10],
|
||
})
|
||
|
||
const marker = window.L.marker([latNum, lngNum], {
|
||
icon: projectIconObj,
|
||
zIndexOffset: 1000, // 提升图标层级,确保在地图其他元素之上
|
||
})
|
||
|
||
// 点击 marker 打开对应类型的信息弹窗
|
||
marker.on('click', () => {
|
||
if (type === 'riskPoint') {
|
||
// 风险点类型,触发父组件打开涉灾隐患点情况弹窗
|
||
emit('openHazardPointSituation', item)
|
||
} else if (type === 'road') {
|
||
// 路段类型,触发父组件打开路段情况弹窗
|
||
emit('openRoadSectionSituation', item)
|
||
} else {
|
||
openMapInfoDialog(type, item)
|
||
}
|
||
})
|
||
|
||
marker.addTo(mapInstance)
|
||
newMarkers.push(marker)
|
||
} else {
|
||
console.warn('无效的坐标:', item.COORDINATE_POINT, item)
|
||
}
|
||
} else {
|
||
console.warn('缺少坐标数据:', item)
|
||
}
|
||
})
|
||
|
||
// 将新标记存储到对应类型中
|
||
projectMarkersMap.set(typeKey, newMarkers)
|
||
console.log(`已添加 ${newMarkers.length} 个类型为 ${typeKey} 的标记`)
|
||
}
|
||
|
||
// 确定主要预警颜色(出现最多的预警等级)
|
||
const getMainWarningColor = (levels) => {
|
||
if (!levels || Object.keys(levels).length === 0) return '#1890ff' // 默认蓝色
|
||
|
||
// 预警等级优先级:红色 > 橙色 > 黄色 > 蓝色
|
||
const priority = ['红色', '橙色', '黄色', '蓝色']
|
||
|
||
// 按数量排序
|
||
const sortedLevels = Object.entries(levels)
|
||
.map(([level, count]) => ({ level, count }))
|
||
.sort((a, b) => b.count - a.count)
|
||
|
||
// 如果有多个等级数量相同,按优先级排序
|
||
const maxCount = sortedLevels[0].count
|
||
const maxLevels = sortedLevels.filter((item) => item.count === maxCount)
|
||
|
||
if (maxLevels.length === 1) {
|
||
return getColorByLevel(maxLevels[0].level)
|
||
} else {
|
||
// 按优先级选择
|
||
for (const level of priority) {
|
||
if (maxLevels.some((item) => item.level === level)) {
|
||
return getColorByLevel(level)
|
||
}
|
||
}
|
||
return getColorByLevel(maxLevels[0].level)
|
||
}
|
||
}
|
||
|
||
// 统计各区县的预警数量(按预警等级区分)
|
||
const countWarningsByCounty = (data) => {
|
||
if (!data || !Array.isArray(data)) return {}
|
||
|
||
const stats = {}
|
||
data.forEach((item) => {
|
||
// 根据实际数据结构调整字段名
|
||
const rawCountyName = item.countyName || item.name || item.qxmc || item.gl1Qxmc
|
||
const riskLevel = item.riskLevel || item.level || item.warningLevel || item.gl1Yjdj
|
||
|
||
// 简化区县名称
|
||
const countyName = simplifyDistrictName(rawCountyName)
|
||
|
||
if (countyName) {
|
||
if (!stats[countyName]) {
|
||
stats[countyName] = {
|
||
total: 0,
|
||
levels: {},
|
||
}
|
||
}
|
||
|
||
// 统计总数
|
||
stats[countyName].total += 1
|
||
|
||
// 按预警等级统计
|
||
const level = riskLevel || '未知'
|
||
if (stats[countyName].levels[level]) {
|
||
stats[countyName].levels[level] += 1
|
||
} else {
|
||
stats[countyName].levels[level] = 1
|
||
}
|
||
}
|
||
})
|
||
|
||
// 合并渝北区和江北区为两江新区
|
||
if (stats['渝北区'] || stats['江北区']) {
|
||
const yubeiData = stats['渝北区'] || {
|
||
total: 0,
|
||
levels: {},
|
||
}
|
||
const jiangbeiData = stats['江北区'] || {
|
||
total: 0,
|
||
levels: {},
|
||
}
|
||
|
||
// 合并总数
|
||
const total = yubeiData.total + jiangbeiData.total
|
||
|
||
// 合并各等级预警数量
|
||
const levels = { ...yubeiData.levels }
|
||
Object.entries(jiangbeiData.levels).forEach(([level, count]) => {
|
||
if (levels[level]) {
|
||
levels[level] += count
|
||
} else {
|
||
levels[level] = count
|
||
}
|
||
})
|
||
|
||
// 创建两江新区数据
|
||
stats['两江新区'] = {
|
||
total,
|
||
levels,
|
||
}
|
||
|
||
// 删除渝北区和江北区
|
||
delete stats['渝北区']
|
||
delete stats['江北区']
|
||
}
|
||
|
||
// 转换为数组格式并按总数排序(数量从大到小)
|
||
const sortedList = Object.entries(stats)
|
||
.map(([name, data]) => ({
|
||
name: name.substring(0, 2),
|
||
total: data.total,
|
||
levels: data.levels,
|
||
}))
|
||
.sort((a, b) => b.total - a.total)
|
||
|
||
return {
|
||
byName: stats, // 对象格式:{ 区县名: { total: 总数, levels: { 等级: 数量 } } }
|
||
sortedList: sortedList, // 数组格式:[{ name: 区县名, total: 总数, levels: { 等级: 数量 } }]
|
||
}
|
||
}
|
||
|
||
// 重庆地图 GeoJSON API 地址 - 根据 URL 参数动态判断
|
||
const getGeoJsonUrl = () => {
|
||
const urlParams = new URLSearchParams(window.location.search)
|
||
const mapParam = urlParams.get('Map')
|
||
if (mapParam === 'dev') {
|
||
return 'https://geo.datav.aliyun.com/areas_v3/bound/500000_full.json'
|
||
}
|
||
return 'http://58.144.223.142:11501/aliyun-geo/bound/500000_full.json'
|
||
}
|
||
const GEOJSON_URL = getGeoJsonUrl()
|
||
|
||
// 天地图配置缓存
|
||
let tiandituConfig = null
|
||
|
||
// 获取天地图配置
|
||
const getTiandituConfig = async () => {
|
||
// 如果已有缓存,直接返回
|
||
if (tiandituConfig) {
|
||
return tiandituConfig
|
||
}
|
||
|
||
try {
|
||
const res = await request({
|
||
url: '/snow-ops-platform/dataDirectory/queryCatalog',
|
||
method: 'GET',
|
||
params: {
|
||
pcatalog: 'TDT',
|
||
},
|
||
})
|
||
|
||
if (res.code === '00000' && res.data && res.data.length > 0) {
|
||
// 查找天地图配置
|
||
const tiandituItem = res.data.find((item) => item.Name === '天地图' || item.Name?.includes('天地图'))
|
||
|
||
if (tiandituItem && tiandituItem.Children && tiandituItem.Children.length > 0) {
|
||
// 查找影像底图
|
||
const imgLayer = tiandituItem.Children.find((child) => child.Name?.includes('影像') || child.Name?.includes('底图'))
|
||
|
||
// 查找行政区划图层
|
||
const boundaryLayer = tiandituItem.Children.find((child) => child.Name?.includes('行政区划') || child.Name?.includes('边界'))
|
||
|
||
const config = {}
|
||
|
||
if (imgLayer && imgLayer.Attribute && imgLayer.Attribute.servicePath) {
|
||
config.imgLayer = {
|
||
name: imgLayer.Name,
|
||
servicePath: imgLayer.Attribute.servicePath,
|
||
}
|
||
}
|
||
|
||
if (boundaryLayer && boundaryLayer.Attribute && boundaryLayer.Attribute.servicePath) {
|
||
config.boundaryLayer = {
|
||
name: boundaryLayer.Name,
|
||
servicePath: boundaryLayer.Attribute.servicePath,
|
||
}
|
||
}
|
||
|
||
if (Object.keys(config).length > 0) {
|
||
tiandituConfig = config
|
||
console.log('获取天地图配置成功:', tiandituConfig)
|
||
return tiandituConfig
|
||
}
|
||
}
|
||
}
|
||
|
||
console.warn('未找到天地图配置,使用默认配置')
|
||
return null
|
||
} catch (error) {
|
||
console.error('获取天地图配置失败:', error)
|
||
return null
|
||
}
|
||
}
|
||
|
||
// 加载地图数据
|
||
const loadMapData = async () => {
|
||
loading.value = true
|
||
error.value = null
|
||
|
||
try {
|
||
// 初始化地图 - 使用天地图配置
|
||
await initMap()
|
||
} catch (err) {
|
||
console.error('加载地图数据失败:', err)
|
||
error.value = '地图数据加载失败,请检查网络连接'
|
||
} finally {
|
||
loading.value = false
|
||
}
|
||
}
|
||
|
||
// 处理行政区划合并
|
||
const processDistrictMerge = (geoJsonData) => {
|
||
if (!geoJsonData || !geoJsonData.features) return
|
||
|
||
// 查找渝北区和江北区的特征
|
||
const yubeiIndex = geoJsonData.features.findIndex((f) => f.properties && f.properties.name === '渝北区')
|
||
const jiangbeiIndex = geoJsonData.features.findIndex((f) => f.properties && f.properties.name === '江北区')
|
||
|
||
// 如果两个区都存在,合并为两江新区
|
||
if (yubeiIndex !== -1 && jiangbeiIndex !== -1) {
|
||
const yubeiFeature = geoJsonData.features[yubeiIndex]
|
||
const jiangbeiFeature = geoJsonData.features[jiangbeiIndex]
|
||
|
||
// 创建两江新区的特征
|
||
const liangjiangFeature = JSON.parse(JSON.stringify(yubeiFeature))
|
||
liangjiangFeature.properties.name = '两江新区'
|
||
liangjiangFeature.properties.adcode = '500006' // 使用新的行政区划代码
|
||
|
||
// 如果是 MultiPolygon,合并两个区的坐标
|
||
if (yubeiFeature.geometry.type === 'MultiPolygon' && jiangbeiFeature.geometry.type === 'MultiPolygon') {
|
||
liangjiangFeature.geometry.coordinates = [...yubeiFeature.geometry.coordinates, ...jiangbeiFeature.geometry.coordinates]
|
||
} else if (yubeiFeature.geometry.type === 'Polygon' && jiangbeiFeature.geometry.type === 'Polygon') {
|
||
liangjiangFeature.geometry.type = 'MultiPolygon'
|
||
liangjiangFeature.geometry.coordinates = [[yubeiFeature.geometry.coordinates], [jiangbeiFeature.geometry.coordinates]]
|
||
}
|
||
|
||
// 移除原来的两个区
|
||
geoJsonData.features.splice(Math.max(yubeiIndex, jiangbeiIndex), 1)
|
||
geoJsonData.features.splice(Math.min(yubeiIndex, jiangbeiIndex), 1)
|
||
|
||
// 添加合并后的两江新区
|
||
geoJsonData.features.push(liangjiangFeature)
|
||
}
|
||
}
|
||
|
||
// 计算区县中心点
|
||
const getCentroid = (coordinates) => {
|
||
let sumLat = 0
|
||
let sumLng = 0
|
||
let count = 0
|
||
|
||
const processCoordinates = (coords) => {
|
||
if (typeof coords[0] === 'number') {
|
||
// 单个坐标点 [lng, lat]
|
||
sumLng += coords[0]
|
||
sumLat += coords[1]
|
||
count++
|
||
} else if (Array.isArray(coords[0])) {
|
||
// 坐标数组
|
||
coords.forEach(processCoordinates)
|
||
}
|
||
}
|
||
|
||
processCoordinates(coordinates)
|
||
|
||
return count > 0 ? [sumLat / count, sumLng / count] : null
|
||
}
|
||
|
||
// 简化区县名称
|
||
const simplifyDistrictName = (name) => {
|
||
const nameMap = {
|
||
酉阳土家族苗族自治县: '酉阳县',
|
||
秀山土家族苗族自治县: '秀山县',
|
||
彭水苗族土家族自治县: '彭水县',
|
||
石柱土家族自治县: '石柱县',
|
||
}
|
||
return nameMap[name] || name
|
||
}
|
||
|
||
// 根据区县名称匹配预警数据
|
||
const getCountyWarningData = (countyName) => {
|
||
if (!affectedCountyData.value || !affectedCountyData.value.byName) {
|
||
return null
|
||
}
|
||
|
||
// 简化区县名称进行匹配
|
||
const simplifiedName = simplifyDistrictName(countyName)
|
||
const shortName = simplifiedName.replace(/区$/, '').replace(/县$/, '')
|
||
|
||
// 尝试直接匹配
|
||
if (affectedCountyData.value.byName[simplifiedName]) {
|
||
return affectedCountyData.value.byName[simplifiedName]
|
||
}
|
||
|
||
// 尝试匹配短名称(去掉"区"、"县"后缀)
|
||
for (const [name, data] of Object.entries(affectedCountyData.value.byName)) {
|
||
const dataShortName = name.replace(/区$/, '').replace(/县$/, '')
|
||
if (dataShortName === shortName) {
|
||
return data
|
||
}
|
||
}
|
||
|
||
// 特殊处理:两江新区
|
||
if (shortName === '两江' || countyName === '两江新区') {
|
||
return affectedCountyData.value.byName['两江新区']
|
||
}
|
||
|
||
return null
|
||
}
|
||
|
||
// 根据预警数据获取区县填充颜色
|
||
const getCountyFillColor = (countyName) => {
|
||
const warningData = getCountyWarningData(countyName)
|
||
if (!warningData || !warningData.levels) {
|
||
return 'rgb(50, 108, 112, 0.6)' // 默认绿色,90%透明度
|
||
}
|
||
const color = getMainWarningColor(warningData.levels)
|
||
// 将颜色转换为半透明填充色,使用E6(约90%透明度)
|
||
return color
|
||
}
|
||
|
||
// 根据预警数据获取区县边框颜色
|
||
const getCountyStrokeColor = (countyName) => {
|
||
return '#ffffff' // 默认白色
|
||
const warningData = getCountyWarningData(countyName)
|
||
if (!warningData || !warningData.levels) {
|
||
return '' // 默认白色
|
||
}
|
||
return getMainWarningColor(warningData.levels)
|
||
}
|
||
|
||
// 加载重庆区县 GeoJSON 数据并着色
|
||
const loadDistrictGeoJson = async () => {
|
||
try {
|
||
// 清除已有的 GeoJSON 图层
|
||
if (geoJsonLayer) {
|
||
mapInstance.removeLayer(geoJsonLayer)
|
||
geoJsonLayer = null
|
||
}
|
||
|
||
// 清除已有的区县标签
|
||
if (window.districtLabelMarkers) {
|
||
window.districtLabelMarkers.forEach((marker) => {
|
||
if (mapInstance) {
|
||
mapInstance.removeLayer(marker)
|
||
}
|
||
})
|
||
}
|
||
window.districtLabelMarkers = []
|
||
|
||
// 获取 GeoJSON 数据
|
||
const response = await fetch(GEOJSON_URL)
|
||
if (!response.ok) {
|
||
throw new Error(`获取 GeoJSON 数据失败: ${response.status}`)
|
||
}
|
||
|
||
const geoJsonData = await response.json()
|
||
console.log('重庆 GeoJSON 数据:', geoJsonData)
|
||
|
||
// 处理行政区划合并(渝北+江北=两江新区)
|
||
processDistrictMerge(geoJsonData)
|
||
|
||
// 创建 GeoJSON 图层
|
||
geoJsonLayer = window.L.geoJson(geoJsonData, {
|
||
style: (feature) => {
|
||
const countyName = feature.properties?.name || ''
|
||
const fillColor = getCountyFillColor(countyName)
|
||
const strokeColor = getCountyStrokeColor(countyName)
|
||
|
||
return {
|
||
fillColor: fillColor,
|
||
weight: 1,
|
||
opacity: 1,
|
||
color: strokeColor, // 边框颜色
|
||
dashArray: '',
|
||
fillOpacity: 0.75, // 增加填充透明度,使颜色更明显
|
||
}
|
||
},
|
||
onEachFeature: (feature, layer) => {
|
||
const countyName = feature.properties?.name || ''
|
||
const warningData = getCountyWarningData(countyName)
|
||
|
||
// 获取区县中心点并添加标签
|
||
const bounds = layer.getBounds()
|
||
const center = bounds.getCenter()
|
||
|
||
// 简化区县名称显示,添加尾缀
|
||
const simplifyDisplayName = (name) => {
|
||
const nameMap = {
|
||
彭水苗族土家族自治县: '彭水县',
|
||
酉阳土家族苗族自治县: '酉阳县',
|
||
秀山土家族苗族自治县: '秀山县',
|
||
石柱土家族自治县: '石柱县',
|
||
}
|
||
// 先检查是否需要特殊处理
|
||
if (nameMap[name]) {
|
||
return nameMap[name]
|
||
}
|
||
// 如果已经有"区"或"县"后缀,直接返回
|
||
if (name.endsWith('区') || name.endsWith('县')) {
|
||
return name
|
||
}
|
||
// 根据名称判断添加"区"还是"县"
|
||
// 重庆主城9区:渝中、大渡口、江北、沙坪坝、九龙坡、南岸、北碚、渝北、巴南
|
||
const districtList = ['渝中', '大渡口', '江北', '沙坪坝', '九龙坡', '南岸', '北碚', '渝北', '巴南', '两江新区']
|
||
if (districtList.includes(name)) {
|
||
return name + '区'
|
||
}
|
||
// 其他默认添加"县"
|
||
return name + '县'
|
||
}
|
||
const displayName = simplifyDisplayName(countyName)
|
||
|
||
// 创建区县名称标签
|
||
const labelIcon = window.L.divIcon({
|
||
className: 'district-name-label',
|
||
html: `<div class="district-name-text">${displayName}</div>`,
|
||
iconSize: [80, 20],
|
||
iconAnchor: [40, 10],
|
||
})
|
||
|
||
const labelMarker = window.L.marker(center, {
|
||
icon: labelIcon,
|
||
interactive: false,
|
||
zIndexOffset: 300,
|
||
})
|
||
|
||
labelMarker.addTo(mapInstance)
|
||
window.districtLabelMarkers.push(labelMarker)
|
||
|
||
// 获取当前区县的边框颜色
|
||
const strokeColor = getCountyStrokeColor(countyName)
|
||
|
||
// 添加交互事件
|
||
layer.on({
|
||
mouseover: (e) => {
|
||
const targetLayer = e.target
|
||
// 悬浮时高亮边框,使用当前区县的边框颜色,加粗边框
|
||
targetLayer.setStyle({
|
||
weight: 4,
|
||
color: strokeColor,
|
||
fillOpacity: 0.85, // 悬浮时填充更明显的颜色
|
||
})
|
||
// 将当前图层置于最上层,避免被相邻区县遮挡
|
||
targetLayer.bringToFront()
|
||
},
|
||
mouseout: (e) => {
|
||
const targetLayer = e.target
|
||
// 恢复原始样式
|
||
geoJsonLayer.resetStyle(targetLayer)
|
||
},
|
||
click: (e) => {
|
||
const targetLayer = e.target
|
||
const bounds = targetLayer.getBounds()
|
||
mapInstance.fitBounds(bounds, { padding: [50, 50] })
|
||
|
||
// 触发区县点击事件
|
||
emit('districtClick', {
|
||
name: countyName,
|
||
data: warningData || { total: 0, levels: {} },
|
||
})
|
||
},
|
||
})
|
||
},
|
||
})
|
||
|
||
// 添加图层到地图
|
||
geoJsonLayer.addTo(mapInstance)
|
||
console.log('已添加区县 GeoJSON 图层')
|
||
|
||
// 将 GeoJSON 图层置于底图之上,其他标记之下
|
||
geoJsonLayer.setZIndex(100)
|
||
} catch (err) {
|
||
console.error('加载区县 GeoJSON 数据失败:', err)
|
||
}
|
||
}
|
||
|
||
// 根据预警等级获取颜色
|
||
const getColorByLevel = (level) => {
|
||
const colorMap = {
|
||
红色预警: '#912210',
|
||
橙色预警: '#BA6527',
|
||
黄色预警: '#A47109',
|
||
蓝色预警: '#185A91',
|
||
未知: '#1890ff',
|
||
}
|
||
return colorMap[level] || '#1890ff'
|
||
}
|
||
// 初始化地图
|
||
const initMap = async () => {
|
||
if (!mapContainer.value) return
|
||
|
||
try {
|
||
// 清除旧地图实例
|
||
if (mapInstance) {
|
||
mapInstance.remove()
|
||
}
|
||
|
||
// 创建地图实例
|
||
mapInstance = new window.L.Map(mapContainer.value, {
|
||
center: [29.563, 106.551], // 重庆中心坐标
|
||
zoom: 8,
|
||
minZoom: 6,
|
||
maxZoom: 18,
|
||
zoomControl: false,
|
||
attributionControl: false,
|
||
})
|
||
|
||
// 获取天地图配置
|
||
const tdtConfig = await getTiandituConfig()
|
||
|
||
// 添加天地图影像底图
|
||
if (tdtConfig && tdtConfig.imgLayer && tdtConfig.imgLayer.servicePath) {
|
||
let servicePath = tdtConfig.imgLayer.servicePath
|
||
// 确保包含 tk 参数(天地图密钥)
|
||
if (!servicePath.includes('tk=')) {
|
||
// 使用默认的测试密钥,生产环境应该替换为正式密钥
|
||
servicePath += '&tk=b78ed71126d03ee82ce658731344a897'
|
||
}
|
||
// 天地图 URL 格式为 https://t{s}.tianditu.gov.cn/...
|
||
// subdomains 应该配置为 ['0', '1', ...] 以生成 t0, t1 等子域名
|
||
const tileLayer = new window.L.TileLayer(servicePath, {
|
||
subdomains: ['0', '1', '2', '3', '4', '5', '6', '7'],
|
||
maxZoom: 18,
|
||
attribution: '天地图',
|
||
})
|
||
mapInstance.addLayer(tileLayer)
|
||
console.log('已添加天地图影像底图:', tdtConfig.imgLayer.name)
|
||
} else {
|
||
console.warn('未获取到天地图影像底图配置,使用备用底图')
|
||
// 使用备用底图
|
||
const backupLayer = new window.L.TileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
|
||
subdomains: ['a', 'b', 'c'],
|
||
maxZoom: 19,
|
||
attribution: 'OpenStreetMap',
|
||
})
|
||
mapInstance.addLayer(backupLayer)
|
||
}
|
||
|
||
// 添加天地图行政区划图层(边界线)
|
||
if (tdtConfig && tdtConfig.boundaryLayer && tdtConfig.boundaryLayer.servicePath) {
|
||
let boundaryPath = tdtConfig.boundaryLayer.servicePath
|
||
// 确保包含 tk 参数
|
||
if (!boundaryPath.includes('tk=')) {
|
||
boundaryPath += '&tk=b78ed71126d03ee82ce658731344a897'
|
||
}
|
||
const boundaryLayer = new window.L.TileLayer(boundaryPath, {
|
||
subdomains: ['0', '1', '2', '3', '4', '5', '6', '7'],
|
||
maxZoom: 18,
|
||
attribution: '天地图行政区划',
|
||
})
|
||
mapInstance.addLayer(boundaryLayer)
|
||
console.log('已添加天地图行政区划图层:', tdtConfig.boundaryLayer.name)
|
||
} else {
|
||
console.warn('未获取到天地图行政区划图层配置')
|
||
}
|
||
|
||
// 加载重庆区县 GeoJSON 并着色
|
||
await loadDistrictGeoJson()
|
||
|
||
// 根据区县统计数据添加区县标记(使用固定坐标)
|
||
const districtCenters = {
|
||
渝中: [29.563, 106.571],
|
||
江北: [29.575, 106.573],
|
||
南岸: [29.523, 106.613],
|
||
九龙坡: [29.523, 106.513],
|
||
沙坪坝: [29.543, 106.453],
|
||
大渡口: [29.488, 106.483],
|
||
北碚: [29.803, 106.403],
|
||
渝北: [29.713, 106.623],
|
||
巴南: [29.383, 106.523],
|
||
万州: [30.803, 108.403],
|
||
涪陵: [29.703, 107.393],
|
||
永川: [29.353, 105.893],
|
||
合川: [29.973, 106.273],
|
||
江津: [29.283, 106.253],
|
||
南川: [29.153, 107.093],
|
||
綦江: [29.023, 106.653],
|
||
大足: [29.703, 105.713],
|
||
璧山: [29.593, 106.223],
|
||
铜梁: [29.843, 106.053],
|
||
潼南: [30.183, 105.833],
|
||
荣昌: [29.403, 105.593],
|
||
开州: [31.163, 108.393],
|
||
梁平: [30.673, 107.803],
|
||
武隆: [29.323, 107.753],
|
||
城口: [31.943, 108.663],
|
||
丰都: [29.863, 107.723],
|
||
垫江: [30.323, 107.333],
|
||
忠县: [30.303, 108.033],
|
||
云阳: [30.933, 108.693],
|
||
奉节: [31.023, 109.463],
|
||
巫山: [31.073, 109.873],
|
||
巫溪: [31.403, 109.633],
|
||
石柱: [30.003, 108.113],
|
||
秀山: [28.453, 108.993],
|
||
酉阳: [28.843, 108.773],
|
||
彭水: [29.293, 108.173],
|
||
两江: [29.723, 106.583],
|
||
}
|
||
|
||
// 根据统计数据添加区县标签(已在loadDistrictGeoJson中添加,此处注释避免重复)
|
||
// if (affectedCountyData.value && affectedCountyData.value.byName) {
|
||
// Object.entries(affectedCountyData.value.byName).forEach(([districtName, data]) => {
|
||
// const center = districtCenters[districtName]
|
||
// if (center) {
|
||
// const displayName = districtName
|
||
// const label = window.L.divIcon({
|
||
// className: 'district-label',
|
||
// html: `<div class="label-content">${displayName}</div>`,
|
||
// iconSize: [80, 30],
|
||
// iconAnchor: [40, 15],
|
||
// })
|
||
|
||
// const marker = window.L.marker(center, {
|
||
// icon: label,
|
||
// interactive: false,
|
||
// zIndexOffset: 500,
|
||
// })
|
||
// marker.addTo(mapInstance)
|
||
// }
|
||
// })
|
||
// }
|
||
|
||
// 调整视图以适应重庆边界
|
||
const chongqingBounds = [
|
||
[28.0, 105.0], // 西南角
|
||
[32.0, 110.0], // 东北角
|
||
]
|
||
mapInstance.fitBounds(chongqingBounds)
|
||
} catch (err) {
|
||
console.error('初始化地图失败:', err)
|
||
error.value = '地图初始化失败'
|
||
}
|
||
}
|
||
|
||
// 监听 activeitem 变化
|
||
watch(
|
||
() => props.activeitem,
|
||
async (newVal) => {
|
||
console.log('activeitem 变化:', newVal)
|
||
switch (newVal.label) {
|
||
case '涉灾隐患点':
|
||
// 点击涉灾隐患点时,获取所有风险点数据
|
||
await getRiskPointData()
|
||
break
|
||
case '驻地':
|
||
await getAffectedProjectData(false)
|
||
break
|
||
case '隧道':
|
||
await getAffectedTunnelData(false)
|
||
break
|
||
case '边坡':
|
||
break
|
||
case '桥梁':
|
||
await getAffectedBridgeData(false)
|
||
break
|
||
case '路段':
|
||
await getAffectedRoadSectionData(false)
|
||
break
|
||
case '队伍':
|
||
await getEmergencyForceData()
|
||
break
|
||
case '危大工程':
|
||
await getDangerProjectData(false)
|
||
break
|
||
default:
|
||
break
|
||
}
|
||
},
|
||
{ immediate: false },
|
||
)
|
||
|
||
// 处理隐患点点击事件
|
||
const handleHazardItemClick = async (item, flag) => {
|
||
console.log('处理隐患点点击:', item)
|
||
// 根据隐患点类型映射风险等级
|
||
const riskLevelMap = {
|
||
重大路内隐患点: '重大隐患',
|
||
重大路外隐患点: '重大隐患',
|
||
较大路内隐患点: '较大隐患',
|
||
较大路外隐患点: '较大隐患',
|
||
一般路内隐患点: '一般隐患',
|
||
一般路外隐患点: '一般隐患',
|
||
}
|
||
const riskLevel = riskLevelMap[item.label] || ''
|
||
await getRiskPointData(riskLevel, item, flag)
|
||
}
|
||
const flag = ref(true)
|
||
// 监听 dateRange 变化,重新加载数据
|
||
watch(
|
||
() => props.dateRange,
|
||
async (newVal, oldVal) => {
|
||
console.log('dateRange 变化:', newVal, oldVal)
|
||
// 先重新加载受影响区县数据
|
||
await clearProjectMarkers() // 清除项目标记
|
||
await getAffectedCountyData()
|
||
},
|
||
{ deep: true },
|
||
)
|
||
|
||
// 监听 affectedCountyData 变化,重新加载区县 GeoJSON 着色
|
||
watch(
|
||
() => affectedCountyData.value,
|
||
async (newVal) => {
|
||
console.log('affectedCountyData 变化,重新加载区县着色:', newVal)
|
||
if (mapInstance && newVal && newVal.byName) {
|
||
await loadDistrictGeoJson()
|
||
}
|
||
},
|
||
{ deep: true },
|
||
)
|
||
|
||
// 组件挂载时加载地图
|
||
onMounted(() => {
|
||
// 获取受影响对象数据
|
||
// getAffectedCountyData()
|
||
|
||
// 检查 Leaflet 是否已加载
|
||
if (typeof window.L === 'undefined') {
|
||
// 动态加载 Leaflet CSS 和 JS
|
||
const link = document.createElement('link')
|
||
link.rel = 'stylesheet'
|
||
link.href = 'https://unpkg.com/leaflet@1.9.4/dist/leaflet.css'
|
||
link.integrity = 'sha256-p4NxAoJBhIIN+hmNHrzRCf9tD/miZyoHS5obTRR9BMY='
|
||
link.crossOrigin = ''
|
||
document.head.appendChild(link)
|
||
|
||
const script = document.createElement('script')
|
||
script.src = 'https://unpkg.com/leaflet@1.9.4/dist/leaflet.js'
|
||
script.integrity = 'sha256-20nQCchB9co0qIjJZRGuk2/Z9VM+kNiyxNV1lvTlZBo='
|
||
script.crossOrigin = ''
|
||
script.onload = loadMapData
|
||
document.head.appendChild(script)
|
||
} else {
|
||
loadMapData()
|
||
}
|
||
})
|
||
|
||
// 组件卸载时清理资源
|
||
onUnmounted(() => {
|
||
if (mapInstance) {
|
||
mapInstance.remove()
|
||
mapInstance = null
|
||
}
|
||
})
|
||
|
||
// 根据区县名称定位地图
|
||
const locateToDistrict = (countyName) => {
|
||
if (!mapInstance || !geoJsonLayer) {
|
||
console.warn('地图未初始化,无法定位')
|
||
return
|
||
}
|
||
|
||
// 简化区县名称
|
||
const simplifyName = (name) => {
|
||
return name.replace('土家族苗族自治县', '').replace('苗族土家族自治县', '').replace('自治县', '').replace('区', '').replace('县', '')
|
||
}
|
||
|
||
const targetName = simplifyName(countyName)
|
||
|
||
// 查找对应的区县图层
|
||
let targetLayer = null
|
||
geoJsonLayer.eachLayer((layer) => {
|
||
const layerName = layer.feature?.properties?.name || ''
|
||
if (simplifyName(layerName) === targetName) {
|
||
targetLayer = layer
|
||
}
|
||
})
|
||
|
||
if (targetLayer) {
|
||
// 获取区县的中心点
|
||
const bounds = targetLayer.getBounds()
|
||
const center = bounds.getCenter()
|
||
|
||
// 移动地图到该中心点并放大
|
||
// mapInstance.setView(center, 10);
|
||
|
||
// 高亮显示该区县
|
||
if (selectedLayer) {
|
||
geoJsonLayer.resetStyle(selectedLayer)
|
||
}
|
||
// targetLayer.setStyle({
|
||
// fillColor: "#ff7a00",
|
||
// fillOpacity: 0.6,
|
||
// weight: 3,
|
||
// color: "#ff4d4f",
|
||
// });
|
||
selectedLayer = targetLayer
|
||
} else {
|
||
console.warn('未找到区县:', countyName)
|
||
}
|
||
}
|
||
|
||
// 暴露方法给父组件调用
|
||
defineExpose({
|
||
getEmergencyForceData,
|
||
clearProjectMarkers,
|
||
openCenterCard,
|
||
locateToDistrict,
|
||
handleHazardItemClick,
|
||
getRiskPointData,
|
||
})
|
||
</script>
|
||
|
||
<style lang="scss">
|
||
// 区县标签样式(需要全局样式,因为 Leaflet 动态添加元素)
|
||
.district-label {
|
||
background: transparent !important;
|
||
border: none !important;
|
||
|
||
.label-content {
|
||
background: transparent;
|
||
color: #fff;
|
||
padding: vw(4) vw(8);
|
||
border-radius: vw(2);
|
||
font-weight: 500;
|
||
text-align: center;
|
||
white-space: nowrap;
|
||
text-shadow:
|
||
0 1px 3px rgba(0, 0, 0, 0.8),
|
||
0 0 2px rgba(0, 0, 0, 0.5);
|
||
letter-spacing: 0.5px;
|
||
cursor: pointer;
|
||
width: fit-content;
|
||
transition: all 0.3s;
|
||
|
||
&:hover {
|
||
color: #40a9ff;
|
||
text-shadow: 0 0 10px rgba(64, 169, 255, 0.8);
|
||
transform: scale(1.05);
|
||
}
|
||
}
|
||
}
|
||
|
||
// 区县名称标签样式
|
||
.district-name-label {
|
||
background: transparent !important;
|
||
border: none !important;
|
||
|
||
.district-name-text {
|
||
color: #fff;
|
||
font-size: 12px;
|
||
font-weight: 600;
|
||
text-align: center;
|
||
white-space: nowrap;
|
||
text-shadow:
|
||
0 1px 3px rgba(0, 0, 0, 0.9),
|
||
0 0 4px rgba(0, 0, 0, 0.7);
|
||
letter-spacing: 1px;
|
||
pointer-events: none;
|
||
}
|
||
}
|
||
|
||
// 区县弹窗样式
|
||
:deep(.county-popup) {
|
||
.leaflet-popup-content-wrapper {
|
||
background: rgba(0, 20, 40, 0.95);
|
||
border: 1px solid rgba(64, 169, 255, 0.5);
|
||
border-radius: 8px;
|
||
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.5);
|
||
}
|
||
|
||
.leaflet-popup-content {
|
||
margin: 0;
|
||
color: #fff;
|
||
font-size: 13px;
|
||
line-height: 1.5;
|
||
}
|
||
|
||
.leaflet-popup-tip {
|
||
background: rgba(0, 20, 40, 0.95);
|
||
border: 1px solid rgba(64, 169, 255, 0.5);
|
||
}
|
||
|
||
.leaflet-popup-close-button {
|
||
color: #fff;
|
||
font-size: 18px;
|
||
padding: 4px;
|
||
transition: color 0.3s;
|
||
|
||
&:hover {
|
||
color: #40a9ff;
|
||
}
|
||
}
|
||
}
|
||
</style>
|
||
|
||
<style lang="scss" scoped>
|
||
// 视频屏幕自适应 - 基于视口宽度动态调整
|
||
@function vw($px) {
|
||
@return calc($px / 1920 * 100vw);
|
||
}
|
||
|
||
.chongqing-map-container {
|
||
width: 100%;
|
||
height: 100%;
|
||
position: relative;
|
||
}
|
||
|
||
.map-container {
|
||
width: 100%;
|
||
height: 100%;
|
||
}
|
||
|
||
.loading-overlay,
|
||
.error-overlay {
|
||
position: absolute;
|
||
top: 0;
|
||
left: 0;
|
||
width: 100%;
|
||
height: 100%;
|
||
display: flex;
|
||
flex-direction: column;
|
||
align-items: center;
|
||
justify-content: center;
|
||
background: rgba(0, 0, 0, 0.7);
|
||
z-index: 1000;
|
||
}
|
||
|
||
.loading-spinner {
|
||
width: vw(40);
|
||
height: vw(40);
|
||
border: vw(4) solid #3b82f6;
|
||
border-top: vw(4) solid transparent;
|
||
border-radius: 50%;
|
||
animation: spin 1s linear infinite;
|
||
margin-bottom: vw(10);
|
||
}
|
||
|
||
.loading-text {
|
||
color: #fff;
|
||
font-size: vw(14);
|
||
}
|
||
|
||
.error-text {
|
||
color: #ff6b6b;
|
||
font-size: vw(14);
|
||
margin-bottom: vw(10);
|
||
}
|
||
|
||
.retry-btn {
|
||
background: #3b82f6;
|
||
color: white;
|
||
border: none;
|
||
padding: vw(8) vw(16);
|
||
border-radius: vw(4);
|
||
cursor: pointer;
|
||
font-size: vw(12);
|
||
|
||
&:hover {
|
||
background: #2563eb;
|
||
}
|
||
}
|
||
|
||
@keyframes spin {
|
||
0% {
|
||
transform: rotate(0deg);
|
||
}
|
||
100% {
|
||
transform: rotate(360deg);
|
||
}
|
||
}
|
||
|
||
// Leaflet 地图样式覆盖
|
||
:deep(.leaflet-container) {
|
||
background: #0f1c2e !important;
|
||
.leaflet-popup {
|
||
left: -20px !important;
|
||
}
|
||
|
||
.leaflet-popup-content-wrapper {
|
||
background: rgba(24, 144, 255, 0.95);
|
||
border-radius: vw(4);
|
||
padding: vw(6) vw(12);
|
||
min-width: vw(80);
|
||
box-shadow: 0 2px 12px rgba(0, 0, 0, 0.5);
|
||
|
||
.map-popup {
|
||
color: #fff;
|
||
font-size: vw(12);
|
||
text-align: center;
|
||
margin: 0;
|
||
padding: 0;
|
||
font-weight: 500;
|
||
}
|
||
}
|
||
|
||
.leaflet-popup-tip {
|
||
background: rgba(24, 144, 255, 0.95);
|
||
}
|
||
|
||
.leaflet-popup-content {
|
||
width: max-content !important;
|
||
margin: vw(10) 10px vw(10) 0;
|
||
line-height: 1.3;
|
||
}
|
||
}
|
||
|
||
// 区县高亮样式
|
||
:deep(.leaflet-interactive) {
|
||
transition: all 0.3s;
|
||
cursor: pointer;
|
||
}
|
||
|
||
.project-popup {
|
||
width: vw(120);
|
||
height: vw(20);
|
||
color: #fff;
|
||
font-size: vw(12);
|
||
text-align: center;
|
||
margin: 0;
|
||
padding: 15px;
|
||
font-weight: 500;
|
||
}
|
||
|
||
// 区县卡片样式
|
||
:deep(.county-card-icon) {
|
||
background: transparent !important;
|
||
border: none !important;
|
||
|
||
// .center-info-card-container {
|
||
// width: 100%;
|
||
// min-width: 120px;
|
||
// }
|
||
|
||
.center-info-card {
|
||
// box-shadow: 0 4px 16px rgba(0, 0, 0, 0.4);
|
||
cursor: pointer;
|
||
transition: all 0.3s;
|
||
|
||
&:hover {
|
||
transform: translateY(-2px);
|
||
box-shadow: 0 6px 20px rgba(24, 144, 255, 0.5);
|
||
filter: brightness(1.1);
|
||
}
|
||
}
|
||
}
|
||
</style>
|