|
|
@@ -1,15 +1,19 @@
|
|
|
-import { Button } from "@contentful/f36-components";
|
|
|
+// import { Button } from "@contentful/f36-components";
|
|
|
+import { GearSixIcon } from "@contentful/f36-icons";
|
|
|
import { useAuth } from "../../contexts/AuthContext";
|
|
|
import { useMenu } from "../../contexts/MenuContext";
|
|
|
import { useNavigate, useLocation } from "react-router-dom";
|
|
|
-import { useEffect } from "react";
|
|
|
+import { useEffect, useState } from "react";
|
|
|
+import type { JeecgMenu } from "../../types/menu";
|
|
|
import styles from "./index.module.css";
|
|
|
|
|
|
export default function Header() {
|
|
|
const { menus, loading } = useAuth();
|
|
|
- const { activeFirstMenu, setActiveFirstMenu, setActiveSecondMenu } = useMenu();
|
|
|
+ const { activeFirstMenu, setActiveFirstMenu, setActiveSecondMenu } =
|
|
|
+ useMenu();
|
|
|
const navigate = useNavigate();
|
|
|
const location = useLocation();
|
|
|
+ const [hoveredMenu, setHoveredMenu] = useState<JeecgMenu | null>(null);
|
|
|
|
|
|
// 获取顶级菜单(一级菜单)
|
|
|
const topMenus = menus.filter((menu) => !menu.hidden && !menu.meta?.hideMenu);
|
|
|
@@ -19,7 +23,7 @@ export default function Header() {
|
|
|
const path = location.pathname;
|
|
|
const segments = path.split("/").filter(Boolean);
|
|
|
const firstPath = segments[0];
|
|
|
-
|
|
|
+
|
|
|
// 尝试匹配一级菜单
|
|
|
const matchedMenu = topMenus.find((menu) => {
|
|
|
const menuPath = menu.path || "";
|
|
|
@@ -31,16 +35,24 @@ export default function Header() {
|
|
|
setActiveFirstMenu(matchedMenu);
|
|
|
setActiveSecondMenu(null);
|
|
|
}
|
|
|
- }, [location.pathname, topMenus, activeFirstMenu, setActiveFirstMenu, setActiveSecondMenu]);
|
|
|
+ }, [
|
|
|
+ location.pathname,
|
|
|
+ topMenus,
|
|
|
+ activeFirstMenu,
|
|
|
+ setActiveFirstMenu,
|
|
|
+ setActiveSecondMenu,
|
|
|
+ ]);
|
|
|
|
|
|
- const handleMenuClick = (menu: typeof topMenus[0]) => {
|
|
|
+ const handleMenuClick = (menu: (typeof topMenus)[0]) => {
|
|
|
console.log("Header: Clicked menu", menu.name, menu.path);
|
|
|
setActiveFirstMenu(menu);
|
|
|
setActiveSecondMenu(null);
|
|
|
|
|
|
// 如果有子菜单,导航到第一个可见子菜单
|
|
|
if (menu.children && menu.children.length > 0) {
|
|
|
- const firstChild = menu.children.find((child) => !child.hidden && !child.meta?.hideMenu);
|
|
|
+ const firstChild = menu.children.find(
|
|
|
+ (child) => !child.hidden && !child.meta?.hideMenu
|
|
|
+ );
|
|
|
if (firstChild) {
|
|
|
const childPath = firstChild.path || "";
|
|
|
console.log("Header: Navigating to first child", childPath);
|
|
|
@@ -60,11 +72,17 @@ export default function Header() {
|
|
|
{/* 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"/>
|
|
|
+ <circle cx="12" cy="12" r="10" fill="#0066ff" />
|
|
|
+ <path
|
|
|
+ d="M8 12L11 15L16 9"
|
|
|
+ stroke="white"
|
|
|
+ strokeWidth="2"
|
|
|
+ strokeLinecap="round"
|
|
|
+ strokeLinejoin="round"
|
|
|
+ />
|
|
|
</svg>
|
|
|
</div>
|
|
|
-
|
|
|
+
|
|
|
{/* Card Navigation */}
|
|
|
{loading ? (
|
|
|
<div className={styles.loading}>加载中...</div>
|
|
|
@@ -72,15 +90,61 @@ export default function Header() {
|
|
|
<nav className={styles.menuCards}>
|
|
|
{topMenus.map((menu) => {
|
|
|
const isActive = activeFirstMenu?.id === menu.id;
|
|
|
-
|
|
|
+ const hasChildren = menu.children && menu.children.length > 0;
|
|
|
+
|
|
|
return (
|
|
|
- <button
|
|
|
+ <div
|
|
|
key={menu.id}
|
|
|
- className={`${styles.menuCard} ${isActive ? styles.menuCardActive : ""}`}
|
|
|
- onClick={() => handleMenuClick(menu)}
|
|
|
+ className={styles.menuCardWrapper}
|
|
|
+ onMouseEnter={() => hasChildren && setHoveredMenu(menu)}
|
|
|
+ onMouseLeave={() => setHoveredMenu(null)}
|
|
|
>
|
|
|
- <span className={styles.menuText}>{menu.meta?.title || menu.name}</span>
|
|
|
- </button>
|
|
|
+ <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}`;
|
|
|
+
|
|
|
+ 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>
|
|
|
@@ -89,18 +153,22 @@ export default function Header() {
|
|
|
|
|
|
<div className={styles.right}>
|
|
|
{/* Environment Selector */}
|
|
|
- <div className={styles.envSelector}>
|
|
|
+ {/* <div className={styles.envSelector}>
|
|
|
<Button variant="positive" size="small">
|
|
|
Blank
|
|
|
</Button>
|
|
|
- <Button variant="secondary" size="small" className={styles.branchButton}>
|
|
|
+ <Button
|
|
|
+ variant="secondary"
|
|
|
+ size="small"
|
|
|
+ className={styles.branchButton}
|
|
|
+ >
|
|
|
master 🔽
|
|
|
</Button>
|
|
|
- </div>
|
|
|
+ </div> */}
|
|
|
|
|
|
{/* Action Icons */}
|
|
|
<div className={styles.actions}>
|
|
|
- <button className={styles.iconButton} title="Search">
|
|
|
+ {/* <button className={styles.iconButton} title="Search">
|
|
|
🔍
|
|
|
</button>
|
|
|
<button className={styles.iconButton} title="Help">
|
|
|
@@ -108,9 +176,9 @@ export default function Header() {
|
|
|
</button>
|
|
|
<button className={styles.iconButton} title="Notifications">
|
|
|
🔔
|
|
|
- </button>
|
|
|
+ </button> */}
|
|
|
<button className={styles.iconButton} title="Settings">
|
|
|
- ⚙️
|
|
|
+ <GearSixIcon size="small" />
|
|
|
</button>
|
|
|
<button className={styles.iconButton} title="User">
|
|
|
<div className={styles.avatar}>👤</div>
|