feat: 社保AI建议、合同附件多文件上传预览、删除合同、扩展上传格式
This commit is contained in:
@@ -117,7 +117,7 @@ function BatchManager() {
|
||||
setShowCreateModal(false)
|
||||
toast.success('发薪批次已创建')
|
||||
},
|
||||
onError: () => toast.error('创建失败'),
|
||||
onError: (err: any) => toast.error(err?.response?.data?.error?.message || '创建失败'),
|
||||
})
|
||||
|
||||
const deleteBatchMutation = useMutation({
|
||||
@@ -140,11 +140,26 @@ function BatchManager() {
|
||||
onError: () => toast.error('重命名失败'),
|
||||
})
|
||||
|
||||
const unarchiveBatchMutation = useMutation({
|
||||
mutationFn: (batchId: string) => api.post(`/payroll2/batches/${batchId}/unarchive`),
|
||||
onSuccess: () => {
|
||||
queryClient.invalidateQueries({ queryKey: ['batches'] })
|
||||
queryClient.invalidateQueries({ queryKey: ['batch-check'] })
|
||||
queryClient.invalidateQueries({ queryKey: ['dashboard'] })
|
||||
toast.success('已取消归档,批次恢复为草稿状态')
|
||||
},
|
||||
onError: (err: any) => toast.error(err?.response?.data?.error?.message || '取消归档失败'),
|
||||
})
|
||||
|
||||
const [renamingId, setRenamingId] = useState<string | null>(null)
|
||||
const [renameValue, setRenameValue] = useState('')
|
||||
|
||||
if (selectedBatchId) {
|
||||
return <BatchDetail batchId={selectedBatchId} onBack={() => setSelectedBatchId(null)} />
|
||||
return <BatchDetail batchId={selectedBatchId} onBack={() => {
|
||||
queryClient.invalidateQueries({ queryKey: ['batches'] })
|
||||
queryClient.invalidateQueries({ queryKey: ['batch-check'] })
|
||||
setSelectedBatchId(null)
|
||||
}} />
|
||||
}
|
||||
|
||||
return (
|
||||
@@ -190,7 +205,14 @@ function BatchManager() {
|
||||
<span className="text-xs text-gray-500 whitespace-nowrap shrink-0">{batches.length} 个批次</span>
|
||||
)}
|
||||
<div className="flex-1" />
|
||||
<Button onClick={() => { setCreateType('REGULAR'); setShowCreateModal(true) }} className="shrink-0">
|
||||
<Button onClick={() => {
|
||||
const hasDraft = batches?.some((b: any) => b.status === 'DRAFT' && b.month === month)
|
||||
if (hasDraft) {
|
||||
toast.error('当月存在未归档的批次,请先归档后再创建新批次')
|
||||
return
|
||||
}
|
||||
setCreateType('REGULAR'); setShowCreateModal(true)
|
||||
}} className="shrink-0">
|
||||
<Plus className="w-4 h-4 mr-1" />创建发薪批次
|
||||
</Button>
|
||||
</div>
|
||||
@@ -353,6 +375,21 @@ function BatchManager() {
|
||||
</button>
|
||||
</>
|
||||
)}
|
||||
{batch.status === 'ARCHIVED' && (
|
||||
<button
|
||||
onClick={async () => {
|
||||
if (await confirm({ title: '取消归档', message: `确认取消归档「${batch.name}」?取消后批次恢复为草稿状态,对应的工资条和个税累计将被撤销。`, variant: 'primary' })) {
|
||||
unarchiveBatchMutation.mutate(batch.id)
|
||||
}
|
||||
}}
|
||||
disabled={unarchiveBatchMutation.isPending}
|
||||
className="p-1 rounded hover:bg-amber-50 text-gray-500 hover:text-warning transition-colors"
|
||||
aria-label="取消归档"
|
||||
title="取消归档"
|
||||
>
|
||||
<Archive className="w-3.5 h-3.5" />
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
@@ -391,12 +428,16 @@ function BatchDetail({ batchId, onBack }: { batchId: string; onBack: () => void
|
||||
api.put(`/payroll2/batches/${batchId}/entries/${employeeId}`, data),
|
||||
onSuccess: () => {
|
||||
queryClient.invalidateQueries({ queryKey: ['batch-detail'] })
|
||||
queryClient.invalidateQueries({ queryKey: ['batches'] })
|
||||
},
|
||||
})
|
||||
|
||||
const removeEmployeeMutation = useMutation({
|
||||
mutationFn: (employeeId: string) => api.delete(`/payroll2/batches/${batchId}/employees/${employeeId}`),
|
||||
onSuccess: () => queryClient.invalidateQueries({ queryKey: ['batch-detail'] }),
|
||||
onSuccess: () => {
|
||||
queryClient.invalidateQueries({ queryKey: ['batch-detail'] })
|
||||
queryClient.invalidateQueries({ queryKey: ['batches'] })
|
||||
},
|
||||
})
|
||||
|
||||
const archiveMutation = useMutation({
|
||||
@@ -410,10 +451,23 @@ function BatchDetail({ batchId, onBack }: { batchId: string; onBack: () => void
|
||||
},
|
||||
})
|
||||
|
||||
const unarchiveMutation = useMutation({
|
||||
mutationFn: () => api.post(`/payroll2/batches/${batchId}/unarchive`),
|
||||
onSuccess: () => {
|
||||
queryClient.invalidateQueries({ queryKey: ['batch-detail'] })
|
||||
queryClient.invalidateQueries({ queryKey: ['batches'] })
|
||||
queryClient.invalidateQueries({ queryKey: ['batch-check'] })
|
||||
queryClient.invalidateQueries({ queryKey: ['dashboard'] })
|
||||
toast.success('已取消归档,批次恢复为草稿状态')
|
||||
},
|
||||
onError: (err: any) => toast.error(err?.response?.data?.error?.message || '取消归档失败'),
|
||||
})
|
||||
|
||||
const importOvertimeMutation = useMutation({
|
||||
mutationFn: () => api.post(`/payroll/overtime/import-to-batch/${batchId}`),
|
||||
onSuccess: (res: any) => {
|
||||
queryClient.invalidateQueries({ queryKey: ['batch-detail'] })
|
||||
queryClient.invalidateQueries({ queryKey: ['batches'] })
|
||||
queryClient.invalidateQueries({ queryKey: ['overtime-records'] })
|
||||
if (res.data?.imported > 0) {
|
||||
toast.success(`成功导入 ${res.data.imported} 条加班费记录`)
|
||||
@@ -443,6 +497,7 @@ function BatchDetail({ batchId, onBack }: { batchId: string; onBack: () => void
|
||||
},
|
||||
onSuccess: (data: any) => {
|
||||
queryClient.invalidateQueries({ queryKey: ['batch-detail'] })
|
||||
queryClient.invalidateQueries({ queryKey: ['batches'] })
|
||||
setPayrollImportResult(data)
|
||||
if (data.updated > 0) {
|
||||
toast.success(`成功更新 ${data.updated} 条工资记录`)
|
||||
@@ -640,11 +695,25 @@ function BatchDetail({ batchId, onBack }: { batchId: string; onBack: () => void
|
||||
</div>
|
||||
)}
|
||||
{isArchived && (
|
||||
<a href={`/api/v1/payroll2/batches/${batchId}/export?format=csv`} download>
|
||||
<Button variant="secondary" size="sm">
|
||||
<Download className="w-4 h-4 mr-1" />银行代发文件
|
||||
<div className="flex gap-2">
|
||||
<a href={`/api/v1/payroll2/batches/${batchId}/export?format=csv`} download>
|
||||
<Button variant="secondary" size="sm">
|
||||
<Download className="w-4 h-4 mr-1" />银行代发文件
|
||||
</Button>
|
||||
</a>
|
||||
<Button
|
||||
variant="secondary"
|
||||
size="sm"
|
||||
onClick={async () => {
|
||||
if (await confirm({ title: '取消归档', message: '确认取消归档?取消后批次恢复为草稿状态,对应的工资条和个税累计将被撤销。', variant: 'primary' })) {
|
||||
unarchiveMutation.mutate()
|
||||
}
|
||||
}}
|
||||
disabled={unarchiveMutation.isPending}
|
||||
>
|
||||
{unarchiveMutation.isPending ? '取消中...' : '取消归档'}
|
||||
</Button>
|
||||
</a>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
@@ -816,6 +885,7 @@ function AddEmployeeToBatch({ batchId, onClose }: { batchId: string; onClose: ()
|
||||
mutationFn: (employeeIds: string[]) => api.post(`/payroll2/batches/${batchId}/employees`, { employeeIds }),
|
||||
onSuccess: () => {
|
||||
queryClient.invalidateQueries({ queryKey: ['batch-detail'] })
|
||||
queryClient.invalidateQueries({ queryKey: ['batches'] })
|
||||
onClose()
|
||||
},
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user