以下代码都为部分核心代码
一.根据不同的登录用户,返回不同的权限列表 ,以下是三种不同用户限权列表
const pression = {
//超级管理员
BigAdmin: [{
key: "screen",
icon: "FileOutlined",
label: "数据图表",
},
{
key: "users",
icon: "UserOutlined",
label: "用户列表",
},
{
key: "food",
icon: "PieChartOutlined",
label: "食谱",
},
{
key: "discuss",
icon: "CommentOutlined",
label: "评论",
}],
//普通管理员
Admin: [{
key: "screen",
icon: "FileOutlined",
label: "数据图表",
},
{
key: "food",
icon: "PieChartOutlined",
label: "食谱",
}, {
key: "discuss",
icon: "CommentOutlined",
label: "评论",
}],
//普通用户
commont: [
{
key: "screen",
icon: "FileOutlined",
label: "数据图表",
}
]
}
二.实现动态菜单
用户登录后先将权限列表存入本地浏览器,再跳转到系统页面,并在首次加载时候获取本地权限列表数据,进行动态渲染
TypeScript
//假如登录的是超级管理员,对应的权限列表应如下,这里使用了ant design的菜单组件,为了方便所以使用符合该菜单组件的数据结构
BigAdmin: [{
key: "screen",
icon: "FileOutlined",
label: "数据图表",
},
{
key: "users",
icon: "UserOutlined",
label: "用户列表",
},
{
key: "food",
icon: "PieChartOutlined",
label: "食谱",
},
{
key: "discuss",
icon: "CommentOutlined",
label: "评论",
}],
//-----------------
import * as Icons from '@ant-design/icons';
//--自定义组件将字符串转化为对应的组件---------
const ICONFONT = (props: any) => {
const { icon } = props
//动态菜单不能确定需要那些icon,全部引入作为Icons
return React.createElement(Icons[icon])
}
const [Enums, setenum] = useState<any>()
const AdminInfo = useSelector((state: any) => state.AdminStore.AdminInfo)
useEffect(() => {
//登录后,系统首页获取本地权限列表,因为这里使用了ant design的icon,需要将字符串转化为对应的组件,这里封装的自定义组件
let items = JSON.parse(JSON.stringify(AdminInfo.pression))
items.map((item: any) => {
item.icon = item.icon && <ICONFONT icon={item.icon}></ICONFONT>
})
setenum([...items])
}, [])
//在菜单组件中使用
<Menu selectedKeys={['screen']} mode="inline" items={Enums} onClick={chooose} />
三.实现动态路由
先把动态路由和静态路由分离出来,在路由配置文件中,这里需要进行动态添加的路由为 path: "/backstage"下的子路由,需要给他一个name,后续方便查找
TypeScript
//这里也很重要,页面加载时候,先判断本地是否存入了权限列表
import { Admin } from "@/utils/local"
//获取本地存储的信息
const rs = Admin.get_Admin()
//路由懒加载
const lazyComponent = (Component: FC) => {
return <Suspense fallback={Skeletons}>
<Component />
</Suspense>
}
//静态路由
const routers = [
{
path: '/',
element: <Navigate to="/home" />
},
{
path: "/",
element: <App />,
children: [
{
path: "/home",
element: lazyComponent(Home),
},
{
path: "/category",
element: lazyComponent(About),
children: [
{
path: "/category",
element: <Navigate to={'/category/mainfood'}></Navigate>
},
{
path: "mainfood",
element: lazyComponent(Mainfood)
},
{
path: "bake",
element: lazyComponent(Bake)
},
{
path: "beverage",
element: lazyComponent(Beverage)
},
{
path: "soup",
element: lazyComponent(Soup)
}
]
}, {
path: "/user",
element: lazyComponent(Test),
}, {
path: "/edituser",
element: lazyComponent(EeditUser)
},
{
path: "/editfood",
element: lazyComponent(Edit_Food)
}
]
},
{
path: "/detaile/:id",
element: lazyComponent(Datile)
},
{
path: "/login",
element: <Login></Login>
},
{
//需要动态添加的路由在此处
path: "/backstage",
element: lazyComponent(Back),
name: "back",
children: [
{
path: "/backstage",
element: <Navigate to="/backstage/screen"></Navigate>
},
{
path: "screen",
element: lazyComponent(Screen)
},
]
},
{
path: "*",
element: <Error></Error>
}
]
//准备需要添加的动态路由,将添加到 path: "/backstage"的子路由下,
//这里的id必须必须填写,而且必须唯一,
//如:id:'4-2',4表示在一级路由下索引为4的路由,2表示在该路由下子路由的索引
const dtRoute = [
{
path: "users",
element: lazyComponent(Users),
id: '4-2'
},
{
path: "food",
id: '4-3',
element: lazyComponent(Food)
},
{
path: "discuss",
id: '4-4',
element: lazyComponent(Discuss)
}
]
定义一个动态添加路由的函数
TypeScript
const dtRoute = [
{
path: "users",
element: lazyComponent(Users),
id: '4-2'
},
{
path: "food",
id: '4-3',
element: lazyComponent(Food)
},
{
path: "discuss",
id: '4-4',
element: lazyComponent(Discuss)
}
]
const route = createBrowserRouter(routers)
//这里需要导出函数,在登录后调用,pression即为后端返回的列表
export function addrouter(pression: any[]) {
//找到需要动态添加的路由,name为back,即为之前的path:"/backstage"路由
const baseRouter = route.routes.find((rt: any) => rt.name === "back")
console.log(baseRouter)
if (pression.length !== 0) {
//判断传入的权限列表是否在动态路由之中,有则追加进去
for (const use of pression) {
for (const rt of dtRoute) {
if (rt.path === use.key) {
baseRouter?.children?.push(rt)
} else {
}
}
}
} else {
//如果传入的为空表示,退出登录,更新路由,这里取的最前两个静态路由,即删除动态路由
baseRouter?.children?.splice(2)
}
}
//这里之前已提到,
import { Admin } from "@/utils/local"
const rs = Admin.get_Admin()
//-------页面刷新时候会重新执行路由文件---访问其它动态路由的时候会丢失找不到,
//因为动态路由只在登录的时候进行了添加,而页面刷新会让整个路由重新执行,而不会重新动态添加--
//所以刷新的时候要判断本地是否存储了动态路由信息,并再次添加
if (rs) {
addrouter(rs.pression)
}
在redux中使用动态添加路由的函数
TypeScript
import { createSlice } from "@reduxjs/toolkit";
import { Admin } from "@/utils/local";
import { Admin_Loing } from '@/apis/admin/admin'
import { message } from "antd"
import type { AppDispatch } from "../index"
//导入动态添加路由的函数
import { addrouter } from "@/router"
const AdminStore = createSlice({
name: "RootStore",
initialState: {
AdminInfo: Admin.get_Admin() || {
pression: [],
user: {}
}
},
reducers: {
//定义登录方法,需要在登录后动态添加路由
Login: (state, { payload }) => {
state.AdminInfo = payload
Admin.set_Admin(payload)
//调用动态添加路由的函数,并传入登录后传来的权限列表
addrouter(payload.pression)
},
GoOut: (state) => {
state.AdminInfo = {
pression: [],
user: {}
}
Admin.remove_Admin()
addrouter([])
},
},
})
//导出Login
export const { Login, GoOut } = AdminStore.actions
//,登录是异步的,所以定义异步登录方法
const axios_admin_login = (data: any) => {
return async (dispatch: AppDispatch) => {
const res = await Admin_Loing(data)
if (res.status == 200) {
//登录成功后调用Login,并传入包含权限列表的数据
dispatch(Login(res.data))
message.success("登录成功")
return true
} else {
return false
}
}
}
//最后导出异步登录,在登录界面使用
export { axios_admin_login }
export default AdminStore.reducer