优化双平台后台界面
This commit is contained in:
@@ -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>
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user