feat: implement PolyWeather dashboard layout with integrated map, weather visualization, and detail panels

This commit is contained in:
2569718930@qq.com
2026-04-23 22:35:34 +08:00
parent e2eb5eb429
commit 79a4333aaf
10 changed files with 1029 additions and 773 deletions
+51 -38
View File
@@ -23,33 +23,32 @@ export type WeatherAuraEffect =
const RISK_AURA: Record<
string,
Omit<WeatherAuraProfile, "intensity" | "drift" | "effect" | "effectIntensity">
> =
{
high: {
primary: "#ff7c2a",
secondary: "#ffcf66",
tertiary: "#56c7ff",
particleOpacity: 0.42,
},
medium: {
primary: "#f6c453",
secondary: "#5eead4",
tertiary: "#7dd3fc",
particleOpacity: 0.34,
},
low: {
primary: "#38bdf8",
secondary: "#22d3ee",
tertiary: "#34d399",
particleOpacity: 0.28,
},
default: {
primary: "#6ee7ff",
secondary: "#7c8dff",
tertiary: "#60a5fa",
particleOpacity: 0.3,
},
};
> = {
high: {
primary: "#ff7c2a",
secondary: "#ffcf66",
tertiary: "#56c7ff",
particleOpacity: 0.42,
},
medium: {
primary: "#f6c453",
secondary: "#5eead4",
tertiary: "#7dd3fc",
particleOpacity: 0.34,
},
low: {
primary: "#38bdf8",
secondary: "#00E0A4",
tertiary: "#34d399",
particleOpacity: 0.28,
},
default: {
primary: "#6ee7ff",
secondary: "#7c8dff",
tertiary: "#60a5fa",
particleOpacity: 0.3,
},
};
function clamp(value: number, min: number, max: number) {
return Math.min(Math.max(value, min), max);
@@ -61,7 +60,9 @@ export function getWeatherAuraProfile(
): WeatherAuraProfile {
const dominantRisk =
String(detail?.risk?.level || "").toLowerCase() ||
String(cities.find((city) => city.risk_level)?.risk_level || "").toLowerCase() ||
String(
cities.find((city) => city.risk_level)?.risk_level || "",
).toLowerCase() ||
"default";
const base = RISK_AURA[dominantRisk] || RISK_AURA.default;
@@ -69,7 +70,11 @@ export function getWeatherAuraProfile(
const validTemp = Number.isFinite(currentTemp) ? currentTemp : 18;
const intensity = clamp(0.7 + validTemp / 40, 0.72, 1.45);
const drift = clamp(0.45 + validTemp / 30, 0.55, 1.35);
const particleOpacity = clamp(base.particleOpacity + (intensity - 1) * 0.08, 0.22, 0.48);
const particleOpacity = clamp(
base.particleOpacity + (intensity - 1) * 0.08,
0.22,
0.48,
);
const wxText = `${String(detail?.current?.wx_desc || "")} ${String(
detail?.current?.cloud_desc || "",
)}`.toUpperCase();
@@ -77,9 +82,7 @@ export function getWeatherAuraProfile(
const humidity = Number(detail?.current?.humidity);
let effect: WeatherAuraEffect = "clear";
if (
/(TS|VCTS|THUNDER|雷暴|LIGHTNING)/.test(wxText)
) {
if (/(TS|VCTS|THUNDER|雷暴|LIGHTNING)/.test(wxText)) {
effect = "storm";
} else if (/(SN|SG|GS|ICE|SLEET|雪|霰)/.test(wxText)) {
effect = "snow";
@@ -87,9 +90,7 @@ export function getWeatherAuraProfile(
effect = "rain";
} else if (/(FG|BR|HZ|FU|MIST|FOG|雾|霾)/.test(wxText)) {
effect = "fog";
} else if (
/(BKN|OVC|SCT|FEW|CLOUD|云|阴)/.test(wxText)
) {
} else if (/(BKN|OVC|SCT|FEW|CLOUD|云|阴)/.test(wxText)) {
effect = "cloud";
} else if (Number.isFinite(windSpeed) && windSpeed >= 18) {
effect = "wind";
@@ -113,11 +114,23 @@ export function getWeatherAuraProfile(
1.7,
)
: effect === "snow"
? clamp(0.8 + (Number.isFinite(windSpeed) ? windSpeed / 50 : 0), 0.78, 1.4)
? clamp(
0.8 + (Number.isFinite(windSpeed) ? windSpeed / 50 : 0),
0.78,
1.4,
)
: effect === "fog"
? clamp(0.78 + (Number.isFinite(humidity) ? humidity / 240 : 0), 0.75, 1.3)
? clamp(
0.78 + (Number.isFinite(humidity) ? humidity / 240 : 0),
0.75,
1.3,
)
: effect === "wind"
? clamp(0.72 + (Number.isFinite(windSpeed) ? windSpeed / 40 : 0), 0.72, 1.5)
? clamp(
0.72 + (Number.isFinite(windSpeed) ? windSpeed / 40 : 0),
0.72,
1.5,
)
: effect === "cloud"
? 0.82
: 0.72;