cfa12c9b7f
- adminPool 连服务器本地 5432 sbrain_admin (SCRAM 认证) - tenantPool/pool 连 FRP 隧道 15432 bill_query (trust 认证) - query() 自动注入 store_code 数据权限 (store/regional 角色) - 区域经理 region 字段存储逗号分隔的 store_code 列表 - /overview 和 /overview/daily 对 scoped 用户从 bill_fact 聚合 - tenant_users.region 扩展为 VARCHAR(500) - deploy.sh .env 增加 ADMIN_DB_* 配置 - run.md 更新数据库连接架构说明 - 新增海淀区区域经理用户 (21 家海淀区门店)
61 lines
2.8 KiB
TypeScript
61 lines
2.8 KiB
TypeScript
import { useQuery } from '@tanstack/react-query'
|
||
import api from '@/lib/api'
|
||
import { MetricCard } from '@/components/MetricCard'
|
||
import { LoadingSpinner } from '@/components/LoadingSpinner'
|
||
import { cn } from '@/lib/utils'
|
||
|
||
export function OverviewTab() {
|
||
const { data, isLoading } = useQuery({
|
||
queryKey: ['admin-tenants'],
|
||
queryFn: () => api.get('/admin/tenants'),
|
||
})
|
||
|
||
if (isLoading) return <LoadingSpinner text="加载平台数据..." />
|
||
|
||
const tenants = (data as any)?.data || []
|
||
const activeCount = tenants.filter((t: any) => t.status === 'active').length
|
||
const inactiveCount = tenants.filter((t: any) => t.status === 'inactive').length
|
||
|
||
return (
|
||
<div className="space-y-4">
|
||
<div className="grid grid-cols-4 gap-3">
|
||
<MetricCard title="租户总数" value={tenants.length} description="已注册的租户数量" />
|
||
<MetricCard title="运行中" value={activeCount} status="good" description="状态为 active 的租户" />
|
||
<MetricCard title="已停用" value={inactiveCount} status={inactiveCount > 0 ? 'warn' : undefined} description="状态为 inactive 的租户" />
|
||
<MetricCard title="FRP端口数" value={tenants.length} description="已分配的 FRP 隧道端口数量" />
|
||
</div>
|
||
|
||
<div className="rounded-lg border bg-card p-4">
|
||
<h2 className="mb-3 text-sm font-bold">租户状态一览</h2>
|
||
<div className="space-y-2">
|
||
{tenants.map((t: any) => (
|
||
<div key={t.tenant_id} className="flex items-center justify-between rounded-md border px-3 py-2">
|
||
<div className="flex items-center gap-3">
|
||
<span className={cn(
|
||
'h-2 w-2 rounded-full',
|
||
t.status === 'active' ? 'bg-green-500' : 'bg-gray-400'
|
||
)} />
|
||
<span className="font-medium text-sm">{t.tenant_name}</span>
|
||
<span className="font-mono text-xs text-muted-foreground">{t.tenant_id}</span>
|
||
</div>
|
||
<div className="flex items-center gap-4 text-xs text-muted-foreground">
|
||
<span>DB: <span className="font-mono">{t.db_host}:{t.db_port}</span></span>
|
||
<span>FRP: <span className="font-mono">{t.frp_port}</span></span>
|
||
<span className={cn(
|
||
'rounded px-2 py-0.5 font-medium',
|
||
t.status === 'active' ? 'bg-green-100 text-green-700' : 'bg-gray-100 text-gray-500'
|
||
)}>
|
||
{t.status === 'active' ? '运行中' : '已停用'}
|
||
</span>
|
||
</div>
|
||
</div>
|
||
))}
|
||
{tenants.length === 0 && (
|
||
<p className="py-4 text-center text-sm text-muted-foreground">暂无租户,请到「租户管理」创建</p>
|
||
)}
|
||
</div>
|
||
</div>
|
||
</div>
|
||
)
|
||
}
|