fix: 修复前端登录体验和API调用问题

- 修复路由守卫:未登录时直接跳转,不显示提示信息
- 修复API拦截器:401错误直接跳转,无需确认
- 移除不必要的ElMessageBox确认框
- 优化Token过期处理逻辑
- 修复文件管理API引入路径和URL前缀
- 修复调拨/回收管理API端点不匹配问题
- 修复通知管理API方法不匹配问题
- 统一系统配置API路径为单数形式

影响文件:
- src/router/index.ts
- src/api/request.ts
- src/api/file.ts
- src/api/index.ts

测试状态:
- 前端构建通过
- 所有API路径已验证
- 登录流程测试通过

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
Claude
2026-01-25 00:26:33 +08:00
commit e48975f9d5
151 changed files with 39477 additions and 0 deletions

258
tests/e2e/login.spec.ts Normal file
View File

@@ -0,0 +1,258 @@
/**
* 登录流程E2E测试
*
* 测试内容:
* - 正常登录流程
* - 错误密码处理
* - 验证码验证
* - Token过期处理
* - 记住密码功能
*/
import { test, expect } from '@playwright/test'
test.describe('登录流程测试', () => {
test.beforeEach(async ({ page }) => {
await page.goto('http://localhost:5173/login')
})
test('应该成功登录并跳转到首页', async ({ page }) => {
// 输入用户名和密码
await page.fill('input[name="username"]', 'admin')
await page.fill('input[name="password"]', 'Admin123')
// 输入验证码 (假设测试环境验证码固定为1234)
await page.fill('input[name="captcha"]', '1234')
// 点击登录按钮
await page.click('button[type="submit"]')
// 等待跳转
await page.waitForURL('http://localhost:5173/')
// 验证URL已跳转到首页
expect(page.url()).toBe('http://localhost:5173/')
// 验证显示了用户信息
await expect(page.locator('.user-info')).toBeVisible()
await expect(page.locator('.user-info')).toContainText('admin')
})
test('应该显示用户名或密码错误', async ({ page }) => {
await page.fill('input[name="username"]', 'admin')
await page.fill('input[name="password"]', 'WrongPassword')
await page.fill('input[name="captcha"]', '1234')
await page.click('button[type="submit"]')
// 等待错误消息显示
await expect(page.locator('.el-message--error')).toBeVisible()
await expect(page.locator('.el-message--error')).toContainText('用户名或密码错误')
})
test('应该验证必填字段', async ({ page }) => {
// 不填写任何字段直接提交
await page.click('button[type="submit"]')
// 验证表单验证错误
await expect(page.locator('input[name="username"] + .el-form-item__error')).toBeVisible()
await expect(page.locator('input[name="password"] + .el-form-item__error')).toBeVisible()
})
test('应该验证用户名格式', async ({ page }) => {
// 输入无效用户名
await page.fill('input[name="username"]', 'ab') // 太短
await page.fill('input[name="password"]', 'Admin123')
await page.click('button[type="submit"]')
// 应该显示用户名格式错误
await expect(page.locator('.el-form-item__error')).toBeVisible()
})
test('应该验证密码强度', async ({ page }) => {
await page.fill('input[name="username"]', 'admin')
await page.fill('input[name="password"]', 'weak') // 弱密码
await page.click('button[type="submit"]')
// 应该显示密码强度错误
await expect(page.locator('.el-form-item__error')).toBeVisible()
})
test('应该刷新验证码', async ({ page }) => {
const captchaImage = page.locator('.captcha-image img')
// 获取初始验证码图片URL
const initialSrc = await captchaImage.getAttribute('src')
// 点击刷新验证码
await page.click('.refresh-captcha')
// 等待图片重新加载
await page.waitForLoadState('networkidle')
// 验证验证码已更新
const newSrc = await captchaImage.getAttribute('src')
expect(newSrc).not.toBe(initialSrc)
})
test('应该支持记住密码功能', async ({ page }) => {
// 勾选记住密码
await page.check('input[name="remember"]')
await page.fill('input[name="username"]', 'admin')
await page.fill('input[name="password"]', 'Admin123')
await page.fill('input[name="captcha"]', '1234')
await page.click('button[type="submit"]')
await page.waitForURL('http://localhost:5173/')
// 验证localStorage中保存了用户信息
const rememberedUser = await page.evaluate(() => {
return localStorage.getItem('rememberedUser')
})
expect(rememberedUser).toBeTruthy()
// 退出登录
await page.click('.logout-button')
// 返回登录页
await page.goto('http://localhost:5173/login')
// 验证用户名已填充
const username = await page.inputValue('input[name="username"]')
expect(username).toBe('admin')
})
test('应该处理验证码错误', async ({ page }) => {
await page.fill('input[name="username"]', 'admin')
await page.fill('input[name="password"]', 'Admin123')
await page.fill('input[name="captcha"]', '9999') // 错误验证码
await page.click('button[type="submit"]')
// 应该显示验证码错误
await expect(page.locator('.el-message--error')).toBeVisible()
await expect(page.locator('.el-message--error')).toContainText('验证码错误')
})
test('应该限制登录尝试次数', async ({ page }) => {
// 尝试多次错误登录
for (let i = 0; i < 5; i++) {
await page.fill('input[name="username"]', 'admin')
await page.fill('input[name="password"]', 'WrongPassword')
await page.fill('input[name="captcha"]', '1234')
await page.click('button[type="submit"]')
await page.waitForTimeout(500)
}
// 第6次应该被锁定
await page.fill('input[name="username"]', 'admin')
await page.fill('input[name="password"]', 'Admin123')
await page.fill('input[name="captcha"]', '1234')
await page.click('button[type="submit"]')
await expect(page.locator('.el-message--error')).toBeVisible()
await expect(page.locator('.el-message--error')).toContainText('账户已锁定')
})
test('应该支持回车键登录', async ({ page }) => {
await page.fill('input[name="username"]', 'admin')
await page.fill('input[name="password"]', 'Admin123')
await page.fill('input[name="captcha"]', '1234')
// 在密码框按回车
await page.press('input[name="password"]', 'Enter')
// 应该提交登录
await page.waitForURL('http://localhost:5173/')
expect(page.url()).toBe('http://localhost:5173/')
})
test('应该处理网络错误', async ({ page, context }) => {
// 模拟网络断开
await context.setOffline(true)
await page.fill('input[name="username"]', 'admin')
await page.fill('input[name="password"]', 'Admin123')
await page.fill('input[name="captcha"]', '1234')
await page.click('button[type="submit"]')
// 应该显示网络错误
await expect(page.locator('.el-message--error')).toBeVisible()
await expect(page.locator('.el-message--error')).toContainText('网络错误')
// 恢复网络
await context.setOffline(false)
})
})
test.describe('Token过期处理', () => {
test('应该在Token过期时自动刷新', async ({ page }) => {
// 登录
await page.goto('http://localhost:5173/login')
await page.fill('input[name="username"]', 'admin')
await page.fill('input[name="password"]', 'Admin123')
await page.fill('input[name="captcha"]', '1234')
await page.click('button[type="submit"]')
await page.waitForURL('http://localhost:5173/')
// 模拟Token过期(通过设置过期Token)
await page.evaluate(() => {
localStorage.setItem('token', 'expired_token')
localStorage.setItem('refreshToken', 'valid_refresh_token')
})
// 刷新页面
await page.reload()
// 应该自动刷新Token并正常显示
await expect(page.locator('.user-info')).toBeVisible()
})
test('应该在刷新Token失败时跳转登录页', async ({ page }) => {
await page.goto('http://localhost:5173/')
await page.evaluate(() => {
localStorage.setItem('token', 'expired_token')
localStorage.setItem('refreshToken', 'expired_refresh_token')
})
// 刷新页面
await page.reload()
// 应该跳转到登录页
await page.waitForURL('http://localhost:5173/login')
expect(page.url()).toBe('http://localhost:5173/login')
})
})
test.describe('跨浏览器测试', () => {
test('应该在Chrome中正常工作', async ({ page, browserName }) => {
test.skip(browserName !== 'chromium')
await page.goto('http://localhost:5173/login')
await page.fill('input[name="username"]', 'admin')
await page.fill('input[name="password"]', 'Admin123')
await page.fill('input[name="captcha"]', '1234')
await page.click('button[type="submit"]')
await page.waitForURL('http://localhost:5173/')
expect(page.url()).toBe('http://localhost:5173/')
})
test('应该在Firefox中正常工作', async ({ page, browserName }) => {
test.skip(browserName !== 'firefox')
await page.goto('http://localhost:5173/login')
await page.fill('input[name="username"]', 'admin')
await page.fill('input[name="password"]', 'Admin123')
await page.fill('input[name="captcha"]', '1234')
await page.click('button[type="submit"]')
await page.waitForURL('http://localhost:5173/')
expect(page.url()).toBe('http://localhost:5173/')
})
})