feat: GeoScene frontend POC + Docker deploy for remote host
Migrate maps to @geoscene/core, polish monitoring/alerts UX, fix timeline basemap flicker and district alert regions, and ship compose/nginx Docker deploy assets with CBPOA_ROOT data mounts. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -1,14 +1,9 @@
|
||||
import { useState, useEffect } from 'react';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
import { TESTIDS } from '@/utils/testids';
|
||||
import { useSessionStore, ROLES, type Role } from '@/stores/sessionStore';
|
||||
import { ROLE_LABELS, roleDefaultPath } from '@/utils/roleViews';
|
||||
|
||||
interface TopNavProps {
|
||||
onLogout?: () => void;
|
||||
// 移动端汉堡按钮:切换侧栏抽屉。
|
||||
onToggleMenu?: () => void;
|
||||
// 抽屉是否展开(用于汉堡按钮的 aria-expanded)。
|
||||
isMenuOpen?: boolean;
|
||||
}
|
||||
|
||||
@@ -18,44 +13,16 @@ function Clock() {
|
||||
const id = setInterval(() => setTime(new Date()), 1000);
|
||||
return () => clearInterval(id);
|
||||
}, []);
|
||||
return <span>{time.toLocaleTimeString('zh-CN', { hour: '2-digit', minute: '2-digit' })}</span>;
|
||||
}
|
||||
|
||||
// 视角切换器:纯前端视图预设(D2)。刻意标注「视角」而非「权限」——不是访问控制。
|
||||
// 切换时持久化角色并跳转到该视角的默认落地页。
|
||||
function PerspectiveSwitcher() {
|
||||
const role = useSessionStore((s) => s.role);
|
||||
const setRole = useSessionStore((s) => s.setRole);
|
||||
const navigate = useNavigate();
|
||||
|
||||
const handleChange = (next: Role) => {
|
||||
setRole(next);
|
||||
navigate(roleDefaultPath(next));
|
||||
};
|
||||
|
||||
return (
|
||||
<label className="flex items-center gap-1.5 text-[13px] text-text-secondary">
|
||||
<span className="text-text-muted hidden sm:inline">视角</span>
|
||||
<select
|
||||
data-testid={TESTIDS.perspectiveSwitcher}
|
||||
value={role}
|
||||
onChange={(e) => handleChange(e.target.value as Role)}
|
||||
aria-label="切换视角"
|
||||
className="bg-bg-card border border-border rounded-md px-2 py-1 text-[13px] text-text-primary hover:bg-bg-hover focus:outline-none focus:ring-1 focus:ring-primary cursor-pointer"
|
||||
>
|
||||
{ROLES.map((r) => (
|
||||
<option key={r} value={r} data-testid={`${TESTIDS.perspectiveOption}-${r}`}>
|
||||
{ROLE_LABELS[r]}
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
</label>
|
||||
<span className="font-mono tabular-nums">
|
||||
{time.toLocaleTimeString('zh-CN', { hour: '2-digit', minute: '2-digit' })}
|
||||
</span>
|
||||
);
|
||||
}
|
||||
|
||||
export function TopNav({ onLogout, onToggleMenu, isMenuOpen = false }: TopNavProps) {
|
||||
return (
|
||||
<nav className="h-[52px] bg-bg-card border-b border-border flex items-center px-5 z-50">
|
||||
<nav className="h-[54px] shrink-0 bg-bg-card/95 border-b border-border flex items-center px-4 sm:px-5 z-50 backdrop-blur-md">
|
||||
{onToggleMenu && (
|
||||
<button
|
||||
type="button"
|
||||
@@ -64,7 +31,7 @@ export function TopNav({ onLogout, onToggleMenu, isMenuOpen = false }: TopNavPro
|
||||
aria-expanded={isMenuOpen}
|
||||
aria-controls="app-drawer"
|
||||
data-testid={TESTIDS.hamburger}
|
||||
className="lg:hidden mr-3 -ml-1 w-9 h-9 flex items-center justify-center rounded-md text-text-secondary hover:bg-bg-hover transition-colors"
|
||||
className="lg:hidden mr-2.5 -ml-0.5 w-9 h-9 flex items-center justify-center rounded-lg text-text-secondary hover:bg-bg-hover transition-colors"
|
||||
>
|
||||
<svg className="w-5 h-5" fill="none" stroke="currentColor" strokeWidth="2" viewBox="0 0 24 24">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" d="M4 6h16M4 12h16M4 18h16" />
|
||||
@@ -72,37 +39,38 @@ export function TopNav({ onLogout, onToggleMenu, isMenuOpen = false }: TopNavPro
|
||||
</button>
|
||||
)}
|
||||
|
||||
<div className="flex items-center gap-3">
|
||||
<div className="w-7 h-7 bg-primary rounded-md flex items-center justify-center">
|
||||
<div className="flex items-center gap-3 min-w-0">
|
||||
<div
|
||||
className="w-8 h-8 rounded-lg bg-gradient-to-br from-primary to-mist flex items-center justify-center shadow-soft shrink-0"
|
||||
aria-hidden
|
||||
>
|
||||
<svg className="w-4 h-4 fill-white" viewBox="0 0 24 24">
|
||||
<path d="M19 3H5c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2zm-7 3c1.93 0 3.5 1.57 3.5 3.5S13.93 13 12 13s-3.5-1.57-3.5-3.5S10.07 6 12 6zm7 13H5v-.23c0-.62.28-1.2.76-1.58C7.47 15.82 9.64 15 12 15s4.53.82 6.24 2.19c.48.38.76.97.76 1.58V19z" />
|
||||
<path d="M12 3c-1.2 2.4-3.5 4-6 4 .6 3.4 2.8 6.2 6 7.5 3.2-1.3 5.4-4.1 6-7.5-2.5 0-4.8-1.6-6-4zm0 14.5c-2.2-.9-4-2.5-5.2-4.5C5.5 15.2 4 18 4 21h16c0-3-1.5-5.8-2.8-8-1.2 2-3 3.6-5.2 4.5z" />
|
||||
</svg>
|
||||
</div>
|
||||
<span className="font-display font-semibold text-[15px] text-text-primary">
|
||||
WuhanChildRisk
|
||||
</span>
|
||||
<div className="min-w-0 leading-tight">
|
||||
<span className="brand-mark text-[16px] block truncate">CBPOA</span>
|
||||
<span className="text-[11px] text-text-muted hidden sm:block truncate">
|
||||
武汉儿童呼吸疾病风险评估
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="w-px h-5 bg-border ml-4 mr-4 hidden sm:block" />
|
||||
<div className="w-px h-6 bg-border ml-4 mr-4 hidden md:block" />
|
||||
|
||||
<span className="text-[13px] text-text-secondary hidden sm:inline">
|
||||
儿童呼吸道疾病风险监测预警平台
|
||||
<span className="text-[12px] text-text-secondary hidden md:inline truncate">
|
||||
监测 · 预警 · 空间风险
|
||||
</span>
|
||||
|
||||
<div className="ml-auto flex items-center gap-5">
|
||||
<div className="ml-auto flex items-center gap-4">
|
||||
<span className="text-[12px] text-text-muted hidden sm:inline">
|
||||
<Clock />
|
||||
</span>
|
||||
<div className="flex items-center gap-2 text-[13px] text-text-secondary">
|
||||
<svg className="w-4 h-4" fill="currentColor" viewBox="0 0 24 24">
|
||||
<path d="M12 12c2.21 0 4-1.79 4-4s-1.79-4-4-4-4 1.79-4 4 1.79 4 4 4zm0 2c-2.67 0-8 1.34-8 4v2h16v-2c0-2.66-5.33-4-8-4z" />
|
||||
</svg>
|
||||
<PerspectiveSwitcher />
|
||||
</div>
|
||||
{onLogout && (
|
||||
<button
|
||||
type="button"
|
||||
onClick={onLogout}
|
||||
className="text-[12px] text-text-muted hover:text-danger transition-colors"
|
||||
className="text-[12px] text-text-muted hover:text-danger transition-colors px-2 py-1 rounded-md hover:bg-danger-light/60"
|
||||
>
|
||||
退出
|
||||
</button>
|
||||
|
||||
Reference in New Issue
Block a user