Files
Genarrative/apps/admin-web/src/components/useAdminWriteConfirm.tsx
T
kdletters 8862887baa 新增后台充值退款管理与微信V3退款闭环
新增充值订单查询、通用用户详情、钱包冻结与部分退款操作
接入微信V3退款申请、回调、主动查单、交易账单对账与安全诊断
新增退款占用、权益追回、异常欠账和消费限制事务
补齐Native支付SSE自动收口及退款编号与部分退款校验
同步SpacetimeDB schema、生成绑定、测试与项目文档
2026-07-14 12:16:01 +08:00

110 lines
2.9 KiB
TypeScript

import {useCallback, useEffect, useRef, useState} from 'react';
interface AdminWriteConfirmOptions {
action: string;
target: string;
}
interface PendingConfirm extends AdminWriteConfirmOptions {
resolve: (confirmed: boolean) => void;
}
export function useAdminWriteConfirm() {
const [pendingConfirm, setPendingConfirm] = useState<PendingConfirm | null>(null);
const cancelButtonRef = useRef<HTMLButtonElement | null>(null);
const confirmWrite = useCallback((options: AdminWriteConfirmOptions) => {
return new Promise<boolean>((resolve) => {
setPendingConfirm((current) => {
if (current) {
current.resolve(false);
}
return {...options, resolve};
});
});
}, []);
const closeConfirm = useCallback(
(confirmed: boolean) => {
const current = pendingConfirm;
if (!current) {
return;
}
setPendingConfirm(null);
current.resolve(confirmed);
},
[pendingConfirm],
);
useEffect(() => {
if (!pendingConfirm) {
return;
}
cancelButtonRef.current?.focus();
const handleKeyDown = (event: KeyboardEvent) => {
if (event.key === 'Escape') {
event.preventDefault();
closeConfirm(false);
}
};
window.addEventListener('keydown', handleKeyDown);
return () => window.removeEventListener('keydown', handleKeyDown);
}, [closeConfirm, pendingConfirm]);
const confirmDialog = pendingConfirm ? (
<div
aria-modal="true"
aria-labelledby="admin-write-confirm-title"
className="admin-confirm-backdrop"
role="dialog"
onMouseDown={(event) => {
if (event.target === event.currentTarget) {
closeConfirm(false);
}
}}
>
<section className="admin-confirm-panel">
<div className="admin-panel-heading">
<h3 id="admin-write-confirm-title">确认操作</h3>
<span>{pendingConfirm.action}</span>
</div>
<dl className="admin-info-list">
<div>
<dt>操作</dt>
<dd>{pendingConfirm.action}</dd>
</div>
<div>
<dt>对象</dt>
<dd>{pendingConfirm.target}</dd>
</div>
</dl>
<div className="admin-confirm-warning">该操作会立即影响线上数据</div>
<div className="admin-confirm-actions">
<button
className="admin-secondary-button"
ref={cancelButtonRef}
type="button"
onClick={() => closeConfirm(false)}
>
取消
</button>
<button
className="admin-danger-button"
type="button"
onClick={() => closeConfirm(true)}
>
确认
</button>
</div>
</section>
</div>
) : null;
return {
confirmWrite,
confirmDialog,
isConfirming: pendingConfirm !== null,
};
}