Files
SBrainCO/client/src/components/admin/OverviewTab.tsx
T
freedakgmail cfa12c9b7f feat: 多租户数据权限 + 数据库连接修正
- 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 家海淀区门店)
2026-08-02 20:10:42 +08:00

61 lines
2.8 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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>
)
}