feat(admin, settlement, dashboard): strengthen permission gating and billing workflows
This commit is contained in:
@@ -165,9 +165,6 @@ export function AgentsPlayersPanel({
|
||||
const viewPlayerLabel = t("players:viewDetail", { defaultValue: "查看玩家详情" });
|
||||
const editPlayerLabel = t("players:editPlayer", { defaultValue: "编辑玩家" });
|
||||
const deletePlayerLabel = t("players:deletePlayer", { defaultValue: "删除玩家" });
|
||||
const settlementCenterLabel = t("playersPanel.gotoSettlementCenter", {
|
||||
defaultValue: "去结算中心",
|
||||
});
|
||||
const profile = useAdminProfile();
|
||||
const boundAgent = profile?.agent ?? null;
|
||||
const isSuperAdmin = profile?.is_super_admin === true;
|
||||
@@ -521,10 +518,15 @@ export function AgentsPlayersPanel({
|
||||
|
||||
async function handlePayBill(): Promise<void> {
|
||||
if (selectedBill === null) return;
|
||||
const amount = parseBillingAmount(payAmount || String(selectedBill.unpaid_amount ?? 0));
|
||||
if (amount === null || amount <= 0 || amount > Number(selectedBill.unpaid_amount ?? 0)) {
|
||||
toast.error(t("playersPanel.paymentAmountInvalid", { defaultValue: "请输入有效的收付金额" }));
|
||||
return;
|
||||
}
|
||||
setBillingBusy(true);
|
||||
try {
|
||||
await postSettlementBillPayment(selectedBill.id, {
|
||||
amount: Number(payAmount || selectedBill.unpaid_amount || 0),
|
||||
amount,
|
||||
method: payMethod.trim() || undefined,
|
||||
proof: payProof.trim() || undefined,
|
||||
});
|
||||
@@ -546,10 +548,15 @@ export function AgentsPlayersPanel({
|
||||
|
||||
async function handleWriteOffBill(): Promise<void> {
|
||||
if (selectedBill === null) return;
|
||||
const reason = badDebtReason.trim();
|
||||
if (!reason) {
|
||||
toast.error(t("playersPanel.badDebtReasonRequired", { defaultValue: "请填写核销原因" }));
|
||||
return;
|
||||
}
|
||||
setBillingBusy(true);
|
||||
try {
|
||||
await postSettlementBillBadDebtWriteOff(selectedBill.id, {
|
||||
reason: badDebtReason.trim() || undefined,
|
||||
reason,
|
||||
});
|
||||
toast.success(t("playersPanel.billWrittenOff", { defaultValue: "已核销坏账" }));
|
||||
await load();
|
||||
@@ -567,6 +574,68 @@ export function AgentsPlayersPanel({
|
||||
}
|
||||
}
|
||||
|
||||
function parseBillingAmount(raw: string): number | null {
|
||||
const value = Number(raw);
|
||||
if (!Number.isFinite(value) || !Number.isInteger(value)) {
|
||||
return null;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
function requestConfirmBillAction(): void {
|
||||
if (selectedBill === null) return;
|
||||
requestConfirm({
|
||||
title: t("playersPanel.confirmBillTitle", { defaultValue: "确认账单?" }),
|
||||
description: t("playersPanel.confirmBillDescription", {
|
||||
defaultValue: "确认后账单会进入待收付状态,请确认金额与玩家无误。",
|
||||
}),
|
||||
confirmLabel: t("agents:settlementBills.confirm", { defaultValue: "确认账单" }),
|
||||
confirmVariant: "default",
|
||||
onConfirm: handleConfirmBill,
|
||||
});
|
||||
}
|
||||
|
||||
function requestPayBillAction(): void {
|
||||
if (selectedBill === null) return;
|
||||
const amount = parseBillingAmount(payAmount || String(selectedBill.unpaid_amount ?? 0));
|
||||
if (amount === null || amount <= 0) {
|
||||
toast.error(t("playersPanel.paymentAmountInvalid", { defaultValue: "请输入大于 0 的整数金额" }));
|
||||
return;
|
||||
}
|
||||
if (amount > Number(selectedBill.unpaid_amount ?? 0)) {
|
||||
toast.error(t("playersPanel.paymentAmountTooLarge", { defaultValue: "收付金额不能超过未结金额" }));
|
||||
return;
|
||||
}
|
||||
|
||||
requestConfirm({
|
||||
title: t("playersPanel.payBillConfirmTitle", { defaultValue: "确认登记收付?" }),
|
||||
description: t("playersPanel.payBillConfirmDescription", {
|
||||
defaultValue: "这会写入收付记录并更新玩家账单金额,请确认金额与凭证无误。",
|
||||
}),
|
||||
confirmLabel: t("agents:settlementBills.paid", { defaultValue: "登记收付" }),
|
||||
confirmVariant: "default",
|
||||
onConfirm: handlePayBill,
|
||||
});
|
||||
}
|
||||
|
||||
function requestWriteOffBillAction(): void {
|
||||
if (selectedBill === null) return;
|
||||
if (!badDebtReason.trim()) {
|
||||
toast.error(t("playersPanel.badDebtReasonRequired", { defaultValue: "请填写核销原因" }));
|
||||
return;
|
||||
}
|
||||
|
||||
requestConfirm({
|
||||
title: t("playersPanel.writeOffBillConfirmTitle", { defaultValue: "确认核销坏账?" }),
|
||||
description: t("playersPanel.writeOffBillConfirmDescription", {
|
||||
defaultValue: "核销会把该玩家账单未结金额归档为坏账记录,请确认已无法收回。",
|
||||
}),
|
||||
confirmLabel: t("agents:settlementBills.confirmBadDebt", { defaultValue: "确认核销" }),
|
||||
confirmVariant: "destructive",
|
||||
onConfirm: handleWriteOffBill,
|
||||
});
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="space-y-4">
|
||||
<ConfirmDialog />
|
||||
@@ -936,7 +1005,7 @@ export function AgentsPlayersPanel({
|
||||
</div>
|
||||
|
||||
{selectedBill.status === "pending_confirm" ? (
|
||||
<Button type="button" className="w-full" disabled={billingBusy} onClick={() => void handleConfirmBill()}>
|
||||
<Button type="button" className="w-full" disabled={billingBusy || confirmBusy} onClick={requestConfirmBillAction}>
|
||||
{t("agents:settlementBills.confirm", { defaultValue: "确认账单" })}
|
||||
</Button>
|
||||
) : null}
|
||||
@@ -967,7 +1036,7 @@ export function AgentsPlayersPanel({
|
||||
})}
|
||||
/>
|
||||
</div>
|
||||
<Button type="button" className="w-full" disabled={billingBusy} onClick={() => void handlePayBill()}>
|
||||
<Button type="button" className="w-full" disabled={billingBusy || confirmBusy} onClick={requestPayBillAction}>
|
||||
{t("agents:settlementBills.paid", { defaultValue: "登记收付" })}
|
||||
</Button>
|
||||
|
||||
@@ -981,7 +1050,7 @@ export function AgentsPlayersPanel({
|
||||
})}
|
||||
/>
|
||||
</div>
|
||||
<Button type="button" variant="destructive" className="w-full" disabled={billingBusy} onClick={() => void handleWriteOffBill()}>
|
||||
<Button type="button" variant="destructive" className="w-full" disabled={billingBusy || confirmBusy} onClick={requestWriteOffBillAction}>
|
||||
{t("agents:settlementBills.confirmBadDebt", { defaultValue: "确认核销" })}
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user