fix: Fix problems and bugs

This commit is contained in:
2025-07-28 08:18:21 +03:00
parent 470a58a3fa
commit 4f038551a2
7 changed files with 381 additions and 148 deletions

View File

@@ -75,6 +75,19 @@ import { makeAutoObservable } from "mobx";
import { stationsStore, routeStore, sightsStore } from "@shared";
// Функция для сброса кешей карты
export const clearMapCaches = () => {
// Сброс кешей маршрутов
mapStore.routes = [];
mapStore.stations = [];
mapStore.sights = [];
// Сброс кешей MapService если он доступен
if (typeof window !== "undefined" && (window as any).mapServiceInstance) {
(window as any).mapServiceInstance.clearCaches();
}
};
interface ApiRoute {
id: number;
route_number: string;
@@ -328,6 +341,11 @@ class MapStore {
const mapStore = new MapStore();
// Делаем mapStore доступным глобально для сброса кешей
if (typeof window !== "undefined") {
(window as any).mapStore = mapStore;
}
// --- CONFIGURATION ---
export const mapConfig = {
center: [30.311, 59.94] as [number, number],
@@ -453,7 +471,7 @@ class MapService {
public routeLayer: VectorLayer<VectorSource<Feature<LineString>>>; // Public for deselect
private clusterSource: Cluster;
private clusterStyleCache: { [key: number]: Style };
private unclusteredRouteIds: Set<string | number> = new Set();
private tooltipElement: HTMLElement;
private tooltipOverlay: Overlay | null;
private mode: string | null;
@@ -488,8 +506,7 @@ class MapService {
private sightIconStyle: Style;
private selectedSightIconStyle: Style;
private drawSightIconStyle: Style;
private routeIconStyle: Style;
private selectedRouteIconStyle: Style;
private universalHoverStylePoint: Style;
private hoverSightIconStyle: Style;
private universalHoverStyleLine: Style;
@@ -574,21 +591,6 @@ class MapService {
}),
});
this.routeIconStyle = new Style({
image: new CircleStyle({
radius: 8,
fill: new Fill({ color: "rgba(34, 197, 94, 0.8)" }), // Green
stroke: new Stroke({ color: "#ffffff", width: 1.5 }),
}),
});
this.selectedRouteIconStyle = new Style({
image: new CircleStyle({
radius: 10,
fill: new Fill({ color: "rgba(221, 107, 32, 0.9)" }), // Orange on select
stroke: new Stroke({ color: "#ffffff", width: 2 }),
}),
});
this.sightIconStyle = new Style({
image: new RegularShape({
fill: new Fill({ color: "rgba(139, 92, 246, 0.8)" }),
@@ -659,10 +661,7 @@ class MapService {
if (!feature) return this.defaultStyle;
const fId = feature.getId();
if (fId === undefined || !this.unclusteredRouteIds.has(fId)) {
return null;
}
// Все маршруты всегда отображаются, так как они не кластеризуются
const isSelected =
this.selectInteraction?.getFeatures().getArray().includes(feature) ||
(fId !== undefined && this.selectedIds.has(fId));
@@ -705,8 +704,6 @@ class MapService {
const originalFeature = featuresInCluster[0];
const fId = originalFeature.getId();
const featureType = originalFeature.get("featureType");
const isProxy = originalFeature.get("isProxy");
if (isProxy) return new Style(); // Invisible empty style
const isSelected = fId !== undefined && this.selectedIds.has(fId);
const isHovered = this.hoveredFeatureId === fId;
@@ -719,45 +716,20 @@ class MapService {
if (isSelected) {
if (featureType === "sight") return this.selectedSightIconStyle;
if (featureType === "route") return this.selectedRouteIconStyle;
return this.selectedBusIconStyle;
}
if (featureType === "sight") return this.sightIconStyle;
if (featureType === "route") return this.routeIconStyle;
return this.busIconStyle;
}
},
});
this.clusterSource.on("change", () => {
const newUnclusteredRouteIds = new Set<string | number>();
this.clusterSource
.getFeatures()
.forEach((clusterFeature: Feature<any>) => {
const originalFeatures = clusterFeature.get(
"features"
) as Feature<Point>[];
if (originalFeatures && originalFeatures.length === 1) {
const originalFeature = originalFeatures[0];
if (originalFeature.get("featureType") === "route") {
const featureId = originalFeature.getId();
if (featureId !== undefined) {
newUnclusteredRouteIds.add(featureId);
}
}
}
});
if (
newUnclusteredRouteIds.size !== this.unclusteredRouteIds.size ||
![...newUnclusteredRouteIds].every((id) =>
this.unclusteredRouteIds.has(id)
)
) {
this.unclusteredRouteIds = newUnclusteredRouteIds;
this.routeLayer.changed();
}
// Поскольку маршруты больше не добавляются как точки,
// нам не нужно отслеживать unclusteredRouteIds
// Все маршруты всегда отображаются как линии
this.routeLayer.changed();
});
this.boundHandlePointerMove = this.handlePointerMove.bind(this);
@@ -1209,23 +1181,7 @@ class MapService {
lineFeature.set("featureType", "route");
lineFeatures.push(lineFeature);
if (route.center_longitude != null && route.center_latitude != null) {
const centerPoint = new Point(
transform(
[route.center_longitude, route.center_latitude],
"EPSG:4326",
projection
)
);
const proxyPointFeature = new Feature({
geometry: centerPoint,
name: route.route_number,
isProxy: true,
});
proxyPointFeature.setId(routeId);
proxyPointFeature.set("featureType", "route");
pointFeatures.push(proxyPointFeature);
}
// Не создаем прокси-точки для маршрутов - они должны оставаться только линиями
});
this.pointSource.addFeatures(pointFeatures);
@@ -1926,6 +1882,32 @@ class MapService {
return this.map;
}
// Метод для сброса кешей карты
public clearCaches() {
this.clusterStyleCache = {};
this.history = [];
this.historyIndex = -1;
this.beforeActionState = null;
this.hoveredFeatureId = null;
this.selectedIds.clear();
// Очищаем источники данных
if (this.pointSource) {
this.pointSource.clear();
}
if (this.lineSource) {
this.lineSource.clear();
}
// Обновляем слои
if (this.clusterLayer) {
this.clusterLayer.changed();
}
if (this.routeLayer) {
this.routeLayer.changed();
}
}
public saveCurrentPosition(): void {
if (!this.map) return;
const center = this.map.getView().getCenter();
@@ -1941,20 +1923,6 @@ class MapService {
const featureId = feature.getId();
if (!featureType || featureId === undefined || !this.map) return;
if (
featureType === "route" &&
feature.getGeometry()?.getType() === "LineString"
) {
const proxyPoint = this.pointSource.getFeatureById(
featureId
) as Feature<Point>;
if (proxyPoint) {
const lineGeom = feature.getGeometry() as LineString;
const newCenter = getCenter(lineGeom.getExtent());
proxyPoint.getGeometry()?.setCoordinates(newCenter);
}
}
if (typeof featureId === "number" || !String(featureId).includes("-")) {
console.warn(
"Skipping save for feature with non-standard ID:",
@@ -2023,22 +1991,7 @@ class MapService {
);
feature.setGeometry(lineGeom);
// Create and add proxy point
const centerPointGeom = new Point(
transform(
[routeData.center_longitude, routeData.center_latitude],
"EPSG:4326",
projection
)
);
const proxyPointFeature = new Feature({
geometry: centerPointGeom,
name: displayName,
isProxy: true,
});
proxyPointFeature.setId(newFeatureId);
proxyPointFeature.set("featureType", "route");
this.pointSource.addFeature(proxyPointFeature);
// Не создаем прокси-точку для маршрута - только линия
} else {
// For points: update existing
feature.setId(newFeatureId);
@@ -2603,6 +2556,12 @@ export const MapPage: React.FC = () => {
setSelectedIds
);
setMapServiceInstance(service);
// Делаем mapServiceInstance доступным глобально для сброса кешей
if (typeof window !== "undefined") {
(window as any).mapServiceInstance = service;
}
loadInitialData(service);
} catch (e: any) {
setError(
@@ -2615,6 +2574,11 @@ export const MapPage: React.FC = () => {
return () => {
service?.destroy();
setMapServiceInstance(null);
// Удаляем глобальную ссылку
if (typeof window !== "undefined") {
delete (window as any).mapServiceInstance;
}
};
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);