合并 加载应急统计数据和加载点位数据的接口 一个接口实现两个效果 避免接口的异步问题

This commit is contained in:
huangchenhao 2025-12-08 10:41:14 +08:00
parent f1fe8dd14c
commit 9874a8eda8
2 changed files with 110 additions and 192 deletions

View File

@ -95,8 +95,8 @@
<div class="stat-card stat-card--personnel"> <div class="stat-card stat-card--personnel">
<span class="stat-label">应急人员</span> <span class="stat-label">应急人员</span>
<div class="stat-value-wrapper"> <div class="stat-value-wrapper">
<!-- <span class="stat-value">{{ forcePreset.personnel }}</span> --> <span class="stat-value">{{ forcePreset.personnel }}</span>
<span class="stat-value">{{ distance === 30 ? 47 : 69 }}</span> <!-- <span class="stat-value">{{ distance === 30 ? 47 : 69 }}</span> -->
<span class="stat-unit"></span> <span class="stat-unit"></span>
</div> </div>
</div> </div>

View File

@ -6,28 +6,16 @@
<!-- 主内容区域 --> <!-- 主内容区域 -->
<div class="situational-awareness__main"> <div class="situational-awareness__main">
<!-- 地图底层 --> <!-- 地图底层 -->
<div <div class="situational-awareness__map-layer" :class="{ 'is-compare-mode': isCompareMode }">
class="situational-awareness__map-layer"
:class="{ 'is-compare-mode': isCompareMode }"
>
<!-- 左侧地图容器对比模式下显示灾前场景 --> <!-- 左侧地图容器对比模式下显示灾前场景 -->
<div <div id="leftCesiumContainer" class="situational-awareness__left-map"></div>
id="leftCesiumContainer"
class="situational-awareness__left-map"
></div>
<!-- 中间分割线 --> <!-- 中间分割线 -->
<div <div v-if="isCompareMode" class="situational-awareness__center-divider"></div>
v-if="isCompareMode"
class="situational-awareness__center-divider"
></div>
<!-- 右侧地图主地图 - 灾后场景 --> <!-- 右侧地图主地图 - 灾后场景 -->
<div class="situational-awareness__right-map"> <div class="situational-awareness__right-map">
<MapViewer <MapViewer :active-tool-key="activeToolKey" @tool-change="handleMapToolChange" />
:active-tool-key="activeToolKey"
@tool-change="handleMapToolChange"
/>
</div> </div>
</div> </div>
@ -36,22 +24,12 @@
<!-- 场景标签层 - 独立层级显示在遮罩层之上 --> <!-- 场景标签层 - 独立层级显示在遮罩层之上 -->
<div class="situational-awareness__scene-labels-layer"> <div class="situational-awareness__scene-labels-layer">
<ActionButton <ActionButton type="primary" v-if="isCompareMode" class="situational-awareness__sync-btn"
type="primary" @click="toggleCameraSync" :text="isCameraSyncEnabled ? '同步灾前灾后实景' : '未同步灾前灾后实景'
v-if="isCompareMode" ">
class="situational-awareness__sync-btn"
@click="toggleCameraSync"
:text="
isCameraSyncEnabled ? '同步灾前灾后实景' : '未同步灾前灾后实景'
"
>
</ActionButton> </ActionButton>
<!-- 灾前现场实景标签 - 在中间分割线左侧 --> <!-- 灾前现场实景标签 - 在中间分割线左侧 -->
<SceneLabel <SceneLabel v-if="isCompareMode" text="灾前现场实景" position="center-left" />
v-if="isCompareMode"
text="灾前现场实景"
position="center-left"
/>
<!-- 灾后现场实景标签 - 在右侧面板左边 --> <!-- 灾后现场实景标签 - 在右侧面板左边 -->
<SceneLabel text="灾后现场实景" position="right-left" /> <SceneLabel text="灾后现场实景" position="right-left" />
@ -60,24 +38,16 @@
<!-- 浮动面板层 --> <!-- 浮动面板层 -->
<div class="situational-awareness__panels-layer"> <div class="situational-awareness__panels-layer">
<Transition name="panel-slide-left"> <Transition name="panel-slide-left">
<div <div v-show="!isLeftPanelCollapsed"
v-show="!isLeftPanelCollapsed" class="situational-awareness__panel-column situational-awareness__panel-column--left">
class="situational-awareness__panel-column situational-awareness__panel-column--left" <LeftPanel @start-dispatch="handleStartDispatch" @view-plan="handleViewPlan"
> @force-preset-toggle="handleForcePresetToggle" @quick-response-toggle="handleQuickResponseToggle" />
<LeftPanel
@start-dispatch="handleStartDispatch"
@view-plan="handleViewPlan"
@force-preset-toggle="handleForcePresetToggle"
@quick-response-toggle="handleQuickResponseToggle"
/>
</div> </div>
</Transition> </Transition>
<Transition name="panel-slide-right"> <Transition name="panel-slide-right">
<div <div v-show="!isRightPanelCollapsed"
v-show="!isRightPanelCollapsed" class="situational-awareness__panel-column situational-awareness__panel-column--right">
class="situational-awareness__panel-column situational-awareness__panel-column--right"
>
<RightPanel @quick-disposal-click="handleQuickDisposalClick" /> <RightPanel @quick-disposal-click="handleQuickDisposalClick" />
</div> </div>
</Transition> </Transition>
@ -86,33 +56,18 @@
<!-- 折叠按钮层 --> <!-- 折叠按钮层 -->
<div class="situational-awareness__collapse-buttons-layer"> <div class="situational-awareness__collapse-buttons-layer">
<!-- 左侧折叠按钮 --> <!-- 左侧折叠按钮 -->
<button <button class="situational-awareness__collapse-btn situational-awareness__collapse-btn--left"
class="situational-awareness__collapse-btn situational-awareness__collapse-btn--left" :class="{ 'is-collapsed': isLeftPanelCollapsed }" @click="toggleLeftPanel"
:class="{ 'is-collapsed': isLeftPanelCollapsed }" :aria-label="isLeftPanelCollapsed ? '展开左侧面板' : '收起左侧面板'">
@click="toggleLeftPanel" <img :src="isLeftPanelCollapsed ? collapseRightArrow : collapseLeftArrow" alt="" class="collapse-arrow" />
:aria-label="isLeftPanelCollapsed ? '展开左侧面板' : '收起左侧面板'"
>
<img
:src="isLeftPanelCollapsed ? collapseRightArrow : collapseLeftArrow"
alt=""
class="collapse-arrow"
/>
</button> </button>
<!-- 右侧折叠按钮 --> <!-- 右侧折叠按钮 -->
<button <button class="situational-awareness__collapse-btn situational-awareness__collapse-btn--right"
class="situational-awareness__collapse-btn situational-awareness__collapse-btn--right" :class="{ 'is-collapsed': isRightPanelCollapsed }" @click="toggleRightPanel"
:class="{ 'is-collapsed': isRightPanelCollapsed }" :aria-label="isRightPanelCollapsed ? '展开右侧面板' : '收起右侧面板'">
@click="toggleRightPanel" <img :src="isRightPanelCollapsed ? collapseLeftArrow : collapseRightArrow
:aria-label="isRightPanelCollapsed ? '展开右侧面板' : '收起右侧面板'" " alt="" class="collapse-arrow" />
>
<img
:src="
isRightPanelCollapsed ? collapseLeftArrow : collapseRightArrow
"
alt=""
class="collapse-arrow"
/>
</button> </button>
</div> </div>
@ -123,25 +78,13 @@
<!-- 地图 Tooltip - 用于显示地图标记点的轻量级信息提示框 --> <!-- 地图 Tooltip - 用于显示地图标记点的轻量级信息提示框 -->
<div class="situational-awareness__tooltip-layer"> <div class="situational-awareness__tooltip-layer">
<MapTooltip <MapTooltip :visible="mapTooltip.visible" :x="mapTooltip.x" :y="mapTooltip.y" :title="mapTooltip.title"
:visible="mapTooltip.visible" :icon="mapTooltip.icon" :z-index="mapTooltip.zIndex"
:x="mapTooltip.x" :show-video-icon="mapTooltip.data && mapTooltip.data.supportVideo" :video-icon-src="mediaIcon"
:y="mapTooltip.y" @video-icon-click="handleVideoIconClick" @close="handleMapTooltipClose">
:title="mapTooltip.title"
:icon="mapTooltip.icon"
:z-index="mapTooltip.zIndex"
:show-video-icon="mapTooltip.data && mapTooltip.data.supportVideo"
:video-icon-src="mediaIcon"
@video-icon-click="handleVideoIconClick"
@close="handleMapTooltipClose"
>
<!-- Tooltip 内容插槽 - 根据实际业务数据渲染 --> <!-- Tooltip 内容插槽 - 根据实际业务数据渲染 -->
<template v-if="mapTooltip.data && mapTooltip.data.fields"> <template v-if="mapTooltip.data && mapTooltip.data.fields">
<div <div v-for="(field, index) in mapTooltip.data.fields" :key="index" class="tooltip-field-item">
v-for="(field, index) in mapTooltip.data.fields"
:key="index"
class="tooltip-field-item"
>
<span class="tooltip-field-label">{{ field.label }}</span> <span class="tooltip-field-label">{{ field.label }}</span>
<span class="tooltip-field-value">{{ field.value }}</span> <span class="tooltip-field-value">{{ field.value }}</span>
</div> </div>
@ -149,12 +92,8 @@
<!-- 操作按钮插槽 --> <!-- 操作按钮插槽 -->
<template v-if="mapTooltip.data && mapTooltip.data.actions" #actions> <template v-if="mapTooltip.data && mapTooltip.data.actions" #actions>
<button <button v-for="(action, index) in mapTooltip.data.actions" :key="index" class="tooltip-action-btn"
v-for="(action, index) in mapTooltip.data.actions" @click="handleTooltipAction(action)">
:key="index"
class="tooltip-action-btn"
@click="handleTooltipAction(action)"
>
{{ action.label }} {{ action.label }}
</button> </button>
</template> </template>
@ -163,11 +102,7 @@
<!-- 加载动画层 - 一键启动后显示 --> <!-- 加载动画层 - 一键启动后显示 -->
<div v-if="showLoading" class="situational-awareness__loading-layer"> <div v-if="showLoading" class="situational-awareness__loading-layer">
<img <img src="./assets/images/加载3.gif" alt="加载中" class="situational-awareness__loading-gif" />
src="./assets/images/加载3.gif"
alt="加载中"
class="situational-awareness__loading-gif"
/>
<!-- <img <!-- <img
src="./assets/images/加载.gif" src="./assets/images/加载.gif"
alt="加载中" alt="加载中"
@ -177,50 +112,26 @@
</div> </div>
<!-- 弹窗组件 --> <!-- 弹窗组件 -->
<PersonnelDetail <PersonnelDetail :visible="showPersonnelDetail" :personnel-data="selectedPersonnel"
:visible="showPersonnelDetail" @close="showPersonnelDetail = false" @link="handlePersonnelLink" />
:personnel-data="selectedPersonnel"
@close="showPersonnelDetail = false"
@link="handlePersonnelLink"
/>
<EmergencyCenterDetail <EmergencyCenterDetail :visible="showCenterDetail" :center-data="selectedCenter"
:visible="showCenterDetail" @close="showCenterDetail = false" />
:center-data="selectedCenter"
@close="showCenterDetail = false"
/>
<!-- 视频监控弹窗 --> <!-- 视频监控弹窗 -->
<VideoModal <VideoModal v-if="showVideoModal" :visible="showVideoModal" :monitor="selectedVideoMonitor"
v-if="showVideoModal" @close="handleVideoModalClose" />
:visible="showVideoModal"
:monitor="selectedVideoMonitor"
@close="handleVideoModalClose"
/>
<!-- 智能应急方案弹窗 --> <!-- 智能应急方案弹窗 -->
<StretchableModal <StretchableModal :visible="showStretchableModal" title="公路灾害现场处置推荐方案" :close-on-click-modal="true"
:visible="showStretchableModal" :close-on-press-escape="true" :show-close="true" @close="showStretchableModal = false"
title="公路灾害现场处置推荐方案" width="clamp(100px, 50vw, 1400px)">
:close-on-click-modal="true"
:close-on-press-escape="true"
:show-close="true"
@close="showStretchableModal = false"
width="clamp(100px, 50vw, 1400px)"
>
<!-- 使用应急方案内容组件 --> <!-- 使用应急方案内容组件 -->
<EmergencyPlanContent <EmergencyPlanContent :stations="disasterData.forcePreset.value.stations" />
:stations="disasterData.forcePreset.value.stations"
/>
<!-- 底部一键启动按钮 --> <!-- 底部一键启动按钮 -->
<template #extraButtons> <template #extraButtons>
<ActionButton <ActionButton text="响应调度" type="inModal" size="medium" @click="handleModalStartDispatch" />
text="响应调度"
type="inModal"
size="medium"
@click="handleModalStartDispatch"
/>
</template> </template>
</StretchableModal> </StretchableModal>
</div> </div>
@ -770,6 +681,9 @@ const handleForcePresetToggle = async () => {
if (!showMarkersAndRange.value) { if (!showMarkersAndRange.value) {
// , // ,
// 50km
disasterData.updateSearchRadius(50);
// 1. (使) // 1. (使)
if (isCompareMode.value) { if (isCompareMode.value) {
console.log("[index.vue] 快速匹配需要关闭对比模式"); console.log("[index.vue] 快速匹配需要关闭对比模式");
@ -1034,11 +948,11 @@ const handleDistanceChange = async (newDistance) => {
// 3. // 3.
// await loadEmergencyResources(DISASTER_CENTER.lon, DISASTER_CENTER.lat) // await loadEmergencyResources(DISASTER_CENTER.lon, DISASTER_CENTER.lat)
// 4. // 3. ()
await loadReserveCentersAndPresets(DISASTER_CENTER.lon, DISASTER_CENTER.lat); await loadReserveCentersAndPresetsAndStatistics(DISASTER_CENTER.lon, DISASTER_CENTER.lat);
// 5. () // // 5. ()
await loadEmergencyBaseAndPreset(DISASTER_CENTER.lon, DISASTER_CENTER.lat); // await loadEmergencyBaseAndPreset(DISASTER_CENTER.lon, DISASTER_CENTER.lat);
}; };
/** /**
@ -1065,7 +979,7 @@ const showAllMarkersAndRange = async () => {
// 2. / // 2. /
console.log("[index.vue] 加载全部储备中心/预置点到地图..."); console.log("[index.vue] 加载全部储备中心/预置点到地图...");
await loadReserveCentersAndPresets( await loadReserveCentersAndPresetsAndStatistics(
DISASTER_CENTER.lon, DISASTER_CENTER.lon,
DISASTER_CENTER.lat, DISASTER_CENTER.lat,
true true
@ -1269,12 +1183,12 @@ const loadEmergencyResources = async (longitude, latitude) => {
}; };
/** /**
* 加载储备中心和预置点数据 * 加载储备中心和预置点数据 以及加载应急力量统计数据
* @param {number} longitude - 经度可选不传则查询全部 * @param {number} longitude - 经度可选不传则查询全部
* @param {number} latitude - 纬度可选不传则查询全部 * @param {number} latitude - 纬度可选不传则查询全部
* @param {boolean} loadAllForMap - 是否加载全部点位到地图true时不限制距离 * @param {boolean} loadAllForMap - 是否加载全部点位到地图true时不限制距离
*/ */
const loadReserveCentersAndPresets = async ( const loadReserveCentersAndPresetsAndStatistics = async (
longitude, longitude,
latitude, latitude,
loadAllForMap = false loadAllForMap = false
@ -1292,21 +1206,21 @@ const loadReserveCentersAndPresets = async (
// loadAllForMap true // loadAllForMap true
const response = await request({ const response = await request({
url: `/snow-ops-platform/yhYjll/list`, url: `/snow-ops-platform/yhYjll/listWithStatistics`,
method: "GET", method: "GET",
params, params,
}); });
if (response?.data && Array.isArray(response.data)) { if (response?.data && Array.isArray(response.data?.list)) {
console.log( console.log(
"[index.vue] 储备中心和预置点数据加载成功:", "[index.vue] 储备中心和预置点数据加载成功:",
response.data.length, response.data?.list.length,
"个点位" "个点位"
); );
// 1. stations // 1. stations
const transformedStations = disasterData.transformReserveDataToStations( const transformedStations = disasterData.transformReserveDataToStations(
response.data, response.data.list,
{ longitude, latitude } { longitude, latitude }
); );
@ -1320,13 +1234,22 @@ const loadReserveCentersAndPresets = async (
); );
} }
// 3. // 3.
if (response?.data?.statistics) {
// (,stations)
disasterData.updateForcePreset(response.data.statistics, { onlyStatistics: true });
console.log("[index.vue] 应急统计数据加载成功:", response.data.statistics);
} else {
console.warn("[index.vue] 应急统计数据接口返回数据为空");
}
// 4.
if (mapStore.viewer) { if (mapStore.viewer) {
console.log("[index.vue] 添加储备中心和预置点地图标记..."); console.log("[index.vue] 添加储备中心和预置点地图标记...");
clearReserveCenterMarkers(mapStore.viewer); clearReserveCenterMarkers(mapStore.viewer);
await addReserveCenterMarkers( await addReserveCenterMarkers(
mapStore.viewer, mapStore.viewer,
response.data, response.data.list,
{ {
heightOffset: 10, heightOffset: 10,
}, },
@ -1354,36 +1277,36 @@ const loadReserveCentersAndPresets = async (
* 加载应急基地与预置点应急装备应急物资应急人员的统计数量 * 加载应急基地与预置点应急装备应急物资应急人员的统计数量
* /snow-ops-platform/yhYjll/statistics * /snow-ops-platform/yhYjll/statistics
*/ */
const loadEmergencyBaseAndPreset = async (longitude, latitude) => { // const loadEmergencyBaseAndPreset = async (longitude, latitude) => {
try { // try {
const response = await request({ // const response = await request({
url: `/snow-ops-platform/yhYjll/statistics`, // url: `/snow-ops-platform/yhYjll/statistics`,
method: "GET", // method: "GET",
params: { // params: {
longitude, // longitude,
latitude, // latitude,
maxDistance: disasterData.forcePreset.value.searchRadius, // maxDistance: disasterData.forcePreset.value.searchRadius,
}, // },
}); // });
if (response?.data) { // if (response?.data) {
// (,stations) // // (,stations)
disasterData.updateForcePreset(response.data, { onlyStatistics: true }); // disasterData.updateForcePreset(response.data, { onlyStatistics: true });
console.log("[index.vue] 应急统计数据加载成功:", response.data); // console.log("[index.vue] :", response.data);
} else { // } else {
console.warn("[index.vue] 应急统计数据接口返回数据为空"); // console.warn("[index.vue] ");
} // }
return response; // return response;
} catch (error) { // } catch (error) {
console.error("[index.vue] 加载应急统计数据失败:", error); // console.error("[index.vue] :", error);
ElMessage.warning({ // ElMessage.warning({
message: "应急统计数据加载失败", // message: "",
duration: 3000, // duration: 3000,
}); // });
return null; // return null;
} // }
}; // };
/** /**
* 查询应急装备和应急物资列表 /snow-ops-platform/yhYjll/listMaterials * 查询应急装备和应急物资列表 /snow-ops-platform/yhYjll/listMaterials
@ -1635,13 +1558,13 @@ const initializeScene = async () => {
hideMarkers(); hideMarkers();
console.log("[index.vue] 已隐藏应急资源标记(等待快速匹配激活)"); console.log("[index.vue] 已隐藏应急资源标记(等待快速匹配激活)");
// 12. // 12.
console.log("[index.vue] 加载储备中心和预置点数据..."); console.log("[index.vue] 加载储备中心和预置点数据...");
await loadReserveCentersAndPresets(DISASTER_CENTER.lon, DISASTER_CENTER.lat); await loadReserveCentersAndPresetsAndStatistics(DISASTER_CENTER.lon, DISASTER_CENTER.lat);
// 13. // // 13.
console.log("[index.vue] 加载应急统计数据..."); // console.log("[index.vue] ...");
await loadEmergencyBaseAndPreset(DISASTER_CENTER.lon, DISASTER_CENTER.lat); // await loadEmergencyBaseAndPreset(DISASTER_CENTER.lon, DISASTER_CENTER.lat);
cesiumViewer.value = viewer; cesiumViewer.value = viewer;
console.log("[index.vue] 场景初始化完成"); console.log("[index.vue] 场景初始化完成");
@ -1748,6 +1671,7 @@ if (token) {
@supports (width: 1cqw) { @supports (width: 1cqw) {
--cq-inline-100: min(100cqw, 1920px); --cq-inline-100: min(100cqw, 1920px);
} }
@supports (height: 1cqh) { @supports (height: 1cqh) {
--cq-block-100: min(100cqh, 1080px); --cq-block-100: min(100cqh, 1080px);
} }
@ -1757,9 +1681,7 @@ if (token) {
--sa-right-width: calc(486 / 1920 * var(--cq-inline-100, 100vw)); --sa-right-width: calc(486 / 1920 * var(--cq-inline-100, 100vw));
--sa-gap: calc(16 / 1920 * var(--cq-inline-100, 100vw)); --sa-gap: calc(16 / 1920 * var(--cq-inline-100, 100vw));
--sa-padding: calc(16 / 1920 * var(--cq-inline-100, 100vw)); --sa-padding: calc(16 / 1920 * var(--cq-inline-100, 100vw));
--sa-header-height: calc( --sa-header-height: calc(131 / 1080 * var(--cq-block-100, 100vh)); // Header
131 / 1080 * var(--cq-block-100, 100vh)
); // Header
--sa-min-width: 1280px; --sa-min-width: 1280px;
--sa-min-height: 720px; --sa-min-height: 720px;
@ -1847,12 +1769,10 @@ if (token) {
top: 0; top: 0;
bottom: 0; bottom: 0;
width: 2px; width: 2px;
background: linear-gradient( background: linear-gradient(to bottom,
to bottom,
rgba(255, 255, 255, 0.1), rgba(255, 255, 255, 0.1),
rgba(255, 255, 255, 0.5), rgba(255, 255, 255, 0.5),
rgba(255, 255, 255, 0.1) rgba(255, 255, 255, 0.1));
);
z-index: 1; z-index: 1;
pointer-events: none; pointer-events: none;
} }
@ -1864,8 +1784,7 @@ if (token) {
z-index: 1; z-index: 1;
pointer-events: none; // pointer-events: none; //
// 使 cockpit // 使 cockpit
background: url(@/views/cockpit/assets/img/遮罩层.png) center/cover background: url(@/views/cockpit/assets/img/遮罩层.png) center/cover no-repeat;
no-repeat;
} }
// - // -
@ -2091,8 +2010,7 @@ if (token) {
min-width: vw(100); min-width: vw(100);
height: vh(36); height: vh(36);
padding: 0 vw(24); padding: 0 vw(24);
background: url("./assets/images/地图tooltip-button.png") no-repeat background: url("./assets/images/地图tooltip-button.png") no-repeat center/100% 100%;
center/100% 100%;
border: none; border: none;
color: var(--text-white); color: var(--text-white);
font-size: fs(14); font-size: fs(14);