admin-projects/src/pages/Billing.tsx

31 lines
2.1 KiB
TypeScript
Raw Normal View History

import { useState } from 'react'
import { useQuery, useQueryClient } from '@tanstack/react-query'
import { Card, Row, Col, Statistic, Button, Tag, Space, Typography, App } from 'antd'
import { DollarOutlined, ReloadOutlined, LinkOutlined } from '@ant-design/icons'
import { getBilling, type BillingInfo } from '@/services/billing-api'
const { Text } = Typography
function BillingCard({ p }: { p: BillingInfo }) {
const color = p.status === 'ok' ? (parseFloat(p.balance) < 10 ? '#faad14' : '#52c41a') : '#999'
return (
<Card title={<Space><DollarOutlined />{p.name}<Tag color={p.status === 'ok' ? 'green' : 'default'}>{p.status === 'ok' ? '正常' : '未知'}</Tag></Space>}
extra={<Button type="link" size="small" icon={<LinkOutlined />} href={p.consoleUrl} target="_blank"></Button>}>
<Statistic title="余额" value={p.balance} valueStyle={{ color, fontSize: 28 }} suffix={<Text type="secondary" style={{ fontSize: 14 }}>{p.currency}</Text>} />
<div style={{ marginTop: 8 }}><Text type="secondary" style={{ fontSize: 12 }}>: {p.model}</Text><br /><Text type="secondary" style={{ fontSize: 12 }}>{p.note}</Text></div>
</Card>
)
}
function BillingContent() {
const qc = useQueryClient(); const [refreshing, setRefreshing] = useState(false)
const { data } = useQuery({ queryKey: ['billing'], queryFn: getBilling, staleTime: 60_000 })
return (
<div>
<div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: 16 }}>
<Typography.Title level={5} style={{ margin: 0 }}><DollarOutlined /> API </Typography.Title>
<Button icon={<ReloadOutlined spin={refreshing} />} onClick={async () => { setRefreshing(true); await qc.invalidateQueries({ queryKey: ['billing'] }); setTimeout(() => setRefreshing(false), 800) }} loading={refreshing}></Button>
</div>
<Row gutter={[16, 16]}>{(data?.providers || []).map(p => <Col xs={24} sm={12} lg={6} key={p.name}><BillingCard p={p} /></Col>)}</Row>
</div>
)
}
export default function BillingPage() { return <App><BillingContent /></App> }