Move model views out of the dashboard utility bulk

Probability and multi-model view adapters were still embedded in dashboard-utils even though table, panel, modal, and city-card views use them as small pure selectors. This moves them into model-utils and keeps dashboard-utils re-export compatibility.

Constraint: Preserve model/probability return shapes and existing fallback behavior.

Rejected: Merge model-utils with chart-utils | model selectors and chart data preparation change at different rates.

Confidence: high

Scope-risk: narrow

Reversibility: clean

Tested: TypeScript diagnostics for model-utils, dashboard-utils, and PanelSections

Tested: npm run build

Not-tested: Bundle analyzer size comparison.
This commit is contained in:
2569718930@qq.com
2026-04-28 12:07:43 +08:00
parent ecbba9160d
commit 5dc39b1573
6 changed files with 56 additions and 59 deletions
@@ -26,10 +26,9 @@ import {
import { getTemperatureChartData } from "@/lib/chart-utils";
import {
getFutureModalView,
getModelView,
getProbabilityView,
getWeatherSummary,
} from "@/lib/dashboard-utils";
import { getModelView, getProbabilityView } from "@/lib/model-utils";
import { getTodayPaceView } from "@/lib/pace-utils";
import { dashboardClient } from "@/lib/dashboard-client";
import {
@@ -8,10 +8,7 @@ import type {
ScanOpportunityRow,
} from "@/lib/dashboard-types";
import { getLocalizedCityName } from "@/lib/dashboard-home-copy";
import {
getModelView,
getProbabilityView,
} from "@/lib/dashboard-utils";
import { getModelView, getProbabilityView } from "@/lib/model-utils";
import { getTodayPaceView } from "@/lib/pace-utils";
import {
formatTemperatureValue,
@@ -15,11 +15,10 @@ import {
import { getTemperatureChartData } from "@/lib/chart-utils";
import {
getHeroMetaItems,
getModelView,
getProbabilityView,
getRiskBadgeLabel,
getWeatherSummary,
} from "@/lib/dashboard-utils";
import { getModelView, getProbabilityView } from "@/lib/model-utils";
import {
normalizeObservationSourceCode,
normalizeObservationSourceLabel,
@@ -21,7 +21,7 @@ import {
useCityMarketScan,
} from "@/components/dashboard/scan-terminal/use-ai-city-card-data";
import type { CityDetail, ScanOpportunityRow } from "@/lib/dashboard-types";
import { getModelView } from "@/lib/dashboard-utils";
import { getModelView } from "@/lib/model-utils";
import { getTodayPaceView } from "@/lib/pace-utils";
import { formatTemperatureValue } from "@/lib/temperature-utils";
+1 -50
View File
@@ -22,6 +22,7 @@ import { formatTafMarkerType } from "@/lib/taf-utils";
import { normalizeHm } from "@/lib/time-utils";
export { getTemperatureChartData } from "@/lib/chart-utils";
export { getModelView, getProbabilityView } from "@/lib/model-utils";
export { getTodayPaceView } from "@/lib/pace-utils";
export {
formatTemperatureValue,
@@ -246,56 +247,6 @@ export function getHeroMetaItems(detail: CityDetail, locale: Locale = "zh-CN") {
return parts;
}
export function getProbabilityView(detail: CityDetail, targetDate?: string | null) {
const date = targetDate || detail.local_date;
if (date === detail.local_date) {
return {
calibrationMode: detail.probabilities?.calibration_mode ?? null,
calibrationVersion: detail.probabilities?.calibration_version ?? null,
engine: detail.probabilities?.engine ?? null,
mu: detail.probabilities?.mu ?? null,
probabilities: detail.probabilities?.distribution || [],
probabilitiesAll:
detail.probabilities?.distribution_all ||
detail.probabilities?.distribution ||
[],
shadowProbabilities: detail.probabilities?.shadow_distribution || [],
shadowProbabilitiesAll:
detail.probabilities?.shadow_distribution_all ||
detail.probabilities?.shadow_distribution ||
[],
};
}
const daily = detail.multi_model_daily?.[date];
return {
calibrationMode: null,
calibrationVersion: null,
engine: null,
mu: daily?.deb?.prediction ?? null,
probabilities: daily?.probabilities || [],
probabilitiesAll: daily?.probabilities_all || daily?.probabilities || [],
shadowProbabilities: [],
shadowProbabilitiesAll: [],
};
}
export function getModelView(detail: CityDetail, targetDate?: string | null) {
const date = targetDate || detail.local_date;
const daily = detail.multi_model_daily?.[date];
if (daily) {
return {
deb: daily.deb?.prediction ?? null,
models: daily.models || {},
};
}
return {
deb: detail.deb?.prediction ?? null,
models: detail.multi_model || {},
};
}
export function parseAiAnalysis(analysis: CityDetail["ai_analysis"]) {
const fallback = {
bullets: [] as string[],
+51
View File
@@ -0,0 +1,51 @@
import type { CityDetail } from "@/lib/dashboard-types";
export function getProbabilityView(detail: CityDetail, targetDate?: string | null) {
const date = targetDate || detail.local_date;
if (date === detail.local_date) {
return {
calibrationMode: detail.probabilities?.calibration_mode ?? null,
calibrationVersion: detail.probabilities?.calibration_version ?? null,
engine: detail.probabilities?.engine ?? null,
mu: detail.probabilities?.mu ?? null,
probabilities: detail.probabilities?.distribution || [],
probabilitiesAll:
detail.probabilities?.distribution_all ||
detail.probabilities?.distribution ||
[],
shadowProbabilities: detail.probabilities?.shadow_distribution || [],
shadowProbabilitiesAll:
detail.probabilities?.shadow_distribution_all ||
detail.probabilities?.shadow_distribution ||
[],
};
}
const daily = detail.multi_model_daily?.[date];
return {
calibrationMode: null,
calibrationVersion: null,
engine: null,
mu: daily?.deb?.prediction ?? null,
probabilities: daily?.probabilities || [],
probabilitiesAll: daily?.probabilities_all || daily?.probabilities || [],
shadowProbabilities: [],
shadowProbabilitiesAll: [],
};
}
export function getModelView(detail: CityDetail, targetDate?: string | null) {
const date = targetDate || detail.local_date;
const daily = detail.multi_model_daily?.[date];
if (daily) {
return {
deb: daily.deb?.prediction ?? null,
models: daily.models || {},
};
}
return {
deb: detail.deb?.prediction ?? null,
models: detail.multi_model || {},
};
}