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
+46
View File
@@ -14,6 +14,15 @@ function dateToMonth(date: Date): string {
return `${y}-${m}`
}
/** 安全解密数字(工资字段) */
function safeDecryptNum(encrypted: string): number {
try {
return Number(decrypt(encrypted)) || 0
} catch {
return Number(encrypted) || 0
}
}
async function clampSocialInsBase(orgId: string, base: number, city?: string, accountId?: string): Promise<number> {
// 优先按账户查年度标准
if (accountId) {
@@ -271,6 +280,9 @@ export async function getEmployees(orgId: string, params: { page?: number; pageS
} catch {
decryptedSalary = Number(emp.monthlySalary) || 0
}
// 解密工资结构
const decryptedBaseSalary = emp.baseSalary ? safeDecryptNum(emp.baseSalary) : null
const decryptedPerformanceSalary = emp.performanceSalary ? safeDecryptNum(emp.performanceSalary) : null
return {
id: emp.id,
@@ -279,6 +291,8 @@ export async function getEmployees(orgId: string, params: { page?: number; pageS
hireDate: emp.hireDate.toISOString().slice(0, 10),
status: emp.status,
monthlySalary: decryptedSalary,
baseSalary: decryptedBaseSalary,
performanceSalary: decryptedPerformanceSalary,
contractStatus: contractInfo.status,
contractStatusText: contractInfo.statusText,
riskLevel: contractInfo.riskLevel,
@@ -315,6 +329,8 @@ export async function getEmployeeDetail(orgId: string, id: string) {
return {
...employee,
monthlySalary: decryptedSalary,
baseSalary: employee.baseSalary ? safeDecryptNum(employee.baseSalary) : null,
performanceSalary: employee.performanceSalary ? safeDecryptNum(employee.performanceSalary) : null,
}
}
@@ -368,6 +384,9 @@ export async function createEmployee(orgId: string, userId: string, data: any) {
const hireDate = new Date(data.hireDate)
const hireMonth = dateToMonth(hireDate)
const salaryNum = Number(data.monthlySalary) || 0
// 工资结构:基本工资 + 绩效工资 = 月薪总额
const baseSalaryNum = Number(data.baseSalary) || 0
const performanceSalaryNum = Number(data.performanceSalary) || 0
const city = data.city || '北京'
// 劳务协议/实习协议:不缴纳社保公积金,基数强制 0
const isNoSocialContract = data.contract && ['LABOR', 'INTERNSHIP'].includes(data.contract.contractType)
@@ -389,6 +408,8 @@ export async function createEmployee(orgId: string, userId: string, data: any) {
department: data.department,
hireDate,
monthlySalary: encrypt(data.monthlySalary),
baseSalary: encrypt(String(baseSalaryNum)),
performanceSalary: encrypt(String(performanceSalaryNum)),
gender: data.gender,
femaleWorkerType: data.femaleWorkerType,
phone: data.phone,
@@ -488,6 +509,8 @@ export async function createEmployee(orgId: string, userId: string, data: any) {
contractYears: data.contract.contractYears || 3,
probationMonths: data.contract.probationMonths || 0,
probationSalary: data.contract.probationSalary || 0,
baseSalary: baseSalaryNum,
performanceSalary: performanceSalaryNum,
createdBy: userId,
},
})
@@ -526,6 +549,10 @@ export async function rehireEmployee(orgId: string, userId: string, id: string,
const newHireMonth = dateToMonth(newHireDate)
const salaryNum = Number(decrypt(employee.monthlySalary)) || 0
// 重新入职时支持更新工资结构
const baseSalaryNum = data.baseSalary != null ? Number(data.baseSalary) : (employee.baseSalary ? Number(decrypt(employee.baseSalary)) || 0 : 0)
const performanceSalaryNum = data.performanceSalary != null ? Number(data.performanceSalary) : (employee.performanceSalary ? Number(decrypt(employee.performanceSalary)) || 0 : 0)
const newMonthlySalary = data.baseSalary != null ? baseSalaryNum + performanceSalaryNum : salaryNum
const city = data.city || employee.city || '北京'
const rawSocialInsBase = data.socialInsBase != null ? Number(data.socialInsBase) : salaryNum
const rawHousingFundBase = data.housingFundBase != null ? Number(data.housingFundBase) : salaryNum
@@ -575,6 +602,12 @@ export async function rehireEmployee(orgId: string, userId: string, id: string,
housingFundStartMonth,
housingFundEndMonth: null,
city: data.city || employee.city || '北京',
// 更新工资结构
...(data.baseSalary != null ? {
monthlySalary: encrypt(String(newMonthlySalary)),
baseSalary: encrypt(String(baseSalaryNum)),
performanceSalary: encrypt(String(performanceSalaryNum)),
} : {}),
},
})
@@ -657,6 +690,8 @@ export async function rehireEmployee(orgId: string, userId: string, id: string,
contractYears: data.contract.contractYears || 3,
probationMonths: data.contract.probationMonths || 0,
probationSalary: data.contract.probationSalary || 0,
baseSalary: baseSalaryNum,
performanceSalary: performanceSalaryNum,
createdBy: userId,
},
})
@@ -702,6 +737,13 @@ export async function updateEmployee(orgId: string, id: string, data: any) {
const oldSalary = Number(decrypt(employee.monthlySalary)) || 0
const newSalary = Number(data.monthlySalary) || 0
updateData.monthlySalary = encrypt(data.monthlySalary)
// 同步更新工资结构
if (data.baseSalary !== undefined) {
updateData.baseSalary = encrypt(String(Number(data.baseSalary) || 0))
}
if (data.performanceSalary !== undefined) {
updateData.performanceSalary = encrypt(String(Number(data.performanceSalary) || 0))
}
// 记录薪资变更
if (oldSalary !== newSalary) {
const now = new Date()
@@ -912,6 +954,8 @@ export async function batchRenew(orgId: string, userId: string, contractIds: str
contractYears: years,
probationMonths: 0,
probationSalary: 0,
baseSalary: contract.baseSalary || 0,
performanceSalary: contract.performanceSalary || 0,
renewalCount: contract.renewalCount + 1,
createdBy: userId,
},
@@ -963,6 +1007,8 @@ export async function addContract(orgId: string, userId: string, data: any) {
contractYears: data.contractYears || 3,
probationMonths: data.probationMonths || 0,
probationSalary: data.probationSalary || 0,
baseSalary: data.baseSalary || 0,
performanceSalary: data.performanceSalary || 0,
attachmentName: data.attachmentUrl ? '合同扫描件' : null,
attachmentUrl: data.attachmentUrl || null,
electronicContractNo: data.electronicContractNo || null,