dakeliu/Dkl-vue3-ui-DP/src/views/largeScreen/components/earlyWarning/index.vue

1082 lines
29 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<div class="card__content-box">
<!-- 滚动表格区域 -->
<div class="table">
<!-- 表头容器 - 固定显示 -->
<div class="table__header-container">
<div class="table__item table__header">
<div>序号</div>
<div>预警名称</div>
<div>预警点位</div>
<div>预警摄像</div>
<div>发布时间</div>
</div>
</div>
<!-- 内容滚动区域 -->
<div class="table__body-container">
<div class="scroll" v-if="tableData.length > 0">
<vue3-seamless-scroll :step="0.5" :hover="true" :list="tableData">
<div
class="table__item"
v-for="(item, index) in tableData"
:key="index"
@click="handleRowClick(item)"
>
<div class="scroll_table item_num">{{ index + 1 }}</div>
<!-- 1. 预警名称添加Tooltip -->
<el-tooltip
effect="dark"
placement="top"
:content="item.warningSigns"
teleported
popper-class="table-tooltip"
>
<div class="scroll_table item_courtName tooltip">
{{ item.warningSigns }}
</div>
</el-tooltip>
<!-- 2. 预警点位添加Tooltip -->
<el-tooltip
effect="dark"
placement="top"
:content="item.pointName"
teleported
popper-class="table-tooltip"
>
<div class="scroll_table item_punisherName tooltip">
{{ item.pointName }}
</div>
</el-tooltip>
<!-- 3. 预警摄像添加Tooltip -->
<el-tooltip
effect="dark"
placement="top"
:content="item.cameraName"
teleported
popper-class="table-tooltip"
>
<div class="scroll_table item_punisherName tooltip">
{{ item.cameraName }}
</div>
</el-tooltip>
<!-- 4. 发布时间添加Tooltip -->
<el-tooltip
effect="dark"
placement="top"
:content="item.releaseTime"
teleported
popper-class="table-tooltip"
>
<div class="scroll_table item_courtName tooltip">
{{ item.releaseTime }}
</div>
</el-tooltip>
</div>
</vue3-seamless-scroll>
</div>
<div v-else class="nocontent">
<el-empty :image-size="100" description="暂无数据" />
</div>
</div>
</div>
<!-- 处置状态弹窗 -->
<LargeScreenModal
v-model:open="chargeOpen"
:title="modalTitle"
>
<div class="tableWrap largeScreen">
<el-descriptions
class="largeScreen__descriptions"
:column="2"
size="large"
border
>
<template v-for="item in formColumns">
<el-descriptions-item :label="item.label">{{
formInfo[item.key] || "-"
}}</el-descriptions-item>
</template>
</el-descriptions>
</div>
<div class="custom-timeline">
<div
v-for="(item, index) in timelineData"
:key="index"
class="timeline-item"
>
<div class="timeline-dot"></div>
<div class="timeline-content">
<div class="timeline-time">处理时间: {{ item.createTime }}</div>
<div class="timeline-desc">
<p>状态:{{ getStatusLabel(item.disposalStatus) }}</p>
<p>措施:{{ item.disposalMeasures || "-" }}</p>
</div>
</div>
</div>
</div>
</LargeScreenModal>
<!-- 预警详情与热力图弹窗 -->
<LargeScreenModal
v-model:open="warningModalOpen"
title="预警详情与热力图"
:width="styleUtil.px2vw(1200)"
:height="styleUtil.px2vh(800)"
@close="handleClose"
>
<div class="titles">{{ titlesTitle }}</div>
<!-- 详情区域 -->
<div class="detail-section">
<el-descriptions
class="largeScreen__descriptions"
:column="2"
size="large"
border
>
<template #extra>
</template>
<template v-for="item in warningFormColumns">
<!-- <el-descriptions-item v-if="item.label == '监控/外呼'" :label="item.label"> -->
<el-descriptions-item v-if="item.label == '监控'" :label="item.label">
<el-button type="primary" size="default" @click="lookJK">
查看监控
</el-button>
<!-- <el-button type="primary" size="default" @click="CALL">
外呼
</el-button> -->
</el-descriptions-item>
<el-descriptions-item v-else :label="item.label">{{
warningDetail[item.key] || "-"
}}</el-descriptions-item>
</template>
</el-descriptions>
</div>
<!-- 地图区域 -->
<div class="map-section">
<div id="warningMap" ref="mapContainer"></div>
<!-- 加载状态 -->
<div v-if="isLoading" class="map-loading">
<el-icon class="is-loading"><Loading /></el-icon>加载中...
</div>
<!-- 热力图图例 -->
<div class="map-legend" v-if="showLegend && showHeatmap">
<h4>客流热力等级</h4>
<div class="legend-gradient"></div>
<div class="legend-labels">
<span>低</span><span>中</span><span>高</span>
</div>
</div>
</div>
</LargeScreenModal>
<!-- 视频弹窗 -->
<LargeScreenModal
v-model:open="chargeOpenJK"
title="监控视频"
:width="styleUtil.px2vw(1200)"
:height="styleUtil.px2vh(800)"
@close="handleVideoClose"
>
<div class="tableWrap largeScreen">
<VideoMonitor
:video-url="currentVideoUrl"
width="100%"
height="100%"
style="margin-left: 40px"
/>
</div>
</LargeScreenModal>
<!-- 外呼的弹窗 -->
<LargeScreenModal
v-model:open="callDialog"
title="外呼"
:width="styleUtil.px2vw(400)"
:height="styleUtil.px2vh(500)"
@close="handleVideoClose2"
>
<el-row>
<el-col :span="24">
<div class="callDemo">
<Call ref="CallRef" :listRows="callForm" width="100%" height="400px" />
</div>
</el-col>
</el-row>
</LargeScreenModal>
</div>
</template>
<script setup>
import {
ref,
reactive,
onMounted,
getCurrentInstance,
nextTick,
watch,
} from "vue";
import {
getPoints,
} from "@/api/points";
import Call from "@/components/call.vue";
import styleUtil from "@/utils/styleUtils";
import LargeScreenModal from "../largeScreenModal.vue";
import { Vue3SeamlessScroll } from "vue3-seamless-scroll";
import L from "leaflet";
import "leaflet/dist/leaflet.css";
import "leaflet.heat/dist/leaflet-heat.js"; // 与主地图一致的热力图库
import { ElIcon, ElEmpty, ElMessage, ElSlider } from "element-plus";
import { Loading } from "@element-plus/icons-vue";
import _ from "lodash";
import VideoMonitor from "../../../../components/VideoMonitor.vue";
import moment from "moment";
// 接口导入 - 与主地图使用相同的接口
import {
getchuzhiList,
getchuzhiListDetail,
getchuzhiListLiucheng,
getYJRLi,
getYJRLiDetalis,
getYJRLiJK,
} from "@/api/map.js";
// 图标 - 与主地图一致
import warningIcon from "@/assets/images/yujing2.png";
// 常量定义 - 与主地图完全一致
const CHINA_BOUNDS = {
minLat: 18.1,
maxLat: 53.5,
minLng: 73.6,
maxLng: 135.1,
};
const DEFAULT_COORDS = [34.22, 108.96]; // 西安
const DEFAULT_ZOOM = 11;
const PARAM_ZOOM = 16;
const { proxy } = getCurrentInstance();
const emit = defineEmits(["update-map-params"]);
const { disposal_status } = proxy.useDict("disposal_status");
// 表格数据
const tableData = ref([]);
// 处置弹窗相关
const chargeOpen = ref(false);
const modalTitle = ref("处置状态查看");
const formInfo = ref({});
const timelineData = ref([]);
const formColumns = ref([
{ label: "预警唯一标识符", key: "warningSigns" },
{ label: "发布单位名称", key: "unitName" },
{ label: "发布单位编码", key: "unitCode" },
{ label: "发布时间", key: "releaseTime" },
{ label: "经度", key: "lng" },
{ label: "纬度", key: "lat" },
{ label: "可能波及范围", key: "scopeImpact" },
{ label: "异常数据值", key: "abnormalData" },
{ label: "预计持续时间", key: "expectedDuration" },
//{ label: "监控/外呼", key: "expectedDuration" },
{ label: "监控", key: "expectedDuration" },
]);
// 预警详情弹窗相关
const warningModalOpen = ref(false);
const warningDetail = ref({});
const currentRow = ref(null);
const isLoading = ref(false);
const showLegend = ref(false);
const showNoData = ref(false);
const showHeatmap = ref(true);
const showMarker = ref(true);
// 热力图参数 - 与主地图完全一致
const heatRadius = ref(50); // 热力图半径
const heatBlur = ref(30); // 热力图模糊度
const heatMaxZoom = ref(10); // 最大缩放级别
const heatMaxOpacity = ref(0.8); // 最大透明度
// 地图相关 - 与主地图保持一致的图层结构
const mapContainer = ref(null);
const mapInstance = ref(null);
const markerObj = ref(null);
const heatmapLayer = ref(null); // 热力图专用图层
const markersLayer = ref(null); // 标记专用图层
let currentHeatmapLayer = null; // 当前热力图层实例
const filePathS = ref("");
const chargeOpenJK = ref(false);
const currentVideoUrl = ref("");
// 预警详情表格配置
const warningFormColumns = ref([
{ label: "预警名称", key: "warningSigns" },
{ label: "预警点位", key: "pointName" },
{ label: "预警摄像", key: "cameraName" },
{ label: "发布时间", key: "releaseTime" },
{ label: "经度", key: "lng" },
{ label: "纬度", key: "lat" },
{ label: "可能波及范围", key: "scopeImpact" },
{ label: "异常数据值", key: "abnormalData" },
{ label: "预计持续时间", key: "expectedDuration" },
// { label: "监控/外呼", key: "expectedDuration" }
{ label: "监控", key: "expectedDuration" }
]);
const titlesTitle = ref('')
const currentWarningId = ref('')
// 行点击事件 - 与主地图逻辑一致
const handleRowClick = async (row) => {
console.log(row);
currentWarningId.value = row.unitCode;
filePathS.value = row.filePath;
currentRow.value = row;
isLoading.value = true;
warningModalOpen.value = true;
showNoData.value = false;
showHeatmap.value = true;
showMarker.value = true;
try {
// 解析经纬度
const lat = Number(row.lat);
const lng = Number(row.lng);
const hasValidCoord = !isNaN(lat) && !isNaN(lng) && isInChina(lat, lng);
if (hasValidCoord) {
emit("update-map-params", {
latitude: lat.toFixed(6),
longitude: lng.toFixed(6),
monitoringTypeName: "warning",
});
} else {
ElMessage.warning("经纬度无效,使用默认位置");
}
await nextTick();
let startTime = moment(row.releaseTime).format("YYYY-MM-DD");
// 并行加载数据 - 与主地图使用相同的接口
const [detailRes, heatRes] = await Promise.all([
getYJRLiDetalis(row.id).catch((err) => ({ code: 500, data: null })),
getYJRLi({
srcIndex: row.filePath || row.id,
startTime: startTime ? `${startTime} 00:00:00` : "",
endTime: row.releaseTime ? `${row.releaseTime}` : "",
}).catch((err) => ({ code: 500, data: null })),
]);
console.log(heatRes, "热力");
// 处理详情数据
warningDetail.value = detailRes.code === 200 ? detailRes.data || {} : {};
console.log(warningDetail.value,'warningDetailwarningDetail');
const index = warningDetail.value.warningSigns.indexOf('秒');
titlesTitle.value = index !== -1 ? warningDetail.value.warningSigns.slice(index + 1) : '';
// 初始化地图(与主地图配置一致)
await initMap(row);
// 处理热力图数据 - 与主地图逻辑一致
if (heatRes.code === 200 && heatRes.data && heatRes.data.length) {
renderHeatmap(heatRes.data); // 使用与主地图相同的热力图渲染
showLegend.value = true;
showNoData.value = false;
// 调整地图视图到数据区域 - 与主地图逻辑一致
const bounds = L.latLngBounds(
heatRes.data.map((item) => [
Number(item.latitude || item.lat),
Number(item.longitude || item.lng),
])
);
if (bounds.isValid()) {
mapInstance.value.fitBounds(bounds, {
padding: [80, 80], // 可选:增加边距,让视图更宽松
maxZoom: 14, // 核心属性:限制最大缩放级别
});
}
} else {
showNoData.value = true;
showLegend.value = false;
ElMessage.info("未获取到热力图数据");
}
} catch (error) {
console.error("加载失败:", error);
ElMessage.error("加载数据失败,请重试");
await initMap(currentRow.value);
} finally {
isLoading.value = false;
}
};
// 初始化地图 - 与主地图配置完全一致
const initMap = async (row) => {
const lat = Number(row.lat) || DEFAULT_COORDS[0];
const lng = Number(row.lng) || DEFAULT_COORDS[1];
await nextTick();
if (!mapContainer.value) {
console.error("地图容器不存在");
return;
}
// 设置容器尺寸
mapContainer.value.style.width = "100%";
mapContainer.value.style.height = "100%";
// 创建瓦片地图
L.CRS.EPSG4490 = L.extend({},
L.CRS.EPSG4326,
{
code: 'EPSG:4490',
scale: function (zoom) { return 256 * Math.pow(2, zoom - 1); }
}
);
// 创建地图实例(与主地图参数一致)
if (!mapInstance.value) {
mapInstance.value = L.map(mapContainer.value, {
center: [lat, lng],
zoom: 12,
maxZoom: 16, // 可选:限制手动缩放的最大级别
minZoom: 8,
zoomControl: false,
attributionControl: false,
dragging: true,
touchZoom: true,
scrollWheelZoom: true,
doubleClickZoom: true,
// 配置瓦片地图
crs: L.CRS.EPSG4490,
});
// 添加与主地图相同的底图
L.tileLayer(
// "https://webst01.is.autonavi.com/appmaptile?style=6&x={x}&y={y}&z={z}",
"https://10.22.245.209:18888/kgis/rest/services/GETileWGS20240425/MapServer/tile/{z}/{y}/{x}",
{ subdomains: ["1", "2", "3", "4"], maxZoom: 20 }
).addTo(mapInstance.value);
// // 添加与主地图相同的底图
// const tileLayer1 = L.tileLayer(
// "https://webst01.is.autonavi.com/appmaptile?style=6&x={x}&y={y}&z={z}",
// { subdomains: ["1", "2", "3", "4"] }
// ).addTo(mapInstance.value);
// const tileLayer2 = L.tileLayer(
// "https://webst0{s}.is.autonavi.com/appmaptile?style=8&x={x}&y={y}&z={z}&lang=zh_cn&size=1&scale=1",
// { subdomains: ["1", "2", "3", "4"], pane: "markerPane" }
// ).addTo(mapInstance.value);
// 添加相同的缩放控件
L.control.zoom({ position: "topright" }).addTo(mapInstance.value);
// 创建图层(与主地图图层结构一致)
heatmapLayer.value = L.layerGroup().addTo(mapInstance.value);
markersLayer.value = L.layerGroup().addTo(mapInstance.value);
// 保持与主地图相同的图层顺序
heatmapLayer.value.setZIndex(300);
markersLayer.value.setZIndex(400);
// 监听地图事件 - 与主地图一致
mapInstance.value.on(
"zoomend moveend",
_.debounce(() => {
if (mapInstance.value) {
mapInstance.value.invalidateSize();
}
}, 100)
);
} else {
mapInstance.value.setView([lat, lng], PARAM_ZOOM);
clearHeatmap();
}
// 添加预警标记 - 与主地图标记样式一致
if (row) {
addWarningMarker(lat, lng, row);
}
};
// 添加预警标记 - 与主地图标记逻辑一致
const addWarningMarker = (lat, lng, row) => {
if (markerObj.value) {
markersLayer.value.removeLayer(markerObj.value);
}
// 创建与主地图一致的图标
const icon = L.icon({
iconUrl: warningIcon,
iconSize: [32, 32],
iconAnchor: [16, 32],
popupAnchor: [0, -32],
});
markerObj.value = L.marker([lat, lng], {
icon: icon,
zIndexOffset: 1000, // 确保标记在热力图上方
});
// 绑定与主地图风格一致的弹窗内容
markerObj.value.bindPopup(`
<b>${row.warningSigns || "预警信息"}</b><br>
点位: ${row.pointName || "未知"}<br>
时间: ${row.releaseTime || "未知"}
`);
markersLayer.value.addLayer(markerObj.value);
markerObj.value.openPopup();
if (!showMarker.value) {
markersLayer.value.removeLayer(markerObj.value);
}
};
// 修改后的renderHeatmap函数只显示一个圆圈
const renderHeatmap = (heatData) => {
// 清除现有热力图
clearHeatmap();
if (!mapInstance.value || !heatData || !heatData.length) {
return;
}
try {
// 获取第一条数据
const firstItem = heatData[0];
if (!firstItem) {
showNoData.value = true;
ElMessage.warning("没有有效的热力图数据");
return;
}
// 提取并验证经纬度和强度值
const lat = Number(
firstItem.latitude || firstItem.lat || firstItem.LAT || firstItem.Latitude
);
const lng = Number(
firstItem.longitude ||
firstItem.lng ||
firstItem.LNG ||
firstItem.Longitude
);
// 计算强度值
const enterValue = Number(
firstItem.enter || firstItem.count || firstItem.value || 50
);
let intensity;
if (enterValue <= 0) {
intensity = 30;
} else {
if (enterValue < 10000) {
intensity = 50;
} else if (enterValue < 100000) {
intensity = 70;
} else {
intensity = 100;
}
}
// 有效性检查
if (isNaN(lat) || isNaN(lng)) {
showNoData.value = true;
ElMessage.warning(`经纬度无效: lat=${lat}, lng=${lng}`);
console.error("无效的经纬度数据:", firstItem);
return;
}
if (!isInChina(lat, lng)) {
showNoData.value = true;
ElMessage.warning(`经纬度不在有效范围内: lat=${lat}, lng=${lng}`);
console.error("经纬度超出范围:", firstItem);
return;
}
// 只使用单个点
const points = [
[lat, lng, intensity], // 仅保留中心点位
];
// 确保热力图容器图层已添加到地图
if (!mapInstance.value.hasLayer(heatmapLayer.value)) {
mapInstance.value.addLayer(heatmapLayer.value);
}
// 创建热力图图层
currentHeatmapLayer = L.heatLayer(points, {
radius: 60, // 可以根据需要调整半径大小
blur: 40, // 可以根据需要调整模糊度
maxZoom: heatMaxZoom.value,
maxOpacity: 0.9,
gradient: {
0.1: "#FFEDA0",
0.3: "#FED976",
0.5: "#FEB24C",
0.7: "#FD8D3C",
0.9: "#FC4E2A",
1: "#E31A1C",
},
});
// 添加热力图到容器图层
heatmapLayer.value.addLayer(currentHeatmapLayer);
// 确保标记层在热力图上方
if (markersLayer.value) {
mapInstance.value.removeLayer(markersLayer.value);
mapInstance.value.addLayer(markersLayer.value);
}
// 显示图例
showLegend.value = true;
showNoData.value = false;
console.log(`成功渲染热力图,经纬度: (${lat}, ${lng}),强度: ${intensity}`);
} catch (error) {
console.error("热力图渲染失败:", error);
ElMessage.error(`热力图渲染失败: ${error.message}`);
}
};
// 更新热力图设置
const updateHeatmapSettings = () => {
if (currentRow.value && currentHeatmapLayer) {
clearHeatmap();
// 重新获取数据并渲染
getYJRLi({
srcIndex: currentRow.value.filePath || currentRow.value.id,
startTime: currentRow.value.releaseTime
? `${currentRow.value.releaseTime} 00:00:00`
: "",
endTime: currentRow.value.releaseTime
? `${currentRow.value.releaseTime} 23:59:59`
: "",
}).then((res) => {
if (res.code === 200 && res.data && res.data.length) {
renderHeatmap(res.data);
}
});
}
};
// 切换热力图显示 - 与主地图逻辑一致
const toggleHeatmap = () => {
showHeatmap.value = !showHeatmap.value;
if (showHeatmap.value) {
mapInstance.value.addLayer(heatmapLayer.value);
showLegend.value = true;
} else {
mapInstance.value.removeLayer(heatmapLayer.value);
showLegend.value = false;
}
};
// 切换标记显示
const toggleMarker = () => {
showMarker.value = !showMarker.value;
if (showMarker.value && markerObj.value) {
markersLayer.value.addLayer(markerObj.value);
} else if (markerObj.value) {
markersLayer.value.removeLayer(markerObj.value);
}
};
// 清除热力图 - 与主地图逻辑一致
const clearHeatmap = () => {
if (
currentHeatmapLayer &&
heatmapLayer.value &&
heatmapLayer.value.hasLayer(currentHeatmapLayer)
) {
heatmapLayer.value.removeLayer(currentHeatmapLayer);
}
currentHeatmapLayer = null;
};
// 查看监控
const lookJK = () => {
let params = {
typeinfo: filePathS.value,
};
getYJRLiJK(params).then((res) => {
console.log(res, "监控返回");
if (res.code === 200 && res.msg) {
currentVideoUrl.value = res.msg;
chargeOpenJK.value = true;
} else {
proxy.$modal.msgWarning("无监控数据");
}
});
};
const callDialog = ref(false);
const callForm = ref({});
// 外呼
const CALL = () => {
resetCall();
getPoints(currentWarningId.value).then((response) => {
console.log(response,'rq');
callForm.value = {
phone: response.data.dutyPhone
}
});
callDialog.value = true;
}
// 重置外呼内容
function resetCall() {
callDialog.value = false;
callForm.value = {};
}
// 关闭弹窗
const handleVideoClose = () => {
chargeOpenJK.value = false;
currentVideoUrl.value = "";
};
// 关闭弹窗清理 - 与主地图一致
const handleClose = () => {
if (mapInstance.value) {
mapInstance.value.off();
mapInstance.value.remove();
mapInstance.value = null;
}
warningModalOpen.value = false;
clearHeatmap();
showLegend.value = false;
};
// 查看处置详情
const handleLook = (row) => {
getchuzhiListDetail(row.warningId).then((res) => {
formInfo.value = res.data || {};
});
getchuzhiListLiucheng({ handleId: row.id }).then((res) => {
timelineData.value = res.rows || [];
chargeOpen.value = true;
});
};
// 检查是否在中国范围内 - 与主地图一致
const isInChina = (lat, lng) => {
return (
lat >= CHINA_BOUNDS.minLat &&
lat <= CHINA_BOUNDS.maxLat &&
lng >= CHINA_BOUNDS.minLng &&
lng <= CHINA_BOUNDS.maxLng
);
};
const getStatusLabel = (value) => {
const found = disposal_status.value.find((item) => item.value === value);
return found ? found.label : "未知状态";
};
// 加载表格数据
const loadTableData = () => {
getchuzhiList({ warningStatus: 1 })
.then((res) => {
tableData.value = res.rows || [];
})
.catch((err) => {
console.error("加载表格数据失败:", err);
ElMessage.error("加载表格数据失败");
});
};
// 初始化 - 修复Leaflet图标路径问题与主地图一致
onMounted(() => {
loadTableData();
// 修复Leaflet默认图标路径问题
if (!L.Icon.Default.prototype._getIconUrl) {
delete L.Icon.Default.prototype._getIconUrl;
L.Icon.Default.mergeOptions({
iconRetinaUrl: require("leaflet/dist/images/marker-icon-2x.png"),
iconUrl: require("leaflet/dist/images/marker-icon.png"),
shadowUrl: require("leaflet/dist/images/marker-shadow.png"),
});
}
});
</script>
<style lang="scss" scoped>
@use "@/assets/styles/computed.scss" as calculate;
$tableColumnLength: 4;
$table__title__height: calculate.vh(45px);
$table__item__height: calculate.vh(35px);
.titles {
color: #fff;
text-align: center;
font-size: calculate.px2font(20px);
}
// 基础样式
.nocontent {
width: 100%;
display: flex;
align-items: center;
justify-content: center;
&:deep(.el-empty) {
padding: calculate.vh(50px) 0;
.el-empty__image {
width: calculate.vw(100px) !important;
}
.el-empty__description {
margin-top: calculate.vh(40px);
p {
font-size: calculate.px2font(20px);
}
}
}
}
.callDemo {
&:deep(#demo) {
display: flex;
flex-direction: column;
align-items: center;
gap: 10px;
.el-button {
max-width: 100px;
}
}
}
// 弹窗样式
.detail-section {
height: 50%;
padding: 10px;
overflow: auto;
box-sizing: border-box;
}
.map-controls {
height: 5%;
padding: 5px 10px;
display: flex;
align-items: center;
gap: 15px;
box-sizing: border-box;
:deep(.el-slider) {
width: 150px;
}
}
.map-section {
height: calculate.vh(500px);
position: relative;
padding: calculate.vh(10px);
box-sizing: border-box;
margin-top: calculate.vh(10px);
}
#warningMap {
width: 100%;
height: 100%;
border-radius: 4px;
position: relative;
z-index: 1;
}
// 地图状态样式 - 与主地图一致
.map-loading,
.no-data {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
z-index: 1001;
background: rgba(255, 255, 255, 0.9);
padding: 20px;
border-radius: 8px;
}
.map-loading {
display: flex;
align-items: center;
gap: 10px;
color: #555;
}
// 热力图图例 - 与主地图完全一致
.map-legend {
position: absolute;
bottom: 30px;
right: 20px;
z-index: 1000;
background: rgba(255, 255, 255, 0.9);
padding: 12px;
border-radius: 8px;
color: #333;
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
border: 1px solid #eee;
}
.map-legend h4 {
margin: 0 0 8px 0;
font-size: 14px;
color: #333;
}
.legend-gradient {
height: 20px;
width: 180px;
background: linear-gradient(
to right,
#ffeda0,
/* 低 */ #fed976,
#feb24c,
#fd8d3c,
#fc4e2a,
#e31a1c /* 高 */
);
margin: 5px 0;
border-radius: 4px;
}
.legend-labels {
display: flex;
justify-content: space-between;
font-size: 12px;
color: #555;
}
.card__content-box {
.table {
width: 100%;
height: 100%;
display: flex;
flex-direction: column;
overflow: hidden;
margin-top: 2vh;
// 表头容器 - 固定不滚动
&__header-container {
flex-shrink: 0; // 不缩小
border-bottom: 1px solid #888; // 增加分隔线
}
// 内容容器 - 可滚动
&__body-container {
flex-grow: 1; // 占满剩余空间
overflow-y: auto; // 允许垂直滚动
// 添加滚动条样式(可选)
&::-webkit-scrollbar {
width: 0;
}
&::-webkit-scrollbar-thumb {
background-color: rgba(255, 255, 255, 0.3);
border-radius: 3px;
}
}
&__item {
line-height: $table__item__height;
display: grid;
grid-template-columns: 2fr repeat(4, minmax(0, 3fr));
color: #fff;
text-align: center;
font-size: calculate.px2font(14px);
font-weight: bold;
border-radius: calculate.px2font(2px);
padding: calculate.vh(7px) 0;
cursor: pointer;
border-bottom: 10px solid #0b2342; // 红色分隔线(若只需纯间距可删除此句)
// background-color: #29689e;
}
&__item:nth-child(2n) {
// background-color: #3c93d2;
}
&__item:hover {
box-shadow: inset 0 0 0 calculate.px2font(3px) #266fff;
}
// 表头样式
&__header {
font-weight: bold;
border-bottom: none;
}
}
}
// 时间线样式
.custom-timeline {
position: relative;
padding-left: 20px;
margin-top: 4%;
.timeline-item {
position: relative;
padding-bottom: 20px;
&:not(:last-child)::after {
content: "";
position: absolute;
left: -2px;
top: 16px;
height: calc(100% - 16px);
width: 2px;
background: #e4e7ed;
}
}
.timeline-dot {
position: absolute;
left: -8px;
top: 4px;
width: 12px;
height: 12px;
border-radius: 50%;
background: #409eff;
}
.timeline-content {
padding-left: 20px;
color: #fff;
}
}
.tooltip {
width: 98%;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
// 图层层级控制 - 与主地图完全一致
:deep(.leaflet-map-pane) {
z-index: 2 !important;
}
:deep(.leaflet-tile-pane) {
z-index: 2 !important;
} /* 底图 */
:deep(.leaflet-overlay-pane) {
z-index: 3 !important;
} /* 热力图在底图上方 */
:deep(.leaflet-marker-pane) {
z-index: 4 !important;
} /* 标记在热力图上方 */
:deep(.leaflet-popup-pane) {
z-index: 5 !important;
} /* 弹窗在最上方 */
.largeScreen__descriptions {
:deep(.el-button){
background: #0479fe;
}
}
</style>