优化参航单位地图信息弹窗交互逻辑

This commit is contained in:
hym 2026-04-05 16:54:15 +08:00
parent 399ad08a12
commit 4fc5d6edea
6 changed files with 83 additions and 40 deletions

View File

@ -146,15 +146,11 @@ export function setHighlight(layerId, sourceId, mapId = "homeMap") {
let hoveredLineId = null;
let clickedLineId = null;
Vue.config.maps[mapId].on('click', layerId, async (e) => {
if (!e.features || e.features.length <= 0)
sourceLayer = e.features[0].sourceLayer;
// 排除没有 sourceLayer 的情况(如点击事件中 features 可能来自多个图层,某些图层缺乏 sourceLayer 属性会导致后续 setFeatureState 报错)
if (!sourceLayer)
return;
if (mapId == "workUnitMap_uuv") {
// 相关逻辑位于单位统计页面
// showUnitReport(mapId, e.features[0].properties.unit_name, e.features[0].geometry.coordinates);
return;
}
// // 查询点击点周围的所有要素(使用边界框)
// const bbox = [
// [e.point.x - 10, e.point.y - 10], // 左上
@ -194,21 +190,18 @@ export function setHighlight(layerId, sourceId, mapId = "homeMap") {
// 单击时加入选择列表(用于单击 ESC 键时取消选择状态)
clickedLineIds.push(clickedLineId);
// document.removeEventListener("keydown", removeHighlight, true);
document.addEventListener("keydown", (e) => {
removeHighlight(e, sourceId);
}, { once: true }); // once一个布尔值表示 listener 在添加之后最多只调用一次。如果为 truelistener 会在其被调用之后自动移除。
// }, true);
// document.addEventListener("keydown", (e) => {
// removeHighlight(e, sourceId);
// }, { once: true }); // once一个布尔值表示 listener 在添加之后最多只调用一次。如果为 truelistener 会在其被调用之后自动移除。
}
});
// When the user moves their mouse over the state-fill layer, we'll update the feature state for the feature under the mouse.
Vue.config.maps[mapId].on('mousemove', layerId, (e) => {
if (mapId == "workUnitMap_uuv")
return;
if (e.features.length <= 0)
return;
sourceLayer = e.features[0].sourceLayer;
// 排除没有 sourceLayer 的情况(如点击事件中 features 可能来自多个图层,某些图层缺乏 sourceLayer 属性会导致后续 setFeatureState 报错)
if (!sourceLayer)
return;
if (hoveredLineId) {
Vue.config.maps[mapId].setFeatureState(
{ source: sourceId, sourceLayer: sourceLayer, id: hoveredLineId },
@ -234,7 +227,7 @@ export function setHighlight(layerId, sourceId, mapId = "homeMap") {
});
}
export async function showUnitReport(mapId, unit, coordinates) {
export async function showUnitReport(mapId, unit, coordinate, callbackOnClose = null) {
const result = await (await fetch(`${Vue.config.baseUrl}/ds/report/unit/total.htm?unit=${unit}`)).json();
const unitReports = result.array || [];
if (!unitReports.length) {
@ -266,7 +259,7 @@ export async function showUnitReport(mapId, unit, coordinates) {
</tr>
</table>`;
showPopupDetails(mapId, coordinates, description, "350px");
showPopupDetails({ mapId, coordinate, description, maxWidth: "350px", callbackOnClose: callbackOnClose });
}
export function changeCursor(layerId, mapId = "homeMap", cursor = "pointer") {
@ -324,7 +317,7 @@ export function showSampleLineInfo(layerId, mapId = "homeMap") {
</tr>
</table>`;
showPopupDetails(mapId, e.lngLat, description, "450px");
showPopupDetails({ mapId, coordinate: e.lngLat, description, maxWidth: "450px" });
});
}
@ -369,7 +362,7 @@ export function showSampleStationInfo(layerId, mapId = "homeMap") {
</tr>
</table>`;
showPopupDetails(mapId, feature.geometry.coordinates, description, "450px");
showPopupDetails({ mapId, coordinate: feature.geometry.coordinates, description, maxWidth: "450px" });
});
}
@ -413,12 +406,12 @@ export function showTrackInfo(layerId, mapId = "homeMap") {
</tr>
</table>`;
showPopupDetails(mapId, coordinate, description, "420px");
showPopupDetails({ coordinate, mapId, description, maxWidth: "420px" });
})
});
}
export function showPopupDetails(mapId, coordinate, description, maxWidth = "300px", closeButton = true, closeOnClick = true, options = {}) {
export function showPopupDetails({ mapId, coordinate, description, maxWidth = "300px", closeButton = true, closeOnClick = true, options = {}, callbackOnClose }) {
const map = Vue.config.maps[mapId];
if (!map)
return;
@ -446,7 +439,13 @@ export function showPopupDetails(mapId, coordinate, description, maxWidth = "300
.setOffset(10)
.setMaxWidth(maxWidth)
.addTo(map);
sharedPopup.on('close', () => { sharedPopup = null; });
sharedPopup.on('close', () => {
sharedPopup = null;
// 关闭弹窗时执行回调(如有)
if (callbackOnClose && typeof callbackOnClose === "function") {
callbackOnClose();
}
});
}
} else {
sharedPopup = new window.mapboxgl.Popup(popupOptions)
@ -455,13 +454,17 @@ export function showPopupDetails(mapId, coordinate, description, maxWidth = "300
.setOffset(10)
.setMaxWidth(maxWidth)
.addTo(map);
sharedPopup.on('close', () => { sharedPopup = null; });
sharedPopup.on('close', () => {
sharedPopup = null;
// 关闭弹窗时执行回调(如有)
if (callbackOnClose && typeof callbackOnClose === "function") {
callbackOnClose();
}
});
}
}
export async function loadJsonLineFeature(layerId, color, geojson, mapId) {
// console.log("layerId: ",layerId);
let source = Vue.config.maps[mapId].getSource(layerId);
if (!source) {
Vue.config.maps[mapId].addSource(layerId, {
@ -551,13 +554,11 @@ export async function loadJsonPointFeature(layerId, color, geojson, text_field,
export async function highlightFeaturesByProperty(mapId, sourceId, layerId, propertyName = "sample_name", targetValue = "TARGET_VALUE") {
// 清空所有高亮状态(可选)
Vue.config.maps[mapId].querySourceFeatures(sourceId).forEach(f => {
console.log("AAAAA: ", f);
Vue.config.maps[mapId].setFeatureState({ source: sourceId, id: f.id }, { highlight: false });
});
// 标记匹配要素
Vue.config.maps[mapId].querySourceFeatures(sourceId).forEach(f => {
console.log("BBBBB: ", f);
if (f.properties && f.properties[propertyName] === targetValue) {
Vue.config.maps[mapId].setFeatureState({ source: sourceId, id: f.id }, { highlight: true });
}

View File

@ -64,6 +64,7 @@
</template>
<script>
import Vue from 'vue'
/**
* 无人航行潜水器汇总统计页请求 `/report/platform/auv` 系列接口交互与遥控潜水器汇总页一致ECharts
*/
@ -71,8 +72,8 @@ import BarChart from './components/BarChart.vue'
import PieChart from './components/PieChart.vue'
import { reportList, platformAuv, auvUnit, auvUnitYear, auvDiving } from '../../api/statistics'
import { filterDictItems } from '../../utils/index'
import { initMapbox, handleWheel, initSprites, triggerLayerClick } from "@/utils/mapbox-utils";
import { loadVectorLayer, loadJsonLineFeature, loadJsonPointFeature, highlightFeaturesByProperty } from '@/utils/vector-layer-utils'
import { initMapbox } from "@/utils/mapbox-utils";
import { loadVectorLayer, loadJsonPointFeature,showUnitReport } from '@/utils/vector-layer-utils'
import { ColorGenerator } from '@/utils/color-generator';
export default {
@ -207,6 +208,13 @@ export default {
});
loadJsonPointFeature("workUnit", "blue", geojson, "unit_name", "workUnitMap_auv");
Vue.config.maps['workUnitMap_auv'].on('click', 'workUnit', async (e) => {
if (!e.features || e.features.length <= 0)
return;
//
showUnitReport('workUnitMap_auv', e.features[0].properties.unit_name, e.features[0].geometry.coordinates);
});
const typeCount = res.array.reduce((acc, unit) => {
const type = unit.unit_type
acc[type] = (acc[type] || 0) + 1 // +11

View File

@ -64,13 +64,13 @@
</template>
<script>
import Vue from 'vue'
import BarChart from './components/BarChart.vue'
import PieChart from './components/PieChart.vue'
import { setImageryViewModels } from '../../utils/common'
import { reportList, platformHov, hovUnit, hovUnitYear, hovDiving } from '../../api/statistics'
import { filterDictItems } from '../../utils/index'
import { initMapbox, handleWheel, initSprites, triggerLayerClick } from "@/utils/mapbox-utils";
import { loadVectorLayer, loadJsonLineFeature, loadJsonPointFeature, highlightFeaturesByProperty } from '@/utils/vector-layer-utils'
import { initMapbox } from "@/utils/mapbox-utils";
import { loadVectorLayer, loadJsonPointFeature,showUnitReport } from '@/utils/vector-layer-utils'
import { ColorGenerator } from '@/utils/color-generator';
export default {
@ -205,6 +205,13 @@ export default {
});
loadJsonPointFeature("workUnit", "blue", geojson, "unit_name", "workUnitMap_hov");
Vue.config.maps['workUnitMap_hov'].on('click', 'workUnit', async (e) => {
if (!e.features || e.features.length <= 0)
return;
//
showUnitReport('workUnitMap_hov', e.features[0].properties.unit_name, e.features[0].geometry.coordinates);
});
const typeCount = res.array.reduce((acc, unit) => {
const type = unit.unit_type
acc[type] = (acc[type] || 0) + 1 // +11

View File

@ -64,6 +64,7 @@
</template>
<script>
import Vue from 'vue'
/**
* 遥控潜水器汇总统计页请求 `/report/platform/rov` 系列接口
*/
@ -71,8 +72,8 @@ import BarChart from './components/BarChart.vue'
import PieChart from './components/PieChart.vue'
import { reportList, platformRov, rovUnit, rovUnitYear, rovDiving } from '../../api/statistics'
import { filterDictItems } from '../../utils/index'
import { initMapbox, handleWheel, initSprites, triggerLayerClick } from "@/utils/mapbox-utils";
import { loadVectorLayer, loadJsonLineFeature, loadJsonPointFeature, highlightFeaturesByProperty } from '@/utils/vector-layer-utils'
import { initMapbox } from "@/utils/mapbox-utils";
import { loadVectorLayer, loadJsonPointFeature,showUnitReport } from '@/utils/vector-layer-utils'
import { ColorGenerator } from '@/utils/color-generator';
export default {
@ -207,6 +208,13 @@ export default {
});
loadJsonPointFeature("workUnit", "blue", geojson, "unit_name", "workUnitMap_rov");
Vue.config.maps['workUnitMap_rov'].on('click', 'workUnit', async (e) => {
if (!e.features || e.features.length <= 0)
return;
//
showUnitReport('workUnitMap_rov', e.features[0].properties.unit_name, e.features[0].geometry.coordinates);
});
const typeCount = res.array.reduce((acc, unit) => {
const type = unit.unit_type
acc[type] = (acc[type] || 0) + 1 // +11

View File

@ -70,12 +70,13 @@
</template>
<script>
import Vue from 'vue'
import BarChart from './components/BarChart.vue'
import PieChart from './components/PieChart.vue'
import { reportList, shipTotal, shipUnit, shipUnitYear, shipVoyage } from '../../api/statistics'
import { getYearRange, filterDictItems } from '../../utils/index'
import { initMapbox } from '@/utils/mapbox-utils'
import { loadVectorLayer, loadJsonPointFeature } from '@/utils/vector-layer-utils'
import { loadVectorLayer, loadJsonPointFeature, showUnitReport } from '@/utils/vector-layer-utils'
import { ColorGenerator } from '@/utils/color-generator'
export default {
@ -218,6 +219,13 @@ export default {
})
loadJsonPointFeature('workUnit', 'blue', geojson, 'unit_name', 'workUnitMap_ship')
Vue.config.maps['workUnitMap_ship'].on('click', 'workUnit', async (e) => {
if (!e.features || e.features.length <= 0)
return;
//
showUnitReport('workUnitMap_ship', e.features[0].properties.unit_name, e.features[0].geometry.coordinates);
});
const typeCount = res.array.reduce((acc, unit) => {
const type = unit.unit_type
acc[type] = (acc[type] || 0) + 1 // +11

View File

@ -59,9 +59,8 @@ import Vue from 'vue'
import BarChart from './components/BarChart.vue'
import { unitTotal, hovUnit, shipUnit, shipVoyage } from '../../api/statistics'
import { getYearRange } from '../../utils/index'
import { initMapbox, handleWheel } from '@/utils/mapbox-utils'
import { loadVectorLayer, loadJsonPointFeature, showPopupDetails, showUnitReport } from '@/utils/vector-layer-utils'
import { ColorGenerator } from '@/utils/color-generator'
import { initMapbox } from '@/utils/mapbox-utils'
import { loadJsonPointFeature, showUnitReport } from '@/utils/vector-layer-utils'
/**
* @param {*} v 原始值
@ -340,12 +339,24 @@ export default {
if (!e.features || e.features.length <= 0)
return;
//
showUnitReport('workUnitMap_uuv', e.features[0].properties.unit_name, e.features[0].geometry.coordinates);
showUnitReport('workUnitMap_uuv', e.features[0].properties.unit_name, e.features[0].geometry.coordinates,this.resetFilter);
//
this.applyUnitTotalRows((this.unitTotalRawList || []).filter(row => row.unit_name === e.features[0].properties.unit_name));
});
})
},
resetFilter(){
const raw = this.unitTotalRawList || []
const sel = this.selectedUnits || []
if (!sel.length) {
this.applyUnitTotalRows(raw)
return
}
const nameSet = new Set(sel.map(u => u && u.unit_name).filter(Boolean))
const filtered = raw.filter(row => nameSet.has(row.unit_name))
this.applyUnitTotalRows(filtered)
}
}
}
</script>