2968484d2d
- Money.tsx (2260行→54行): 拆分为 money/ 子目录4个组件, React.lazy二级分割 - AIAssistant.tsx (2038行→63行): 拆分为 ai-assistant/ 子目录6个组件, React.lazy二级分割 - xlsx改为动态导入, OvertimeTab从345KB降至12.7KB - api-services.ts: 请求参数 any→Record<string,unknown> - 移除前端3处console.log残留 - 后端console替换为pino logger - 前后端未使用import/变量清理 - Zod schema验证: termination/platform/special-status/work-process - 新增 leave.routes.ts, acceptance-test.routes.ts - UI组件: PageGuide, QueryError, Stepper
155 lines
8.0 KiB
TypeScript
155 lines
8.0 KiB
TypeScript
import { useState } from 'react'
|
|
import { toast } from 'sonner'
|
|
import { useMutation } from '@tanstack/react-query'
|
|
import { socialInsuranceApi } from '../../lib/api-services'
|
|
import { Input } from '../../components/ui/Input'
|
|
|
|
const fmt = (n: number) => (n || 0).toLocaleString('zh-CN', { minimumFractionDigits: 2, maximumFractionDigits: 2 })
|
|
|
|
/** 月度办理社保行组件(可展开查看各险种明细,支持修改基数) */
|
|
export function MonthlyRow({ item: i, type, onCorrected }: { item: any; type: 'add' | 'sub' | 'normal'; onCorrected?: () => void }) {
|
|
const [expanded, setExpanded] = useState(false)
|
|
const [editing, setEditing] = useState(false)
|
|
const [editBase, setEditBase] = useState(i.base?.toString() || '')
|
|
const typeLabel = type === 'add' ? (i.changeType === 'CITY_CHANGE' ? '新增(城市变更)' : '新增') : type === 'sub' ? (i.changeType === 'CITY_CHANGE' ? '减员(城市变更)' : '减员') : '正常'
|
|
const typeClass = type === 'add' ? 'bg-green-50 text-safe' : type === 'sub' ? 'bg-red-50 text-danger' : 'bg-gray-100 text-gray-500'
|
|
const d = i.detail
|
|
|
|
const correctMutation = useMutation({
|
|
mutationFn: (data: { base: number }) => socialInsuranceApi.correctRecord('social', i.recordId, data),
|
|
onSuccess: () => {
|
|
setEditing(false)
|
|
toast.success('基数已修改')
|
|
onCorrected?.()
|
|
},
|
|
onError: () => toast.error('修改失败'),
|
|
})
|
|
|
|
const handleSaveBase = () => {
|
|
const val = Number(editBase) || 0
|
|
if (val <= 0) { toast.error('基数必须大于0'); return }
|
|
correctMutation.mutate({ base: val })
|
|
}
|
|
|
|
return (
|
|
<>
|
|
<tr className="border-b last:border-0 hover:bg-gray-50 cursor-pointer" onClick={() => setExpanded(!expanded)}>
|
|
<td className="py-1.5">{i.name} {d && <span className="text-gray-300 text-xs">{expanded ? '▾' : '▸'}</span>}</td>
|
|
<td className="py-1.5 text-gray-500">{i.department}</td>
|
|
<td className="py-1.5"><span className={`px-2 py-0.5 rounded text-xs ${typeClass}`}>{typeLabel}</span></td>
|
|
<td className="py-1.5 text-right">
|
|
{editing ? (
|
|
<span onClick={(e) => e.stopPropagation()} className="inline-flex items-center gap-1">
|
|
<Input type="number" step="0.01" min="0" className="!w-24 text-right text-xs" value={editBase}
|
|
onChange={(e) => setEditBase(e.target.value)} autoFocus />
|
|
<button className="text-xs text-primary hover:underline" onClick={handleSaveBase} disabled={correctMutation.isPending}>
|
|
{correctMutation.isPending ? '...' : '保存'}
|
|
</button>
|
|
<button className="text-xs text-gray-400 hover:underline" onClick={() => { setEditing(false); setEditBase(i.base?.toString() || '') }}>取消</button>
|
|
</span>
|
|
) : (
|
|
<span className="inline-flex items-center gap-1">
|
|
¥{fmt(i.base)}
|
|
{i.recordId && type !== 'sub' && (
|
|
<button className="text-xs text-gray-400 hover:text-primary" onClick={(e) => { e.stopPropagation(); setEditing(true); setEditBase(i.base?.toString() || '') }}>
|
|
修改
|
|
</button>
|
|
)}
|
|
</span>
|
|
)}
|
|
</td>
|
|
<td className="py-1.5 text-right text-danger">{d ? `¥${fmt(d.totalOrg)}` : '-'}</td>
|
|
<td className="py-1.5 text-right text-warning">{d ? `¥${fmt(d.totalEmp)}` : '-'}</td>
|
|
<td className="py-1.5 text-right font-medium text-primary">{d ? `¥${fmt(d.total)}` : '-'}</td>
|
|
<td className="py-1.5 text-gray-400 text-xs">{type === 'add' ? `${i.startMonth} →` : type === 'sub' ? `→ ${i.endMonth}` : `${i.startMonth} ~ ${i.endMonth || '在保'}`}</td>
|
|
</tr>
|
|
{expanded && d && (
|
|
<tr className="bg-gray-50/50">
|
|
<td colSpan={8} className="py-2 px-8">
|
|
<table className="w-full text-xs">
|
|
<thead>
|
|
<tr className="border-b text-gray-400">
|
|
<th className="py-1 text-left">险种</th>
|
|
<th className="py-1 text-right">企业比例</th>
|
|
<th className="py-1 text-right">个人比例</th>
|
|
<th className="py-1 text-right">企业缴纳</th>
|
|
<th className="py-1 text-right">个人缴纳</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
{d.items.map((item: any) => (
|
|
<tr key={item.name} className="border-b last:border-0">
|
|
<td className="py-1">{item.name}</td>
|
|
<td className="py-1 text-right text-gray-500">{item.orgRate}%</td>
|
|
<td className="py-1 text-right text-gray-500">{item.empRate > 0 ? `${item.empRate}%` : '-'}</td>
|
|
<td className="py-1 text-right">¥{fmt(item.orgAmount)}</td>
|
|
<td className="py-1 text-right">{item.empAmount > 0 ? `¥${fmt(item.empAmount)}` : '-'}</td>
|
|
</tr>
|
|
))}
|
|
</tbody>
|
|
</table>
|
|
</td>
|
|
</tr>
|
|
)}
|
|
</>
|
|
)
|
|
}
|
|
|
|
/** 月度办理公积金行组件(支持修改基数) */
|
|
export function MonthlyHousingRow({ item: i, type, onCorrected }: { item: any; type: 'add' | 'sub' | 'normal'; onCorrected?: () => void }) {
|
|
const [editing, setEditing] = useState(false)
|
|
const [editBase, setEditBase] = useState(i.base?.toString() || '')
|
|
const typeLabel = type === 'add' ? (i.changeType === 'CITY_CHANGE' ? '新增(城市变更)' : '新增') : type === 'sub' ? (i.changeType === 'CITY_CHANGE' ? '减员(城市变更)' : '减员') : '正常'
|
|
const typeClass = type === 'add' ? 'bg-green-50 text-safe' : type === 'sub' ? 'bg-red-50 text-danger' : 'bg-gray-100 text-gray-500'
|
|
const d = i.detail
|
|
|
|
const correctMutation = useMutation({
|
|
mutationFn: (data: { base: number }) => socialInsuranceApi.correctRecord('housing', i.recordId, data),
|
|
onSuccess: () => {
|
|
setEditing(false)
|
|
toast.success('基数已修改')
|
|
onCorrected?.()
|
|
},
|
|
onError: () => toast.error('修改失败'),
|
|
})
|
|
|
|
const handleSaveBase = () => {
|
|
const val = Number(editBase) || 0
|
|
if (val <= 0) { toast.error('基数必须大于0'); return }
|
|
correctMutation.mutate({ base: val })
|
|
}
|
|
|
|
return (
|
|
<tr className="border-b last:border-0 hover:bg-gray-50">
|
|
<td className="py-1.5">{i.name}</td>
|
|
<td className="py-1.5 text-gray-500">{i.department}</td>
|
|
<td className="py-1.5"><span className={`px-2 py-0.5 rounded text-xs ${typeClass}`}>{typeLabel}</span></td>
|
|
<td className="py-1.5 text-right">
|
|
{editing ? (
|
|
<span className="inline-flex items-center gap-1">
|
|
<Input type="number" step="0.01" min="0" className="!w-24 text-right text-xs" value={editBase}
|
|
onChange={(e) => setEditBase(e.target.value)} autoFocus />
|
|
<button className="text-xs text-primary hover:underline" onClick={handleSaveBase} disabled={correctMutation.isPending}>
|
|
{correctMutation.isPending ? '...' : '保存'}
|
|
</button>
|
|
<button className="text-xs text-gray-400 hover:underline" onClick={() => { setEditing(false); setEditBase(i.base?.toString() || '') }}>取消</button>
|
|
</span>
|
|
) : (
|
|
<span className="inline-flex items-center gap-1">
|
|
¥{fmt(i.base)}
|
|
{i.recordId && type !== 'sub' && (
|
|
<button className="text-xs text-gray-400 hover:text-primary" onClick={() => { setEditing(true); setEditBase(i.base?.toString() || '') }}>
|
|
修改
|
|
</button>
|
|
)}
|
|
</span>
|
|
)}
|
|
</td>
|
|
<td className="py-1.5 text-right text-danger">{d ? `¥${fmt(d.orgAmount)}` : '-'}</td>
|
|
<td className="py-1.5 text-right text-warning">{d ? `¥${fmt(d.empAmount)}` : '-'}</td>
|
|
<td className="py-1.5 text-right font-medium text-primary">{d ? `¥${fmt(d.total)}` : '-'}</td>
|
|
<td className="py-1.5 text-gray-400 text-xs">{type === 'add' ? `${i.startMonth} →` : type === 'sub' ? `→ ${i.endMonth}` : `${i.startMonth} ~ ${i.endMonth || '在保'}`}</td>
|
|
</tr>
|
|
)
|
|
}
|