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

View File

@@ -0,0 +1,339 @@
/**
* 资产列表组件测试
*
* 测试内容:
* - 组件渲染
* - 数据加载
* - 搜索功能
* - 分页功能
* - 事件触发
*/
import { describe, it, expect, vi, beforeEach } from 'vitest'
import { mount, VueWrapper } from '@vue/test-utils'
import { createPinia, setActivePinia } from 'pinia'
import ElementPlus from 'element-plus'
import AssetList from '@/views/assets/AssetList.vue'
import * as assetApi from '@/api/assets'
// Mock API模块
vi.mock('@/api/assets', () => ({
getAssetList: vi.fn(),
deleteAsset: vi.fn(),
getAssetStatistics: vi.fn()
}))
describe('AssetList组件', () => {
let wrapper: VueWrapper<any>
let pinia: any
beforeEach(() => {
// 创建新的Pinia实例
pinia = createPinia()
setActivePinia(pinia)
// Mock API响应
vi.mocked(assetApi.getAssetList).mockResolvedValue({
items: [
{
id: 1,
assetCode: 'ASSET-20250124-0001',
assetName: '联想台式机',
deviceType: { id: 1, typeName: '计算机' },
organization: { id: 1, orgName: '天河网点' },
status: 'in_use',
purchaseDate: '2024-01-15',
purchasePrice: 4500.00
}
],
total: 1,
page: 1,
pageSize: 20
})
vi.mocked(assetApi.getAssetStatistics).mockResolvedValue({
totalCount: 100,
totalValue: 500000.00,
statusDistribution: {
in_stock: 30,
in_use: 50,
maintenance: 10,
scrapped: 10
}
})
})
afterEach(() => {
if (wrapper) {
wrapper.unmount()
}
vi.clearAllMocks()
})
it('应该正确渲染组件', () => {
wrapper = mount(AssetList, {
global: {
plugins: [pinia, ElementPlus],
stubs: {
'el-table': true,
'el-pagination': true,
'el-input': true,
'el-button': true
}
}
})
expect(wrapper.find('.asset-list').exists()).toBe(true)
})
it('应该在挂载时加载资产列表', async () => {
wrapper = mount(AssetList, {
global: {
plugins: [pinia, ElementPlus],
stubs: {
'el-table': true,
'el-pagination': true
}
}
})
await wrapper.vm.$nextTick()
expect(assetApi.getAssetList).toHaveBeenCalledWith({
page: 1,
page_size: 20
})
})
it('应该显示资产统计数据', async () => {
wrapper = mount(AssetList, {
global: {
plugins: [pinia, ElementPlus],
stubs: {
'el-table': true,
'el-statistic': true
}
}
})
await wrapper.vm.$nextTick()
await wrapper.vm.$nextTick() // 等待统计数据加载
expect(assetApi.getAssetStatistics).toHaveBeenCalled()
})
it('应该支持搜索功能', async () => {
wrapper = mount(AssetList, {
global: {
plugins: [pinia, ElementPlus],
stubs: {
'el-table': true,
'el-input': true,
'el-button': true
}
}
})
const searchKeyword = '联想'
wrapper.vm.searchKeyword = searchKeyword
await wrapper.vm.handleSearch()
expect(assetApi.getAssetList).toHaveBeenCalledWith({
page: 1,
page_size: 20,
keyword: searchKeyword
})
})
it('应该支持分页功能', async () => {
wrapper = mount(AssetList, {
global: {
plugins: [pinia, ElementPlus],
stubs: {
'el-table': true,
'el-pagination': true
}
}
})
wrapper.vm.pagination.page = 2
await wrapper.vm.fetchAssets()
expect(assetApi.getAssetList).toHaveBeenCalledWith({
page: 2,
page_size: 20
})
})
it('应该触发刷新事件', async () => {
wrapper = mount(AssetList, {
global: {
plugins: [pinia, ElementPlus],
stubs: {
'el-table': true,
'el-button': true
}
}
})
const refreshSpy = vi.spyOn(wrapper.vm, 'fetchAssets')
await wrapper.vm.handleRefresh()
expect(refreshSpy).toHaveBeenCalled()
})
it('应该打开创建对话框', async () => {
wrapper = mount(AssetList, {
global: {
plugins: [pinia, ElementPlus],
stubs: {
'el-table': true,
'el-button': true,
'asset-create-dialog': true
}
}
})
await wrapper.vm.openCreateDialog()
expect(wrapper.vm.createDialogVisible).toBe(true)
})
it('应该打开编辑对话框', async () => {
const mockAsset = {
id: 1,
assetCode: 'ASSET-20250124-0001',
assetName: '联想台式机'
}
wrapper = mount(AssetList, {
global: {
plugins: [pinia, ElementPlus],
stubs: {
'el-table': true,
'asset-edit-dialog': true
}
}
})
await wrapper.vm.openEditDialog(mockAsset)
expect(wrapper.vm.editDialogVisible).toBe(true)
expect(wrapper.vm.currentAsset).toEqual(mockAsset)
})
it('应该删除资产', async () => {
vi.mocked(assetApi.deleteAsset).mockResolvedValue({})
wrapper = mount(AssetList, {
global: {
plugins: [pinia, ElementPlus],
stubs: {
'el-table': true,
'el-button': true
}
}
})
// Mock确认对话框
vi.spyOn(wrapper.vm as any, '$confirm').mockResolvedValue('confirm')
await wrapper.vm.handleDelete(1)
expect(assetApi.deleteAsset).toHaveBeenCalledWith(1)
})
it('应该在搜索时重置页码', async () => {
wrapper = mount(AssetList, {
global: {
plugins: [pinia, ElementPlus],
stubs: {
'el-table': true,
'el-input': true
}
}
})
wrapper.vm.pagination.page = 5
wrapper.vm.searchKeyword = '测试'
await wrapper.vm.handleSearch()
expect(wrapper.vm.pagination.page).toBe(1)
})
it('应该显示加载状态', async () => {
wrapper = mount(AssetList, {
global: {
plugins: [pinia, ElementPlus],
stubs: {
'el-table': true
}
}
})
wrapper.vm.loading = true
await wrapper.vm.$nextTick()
expect(wrapper.find('.loading').exists()).toBe(true)
})
it('应该处理API错误', async () => {
vi.mocked(assetApi.getAssetList).mockRejectedValue(new Error('网络错误'))
wrapper = mount(AssetList, {
global: {
plugins: [pinia, ElementPlus],
stubs: {
'el-table': true
}
}
})
const consoleSpy = vi.spyOn(console, 'error').mockImplementation(() => {})
await wrapper.vm.fetchAssets()
expect(consoleSpy).toHaveBeenCalled()
consoleSpy.mockRestore()
})
it('应该支持状态筛选', async () => {
wrapper = mount(AssetList, {
global: {
plugins: [pinia, ElementPlus],
stubs: {
'el-table': true,
'el-select': true
}
}
})
wrapper.vm.filters.status = 'in_use'
await wrapper.vm.handleFilter()
expect(assetApi.getAssetList).toHaveBeenCalledWith({
page: 1,
page_size: 20,
status: 'in_use'
})
})
it('应该支持设备类型筛选', async () => {
wrapper = mount(AssetList, {
global: {
plugins: [pinia, ElementPlus],
stubs: {
'el-table': true,
'el-select': true
}
}
})
wrapper.vm.filters.deviceTypeId = 1
await wrapper.vm.handleFilter()
expect(assetApi.getAssetList).toHaveBeenCalledWith({
page: 1,
page_size: 20,
device_type_id: 1
})
})
})