Files
lislgosms/src/apps/client/ClientBillingPage.tsx
T

78 lines
3.9 KiB
TypeScript

import { useEffect, useState } from 'react';
import { WalletCards } from 'lucide-react';
import { MoneyText, Pagination, Tag } from '@/components/ui';
import { clientApi, type RechargeOrder } from '@/api/adminApi';
import { formatCents } from '@/utils/currency';
import { formatDateTime } from '@/utils/dateTime';
export function ClientBillingPage() {
const [balanceCents, setBalanceCents] = useState(0);
const [creditCents, setCreditCents] = useState(0);
const [orders, setOrders] = useState<RechargeOrder[]>([]);
const [page, setPage] = useState(1);
const [loading, setLoading] = useState(true);
const [error, setError] = useState('');
const [total, setTotal] = useState(0);
const pageSize = 10;
const totalPages = Math.max(1, Math.ceil(total / pageSize));
const currentPage = Math.min(page, totalPages);
const visibleOrders = orders;
useEffect(() => {
setLoading(true);
Promise.all([clientApi.getDashboard(), clientApi.listOrdersPage({ page, pageSize })])
.then(([dashboard, result]) => {
setBalanceCents(dashboard.accounts[0]?.balanceCents ?? 0);
setCreditCents(dashboard.accounts[0]?.creditCents ?? 0);
setOrders(result.items);
setTotal(result.total);
setError('');
})
.catch((reason: Error) => setError(reason.message || '账户信息加载失败'))
.finally(() => setLoading(false));
}, [page]);
return (
<section className="page-stack client-billing-page">
<div className="sms-send-title">
<span className="sms-send-title__icon"><WalletCards size={22} /></span>
<h1>账户余额</h1>
</div>
<div className="dashboard-grid enterprise-summary-grid">
<div className="surface mini-status-card">
<WalletCards size={22} />
<div><span>现金余额</span><strong className="client-billing-amount">¥{formatCents(balanceCents)}</strong><small>充值和扣费后的账面余额。</small></div>
</div>
<div className="surface mini-status-card">
<WalletCards size={22} />
<div><span>可用发送额度</span><strong className="client-billing-amount">¥{formatCents(balanceCents + creditCents)}</strong><small>大于 0 时可以发送短信。</small></div>
</div>
</div>
<div className="surface section-stack">
<div className="section-heading"><div><h2>充值记录</h2><p className="muted">如需充值,请联系平台运营人员。</p></div><Tag tone="info">{total} </Tag></div>
{loading ? <p className="muted">正在加载账户信息...</p> : null}
{error ? <p className="form-error">{error}</p> : null}
{!loading && !error ? (
<div className="ui-table-wrap">
<table className="ui-table">
<thead><tr><th>订单号</th><th>充值时间</th><th>充值金额</th><th>状态</th><th>备注</th></tr></thead>
<tbody>
{visibleOrders.length === 0 ? <tr><td className="ui-table__empty" colSpan={5}>暂无充值记录</td></tr> : visibleOrders.map((order) => (
<tr key={order.id}>
<td><span className="table-mono-id">{order.orderNo}</span></td>
<td>{formatDateTime(order.paidAt ?? order.createdAt)}</td>
<td><MoneyText>¥{formatCents(order.amountCents)}</MoneyText></td>
<td><Tag tone={order.status === 'paid' ? 'success' : 'warning'}>{order.status === 'paid' ? '已入账' : order.status}</Tag></td>
<td>{order.remark || '-'}</td>
</tr>
))}
</tbody>
</table>
</div>
) : null}
<Pagination nextDisabled={currentPage >= totalPages} onNext={() => setPage((value) => Math.min(totalPages, value + 1))} onPrevious={() => setPage((value) => Math.max(1, value - 1))} page={currentPage} totalPages={totalPages} onPageChange={setPage} previousDisabled={currentPage <= 1} total={total} />
</div>
</section>
);
}