This commit is contained in:
张成
2025-10-08 19:20:24 +08:00
parent 845658f193
commit 7e888970d3
11 changed files with 1726 additions and 68 deletions

View File

@@ -0,0 +1,387 @@
# 未登录接口调用问题修复
## 🔍 问题描述
### 错误现象
在用户未登录时,`sys_parameter/key` 接口被调用,因为没有 token 导致接口报错。
### 错误原因
`demo-project/src/main.js``mounted` 钩子中,无条件调用了 `getSysTitle` action
```javascript
mounted() {
AdminFramework.uiTool.setRem()
// ❌ 无论是否登录都调用
this.$store.dispatch('app/getSysTitle', {
defaultTitle: 'Demo 管理系统',
defaultLogo: ''
})
}
```
`getSysTitle` action 会调用后端接口:
- `paramSetupServer.getOne('sys_title')` - 获取系统标题
- `paramSetupServer.getOne('sys_logo')` - 获取系统 Logo
这些接口需要 token 认证,但在应用启动时用户可能还没登录,导致接口调用失败。
### 调用时机问题
```
应用启动
Vue 实例 mounted
调用 getSysTitle ← ❌ 此时可能未登录
调用后端接口
没有 token接口报错 ❌
```
## ✅ 解决方案
### 方案:只在已登录时调用接口
**核心思路**
1. 在调用 `getSysTitle` 前检查是否已登录(是否有 token
2. 已登录:调用接口获取系统标题
3. 未登录:直接使用默认标题
### 修改 1`demo-project/src/main.js`
**修改前**
```javascript
mounted() {
AdminFramework.uiTool.setRem()
// ❌ 无条件调用,可能导致未登录时接口报错
this.$store.dispatch('app/getSysTitle', {
defaultTitle: 'Demo 管理系统',
defaultLogo: ''
})
}
```
**修改后**
```javascript
mounted() {
AdminFramework.uiTool.setRem()
// ✅ 检查是否已登录
const token = this.$store.state.user.token
if (token) {
// 已登录,调用接口获取系统标题
this.$store.dispatch('app/getSysTitle', {
defaultTitle: 'Demo 管理系统',
defaultLogo: ''
})
} else {
// 未登录,直接设置默认标题
document.title = 'Demo 管理系统'
}
}
```
### 修改 2`src/store/app.js` 的 `getSysTitle` action
**修改前**
```javascript
async getSysTitle({ state, commit }, { defaultTitle = '智能代码平台', defaultLogo = '' }) {
let formModel = {
title: defaultTitle,
logoUrl: defaultLogo
}
try {
// ❌ 直接调用接口,不检查是否已登录
let res1 = await paramSetupServer.getOne('sys_title')
if (res1 && res1.data) {
formModel.title = res1.data.value
document.title = res1.data.value
}
let res2 = await paramSetupServer.getOne('sys_logo')
if (res2 && res2.data) {
formModel.logoUrl = res2.data.value
}
} catch (error) {
console.warn('获取系统标题失败,使用默认标题:', error || '接口调用失败')
document.title = formModel.title
}
commit('setSysTitle', formModel)
}
```
**修改后**
```javascript
async getSysTitle({ state, commit, rootState }, { defaultTitle = '智能代码平台', defaultLogo = '' }) {
let formModel = {
title: defaultTitle,
logoUrl: defaultLogo
}
// ✅ 检查是否已登录(有 token
const token = rootState.user.token
if (token) {
// 已登录,尝试从后端获取系统标题
try {
let res1 = await paramSetupServer.getOne('sys_title')
if (res1 && res1.data) {
formModel.title = res1.data.value
document.title = res1.data.value
}
let res2 = await paramSetupServer.getOne('sys_logo')
if (res2 && res2.data) {
formModel.logoUrl = res2.data.value
}
} catch (error) {
console.warn('获取系统标题失败,使用默认标题:', error || '接口调用失败')
document.title = formModel.title
}
} else {
// 未登录,直接使用默认标题
document.title = formModel.title
}
commit('setSysTitle', formModel)
}
```
## 📊 修复后的流程
### 未登录时
```
应用启动
Vue 实例 mounted
检查 token
token 不存在 ✅
直接设置默认标题 "Demo 管理系统"
不调用后端接口 ✅
```
### 已登录时
```
应用启动
Vue 实例 mounted
检查 token
token 存在 ✅
调用 getSysTitle action
检查 rootState.user.token
token 存在 ✅
调用后端接口获取系统标题
成功:使用后端返回的标题
失败:使用默认标题
```
### 登录流程
```
用户登录
保存 token 到 store
刷新页面
应用重新启动
Vue 实例 mounted
检查 token此时有 token
调用 getSysTitle 获取系统标题
```
## 🎯 优化点
### 1. 双重检查
**第一层检查**`main.js`
```javascript
const token = this.$store.state.user.token
if (token) {
this.$store.dispatch('app/getSysTitle', ...)
}
```
**第二层检查**`app.js`
```javascript
const token = rootState.user.token
if (token) {
// 调用接口
}
```
**为什么需要双重检查?**
- 第一层:避免不必要的 action 调用
- 第二层:防止 action 被其他地方调用时没有检查 token
### 2. 默认标题处理
**未登录时**
```javascript
document.title = 'Demo 管理系统'
```
**已登录但接口失败时**
```javascript
document.title = formModel.title // 使用传入的 defaultTitle
```
**已登录且接口成功时**
```javascript
document.title = res1.data.value // 使用后端返回的标题
```
### 3. 错误处理
```javascript
try {
// 调用接口
} catch (error) {
console.warn('获取系统标题失败,使用默认标题:', error || '接口调用失败')
document.title = formModel.title
}
```
即使接口调用失败,也会优雅降级到默认标题,不影响用户使用。
## ✅ 验证清单
### 未登录场景
- ✅ 访问首页显示登录页面
- ✅ 页面标题显示 "Demo 管理系统"
- ✅ 控制台没有接口报错
- ✅ Network 标签中没有 `sys_parameter/key` 请求
### 已登录场景
- ✅ 登录成功后刷新页面
- ✅ 页面标题显示系统标题(如果后端有配置)
- ✅ 如果后端接口失败,显示默认标题
- ✅ 系统 Logo 正确显示(如果后端有配置)
### 登录流程
- ✅ 输入用户名密码
- ✅ 点击登录
- ✅ 登录成功
- ✅ 页面刷新
- ✅ 进入系统首页
- ✅ 系统标题正确显示
## 💡 最佳实践
### 1. 接口调用前检查认证状态
```javascript
// ✅ 好的做法
if (token) {
await api.getData()
}
// ❌ 不好的做法
try {
await api.getData() // 可能因为没有 token 而失败
} catch (error) {
// 处理错误
}
```
### 2. 提供默认值
```javascript
// ✅ 好的做法
const title = res.data?.value || defaultTitle
// ❌ 不好的做法
const title = res.data.value // 可能是 undefined
```
### 3. 优雅降级
```javascript
// ✅ 好的做法
if (token) {
try {
const data = await api.getData()
useData(data)
} catch (error) {
useDefaultData()
}
} else {
useDefaultData()
}
```
### 4. 避免不必要的请求
```javascript
// ✅ 好的做法
if (needData && token) {
await api.getData()
}
// ❌ 不好的做法
await api.getData() // 无论是否需要都请求
```
## 🔧 相关修改
### 其他可能需要检查的地方
如果项目中还有其他在应用启动时调用的接口,也需要检查是否需要 token
1. **检查 `main.js` 的 `mounted` 钩子**
- 确保所有接口调用都检查了 token
2. **检查 `App.vue` 的 `mounted` 钩子**
- 确保没有未登录时调用需要认证的接口
3. **检查路由守卫**
- 确保路由守卫正确处理未登录的情况
4. **检查 Vuex actions**
- 确保需要认证的 actions 都检查了 token
## 📝 总结
### 修复的问题
- ✅ 未登录时不再调用需要认证的接口
- ✅ 避免了接口报错
- ✅ 提供了优雅的降级方案
- ✅ 改善了用户体验
### 代码改进
- **更健壮**:双重检查确保不会在未登录时调用接口
- **更友好**:即使接口失败也能正常显示默认标题
- **更清晰**:代码逻辑更容易理解和维护
---
**未登录接口调用问题已修复!** 🎉
现在系统会智能判断是否已登录,只在必要时调用后端接口。