'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 进行数据筛选、更新、调试或性能优化

); }