fix: portal端token过期后自动跳转登录页,避免显示空数据

- portalAxios 401 响应拦截器:清除 token 并跳转 /portal/login
- PortalLayoutWrapper:检查 token 是否存在,不存在则跳转登录页
- 排除 /portal/login 和 /portal/auto-login 页面避免重复跳转

Generated with [Devin](https://devin.ai)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
This commit is contained in:
selfrelease
2026-08-17 15:06:46 +08:00
parent c66ea290eb
commit 5196d7c80a
23 changed files with 812 additions and 74 deletions
+27 -1
View File
@@ -294,6 +294,12 @@ export const attendanceApi = {
/** 批量确认 */
batchConfirm: (data: { month: string; all?: boolean; ids?: string[] }) =>
post('/attendance/batch-confirm', data).then(unwrap<any>()),
/** 批量创建/更新考勤确认记录 */
batchCreate: (data: { month: string; items: any[] }) =>
post('/attendance/batch', data).then(unwrap<any>()),
/** 更新考勤确认扣款金额 */
updateDeduction: (id: string, deductionAmount: number) =>
put(`/attendance/confirmations/${id}/deduction`, { deductionAmount }).then(unwrap<any>()),
/** 单条确认 */
confirm: (data: { employeeId: string; month: string }) =>
post('/attendance/confirm', data).then(unwrap<any>()),
@@ -480,6 +486,15 @@ export const payrollApi = {
/** 获取提成奖金到批次 */
fetchBonusToBatch: (batchId: string) =>
post(`/payroll2/batches/${batchId}/fetch-bonus`).then(unwrap<any>()),
/** 获取绩效工资到批次(按考核系数计算) */
fetchPerformanceToBatch: (batchId: string) =>
post(`/payroll2/batches/${batchId}/fetch-performance`).then(unwrap<any>()),
/** 获取违纪扣款到批次 */
fetchDisciplinaryToBatch: (batchId: string) =>
post(`/payroll2/batches/${batchId}/fetch-disciplinary`).then(unwrap<any>()),
/** 获取考勤扣款到批次 */
fetchAttendanceDeductionToBatch: (batchId: string) =>
post(`/payroll2/batches/${batchId}/fetch-attendance-deduction`).then(unwrap<any>()),
/** 加班费配置 */
overtimeConfig: () =>
get('/payroll/overtime/config').then(unwrap<any>()),
@@ -1096,9 +1111,20 @@ portalAxios.interceptors.request.use((config: any) => {
return config
})
// 与管理端 api 实例一致:response interceptor 返回 response.data(后端 JSON body
// 401 时清除 token 并跳转登录页(避免 token 过期后显示空数据)
portalAxios.interceptors.response.use(
(response) => response.data,
(error) => Promise.reject(error),
(error) => {
if (error?.response?.status === 401) {
localStorage.removeItem('portalToken')
localStorage.removeItem('portalEmployee')
// 避免在登录页重复跳转
if (!window.location.pathname.includes('/portal/login') && !window.location.pathname.includes('/portal/auto-login')) {
window.location.href = '/portal/login'
}
}
return Promise.reject(error)
},
)
const portalGet = ((url: string, config?: any) => portalAxios.get(url, config)) as any
const portalPost = ((url: string, data?: any, config?: any) => portalAxios.post(url, data, config)) as any