优化: 重新入职弹窗对齐添加员工
1. 移除孕期/医疗期/工伤复选框(重新入职自动重置为false) 2. 缺省入职日期和合同开始日期为今天,默认3年合同结束日期 3. 签约时长与合同结束日期双向自动计算 4. 试用期长度按劳动合同法校验+试用期工资必填及80%校验 5. 试用期工资校验使用员工已有月工资作为基准
This commit is contained in:
+132
-58
@@ -789,26 +789,101 @@ function RehireModal({ employee, onClose, onSubmit, loading, error }: {
|
||||
loading: boolean
|
||||
error: any
|
||||
}) {
|
||||
const todayStr = new Date().toISOString().slice(0, 10)
|
||||
const defaultEndDate = (() => {
|
||||
const d = new Date()
|
||||
d.setFullYear(d.getFullYear() + 3)
|
||||
d.setDate(d.getDate() - 1)
|
||||
return d.toISOString().slice(0, 10)
|
||||
})()
|
||||
const [form, setForm] = useState({
|
||||
hireDate: new Date().toISOString().slice(0, 10),
|
||||
isPregnant: false,
|
||||
isInMedicalPeriod: false,
|
||||
isWorkInjured: false,
|
||||
hireDate: todayStr,
|
||||
contractType: 'FIXED' as 'FIXED' | 'UNFIXED' | 'UNSIGNED',
|
||||
signDate: '',
|
||||
startDate: '',
|
||||
endDate: '',
|
||||
startDate: todayStr,
|
||||
endDate: defaultEndDate,
|
||||
contractYears: 3,
|
||||
probationMonths: 0,
|
||||
probationSalary: 0,
|
||||
})
|
||||
|
||||
// 计算合同月数
|
||||
const contractMonths = (() => {
|
||||
if (form.contractType !== 'FIXED' || !form.startDate) return 0
|
||||
if (form.endDate) {
|
||||
const start = new Date(form.startDate)
|
||||
const end = new Date(form.endDate)
|
||||
return Math.round((end.getTime() - start.getTime()) / (1000 * 60 * 60 * 24 * 30.44))
|
||||
}
|
||||
return form.contractYears * 12
|
||||
})()
|
||||
|
||||
// 试用期上限(劳动合同法第19条)
|
||||
const probationMax = (() => {
|
||||
if (contractMonths >= 36) return 6
|
||||
if (contractMonths >= 12) return 2
|
||||
if (contractMonths >= 3) return 1
|
||||
return 0
|
||||
})()
|
||||
|
||||
const probationError = (() => {
|
||||
if (form.probationMonths <= 0) return ''
|
||||
if (contractMonths > 0 && contractMonths < 3) return '合同不足3个月,不得约定试用期'
|
||||
if (form.probationMonths > probationMax) return `合同${contractMonths}个月,试用期最多${probationMax}个月`
|
||||
return ''
|
||||
})()
|
||||
|
||||
const monthlySalaryNum = employee?.monthlySalary || 0
|
||||
const probationSalaryError = (() => {
|
||||
if (form.probationMonths <= 0) return ''
|
||||
if (form.probationSalary <= 0) return '有试用期时试用期工资必填'
|
||||
if (monthlySalaryNum > 0 && form.probationSalary < monthlySalaryNum * 0.8) {
|
||||
return `试用期工资不得低于转正工资的80%(最低¥${(monthlySalaryNum * 0.8).toFixed(0)})`
|
||||
}
|
||||
return ''
|
||||
})()
|
||||
|
||||
// 合同结束日期自动计算
|
||||
const handleContractYearsChange = (years: number) => {
|
||||
if (!form.startDate || years <= 0) {
|
||||
setForm({ ...form, contractYears: years, endDate: '' })
|
||||
return
|
||||
}
|
||||
const start = new Date(form.startDate)
|
||||
const end = new Date(start)
|
||||
end.setFullYear(end.getFullYear() + years)
|
||||
end.setDate(end.getDate() - 1)
|
||||
setForm({ ...form, contractYears: years, endDate: end.toISOString().slice(0, 10) })
|
||||
}
|
||||
|
||||
// 合同结束日期变更 → 自动计算签约年限
|
||||
const handleEndDateChange = (endDate: string) => {
|
||||
if (!form.startDate || !endDate) {
|
||||
setForm({ ...form, endDate, contractYears: 0 })
|
||||
return
|
||||
}
|
||||
const start = new Date(form.startDate)
|
||||
const end = new Date(endDate)
|
||||
const months = Math.round((end.getTime() - start.getTime()) / (1000 * 60 * 60 * 24 * 30.44))
|
||||
setForm({ ...form, endDate, contractYears: Math.max(1, Math.round(months / 12)) })
|
||||
}
|
||||
|
||||
// 开始日期变更 → 重新计算结束日期
|
||||
const handleStartDateChange = (startDate: string) => {
|
||||
if (form.contractType === 'FIXED' && form.contractYears > 0 && startDate) {
|
||||
const start = new Date(startDate)
|
||||
const end = new Date(start)
|
||||
end.setFullYear(end.getFullYear() + form.contractYears)
|
||||
end.setDate(end.getDate() - 1)
|
||||
setForm({ ...form, startDate, endDate: end.toISOString().slice(0, 10) })
|
||||
} else {
|
||||
setForm({ ...form, startDate })
|
||||
}
|
||||
}
|
||||
|
||||
const handleSubmit = () => {
|
||||
const data: any = {
|
||||
hireDate: new Date(form.hireDate).toISOString(),
|
||||
isPregnant: form.isPregnant,
|
||||
isInMedicalPeriod: form.isInMedicalPeriod,
|
||||
isWorkInjured: form.isWorkInjured,
|
||||
}
|
||||
if (form.contractType !== 'UNSIGNED' && form.startDate) {
|
||||
data.contract = {
|
||||
@@ -824,78 +899,79 @@ function RehireModal({ employee, onClose, onSubmit, loading, error }: {
|
||||
onSubmit(data)
|
||||
}
|
||||
|
||||
const canSubmit = form.hireDate
|
||||
&& (form.contractType === 'UNSIGNED' || form.startDate)
|
||||
&& !probationError && !probationSalaryError
|
||||
|
||||
return (
|
||||
<Modal open onClose={onClose} title={`重新入职 - ${employee.name}`}>
|
||||
<div className="space-y-3">
|
||||
<div className="bg-blue-50 text-blue-700 text-xs px-3 py-2 rounded-md">
|
||||
复用员工已有基本信息(姓名、部门、手机等),只需填写新入职日期和劳动合同。
|
||||
复用员工已有基本信息(姓名、部门、身份证号等),只需填写新入职日期和劳动合同。
|
||||
</div>
|
||||
<div className="grid grid-cols-2 gap-3">
|
||||
<div>
|
||||
<Label>员工</Label>
|
||||
<div className="text-xs text-gray-600 py-1.5">{employee.name}</div>
|
||||
</div>
|
||||
<div>
|
||||
<Label>部门</Label>
|
||||
<div className="text-xs text-gray-600 py-1.5">{employee.department}</div>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<Label>员工</Label>
|
||||
<div className="text-xs text-gray-600">{employee.name} - {employee.department}</div>
|
||||
</div>
|
||||
<div>
|
||||
<Label>新入职日期</Label>
|
||||
<Input
|
||||
type="date"
|
||||
value={form.hireDate}
|
||||
onChange={(e) => setForm({ ...form, hireDate: e.target.value })}
|
||||
/>
|
||||
</div>
|
||||
<div className="flex gap-4 text-xs">
|
||||
<label className="flex items-center gap-1">
|
||||
<input type="checkbox" checked={form.isPregnant} onChange={(e) => setForm({ ...form, isPregnant: e.target.checked })} />
|
||||
孕期
|
||||
</label>
|
||||
<label className="flex items-center gap-1">
|
||||
<input type="checkbox" checked={form.isInMedicalPeriod} onChange={(e) => setForm({ ...form, isInMedicalPeriod: e.target.checked })} />
|
||||
医疗期
|
||||
</label>
|
||||
<label className="flex items-center gap-1">
|
||||
<input type="checkbox" checked={form.isWorkInjured} onChange={(e) => setForm({ ...form, isWorkInjured: e.target.checked })} />
|
||||
工伤
|
||||
</label>
|
||||
<Label>新入职日期 *</Label>
|
||||
<Input type="date" value={form.hireDate} onChange={(e) => setForm({ ...form, hireDate: e.target.value })} />
|
||||
</div>
|
||||
<div className="border-t pt-3">
|
||||
<Label>劳动合同</Label>
|
||||
<Select value={form.contractType} onChange={(e) => setForm({ ...form, contractType: e.target.value as any })}>
|
||||
<Label>合同类型</Label>
|
||||
<Select value={form.contractType} onChange={(e) => setForm({ ...form, contractType: e.target.value as any, endDate: e.target.value === 'UNFIXED' ? '' : form.endDate })}>
|
||||
<option value="FIXED">固定期限</option>
|
||||
<option value="UNFIXED">无固定期限</option>
|
||||
<option value="UNSIGNED">暂不签订</option>
|
||||
</Select>
|
||||
</div>
|
||||
{form.contractType !== 'UNSIGNED' && (
|
||||
<div className="space-y-2">
|
||||
<div className="grid grid-cols-2 gap-2">
|
||||
<div className="space-y-3">
|
||||
<div className="grid grid-cols-2 gap-3">
|
||||
<div>
|
||||
<Label>签订日期</Label>
|
||||
<Input type="date" value={form.signDate} onChange={(e) => setForm({ ...form, signDate: e.target.value })} />
|
||||
<div className="text-xs text-gray-400 mt-0.5">留空表示尚未签订</div>
|
||||
</div>
|
||||
<div>
|
||||
<Label>合同起始日</Label>
|
||||
<Input type="date" value={form.startDate} onChange={(e) => setForm({ ...form, startDate: e.target.value })} />
|
||||
</div>
|
||||
<div><Label>合同开始日期 *</Label><Input type="date" value={form.startDate} onChange={(e) => handleStartDateChange(e.target.value)} /></div>
|
||||
</div>
|
||||
{form.contractType === 'FIXED' && (
|
||||
<div className="grid grid-cols-2 gap-2">
|
||||
<div>
|
||||
<Label>合同到期日</Label>
|
||||
<Input type="date" value={form.endDate} onChange={(e) => setForm({ ...form, endDate: e.target.value })} />
|
||||
<>
|
||||
<div className="grid grid-cols-2 gap-3">
|
||||
<div>
|
||||
<Label>签约时长(年)</Label>
|
||||
<Input type="number" value={form.contractYears} onChange={(e) => handleContractYearsChange(parseInt(e.target.value) || 0)} min={1} />
|
||||
</div>
|
||||
<div>
|
||||
<Label>合同结束日期</Label>
|
||||
<Input type="date" value={form.endDate} onChange={(e) => handleEndDateChange(e.target.value)} />
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<Label>合同年限</Label>
|
||||
<Input type="number" value={form.contractYears} onChange={(e) => setForm({ ...form, contractYears: parseInt(e.target.value) || 3 })} />
|
||||
</div>
|
||||
</div>
|
||||
<div className="text-xs text-gray-400">修改签约时长自动计算结束日期,修改结束日期自动计算签约时长</div>
|
||||
</>
|
||||
)}
|
||||
<div className="grid grid-cols-2 gap-2">
|
||||
<div className="grid grid-cols-2 gap-3">
|
||||
<div>
|
||||
<Label>试用期(月)</Label>
|
||||
<Input type="number" value={form.probationMonths} onChange={(e) => setForm({ ...form, probationMonths: parseInt(e.target.value) || 0 })} />
|
||||
<Input type="number" value={form.probationMonths} onChange={(e) => setForm({ ...form, probationMonths: parseInt(e.target.value) || 0 })} min={0} max={6} />
|
||||
{contractMonths > 0 && (
|
||||
<div className="text-xs text-gray-400 mt-0.5">法定上限:{probationMax}个月</div>
|
||||
)}
|
||||
{probationError && <div className="text-xs text-danger mt-0.5">{probationError}</div>}
|
||||
</div>
|
||||
<div>
|
||||
<Label>试用期工资</Label>
|
||||
<Input type="number" value={form.probationSalary} onChange={(e) => setForm({ ...form, probationSalary: parseFloat(e.target.value) || 0 })} />
|
||||
<Label>试用期工资{form.probationMonths > 0 ? ' *' : ''}</Label>
|
||||
<Input type="number" value={form.probationSalary} onChange={(e) => setForm({ ...form, probationSalary: parseFloat(e.target.value) || 0 })} disabled={form.probationMonths <= 0} />
|
||||
{monthlySalaryNum > 0 && form.probationMonths > 0 && (
|
||||
<div className="text-xs text-gray-400 mt-0.5">不低于转正工资80%(≥¥{(monthlySalaryNum * 0.8).toFixed(0)})</div>
|
||||
)}
|
||||
{probationSalaryError && <div className="text-xs text-danger mt-0.5">{probationSalaryError}</div>}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -907,9 +983,7 @@ function RehireModal({ employee, onClose, onSubmit, loading, error }: {
|
||||
)}
|
||||
<div className="flex justify-end gap-2 pt-2">
|
||||
<Button variant="secondary" onClick={onClose}>取消</Button>
|
||||
<Button onClick={handleSubmit} disabled={loading}>
|
||||
{loading ? '提交中...' : '确认入职'}
|
||||
</Button>
|
||||
<Button onClick={handleSubmit} disabled={loading || !canSubmit}>{loading ? '提交中...' : '确认入职'}</Button>
|
||||
</div>
|
||||
</div>
|
||||
</Modal>
|
||||
|
||||
Reference in New Issue
Block a user