148 行
5.2 KiB
TypeScript
148 行
5.2 KiB
TypeScript
import type { UseMutationResult, UseQueryResult } from "@tanstack/react-query";
|
|
import type { InvoicePoolListResponse } from "../../api/invoicePool";
|
|
import type { ReimbursementListResponse } from "../../api/reimbursements";
|
|
import type { TravelRequestDetail, TravelRequestListResponse } from "../../api/travelRequests";
|
|
import { Button } from "../../components/ui/button";
|
|
|
|
export type InvoiceBindMode = "reimbursement" | "travelOrder";
|
|
|
|
export type InvoiceBindCardProps = {
|
|
listQuery: UseQueryResult<InvoicePoolListResponse, Error>;
|
|
reimbursementQuery: UseQueryResult<ReimbursementListResponse, Error>;
|
|
travelRequestQuery: UseQueryResult<TravelRequestListResponse, Error>;
|
|
travelRequestDetailQuery: UseQueryResult<TravelRequestDetail, Error>;
|
|
bindMutation: UseMutationResult<Record<string, unknown>, Error, void>;
|
|
bindMode: InvoiceBindMode;
|
|
setBindMode: (mode: InvoiceBindMode) => void;
|
|
canManageTravelPayments: boolean;
|
|
selectedInvoiceId: string;
|
|
setSelectedInvoiceId: (id: string) => void;
|
|
selectedReimbursementId: string;
|
|
setSelectedReimbursementId: (id: string) => void;
|
|
selectedTravelRequestId: string;
|
|
setSelectedTravelRequestId: (id: string) => void;
|
|
selectedTravelOrderId: string;
|
|
setSelectedTravelOrderId: (id: string) => void;
|
|
};
|
|
|
|
export function InvoiceBindCard({
|
|
listQuery,
|
|
reimbursementQuery,
|
|
travelRequestQuery,
|
|
travelRequestDetailQuery,
|
|
bindMutation,
|
|
bindMode,
|
|
setBindMode,
|
|
canManageTravelPayments,
|
|
selectedInvoiceId,
|
|
setSelectedInvoiceId,
|
|
selectedReimbursementId,
|
|
setSelectedReimbursementId,
|
|
selectedTravelRequestId,
|
|
setSelectedTravelRequestId,
|
|
selectedTravelOrderId,
|
|
setSelectedTravelOrderId,
|
|
}: InvoiceBindCardProps) {
|
|
const bindableTravelOrders = (travelRequestDetailQuery.data?.travelOrders ?? []).filter((item) =>
|
|
["confirmed", "paid"].includes(item.status),
|
|
);
|
|
|
|
return (
|
|
<div className="card-std p-6 space-y-4">
|
|
<div className="flex items-center justify-between gap-3">
|
|
<h3 className="card-title">绑定业务单据</h3>
|
|
{canManageTravelPayments ? (
|
|
<select
|
|
className="select-std max-w-[180px]"
|
|
value={bindMode}
|
|
onChange={(event) => setBindMode(event.target.value as InvoiceBindMode)}
|
|
>
|
|
<option value="reimbursement">绑定报销</option>
|
|
<option value="travelOrder">绑定差旅订单</option>
|
|
</select>
|
|
) : null}
|
|
</div>
|
|
|
|
<select
|
|
className="select-std"
|
|
data-testid="invoice-bind-invoice"
|
|
onChange={(event) => setSelectedInvoiceId(event.target.value)}
|
|
value={selectedInvoiceId}
|
|
>
|
|
<option value="">选择票据</option>
|
|
{(listQuery.data?.items ?? []).map((item) => (
|
|
<option key={item.id} value={item.id}>
|
|
{item.invoiceCode ? `${item.invoiceCode}-${item.invoiceNumber}` : item.id.substring(0, 8)} ({item.status})
|
|
</option>
|
|
))}
|
|
</select>
|
|
|
|
{bindMode === "reimbursement" ? (
|
|
<select
|
|
className="select-std"
|
|
data-testid="invoice-bind-reimbursement"
|
|
onChange={(event) => setSelectedReimbursementId(event.target.value)}
|
|
value={selectedReimbursementId}
|
|
>
|
|
<option value="">选择报销单</option>
|
|
{(reimbursementQuery.data?.items ?? []).map((item) => (
|
|
<option key={item.id} value={item.id}>
|
|
{item.formNumber ?? item.id} ({item.status})
|
|
</option>
|
|
))}
|
|
</select>
|
|
) : (
|
|
<>
|
|
<select
|
|
className="select-std"
|
|
data-testid="invoice-bind-travel-request"
|
|
onChange={(event) => {
|
|
setSelectedTravelRequestId(event.target.value);
|
|
setSelectedTravelOrderId("");
|
|
}}
|
|
value={selectedTravelRequestId}
|
|
>
|
|
<option value="">选择差旅申请</option>
|
|
{(travelRequestQuery.data?.items ?? []).map((item) => (
|
|
<option key={item.id} value={item.id}>
|
|
{item.requestNo ?? item.purpose} ({item.status})
|
|
</option>
|
|
))}
|
|
</select>
|
|
|
|
<select
|
|
className="select-std"
|
|
data-testid="invoice-bind-travel-order"
|
|
onChange={(event) => setSelectedTravelOrderId(event.target.value)}
|
|
value={selectedTravelOrderId}
|
|
disabled={!selectedTravelRequestId}
|
|
>
|
|
<option value="">选择差旅订单</option>
|
|
{bindableTravelOrders.map((item) => (
|
|
<option key={item.id} value={item.id}>
|
|
{item.externalOrderNo} ({item.status})
|
|
</option>
|
|
))}
|
|
</select>
|
|
</>
|
|
)}
|
|
|
|
<div className="pt-2">
|
|
<Button
|
|
data-testid="invoice-bind-submit"
|
|
className="btn-primary"
|
|
disabled={
|
|
bindMutation.isPending ||
|
|
!selectedInvoiceId ||
|
|
(bindMode === "reimbursement" ? !selectedReimbursementId : !selectedTravelOrderId)
|
|
}
|
|
onClick={() => bindMutation.mutate()}
|
|
type="button"
|
|
>
|
|
{bindMutation.isPending ? "绑定中..." : "绑定票据"}
|
|
</Button>
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|