| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107 |
- /**
- * 页面头部组件 - 移动端优化
- *
- * 核心概念:
- * - Web UI 是 MCP 测试工具,不需要全局登录
- * - 显示已连接的 MCP 服务器数量
- * - 移动端:两行布局,避免挤在一起
- */
- 'use client';
- import { useState, useEffect } from 'react';
- import Link from 'next/link';
- import { MCP_SERVERS, mcpTokenManager } from '@/lib/mcp-token-manager';
- export default function Header() {
- // 客户端挂载标志 - 用于避免 hydration 错误
- const [mounted, setMounted] = useState(false);
- // 初始值设为 0,避免 SSR/CSR 不匹配
- const [enabledCount, setEnabledCount] = useState(0);
- const [loggedInCount, setLoggedInCount] = useState(0);
- // 从 MCP_SERVERS 配置获取总数
- const total = Object.keys(MCP_SERVERS).length;
- useEffect(() => {
- // 设置客户端挂载标志
- setMounted(true);
- // 从 localStorage 读取真实状态(只在客户端执行)
- const updateStatus = () => {
- const enabled = mcpTokenManager.getEnabledMcpList().length;
- const loggedIn = mcpTokenManager.getLoggedInMcpList().length;
- console.log('[Header] updateStatus:', { enabled, loggedIn });
- setEnabledCount(enabled);
- setLoggedInCount(loggedIn);
- };
- // 初始化时更新状态
- updateStatus();
- // 监听 storage 变化(其他标签页修改 localStorage)
- const handleStorageChange = () => updateStatus();
- window.addEventListener('storage', handleStorageChange);
- // 自定义事件:MCP 启用状态变化(当前页面内)
- const handleMcpEnabledChange = () => updateStatus();
- window.addEventListener('mcp-enabled-change', handleMcpEnabledChange);
- // 监听 focus 事件,确保页面获得焦点时更新状态
- const handleFocus = () => updateStatus();
- window.addEventListener('focus', handleFocus);
- return () => {
- window.removeEventListener('storage', handleStorageChange);
- window.removeEventListener('mcp-enabled-change', handleMcpEnabledChange);
- window.removeEventListener('focus', handleFocus);
- };
- }, []); // 空依赖数组,只在挂载时执行一次
- return (
- <header className="bg-white dark:bg-gray-800 border-b dark:border-gray-700 px-3 py-2 md:px-6 md:py-4 safe-area-inset-top" suppressHydrationWarning>
- {/* 移动端:两行布局 */}
- <div className="flex flex-col md:flex-row md:items-center md:justify-between gap-2 md:gap-0">
- {/* 第一行:标题和导航 */}
- <div className="flex items-center justify-between">
- <h1 className="text-lg md:text-xl font-bold text-gray-800 dark:text-white">
- AI MCP Web UI
- </h1>
- <nav className="flex space-x-2 md:space-x-4">
- <Link href="/" className="text-sm md:text-base text-gray-600 dark:text-gray-300 hover:text-blue-500 px-2 py-1">
- 聊天
- </Link>
- <Link href="/mcp" className="text-sm md:text-base text-gray-600 dark:text-gray-300 hover:text-blue-500 px-2 py-1">
- MCP 管理
- </Link>
- </nav>
- </div>
- {/* 第二行(移动端)/ 右侧(桌面端):MCP 状态 */}
- <div className="flex items-center justify-between md:justify-end space-x-2 md:space-x-4">
- {/* MCP 连接状态 */}
- <div className="flex items-center space-x-1 md:space-x-2 text-xs md:text-sm">
- <span className="text-gray-600 dark:text-gray-400">MCP:</span>
- <span className={`px-1.5 md:px-2 py-0.5 md:py-1 rounded ${
- mounted && enabledCount > 0
- ? 'bg-purple-100 dark:bg-purple-900 text-purple-800 dark:text-purple-200'
- : 'bg-gray-100 dark:bg-gray-700 text-gray-600 dark:text-gray-400'
- }`}>
- {mounted ? `已启用 ${enabledCount}/${total}` : '加载中...'}
- </span>
- {mounted && loggedInCount > 0 && (
- <span className="hidden sm:inline-block px-1.5 md:px-2 py-0.5 md:py-1 rounded bg-green-100 dark:bg-green-900 text-green-800 dark:text-green-200">
- 已登录 {loggedInCount}/{total}
- </span>
- )}
- </div>
- <Link
- href="/mcp"
- className="text-xs md:text-sm px-2 md:px-4 py-1.5 md:py-2 bg-blue-500 text-white rounded-lg hover:bg-blue-600 transition-colors"
- >
- {mounted ? (loggedInCount > 0 ? '管理' : '连接 MCP') : 'MCP'}
- </Link>
- </div>
- </div>
- </header>
- );
- }
|