Frame homepage map and auto-focus top opportunity

This commit is contained in:
2569718930@qq.com
2026-04-22 23:47:28 +08:00
parent fe804bc915
commit 1275906a19
2 changed files with 75 additions and 5 deletions
@@ -67,12 +67,19 @@
/* ── Map ── */
.root :global(.map) {
width: 100%;
height: 100%;
position: absolute;
top: 0;
left: 0;
top: calc(var(--header-height) + 16px);
right: 418px;
bottom: 128px;
left: calc(var(--sidebar-width) + 30px);
z-index: 1;
overflow: hidden;
border: 1px solid rgba(34, 211, 238, 0.12);
border-radius: 22px;
background: #020617;
box-shadow:
0 28px 80px rgba(0, 0, 0, 0.38),
inset 0 1px 0 rgba(255, 255, 255, 0.05);
}
.weatherAura {
@@ -890,6 +897,31 @@
background: rgba(148, 163, 184, 0.1);
}
.root :global(.home-top-opportunity-label) {
display: inline-flex;
align-self: flex-start;
align-items: center;
gap: 7px;
padding: 5px 8px;
border: 1px solid rgba(34, 211, 238, 0.18);
border-radius: 999px;
background: rgba(8, 145, 178, 0.1);
color: #67e8f9;
font-size: 10px;
font-weight: 900;
letter-spacing: 0.12em;
text-transform: uppercase;
}
.root :global(.home-top-opportunity-label::before) {
content: "";
width: 6px;
height: 6px;
border-radius: 999px;
background: #22c55e;
box-shadow: 0 0 12px rgba(34, 197, 94, 0.72);
}
.root :global(.home-card-titlebar) {
display: flex;
justify-content: space-between;
@@ -1468,6 +1500,10 @@
}
@media (max-width: 1360px) {
.root :global(.map) {
right: 18px;
}
.root :global(.home-intelligence-panel) {
display: none;
}
@@ -1478,6 +1514,11 @@
}
@media (max-width: 1020px) {
.root :global(.map) {
left: 18px;
bottom: 188px;
}
.root :global(.home-opportunity-strip) {
left: 18px;
grid-template-columns: 1fr;
@@ -1489,6 +1530,13 @@
}
@media (max-width: 720px) {
.root :global(.map) {
inset: var(--header-height) 0 0;
border-radius: 0;
border-left: 0;
border-right: 0;
}
.root :global(.home-opportunity-strip) {
display: none;
}
@@ -2,7 +2,7 @@
import clsx from "clsx";
import dynamic from "next/dynamic";
import Link from "next/link";
import { useEffect, useMemo } from "react";
import { useEffect, useMemo, useRef } from "react";
import styles from "./Dashboard.module.css";
import detailChromeStyles from "./DetailPanelChrome.module.css";
import modalChromeStyles from "./ModalChrome.module.css";
@@ -122,7 +122,13 @@ function buildSnapshot(
detail?.risk?.level;
const hitRate = Number(city.deb_recent_hit_rate ?? 0);
const sampleCount = Number(city.deb_recent_sample_count ?? 0);
const edgePercent = Number(detail?.market_scan?.edge_percent);
const normalizedEdge =
Number.isFinite(edgePercent) && Math.abs(edgePercent) <= 1
? edgePercent * 100
: edgePercent;
const score =
(Number.isFinite(normalizedEdge) ? 1000 + normalizedEdge * 10 : 0) +
(RISK_SCORE[String(tier || "")] || 0) * 100 +
hitRate * 100 +
Math.min(sampleCount, 60) / 10;
@@ -288,6 +294,10 @@ function HomeIntelligencePanel({ snapshots }: { snapshots: CitySnapshot[] }) {
×
</button>
<div className="home-top-opportunity-label">
{locale === "en-US" ? "Todays Top Opportunity" : "今日最佳机会"}
</div>
<div className="home-card-titlebar">
<div>
<h2>{summary?.display_name || detail?.display_name || city.display_name}</h2>
@@ -520,6 +530,7 @@ function OpportunityStrip({ snapshots }: { snapshots: CitySnapshot[] }) {
function DashboardScreen() {
const store = useDashboardStore();
const { t } = useI18n();
const didAutoFocusRef = useRef(false);
const activeSummary = store.selectedCity
? store.citySummariesByName[store.selectedCity] || null
: null;
@@ -581,6 +592,17 @@ function DashboardScreen() {
const showHomepageChrome =
!store.isPanelOpen && !store.historyState.isOpen && !store.futureModalDate;
useEffect(() => {
if (didAutoFocusRef.current) return;
if (!showHomepageChrome) return;
if (store.selectedCity) return;
const topOpportunity = homepageSnapshots[0]?.city.name;
if (!topOpportunity) return;
didAutoFocusRef.current = true;
void store.focusCity(topOpportunity);
}, [homepageSnapshots, showHomepageChrome, store]);
return (
<div
className={clsx(