前端导航权限优化:如何根据后端权限控制导航项显示?
导航栏权限控制优化问题
我有一个导航栏组件,后端返回的权限集合存储在全局上下文的user对象中,可通过user.permissions获取,结构如下:
{addCustomer: false, editAndDeleteCustomer: false, showAllCustomers: false, addEmployee: false, editAndDeleteEmployee: false, …}
现在需要优化我的NavLinks组件,使其仅显示权限值为true对应的菜单项。例如当addCustomer为false时,“添加客户”菜单项不应出现在菜单列表里。
以下是我的实现代码:
const NavLinks = ({ ToggleSideBar }) => { const { user } = useAppContext(); const links = [ { id: 1, text: "panel", path: "/", icon: <IoBarChartSharp />, menuItems: ["panel"], }, { id: 2, text: "employee", path: ["add-employee", "all-employee"], icon: <MdQueryStats />, menuItems: [ "add new employee", "all employees", ], }, { id: 3, text: "customer", path: ["add-customer", "emp-customers", "all-customers"], icon: <FaWpforms />, menuItems: [ "add new customer", "my customers", "all customers", ], }, { id: 4, text: "التقارير", path: ["profile", "aw", "all-jobs", "add-job"], icon: <ImProfile />, menuItems: [ "add customer", "customer Roles", "all customers", "الموظف المثالي", ], }, ]; const [isDropdownOpen2, setIsDropdownOpen2] = useState(false); const [isDropdownOpen3, setIsDropdownOpen3] = useState(false); const toggleDropdown2 = () => { setIsDropdownOpen2(!isDropdownOpen2); }; const toggleDropdown3 = () => { setIsDropdownOpen3(!isDropdownOpen3); }; const [activeStatus, setActiveStatus] = useState({ firstnavcontainer: false, secondnavcontainer: false, thirdnavcontainer: false, }); const firstFunction = () => { setActiveStatus((prevState) => ({ firstnavcontainer: true, secondnavcontainer: false, thirdnavcontainer: false, })); }; const secondFunction = () => { setActiveStatus((prevState) => ({ ...prevState, firstnavcontainer: false, secondnavcontainer: true, thirdnavcontainer: false, })); console.log(activeStatus); }; const thirdFunction = () => { setActiveStatus((prevState) => ({ ...prevState, firstnavcontainer: false, secondnavcontainer: false, thirdnavcontainer: true, })); }; return ( <div className="nav-links"> {links.map((link) => { const { text, path, id, icon, menuItems } = link; return ( <> {/* first item */} {id === 1 && ( <div className="nav-item extra" onClick={firstFunction}> <ul className="dropdown-menu"> {menuItems.map((item, index) => ( <NavLink to={path[index]} className={({ isActive }) => isActive ? "dropdownmenu-item red-background" : "dropdownmenu-item" } > <div className="dropdown-itemcontainer"> <span className="dropdown-itemtext">{item}</span> </div> </NavLink> ))} </ul> </div> )} {/* second section */} {id === 2 && ( <div className="nav-item extra" onClick={secondFunction}> <button className={`dropdown-toggle ${ activeStatus.secondnavcontainer ? "red-background" : "" }`} > <div className="dropdownbtn"> <span className="icon">{icon}</span> <span>{text}</span> </div> <BsChevronDown className="dropdown-icon" onClick={toggleDropdown2} /> </button> {isDropdownOpen2 && ( <ul className="dropdown-menu"> {menuItems.map((item, index) => ( <NavLink to={path[index]} className={({ isActive }) => isActive ? "dropdownmenu-item active" : "dropdownmenu-item" } > <div className="dropdown-itemcontainer"> <span className="dropdown-itemtext">{item}</span> </div> </NavLink> ))} </ul> )} </div> )} {/* third section */} {id === 3 && ( <div className="nav-item extra" onClick={thirdFunction}> <button className={`dropdown-toggle ${ activeStatus.thirdnavcontainer ? "red-background" : "" }`} > <div className="dropdownbtn"> <span className="icon">{icon}</span> <span>{text}</span> </div> <BsChevronDown className="dropdown-icon" onClick={toggleDropdown3} /> </button> {isDropdownOpen3 && ( <ul className="dropdown-menu"> {menuItems.map((item, index) => ( <NavLink to={path[index]} className={({ isActive }) => isActive ? "dropdownmenu-item active" : "dropdownmenu-item" } > <div className="dropdown-itemcontainer"> <span className="dropdown-itemtext">{item}</span> </div> </NavLink> ))} </ul> )} </div> )} </> ); })} </div> ); }; export default NavLinks;
想请教如何实现权限过滤,另外有没有更优的权限控制方案?
解决方案与优化思路
一、基础实现:绑定权限并过滤菜单项
首先修改links结构,给每个子菜单项关联对应的权限字段,让权限和菜单配置一一对应:
const links = [ { id: 1, text: "panel", path: "/", icon: <IoBarChartSharp />, subItems: [{ label: "panel", permission: null }], // 无需权限的项设为null }, { id: 2, text: "employee", icon: <MdQueryStats />, subItems: [ { label: "add new employee", path: "add-employee", permission: "addEmployee" }, { label: "all employees", path: "all-employee", permission: "showAllEmployees" }, ], }, { id: 3, text: "customer", icon: <FaWpforms />, subItems: [ { label: "add new customer", path: "add-customer", permission: "addCustomer" }, { label: "my customers", path: "emp-customers", permission: null }, { label: "all customers", path: "all-customers", permission: "showAllCustomers" }, ], }, // 其他项同理修改 ];
然后添加过滤函数,只保留有权限或无需权限的菜单项:
const filterMenuItems = (items) => { if (!user?.permissions) return []; return items.filter(item => { // 无需权限的直接保留 if (item.permission === null) return true; // 校验权限字段是否为true return user.permissions[item.permission] === true; }); };
二、重构组件,消除重复代码
原组件中针对不同id写了大量重复的下拉逻辑,可将下拉导航项抽成独立组件,减少冗余:
// 抽离的下拉导航项组件 const DropdownNavItem = ({ text, icon, subItems, isActive, onToggle, isOpen }) => { const filteredSubItems = filterMenuItems(subItems); // 过滤后无项则不渲染该导航 if (filteredSubItems.length === 0) return null; return ( <div className="nav-item extra"> <button className={`dropdown-toggle ${isActive ? "red-background" : ""}`} onClick={onToggle} > <div className="dropdownbtn"> <span className="icon">{icon}</span> <span>{text}</span> </div> <BsChevronDown className="dropdown-icon" /> </button> {isOpen && ( <ul className="dropdown-menu"> {filteredSubItems.map((item, index) => ( <NavLink key={index} to={item.path} className={({ isActive }) => isActive ? "dropdownmenu-item active" : "dropdownmenu-item" } > <div className="dropdown-itemcontainer"> <span className="dropdown-itemtext">{item.label}</span> </div> </NavLink> ))} </ul> )} </div> ); };
主组件中用统一状态管理下拉展开和激活状态:
const NavLinks = ({ ToggleSideBar }) => { const { user } = useAppContext(); // 用对象管理所有下拉的展开状态,key为导航项id const [dropdownStates, setDropdownStates] = useState({}); const [activeNavId, setActiveNavId] = useState(null); const toggleDropdown = (id) => { setDropdownStates(prev => ({ ...prev, [id]: !prev[id] })); }; const filterMenuItems = (items) => { if (!user?.permissions) return []; return items.filter(item => { if (item.permission === null) return true; return user.permissions[item.permission] === true; }); }; return ( <div className="nav-links"> {links.map((link) => { const { id, text, icon, subItems, path } = link; // 处理带下拉的导航项 if (subItems) { return ( <DropdownNavItem key={id} text={text} icon={icon} subItems={subItems} isActive={activeNavId === id} onToggle={() => { toggleDropdown(id); setActiveNavId(id); }} isOpen={dropdownStates[id]} /> ); } // 处理无下拉的单链接 else { return ( <div key={id} className="nav-item extra" onClick={() => setActiveNavId(id)} > <ul className="dropdown-menu"> <NavLink to={path} className={({ isActive }) => isActive ? "dropdownmenu-item red-background" : "dropdownmenu-item" } > <div className="dropdown-itemcontainer"> <span className="dropdown-itemtext">{text}</span> </div> </NavLink> </ul> </div> ); } })} </div> ); };
三、更优的权限控制方案
- 路由权限守卫:仅隐藏菜单项不够,还要拦截用户直接输入URL访问无权限页面的行为,可自定义路由组件实现:
const ProtectedRoute = ({ children, requiredPermission }) => { const { user } = useAppContext(); if (!user?.permissions[requiredPermission]) { return <Navigate to="/" replace />; } return children; }; // 使用示例 <Route path="/add-customer" element={ <ProtectedRoute requiredPermission="addCustomer"> <AddCustomerPage /> </ProtectedRoute> } />
统一权限配置文件:把权限标识、菜单、路由配置放到单独的
permissions.js文件中,方便统一维护,避免组件内硬编码。动态路由生成:根据用户权限动态生成路由表,适合大型项目,减少手动路由配置的工作量。
权限变更监听:如果用户权限可能在会话中更新,监听全局上下文的
user变化,自动同步菜单和路由权限。
内容的提问来源于stack exchange,提问作者Mohammed M
相关产品推荐
相关产品推荐

