TopBar.tsx 3.46 KB
// REQ-USR-003 / REQ-USR-004: 顶栏(Logo + 全部导航按钮 + 标签条 + 右侧搜索/通知/当前用户/更多)。
import { MenuOutlined, SearchOutlined, BellOutlined, HomeOutlined } from '@ant-design/icons';
import type { AuthUser } from '../../api/types';
import type { TabItem } from './useTabStack';
import CurrentUserMenu from './CurrentUserMenu';
import styles from './AppLayout.module.css';

interface TopBarProps {
  user: AuthUser | null;
  tabs: TabItem[];
  activeKey: string;
  navOverlayOpen: boolean;
  onToggleNav: () => void;
  onSelectTab: (key: string) => void;
  onCloseTab: (key: string) => void;
  onLogout: () => void;
  onLogoHome: () => void;
}

export default function TopBar({
  user,
  tabs,
  activeKey,
  navOverlayOpen,
  onToggleNav,
  onSelectTab,
  onCloseTab,
  onLogout,
  onLogoHome,
}: TopBarProps) {
  return (
    <div className={styles.topbar}>
      {/* 品牌 Logo(鹿角 SVG),点击回主页 */}
      <button type="button" className={styles.logo} title="主页" onClick={onLogoHome} aria-label="品牌Logo 回到主页">
        <svg viewBox="0 0 64 64" fill="currentColor" aria-hidden="true">
          <path d="M14 10c2 4 1 8-1 11 3-1 7 0 10 3 1-4 4-7 8-7-3 3-4 7-3 11l4 1c-1 3 0 6 3 8-3 0-6 1-8 4-1-3-4-5-8-5 2-3 2-7 0-10-3 1-7 0-10-3 3 0 5-2 6-5l-1-8z" />
          <path d="M48 14c-2 3-2 6-1 9-2-2-5-2-8-1 1 3 1 6-1 9 3 0 5 2 6 5 1-3 4-5 7-5-2-3-2-6 0-9 2 1 5 1 7-1-2 0-4-1-5-3-1-2-3-4-5-4z" />
          <path d="M28 38c2 3 5 5 9 5 1 4 4 7 8 8-3 2-5 5-5 9-3-2-7-3-11-2 1-3 1-7-1-10-3 0-6-1-8-4 3-1 6-3 8-6z" />
        </svg>
      </button>

      <div className={styles.tabs}>
        <button
          type="button"
          className={`${styles.navBtn} ${navOverlayOpen ? styles.navBtnActive : ''}`}
          aria-pressed={navOverlayOpen}
          onClick={onToggleNav}
        >
          <MenuOutlined aria-hidden />
          全部导航
        </button>

        {tabs.map((tab) => (
          <button
            type="button"
            key={tab.key}
            data-testid={`tab-${tab.key}`}
            className={`${styles.tab} ${activeKey === tab.key ? styles.tabActive : ''}`}
            aria-pressed={activeKey === tab.key}
            onClick={() => onSelectTab(tab.key)}
          >
            {tab.key === 'home' && <HomeOutlined aria-hidden />}
            {tab.title}
            {tab.closable && (
              <span
                role="button"
                tabIndex={0}
                aria-label={`关闭${tab.title}`}
                data-testid={`tab-close-${tab.key}`}
                className={styles.tabClose}
                onClick={(e) => {
                  e.stopPropagation();
                  onCloseTab(tab.key);
                }}
                onKeyDown={(e) => {
                  if (e.key === 'Enter' || e.key === ' ') {
                    e.stopPropagation();
                    onCloseTab(tab.key);
                  }
                }}
              >

              </span>
            )}
          </button>
        ))}
      </div>

      <div className={styles.right}>
        <span className={styles.rightIcon} title="搜索" aria-hidden="true">
          <SearchOutlined />
        </span>
        <span className={styles.rightIcon} title="通知" aria-hidden="true">
          <BellOutlined />
        </span>
        <CurrentUserMenu user={user} onLogout={onLogout} />
        <span className={styles.more} aria-hidden="true">

        </span>
      </div>
    </div>
  );
}