aslan-admin/docs/问题排查记录-权限平台与开发服务地址.md
2026-04-24 10:11:46 +08:00

338 lines
11 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 问题排查记录:权限平台默认值与开发服务 Network 地址
## 记录时间
- 2026-04
## 背景
本次排查涉及四个独立但连续出现的问题:
1. 页面初始化时,`this.permissionsSysOriginPlatforms[0]` 偶发拿不到,导致默认平台没有赋值,后续 `renderData(true)` 不执行。
2. 本地执行 `npm run dev` 后,控制台一直显示 `Network: unavailable`,但同机器的其他项目可以正常显示类似 `http://192.168.0.115:5173/` 的局域网访问地址。
3. `src/views/user/user-table/index.vue` 页面刷新后,搜索栏中的系统默认选项为空,列表也没有自动刷出;只有再次手动点击搜索后,默认系统和列表才恢复正常。
4. 除首页外,进入其他页面后刷新浏览器会出现页面空白。
---
## 一、权限平台默认值偶发为空
### 现象
页面中存在如下初始化逻辑:
```js
const querySystem = this.permissionsSysOriginPlatforms[0];
if (!querySystem) {
return;
}
that.listQuery.sysOrigins = [querySystem.value];
that.renderData(true);
```
`this.permissionsSysOriginPlatforms[0]` 为空时,页面会直接返回,默认查询条件与首屏数据都不会初始化。
### 结论
`permissionsSysOriginPlatforms` 不是接口直接返回的平台数组,而是:
1. 按钮权限接口先返回权限别名列表。
2. 前端再用本地写死的 `sysOriginPlatforms` 常量进行过滤。
3. 过滤结果再写入 Vuex页面最终通过 getter 读取。
也就是说:
- 平台候选项来源于前端常量。
- 当前用户能拿到哪些平台,取决于按钮权限接口返回的权限别名。
### 关键代码链路
- 页面读取位置:`src/views/user/user-table/index.vue`
- getter 映射位置:`src/store/getters.js`
- Vuex 组装位置:`src/store/modules/user.js`
- 平台常量定义:`src/constant/origin.js`
- 按钮权限接口:`src/api/ops-system.js`
### 根因
根因是时序问题,不是页面本地代码本身写错:
1. 路由守卫中会调用 `store.dispatch('user/buttonPermissions')`
2. 但原逻辑没有 `await` 这个异步请求。
3. 页面已经进入并执行 `created()` 时,权限数据可能还没回填到 Vuex。
4. 于是 `this.permissionsSysOriginPlatforms[0]` 偶发为空。
另外还有一个补充前提:
- 当前前端常量里只有 `ATYOU` 一个系统平台,并且依赖权限 `query:atyou`
- 如果接口没有返回这个权限,过滤后数组本身也会为空。
### 已做处理
已在 `src/permission.js` 中将:
```js
store.dispatch('user/buttonPermissions')
```
修改为:
```js
await store.dispatch('user/buttonPermissions')
```
### 处理结果
调整后,路由放行前会先等待按钮权限加载完成,页面 `created()` 再读取 `permissionsSysOriginPlatforms` 时,不会再因为异步竞争导致首项为空。
### 后续建议
- 若后续仍出现空数组,需要优先检查接口返回是否包含 `query:atyou`
- 如果页面还需要进一步增强健壮性,可以在页面侧增加 watcher 或兜底默认逻辑,但当前首先应保证路由阶段权限已完成回填。
---
## 三、`user-table` 刷新后系统默认项为空
### 现象
`src/views/user/user-table/index.vue` 页面中:
- 首次进入时有概率搜索栏中的“系统”默认值为空。
- 刷新页面后更容易复现。
- 首屏列表没有自动加载。
- 用户手动点击一次搜索后,系统默认选项和列表又会恢复正常。
### 根因
这个问题本质上还是权限平台数据的异步时序问题,但触发点更具体:
1. 页面在 `created()` 中直接读取 `this.permissionsSysOriginPlatforms[0]`
2. 如果刷新时权限平台数据尚未回填,`querySystem` 就为空。
3. 原逻辑在判空后会直接 `return`,导致:
- `listQuery.sysOrigins` 没有默认值。
- `renderData(true)` 不执行。
- 页面其余初始化流程也被提前中断。
4. 等到权限数据后续回填后,页面没有额外 watcher 继续补初始化,所以只能依赖用户再次手动搜索。
### 已做处理
本次针对这个页面做了两层修复:
1.`src/permission.js` 中继续保留并确认:
```js
await store.dispatch('user/buttonPermissions')
```
确保路由放行前优先等待按钮权限加载完成。
2.`src/views/user/user-table/index.vue` 中增加页面级兜底:
- 新增 `hasInitializedSysOrigin` 标记,避免重复初始化。
- 监听 `permissionsSysOriginPlatforms`
- 当权限平台列表第一次变为有值时:
- 自动取第一项写入 `listQuery.sysOrigins`
- 自动执行 `renderData(true)`
3. 去掉 `created()` 中“拿不到系统平台就直接 return”的初始化方式避免首屏逻辑因为一次空值被永久跳过。
### 处理结果
调整后,即使刷新页面时 `permissionsSysOriginPlatforms` 比页面创建更晚回填:
- 搜索栏系统默认值也会在权限到达后自动补上。
- 首屏列表会自动触发加载。
- 不再需要用户手动点击搜索来恢复页面状态。
### 涉及文件
- `src/permission.js`
- `src/views/user/user-table/index.vue`
---
## 四、非首页刷新后页面白屏
### 现象
- 首页通常可以正常刷新。
- 进入其他菜单页后,直接刷新浏览器会出现页面空白。
- 这类问题在二级、三级动态路由页面上更容易复现。
### 根因
问题出在刷新时的动态路由注入时序:
1. 刷新页面后,应用会重新进入 `src/permission.js` 中的路由守卫。
2. 当前项目的很多页面并不是一开始就存在于静态路由中,而是依赖 `getMenus()` 返回菜单后再通过 `router.addRoutes()` 动态注入。
3. 原逻辑中虽然调用了 `getMenus()`,但没有等待菜单加载和动态路由注入真正完成,就提前执行了 `next()`
4. 当用户刷新的是非首页深层路由时,当前目标路由可能还没注册到 router 中,于是会出现匹配失败,最终表现为空白页。
首页之所以通常不受影响,是因为首页对应的是默认静态路由,不依赖后续动态注入。
### 已做处理
已将 `src/permission.js` 中刷新后的初始化流程改为严格串行:
```js
await store.dispatch('user/getInfo')
const grantRouters = await store.dispatch('user/getMenus')
if (grantRouters && grantRouters.length > 0) {
router.addRoutes(grantRouters)
}
store.dispatch('user/getUserDashboard')
await store.dispatch('user/buttonPermissions')
next({ ...to, replace: true })
```
### 修复要点
这次修复包含两个关键点:
1. 先等待 `getMenus()` 返回,再执行 `router.addRoutes(grantRouters)`
2. 动态路由注入完成后,不再直接 `next()`,而是使用:
```js
next({ ...to, replace: true })
```
这样会用当前目标地址重新进入一次匹配流程,确保刚注入的动态路由能够正确命中当前页面。
### 处理结果
调整后:
- 刷新非首页页面时,不会再因为动态路由尚未注册而白屏。
- 当前页面会在动态路由注入完成后重新匹配并正常进入。
### 涉及文件
- `src/permission.js`
---
## 二、开发服务 `Network: unavailable`
### 现象
执行 `npm run dev` 后输出类似:
```txt
App running at:
- Local: http://localhost:9528
- Network: unavailable
```
但同一台机器上的其他项目可以正常显示局域网访问地址。
### 初步排查结论
这不是项目无法启动,也不是没有局域网 IP而是 Vue CLI 选错了网卡。
通过系统网络信息确认:
- 当前机器存在可用私网地址:`192.168.0.115`
- 同时还存在一个虚拟网卡地址:`26.26.26.1`
### 根因
项目使用的是较老版本的 `@vue/cli-service 3.6.0`
该版本在生成 `Network` 地址时,会在内部调用 `address.ip()` 取一个 IPv4 地址,然后只接受以下私网地址范围:
- `10.x.x.x`
- `172.16.x.x``172.31.x.x`
- `192.168.x.x`
如果拿到的不是私网地址,就会直接将 `Network` 显示为 `unavailable`
本次机器环境中Vue CLI 优先拿到的是虚拟网卡 `26.26.26.1`,由于它不属于私网范围,因此被直接丢弃,最终控制台显示 `Network: unavailable`
### 关键代码位置
- 输出逻辑:`node_modules/@vue/cli-service/lib/commands/serve.js`
- 局域网地址选择逻辑:`node_modules/@vue/cli-service/lib/util/prepareURLs.js`
### 已尝试处理
#### 第一步
先在 `vue.config.js` 中增加:
```js
host: '0.0.0.0'
```
目的:
- 让 dev server 监听所有网卡,而不是只监听本机回环地址。
结果:
- 监听范围已放开。
-`Network` 仍然显示 `unavailable`
- 说明问题不在监听地址,而在 Vue CLI 的网卡识别逻辑。
#### 第二步
`vue.config.js` 中新增自定义 LAN IP 选择逻辑,替代 Vue CLI 默认的“随便取一个 IPv4”的方式。
处理思路:
1. 使用 `os.networkInterfaces()` 枚举网卡。
2. 只保留私网 IPv4。
3. 优先选择 `WLAN / Wi-Fi / Ethernet / 以太网` 这类常见真实网卡。
4. 排除 `TAP / VPN / Virtual / VMware / Hyper-V / Docker / vEthernet` 等虚拟网卡。
5. 将最终结果写入 `devServer.public`
### 当前配置结果
`vue.config.js` 已调整为:
- `host: '0.0.0.0'`
- `public: lanIp ? \`${lanIp}:${port}\` : undefined`
并增加了自动识别私网地址的辅助函数。
### 处理结果
此修改的目的不是单纯让服务“能访问”,而是:
1. 明确让服务监听所有网卡。
2. 明确告诉旧版 Vue CLI 应该向外公布哪个局域网地址。
3. 避免其误选虚拟网卡后把 Network 判成 unavailable。
### 备用方案
如果后续本机 IP 变化,或当前网络环境特殊,也可以临时手动指定:
```powershell
$env:DEV_SERVER_PUBLIC_IP='192.168.0.115'
npm run dev
```
或:
```powershell
npm run dev -- --public 192.168.0.115:9528
```
---
## 本次涉及的改动文件
- `src/permission.js`
- `src/views/user/user-table/index.vue`
- `vue.config.js`
- `docs/问题排查记录-权限平台与开发服务地址.md`
## 本次经验总结
1. 页面里出现“偶发空值”时,优先检查数据来源是不是异步回填,而不是只盯页面局部逻辑。
2. `permissionsSysOriginPlatforms` 这类字段要区分“候选项来源”和“最终权限结果来源”。
3. 老版本 Vue CLI 对网卡选择并不稳定,机器上存在 VPN、TAP、虚拟网卡时`Network: unavailable` 往往是地址识别问题,不代表 dev server 本身不可用。
4. 对局域网访问有明确需求的项目,最好显式配置 `devServer.host``devServer.public`,不要完全依赖工具链自动推断。
5. 对首屏依赖异步权限数据的页面,不应只在 `created()` 中做一次性读取;更稳妥的方式是配合 watcher 或显式初始化状态,在权限到达后自动补齐默认值与首屏请求。
6. 涉及动态路由的后台项目,刷新页面时必须保证“菜单加载 -> 路由注入 -> 再放行当前路由”是串行完成的,否则深层页面极易出现刷新白屏。