wordpress网站手机端菜单栏,家装公司排名,珠海网站建设技术外包,wordpress 添加账号前言 本文将引导开发者从零基础开始#xff0c;运用vite、react、react-router、react-redux、Ant Design、less、tailwindcss、axios等前沿技术栈#xff0c;构建一个高效、响应式的后台管理系统。通过详细的步骤和实践指导#xff0c;文章旨在为开发者揭示如何利用这些技术… 前言 本文将引导开发者从零基础开始运用vite、react、react-router、react-redux、Ant Design、less、tailwindcss、axios等前沿技术栈构建一个高效、响应式的后台管理系统。通过详细的步骤和实践指导文章旨在为开发者揭示如何利用这些技术工具从项目构思到最终实现的全过程提供清晰的开发思路和实用的技术应用技巧。 项目gitee地址lbking666666/enqi-admin 本系列文章
使用vitereacttsAnt Design开发后台管理项目一使用vitereacttsAnt Design开发后台管理项目二使用vitereacttsAnt Design开发后台管理项目三使用vitereacttsAnt Design开发后台管理项目四使用vitereacttsAnt Design开发后台管理项目五
最近比较忙更新比较慢本章节添加面包屑和tab标签页及一些优化下一章节系统管理下的用户管理和角色管理
状态管理
在store文件夹下的reducers下新增menu.ts文件记录左侧菜单展开和点击及所有已经打开的状态
代码如下
import { createSlice } from reduxjs/toolkit;
import type { RootState } from /store/index.ts;
import type { MenuState, OpenedMenu } from /types/menu.ts;const initialState: MenuState {openMenuKey: [], // 展开的菜单栏的key 用于侧边栏selectMenuKey: [], // 选中菜单栏的key 用户侧边栏openedMenu: [], // 保存已经打开的菜单栏 用于顶部导航currentPath: , // 页面当前路径
};
export const menuSlice createSlice({name: menu,initialState,reducers: {setOpenKey(state, action) {const oldKeys state.openMenuKey;const keys action.payload;const isSame keys.every((item: string, index: number) item oldKeys[index]);const flag keys.length oldKeys.length isSame;if (flag) {return state;}return { ...state, openMenuKey: keys };},setCurrent(state, action) {const keys action.payload;if (state.selectMenuKey[0] keys[0]) {return state;}const openedMenu [...state.openedMenu];const useCurrentPath openedMenu.find((item: OpenedMenu) item.key keys[0]);return {...state,selectMenuKey: keys,currentPath: useCurrentPath?.path || /,};},addMenu(state, action) {const menuItem action.payload;if (state.openedMenu.find((item) item.path menuItem.path)) {return state;} else {const openedMenu [...state.openedMenu];const currentPath menuItem.path;openedMenu.push(menuItem);return { ...state, openedMenu, currentPath };}},removeMenu(state, action) {const keys action.payload;const openedMenu state.openedMenu.filter((i) !keys.includes(i.key));const currentPath openedMenu.length 0 ? openedMenu[openedMenu.length - 1].path : /;if (state.openedMenu.length openedMenu.length) {return state;}return { ...state, openedMenu, currentPath };},clearMenu(state) {const currentPath ;const openedMenu: OpenedMenu[] [];return { ...state, openedMenu, currentPath };},},
});export const { setCurrent, setOpenKey, addMenu, removeMenu, clearMenu } menuSlice.actions;
export const selectOpenKey (state: RootState) state.menu.openMenuKey;
export const selectMenu (state: RootState) state.menu.selectMenuKey;
export const selectOpenedMenu (state: RootState) state.menu.openedMenu;
export const selectCurrentPath (state: RootState) state.menu.currentPath;
export default menuSlice.reducer;types文件夹下新增menu.d.ts类型定义代码如下
// 菜单项属性
export interface MenuItemProps {id?: string;key: string;icon?: string;label: string;children?: MenuItemProps[];
}
export interface OpenedMenu {key: stringpath: stringtitle: string
}
// 菜单状态属性
export interface MenuState {openedMenu: OpenedMenu[]openMenuKey: string[]selectMenuKey: string[]currentPath: string
}
export interface MenuItem {[MENU_ICON]: string | null[MENU_KEEPALIVE]: string[MENU_KEY]: string | number[MENU_ORDER]?: number[MENU_PARENTKEY]: number | null[MENU_PATH]: string[MENU_TITLE]: string[MENU_CHILDREN]?: MenuList[MENU_PARENTPATH]?: string[MENU_SHOW]?: boolean | string[key: string]: any
}在store文件夹中的index.ts中引入menu
import { configureStore } from reduxjs/toolkit;
import globalReducer from ./reducers/global;
import menuReducer from ./reducers/menu;
//处理eslint报错
/* eslint-disable typescript-eslint/no-unused-vars */
const store configureStore({reducer: {global: globalReducer,menu: menuReducer,},
});// 从 store 本身推断 RootState 和 AppDispatch 类型
export type RootState ReturnTypetypeof store.getState;
// 推断类型{posts: PostsState, comments: CommentsState, users: UsersState}
export type AppDispatch typeof store.dispatch;export default store;
hook管理 因为之前的使用的文件名称是UseGlobal.hooks.ts这个并不是设置全局配置的有点歧义把原来UseGlobal.hooks.ts的内容复制一份放入到新建一个UseStore.hooks.ts文件 把之前组件中使用到UseGlobal.hooks.ts的地方抽离到hook中 UseGlobal.hooks.ts代码如下 import { useAppSelector, useAppDispatch } from /hooks/UseStore.hooks;
import { useCallback } from react;
import {setCollapsed,selectCollapsed,selectShowSetting,setShowSetting,selectColorPrimary,selectIsDark,selectIsRadius,setIsDark,setColorPrimary,setIsRadius,
} from /store/reducers/global;//获取当前菜单栏是否折叠状态
export const useIsCollapsed () useAppSelector(selectCollapsed);
//获取当前弹窗是否显示状态
export const useShowPoup () useAppSelector(selectShowSetting);
//获取当前主题颜色的值
export const useCurColor () useAppSelector(selectColorPrimary);
//获取当前主题是否是暗黑模式
export const useIsSelectdDark () useAppSelector(selectIsDark);
//获取当前主题是否是圆角
export const useIsSelectdRadius () useAppSelector(selectIsRadius);export const useDispatchGlobal () {const dispatch useAppDispatch();// 更改菜单栏的折叠状态const stateHandleCollapsed useCallback(() {dispatch(setCollapsed());}, [dispatch]);// 更新主题颜色const stateHandleColorPrimary useCallback((color: string) {dispatch(setColorPrimary(color));},[dispatch]);// 切换主题是否是暗黑模式const stateHandleIsDark useCallback(() {dispatch(setIsDark());},[dispatch]);// 切换主题是否是圆角const stateHandleIsRadius useCallback(() {dispatch(setIsRadius());},[dispatch]);// 更新是否显示设置弹窗const stateHandleShowPopup useCallback((isShow: boolean) {dispatch(setShowSetting(isShow));},[dispatch]);return {stateHandleCollapsed,stateHandleColorPrimary,stateHandleIsDark,stateHandleIsRadius,stateHandleShowPopup,};
};UseStore.hooks.ts代码如下
import { TypedUseSelectorHook, useDispatch, useSelector } from react-redux;
import type { RootState, AppDispatch } from /store/index;// 在整个应用程序中使用而不是简单的 useDispatch 和 useSelector
export const useAppDispatch: () AppDispatch useDispatch;
export const useAppSelector: TypedUseSelectorHookRootState useSelector;
面包屑和顶部tab的hook
hooks文件夹下新增UseMenu.hooks.ts文件把状态及hook的方法封装到这里代码如下
import { useAppSelector, useAppDispatch } from /hooks/UseStore.hooks;
import { useCallback } from react;
import type { OpenedMenu } from /types/menu.ts;
import {selectOpenKey,selectMenu,selectOpenedMenu,selectCurrentPath,setOpenKey,setCurrent,addMenu,removeMenu,clearMenu,
} from /store/reducers/menu;//获取当前菜单展开的key
export const useOpenKey () useAppSelector(selectOpenKey);//获取当前菜单
export const useMenu () useAppSelector(selectMenu);//获取当前菜单列表
export const useOpenedMenu () useAppSelector(selectOpenedMenu);//获取当前路径
export const useCurrentPath () useAppSelector(selectCurrentPath);export const useDispatchMenu () {const dispatch useAppDispatch();//修改菜单展开的keyconst stateChangeOpenKey useCallback((menu: string[]) {dispatch(setOpenKey(menu));},[dispatch]);//修改当前菜单const stateChangeCurrent useCallback((menu: string[]) {dispatch(setCurrent(menu));},[dispatch]);//添加菜单const stateAddMenu useCallback((menu: OpenedMenu) {dispatch(addMenu(menu));},[dispatch]);//删除菜单const stateRemoveMenu useCallback((menu: string) {dispatch(removeMenu(menu));},[dispatch]);//清空菜单const stateClearMenu useCallback(() {dispatch(clearMenu());}, [dispatch]);return {stateChangeOpenKey,stateChangeCurrent,stateAddMenu,stateRemoveMenu,stateClearMenu,};
};面包屑和顶部tab标签
面包屑
修改header.tsx文件使用antd的组件Breadcurmb根据当前useCurrentPath获取到当前url的路由地址进行分隔和组装Breadcurmb所需数据格式的组装
import React from react;
import { Button, Layout, theme, Flex, Breadcrumb } from antd;
import {HomeOutlined,MenuFoldOutlined,MenuUnfoldOutlined,SettingOutlined,
} from ant-design/icons;
import { useShowPoup, useDispatchGlobal } from /hooks/UseGlobal.hooks;
import { useCurrentPath } from /hooks/UseMenu.hooks;
import Setting from ./setting;
import { MenuItemProps } from /types/menu;
import { NavigateFunction } from react-router-dom;
const { Header } Layout;
interface AppSiderProps {menu: MenuItemProps[];collapsed: boolean;navigate:NavigateFunction;
}
const setMenuData (arr: MenuItemProps[], keys: string[]) {const menuList:Array{title:React.ReactNode,href?:string} [{ title: HomeOutlined /, href: / }];const subKey keys[0];const key keys[1];arr.forEach((item) {if (item.key subKey) {menuList.push({ title: {item.label} / });if (item.children) {item.children.forEach((child) {if (child.key key) {menuList.push({ title: {child.label} / });}});}}});return menuList;
};
const AppHeader: React.FCAppSiderProps ({ menu, collapsed,navigate }) {const {token: { colorBgContainer },} theme.useToken();const showPoup: boolean useShowPoup();const { stateHandleShowPopup, stateHandleCollapsed } useDispatchGlobal();const currentMenu useCurrentPath();const menuList setMenuData(JSON.parse(JSON.stringify(menu)),currentMenu.split(/));const handleLink (item: { href?: string }) () {if (item.href) {navigate(item.href)}};console.log(menu, currentMenu, menuList, menu);//设置按钮点击事件return (Header style{{ padding: 0, background: colorBgContainer }}Flex gapmiddle justifyspace-between aligncenterFlex justifyspace-between aligncenterButtontypetexticon{collapsed ? MenuUnfoldOutlined / : MenuFoldOutlined /}style{{fontSize: 16px,width: 64,height: 64,}}onClick{stateHandleCollapsed}/Breadcrumbitems{menuList}itemRender{(item) span onClick{handleLink(item)}{item.title}/span}//FlexButtontypeprimaryclassNamemr-4icon{SettingOutlined /}onClick{() {stateHandleShowPopup(true);}}//FlexSetting showPoup{showPoup} //Header);
};
export default AppHeader;tab标签
修改main.tsx文件使用antd的Tag组件根据状态中存储的所有打开的页面显示和添加关闭操作
import { Layout, theme, Flex, Divider, Tag } from antd;
import { useCallback, useEffect } from react;
import {useOpenedMenu,useCurrentPath,useDispatchMenu,
} from /hooks/UseMenu.hooks;
import { useIsCollapsed } from /hooks/UseGlobal.hooks;
import { NavigateFunction, Outlet } from react-router-dom;
import type { OpenedMenu } from /types/menu.ts;
import { HomeOutlined } from ant-design/icons;
const { Content } Layout;
interface AppMainProps {pathname: string;navigate: NavigateFunction;
}
const homeTag: OpenedMenu {key: home,path: /,title: 首页,
};
//获取路径的层级
const getPathParts (path: string): string[] path.replace(/, ).split(/);
const AppMain: React.FCAppMainProps ({ pathname, navigate }) {const {token: { colorBgContainer, borderRadiusLG,colorPrimaryBg, colorPrimary },} theme.useToken();const tabList useOpenedMenu();const currentMenu useCurrentPath();const isCIsCollapsed useIsCollapsed();const { stateChangeOpenKey, stateChangeCurrent, stateRemoveMenu } useDispatchMenu();// 点击tab时更新路径状态const handleTabClick (item: OpenedMenu) {navigate(item.path || /);stateChangeCurrent([item.key]);};const handleTabClose (key: string) {stateRemoveMenu(key);// 关闭当前tab并打开上一个const tabMenu tabList.filter((i) !key.includes(i.key));if (tabMenu.length 0) {navigate(/);stateChangeCurrent([home]);return;}const item tabMenu[tabMenu.length - 1];navigate(item.path || /);stateChangeCurrent([item.key]);};// 路径变化时更新菜单状态const onPathChange useCallback(() {const parts getPathParts(pathname);stateChangeOpenKey([parts[0]]);stateChangeCurrent([parts[1] || home]);}, [pathname, stateChangeOpenKey, stateChangeCurrent]);// 菜单展开/收起时更新路径状态useEffect(() {onPathChange();}, [pathname, isCIsCollapsed, onPathChange]);return (Divider style{{ margin: 0 }} /Flexgap0justifyflex-startclassNamebg-white pl-5 pr-5aligncenterTagbordered{false}icon{HomeOutlined /}onClick{() handleTabClick(homeTag)}style{{background: / currentMenu ? colorPrimaryBg : transparent,color: / currentMenu ? colorPrimary : rgba(0, 0, 0, 0.88),}}classNamecursor-pointer flex items-center pt-2 pb-2 pl-4 pr-4 text-base rounded-b-nonespan classNamemr-1{homeTag.title}/span/Tag/{tabList.mapReact.ReactNode((item) {return (Tagbordered{false}onClick{() {handleTabClick(item);}}key{item.key}closablestyle{{background: item.path currentMenu ? colorPrimaryBg : transparent,color:item.path currentMenu ? colorPrimary : rgba(0, 0, 0, 0.88),}}onClose{() handleTabClose(item.key)}classNamecursor-pointer flex items-center pt-2 pb-2 pl-4 text-base rounded-b-nonespan classNamemr-1{item.title}/span/Tag);})}/FlexContentstyle{{margin: 24px 16px,padding: 24,minHeight: 280,background: colorBgContainer,borderRadius: borderRadiusLG,}}Outlet //Content/);
};
export default AppMain;效果如下 优化
对已经完成的部分做一些代码的抽离和封装
1.主题颜色抽离及设置hook方法
types文件夹下新增color.d.ts文件 export interface color {name:string;value:string;
}
在src文件夹下新增utils文件夹创建文件color.ts export const colors [{name: 拂晓蓝,value: #1677ff,},{name: 薄暮,value: #5f80c7,},{name: 日暮,value: #faad14,},{name: 火山,value: #f5686f,},{name: 酱紫,value: #9266f9,},{name: 极光绿,value: #3c9,},{name: 极客蓝,value: #32a2d4,},
];修改setting.tsx文件
import React from react;
import { Button, Flex, Drawer, Space, Switch } from antd;
import { CloseOutlined, CheckOutlined } from ant-design/icons;
import {useCurColor,useIsSelectdDark,useIsSelectdRadius,useDispatchGlobal,
} from /hooks/UseGlobal.hooks;
import { colors } from /utils/color;
type AppSiderProps {showPoup: boolean;
};
const Setting: React.FCAppSiderProps ({ showPoup }) {//主题颜色const curColor: string useCurColor();//暗黑模式const isSelectdDark: boolean useIsSelectdDark();//圆角模式const isSelectdRadius: boolean useIsSelectdRadius();const {stateHandleColorPrimary,stateHandleIsDark,stateHandleIsRadius,stateHandleShowPopup,} useDispatchGlobal();const ColorItem: React.FC{ color: string; isSelectd: boolean } ({color,isSelectd,}) {if (isSelectd) {return (divclassNamew-6 h-6 flex justify-center items-center rounded cursor-pointer itemsstyle{{ background: color }}CheckOutlined style{{ color: #fff }} //div);} else {return (divclassNamew-6 h-6 flex justify-center items-center rounded cursor-pointer itemsstyle{{ background: color }}onClick{() stateHandleColorPrimary(color)}/div);}};return (Drawertitle设置width{300}closeIcon{false}open{showPoup}extra{SpaceButtontypetextonClick{() {stateHandleShowPopup(false);}}icon{CloseOutlined /}/Button/Space}div classNamemb-3 font-bold主题颜色/divFlex gapmiddle justifyspace-between aligncenter{colors.map((item) (ColorItemkey{item.value}color{item.value}isSelectd{curColor item.value}/))}/Flexdiv classNamemb-3 mt-3 font-bold主题模式/divdiv classNameflex justify-between mb-3div classNameflex gap-2span开启暗黑模式/span/divdiv classNameflex gap-2SwitchdefaultCheckedchecked{isSelectdDark}onChange{stateHandleIsDark}//div/divdiv classNameflex justify-betweendiv classNameflex gap-2span开启圆角主题/span/divdiv classNameflex gap-2SwitchdefaultCheckedchecked{isSelectdRadius}onChange{stateHandleIsRadius}//div/div/Drawer);
};export default Setting;2.布局组件hooks抽离和封装
layout文件夹下的index.tsx文件修改
import React, { useEffect, useState } from react;
import { Layout, ConfigProvider, theme } from antd;
import { useNavigate, useLocation } from react-router-dom;
import {useIsCollapsed,useCurColor,useIsSelectdDark,useIsSelectdRadius,
} from /hooks/UseGlobal.hooks;
import AppHeader from ./header;
import AppSider from ./sider;
import AppMain from ./main;
import { MenuItemProps } from /types/menu;
import { getMenu } from /api/menu;
const App: React.FC () {const collapsed: boolean useIsCollapsed();const isDark: boolean useIsSelectdDark();const isRadius: boolean useIsSelectdRadius();const themeColor: string useCurColor();// 菜单数据const [menu, setMenu] useState([] as MenuItemProps[]);const { pathname } useLocation();const navigate useNavigate();// 获取菜单数据useEffect(() {// 获取菜单数据const getData async () {const res await getMenu();const menuData res?.data as MenuItemProps[];// 设置菜单数据setMenu([...menuData]);};getData();}, []);// 简化返回内容的嵌套const appLayout (Layout classNameapp-layoutAppSider menu{menu} pathname{pathname} navigate{navigate} collapsed{collapsed} /LayoutAppHeader menu{menu} collapsed{collapsed} navigate{navigate} /AppMain pathname{pathname} navigate{navigate} //Layout/Layout);return (ConfigProvidertheme{{token: {colorPrimary: themeColor,borderRadius: isRadius ? 6 : 0,motion: true,},algorithm: isDark ? theme.darkAlgorithm : theme.defaultAlgorithm,}}{appLayout}/ConfigProvider);
};export default App;layout文件夹下的main.tsx组件文件修改
import { Layout, theme, Flex, Divider, Tag } from antd;
import { useCallback, useEffect } from react;
import {useOpenedMenu,useCurrentPath,useDispatchMenu,
} from /hooks/UseMenu.hooks;
import { useIsCollapsed } from /hooks/UseGlobal.hooks;
import { NavigateFunction, Outlet } from react-router-dom;
import type { OpenedMenu } from /types/menu.ts;
import { HomeOutlined } from ant-design/icons;
const { Content } Layout;
interface AppMainProps {pathname: string;navigate: NavigateFunction;
}
const homeTag: OpenedMenu {key: home,path: /,title: 首页,
};
//获取路径的层级
const getPathParts (path: string): string[] path.replace(/, ).split(/);
const AppMain: React.FCAppMainProps ({ pathname, navigate }) {const {token: { colorBgContainer, borderRadiusLG,colorPrimaryBg, colorPrimary },} theme.useToken();const tabList useOpenedMenu();const currentMenu useCurrentPath();const isCIsCollapsed useIsCollapsed();const { stateChangeOpenKey, stateChangeCurrent, stateRemoveMenu } useDispatchMenu();// 点击tab时更新路径状态const handleTabClick (item: OpenedMenu) {navigate(item.path || /);stateChangeCurrent([item.key]);};const handleTabClose (key: string) {stateRemoveMenu(key);// 关闭当前tab并打开上一个const tabMenu tabList.filter((i) !key.includes(i.key));if (tabMenu.length 0) {navigate(/);stateChangeCurrent([home]);return;}const item tabMenu[tabMenu.length - 1];navigate(item.path || /);stateChangeCurrent([item.key]);};// 路径变化时更新菜单状态const onPathChange useCallback(() {const parts getPathParts(pathname);stateChangeOpenKey([parts[0]]);stateChangeCurrent([parts[1] || home]);}, [pathname, stateChangeOpenKey, stateChangeCurrent]);// 菜单展开/收起时更新路径状态useEffect(() {onPathChange();}, [pathname, isCIsCollapsed, onPathChange]);return (Divider style{{ margin: 0 }} /Flexgap0justifyflex-startclassNamebg-white pl-5 pr-5aligncenterTagbordered{false}icon{HomeOutlined /}onClick{() handleTabClick(homeTag)}style{{background: / currentMenu ? colorPrimaryBg : transparent,color: / currentMenu ? colorPrimary : rgba(0, 0, 0, 0.88),}}classNamecursor-pointer flex items-center pt-2 pb-2 pl-4 pr-4 text-base rounded-b-nonespan classNamemr-1{homeTag.title}/span/Tag/{tabList.mapReact.ReactNode((item) {return (Tagbordered{false}onClick{() {handleTabClick(item);}}key{item.key}closablestyle{{background: item.path currentMenu ? colorPrimaryBg : transparent,color:item.path currentMenu ? colorPrimary : rgba(0, 0, 0, 0.88),}}onClose{() handleTabClose(item.key)}classNamecursor-pointer flex items-center pt-2 pb-2 pl-4 text-base rounded-b-nonespan classNamemr-1{item.title}/span/Tag);})}/FlexContentstyle{{margin: 24px 16px,padding: 24,minHeight: 280,background: colorBgContainer,borderRadius: borderRadiusLG,}}Outlet //Content/);
};
export default AppMain;layout文件夹下的menu.tsx组件文件修改
import React, { useCallback, useEffect } from react;
import { HomeOutlined, SettingOutlined, ShopOutlined } from ant-design/icons;
import { Menu } from antd;
import { MenuItemProps } from /types/menu;
import { NavigateFunction } from react-router-dom;
import { useOpenKey, useMenu, useDispatchMenu } from /hooks/UseMenu.hooks;
// 图标映射
const Icons {home: HomeOutlined,setting: SettingOutlined,shop: ShopOutlined,
};interface AppMenuProps {pathname:string;menu:MenuItemProps[];navigate:NavigateFunction;
}
// 获取图标组件
const IconByName: React.FC{ iconName: string } ({ iconName }) {// 获取图标组件const IconComponent Icons[iconName as keyof typeof Icons];// 返回图标组件return IconComponent ? IconComponent / : null;
};
// 查找菜单项
const findMenuByKey (arr: MenuItemProps[],key: string
): MenuItemProps | undefined {for (const item of arr) {if (item.key key) {return item;}if (item.children) {const found findMenuByKey(item.children, key);if (found) {return found;}}}return undefined;
};
// 获取路径
const getPathParts (path: string): string[] path.replace(/, ).split(/);
// 侧边栏
const AppMenu: React.FCAppMenuProps ({ menu,pathname,navigate }) {const openKeys useOpenKey();const currentMenu useMenu();const { stateChangeOpenKey: onOpenChange, stateAddMenu } useDispatchMenu();// 设置当前菜单const setTabMenu useCallback((keyPath: string[]) {const itemMenu: MenuItemProps | undefined findMenuByKey(menu,keyPath[1] as string);if (itemMenu) {stateAddMenu({key: itemMenu?.key,path: keyPath.join(/),title: itemMenu?.label,});}},[menu, stateAddMenu]);// 路由地址变化后设置当前菜单useEffect(() {const keyPath getPathParts(pathname);setTabMenu(keyPath);}, [pathname, setTabMenu]);// 点击菜单项const handleMenu ({ keyPath }: { keyPath: string[] }) {const routerPath: string keyPath.reverse().join(/);setTabMenu(keyPath);navigate(routerPath);};// 使用递归查找匹配的菜单项const menuData menu.map((item: MenuItemProps) {return {key: item.key,label: item.label,icon: item.icon ? IconByName iconName{item.icon} / : undefined,children: item.children?.map((child) ({key: child.key,label: child.label,})),};});return (MenuonClick{handleMenu}themedarkselectedKeys{currentMenu}onOpenChange{onOpenChange}openKeys{openKeys}modeinlineitems{menuData}/);
};export default AppMenu;layout文件夹下的sider.tsx文件修改
import React from react;
import { Layout } from antd;
import AppMenu from ./menu;
import { MenuItemProps } from /types/menu;
import { NavigateFunction } from react-router-dom;
const { Sider } Layout;interface AppSiderProps {pathname: string;menu: MenuItemProps[];navigate: NavigateFunction;collapsed: boolean;
}
// 侧边栏
const AppSider: React.FCAppSiderProps ({menu,collapsed,navigate,pathname,
}) {// 返回侧边栏return (Sider trigger{null} collapsible collapsed{collapsed}div classNamedemo-logo-vertical /AppMenu menu{menu} pathname{pathname} navigate{navigate} //Sider);
};export default AppSider;