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,195 @@
<template>
<el-dialog
v-model="visible"
title="回收单详情"
width="900px"
@close="handleClose"
>
<div v-loading="loading">
<!-- 基本信息 -->
<el-descriptions
v-if="detail"
title="基本信息"
:column="2"
border
class="mb-16"
>
<el-descriptions-item label="回收单号">
{{ detail.recoveryNo }}
</el-descriptions-item>
<el-descriptions-item label="审批状态">
<el-tag :type="getStatusType(detail.status)">
{{ getStatusName(detail.status) }}
</el-tag>
</el-descriptions-item>
<el-descriptions-item label="回收机构">
{{ detail.organization?.orgName }}
</el-descriptions-item>
<el-descriptions-item label="资产数量">
{{ detail.assetCount }}
</el-descriptions-item>
<el-descriptions-item label="总价值">
{{ detail.totalValue ? `¥${detail.totalValue.toFixed(2)}` : '-' }}
</el-descriptions-item>
<el-descriptions-item label="申请人">
{{ detail.applicant?.username }}
</el-descriptions-item>
<el-descriptions-item label="申请时间" :span="2">
{{ detail.createdAt }}
</el-descriptions-item>
<el-descriptions-item label="回收原因" :span="2">
{{ detail.reason }}
</el-descriptions-item>
<el-descriptions-item label="备注" :span="2">
{{ detail.remark || '-' }}
</el-descriptions-item>
</el-descriptions>
<!-- 资产明细 -->
<div class="section-title mb-16">资产明细</div>
<el-table
:data="detail?.assets || []"
border
max-height="300"
class="mb-16"
>
<el-table-column prop="assetCode" label="资产编码" width="150" />
<el-table-column prop="assetName" label="资产名称" min-width="150" />
<el-table-column prop="deviceType.typeName" label="设备类型" width="120" />
<el-table-column prop="brand.brandName" label="品牌" width="120" />
<el-table-column prop="model" label="型号" width="150" />
<el-table-column prop="purchasePrice" label="采购价格" width="120">
<template #default="{ row }">
{{ row.purchasePrice ? `¥${row.purchasePrice.toFixed(2)}` : '-' }}
</template>
</el-table-column>
<el-table-column prop="serialNumber" label="序列号" width="150" />
</el-table>
<!-- 审批历史 -->
<div class="section-title mb-16">审批历史</div>
<el-timeline>
<el-timeline-item
v-for="(item, index) in detail?.approvalHistory || []"
:key="index"
:timestamp="item.createdAt"
placement="top"
>
<el-card>
<div class="approval-item">
<div class="approval-header">
<span class="approver">{{ item.approver?.username }}</span>
<el-tag :type="item.approved ? 'success' : 'danger'" size="small">
{{ item.approved ? '通过' : '拒绝' }}
</el-tag>
</div>
<div class="approval-comment" v-if="item.comment">
{{ item.comment }}
</div>
</div>
</el-card>
</el-timeline-item>
</el-timeline>
</div>
<template #footer>
<el-button @click="handleClose">关闭</el-button>
</template>
</el-dialog>
</template>
<script setup lang="ts">
import { ref, computed, watch } from 'vue'
import { getRecoveryDetail } from '@/api'
import { APPROVAL_STATUS } from '@/utils/constants'
import { ElMessage } from 'element-plus'
interface Props {
modelValue: boolean
recoveryId: number | null
}
interface Emits {
(e: 'update:modelValue', value: boolean): void
}
const props = defineProps<Props>()
const emit = defineEmits<Emits>()
const visible = computed({
get: () => props.modelValue,
set: (value) => emit('update:modelValue', value)
})
const loading = ref(false)
const detail = ref<any>(null)
// 获取回收单详情
const fetchDetail = async () => {
if (!props.recoveryId) return
loading.value = true
try {
detail.value = await getRecoveryDetail(props.recoveryId)
} catch (error) {
ElMessage.error('获取详情失败')
} finally {
loading.value = false
}
}
// 关闭对话框
const handleClose = () => {
detail.value = null
visible.value = false
}
// 状态标签类型
const getStatusType = (status: string) => {
const item = Object.values(APPROVAL_STATUS).find(item => item.value === status)
return item?.type || ''
}
// 状态名称
const getStatusName = (status: string) => {
const item = Object.values(APPROVAL_STATUS).find(item => item.value === status)
return item?.label || status
}
watch(() => props.modelValue, (val) => {
if (val && props.recoveryId) {
fetchDetail()
}
})
</script>
<style scoped lang="scss">
.mb-16 {
margin-bottom: 16px;
}
.section-title {
font-size: 16px;
font-weight: bold;
color: #303133;
}
.approval-item {
.approval-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 8px;
.approver {
font-weight: bold;
color: #303133;
}
}
.approval-comment {
color: #606266;
font-size: 14px;
}
}
</style>