更改文件名并修改代码

This commit is contained in:
2026-06-22 21:45:09 +08:00
parent f653067d8b
commit 51c7352a90
6 changed files with 661 additions and 183 deletions
@@ -1,164 +0,0 @@
import { computed, ref } from 'vue';
import { CesiumUtilsSingleton } from '@/utils/cesium/CesiumUtils';
import { useStatusStore } from '@/stores/useStatusStore';
import { useLoadingResourceStore } from '@/stores/useLoadingResourceStore';
import { LoadingResource } from '@/types/common/LoadingResourceType';
import type { PointResource, ResourceConfig } from '@/types/common/useAroundAnalysisType';
/**
* 周边分析搜索组件钩子函数
* @returns 搜索相关的状态和方法
*/
export const useAroundAnalysis = () => {
const statusStore = useStatusStore();//用于访问图层显示状态
const loadingResourceStore = useLoadingResourceStore();//用于访问各类点位数据
// 计算属性:获取图层的显示状态
const poi = computed(() => statusStore.poiLayers);
const map = computed(() => statusStore.mapLayers);
const infra = computed(() => statusStore.infrastructureLayers);
/**
* 资源配置列表
*/
const RESOURCE_CONFIGS: ResourceConfig[] = [
{ key: LoadingResource.SCHOOL, category: 'school', isVisible: () => poi.value.showSchool.show },
{ key: LoadingResource.HOSPITAL, category: 'hospital', isVisible: () => poi.value.showHospital.show },
{ key: LoadingResource.DANGEROUS_SOURCE, category: 'danger', isVisible: () => poi.value.showDangerSource.show },
{ key: LoadingResource.EMERGENCY_SHELTER, category: 'shelter', isVisible: () => poi.value.showRefugeeShelter.show },
{ key: LoadingResource.FIRE_STATION, category: 'fire', isVisible: () => poi.value.showFireStation.show },
{ key: LoadingResource.STORE_POINTS, category: 'store', isVisible: () => poi.value.showReservePoint.show },
{ key: LoadingResource.SUBWAY_STATION, category: 'subway', isVisible: () => poi.value.showSubwayStation.show },
{ key: LoadingResource.LANDSLIDE_HIDDEN_POINT, category: 'hidden-danger', forcedType: 'landslide', isVisible: () => poi.value.showLandslideHiddenPoint.show },
{ key: LoadingResource.DEBRIS_FLOW_HIDDEN_POINT, category: 'hidden-danger', forcedType: 'debris_flow', isVisible: () => poi.value.showDebrisFlowHiddenPoint.show },
{ key: LoadingResource.WATER_LOGGING_HIDDEN_POINT, category: 'hidden-danger', forcedType: 'water_logging', isVisible: () => poi.value.showWaterLoggingHiddenPoint.show },
{ key: LoadingResource.FLASH_FLOOD_HIDDEN_POINT, category: 'hidden-danger', forcedType: 'flash_flood', isVisible: () => poi.value.showFlashFloodHiddenPoint.show },
{ key: LoadingResource.COLLAPSE_HIDDEN_POINT, category: 'hidden-danger', forcedType: 'collapse', isVisible: () => poi.value.showCollapseHiddenPoint.show },
{ key: LoadingResource.RISK_POINT, category: 'risk-point', isVisible: () => map.value.riskPointShow.show },
{ key: LoadingResource.BRIDGE, category: 'bridge', isVisible: () => infra.value.showBridge.show },
{ key: LoadingResource.RESERVOIR, category: 'reservoir', isVisible: () => infra.value.showReservoir.show },
];
/**
* 所有资源数据
*/
const allResources = ref<PointResource[]>([]);
/**
* 计算属性:判断是否允许搜索
*/
const canSearch = computed(() => {
return RESOURCE_CONFIGS.some(config => config.isVisible());
});
/**
* 查询建议回调
* @param queryString - 搜索字符串
* @param cb - 回调函数
*/
function querySearch(queryString: string, cb: (results: PointResource[]) => void) {
if (!canSearch.value) {
cb([]);
return;
}
const lowerQuery = queryString.toLowerCase();
const filteredResults = allResources.value.filter(item => {
const config = RESOURCE_CONFIGS.find(c => c.category === item.category);
let isVisible = false;
if (config) {
if (item.category === 'hidden-danger') {
const type = (item.originalType as string)?.toLowerCase();
isVisible = (type === config.forcedType?.toLowerCase()) && config.isVisible();
} else {
isVisible = config.isVisible();
}
}
if (!isVisible) return false;
if (!queryString) return true;
const matchStr = (item.value || '').toLowerCase();
return matchStr.includes(lowerQuery);
});
cb(filteredResults);
}
/**
* 选择建议回调
* @param item - 选中的点资源
*/
function handleSelect(item: PointResource) {
if (item.lon != null && item.lat != null) {
CesiumUtilsSingleton.flyToTarget([item.lon, item.lat, 6000]);
}
}
/**
* 处理聚焦事件,重新加载数据
*/
function handleFocus() {
loadAllPointData();
}
/**
* 数据处理:将 Store 数据转换为资源格式
* @param infoList - 原始数据列表
* @param category - 资源分类
* @param forcedType - 强制类型
* @returns 转换后的点资源数组
*/
function convertStoreDataToResources(
infoList: Record<string, unknown>[],
category: PointResource['category'],
forcedType?: string,
): PointResource[] {
if (!Array.isArray(infoList)) return [];
return infoList.map((item: Record<string, unknown>) => {
const id = item.id || item._id || item.uuid || 'unknown_id';
const safeId = typeof id === 'string' ? id : typeof id === 'number' ? id : 'unknown_id';
const value = (item.name && String(item.name).trim() !== '')
? String(item.name)
: String(safeId);
return {
...item,
id: safeId,
value: value,
category: category,
originalType: (forcedType || (item.type as string) || (item.disasterType as string))?.toLowerCase(),
};
});
}
/**
* 加载所有点类数据
*/
function loadAllPointData() {
const resources: PointResource[] = [];
RESOURCE_CONFIGS.forEach(config => {
const data = loadingResourceStore.getLoadingResource(config.key).info;
resources.push(...convertStoreDataToResources(data, config.category, config.forcedType));
});
const seenIds = new Map<string | number, PointResource>();
const uniqueResources: PointResource[] = [];
for (const item of resources) {
if (!seenIds.has(item.id)) {
seenIds.set(item.id, item);
uniqueResources.push(item);
}
}
allResources.value = uniqueResources;
}
/**
* 搜索框的值
*/
const state = ref('');
return { state, allResources, canSearch, querySearch, handleSelect, handleFocus, loadAllPointData };
};
@@ -86,7 +86,7 @@ const isCategoryVisible = (category: PointResourceCategory, originalType?: strin
};
// ==================== 响应式状态 ====================
export const useAnalysisButton = (): AnalysisButtonState => {
export const useAroundButton = (): AnalysisButtonState => {
const selectedButtonIndex = ref<number>(-1);
const showAreaDialog = ref(false);
const radius = ref(10);
@@ -0,0 +1,357 @@
import { computed, ref, watch } from 'vue';
import { CesiumUtilsSingleton } from '@/utils/cesium/CesiumUtils';
import { useStatusStore } from '@/stores/useStatusStore';
import { useLoadingResourceStore } from '@/stores/useLoadingResourceStore';
import { LoadingResource } from '@/types/common/LoadingResourceType';
import type { PointResource, ResourceConfig } from '@/types/common/useAroundAnalysisType';
import { Cartesian3, Cartographic } from 'cesium';
import { useCircleDrawer } from './useCircleDrawer';
import { usePulseEffect } from './usePulseEffect';
import { useMarkerManager } from './useMarkerManager';
/**
* 周边分析搜索组件钩子函数
* @returns 搜索相关的状态和方法
*/
export const useAroundSearch = () => {
const statusStore = useStatusStore();//用于访问图层显示状态
const loadingResourceStore = useLoadingResourceStore();//用于访问各类点位数据
// 计算属性:获取图层的显示状态
const poi = computed(() => statusStore.poiLayers);
const map = computed(() => statusStore.mapLayers);
const infra = computed(() => statusStore.infrastructureLayers);
// 区域分析相关状态
const showAreaDialog = ref(false);
const areaRadius = ref(10);
const dialogPosition = ref({ x: 0, y: 0 });
const pendingAnalysisPoint = ref<PointResource | null>(null);
const isAreaAnalysisActive = ref(false);
const currentAnalysisCenter = ref<Cartesian3 | null>(null);
const showPulsePointListFromSearch = ref(false);
const pulsePointsFromSearch = ref<PointResource[]>([]);
// 组合子 Hook
const { drawCircle, clearCircle } = useCircleDrawer();
const { addPulseEffectToPoints, removePulseEffect } = usePulseEffect();
const { addMarker, removeMarker } = useMarkerManager();
/**
* 资源配置列表
*/
const RESOURCE_CONFIGS: ResourceConfig[] = [
{ key: LoadingResource.SCHOOL, category: 'school', isVisible: () => poi.value.showSchool.show },
{ key: LoadingResource.HOSPITAL, category: 'hospital', isVisible: () => poi.value.showHospital.show },
{ key: LoadingResource.DANGEROUS_SOURCE, category: 'danger', isVisible: () => poi.value.showDangerSource.show },
{ key: LoadingResource.EMERGENCY_SHELTER, category: 'shelter', isVisible: () => poi.value.showRefugeeShelter.show },
{ key: LoadingResource.FIRE_STATION, category: 'fire', isVisible: () => poi.value.showFireStation.show },
{ key: LoadingResource.STORE_POINTS, category: 'store', isVisible: () => poi.value.showReservePoint.show },
{ key: LoadingResource.SUBWAY_STATION, category: 'subway', isVisible: () => poi.value.showSubwayStation.show },
{ key: LoadingResource.LANDSLIDE_HIDDEN_POINT, category: 'hidden-danger', forcedType: 'landslide', isVisible: () => poi.value.showLandslideHiddenPoint.show },
{ key: LoadingResource.DEBRIS_FLOW_HIDDEN_POINT, category: 'hidden-danger', forcedType: 'debris_flow', isVisible: () => poi.value.showDebrisFlowHiddenPoint.show },
{ key: LoadingResource.WATER_LOGGING_HIDDEN_POINT, category: 'hidden-danger', forcedType: 'water_logging', isVisible: () => poi.value.showWaterLoggingHiddenPoint.show },
{ key: LoadingResource.FLASH_FLOOD_HIDDEN_POINT, category: 'hidden-danger', forcedType: 'flash_flood', isVisible: () => poi.value.showFlashFloodHiddenPoint.show },
{ key: LoadingResource.COLLAPSE_HIDDEN_POINT, category: 'hidden-danger', forcedType: 'collapse', isVisible: () => poi.value.showCollapseHiddenPoint.show },
{ key: LoadingResource.RISK_POINT, category: 'risk-point', isVisible: () => map.value.riskPointShow.show },
{ key: LoadingResource.BRIDGE, category: 'bridge', isVisible: () => infra.value.showBridge.show },
{ key: LoadingResource.RESERVOIR, category: 'reservoir', isVisible: () => infra.value.showReservoir.show },
];
/**
* 所有资源数据
*/
const allResources = ref<PointResource[]>([]);
/**
* 计算属性:判断是否允许搜索
*/
const canSearch = computed(() => {
return RESOURCE_CONFIGS.some(config => config.isVisible());
});
/**
* 查询建议回调
* @param queryString - 搜索字符串
* @param cb - 回调函数
*/
function querySearch(queryString: string, cb: (results: PointResource[]) => void) {
if (!canSearch.value) {
cb([]);
return;
}
const lowerQuery = queryString.toLowerCase();
const filteredResults = allResources.value.filter(item => {
const config = RESOURCE_CONFIGS.find(c => c.category === item.category);
let isVisible = false;
if (config) {
if (item.category === 'hidden-danger') {
const type = (item.originalType as string)?.toLowerCase();
isVisible = (type === config.forcedType?.toLowerCase()) && config.isVisible();
} else {
isVisible = config.isVisible();
}
}
if (!isVisible) return false;
if (!queryString) return true;
const matchStr = (item.value || '').toLowerCase();
return matchStr.includes(lowerQuery);
});
cb(filteredResults);
}
/**
* 选择建议回调
* @param item - 选中的点资源
*/
async function handleSelect(item: PointResource) {
if (item.lon == null || item.lat == null) return;
await CesiumUtilsSingleton.flyToTarget([item.lon, item.lat, 6000]);
startAreaAnalysis(item);
}
/**
* 开始区域分析
* @param centerPoint - 中心点资源
*/
function startAreaAnalysis(centerPoint: PointResource) {
if (centerPoint.lon == null || centerPoint.lat == null) return;
// 清除之前的分析
clearSearchAreaAnalysis();
// 保存待分析的点
pendingAnalysisPoint.value = centerPoint;
// 设置中心点
const centerPosition = Cartesian3.fromDegrees(centerPoint.lon, centerPoint.lat, 0);
currentAnalysisCenter.value = centerPosition;
isAreaAnalysisActive.value = true;
// 添加标记(红点+四个绿色角)
addMarker(centerPosition);
// 显示区域选择对话框
showAreaDialog.value = true;
// 计算对话框位置(屏幕中心右下20px)
calculateDialogPosition();
}
/**
* 计算对话框位置(在屏幕中心点右下20px,确保在界面内)
*/
function calculateDialogPosition() {
const W = 280, H = 150, P = 10, O = 20;
const cx = window.innerWidth / 2, cy = window.innerHeight / 2;
let x = cx + O, y = cy + O;
x = x + W > window.innerWidth - P ? cx - W - O : x;
y = y + H > window.innerHeight - P ? cy - H - O : y;
dialogPosition.value = {
x: Math.max(P, Math.min(x, window.innerWidth - W - P)),
y: Math.max(P, Math.min(y, window.innerHeight - H - P)),
};
}
/**
* 确认区域分析
*/
function handleAreaConfirm() {
if (!pendingAnalysisPoint.value) return;
const { lon, lat } = pendingAnalysisPoint.value;
if (lon == null || lat == null) return;
// 关闭对话框
showAreaDialog.value = false;
// 绘制圆形
if (currentAnalysisCenter.value) {
drawCircle(currentAnalysisCenter.value, areaRadius.value);
}
// 计算并添加脉冲效果
refreshSearchPulseEffect();
// 飞行到合适的高度以显示整个圆形区域
const flyHeight = Math.max(areaRadius.value * 6000, 10000);
CesiumUtilsSingleton.flyToTarget([lon, lat, flyHeight], 2);
}
/**
* 取消区域分析
*/
function handleAreaCancel() {
showAreaDialog.value = false;
clearSearchAreaAnalysis();
}
/**
* 清除搜索触发的区域分析
*/
function clearSearchAreaAnalysis() {
removeMarker();
clearCircle();
removePulseEffect();
currentAnalysisCenter.value = null;
isAreaAnalysisActive.value = false;
showPulsePointListFromSearch.value = false;
pulsePointsFromSearch.value = [];
pendingAnalysisPoint.value = null;
}
/**
* 刷新搜索触发的脉冲效果
*/
function refreshSearchPulseEffect() {
if (!currentAnalysisCenter.value) {
console.warn('refreshSearchPulseEffect: currentAnalysisCenter.value 为空');
return;
}
removePulseEffect();
const pointsInCircle = getPointsInCircle(currentAnalysisCenter.value, areaRadius.value);
addPulseEffectToPoints(pointsInCircle);
pulsePointsFromSearch.value = pointsInCircle;
showPulsePointListFromSearch.value = true;
}
/**
* 获取圆形范围内的点
* @param centerPosition - 中心位置
* @param radiusKm - 半径(公里)
* @returns 圆形范围内的点资源数组
*/
function getPointsInCircle(centerPosition: Cartesian3, radiusKm: number): PointResource[] {
const cartographic = Cartographic.fromCartesian(centerPosition);
const centerLon = cartographic.longitude * (180 / Math.PI);
const centerLat = cartographic.latitude * (180 / Math.PI);
const radiusMeters = radiusKm * 1000;
return allResources.value.filter(point => {
if (point.lon === undefined || point.lat === undefined) return false;
const distance = calculateDistance(centerLon, centerLat, point.lon, point.lat);
return distance <= radiusMeters && isCategoryVisible(point.category, point.originalType);
});
}
/**
* 计算两点间距离(米)
*/
function calculateDistance(
centerLon: number,
centerLat: number,
pointLon: unknown,
pointLat: unknown
): number {
const EARTH_RADIUS = 6371000;
const pLon = Number(pointLon);
const pLat = Number(pointLat);
if (isNaN(pLon) || isNaN(pLat)) return Infinity;
const dLat = (pLat - centerLat) * Math.PI / 180;
const dLon = (pLon - centerLon) * Math.PI / 180;
const a = Math.sin(dLat / 2) ** 2 +
Math.cos(centerLat * Math.PI / 180) * Math.cos(pLat * Math.PI / 180) *
Math.sin(dLon / 2) ** 2;
return EARTH_RADIUS * 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
}
/**
* 判断分类是否可见(复用 RESOURCE_CONFIGS 配置)
*/
function isCategoryVisible(category?: string, originalType?: string): boolean {
if (!category) return false;
const config = RESOURCE_CONFIGS.find(c => c.category === category);
if (!config) return false;
// 隐患点需要额外判断子类型
if (category === 'hidden-danger') {
return config.forcedType === originalType?.toLowerCase() && config.isVisible();
}
return config.isVisible();
}
// 监听图层可见性变化,自动刷新脉冲效果(复用 RESOURCE_CONFIGS
const layerVisibilityWatchers = RESOURCE_CONFIGS.map(config => config.isVisible);
// 监听图层可见性变化
watch(layerVisibilityWatchers, () => {
if (currentAnalysisCenter.value && showPulsePointListFromSearch.value) {
loadAllPointData(); // ① 重新加载所有数据
refreshSearchPulseEffect(); // ② 刷新脉冲
}
});
// 监听资源数据变化
watch(
() => loadingResourceStore.loadingResource,
() => {
if (currentAnalysisCenter.value && showPulsePointListFromSearch.value) {
loadAllPointData();
refreshSearchPulseEffect();
}
},
{ deep: true }
);
/**
* 处理聚焦事件,重新加载数据
*/
function handleFocus() {
loadAllPointData();
}
/**
* 数据处理:将 Store 数据转换为资源格式
* @param infoList - 原始数据列表
* @param category - 资源分类
* @param forcedType - 强制类型
* @returns 转换后的点资源数组
*/
function convertStoreDataToResources(
infoList: Record<string, unknown>[],
category: PointResource['category'],
forcedType?: string,
): PointResource[] {
if (!Array.isArray(infoList)) return [];
return infoList.map(item => {
const id = item.id || item._id || item.uuid || 'unknown_id';
const safeId = typeof id === 'string' || typeof id === 'number' ? id : 'unknown_id';
const name = item.name && String(item.name).trim() !== '' ? String(item.name) : String(safeId);
return {
...item,
id: safeId,
value: name,
category,
originalType: (forcedType || item.type || item.disasterType)?.toString().toLowerCase(),
};
});
}
/**
* 加载所有点类数据
*/
function loadAllPointData() {
const resources = RESOURCE_CONFIGS.flatMap(config =>
convertStoreDataToResources(loadingResourceStore.getLoadingResource(config.key).info, config.category, config.forcedType)
);
const uniqueMap = new Map<string | number, PointResource>();
resources.forEach(item => uniqueMap.set(item.id, item));
allResources.value = Array.from(uniqueMap.values());
}
/**
* 搜索框的值
*/
const state = ref('');
return {
state,
allResources,
canSearch,
querySearch,
handleSelect,
handleFocus,
loadAllPointData,
// 区域分析相关
showAreaDialog,
areaRadius,
dialogPosition,
isAreaAnalysisActive,
showPulsePointListFromSearch,
pulsePointsFromSearch,
handleAreaConfirm,
handleAreaCancel,
refreshSearchPulseEffect,
clearSearchAreaAnalysis,
};
};