fix: unified nav - TopHud back btn + window controls + login close
This commit is contained in:
@@ -4,12 +4,26 @@ interface TopHudProps {
|
|||||||
title?: string;
|
title?: string;
|
||||||
subtitle?: string;
|
subtitle?: string;
|
||||||
sections?: { label: string; value: string }[];
|
sections?: { label: string; value: string }[];
|
||||||
|
onBack?: () => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function TopHud({ title = '码枪堂 2.0', subtitle, sections = [] }: TopHudProps) {
|
function callElectron(method: string) {
|
||||||
|
try { (window as any).electronAPI?.[method](); } catch {}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function TopHud({ title = '码枪堂 2.0', subtitle, sections = [], onBack }: TopHudProps) {
|
||||||
return (
|
return (
|
||||||
<div className="flex items-center justify-between border-b border-[#333] bg-[#1a1a1a] px-3 py-1">
|
<div className="flex items-center justify-between border-b border-[#333] bg-[#1a1a1a] px-3 py-1 select-none">
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2">
|
||||||
|
{/* Back button */}
|
||||||
|
{onBack && (
|
||||||
|
<button
|
||||||
|
onClick={onBack}
|
||||||
|
className="text-[#555] hover:text-[#e0e0e0] text-[10px] px-1 py-0.5 border border-[#333] hover:border-[#555] transition-colors duration-75"
|
||||||
|
>
|
||||||
|
←
|
||||||
|
</button>
|
||||||
|
)}
|
||||||
<span className="text-[#ff4500] text-[8px]">◆</span>
|
<span className="text-[#ff4500] text-[8px]">◆</span>
|
||||||
<h1 className="text-[9px] font-bold tracking-[0.15em] uppercase text-[#e0e0e0]">{title}</h1>
|
<h1 className="text-[9px] font-bold tracking-[0.15em] uppercase text-[#e0e0e0]">{title}</h1>
|
||||||
{subtitle && (
|
{subtitle && (
|
||||||
@@ -19,7 +33,7 @@ export default function TopHud({ title = '码枪堂 2.0', subtitle, sections = [
|
|||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
<div className="flex items-center gap-3">
|
<div className="flex items-center gap-2">
|
||||||
{sections.map((s, i) => (
|
{sections.map((s, i) => (
|
||||||
<span key={i} className="flex items-center gap-1">
|
<span key={i} className="flex items-center gap-1">
|
||||||
<span className="text-[7px] tracking-[0.15em] uppercase text-[#555]">{s.label}</span>
|
<span className="text-[7px] tracking-[0.15em] uppercase text-[#555]">{s.label}</span>
|
||||||
@@ -27,7 +41,27 @@ export default function TopHud({ title = '码枪堂 2.0', subtitle, sections = [
|
|||||||
</span>
|
</span>
|
||||||
))}
|
))}
|
||||||
<span className="w-[1px] h-3 bg-[#333]" />
|
<span className="w-[1px] h-3 bg-[#333]" />
|
||||||
<span className="text-[8px] font-mono text-[#444]">V0.2.1</span>
|
|
||||||
|
{/* Window controls */}
|
||||||
|
<button
|
||||||
|
onClick={() => callElectron('minimizeWindow')}
|
||||||
|
className="text-[#555] hover:text-[#e0e0e0] text-[14px] leading-none px-1 transition-colors duration-75"
|
||||||
|
>
|
||||||
|
─
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
onClick={() => callElectron('maximizeWindow')}
|
||||||
|
className="text-[#555] hover:text-[#e0e0e0] text-[10px] leading-none px-1 transition-colors duration-75"
|
||||||
|
>
|
||||||
|
☐
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
onClick={() => callElectron('closeWindow')}
|
||||||
|
className="text-[#555] hover:text-[#cc3300] text-[12px] leading-none px-1 transition-colors duration-75"
|
||||||
|
>
|
||||||
|
✕
|
||||||
|
</button>
|
||||||
|
<span className="text-[8px] font-mono text-[#444] ml-1">V0.2.1</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -7,6 +7,10 @@ import { useAuth } from '../hooks/useAuth';
|
|||||||
import { useAuthStore } from '../stores/authStore';
|
import { useAuthStore } from '../stores/authStore';
|
||||||
import { getVipStatus, activateVip } from '../services/auth.api';
|
import { getVipStatus, activateVip } from '../services/auth.api';
|
||||||
|
|
||||||
|
function callElectron(method: string) {
|
||||||
|
try { (window as any).electronAPI?.[method](); } catch {}
|
||||||
|
}
|
||||||
|
|
||||||
export default function Login() {
|
export default function Login() {
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
const { login } = useAuth();
|
const { login } = useAuth();
|
||||||
@@ -50,15 +54,20 @@ export default function Login() {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="h-screen bg-[#1a1a1a] flex flex-col items-center justify-center p-6">
|
<div className="h-screen bg-[#1a1a1a] flex flex-col items-center justify-center p-6">
|
||||||
{/* 品牌 */}
|
{/* 关闭按钮 */}
|
||||||
|
<button
|
||||||
|
onClick={() => callElectron('closeWindow')}
|
||||||
|
className="absolute top-3 right-3 text-[#555] hover:text-[#cc3300] text-[14px] px-2 py-1 border border-[#333] hover:border-[#cc3300] transition-colors duration-75"
|
||||||
|
>
|
||||||
|
✕
|
||||||
|
</button>
|
||||||
|
|
||||||
<div className="text-center mb-6">
|
<div className="text-center mb-6">
|
||||||
<h1 className="text-[14px] font-bold tracking-[0.2em] uppercase text-[#e0e0e0]">码枪堂 2.0</h1>
|
<h1 className="text-[14px] font-bold tracking-[0.2em] uppercase text-[#e0e0e0]">码枪堂 2.0</h1>
|
||||||
<p className="text-[8px] font-mono text-[#555] mt-1">用户认证系统</p>
|
<p className="text-[8px] font-mono text-[#555] mt-1">用户认证系统</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* 认证卡片 */}
|
|
||||||
<Card className="w-full max-w-xs" serial="AUTH-01">
|
<Card className="w-full max-w-xs" serial="AUTH-01">
|
||||||
{/* 标签页 */}
|
|
||||||
<div className="flex border-b border-[#333]">
|
<div className="flex border-b border-[#333]">
|
||||||
{(['login', 'register', 'vip'] as const).map(t => (
|
{(['login', 'register', 'vip'] as const).map(t => (
|
||||||
<button
|
<button
|
||||||
@@ -73,7 +82,6 @@ export default function Login() {
|
|||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* 表单 */}
|
|
||||||
<div className="p-3 space-y-2.5">
|
<div className="p-3 space-y-2.5">
|
||||||
{tab === 'vip' ? (
|
{tab === 'vip' ? (
|
||||||
<>
|
<>
|
||||||
@@ -122,13 +130,10 @@ export default function Login() {
|
|||||||
</p>
|
</p>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<p className="text-[7px] font-mono text-[#444] text-center">
|
<p className="text-[7px] font-mono text-[#444] text-center">SYS-AUTH v1.0</p>
|
||||||
SYS-AUTH v1.0
|
|
||||||
</p>
|
|
||||||
</div>
|
</div>
|
||||||
</Card>
|
</Card>
|
||||||
|
|
||||||
{/* 返回 */}
|
|
||||||
<button
|
<button
|
||||||
onClick={() => navigate('/')}
|
onClick={() => navigate('/')}
|
||||||
className="mt-4 text-[8px] font-mono text-[#555] hover:text-[#888] transition-colors duration-75"
|
className="mt-4 text-[8px] font-mono text-[#555] hover:text-[#888] transition-colors duration-75"
|
||||||
|
|||||||
@@ -81,7 +81,7 @@ export default function SchemeDetail() {
|
|||||||
<div className="flex flex-col gap-3 p-3 flex-1">
|
<div className="flex flex-col gap-3 p-3 flex-1">
|
||||||
<div className="flex items-center justify-between border-b border-[#333] pb-2">
|
<div className="flex items-center justify-between border-b border-[#333] pb-2">
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2">
|
||||||
<button onClick={() => navigate(-1)} className="text-[#555] hover:text-[#e0e0e0] text-[10px] transition-colors duration-75">←</button>
|
|
||||||
<h1 className="text-[11px] font-bold tracking-[0.15em] uppercase">方案详情</h1>
|
<h1 className="text-[11px] font-bold tracking-[0.15em] uppercase">方案详情</h1>
|
||||||
</div>
|
</div>
|
||||||
{scheme.isOfficial && <span className="text-[7px] font-semibold tracking-[0.1em] uppercase text-[#ff4500] border border-[#ff4500]/30 px-1 py-0.5">官方</span>}
|
{scheme.isOfficial && <span className="text-[7px] font-semibold tracking-[0.1em] uppercase text-[#ff4500] border border-[#ff4500]/30 px-1 py-0.5">官方</span>}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import React, { Suspense, lazy } from 'react';
|
import React, { Suspense, lazy } from 'react';
|
||||||
import { Routes, Route } from 'react-router-dom';
|
import { Routes, Route, useNavigate } from 'react-router-dom';
|
||||||
import PageContainer from './components/layout/PageContainer';
|
import PageContainer from './components/layout/PageContainer';
|
||||||
import TopHud from './components/layout/TopHud';
|
import TopHud from './components/layout/TopHud';
|
||||||
|
|
||||||
@@ -30,6 +30,9 @@ const pageTitles: Record<string, string> = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
function PageShell({ children, currentPage }: { children: React.ReactNode; currentPage: string }) {
|
function PageShell({ children, currentPage }: { children: React.ReactNode; currentPage: string }) {
|
||||||
|
const navigate = useNavigate();
|
||||||
|
const isHome = currentPage === 'home';
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="h-screen bg-[#1a1a1a] text-[#e0e0e0] antialiased overflow-hidden flex flex-col" style={{ fontFamily: 'Inter, sans-serif' }}>
|
<div className="h-screen bg-[#1a1a1a] text-[#e0e0e0] antialiased overflow-hidden flex flex-col" style={{ fontFamily: 'Inter, sans-serif' }}>
|
||||||
<TopHud
|
<TopHud
|
||||||
@@ -39,6 +42,7 @@ function PageShell({ children, currentPage }: { children: React.ReactNode; curre
|
|||||||
{ label: '状态', value: '在线' },
|
{ label: '状态', value: '在线' },
|
||||||
{ label: '模式', value: '本地' },
|
{ label: '模式', value: '本地' },
|
||||||
]}
|
]}
|
||||||
|
onBack={isHome ? undefined : () => navigate(-1)}
|
||||||
/>
|
/>
|
||||||
<PageContainer>
|
<PageContainer>
|
||||||
<Suspense fallback={<LoadingFallback />}>
|
<Suspense fallback={<LoadingFallback />}>
|
||||||
|
|||||||
Reference in New Issue
Block a user