refactor: remove old demo pages and static menu logic
1. 删除大量旧的示例页面、组件示例和静态菜单配置 2. 新增菜单扁平数组转树形结构工具函数 3. 重构菜单加载逻辑,改为从后端动态获取并格式化 4. 新增全局权限检查方法和自定义权限指令 5. 优化侧边栏菜单路由跳转逻辑,自动跳转第一个有权限的子页面 6. 移除路由中对旧demo模块的引用
This commit is contained in:
601
docs/menu-permission-migration.md
Normal file
601
docs/menu-permission-migration.md
Normal file
@@ -0,0 +1,601 @@
|
||||
# Menu 权限迁移方案
|
||||
|
||||
> 参照项目:`D:\code\company\SCTMES_MES_V5\vue-app`(以下简称"源项目")
|
||||
|
||||
---
|
||||
|
||||
## 一、源项目(SCTMES)Menu 权限架构
|
||||
|
||||
### 1.1 后端接口
|
||||
|
||||
```
|
||||
GET system_settings/menu_configuration/menu/all
|
||||
```
|
||||
|
||||
API 文件:[src/api/system_settings/menu_configuration/menu.js](file:///D:\code\company\SCTMES_MES_V5\vue-app\src\api\system_settings\menu_configuration\menu.js)
|
||||
|
||||
返回当前用户可见的**扁平数组**,字段如下:
|
||||
|
||||
| 字段 | 类型 | 说明 |
|
||||
|------|------|------|
|
||||
| `menu_id` | number | 主键,用于构建父子关系 |
|
||||
| `parent_id` | number | 父节点 ID,`0` 表示顶栏根节点 |
|
||||
| `url` | string | 前端路由路径,如 `/system_settings/user_management/user` |
|
||||
| `name` | string | 菜单显示名称 |
|
||||
| `icon` | string | 图标名(FontAwesome) |
|
||||
| `is_navi` | number | 是否作为导航菜单展示(1=是) |
|
||||
| `status` | number | 启用/禁用 |
|
||||
| `type` | string | 菜单类型 |
|
||||
| `params` | string | 额外参数 |
|
||||
| `sort` | number | 排序 |
|
||||
| `remark` | string | 备注 |
|
||||
|
||||
### 1.2 菜单数据全链路
|
||||
|
||||
```
|
||||
登录成功 → account.login() → dispatch('load')
|
||||
│
|
||||
▼
|
||||
account.load()
|
||||
│
|
||||
...加载 user/theme/transition/page/menu/size/color...
|
||||
│
|
||||
▼
|
||||
dispatch('d2admin/menu/sourceDataLoad')
|
||||
│
|
||||
┌────────────┴────────────┐
|
||||
│ │
|
||||
localStorage localStorage 为空
|
||||
有缓存 且有 token
|
||||
│ │
|
||||
▼ ▼
|
||||
直接读缓存 GET /menu/all 后端接口
|
||||
│
|
||||
▼
|
||||
写入 localStorage 缓存
|
||||
│
|
||||
└────────────┬────────────┘
|
||||
│
|
||||
▼
|
||||
menu.install(this, sourceData)
|
||||
```
|
||||
|
||||
**Store 文件**:[src/store/modules/d2admin/modules/menu.js](file:///D:\code\company\SCTMES_MES_V5\vue-app\src\store\modules\d2admin\modules\menu.js)
|
||||
|
||||
```js
|
||||
state: {
|
||||
header: [],
|
||||
aside: [],
|
||||
asideCollapse: setting.menu.asideCollapse,
|
||||
asideTransition: setting.menu.asideTransition,
|
||||
authKey: {}, // ← 权限字典 { '/path': '菜单名', ... }
|
||||
sourceData: [] // ← 菜单源数据(来自后端或本地缓存)
|
||||
}
|
||||
```
|
||||
|
||||
### 1.3 `sourceDataLoad` action 核心逻辑
|
||||
|
||||
```js
|
||||
async sourceDataLoad({ state, dispatch }) {
|
||||
// 1. 优先从 localStorage 读取缓存
|
||||
state.sourceData = await dispatch('d2admin/db/get', {
|
||||
dbName: 'database',
|
||||
path: '$menu.sourceData',
|
||||
defaultValue: [],
|
||||
user: true
|
||||
}, { root: true })
|
||||
|
||||
// 2. 缓存为空且已登录 → 调后端获取
|
||||
if (!state.sourceData.length && util.cookies.get('token')) {
|
||||
const res = await getMenuAll(null)
|
||||
state.sourceData = res.data || []
|
||||
await dispatch('d2admin/db/set', {
|
||||
dbName: 'database',
|
||||
path: '$menu.sourceData',
|
||||
value: state.sourceData,
|
||||
user: true
|
||||
}, { root: true })
|
||||
}
|
||||
|
||||
// 3. 处理为 header/aside/authKey
|
||||
menu.install(this, state.sourceData)
|
||||
}
|
||||
```
|
||||
|
||||
### 1.4 `menu.install()` — 扁平数据 → 菜单树 + 权限字典
|
||||
|
||||
**文件**:[src/menu/index.js](file:///D:\code\company\SCTMES_MES_V5\vue-app\src\menu\index.js)
|
||||
|
||||
```js
|
||||
export default {
|
||||
install(vm, source) {
|
||||
// 1. 构建权限字典 { '/path': '菜单名' }
|
||||
vm.commit('d2admin/menu/headerAuth', source)
|
||||
|
||||
// 2. 扁平数据转为 {header: [], aside: []} 树结构
|
||||
const { header, aside } = getMenuData(source)
|
||||
|
||||
// 3. 写入 store → 触发菜单渲染
|
||||
vm.commit('d2admin/menu/headerSet', header)
|
||||
vm.commit('d2admin/menu/asideSet', aside)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
`getMenuData()` 核心流程:
|
||||
|
||||
```
|
||||
source (扁平数组)
|
||||
│
|
||||
├── 过滤: is_navi !== 1 的跳过
|
||||
│
|
||||
├── parent_id === 0 → 推入 header[]
|
||||
│
|
||||
└── 全部节点 → 推入 aside[]
|
||||
│
|
||||
▼
|
||||
util.formatDataToTree(aside) { menu_id ↔ parent_id }
|
||||
│
|
||||
▼
|
||||
树形结构 aside
|
||||
```
|
||||
|
||||
**`headerAuth` mutation**:遍历 `source`,将每个有 `url` 的节点写入 `authKey[url] = name`:
|
||||
|
||||
```js
|
||||
headerAuth(state, source) {
|
||||
let auth = {}
|
||||
source.forEach(value => {
|
||||
if (value.url) {
|
||||
auth[value.url] = value.name
|
||||
}
|
||||
})
|
||||
state.authKey = auth
|
||||
}
|
||||
```
|
||||
|
||||
### 1.5 权限控制系统
|
||||
|
||||
**`$permission()` 方法** — 定义在 [src/plugin/d2admin/index.js](file:///D:\code\company\SCTMES_MES_V5\vue-app\src\plugin\d2admin\index.js#L50):
|
||||
|
||||
```js
|
||||
Vue.prototype.$permission = (value, type = 'menu') => {
|
||||
let path = ''
|
||||
const auth = store.state.d2admin.menu.authKey
|
||||
|
||||
switch (type) {
|
||||
case 'menu': // 直接用 URL 查
|
||||
path = value
|
||||
break
|
||||
case 'router': // 路由 name → URL
|
||||
path = value.name.replace(/-/g, '/')
|
||||
path.slice(0, 1) !== '/' && (path = '/' + path)
|
||||
break
|
||||
}
|
||||
|
||||
return !!(path && Object.prototype.hasOwnProperty.call(auth, path))
|
||||
}
|
||||
```
|
||||
|
||||
**`v-permission` 指令** — 定义在 [src/main.js](file:///D:\code\company\SCTMES_MES_V5\vue-app\src\main.js#L72-L78):
|
||||
|
||||
```js
|
||||
Vue.directive('permission', {
|
||||
bind: (el, binding) => {
|
||||
if (!Vue.prototype.$permission(binding.value)) {
|
||||
el.parentNode ? el.parentNode.removeChild(el) : el.style.display = 'none'
|
||||
}
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
使用示例:
|
||||
```html
|
||||
<el-button v-permission="'/system_settings/user_management/user'">新增用户</el-button>
|
||||
```
|
||||
|
||||
### 1.6 菜单导航权限过滤
|
||||
|
||||
**Menu Mixin** — [src/layout/header-aside/components/mixin/menu.js](file:///D:\code\company\SCTMES_MES_V5\vue-app\src\layout\header-aside\components\mixin\menu.js):
|
||||
|
||||
点击顶栏一级菜单时,不直接跳转,而是查找第一个有权限的子路由:
|
||||
|
||||
```js
|
||||
getRouterAuthPath(index, indexPath) {
|
||||
// 子路由直接访问
|
||||
if (index === '/index' || !indexPath || indexPath.length > 1) {
|
||||
this.$router.push({ path: index })
|
||||
return
|
||||
}
|
||||
|
||||
// 查找一级路由下第一个有权限的子路由
|
||||
let router = null
|
||||
for (const value of frameInRoutes) {
|
||||
if (value.path === index) {
|
||||
router = value.children
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if (!router) {
|
||||
this.$router.push({ path: index })
|
||||
return
|
||||
}
|
||||
|
||||
for (const value of router) {
|
||||
const newPath = index + '/' + value.path
|
||||
if (this.$permission(newPath)) {
|
||||
this.$router.push({ path: newPath })
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 1.7 路由 — 全部静态注册
|
||||
|
||||
路由在 [src/router/routes.js](file:///D:\code\company\SCTMES_MES_V5\vue-app\src\router\routes.js) 中**全部静态定义**,不使用 `addRoute`。
|
||||
|
||||
路由守卫([src/router/index.js](file:///D:\code\company\SCTMES_MES_V5\vue-app\src\router\index.js))仅判断 token 是否存在,不校验具体路由权限。权限控制在**菜单渲染**和**菜单点击时**完成。
|
||||
|
||||
### 1.8 main.js 启动流程
|
||||
|
||||
[src/main.js](file:///D:\code\company\SCTMES_MES_V5\vue-app\src\main.js#L108-L137)
|
||||
|
||||
```js
|
||||
created() {
|
||||
// 仅初始化页面池,不再设置静态菜单
|
||||
this.$store.commit('d2admin/page/init', frameInRoutes)
|
||||
// 静态菜单设置已注释:
|
||||
// this.$store.commit('d2admin/menu/headerSet', menuHeader)
|
||||
// this.$store.commit('d2admin/search/init', menuHeader)
|
||||
},
|
||||
mounted() {
|
||||
this.$store.dispatch('d2admin/account/load') // → sourceDataLoad → 获取菜单
|
||||
},
|
||||
watch: {
|
||||
// $route.matched 切换侧边栏的 watch 已注释
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 二、mes-ui 当前现状 vs 目标
|
||||
|
||||
| 模块 | mes-ui 现状 | 源项目(目标) |
|
||||
|------|-----------|---------------|
|
||||
| 菜单数据来源 | `src/menu/modules/*.js` 静态硬编码 | 后端 `GET /menu/all` + localStorage 缓存 |
|
||||
| `sourceDataLoad` | 仅 localStorage 读取,不参与菜单构建 | localStorage 兜底 + 调后端 → `menu.install()` |
|
||||
| 权限字典 | 无 | `authKey: { '/path': 'name' }` |
|
||||
| 菜单树构建 | 编译时 `supplementPath()` | 运行时 `getMenuData()` + `formatDataToTree()` |
|
||||
| 权限检查 | 无 | `$permission()` + `v-permission` 指令 |
|
||||
| 菜单点击 | 直接 `$router.push` | `getRouterAuthPath()` 查找首个有权限子路由 |
|
||||
| main.js 菜单初始化 | `created` 中 `headerSet(menuHeader)` | 注释掉静态菜单,由 `mounted` 中的 `load()` 触发 |
|
||||
| 路由注册 | 全部静态 | 全部静态(一致) |
|
||||
| 路由守卫 | 仅 token 检查 | 仅 token 检查(一致) |
|
||||
|
||||
---
|
||||
|
||||
## 三、迁移计划
|
||||
|
||||
### 阶段 1️⃣:约定后端接口格式
|
||||
|
||||
后端接口:`GET /api/menu/all`(或复用源项目的 `system_settings/menu_configuration/menu/all`)
|
||||
|
||||
返回格式(扁平数组):
|
||||
|
||||
```json
|
||||
{
|
||||
"code": 0,
|
||||
"data": [
|
||||
{ "menu_id": 1, "parent_id": 0, "url": "/index", "name": "首页", "icon": "home", "is_navi": 1 },
|
||||
{ "menu_id": 2, "parent_id": 0, "url": "/production_master_data", "name": "生产主数据", "icon": "cogs", "is_navi": 1 },
|
||||
{ "menu_id": 3, "parent_id": 2, "url": "/production_master_data/factory_model/factory_area", "name": "工厂区域", "icon": "map-marker", "is_navi": 1 }
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### 阶段 2️⃣:新增/改造 API 层
|
||||
|
||||
**新增** `src/api/menu.js`(参照源项目 [menu.js](file:///D:\code\company\SCTMES_MES_V5\vue-app\src\api\system_settings\menu_configuration\menu.js)):
|
||||
|
||||
```js
|
||||
import { request } from '@/api/_service'
|
||||
|
||||
export function getMenuAll(data) {
|
||||
return request({
|
||||
url: 'menu/all',
|
||||
method: 'get',
|
||||
params: {
|
||||
method: 'system_settings_menu_configuration_menu_all',
|
||||
platform: 'background',
|
||||
...data
|
||||
}
|
||||
})
|
||||
}
|
||||
```
|
||||
|
||||
### 阶段 3️⃣:改造 `menu.js` Store
|
||||
|
||||
**改造** [src/store/modules/d2admin/modules/menu.js](file:///d:\code\mes\mes-ui\src\store\modules\d2admin\modules\menu.js)
|
||||
|
||||
| 改动 | 内容 |
|
||||
|------|------|
|
||||
| state | 新增 `authKey: {}` |
|
||||
| `sourceDataLoad` action | 改为:localStorage 兜底 → 调后端 → `menu.install()` |
|
||||
| mutations | 新增 `headerAuth(state, source)` → 构建 `authKey` 字典 |
|
||||
|
||||
```js
|
||||
// 关键变更 — sourceDataLoad:
|
||||
async sourceDataLoad({ state, commit, dispatch }) {
|
||||
// 1. 先读 localStorage 缓存
|
||||
state.sourceData = await dispatch('d2admin/db/get', {
|
||||
dbName: 'database',
|
||||
path: '$menu.sourceData',
|
||||
defaultValue: [],
|
||||
user: true
|
||||
}, { root: true })
|
||||
|
||||
// 2. 缓存为空且已登录 → 调后端
|
||||
if (!state.sourceData.length && util.cookies.get('token')) {
|
||||
const res = await getMenuAll()
|
||||
state.sourceData = res || []
|
||||
await dispatch('d2admin/db/set', {
|
||||
dbName: 'database',
|
||||
path: '$menu.sourceData',
|
||||
value: state.sourceData,
|
||||
user: true
|
||||
}, { root: true })
|
||||
}
|
||||
|
||||
// 3. 构建菜单树 + 权限字典
|
||||
menuUtil.install(this, state.sourceData)
|
||||
}
|
||||
```
|
||||
|
||||
### 阶段 4️⃣:改造 `src/menu/index.js` — 从静态导出变为工具模块
|
||||
|
||||
将 [src/menu/index.js](file:///d:\code\mes\mes-ui\src\menu\index.js) 从"导出静态菜单配置"改为"菜单处理工具"(参照源项目):
|
||||
|
||||
```js
|
||||
import util from '@/libs/util'
|
||||
|
||||
function getMenuData(arr) {
|
||||
let tree = { header: [], aside: [] }
|
||||
|
||||
arr.forEach(value => {
|
||||
if (!value.is_navi) return // 过滤非导航菜单
|
||||
|
||||
const menuItem = {
|
||||
path: value.url,
|
||||
title: value.name,
|
||||
icon: value.icon,
|
||||
type: value.type,
|
||||
params: value.params
|
||||
}
|
||||
|
||||
if (value.parent_id === 0) {
|
||||
tree.header.push({ ...menuItem })
|
||||
}
|
||||
|
||||
menuItem.menu_id = value.menu_id
|
||||
menuItem.parent_id = value.parent_id
|
||||
tree.aside.push(menuItem)
|
||||
})
|
||||
|
||||
// 扁平数组转树
|
||||
tree.aside = util.formatDataToTree(tree.aside)
|
||||
return tree
|
||||
}
|
||||
|
||||
export default {
|
||||
install(vm, source) {
|
||||
vm.commit('d2admin/menu/headerAuth', source)
|
||||
const { header, aside } = getMenuData(source)
|
||||
vm.commit('d2admin/menu/headerSet', header)
|
||||
vm.commit('d2admin/menu/asideSet', aside)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
原有的静态菜单模块(`src/menu/modules/demo-*.js`)可保留但不再被引用,后续按需清理。
|
||||
|
||||
### 阶段 5️⃣:增加权限控制基础设施
|
||||
|
||||
#### 5.1 `$permission` 方法
|
||||
|
||||
**改造** `src/plugin/d2admin/index.js`,新增:
|
||||
|
||||
```js
|
||||
Vue.prototype.$permission = (value, type = 'menu') => {
|
||||
let path = ''
|
||||
const auth = store.state.d2admin.menu.authKey
|
||||
|
||||
switch (type) {
|
||||
case 'menu':
|
||||
path = value
|
||||
break
|
||||
case 'router':
|
||||
path = value.name.replace(/-/g, '/')
|
||||
path.slice(0, 1) !== '/' && (path = '/' + path)
|
||||
break
|
||||
}
|
||||
|
||||
return !!(path && Object.prototype.hasOwnProperty.call(auth, path))
|
||||
}
|
||||
```
|
||||
|
||||
#### 5.2 `v-permission` 指令
|
||||
|
||||
**改造** `src/main.js`,新增:
|
||||
|
||||
```js
|
||||
Vue.directive('permission', {
|
||||
bind: (el, binding) => {
|
||||
if (!Vue.prototype.$permission(binding.value)) {
|
||||
el.parentNode ? el.parentNode.removeChild(el) : el.style.display = 'none'
|
||||
}
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
#### 5.3 菜单点击权限过滤
|
||||
|
||||
**改造** [src/layout/header-aside/components/mixin/menu.js](file:///d:\code\mes\mes-ui\src\layout\header-aside\components\mixin\menu.js),增加 `getRouterAuthPath` 方法:
|
||||
|
||||
```js
|
||||
import { frameInRoutes } from '@/router/routes'
|
||||
|
||||
methods: {
|
||||
handleMenuSelect(index, indexPath) {
|
||||
if (/^d2-menu-empty-\d+$/.test(index) || index === undefined) {
|
||||
this.$message.warning('临时菜单')
|
||||
} else if (/^https:\/\/|http:\/\//.test(index)) {
|
||||
util.open(index)
|
||||
} else {
|
||||
this.getRouterAuthPath(index, indexPath)
|
||||
}
|
||||
},
|
||||
|
||||
getRouterAuthPath(index, indexPath) {
|
||||
if (index === '/index' || !indexPath || indexPath.length > 1) {
|
||||
this.$router.push({ path: index })
|
||||
return
|
||||
}
|
||||
|
||||
let router = null
|
||||
for (const value of frameInRoutes) {
|
||||
if (value.path === index) {
|
||||
router = value.children
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if (!router) {
|
||||
this.$router.push({ path: index })
|
||||
return
|
||||
}
|
||||
|
||||
for (const value of router) {
|
||||
const newPath = index + '/' + value.path
|
||||
if (this.$permission(newPath)) {
|
||||
this.$router.push({ path: newPath })
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 阶段 6️⃣:改造 `main.js` 启动流程
|
||||
|
||||
**改造** [src/main.js](file:///d:\code\mes\mes-ui\src\main.js):
|
||||
|
||||
```js
|
||||
created() {
|
||||
this.$store.commit('d2admin/page/init', frameInRoutes)
|
||||
// 注释掉静态菜单设置:
|
||||
// this.$store.commit('d2admin/menu/headerSet', menuHeader)
|
||||
// this.$store.commit('d2admin/search/init', menuHeader)
|
||||
},
|
||||
mounted() {
|
||||
this.$store.commit('d2admin/releases/versionShow')
|
||||
this.$store.dispatch('d2admin/account/load') // → sourceDataLoad → 获取后端菜单
|
||||
this.$store.commit('d2admin/ua/get')
|
||||
this.$store.dispatch('d2admin/fullscreen/listen')
|
||||
},
|
||||
watch: {
|
||||
// 注释掉 $route.matched 侧边栏切换(由 store.aside 直接驱动)
|
||||
// '$route.matched': { ... }
|
||||
}
|
||||
```
|
||||
|
||||
### 阶段 7️⃣:增加 `util.formatDataToTree` 工具函数
|
||||
|
||||
**改造** `src/libs/util.js`,新增:
|
||||
|
||||
```js
|
||||
util.formatDataToTree = (data, key = 'menu_id', pid = 'parent_id') => {
|
||||
if (!data || data.length <= 0) return []
|
||||
|
||||
let map = {}
|
||||
data.forEach(value => { map[value[key]] = { ...value } })
|
||||
|
||||
let tree = []
|
||||
data.forEach(item => {
|
||||
const id = item[key]
|
||||
if (map[id][pid] && map[map[id][pid]]) {
|
||||
if (!map[map[id][pid]].children) {
|
||||
map[map[id][pid]].children = []
|
||||
}
|
||||
map[map[id][pid]].children.push(map[id])
|
||||
} else {
|
||||
tree.push(map[id])
|
||||
}
|
||||
})
|
||||
return tree
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 四、影响的文件清单
|
||||
|
||||
| 文件 | 改动类型 | 说明 |
|
||||
|------|----------|------|
|
||||
| `src/api/menu.js` | **新增** | 菜单 API(`getMenuAll`) |
|
||||
| `src/store/modules/d2admin/modules/menu.js` | **改造** | `sourceDataLoad` 加后端调用 + `headerAuth` mutation + `authKey` state |
|
||||
| `src/menu/index.js` | **重写** | 从静态配置改为 `install()` 工具模块 |
|
||||
| `src/plugin/d2admin/index.js` | **改造** | 新增 `$permission` 方法 |
|
||||
| `src/main.js` | **改造** | 注释静态菜单设置;新增 `v-permission` 指令 |
|
||||
| `src/layout/header-aside/components/mixin/menu.js` | **改造** | 增加 `getRouterAuthPath` 权限过滤 |
|
||||
| `src/libs/util.js` | **改造** | 新增 `formatDataToTree` 工具函数 |
|
||||
| `src/menu/modules/demo-*.js` | **后续可删** | 静态菜单数据不再使用,单独清理 |
|
||||
|
||||
---
|
||||
|
||||
## 五、完整时序图
|
||||
|
||||
```
|
||||
用户登录
|
||||
│
|
||||
▼
|
||||
login/index.vue → dispatch('d2admin/account/login')
|
||||
│
|
||||
▼
|
||||
account.js login action
|
||||
├── loginAdminUser() → 后端登录接口
|
||||
├── util.cookies.set() → 存 token/uuid
|
||||
├── dispatch('d2admin/user/set') → 存用户信息
|
||||
└── dispatch('load')
|
||||
│
|
||||
├── d2admin/user/load → 加载用户名
|
||||
├── d2admin/theme/load → 加载主题
|
||||
├── d2admin/transition/load → 加载过渡效果
|
||||
├── d2admin/page/openedLoad → 加载标签页
|
||||
├── d2admin/menu/asideLoad → 加载侧边栏收起设置
|
||||
├── d2admin/size/load → 加载全局尺寸
|
||||
├── d2admin/color/load → 加载颜色设置
|
||||
└── d2admin/menu/sourceDataLoad
|
||||
│
|
||||
├── 先读 localStorage 缓存
|
||||
├── 缓存空 → GET /api/menu/all 后端接口
|
||||
├── 写入 localStorage 缓存
|
||||
└── menu.install(this, sourceData)
|
||||
├── headerAuth → 构建 authKey 权限字典
|
||||
├── getMenuData → 扁平 → {header, aside} 树
|
||||
├── headerSet → 顶栏菜单渲染
|
||||
└── asideSet → 侧边栏菜单渲染
|
||||
│
|
||||
▼
|
||||
跳转到首页 → 菜单已按权限渲染
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 六、注意事项
|
||||
|
||||
1. **路由仍是静态注册的**:mes-ui 不需要改造成动态 `addRoute`,路由全部在 `router/routes.js` 中静态定义即可,权限通过菜单显示/隐藏 + `getRouterAuthPath` 控制。
|
||||
2. **不要删除源项目代码**:源项目在 `D:\code\company\SCTMES_MES_V5\vue-app`,仅作为参照,不对其做任何修改。
|
||||
3. **localStorage 缓存**是离线兜底:首次登录调后端,后续从缓存读取;注销时清空缓存。
|
||||
4. **`block` cookie**:已在登录页 `mounted` 中设为 `'false'`,注销时设为 `'true'`,用于控制错误日志是否弹出提示。
|
||||
Reference in New Issue
Block a user