fix: connect remaining sms pages to real backend

This commit is contained in:
hectorzhao
2026-07-02 19:30:04 +08:00
parent 06562e42dd
commit 131f344ac4
50 changed files with 2074 additions and 6400 deletions
+48 -77
View File
@@ -1,49 +1,42 @@
import { useMemo } from 'react';
import { useEffect, useMemo, useState } from 'react';
import { BarChart3 } from 'lucide-react';
import { adminApi, type DashboardResponse } from '@/api/adminApi';
import { Breadcrumb, Button, Chart, Tag } from '@/components/ui';
import { auditTrend, channelShare, customerGrowth, hourlySendTrend } from '@/mock/chartData';
import { adminService } from '@/mock';
import { createBarOption, createLineOption, createPieOption } from '@/theme/chartOptions';
import { createBarOption, createPieOption } from '@/theme/chartOptions';
export function AdminAnalyticsPage() {
const overview = adminService.getOverview();
const customers = adminService.getCustomers();
const [dashboard, setDashboard] = useState<DashboardResponse | null>(null);
const [tenantStats, setTenantStats] = useState<Array<{ tenantId: string | null; _count: { _all: number }; _sum: { amountCents?: number | null } }>>([]);
const [channelStats, setChannelStats] = useState<Array<{ channelId: string | null; _count: { _all: number }; _sum: { amountCents?: number | null } }>>([]);
const [error, setError] = useState('');
const sendTrendOption = useMemo(
() => createLineOption({
labels: hourlySendTrend.map((item) => item.time),
series: [
{ name: '提交量', data: hourlySendTrend.map((item) => item.sent) },
{ name: '成功量', data: hourlySendTrend.map((item) => item.success) },
],
}),
[],
);
function loadData() {
Promise.all([
adminApi.getDashboard(),
adminApi.listStatistics({ groupBy: 'tenantId' }),
adminApi.listStatistics({ groupBy: 'channelId' }),
])
.then(([dashboardData, tenantData, channelData]) => {
setDashboard(dashboardData);
setTenantStats((Array.isArray(tenantData) ? tenantData : []) as Array<{ tenantId: string | null; _count: { _all: number }; _sum: { amountCents?: number | null } }>);
setChannelStats((Array.isArray(channelData) ? channelData : []) as Array<{ channelId: string | null; _count: { _all: number }; _sum: { amountCents?: number | null } }>);
setError('');
})
.catch((failure: Error) => setError(failure.message || '统计数据加载失败'));
}
const auditTrendOption = useMemo(
() => createBarOption({
labels: auditTrend.map((item) => item.day),
series: [
{ name: '通过', data: auditTrend.map((item) => item.approved) },
{ name: '驳回', data: auditTrend.map((item) => item.rejected) },
{ name: '待审', data: auditTrend.map((item) => item.pending) },
],
}),
[],
);
useEffect(() => {
loadData();
}, []);
const customerGrowthOption = useMemo(
() => createLineOption({
labels: customerGrowth.map((item) => item.month),
series: [
{ name: '活跃客户', data: customerGrowth.map((item) => item.active) },
{ name: '新增客户', data: customerGrowth.map((item) => item.new) },
],
}),
[],
);
const tenantOption = useMemo(() => createBarOption({
labels: tenantStats.map((item) => item.tenantId ?? '未绑定企业'),
series: [{ name: '发送量', data: tenantStats.map((item) => item._count._all) }],
}), [tenantStats]);
const channelShareOption = useMemo(() => createPieOption({ data: channelShare }), []);
const channelOption = useMemo(() => createPieOption({
data: channelStats.map((item) => ({ name: item.channelId ?? '未分配通道', value: item._count._all })),
}), [channelStats]);
return (
<section className="page-stack">
@@ -51,24 +44,25 @@ export function AdminAnalyticsPage() {
<div>
<Breadcrumb items={['数据统计']} />
</div>
<Button icon={<BarChart3 size={16} />} variant="ghost"></Button>
<Button icon={<BarChart3 size={16} />} onClick={loadData} variant="ghost"></Button>
</div>
{error ? <p className="form-error">{error}</p> : null}
<div className="dashboard-grid">
<div className="surface metric-card">
<span></span>
<strong>{overview.todaySubmissions}</strong>
<small></small>
<span></span>
<strong>{dashboard?.today.sent.toLocaleString('zh-CN') ?? 0}</strong>
<small></small>
</div>
<div className="surface metric-card">
<span></span>
<strong>{customers.filter((item) => item.status === 'active').length}</strong>
<small></small>
<span></span>
<strong>{dashboard?.today.successRate ?? 0}%</strong>
<small></small>
</div>
<div className="surface metric-card">
<span></span>
<strong>{overview.channelHealth}%</strong>
<small> 24 </small>
<span></span>
<strong>{dashboard?.pendingAuditCount ?? 0}</strong>
<small>///</small>
</div>
</div>
@@ -76,45 +70,22 @@ export function AdminAnalyticsPage() {
<div className="surface chart-card">
<div className="section-heading">
<div>
<h2></h2>
<p className="muted"> 3 </p>
<h2></h2>
<p className="muted"></p>
</div>
<Tag tone="info"></Tag>
<Tag tone="info"></Tag>
</div>
<Chart height={320} option={sendTrendOption} />
<Chart height={320} option={tenantOption} />
</div>
<div className="surface chart-card">
<div className="section-heading">
<div>
<h2></h2>
<p className="muted"></p>
<p className="muted"></p>
</div>
<Tag tone="accent"></Tag>
</div>
<Chart height={320} option={channelShareOption} />
</div>
</div>
<div className="chart-grid">
<div className="surface chart-card">
<div className="section-heading">
<div>
<h2></h2>
<p className="muted"> 7 </p>
</div>
<Tag tone="warning"></Tag>
</div>
<Chart height={320} option={auditTrendOption} />
</div>
<div className="surface chart-card">
<div className="section-heading">
<div>
<h2></h2>
<p className="muted"></p>
</div>
<Tag tone="success"></Tag>
</div>
<Chart height={320} option={customerGrowthOption} />
<Chart height={320} option={channelOption} />
</div>
</div>
</section>