+ `,
+ iconSize: [16, 16],
+ iconAnchor: [8, 8]
})
}), []);
+ useEffect(() => {
+ if (!isPublicView && navigator.geolocation) {
+ const watchId = navigator.geolocation.watchPosition(
+ (pos) => setUserLocation([pos.coords.latitude, pos.coords.longitude]),
+ (err) => console.warn("Lỗi định vị người dùng:", err),
+ { enableHighAccuracy: true }
+ );
+ return () => navigator.geolocation.clearWatch(watchId);
+ }
+ }, [isPublicView]);
+
const [activeTab, setActiveTab] = useState<'plan' | 'expense' | 'photo' | 'settings'>('plan');
const [viewMode, setViewMode] = useState<'map' | 'timeline'>('timeline');
const [isAddLocationOpen, setIsAddLocationOpen] = useState(false);
@@ -451,8 +475,8 @@ export const TourDetailPage = ({
setIsRoutingLoading(true);
try {
- // Thêm alternatives=3 để yêu cầu tối đa 3 phương án lộ trình khác từ OSRM
- const response = await fetch(`https://router.project-osrm.org/route/v1/${travelMode}/${coordsString}?overview=full&geometries=geojson&alternatives=3`);
+ // Thêm alternatives=true để yêu cầu các phương án lộ trình khác từ OSRM (Lưu ý: OSRM thường chỉ trả về lộ trình thay thế cho 2 điểm tọa độ)
+ const response = await fetch(`https://router.project-osrm.org/route/v1/${travelMode}/${coordsString}?overview=full&geometries=geojson&alternatives=true`);
const data = await response.json();
if (data.code === 'Ok' && data.routes.length > 0) {
setRoutes(data.routes);
@@ -736,6 +760,19 @@ export const TourDetailPage = ({
}
};
+ // Ngăn chặn sự kiện click trên menu lộ trình làm ảnh hưởng bản đồ
+ const routeMenuRef = React.useRef(null);
+ useEffect(() => {
+ if (routeMenu && routeMenuRef.current) {
+ L.DomEvent.disableClickPropagation(routeMenuRef.current);
+ }
+ }, [routeMenu]);
+
+ // Đóng menu lộ trình khi chuyển tab hoặc thay đổi chế độ xem
+ useEffect(() => {
+ setRouteMenu(null);
+ }, [activeTab, viewMode]);
+
// Xác định Điểm xuất phát và Điểm kết thúc hiển thị dưới widget tài chính
const startPoint = useMemo(() =>
legs.flatMap(l => l.locations).find(loc => loc.plannedStart && new Date(loc.plannedStart).getTime() === 0),
@@ -1155,11 +1192,25 @@ export const TourDetailPage = ({
preferCanvas={true}
>
- {canEdit && !isPublicView && setRouteMenu(null)} />} {/* Hide map context menu in public view */}
+ {canEdit && !isPublicView && (
+ setRouteMenu(null)}
+ />
+ )}
{/* Tự động đóng khung Start và End khi dữ liệu thay đổi */}
+ {/* Hiển thị vị trí hiện tại của người dùng */}
+ {userLocation && (
+
+
+
Bạn đang ở đây
+
+
+ )}
+
{/* Vẽ tất cả lộ trình: Vẽ các đường phụ trước, đường chính sau để hiển thị đè lên trên */}
{routes.length > 0 ? (
[...routes]
@@ -1171,23 +1222,40 @@ export const TourDetailPage = ({
})
.map(({ data, index }) => (
[c[1], c[0]])}
color={index === selectedRouteIndex ? "#2563eb" : "#94a3b8"}
- weight={index === selectedRouteIndex ? 6 : 4}
- opacity={index === selectedRouteIndex ? 0.9 : 0.4}
- dashArray={index === selectedRouteIndex ? undefined : "10, 10"}
+ weight={index === selectedRouteIndex ? 6 : 14}
+ opacity={index === selectedRouteIndex ? 1 : 0.4}
+ dashArray={index === selectedRouteIndex ? undefined : "15, 15"}
smoothFactor={1}
eventHandlers={{
click: (e) => {
- L.DomEvent.stopPropagation(e as any);
+ const originalEvent = (e as any).originalEvent;
+ if (originalEvent) L.DomEvent.stopPropagation(originalEvent);
setRouteMenu(null);
setSelectedRouteIndex(index);
},
contextmenu: (e) => {
- L.DomEvent.stopPropagation(e as any);
- L.DomEvent.preventDefault(e as any);
+ const originalEvent = (e as any).originalEvent;
+ if (originalEvent) {
+ L.DomEvent.stopPropagation(originalEvent);
+ L.DomEvent.preventDefault(originalEvent);
+ // Đánh dấu để MapContextMenu biết đã có Layer xử lý
+ (originalEvent as any)._routeTriggered = true;
+ }
+
setRouteMenu({ x: (e as any).containerPoint.x, y: (e as any).containerPoint.y, index });
+ },
+ mouseover: (e) => {
+ if (index !== selectedRouteIndex) {
+ (e.target as L.Polyline).setStyle({ opacity: 0.8, weight: 16, color: '#64748b' });
+ }
+ },
+ mouseout: (e) => {
+ if (index !== selectedRouteIndex) {
+ (e.target as L.Polyline).setStyle({ opacity: 0.4, weight: 14, color: '#94a3b8' });
+ }
}
}}
/>
@@ -1272,6 +1340,7 @@ export const TourDetailPage = ({
{/* Menu ngữ cảnh khi click chuột phải vào con đường */}
{routeMenu && (
e.stopPropagation()}
@@ -1328,9 +1397,14 @@ export const TourDetailPage = ({
)}
{/* Danh sách lựa chọn lộ trình đề xuất */}
- {routes.length > 1 && (
-