From 56ca28233a84c5aa3ca93cae266b2d008ea348e1 Mon Sep 17 00:00:00 2001
From: lbq <1065079612@qq.com>
Date: 星期三, 24 十二月 2025 09:54:13 +0800
Subject: [PATCH] Web页面优化

---
 rsf-admin/src/layout/MyMenu.jsx |  394 +++++++++++++++++++++++++++++++++++--------------------
 1 files changed, 249 insertions(+), 145 deletions(-)

diff --git a/rsf-admin/src/layout/MyMenu.jsx b/rsf-admin/src/layout/MyMenu.jsx
index 347a4f5..9e6b379 100644
--- a/rsf-admin/src/layout/MyMenu.jsx
+++ b/rsf-admin/src/layout/MyMenu.jsx
@@ -1,168 +1,272 @@
 import React, { useState, useEffect, useMemo, Suspense } from "react";
 import {
-    useTranslate,
-    DashboardMenuItem,
-    MenuItemLink,
-    Menu,
-    useSidebarState,
-    usePermissions,
-} from 'react-admin';
-import { useLocation } from 'react-router-dom';
-import { Box } from '@mui/material';
-import SubMenu from './SubMenu';
-import SettingsIcon from '@mui/icons-material/Settings';
-import DashboardIcon from '@mui/icons-material/Dashboard';
-import HorizontalRuleIcon from '@mui/icons-material/HorizontalRule';
-import PersonIcon from '@mui/icons-material/Person';
-import * as Icons from '@mui/icons-material';
+  useTranslate,
+  DashboardMenuItem,
+  MenuItemLink,
+  Menu,
+  useSidebarState,
+  usePermissions,
+} from "react-admin";
+import { useLocation } from "react-router-dom";
+import { Box } from "@mui/material";
+import SubMenu from "./SubMenu";
+import SettingsIcon from "@mui/icons-material/Settings";
+import DashboardIcon from "@mui/icons-material/Dashboard";
+import HorizontalRuleIcon from "@mui/icons-material/HorizontalRule";
+import PersonIcon from "@mui/icons-material/Person";
+import * as Icons from "@mui/icons-material";
 
 const getIconComponent = (iconStr) => {
-    return Icons[iconStr] || HorizontalRuleIcon;
+  return Icons[iconStr] || HorizontalRuleIcon;
 };
 
 export const MyMenu = ({ dense = false }) => {
-    const [state, setState] = useState({});
-    const translate = useTranslate();
-    const location = useLocation();
-    const [sidebarIsOpen] = useSidebarState();
-    const { isPending, permissions } = usePermissions();
+  const [state, setState] = useState({});
+  const translate = useTranslate();
+  const location = useLocation();
+  const [sidebarIsOpen] = useSidebarState();
+  const { isPending, permissions } = usePermissions();
 
-    useEffect(() => {
-        // default open sub menu
-        const defaultExpandMenu = ["menu.system", "menu.dispatcher", "menu.equipment"];
-        permissions?.forEach(item => {
-            if (defaultExpandMenu.includes(item.name)) {
-                setState(state => ({ ...state, [item.route]: true }));
-            }
-        });
-    }, [permissions]);
+  useEffect(() => {
+    // default open sub menu
+    const defaultExpandMenu = [
+      "menu.system",
+      "menu.dispatcher",
+      "menu.equipment",
+    ];
+    permissions?.forEach((item) => {
+      if (defaultExpandMenu.includes(item.name)) {
+        setState((state) => ({ ...state, [item.route]: true }));
+      }
+    });
+  }, [permissions]);
 
-    useEffect(() => {
-        // expand this parent menu
-        const currentPath = location.pathname;
-        const parentRoutes = findParentRoutes(currentPath, permissions)
-        for (const parentRoute of parentRoutes) {
-            setState(state => ({ ...state, [parentRoute]: true }));
-        }
+  useEffect(() => {
+    // expand this parent menu
+    const currentPath = location.pathname;
+    const parentRoutes = findParentRoutes(currentPath, permissions);
+    for (const parentRoute of parentRoutes) {
+      setState((state) => ({ ...state, [parentRoute]: true }));
+    }
+  }, [location.pathname]);
 
-    }, [location.pathname]);
+  const handleToggle = (menu) => {
+    setState((state) => ({ ...state, [menu]: !state[menu] }));
+  };
 
-    const handleToggle = (menu) => {
-        setState(state => ({ ...state, [menu]: !state[menu] }));
-    };
+  const getIcon = (iconStr) => {
+    const IconComponent = getIconComponent(iconStr);
+    if (IconComponent) {
+      return <IconComponent />;
+    } else {
+      return <KeyboardArrowDownIcon />;
+    }
+  };
 
-    const getIcon = (iconStr) => {
-        const IconComponent = getIconComponent(iconStr);
-        if (IconComponent) {
-            return <IconComponent />;
-        }
-    };
+  // 妫�鏌ヨ彍鍗曟槸鍚﹁閫変腑
+  const isSelected = (component) => {
+    if (!component) return false;
+    const currentPath = location.pathname.replace("/", "");
+    return currentPath === component;
+  };
 
-    const generateMenu = (permissions) => {
-        return permissions.map((node) => {
-            if (node.children) {
+  // 妫�鏌ョ埗绾ц彍鍗曟槸鍚︽湁瀛愯彍鍗曡閫変腑
+  const hasSelectedChild = (node) => {
+    if (!node.children) return false;
+    return node.children.some(child => {
+      if (child.children) {
+        return hasSelectedChild(child);
+      }
+      return isSelected(child.component);
+    });
+  };
+
+  // 鍦� MyMenu 缁勪欢鐨� generateMenu 鍑芥暟涓紝纭繚 MenuItemLink 涔熷乏瀵归綈
+  const generateMenu = (permissions) => {
+    return permissions.map((node) => {
+        if (node.children) {
+            const selected = isSelected(node.component) || hasSelectedChild(node);
+            return (
+                <SubMenu
+                    key={node.id}
+                    handleToggle={() => handleToggle(node.route)}
+                    isOpen={state[node.route]}
+                    name={node.name}
+                    dense={dense}
+                    icon={getIcon(node.icon)}
+                    isSelected={selected}
+                >
+                    {generateMenu(node.children)}
+                </SubMenu>
+            );
+        } else {
+            if (node.component) {
+                const selected = isSelected(node.component);
+                // 鍦� generateMenu 鍑芥暟涓殑 MenuItemLink 閮ㄥ垎
                 return (
-                    <SubMenu
+                    <MenuItemLink
                         key={node.id}
-                        handleToggle={() => handleToggle(node.route)}
-                        isOpen={state[node.route]}
-                        name={node.name}
+                        to={node.component}
+                        state={{ _scrollToTop: true }}
+                        primaryText={translate(node.name)}
+                        leftIcon={getIcon(node.icon)}
                         dense={dense}
-                        icon={getIcon(node.icon)}
-                    >
-                        {generateMenu(node.children)}
-                    </SubMenu>
+                        sx={{
+                            backgroundColor: selected ? 'rgba(25, 118, 210, 0.08) !important' : 'transparent',
+                            color: selected ? '#1976d2 !important' : 'text.secondary',
+                            '&:hover': {
+                                backgroundColor: selected ? 'rgba(25, 118, 210, 0.12) !important' : 'rgba(0, 0, 0, 0.04)',
+                            },
+                            borderLeft: 'none',
+                            borderRadius: '4px',
+                            margin: '2px 8px',
+                            width: 'calc(100% - 16px)',
+                            transition: 'all 0.2s ease-in-out',
+                            
+                            // 缂╁皬鏁翠綋闂磋窛
+                            padding: '6px 8px', // 鍑忓皯鍐呰竟璺�
+                            minHeight: '36px', // 绋嶅井鍑忓皬楂樺害
+                            
+                            '& .RaMenuItemLink-icon': {
+                                color: selected ? '#1976d2 !important' : 'text.secondary',
+                                minWidth: '32px !important', // 缂╁皬鍥炬爣鍖哄煙瀹藉害
+                                marginRight: '4px', // 缂╁皬鍥炬爣鍜屾枃瀛楅棿璺�
+                                display: 'flex',
+                                alignItems: 'center',
+                                justifyContent: 'center', // 鍥炬爣灞呬腑鏄剧ず
+                            },
+                            
+                            fontWeight: selected ? 600 : 400,
+                            
+                            // 纭繚鏂囧瓧鍐呭宸﹀榻�
+                            '& .MuiListItemText-root': {
+                                margin: 0,
+                                '& .MuiTypography-root': {
+                                    textAlign: 'left',
+                                    justifyContent: 'flex-start',
+                                    fontSize: '0.875rem', // 绋嶅井鍑忓皬瀛椾綋澶у皬
+                                    lineHeight: '1.3',
+                                }
+                            },
+                        }}
+                    />
                 );
-            } else {
-                if (node.component) {
-                    return (
-                        <MenuItemLink
-                            key={node.id}
-                            to={node.component} // correspond to Resource.name
-                            state={{ _scrollToTop: true }}
-                            // primaryText={translate(`resources.orders.name`, {
-                            //     smart_count: 2,
-                            // })}
-                            primaryText={node.name}
-                            leftIcon={getIcon(node.icon)}
-                            dense={dense}
-                        />
-                    );
-                }
             }
-        });
-    };
+        }
+    });
+  };
 
-    return isPending
-        ? (<div>Waiting for permissions...</div>) :
-        (
-            <Box
-                sx={{
-                    width: sidebarIsOpen ? 200 : 50,
-                    marginTop: 1,
-                    marginBottom: 1,
-                    transition: theme =>
-                        theme.transitions.create('width', {
-                            easing: theme.transitions.easing.sharp,
-                            duration: theme.transitions.duration.leavingScreen,
-                        }),
-                }}
-            >
-                <Menu.Item
-                    to="/dashboard"
-                    primaryText="menu.dashboard"
-                    leftIcon={<DashboardIcon />}
-                />
-                {permissions && (generateMenu(permissions))}
-                {/* <Menu.ResourceItems /> */}
-                <Menu.Item
-                    to="/settings"
-                    primaryText="menu.settings"
-                    leftIcon={<PersonIcon />}
-                />
-            </Box>
-        )
-}
+  // 妫�鏌ュ浐瀹氳彍鍗曟槸鍚﹂�変腑
+  const isDashboardSelected = location.pathname === '/dashboard';
+  const isSettingsSelected = location.pathname === '/settings';
+
+  return isPending ? (
+    <div>Waiting for permissions...</div>
+  ) : (
+    <Box
+      sx={{
+        width: sidebarIsOpen ? 200 : 50,
+        marginTop: 1,
+        marginBottom: 1,
+        transition: (theme) =>
+          theme.transitions.create("width", {
+            easing: theme.transitions.easing.sharp,
+            duration: theme.transitions.duration.leavingScreen,
+          }),
+        // 鑿滃崟瀹瑰櫒鏍峰紡
+        '& .MuiMenuItem-root': {
+          boxSizing: 'border-box',
+        }
+      }}
+    >
+      <Menu.Item
+        to="/dashboard"
+        primaryText="menu.dashboard"
+        leftIcon={<DashboardIcon />}
+        sx={{
+          backgroundColor: isDashboardSelected ? 'rgba(25, 118, 210, 0.08) !important' : 'transparent',
+          color: isDashboardSelected ? '#1976d2 !important' : 'text.secondary',
+          '&:hover': {
+            backgroundColor: isDashboardSelected ? 'rgba(25, 118, 210, 0.12) !important' : 'rgba(0, 0, 0, 0.04)',
+          },
+          borderLeft: isDashboardSelected ? '3px solid #1976d2' : '3px solid transparent',
+          borderRadius: '0 4px 4px 0',
+          margin: '1px 0',
+          width: '100%',
+          transition: 'all 0.2s ease-in-out',
+          '& .MuiListItemIcon-root': {
+            color: isDashboardSelected ? '#1976d2 !important' : 'text.secondary',
+            minWidth: 40,
+          }
+        }}
+      />
+      {permissions && generateMenu(permissions)}
+      <Menu.Item
+        to="/settings"
+        primaryText="menu.settings"
+        leftIcon={<PersonIcon />}
+        sx={{
+          backgroundColor: isSettingsSelected ? 'rgba(25, 118, 210, 0.08) !important' : 'transparent',
+          color: isSettingsSelected ? '#1976d2 !important' : 'text.secondary',
+          '&:hover': {
+            backgroundColor: isSettingsSelected ? 'rgba(25, 118, 210, 0.12) !important' : 'rgba(0, 0, 0, 0.04)',
+          },
+          borderLeft: isSettingsSelected ? '3px solid #1976d2' : '3px solid transparent',
+          borderRadius: '0 4px 4px 0',
+          margin: '1px 0',
+          width: '100%',
+          transition: 'all 0.2s ease-in-out',
+          '& .MuiListItemIcon-root': {
+            color: isSettingsSelected ? '#1976d2 !important' : 'text.secondary',
+            minWidth: 40,
+          }
+        }}
+      />
+    </Box>
+  );
+};
 
 const findParentRoutes = (pathname, permissions) => {
-    if (!pathname || !permissions) {
-        return [];
-    }
-    const findMenu = (currentPermissions, path) => {
-        for (const item of currentPermissions) {
-            if (item.component === path) {
-                return item;
-            }
-            if (item.children) {
-                const found = findMenu(item.children, path);
-                if (found) {
-                    return found;
-                }
-            }
-        }
-        return null;
-    };
-
-    const findParentRoutesRecursive = (item, allPermissions) => {
-        const parentRoutes = [];
-        let current = item;
-        while (current && current.parentId) {
-            const parent = allPermissions.find(permission => permission.id === current.parentId);
-            if (parent) {
-                parentRoutes.push(parent.route);
-                current = parent;
-            } else {
-                break;
-            }
-        }
-
-        return parentRoutes;
-    };
-
-    const currentMenu = findMenu(permissions, pathname.replace("/", ""));
-    if (currentMenu) {
-        return findParentRoutesRecursive(currentMenu, permissions);
-    }
-
+  if (!pathname || !permissions) {
     return [];
+  }
+  const findMenu = (currentPermissions, path) => {
+    for (const item of currentPermissions) {
+      if (item.component === path) {
+        return item;
+      }
+      if (item.children) {
+        const found = findMenu(item.children, path);
+        if (found) {
+          return found;
+        }
+      }
+    }
+    return null;
+  };
+
+  const findParentRoutesRecursive = (item, allPermissions) => {
+    const parentRoutes = [];
+    let current = item;
+    while (current && current.parentId) {
+      const parent = allPermissions.find(
+        (permission) => permission.id === current.parentId,
+      );
+      if (parent) {
+        parentRoutes.push(parent.route);
+        current = parent;
+      } else {
+        break;
+      }
+    }
+
+    return parentRoutes;
+  };
+
+  const currentMenu = findMenu(permissions, pathname.replace("/", ""));
+  if (currentMenu) {
+    return findParentRoutesRecursive(currentMenu, permissions);
+  }
+
+  return [];
 };
\ No newline at end of file

--
Gitblit v1.9.1