feat: 绩效工资改为手动获取+按考核系数着色

后端:
- 建立批次时绩效工资填0,不自动带出
- 获取绩效工资接口返回 details(grade/coefficient/performanceSalary)
- 无考核记录时系数为1.0(绩效工资=基数全额)

前端:
- 存储获取绩效工资返回的 details 到 perfDetails state
- 绩效工资列按系数着色:A级(1.2)绿色/C级(0.8)橙色/D级(0.6)红色
- B级(1.0)和无考核正常色,0值灰色
- hover显示考核等级和系数
This commit is contained in:
freedakgmail
2026-08-19 07:36:22 +08:00
parent c6fcfd36b8
commit a7f3d5f3b5
2 changed files with 65 additions and 4 deletions
+7 -3
View File
@@ -493,11 +493,11 @@ router.post('/batches', async (req: AuthRequest, res: Response, next: NextFuncti
} else if (prevPayslip) {
// 非试用期:优先用上月工资条的基本工资(保持薪资连续性)
baseSalary = prevPayslip.baseSalary
performanceSalary = prevPayslip.performanceSalary || 0
// 绩效工资不自动带出,需手动点"获取绩效工资"按钮
} else if (empBaseSalary > 0 || empPerformanceSalary > 0) {
// 新数据:有工资结构
baseSalary = empBaseSalary
performanceSalary = empPerformanceSalary
// 绩效工资不自动带出,需手动点"获取绩效工资"按钮
} else if (emp.monthlySalary) {
// 旧数据兼容:回退到 monthlySalary
try { baseSalary = Number(decrypt(emp.monthlySalary)) || 0 } catch { baseSalary = Number(emp.monthlySalary) || 0 }
@@ -515,7 +515,7 @@ router.post('/batches', async (req: AuthRequest, res: Response, next: NextFuncti
performanceSalary = 0
} else if (empBaseSalary > 0 || empPerformanceSalary > 0) {
baseSalary = empBaseSalary
performanceSalary = empPerformanceSalary
// 绩效工资不自动带出,需手动点"获取绩效工资"按钮
} else if (emp.monthlySalary && mode === 'custom') {
// custom 模式旧数据兼容
try { baseSalary = Number(decrypt(emp.monthlySalary)) || 0 } catch { baseSalary = Number(emp.monthlySalary) || 0 }
@@ -840,11 +840,13 @@ router.post('/batches/:batchId/fetch-performance', async (req: AuthRequest, res:
let filled = 0
let totalAmount = 0
const details: { employeeId: string; grade: string | null; coefficient: number; performanceSalary: number }[] = []
for (const entry of entries) {
const basePerfSalary = empPerfSalaryMap.get(entry.employeeId) || 0
if (basePerfSalary <= 0) continue // 绩效工资基数为0则跳过
const perfRecord = perfMap.get(entry.employeeId)
const grade = perfRecord?.grade || null
const coefficient = perfRecord ? (gradeCoefficients[perfRecord.grade] || 1.0) : 1.0
const calculatedPerfSalary = Math.round(basePerfSalary * coefficient * 100) / 100
@@ -853,6 +855,7 @@ router.post('/batches/:batchId/fetch-performance', async (req: AuthRequest, res:
data: { performanceSalary: calculatedPerfSalary },
})
await recalcEntry(orgId, batchId, batch.month, batch.type, entry.id, entry.employeeId)
details.push({ employeeId: entry.employeeId, grade, coefficient, performanceSalary: calculatedPerfSalary })
filled++
totalAmount += calculatedPerfSalary
}
@@ -865,6 +868,7 @@ router.post('/batches/:batchId/fetch-performance', async (req: AuthRequest, res:
data: {
filled,
totalAmount: Math.round(totalAmount * 100) / 100,
details,
message: filled > 0
? `已填充 ${filled} 人绩效工资,合计 ¥${Math.round(totalAmount * 100) / 100}${performanceRecords.length > 0 ? `${performanceRecords.length} 人有考核记录)` : '(无考核记录,按系数1.0计算)'}`
: '无绩效工资数据(员工未设置绩效工资基数)',