import { useEffect } from 'react'; import { Alert, Button, Descriptions, Form, Input, Space, Tag, Typography, message } from 'antd'; import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query'; import { Link, useLocation, useNavigate, useParams, useSearchParams } from 'react-router-dom'; import { api } from '../api/client'; import { FormDrawer } from './FormDrawer'; import { CostObjectFields } from './CostObjectFields'; import { FileAttachments } from './FileAttachments'; import { belongLabel, CostTargetLink } from './FinanceCells'; import { COST_TYPE_LABEL, LOAN_STATUS_META, money } from './financeStatus'; type Loan = { id: string; loanNo: string; costType: string; amount: string | number; remaining?: number; purpose: string; status: string; remark?: string | null; createdAt?: string; bidCase?: { id: string; bidNo: string; name?: string; externalNo?: string } | null; project?: { id?: string; projectNo: string; name?: string } | null; applicant?: { displayName: string; employee?: { department?: { name?: string } } }; myActions?: { canEdit: boolean; canSubmit: boolean; canDelete?: boolean; canReview: boolean; canOffset: boolean }; }; export default function LoanDetailPage() { const { id } = useParams(); const [params] = useSearchParams(); const location = useLocation(); const onDesk = location.pathname.startsWith('/finance'); const listPath = onDesk ? '/finance/loans' : '/office/expenses'; const selfPath = onDesk ? '/finance/loans' : '/office/loans'; const isNew = id === 'new' || !id; const navigate = useNavigate(); const qc = useQueryClient(); const [form] = Form.useForm(); const q = useQuery({ queryKey: ['loan', id], enabled: !isNew && Boolean(id), queryFn: () => api.get(`/loans/${id}`), }); const row = q.data?.data as Loan | undefined; useEffect(() => { if (isNew) { form.setFieldsValue({ costType: params.get('costType') || 'BID', bidCaseId: params.get('bidCaseId') || undefined, projectId: params.get('projectId') || undefined, purpose: params.get('purpose') || undefined, }); return; } if (!row) return; form.setFieldsValue({ costType: row.costType, bidCaseId: row.bidCase?.id, projectId: row.project ? (row as Loan & { project?: { id?: string } }).project?.id : undefined, amount: Number(row.amount), purpose: row.purpose, remark: row.remark || undefined, }); }, [isNew, row, form, params]); const saveMut = useMutation({ mutationFn: (body: Record) => (isNew ? api.post('/loans', body) : api.patch(`/loans/${id}`, body)), onSuccess: async (res: { data?: { id?: string } }) => { message.success('已保存'); await qc.invalidateQueries(); if (isNew && res.data?.id) navigate(`${selfPath}/${res.data.id}`, { replace: true }); }, onError: (e: Error) => message.error(e.message), }); const submitMut = useMutation({ mutationFn: async (savedId?: string) => { const target = savedId || id; if (!target || target === 'new') throw new Error('请先保存'); return api.post(`/loans/${target}/submit`); }, onSuccess: async () => { message.success('已提交财务确认'); await qc.invalidateQueries(); }, onError: (e: Error) => message.error(e.message), }); const reviewMut = useMutation({ mutationFn: (body: { result: 'APPROVED' | 'REJECTED'; comment?: string }) => api.post(`/loans/${id}/reviews`, body), onSuccess: async () => { message.success('已办理'); await qc.invalidateQueries(); }, onError: (e: Error) => message.error(e.message), }); const delMut = useMutation({ mutationFn: () => api.delete(`/loans/${id}`), onSuccess: () => { message.success('已删除'); navigate(listPath); }, onError: (e: Error) => message.error(e.message), }); const meta = LOAN_STATUS_META[row?.status || 'DRAFT'] || LOAN_STATUS_META.DRAFT; const editable = isNew || row?.myActions?.canEdit; return ( navigate(listPath)} hideOk createdAt={isNew ? undefined : row?.createdAt} > {row ? ( {meta.text} {COST_TYPE_LABEL[row.costType] || row.costType} 未还 {money(row.remaining ?? row.amount)} ) : null} {row?.myActions?.canReview ? ( } /> ) : null} {row?.myActions?.canOffset ? ( 去冲账 } /> ) : null} {!editable && row ? ( {money(row.amount)} {money(row.remaining)} {row.purpose} {row.applicant?.displayName || '—'} {belongLabel(row)} {row.remark || '—'} ) : (
saveMut.mutate(v)}> {row?.myActions?.canDelete ? ( ) : null} )} {!editable && row ? : null} {!editable && row?.myActions?.canSubmit ? ( ) : null} 财务确认借款后才会放款记账;报销通过才冲减未还金额。
); }