'use client';
import dynamic from 'next/dynamic';
import * as React from 'react';
import { useCallback, useEffect, useState } from 'react';
import { toast } from 'sonner';
import {
Activity,
Cpu,
Database,
Download,
FileText,
HardDrive,
Layers,
RefreshCw,
Server,
Terminal,
} from 'lucide-react';
import {
Card,
CardContent,
CardDescription,
CardHeader,
CardTitle,
} from '@/components/ui/card';
import { Button } from '@/components/ui/button';
import { Skeleton } from '@/components/ui/skeleton';
import services from '@/lib/services';
import type { DBOverview } from '@/lib/services/db-manage';
const sectionFallback = (
);
const TableBrowser = dynamic(
() => import('./components/table-browser').then((mod) => mod.TableBrowser),
{ loading: () => sectionFallback },
);
const CacheManager = dynamic(
() => import('./components/cache-manager').then((mod) => mod.CacheManager),
{ loading: () => sectionFallback },
);
const SQLConsole = dynamic(
() => import('./components/sql-console').then((mod) => mod.SQLConsole),
{ ssr: false },
);
/**
* 格式化数字,每3位加逗号
*/
const formatNumber = (num: number | string) => {
if (num === undefined || num === null) return '0';
return num.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ',');
};
export function DatabasePageClient() {
// 核心状态
const [overview, setOverview] = useState(null);
const [tables, setTables] = useState([]);
const [loadingOverview, setLoadingOverview] = useState(true);
const [loadingTables, setLoadingTables] = useState(true);
const [exporting, setExporting] = useState(false);
// 协调子组件刷新与视图切换状态
const [showConsole, setShowConsole] = useState(false);
const [refreshTrigger, setRefreshTrigger] = useState(0);
// 1. 获取运行概览
const fetchOverview = useCallback(async (isSilent = false) => {
if (!isSilent) setLoadingOverview(true);
try {
const data = await services.dbManage.getOverview();
setOverview(data);
} catch (err) {
toast.error('获取数据库概览失败', {
description: err instanceof Error ? err.message : '未知错误',
});
} finally {
setLoadingOverview(false);
}
}, []);
// 2. 获取表列表
const fetchTables = useCallback(async () => {
setLoadingTables(true);
try {
const data = await services.dbManage.listTables();
setTables(data);
} catch (err) {
toast.error('获取数据库数据表列表失败', {
description: err instanceof Error ? err.message : '未知错误',
});
} finally {
setLoadingTables(false);
}
}, []);
// 3. 协调刷新
const handleRefreshAll = () => {
fetchOverview();
fetchTables();
setRefreshTrigger((prev) => prev + 1);
};
// 4. 导出数据库备份
const handleExport = async () => {
setExporting(true);
try {
const { blob, filename } = await services.dbManage.exportDatabase();
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = filename;
document.body.appendChild(a);
a.click();
a.remove();
URL.revokeObjectURL(url);
toast.success('数据库导出成功', {
description: `已下载归档文件: ${filename}`,
});
} catch (err) {
toast.error('数据库导出失败', {
description: err instanceof Error ? err.message : '导出异常',
});
} finally {
setExporting(false);
}
};
// 初始化加载
useEffect(() => {
fetchOverview();
fetchTables();
}, [fetchOverview, fetchTables]);
// 渲染概览卡片骨架
const renderOverviewSkeleton = () => (
{Array.from({ length: 6 }).map((_, i) => (
))}
);
// 渲染 SQL 查询控台全屏视图
if (showConsole) {
return (
setShowConsole(false)}
/>
);
}
// 正常页面排版布局
return (
{/* 顶部控制与标题 */}
{/* 1. 概览指标卡片组 */}
{loadingOverview
? renderOverviewSkeleton()
: overview && (
{/* 卡片1: 数据库类型 */}
数据库类型
{overview.type}
{/* 卡片2: 数据库版本 */}
版本信息
{overview.version.split(' ').slice(0, 2).join(' ')}
{/* 卡片3: 数据库名称 */}
名称/路径
{overview.name.substring(
overview.name.lastIndexOf('/') + 1,
)}
{/* 卡片4: 数据库大小 */}
数据库大小
{overview.size}
{/* 卡片5: 表数量 */}
物理数据表
{formatNumber(overview.table_count)}
{/* 卡片6: 连接数 */}
活跃连接数
{formatNumber(overview.connections)}
)}
{/* 2. 数据表浏览器区块 */}
{/* 3. 缓存管理区块 */}
{/* 4. 底部功能卡片区 */}
功能区
数据库导出及自定义高级 SQL 执行终端
{/* 功能一: 数据库导出 */}
数据库备份导出
直接导出并下载物理数据库镜像文件(SQLite 导出 .db,PostgreSQL
导出为打包的 .sql 文本文件)
{/* 功能二: SQL 查询终端 */}
SQL 查询控台
打开在线 SQL 控制终端,执行原生的 SQL
进行数据筛选、更新、调试或性能优化
);
}