feat(situational-awareness): 添加场景标签和调度加载动画
- 添加SceneLabel组件,用于在对比模式下标记灾害场景 - 更新页面标题为“渝路智管-应急保通事件处置” - 修改面板标题,并添加强制调度的事件处理 - 使用条件图标增强地图标记,用于应急中心 - 更新3D模型配置URL,并在调度开始时添加加载动画 - 替换面板和工具提示的背景图片 - 添加新的图片素材,包括危险图标和加载GIF
|
Before Width: | Height: | Size: 11 KiB After Width: | Height: | Size: 12 KiB |
|
Before Width: | Height: | Size: 25 KiB After Width: | Height: | Size: 16 KiB |
|
After Width: | Height: | Size: 1.7 KiB |
|
After Width: | Height: | Size: 832 B |
|
After Width: | Height: | Size: 28 KiB |
|
Before Width: | Height: | Size: 13 KiB After Width: | Height: | Size: 12 KiB |
|
After Width: | Height: | Size: 21 KiB |
|
After Width: | Height: | Size: 17 KiB |
@ -10,7 +10,7 @@
|
||||
</CollapsiblePanel>
|
||||
|
||||
<CollapsiblePanel title="快速响应" subtitle="「力量调度」">
|
||||
<ForceDispatch />
|
||||
<ForceDispatch @start-dispatch="handleStartDispatch" />
|
||||
</CollapsiblePanel>
|
||||
</div>
|
||||
|
||||
@ -64,6 +64,16 @@ const isLocationOpen = ref(true)
|
||||
const toggleLocation = () => {
|
||||
isLocationOpen.value = !isLocationOpen.value
|
||||
}
|
||||
|
||||
// 定义对外事件
|
||||
const emit = defineEmits(['start-dispatch'])
|
||||
|
||||
/**
|
||||
* 处理力量调度启动事件,向上传递给父组件
|
||||
*/
|
||||
const handleStartDispatch = (payload) => {
|
||||
emit('start-dispatch', payload)
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
|
||||
@ -12,7 +12,7 @@
|
||||
<div class="logo-section">
|
||||
<img src="../assets/images/3ad857a9ed044c12b0e3b4345af6be59_mergeImage.png" alt="logo" class="logo-image" />
|
||||
</div>
|
||||
<h1 class="page-title">渝路智管-公路安全畅通运行管理</h1>
|
||||
<h1 class="page-title">渝路智管-应急保通事件处置</h1>
|
||||
</div>
|
||||
|
||||
<!-- <div class="page-header__right">
|
||||
@ -123,7 +123,7 @@ const handleBack = () => {
|
||||
-webkit-text-fill-color: transparent;
|
||||
font-size: fs(36);
|
||||
letter-spacing: vw(1.8);
|
||||
font-family: FZLTTHJW--GB1-0, sans-serif;
|
||||
// font-family: FZLTTHJW--GB1-0, sans-serif;
|
||||
white-space: nowrap;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
<template>
|
||||
<div class="right-panel">
|
||||
<CollapsiblePanel title="现场处置" subtitle="「调度指挥」">
|
||||
<CollapsiblePanel title="快速处置" subtitle="「调度指挥」">
|
||||
<DispatchCommand />
|
||||
</CollapsiblePanel>
|
||||
|
||||
|
||||
@ -0,0 +1,85 @@
|
||||
<template>
|
||||
<div class="scene-label" :class="labelClass">
|
||||
<div class="scene-label__content">
|
||||
<img :src="iconSrc" alt="scene" class="scene-label__icon" />
|
||||
<span class="scene-label__text">{{ text }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { computed } from 'vue'
|
||||
import sceneIcon from '../assets/images/SketchPng08621fb3b35614299e29352b8d67ad9c2c7dccf7b9c17d042492671e3bbe19f8.png'
|
||||
|
||||
const props = defineProps({
|
||||
/**
|
||||
* 标签文本
|
||||
*/
|
||||
text: {
|
||||
type: String,
|
||||
required: true
|
||||
},
|
||||
/**
|
||||
* 标签位置:
|
||||
* - 'center-left': 对比模式中间分割线的左侧
|
||||
* - 'right-left': 右侧面板的左边
|
||||
*/
|
||||
position: {
|
||||
type: String,
|
||||
default: 'right-left',
|
||||
validator: (value) => ['center-left', 'right-left'].includes(value)
|
||||
}
|
||||
})
|
||||
|
||||
const labelClass = computed(() => {
|
||||
return `scene-label--${props.position}`
|
||||
})
|
||||
|
||||
const iconSrc = sceneIcon
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
@use '@/styles/mixins.scss' as *;
|
||||
|
||||
.scene-label {
|
||||
position: absolute;
|
||||
top: calc(var(--sa-header-height));
|
||||
z-index: 10;
|
||||
pointer-events: none;
|
||||
|
||||
// 中间分割线左侧(灾前现场实景)
|
||||
&--center-left {
|
||||
left: 50%;
|
||||
transform: translateX(calc(-100% - vw(10)));
|
||||
}
|
||||
|
||||
// 右侧面板左边(灾后现场实景)
|
||||
&--right-left {
|
||||
left: calc(100% - var(--sa-right-width));
|
||||
transform: translateX(calc(-100% - vw(10)));
|
||||
}
|
||||
|
||||
&__content {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: vw(6);
|
||||
padding: vw(5) vw(10);
|
||||
background: rgba(20, 53, 118, 1);
|
||||
border: 1px solid var(--border-color);
|
||||
border-radius: vw(8);
|
||||
}
|
||||
|
||||
&__icon {
|
||||
width: vw(32);
|
||||
height: vw(32);
|
||||
}
|
||||
|
||||
&__text {
|
||||
color: var(--text-white);
|
||||
font-size: fs(15);
|
||||
font-family: SourceHanSansCN-Medium, sans-serif;
|
||||
font-weight: 500;
|
||||
white-space: nowrap;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@ -218,7 +218,7 @@ function onAfterLeave(el) {
|
||||
inset: 0;
|
||||
border-style: solid;
|
||||
border-width: vh(25) vw(30);
|
||||
border-image-source: url('../../assets/images/面板bg.png');
|
||||
border-image-source: url('../../assets/images/通用卡片bg.png');
|
||||
border-image-slice: 25 30 25 30 fill;
|
||||
border-image-width: vh(25) vw(30);
|
||||
border-image-repeat: stretch;
|
||||
|
||||
@ -268,7 +268,7 @@ const handleClose = () => {
|
||||
.map-tooltip__background {
|
||||
position: relative;
|
||||
padding: vh(14) vw(18);
|
||||
background: url('../../assets/images/Tooltip/tooltipBg.png') no-repeat;
|
||||
background: url('../../assets/images/Tooltip/弹窗bg.png') no-repeat;
|
||||
background-size: 100% 100%;
|
||||
border-radius: vw(8);
|
||||
box-shadow: 0 vw(8) vw(24) rgba(0, 0, 0, 0.5);
|
||||
|
||||
@ -31,12 +31,12 @@ defineProps({
|
||||
justify-content: space-between;
|
||||
width: vw(400);
|
||||
height: vh(43);
|
||||
background-image: url('../../assets/images/SketchPng2800be582615dbc26e07b4d56d3fc22a0517aa84065b4d6502827c05f18ca17d.png');
|
||||
background-image: url('../../assets/images/标题栏bg1.png');
|
||||
background-position: 0 -1px;
|
||||
background-repeat: no-repeat;
|
||||
background-size: vw(400) vh(45);
|
||||
padding: 0 vw(20);
|
||||
margin-bottom: vh(20);
|
||||
margin-bottom: vw(10);
|
||||
|
||||
&__content {
|
||||
display: flex;
|
||||
|
||||
@ -6,6 +6,7 @@ import { cesiumDataConfig } from '../config/cesiumData'
|
||||
import soldierIcon from '../assets/images/SketchPngfbec927027ff9e49207749ebaafd229429315341fda199251b6dfb1723ff17fb.png'
|
||||
import deviceIcon from '../assets/images/SketchPng860d54f2a31f5f441fc6a88081224f1e98534bf6d5ca1246e420983bdf690380.png'
|
||||
import emergencyBaseIcon from '../assets/images/应急基地.png'
|
||||
import emergencyCenterIcon from '../assets/images/应急中心.png'
|
||||
|
||||
// 默认高度偏移(米)- 与 WuRenJi 保持一致
|
||||
const DEFAULT_HEIGHT_OFFSET = 100
|
||||
@ -625,10 +626,15 @@ export function useMapMarkers() {
|
||||
false
|
||||
)
|
||||
|
||||
// 根据养护站名称选择图标
|
||||
const stationIcon = station.stationName === '忠县公路交通应急物资储备中心'
|
||||
? emergencyCenterIcon
|
||||
: emergencyBaseIcon
|
||||
|
||||
const entity = viewer.entities.add({
|
||||
position: result.position,
|
||||
billboard: {
|
||||
image: emergencyBaseIcon,
|
||||
image: stationIcon,
|
||||
width: 48,
|
||||
height: 48,
|
||||
verticalOrigin: Cesium.VerticalOrigin.BOTTOM,
|
||||
|
||||
@ -42,7 +42,9 @@ export const AFTER_3DTILES_CONFIG = {
|
||||
name: '灾后3D模型',
|
||||
|
||||
// 3D Tiles 服务 URL
|
||||
url: 'http://222.212.85.86:9000/300bdf2b-a150-406e-be63-d28bd29b409f/model/S107/terra_b3dms/tileset.json',
|
||||
// url: 'http://222.212.85.86:9000/300bdf2b-a150-406e-be63-d28bd29b409f/model/S107/terra_b3dms/tileset.json',
|
||||
url: 'http://222.212.85.86:9000/300bdf2b-a150-406e-be63-d28bd29b409f/model/ylzg/zxyj1119/terra_b3dms/tileset.json',
|
||||
|
||||
|
||||
// 默认可见性(初始化时灾后模型默认显示)
|
||||
visible: true,
|
||||
|
||||
@ -26,6 +26,20 @@
|
||||
<div class="situational-awareness__right-map">
|
||||
<MapViewer @tool-change="handleMapToolChange" />
|
||||
</div>
|
||||
|
||||
<!-- 场景标签层 -->
|
||||
<!-- 灾前现场实景标签 - 在中间分割线左侧 -->
|
||||
<SceneLabel
|
||||
v-if="isCompareMode"
|
||||
text="灾前现场实景"
|
||||
position="center-left"
|
||||
/>
|
||||
|
||||
<!-- 灾后现场实景标签 - 在右侧面板左边 -->
|
||||
<SceneLabel
|
||||
text="灾后现场实景"
|
||||
position="right-left"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<!-- 地图遮罩层 -->
|
||||
@ -36,7 +50,7 @@
|
||||
<div
|
||||
class="situational-awareness__panel-column situational-awareness__panel-column--left"
|
||||
>
|
||||
<LeftPanel />
|
||||
<LeftPanel @start-dispatch="handleStartDispatch" />
|
||||
</div>
|
||||
<div
|
||||
class="situational-awareness__center-spacer"
|
||||
@ -78,6 +92,15 @@
|
||||
</template>
|
||||
</MapTooltip>
|
||||
</div>
|
||||
|
||||
<!-- 加载动画层 - 一键启动后显示 -->
|
||||
<div v-if="showLoading" class="situational-awareness__loading-layer">
|
||||
<img
|
||||
src="./assets/images/加载gif.gif"
|
||||
alt="加载中"
|
||||
class="situational-awareness__loading-gif"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 弹窗组件 -->
|
||||
@ -107,6 +130,7 @@ import RightPanel from "./components/RightPanel/index.vue";
|
||||
import PersonnelDetail from "./components/Popups/PersonnelDetail.vue";
|
||||
import EmergencyCenterDetail from "./components/Popups/EmergencyCenterDetail.vue";
|
||||
import MapTooltip from "./components/shared/MapTooltip.vue";
|
||||
import SceneLabel from "./components/SceneLabel.vue";
|
||||
import { useDisasterData } from "./composables/useDisasterData";
|
||||
import { useDualMapCompare } from "./composables/useDualMapCompare";
|
||||
import { useMapMarkers } from "./composables/useMapMarkers";
|
||||
@ -117,6 +141,7 @@ import { request } from "@shared/utils/request";
|
||||
|
||||
// 标记点图标
|
||||
import emergencyCenterIcon from "./assets/images/应急中心.png";
|
||||
import dangerIcon from "./assets/images/danger.png";
|
||||
import soldierIcon from "./assets/images/SketchPngfbec927027ff9e49207749ebaafd229429315341fda199251b6dfb1723ff17fb.png";
|
||||
import deviceIcon from "./assets/images/SketchPng860d54f2a31f5f441fc6a88081224f1e98534bf6d5ca1246e420983bdf690380.png";
|
||||
import emergencyBaseIcon from "./assets/images/应急基地.png";
|
||||
@ -160,6 +185,9 @@ const { tooltipState: mapTooltip, showTooltip, hideTooltip, updateTooltipPositio
|
||||
// 当前显示 tooltip 的实体(用于相机移动时更新位置)
|
||||
const currentTooltipEntity = ref(null);
|
||||
|
||||
// 加载动画状态
|
||||
const showLoading = ref(false);
|
||||
|
||||
// 3D Tiles加载功能
|
||||
const { load3DTileset, waitForTilesetReady } = use3DTiles();
|
||||
|
||||
@ -190,7 +218,12 @@ const setupMapClickHandler = (viewer) => {
|
||||
} else if (type === 'device') {
|
||||
showMarkerTooltip(viewer, entity, click.position, deviceIcon);
|
||||
} else if (type === 'emergencyBase' || type === 'station') {
|
||||
showMarkerTooltip(viewer, entity, click.position, emergencyBaseIcon);
|
||||
// 对于养护站,根据名称判断使用哪个图标
|
||||
const stationName = entity.properties.name?.getValue() || '';
|
||||
const icon = stationName === '忠县公路交通应急物资储备中心'
|
||||
? emergencyCenterIcon
|
||||
: emergencyBaseIcon;
|
||||
showMarkerTooltip(viewer, entity, click.position, icon);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
@ -274,12 +307,27 @@ const showMarkerTooltip = (viewer, entity, screenPosition, icon) => {
|
||||
{ label: '距离', value: properties.distance?.getValue() || '-' }
|
||||
);
|
||||
} else if (type === 'station') {
|
||||
const stationName = properties.name?.getValue() || '';
|
||||
const distance = properties.distance?.getValue() || 0;
|
||||
|
||||
// 如果是应急中心,显示应急中心信息
|
||||
if (stationName === '忠县公路交通应急物资储备中心') {
|
||||
title = '应急中心';
|
||||
fields.push(
|
||||
{ label: '名称', value: '忠县应急中心' },
|
||||
{ label: '行政等级', value: '国道' },
|
||||
{ label: '隶属单位', value: '交通公路部门' },
|
||||
{ label: '位置信息', value: `目前为止距离现场${distance}公里` }
|
||||
);
|
||||
} else {
|
||||
// 其他养护站显示 tooltip
|
||||
title = '养护站';
|
||||
fields.push(
|
||||
{ label: '名称', value: properties.name?.getValue() || '-' },
|
||||
{ label: '距离', value: `${properties.distance?.getValue() || 0}公里` }
|
||||
{ label: '名称', value: stationName || '-' },
|
||||
{ label: '距离', value: `${distance}公里` }
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// 显示 Tooltip,使用实体的屏幕坐标
|
||||
showTooltip({
|
||||
@ -357,9 +405,9 @@ onMounted(() => {
|
||||
0
|
||||
),
|
||||
billboard: {
|
||||
image: emergencyCenterIcon,
|
||||
image: dangerIcon,
|
||||
width: 36,
|
||||
height: 40,
|
||||
height: 36,
|
||||
verticalOrigin: Cesium.VerticalOrigin.BOTTOM,
|
||||
heightReference: Cesium.HeightReference.CLAMP_TO_GROUND,
|
||||
disableDepthTestDistance: Number.POSITIVE_INFINITY,
|
||||
@ -614,6 +662,22 @@ const handleMapTooltipClose = () => {
|
||||
mapTooltip.value.visible = false;
|
||||
};
|
||||
|
||||
/**
|
||||
* 处理力量调度启动事件
|
||||
* 显示加载动画,3秒后自动隐藏
|
||||
*/
|
||||
const handleStartDispatch = (payload) => {
|
||||
console.log('[index.vue] 启动力量调度:', payload);
|
||||
|
||||
// 显示加载动画
|
||||
showLoading.value = true;
|
||||
|
||||
// 3秒后自动隐藏加载动画
|
||||
setTimeout(() => {
|
||||
showLoading.value = false;
|
||||
}, 3000);
|
||||
};
|
||||
|
||||
/**
|
||||
* 在指定屏幕坐标显示地图 Tooltip
|
||||
*
|
||||
@ -745,7 +809,7 @@ const showMapTooltip = ({ x, y, title = "", icon = "", data = null }) => {
|
||||
--sa-gap: calc(16 / 1920 * var(--cq-inline-100, 100vw));
|
||||
--sa-padding: calc(16 / 1920 * var(--cq-inline-100, 100vw));
|
||||
--sa-header-height: calc(
|
||||
121 / 1080 * var(--cq-block-100, 100vh)
|
||||
131 / 1080 * var(--cq-block-100, 100vh)
|
||||
); // Header 高度
|
||||
--sa-min-width: 1280px;
|
||||
--sa-min-height: 720px;
|
||||
@ -908,6 +972,29 @@ const showMapTooltip = ({ x, y, title = "", icon = "", data = null }) => {
|
||||
z-index: 4; // 高于控件层
|
||||
pointer-events: none; // 容器不拦截事件,点击穿透到地图
|
||||
}
|
||||
|
||||
// 加载动画层 - 一键启动后显示
|
||||
&__loading-layer {
|
||||
position: absolute;
|
||||
top: calc(var(--sa-header-height) + vh(20));
|
||||
left: 0;
|
||||
right: 0;
|
||||
// bottom: 0;
|
||||
z-index: 5; // 高于 Tooltip 层
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
pointer-events: none; // 不阻止点击穿透
|
||||
}
|
||||
|
||||
// 加载 GIF 图片
|
||||
&__loading-gif {
|
||||
width: auto;
|
||||
height: auto;
|
||||
max-width: 80%;
|
||||
max-height: 60%;
|
||||
object-fit: contain;
|
||||
}
|
||||
}
|
||||
|
||||
// Tooltip 内容字段样式
|
||||
|
||||