|
|
@@ -1,5 +1,5 @@
|
|
|
// import { Button } from "@contentful/f36-components";
|
|
|
-import { GearSixIcon } from "@contentful/f36-icons";
|
|
|
+import { GearSixIcon, ListIcon } from "@contentful/f36-icons";
|
|
|
import { useAuth } from "../../contexts/AuthContext";
|
|
|
import { useMenu } from "../../contexts/MenuContext";
|
|
|
import { useNavigate, useLocation } from "react-router-dom";
|
|
|
@@ -14,6 +14,7 @@ export default function Header() {
|
|
|
const navigate = useNavigate();
|
|
|
const location = useLocation();
|
|
|
const [hoveredMenu, setHoveredMenu] = useState<JeecgMenu | null>(null);
|
|
|
+ const [mobileMenuOpen, setMobileMenuOpen] = useState(false);
|
|
|
|
|
|
// 获取顶级菜单(一级菜单)
|
|
|
const topMenus = menus.filter((menu) => !menu.hidden && !menu.meta?.hideMenu);
|
|
|
@@ -45,21 +46,18 @@ export default function Header() {
|
|
|
|
|
|
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
|
|
|
- );
|
|
|
- if (firstChild) {
|
|
|
- const childPath = firstChild.path || "";
|
|
|
- console.log("Header: Navigating to first child", childPath);
|
|
|
- navigate(childPath);
|
|
|
- }
|
|
|
+
|
|
|
+ const hasChildren = menu.children && menu.children.length > 0;
|
|
|
+
|
|
|
+ // 如果有子菜单,只设置激活状态,不跳转
|
|
|
+ if (hasChildren) {
|
|
|
+ setActiveFirstMenu(menu);
|
|
|
+ setActiveSecondMenu(null);
|
|
|
+ // 不导航,让用户点击下拉菜单中的选项
|
|
|
} else {
|
|
|
// 没有子菜单,直接导航到该菜单
|
|
|
+ setActiveFirstMenu(menu);
|
|
|
+ setActiveSecondMenu(null);
|
|
|
const menuPath = menu.path || "";
|
|
|
console.log("Header: Navigating to menu", menuPath);
|
|
|
navigate(menuPath);
|
|
|
@@ -83,6 +81,82 @@ export default function Header() {
|
|
|
</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>
|
|
|
@@ -115,18 +189,20 @@ export default function Header() {
|
|
|
{hasChildren && hoveredMenu?.id === menu.id && (
|
|
|
<div className={styles.dropdown}>
|
|
|
{menu.children
|
|
|
- ?.filter((child) => !child.hidden && !child.meta?.hideMenu)
|
|
|
+ ?.filter(
|
|
|
+ (child) => !child.hidden && !child.meta?.hideMenu
|
|
|
+ )
|
|
|
.map((child) => {
|
|
|
// 构建完整路径
|
|
|
const childPath = child.path || "";
|
|
|
const parentPath = menu.path || "";
|
|
|
-
|
|
|
+
|
|
|
// 如果子路径是绝对路径,直接使用
|
|
|
// 否则拼接父路径
|
|
|
- const fullPath = childPath.startsWith("/")
|
|
|
- ? childPath
|
|
|
+ const fullPath = childPath.startsWith("/")
|
|
|
+ ? childPath
|
|
|
: `${parentPath}/${childPath}`;
|
|
|
-
|
|
|
+
|
|
|
return (
|
|
|
<button
|
|
|
key={child.id}
|
|
|
@@ -152,31 +228,8 @@ export default function Header() {
|
|
|
</div>
|
|
|
|
|
|
<div className={styles.right}>
|
|
|
- {/* Environment Selector */}
|
|
|
- {/* <div className={styles.envSelector}>
|
|
|
- <Button variant="positive" size="small">
|
|
|
- Blank
|
|
|
- </Button>
|
|
|
- <Button
|
|
|
- variant="secondary"
|
|
|
- size="small"
|
|
|
- className={styles.branchButton}
|
|
|
- >
|
|
|
- master 🔽
|
|
|
- </Button>
|
|
|
- </div> */}
|
|
|
-
|
|
|
{/* Action Icons */}
|
|
|
<div className={styles.actions}>
|
|
|
- {/* <button className={styles.iconButton} title="Search">
|
|
|
- 🔍
|
|
|
- </button>
|
|
|
- <button className={styles.iconButton} title="Help">
|
|
|
- ❓
|
|
|
- </button>
|
|
|
- <button className={styles.iconButton} title="Notifications">
|
|
|
- 🔔
|
|
|
- </button> */}
|
|
|
<button className={styles.iconButton} title="Settings">
|
|
|
<GearSixIcon size="small" />
|
|
|
</button>
|