feat: 社保公积金版本管理 + 员工基数调整 + 加班费计算优化 + UI组件改进

- SocialInsuranceConfig 版本化(effectiveFrom/effectiveTo/isCurrent/adjustmentDone)
- 社保配置版本管理接口(列表/新建/按月获取/当前版本)
- 员工基数调整:预览全部在职员工、上年月均工资计算建议基数、可编辑表格、确认后批量保存
- payroll.service 按批次月份匹配对应版本社保配置
- risk.service / seed.ts 同步更新
- 前端社保tab重构为版本管理+调整+试算
- 加班费计算三步流程、CSV导入、批次导入
- UI组件、Dashboard、合同、薪酬等页面优化
This commit is contained in:
freedakgmail
2026-07-23 16:32:46 +08:00
parent 2a09d31ccc
commit c618710a52
31 changed files with 2245 additions and 745 deletions
+15 -5
View File
@@ -14,11 +14,12 @@ export function getContractStatus(contract: {
hireDate: Date
}): { status: string; statusText: string; riskLevel: 'high' | 'medium' | 'low' | 'safe' } {
const today = new Date()
const typeLabel = contract.contractType === 'FIXED' ? '固定期限' : contract.contractType === 'UNFIXED' ? '无固定期限' : ''
if (!contract.signDate || contract.contractType === 'UNSIGNED') {
const days = daysBetween(today, contract.hireDate)
if (days > 365) {
return { status: 'unsigned_over_year', statusText: '已视为无固定期限', riskLevel: 'high' }
return { status: 'unsigned_over_year', statusText: '未签合同(已视为无固定期限)', riskLevel: 'high' }
} else if (days > 30) {
return { status: 'unsigned_over_30', statusText: `未签合同(${days}天)`, riskLevel: 'high' }
}
@@ -28,14 +29,14 @@ export function getContractStatus(contract: {
if (contract.endDate) {
const daysToExpire = daysBetween(contract.endDate, today)
if (daysToExpire < 0) {
return { status: 'expired', statusText: '已到期未续签', riskLevel: 'high' }
return { status: 'expired', statusText: `${typeLabel}·已到期未续签`, riskLevel: 'high' }
} else if (daysToExpire <= 30) {
return { status: 'expiring', statusText: `即将到期(${daysToExpire}天)`, riskLevel: 'medium' }
return { status: 'expiring', statusText: `${typeLabel}·即将到期(${daysToExpire}天)`, riskLevel: 'medium' }
}
return { status: 'active', statusText: '正常', riskLevel: 'safe' }
return { status: 'active', statusText: `${typeLabel}·正常`, riskLevel: 'safe' }
}
return { status: 'unfixed', statusText: '无固定期限', riskLevel: 'safe' }
return { status: 'unfixed', statusText: '无固定期限·正常', riskLevel: 'safe' }
}
export function validateProbation(contractMonths: number, probationMonths: number): { valid: boolean; max: number; message?: string } {
@@ -233,6 +234,11 @@ export async function updateEmployee(orgId: string, id: string, data: any) {
}
if (data.gender !== undefined) updateData.gender = data.gender
if (data.phone !== undefined) updateData.phone = data.phone
if (data.bankName !== undefined) updateData.bankName = data.bankName
if (data.bankAccount !== undefined) updateData.bankAccount = encrypt(data.bankAccount)
if (data.emergencyContact !== undefined) updateData.emergencyContact = data.emergencyContact
if (data.emergencyPhone !== undefined) updateData.emergencyPhone = data.emergencyPhone
if (data.address !== undefined) updateData.address = data.address
if (data.isPregnant !== undefined) updateData.isPregnant = data.isPregnant
if (data.isInMedicalPeriod !== undefined) updateData.isInMedicalPeriod = data.isInMedicalPeriod
if (data.isWorkInjured !== undefined) updateData.isWorkInjured = data.isWorkInjured
@@ -325,6 +331,10 @@ export async function addContract(orgId: string, userId: string, data: any) {
contractYears: data.contractYears || 3,
probationMonths: data.probationMonths || 0,
probationSalary: data.probationSalary || 0,
attachmentName: data.attachmentUrl ? '合同扫描件' : null,
attachmentUrl: data.attachmentUrl || null,
electronicContractNo: data.electronicContractNo || null,
electronicContractUrl: data.electronicContractUrl || null,
createdBy: userId,
},
})