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

1082 lines
29 KiB
Vue
Raw Normal View History

2025-10-17 23:11:55 +08:00
<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>