112 lines
3.7 KiB
JavaScript
112 lines
3.7 KiB
JavaScript
import { useCallback, useEffect, useRef, useState } from "react";
|
|
import { formatCoin, formatPercent } from "../utils/format.js";
|
|
|
|
const MODAL_TRANSITION_MS = 180;
|
|
|
|
export function LuckyGiftPoolModal({ items, onClose }) {
|
|
const [closing, setClosing] = useState(false);
|
|
const closeTimerRef = useRef(null);
|
|
const totals = items.reduce((summary, item) => ({
|
|
payout: summary.payout + item.payout,
|
|
profit: summary.profit + item.profit,
|
|
turnover: summary.turnover + item.turnover
|
|
}), { payout: 0, profit: 0, turnover: 0 });
|
|
|
|
const requestClose = useCallback(() => {
|
|
if (closing) {
|
|
return;
|
|
}
|
|
setClosing(true);
|
|
closeTimerRef.current = window.setTimeout(onClose, MODAL_TRANSITION_MS);
|
|
}, [closing, onClose]);
|
|
|
|
useEffect(() => () => {
|
|
if (closeTimerRef.current) {
|
|
window.clearTimeout(closeTimerRef.current);
|
|
}
|
|
}, []);
|
|
|
|
useEffect(() => {
|
|
const closeOnEscape = (event) => {
|
|
if (event.key === "Escape") {
|
|
requestClose();
|
|
}
|
|
};
|
|
window.addEventListener("keydown", closeOnEscape);
|
|
return () => window.removeEventListener("keydown", closeOnEscape);
|
|
}, [requestClose]);
|
|
|
|
return (
|
|
<div
|
|
className={["pool-detail-modal", closing ? "is-closing" : ""].filter(Boolean).join(" ")}
|
|
role="presentation"
|
|
onMouseDown={(event) => {
|
|
if (event.target === event.currentTarget) {
|
|
requestClose();
|
|
}
|
|
}}
|
|
>
|
|
<div aria-labelledby="lucky-gift-pool-title" aria-modal="true" className="pool-detail-dialog" role="dialog">
|
|
<header className="pool-detail-head">
|
|
<div>
|
|
<h2 id="lucky-gift-pool-title">幸运礼物奖池明细</h2>
|
|
<span>按 pool_id 汇总流水、返奖和利润</span>
|
|
</div>
|
|
<button aria-label="关闭幸运礼物奖池明细" type="button" onClick={requestClose}>
|
|
<span aria-hidden="true" className="modal-close-mark" />
|
|
</button>
|
|
</header>
|
|
|
|
<section className="pool-detail-summary" aria-label="幸运礼物奖池汇总">
|
|
<PoolSummary label="总流水" value={totals.turnover} />
|
|
<PoolSummary label="总返奖" value={totals.payout} />
|
|
<PoolSummary label="总利润" value={totals.profit} tone={totals.profit < 0 ? "negative" : "positive"} />
|
|
</section>
|
|
|
|
<div className="pool-detail-table-wrap">
|
|
<table className="pool-detail-table">
|
|
<thead>
|
|
<tr>
|
|
<th>#</th>
|
|
<th>Pool ID</th>
|
|
<th>流水(金币)</th>
|
|
<th>返奖(金币)</th>
|
|
<th>利润(金币)</th>
|
|
<th>返奖率</th>
|
|
<th>利润率</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
{items.length ? items.map((item, index) => (
|
|
<tr key={item.pool_id}>
|
|
<td>{index + 1}</td>
|
|
<td>{item.pool_id}</td>
|
|
<td>{formatCoin(item.turnover)}</td>
|
|
<td>{formatCoin(item.payout)}</td>
|
|
<td className={item.profit < 0 ? "negative" : "positive"}>{formatCoin(item.profit)}</td>
|
|
<td>{formatPercent(item.payout_rate)}</td>
|
|
<td>{formatPercent(item.profit_rate)}</td>
|
|
</tr>
|
|
)) : (
|
|
<tr>
|
|
<td colSpan={7}>暂无 pool 数据</td>
|
|
</tr>
|
|
)}
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
function PoolSummary({ label, tone = "", value }) {
|
|
return (
|
|
<article className="pool-summary-card">
|
|
<span>{label}</span>
|
|
<strong className={tone}>{formatCoin(value)}</strong>
|
|
<small>金币</small>
|
|
</article>
|
|
);
|
|
}
|