制造企业订单交付评估:物料缺口自动核算与AI化实现
2026/5/7 18:38:28
{ "token": "xxx", "perms": [ "user:list", "user:add", "order:view" ] }import { defineStore } from 'pinia' interface UserData { id: string; name: string; role: string; } interface PermissionState { token: string userData: UserData | null perms: string[] } export const usePermissionStore = defineStore('permission', { state: (): PermissionState => ({ token: '', userData: null, perms: [] }), actions: { setToken(token: string) { this.token = token }, setUserData(user: UserData) { this.userData = user }, setPerms(perms: string[]) { this.perms = perms }, clear() { this.token = '' this.userData = null this.perms = [] }, // 根据 perms 过滤路由 filterRoutes(routes: any[]) { return routes.filter(route => { if (!route.meta?.perms) return true return route.meta.perms.some((p: string) => this.perms.includes(p)) }).map(route => { if (route.children) { route.children = this.filterRoutes(route.children) } return route }) }, // 菜单生成 generateMenus(routes: any[]) { return this.filterRoutes(routes) } } })export const asyncRoutes = [ { path: '/user', name: 'User', component: () => import('@/views/user/index.vue'), meta: { title: '用户管理', perms: ['user:list'] }, children: [ { path: 'add', name: 'UserAdd', component: () => import('@/views/user/add.vue'), meta: { title: '新增用户', perms: ['user:add'] } } ] }, { path: '/order', name: 'Order', component: () => import('@/views/order/index.vue'), meta: { title: '订单管理', perms: ['order:view'] } } ]import { usePermissionStore } from '@/stores/permission' const permissionStore = usePermissionStore() const { token, userData, perms } = await api.login() permissionStore.setToken(token) permissionStore.setUserData(userData) permissionStore.setPerms(perms) // 生成菜单 const menuList = permissionStore.generateMenus(asyncRoutes)<template> <button v-if="hasPerm('user:add')">新增用户</button> </template> <script setup lang="ts"> import { usePermissionStore } from '@/stores/permission' const permissionStore = usePermissionStore() function hasPerm(code: string) { return permissionStore.perms.includes(code) } </script>import { createSlice, PayloadAction } from '@reduxjs/toolkit' interface UserData { id: string name: string role: string } interface PermissionState { token: string userData: UserData | null perms: string[] } const initialState: PermissionState = { token: '', userData: null, perms: [] } const permissionSlice = createSlice({ name: 'permission', initialState, reducers: { setToken: (state, action: PayloadAction<string>) => { state.token = action.payload }, setUserData: (state, action: PayloadAction<UserData>) => { state.userData = action.payload }, setPerms: (state, action: PayloadAction<string[]>) => { state.perms = action.payload }, clear: (state) => { state.token = '' state.userData = null state.perms = [] } } }) export const { setToken, setUserData, setPerms, clear } = permissionSlice.actions export default permissionSlice.reducerimport { configureStore } from '@reduxjs/toolkit' import permissionReducer from './permissionSlice' export const store = configureStore({ reducer: { permission: permissionReducer } }) export type RootState = ReturnType<typeof store.getState> export type AppDispatch = typeof store.dispatchimport { lazy } from 'react' export const asyncRoutes = [ { path: '/user', element: lazy(() => import('@/pages/User')), meta: { title: '用户管理', perms: ['user:list'] }, children: [ { path: 'add', element: lazy(() => import('@/pages/UserAdd')), meta: { title: '新增用户', perms: ['user:add'] } } ] }, { path: '/order', element: lazy(() => import('@/pages/Order')), meta: { title: '订单管理', perms: ['order:view'] } } ]import { useSelector } from 'react-redux' import { RootState } from './store' const perms = useSelector((state: RootState) => state.permission.perms) function filterRoutes(routes: any[], perms: string[]) { return routes.filter(route => { if (!route.meta?.perms) return true return route.meta.perms.some((p: string) => perms.includes(p)) }).map(route => { if (route.children) route.children = filterRoutes(route.children, perms) return route }) } const allowedRoutes = filterRoutes(asyncRoutes, perms)// 菜单渲染 <Menu> {allowedRoutes.map(r => ( <Menu.Item key={r.path}>{r.meta.title}</Menu.Item> ))} </Menu> // 按钮权限 {perms.includes('user:add') && <Button>新增用户</Button>}流程:
{ path: '/user', name: 'User', meta: { title: '用户管理', perms: ['user:view'] } }就算你用后台动态返回菜单,也不能防止用户:
真正的安全是 API 权限验证
所以前端写死菜单 + 权限过滤 = 大厂最佳体验
| 措施 | 目的 |
|---|---|
| 前端菜单过滤 | 体验层:让用户不看到没权限的东西 |
| 后端接口校验 | 安全层:真正的权限防护(不可绕过) |
| 功能点 | Vue3 实现 | React 实现 |
|---|---|---|
| 菜单写死 | asyncRoutes | routes.tsx |
| 权限点存储 | Pinia | Zustand/Redux |
| 菜单过滤 | filterRoutes() | filterRoutes() |
| 按钮权限 | v-if hasPerm() | perms.includes() |
| 安全关键 | 必须后端接口校验 | 必须后端接口校验 |
菜单从后台返回(动态菜单),超级管理员添加菜单,登录后通过用户权限返回菜单的方式
流程: