|
|
@@ -1,11 +1,15 @@
|
|
|
-// import { Button } from "@contentful/f36-components";
|
|
|
-import { GearSixIcon, ListIcon } from "@contentful/f36-icons";
|
|
|
+import { Navbar } from "@contentful/f36-navbar";
|
|
|
+import {
|
|
|
+ MagnifyingGlassIcon,
|
|
|
+ QuestionIcon,
|
|
|
+ GearSixIcon,
|
|
|
+} from "@contentful/f36-icons";
|
|
|
import { useAuth } from "../../contexts/AuthContext";
|
|
|
import { useMenu } from "../../contexts/MenuContext";
|
|
|
import { useNavigate, useLocation } from "react-router-dom";
|
|
|
import { useEffect, useState } from "react";
|
|
|
import type { JeecgMenu } from "../../types/menu";
|
|
|
-import styles from "./index.module.css";
|
|
|
+import SearchModal from "../../components/SearchModal";
|
|
|
|
|
|
export default function Header() {
|
|
|
const { menus, loading } = useAuth();
|
|
|
@@ -13,8 +17,7 @@ export default function Header() {
|
|
|
useMenu();
|
|
|
const navigate = useNavigate();
|
|
|
const location = useLocation();
|
|
|
- const [hoveredMenu, setHoveredMenu] = useState<JeecgMenu | null>(null);
|
|
|
- const [mobileMenuOpen, setMobileMenuOpen] = useState(false);
|
|
|
+ const [isSearchOpen, setIsSearchOpen] = useState(false);
|
|
|
|
|
|
// 获取顶级菜单(一级菜单)
|
|
|
const topMenus = menus.filter((menu) => !menu.hidden && !menu.meta?.hideMenu);
|
|
|
@@ -25,7 +28,6 @@ export default function Header() {
|
|
|
const segments = path.split("/").filter(Boolean);
|
|
|
const firstPath = segments[0];
|
|
|
|
|
|
- // 尝试匹配一级菜单
|
|
|
const matchedMenu = topMenus.find((menu) => {
|
|
|
const menuPath = menu.path || "";
|
|
|
const menuSegments = menuPath.split("/").filter(Boolean);
|
|
|
@@ -44,200 +46,191 @@ export default function Header() {
|
|
|
setActiveSecondMenu,
|
|
|
]);
|
|
|
|
|
|
- const handleMenuClick = (menu: (typeof topMenus)[0]) => {
|
|
|
- console.log("Header: Clicked menu", menu.name, menu.path);
|
|
|
-
|
|
|
- const hasChildren = menu.children && menu.children.length > 0;
|
|
|
-
|
|
|
- // 如果有子菜单,只设置激活状态,不跳转
|
|
|
- if (hasChildren) {
|
|
|
+ // 处理菜单点击
|
|
|
+ const handleMenuClick = (menu: JeecgMenu, child?: JeecgMenu) => {
|
|
|
+ if (child) {
|
|
|
+ // 点击子菜单
|
|
|
setActiveFirstMenu(menu);
|
|
|
- setActiveSecondMenu(null);
|
|
|
- // 不导航,让用户点击下拉菜单中的选项
|
|
|
+ setActiveSecondMenu(child);
|
|
|
+ navigate(child.path || "");
|
|
|
} else {
|
|
|
- // 没有子菜单,直接导航到该菜单
|
|
|
+ // 点击一级菜单(无子菜单)
|
|
|
setActiveFirstMenu(menu);
|
|
|
setActiveSecondMenu(null);
|
|
|
- const menuPath = menu.path || "";
|
|
|
- console.log("Header: Navigating to menu", menuPath);
|
|
|
- navigate(menuPath);
|
|
|
+ navigate(menu.path || "");
|
|
|
}
|
|
|
};
|
|
|
|
|
|
- return (
|
|
|
- <header className={styles.header}>
|
|
|
- <div className={styles.left}>
|
|
|
- {/* Logo */}
|
|
|
- <div className={styles.logo}>
|
|
|
- <svg width="24" height="24" viewBox="0 0 24 24" fill="none">
|
|
|
- <circle cx="12" cy="12" r="10" fill="#0066ff" />
|
|
|
- <path
|
|
|
- d="M8 12L11 15L16 9"
|
|
|
- stroke="white"
|
|
|
- strokeWidth="2"
|
|
|
- strokeLinecap="round"
|
|
|
- strokeLinejoin="round"
|
|
|
+ // 跳转到权限管理
|
|
|
+ const toPermissionPage = () => {
|
|
|
+ debugger;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (loading) {
|
|
|
+ return <div style={{ padding: "16px" }}>加载中...</div>;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 渲染主导航(桌面端)
|
|
|
+ const mainNavigation = (
|
|
|
+ <>
|
|
|
+ {topMenus.map((menu) => {
|
|
|
+ const hasChildren = menu.children && menu.children.length > 0;
|
|
|
+ const isActive = activeFirstMenu?.id === menu.id;
|
|
|
+
|
|
|
+ if (hasChildren) {
|
|
|
+ // 有子菜单的项目
|
|
|
+ return (
|
|
|
+ <Navbar.Item
|
|
|
+ key={menu.id}
|
|
|
+ title={menu.meta?.title || menu.name}
|
|
|
+ isActive={isActive}
|
|
|
+ >
|
|
|
+ {menu.children
|
|
|
+ ?.filter((child) => !child.hidden && !child.meta?.hideMenu)
|
|
|
+ .map((child) => (
|
|
|
+ <Navbar.MenuItem
|
|
|
+ key={child.id}
|
|
|
+ title={child.meta?.title || child.name}
|
|
|
+ onClick={() => handleMenuClick(menu, child)}
|
|
|
+ />
|
|
|
+ ))}
|
|
|
+ </Navbar.Item>
|
|
|
+ );
|
|
|
+ } else {
|
|
|
+ // 无子菜单的项目
|
|
|
+ return (
|
|
|
+ <Navbar.Item
|
|
|
+ key={menu.id}
|
|
|
+ title={menu.meta?.title || menu.name}
|
|
|
+ isActive={isActive}
|
|
|
+ onClick={() => handleMenuClick(menu)}
|
|
|
/>
|
|
|
- </svg>
|
|
|
- </div>
|
|
|
-
|
|
|
- {/* Mobile Menu Button */}
|
|
|
- <div
|
|
|
- className={styles.mobileMenuWrapper}
|
|
|
- onMouseEnter={() => setMobileMenuOpen(true)}
|
|
|
- onMouseLeave={() => setMobileMenuOpen(false)}
|
|
|
- >
|
|
|
- <button className={styles.mobileMenuButton}>
|
|
|
- <span className={styles.menuIcon}><ListIcon /></span>
|
|
|
- <span>Menu</span>
|
|
|
- </button>
|
|
|
-
|
|
|
- {/* Mobile Menu Dropdown */}
|
|
|
- {mobileMenuOpen && (
|
|
|
- <div className={styles.mobileMenuDropdown}>
|
|
|
- {topMenus.map((menu) => {
|
|
|
- const hasChildren = menu.children && menu.children.length > 0;
|
|
|
- const isActive = activeFirstMenu?.id === menu.id;
|
|
|
-
|
|
|
- return (
|
|
|
- <div
|
|
|
- key={menu.id}
|
|
|
- className={styles.mobileMenuItem}
|
|
|
- onMouseEnter={() => hasChildren && setHoveredMenu(menu)}
|
|
|
- onMouseLeave={() => setHoveredMenu(null)}
|
|
|
- >
|
|
|
- <button
|
|
|
- className={`${styles.mobileMenuItemButton} ${
|
|
|
- isActive ? styles.mobileMenuItemActive : ""
|
|
|
- }`}
|
|
|
- onClick={() => !hasChildren && handleMenuClick(menu)}
|
|
|
- >
|
|
|
- <span className={styles.mobileMenuItemText}>
|
|
|
- {menu.meta?.title || menu.name}
|
|
|
- </span>
|
|
|
- {hasChildren && (
|
|
|
- <span className={styles.mobileMenuItemArrow}>›</span>
|
|
|
- )}
|
|
|
- </button>
|
|
|
-
|
|
|
- {/* Submenu */}
|
|
|
- {hasChildren && hoveredMenu?.id === menu.id && (
|
|
|
- <div className={styles.mobileSubmenu}>
|
|
|
- {menu.children
|
|
|
- ?.filter((child) => !child.hidden && !child.meta?.hideMenu)
|
|
|
- .map((child) => {
|
|
|
- const childPath = child.path || "";
|
|
|
- const parentPath = menu.path || "";
|
|
|
- const fullPath = childPath.startsWith("/")
|
|
|
- ? childPath
|
|
|
- : `${parentPath}/${childPath}`;
|
|
|
-
|
|
|
- return (
|
|
|
- <button
|
|
|
- key={child.id}
|
|
|
- className={styles.mobileSubmenuItem}
|
|
|
- onClick={() => {
|
|
|
- setActiveFirstMenu(menu);
|
|
|
- setActiveSecondMenu(child);
|
|
|
- setMobileMenuOpen(false);
|
|
|
- setHoveredMenu(null);
|
|
|
- navigate(fullPath);
|
|
|
- }}
|
|
|
- >
|
|
|
- {child.meta?.title || child.name}
|
|
|
- </button>
|
|
|
- );
|
|
|
- })}
|
|
|
- </div>
|
|
|
- )}
|
|
|
- </div>
|
|
|
- );
|
|
|
- })}
|
|
|
- </div>
|
|
|
- )}
|
|
|
- </div>
|
|
|
-
|
|
|
- {/* Card Navigation */}
|
|
|
- {loading ? (
|
|
|
- <div className={styles.loading}>加载中...</div>
|
|
|
- ) : (
|
|
|
- <nav className={styles.menuCards}>
|
|
|
- {topMenus.map((menu) => {
|
|
|
- const isActive = activeFirstMenu?.id === menu.id;
|
|
|
- const hasChildren = menu.children && menu.children.length > 0;
|
|
|
-
|
|
|
- return (
|
|
|
- <div
|
|
|
- key={menu.id}
|
|
|
- className={styles.menuCardWrapper}
|
|
|
- onMouseEnter={() => hasChildren && setHoveredMenu(menu)}
|
|
|
- onMouseLeave={() => setHoveredMenu(null)}
|
|
|
- >
|
|
|
- <button
|
|
|
- className={`${styles.menuCard} ${
|
|
|
- isActive ? styles.menuCardActive : ""
|
|
|
- }`}
|
|
|
- onClick={() => handleMenuClick(menu)}
|
|
|
- >
|
|
|
- <span className={styles.menuText}>
|
|
|
- {menu.meta?.title || menu.name}
|
|
|
- </span>
|
|
|
- {hasChildren && <span className={styles.menuArrow}>▼</span>}
|
|
|
- </button>
|
|
|
-
|
|
|
- {/* 下拉菜单 */}
|
|
|
- {hasChildren && hoveredMenu?.id === menu.id && (
|
|
|
- <div className={styles.dropdown}>
|
|
|
- {menu.children
|
|
|
- ?.filter(
|
|
|
- (child) => !child.hidden && !child.meta?.hideMenu
|
|
|
- )
|
|
|
- .map((child) => {
|
|
|
- // 构建完整路径
|
|
|
- const childPath = child.path || "";
|
|
|
- const parentPath = menu.path || "";
|
|
|
+ );
|
|
|
+ }
|
|
|
+ })}
|
|
|
+ </>
|
|
|
+ );
|
|
|
|
|
|
- // 如果子路径是绝对路径,直接使用
|
|
|
- // 否则拼接父路径
|
|
|
- const fullPath = childPath.startsWith("/")
|
|
|
- ? childPath
|
|
|
- : `${parentPath}/${childPath}`;
|
|
|
+ // 渲染移动端导航
|
|
|
+ const mobileNavigation = (
|
|
|
+ <>
|
|
|
+ {topMenus.map((menu) => {
|
|
|
+ const hasChildren = menu.children && menu.children.length > 0;
|
|
|
+
|
|
|
+ if (hasChildren) {
|
|
|
+ // 有子菜单的项目
|
|
|
+ return (
|
|
|
+ <Navbar.Submenu key={menu.id} title={menu.meta?.title || menu.name}>
|
|
|
+ {menu.children
|
|
|
+ ?.filter((child) => !child.hidden && !child.meta?.hideMenu)
|
|
|
+ .map((child) => (
|
|
|
+ <Navbar.MenuItem
|
|
|
+ key={child.id}
|
|
|
+ title={child.meta?.title || child.name}
|
|
|
+ onClick={() => handleMenuClick(menu, child)}
|
|
|
+ />
|
|
|
+ ))}
|
|
|
+ </Navbar.Submenu>
|
|
|
+ );
|
|
|
+ } else {
|
|
|
+ // 无子菜单的项目
|
|
|
+ return (
|
|
|
+ <Navbar.MenuItem
|
|
|
+ key={menu.id}
|
|
|
+ title={menu.meta?.title || menu.name}
|
|
|
+ onClick={() => handleMenuClick(menu)}
|
|
|
+ />
|
|
|
+ );
|
|
|
+ }
|
|
|
+ })}
|
|
|
+ </>
|
|
|
+ );
|
|
|
|
|
|
- return (
|
|
|
- <button
|
|
|
- key={child.id}
|
|
|
- className={styles.dropdownItem}
|
|
|
- onClick={() => {
|
|
|
- setActiveFirstMenu(menu);
|
|
|
- setActiveSecondMenu(child);
|
|
|
- setHoveredMenu(null);
|
|
|
- navigate(fullPath);
|
|
|
- }}
|
|
|
- >
|
|
|
- {child.meta?.title || child.name}
|
|
|
- </button>
|
|
|
- );
|
|
|
- })}
|
|
|
- </div>
|
|
|
- )}
|
|
|
- </div>
|
|
|
- );
|
|
|
- })}
|
|
|
- </nav>
|
|
|
- )}
|
|
|
- </div>
|
|
|
+ // 右侧辅助导航
|
|
|
+ const secondaryNavigation = (
|
|
|
+ <>
|
|
|
+ <Navbar.Item
|
|
|
+ label="快速搜索"
|
|
|
+ icon={<MagnifyingGlassIcon />}
|
|
|
+ onClick={() => setIsSearchOpen(true)}
|
|
|
+ />
|
|
|
+ <Navbar.Item label="帮助菜单" icon={<QuestionIcon />}>
|
|
|
+ <Navbar.MenuItem
|
|
|
+ as="a"
|
|
|
+ target="_blank"
|
|
|
+ rel="noopener noreferrer"
|
|
|
+ title="帮助中心"
|
|
|
+ href="https://www.contentful.com/help/"
|
|
|
+ />
|
|
|
+ <Navbar.MenuItem
|
|
|
+ as="a"
|
|
|
+ target="_blank"
|
|
|
+ rel="noopener noreferrer"
|
|
|
+ title="开发者文档"
|
|
|
+ href="https://www.contentful.com/developers/docs/"
|
|
|
+ />
|
|
|
+ <Navbar.MenuDivider />
|
|
|
+ <Navbar.MenuItem
|
|
|
+ as="a"
|
|
|
+ target="_blank"
|
|
|
+ rel="noopener noreferrer"
|
|
|
+ title="获取支持"
|
|
|
+ href="https://support.contentful.com"
|
|
|
+ />
|
|
|
+ </Navbar.Item>
|
|
|
+ <Navbar.Item label="设置菜单" icon={<GearSixIcon />}>
|
|
|
+ <Navbar.MenuSectionTitle>通用</Navbar.MenuSectionTitle>
|
|
|
+ <Navbar.MenuItem title="首页" />
|
|
|
+ <Navbar.MenuItem title="API 密钥" />
|
|
|
+ <Navbar.MenuSectionTitle>空间</Navbar.MenuSectionTitle>
|
|
|
+ <Navbar.MenuItem title="应用" />
|
|
|
+ <Navbar.MenuItem title="权限" onClick={toPermissionPage} />
|
|
|
+ </Navbar.Item>
|
|
|
+ </>
|
|
|
+ );
|
|
|
|
|
|
- <div className={styles.right}>
|
|
|
- {/* Action Icons */}
|
|
|
- <div className={styles.actions}>
|
|
|
- <button className={styles.iconButton} title="Settings">
|
|
|
- <GearSixIcon size="small" />
|
|
|
- </button>
|
|
|
- <button className={styles.iconButton} title="User">
|
|
|
- <div className={styles.avatar}>👤</div>
|
|
|
- </button>
|
|
|
- </div>
|
|
|
- </div>
|
|
|
- </header>
|
|
|
+ return (
|
|
|
+ <>
|
|
|
+ <Navbar
|
|
|
+ mainNavigation={mainNavigation}
|
|
|
+ mobileNavigation={mobileNavigation}
|
|
|
+ // switcher={
|
|
|
+ // <Navbar.Switcher
|
|
|
+ // isAlias={false}
|
|
|
+ // envVariant="master"
|
|
|
+ // space="Content Space"
|
|
|
+ // environment="master"
|
|
|
+ // />
|
|
|
+ // }
|
|
|
+ secondaryNavigation={secondaryNavigation}
|
|
|
+ account={
|
|
|
+ <Navbar.Account username="管理员" initials="A" label="账户设置">
|
|
|
+ <Navbar.MenuItem title="账户设置" />
|
|
|
+ <Navbar.MenuItem title="控制台" />
|
|
|
+ <Navbar.MenuDivider />
|
|
|
+ <Navbar.MenuItem
|
|
|
+ title="外部链接"
|
|
|
+ as="a"
|
|
|
+ href="https://www.contentful.com"
|
|
|
+ target="_blank"
|
|
|
+ rel="noopener noreferrer"
|
|
|
+ />
|
|
|
+ <Navbar.MenuDivider />
|
|
|
+ <Navbar.MenuItem title="退出登录" />
|
|
|
+ </Navbar.Account>
|
|
|
+ }
|
|
|
+ aria={{
|
|
|
+ labelMainNavigation: "主导航",
|
|
|
+ labelSecondaryNavigation: "辅助导航",
|
|
|
+ labelAccount: "我的账户",
|
|
|
+ }}
|
|
|
+ />
|
|
|
+
|
|
|
+ {/* 搜索对话框 */}
|
|
|
+ <SearchModal
|
|
|
+ isOpen={isSearchOpen}
|
|
|
+ onClose={() => setIsSearchOpen(false)}
|
|
|
+ />
|
|
|
+ </>
|
|
|
);
|
|
|
}
|