143 行
4.5 KiB
TypeScript
143 行
4.5 KiB
TypeScript
import { useState } from "react";
|
|
import type { ReimbursementStatus } from "../../api/reimbursements";
|
|
import { Button } from "../../components/ui/button";
|
|
import { Input } from "../../components/ui/input";
|
|
import { Textarea } from "../../components/ui/textarea";
|
|
|
|
type PaymentChannel = "bank" | "alipay" | "wechat" | "cash" | "other";
|
|
|
|
export type PayPayload = {
|
|
paymentVoucherNo: string;
|
|
paymentDate: string;
|
|
paymentChannel: PaymentChannel;
|
|
};
|
|
|
|
export interface ReimbursementActionsProps {
|
|
status: ReimbursementStatus;
|
|
canPay: boolean;
|
|
actionError: string;
|
|
submitDisabled?: boolean;
|
|
submitHint?: string;
|
|
submitNotice?: string;
|
|
isPending: boolean;
|
|
onSubmit: () => void;
|
|
onWithdraw: (comment: string) => void;
|
|
onPay: (payload: PayPayload) => void;
|
|
}
|
|
|
|
export function ReimbursementActions({
|
|
status,
|
|
canPay,
|
|
actionError,
|
|
submitDisabled = false,
|
|
submitHint = "",
|
|
submitNotice = "",
|
|
isPending,
|
|
onSubmit,
|
|
onWithdraw,
|
|
onPay,
|
|
}: ReimbursementActionsProps) {
|
|
const [withdrawComment, setWithdrawComment] = useState("申请撤回并补充说明后重提");
|
|
const [paymentVoucherNo, setPaymentVoucherNo] = useState("");
|
|
const [paymentDate, setPaymentDate] = useState("2026-02-08");
|
|
const [paymentChannel, setPaymentChannel] = useState<PaymentChannel>("bank");
|
|
|
|
return (
|
|
<div className="card-std space-y-4 p-6">
|
|
<h3 className="text-base font-semibold text-zinc-900">可执行动作</h3>
|
|
|
|
{actionError ? (
|
|
<p
|
|
className="rounded-lg border border-red-200 bg-red-50 px-3 py-2 text-sm text-red-600"
|
|
data-testid="reimbursement-action-error"
|
|
>
|
|
{actionError}
|
|
</p>
|
|
) : null}
|
|
|
|
<div className="flex flex-wrap gap-2">
|
|
<Button
|
|
data-testid="reimbursement-submit"
|
|
disabled={isPending || !["draft", "rejected"].includes(status) || submitDisabled}
|
|
onClick={onSubmit}
|
|
type="button"
|
|
>
|
|
提交报销
|
|
</Button>
|
|
</div>
|
|
{submitHint ? <p className="text-sm text-zinc-500">{submitHint}</p> : null}
|
|
{submitNotice ? (
|
|
<p className="rounded-lg border border-amber-200 bg-amber-50 px-3 py-2 text-sm text-amber-700">
|
|
{submitNotice}
|
|
</p>
|
|
) : null}
|
|
|
|
<div className="space-y-2 rounded-lg border border-zinc-200 p-4">
|
|
<p className="text-sm font-medium text-zinc-800">撤回报销</p>
|
|
<Textarea
|
|
data-testid="reimbursement-withdraw-comment"
|
|
onChange={(event) => setWithdrawComment(event.target.value)}
|
|
rows={2}
|
|
value={withdrawComment}
|
|
/>
|
|
<Button
|
|
data-testid="reimbursement-withdraw"
|
|
variant="secondary"
|
|
disabled={isPending || status !== "reviewing" || withdrawComment.trim().length < 1}
|
|
onClick={() => onWithdraw(withdrawComment.trim())}
|
|
type="button"
|
|
>
|
|
撤回
|
|
</Button>
|
|
</div>
|
|
|
|
{canPay ? (
|
|
<div className="space-y-2 rounded-lg border border-zinc-200 p-4">
|
|
<p className="text-sm font-medium text-zinc-800">财务支付</p>
|
|
<Input
|
|
data-testid="reimbursement-pay-voucher-no"
|
|
onChange={(event) => setPaymentVoucherNo(event.target.value)}
|
|
placeholder="付款凭证号"
|
|
value={paymentVoucherNo}
|
|
/>
|
|
<Input
|
|
data-testid="reimbursement-pay-date"
|
|
onChange={(event) => setPaymentDate(event.target.value)}
|
|
type="date"
|
|
value={paymentDate}
|
|
/>
|
|
<select
|
|
className="select-std"
|
|
data-testid="reimbursement-pay-channel"
|
|
onChange={(event) =>
|
|
setPaymentChannel(event.target.value as PaymentChannel)
|
|
}
|
|
value={paymentChannel}
|
|
>
|
|
<option value="bank">bank</option>
|
|
<option value="alipay">alipay</option>
|
|
<option value="wechat">wechat</option>
|
|
<option value="cash">cash</option>
|
|
<option value="other">other</option>
|
|
</select>
|
|
<Button
|
|
data-testid="reimbursement-pay"
|
|
variant="secondary"
|
|
disabled={isPending || !["approved", "paid"].includes(status)}
|
|
onClick={() =>
|
|
onPay({
|
|
paymentVoucherNo: paymentVoucherNo.trim(),
|
|
paymentDate,
|
|
paymentChannel,
|
|
})
|
|
}
|
|
type="button"
|
|
>
|
|
执行支付
|
|
</Button>
|
|
</div>
|
|
) : null}
|
|
</div>
|
|
);
|
|
}
|