优化双平台后台界面

This commit is contained in:
yml2213
2026-07-05 12:25:18 +08:00
parent 32065c75a3
commit 8f48b96fdc
7 changed files with 545 additions and 363 deletions
+180 -37
View File
@@ -1,48 +1,191 @@
import { Card, Col, Row, Statistic, theme } from 'antd';
import { useEffect, useState } from 'react';
import { accountApi, loginApi, type LoginTaskItem } from '../api/modules';
import { Card, Col, Row, Space, Statistic, Tag, Typography, theme } from 'antd';
import { useEffect, useMemo, useState } from 'react';
import {
accountApi,
cookieApi,
huyaApi,
loginApi,
type HuyaTaskItem,
type LoginTaskItem,
} from '../api/modules';
import { usePermissions } from '../hooks/usePermissions';
const { Title, Text } = Typography;
interface PlatformStats {
accounts: number;
tasks: number;
success: number;
failed: number;
}
interface DashboardStats {
douyu: PlatformStats & {
cookies: number;
};
huya: PlatformStats & {
goods: number;
rechargeGoods: number;
};
}
const EMPTY_STATS: DashboardStats = {
douyu: {
accounts: 0,
tasks: 0,
success: 0,
failed: 0,
cookies: 0,
},
huya: {
accounts: 0,
tasks: 0,
success: 0,
failed: 0,
goods: 0,
rechargeGoods: 0,
},
};
function countFailed<T extends { status: string }>(items: T[]) {
return items.filter((item) => ['failed', 'error'].includes(item.status)).length;
}
function StatCard({
title,
value,
color,
loading,
}: {
title: string;
value: number;
color?: string;
loading?: boolean;
}) {
return (
<Col xs={24} sm={12} lg={6}>
<Card size="small" loading={loading}>
<Statistic title={title} value={value} styles={{ content: color ? { color } : undefined }} />
</Card>
</Col>
);
}
export default function DashboardPage() {
const { token } = theme.useToken();
const [stats, setStats] = useState({ accounts: 0, tasks: 0, success: 0, failed: 0 });
const { can, canAny } = usePermissions();
const [loading, setLoading] = useState(true);
const [stats, setStats] = useState<DashboardStats>(EMPTY_STATS);
const canViewDouyuAccounts = canAny(['account:view_all', 'account:view_assigned']);
const canViewDouyuTasks = canAny(['login:batch', 'login:view_all', 'login:view_assigned']);
const canViewCookies = can('cookie:view');
const canViewHuyaAccounts = can('huya:account');
const canViewHuyaTasks = can('huya:task');
useEffect(() => {
Promise.all([accountApi.list(), loginApi.listTasks()])
.then(([accounts, tasks]) => {
setStats({
accounts: accounts.length,
tasks: tasks.length,
success: tasks.filter((t: LoginTaskItem) => t.status === 'success').length,
failed: tasks.filter((t: LoginTaskItem) => ['failed', 'error'].includes(t.status)).length,
});
})
.catch(() => {});
}, []);
let ignore = false;
async function loadDashboard() {
setLoading(true);
const [
douyuAccountsResult,
douyuTasksResult,
cookiesResult,
huyaAccountsResult,
huyaTasksResult,
huyaGoodsResult,
huyaRechargeGoodsResult,
] = await Promise.allSettled([
canViewDouyuAccounts ? accountApi.list() : Promise.resolve([]),
canViewDouyuTasks ? loginApi.listTasks() : Promise.resolve([]),
canViewCookies ? cookieApi.list() : Promise.resolve([]),
canViewHuyaAccounts ? huyaApi.listAccounts() : Promise.resolve([]),
canViewHuyaTasks ? huyaApi.listTasks() : Promise.resolve([]),
canViewHuyaTasks ? huyaApi.listGoods() : Promise.resolve([]),
canViewHuyaTasks ? huyaApi.listRechargeGoods() : Promise.resolve([]),
]);
if (ignore) return;
const douyuAccounts = douyuAccountsResult.status === 'fulfilled' ? douyuAccountsResult.value : [];
const douyuTasks = douyuTasksResult.status === 'fulfilled' ? douyuTasksResult.value : [];
const cookies = cookiesResult.status === 'fulfilled' ? cookiesResult.value : [];
const huyaAccounts = huyaAccountsResult.status === 'fulfilled' ? huyaAccountsResult.value : [];
const huyaTasks = huyaTasksResult.status === 'fulfilled' ? huyaTasksResult.value : [];
const huyaGoods = huyaGoodsResult.status === 'fulfilled' ? huyaGoodsResult.value : [];
const huyaRechargeGoods = huyaRechargeGoodsResult.status === 'fulfilled' ? huyaRechargeGoodsResult.value : [];
setStats({
douyu: {
accounts: douyuAccounts.length,
tasks: douyuTasks.length,
success: douyuTasks.filter((task: LoginTaskItem) => task.status === 'success').length,
failed: countFailed(douyuTasks),
cookies: cookies.length,
},
huya: {
accounts: huyaAccounts.length,
tasks: huyaTasks.length,
success: huyaTasks.filter((task: HuyaTaskItem) => task.status === 'success').length,
failed: countFailed(huyaTasks),
goods: huyaGoods.length,
rechargeGoods: huyaRechargeGoods.length,
},
});
setLoading(false);
}
void loadDashboard();
return () => {
ignore = true;
};
}, [canViewCookies, canViewDouyuAccounts, canViewDouyuTasks, canViewHuyaAccounts, canViewHuyaTasks]);
const totalStats = useMemo(() => ({
accounts: stats.douyu.accounts + stats.huya.accounts,
tasks: stats.douyu.tasks + stats.huya.tasks,
success: stats.douyu.success + stats.huya.success,
failed: stats.douyu.failed + stats.huya.failed,
}), [stats]);
return (
<div>
<h2></h2>
<Row gutter={16}>
<Col span={6}>
<Card>
<Statistic title="账号总数" value={stats.accounts} />
</Card>
</Col>
<Col span={6}>
<Card>
<Statistic title="登录任务总数" value={stats.tasks} />
</Card>
</Col>
<Col span={6}>
<Card>
<Statistic title="成功" value={stats.success} styles={{ content: { color: token.colorSuccess } }} />
</Card>
</Col>
<Col span={6}>
<Card>
<Statistic title="失败" value={stats.failed} styles={{ content: { color: token.colorError } }} />
</Card>
</Col>
<div style={{ marginBottom: 16 }}>
<Space align="center" wrap>
<Title level={2} style={{ margin: 0 }}></Title>
<Tag color="blue"></Tag>
<Tag color="orange"></Tag>
</Space>
</div>
<Row gutter={[12, 12]} style={{ marginBottom: 20 }}>
<StatCard title="账号总数" value={totalStats.accounts} loading={loading} />
<StatCard title="任务总数" value={totalStats.tasks} loading={loading} />
<StatCard title="成功任务" value={totalStats.success} color={token.colorSuccess} loading={loading} />
<StatCard title="失败任务" value={totalStats.failed} color={token.colorError} loading={loading} />
</Row>
<div style={{ marginBottom: 10 }}>
<Title level={4} style={{ margin: 0 }}></Title>
<Text type="secondary">Cookie </Text>
</div>
<Row gutter={[12, 12]} style={{ marginBottom: 20 }}>
<StatCard title="斗鱼账号" value={stats.douyu.accounts} loading={loading} />
<StatCard title="登录任务" value={stats.douyu.tasks} loading={loading} />
<StatCard title="成功 Cookie" value={stats.douyu.cookies} color={token.colorSuccess} loading={loading} />
<StatCard title="失败/异常" value={stats.douyu.failed} color={token.colorError} loading={loading} />
</Row>
<div style={{ marginBottom: 10 }}>
<Title level={4} style={{ margin: 0 }}></Title>
<Text type="secondary"> Cookie</Text>
</div>
<Row gutter={[12, 12]}>
<StatCard title="虎牙账号" value={stats.huya.accounts} loading={loading} />
<StatCard title="虎牙任务" value={stats.huya.tasks} loading={loading} />
<StatCard title="兑换商品" value={stats.huya.goods} loading={loading} />
<StatCard title="充值商品" value={stats.huya.rechargeGoods} loading={loading} />
</Row>
</div>
);