feat: implement MapCanvas and ScanTerminalDashboard components for interactive weather data visualization

This commit is contained in:
2569718930@qq.com
2026-04-24 13:28:38 +08:00
parent 168bf6987c
commit b94289a51a
2 changed files with 16 additions and 4 deletions
@@ -7,8 +7,10 @@ import { useLeafletMap } from "@/hooks/useLeafletMap";
export function MapCanvas({ export function MapCanvas({
onCitySelect, onCitySelect,
selectionMode = "focus",
}: { }: {
onCitySelect?: (cityName: string) => void; onCitySelect?: (cityName: string) => void;
selectionMode?: "focus" | "select";
} = {}) { } = {}) {
const store = useDashboardStore(); const store = useDashboardStore();
const { containerRef } = useLeafletMap({ const { containerRef } = useLeafletMap({
@@ -21,6 +23,10 @@ export function MapCanvas({
onRegisterStopMotion: store.registerMapStopMotion, onRegisterStopMotion: store.registerMapStopMotion,
onSelectCity: (cityName) => { onSelectCity: (cityName) => {
onCitySelect?.(cityName); onCitySelect?.(cityName);
if (selectionMode === "select") {
void store.selectCity(cityName);
return;
}
void store.focusCity(cityName); void store.focusCity(cityName);
}, },
selectedCity: store.selectedCity, selectedCity: store.selectedCity,
@@ -24,6 +24,7 @@ import {
FilterState, FilterState,
ScanFilterPanel, ScanFilterPanel,
} from "@/components/dashboard/ScanFilterPanel"; } from "@/components/dashboard/ScanFilterPanel";
import { DetailPanel as CityDetailPanel } from "@/components/dashboard/DetailPanel";
import { FutureForecastModal } from "@/components/dashboard/FutureForecastModal"; import { FutureForecastModal } from "@/components/dashboard/FutureForecastModal";
import { MapCanvas } from "@/components/dashboard/MapCanvas"; import { MapCanvas } from "@/components/dashboard/MapCanvas";
import { getWindowPhaseMeta } from "@/components/dashboard/OpportunityTable"; import { getWindowPhaseMeta } from "@/components/dashboard/OpportunityTable";
@@ -464,7 +465,7 @@ function buildAssistantContext(params: {
}; };
} }
function DetailPanel({ function ScanOpportunityDetailPanel({
row, row,
marketScan, marketScan,
loading, loading,
@@ -1437,14 +1438,18 @@ function ScanTerminalScreen() {
const handleSelectRow = useCallback((row: ScanOpportunityRow) => { const handleSelectRow = useCallback((row: ScanOpportunityRow) => {
setSelectedRowId(row.id); setSelectedRowId(row.id);
}, []); void store.selectCity(row.city);
}, [store]);
const renderMainView = () => { const renderMainView = () => {
if (resolvedView === "map") { if (resolvedView === "map") {
return ( return (
<div className="scan-map-view"> <div className="scan-map-view">
<div className="scan-map-shell"> <div className="scan-map-shell">
<MapCanvas onCitySelect={handleMapCitySelect} /> <MapCanvas
onCitySelect={handleMapCitySelect}
selectionMode="select"
/>
</div> </div>
<div className="scan-map-caption"> <div className="scan-map-caption">
{locale === "en-US" {locale === "en-US"
@@ -1613,11 +1618,12 @@ function ScanTerminalScreen() {
</section> </section>
</main> </main>
<DetailPanel <ScanOpportunityDetailPanel
row={activeDetailRow} row={activeDetailRow}
marketScan={selectedDetail} marketScan={selectedDetail}
loading={detailLoadingId === activeDetailRow?.id} loading={detailLoadingId === activeDetailRow?.id}
/> />
<CityDetailPanel />
<AssistantWidget <AssistantWidget
terminalData={terminalData} terminalData={terminalData}
rows={timeSortedRows} rows={timeSortedRows}