"use client" import { useState, useEffect } from "react" import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card" import { Button } from "@/components/ui/button" import { Badge } from "@/components/ui/badge" import { Input } from "@/components/ui/input" import { Tabs, TabsList, TabsTrigger } from "@/components/ui/tabs" import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, } from "@/components/ui/dialog" import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue, } from "@/components/ui/select" import { Label } from "@/components/ui/label" import { Textarea } from "@/components/ui/textarea" import { Database, Plus, Search, RefreshCw, Settings, CheckCircle2, Clock, Server, Globe, Webhook, MoreVertical, Eye, Loader2, ArrowRight, Zap, Target, FolderTree, ChevronRight, } from "lucide-react" import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger, } from "@/components/ui/dropdown-menu" import { useRouter } from "next/navigation" // 数据源接口 interface DataSource { id: string name: string nameCn: string description: string type: 'mongodb' | 'mysql' | 'api' | 'webhook' status: 'connected' | 'disconnected' | 'warning' host?: string database?: string endpoint?: string lastSync: string recordCount: number syncFrequency: string collections?: number tables?: number latency?: number dataCategory?: string targetCollection?: string } // 数据分类颜色映射 const CATEGORY_COLORS: Record = { '用户画像': 'bg-blue-100 text-blue-700', '用户数据': 'bg-indigo-100 text-indigo-700', '社交数据': 'bg-pink-100 text-pink-700', '电商数据': 'bg-orange-100 text-orange-700', '私域数据': 'bg-purple-100 text-purple-700', '企业数据': 'bg-cyan-100 text-cyan-700', '金融数据': 'bg-green-100 text-green-700', '物流数据': 'bg-yellow-100 text-yellow-700', '消费数据': 'bg-red-100 text-red-700', '商业数据': 'bg-emerald-100 text-emerald-700', '其他': 'bg-gray-100 text-gray-700', } export default function DataSourcesPage() { const router = useRouter() const [activeTab, setActiveTab] = useState("all") const [searchQuery, setSearchQuery] = useState("") const [showAddDialog, setShowAddDialog] = useState(false) const [dataSources, setDataSources] = useState([]) const [loading, setLoading] = useState(true) const [summary, setSummary] = useState({ total: 0, connected: 0, warning: 0, totalRecords: 0, latency: 0 }) // 新数据源表单 const [newSource, setNewSource] = useState({ type: 'mongodb', name: '', nameCn: '', description: '', host: '', database: '', username: '', password: '', targetCollection: 'KR.用户估值', syncFrequency: 'realtime' }) // 加载数据源 useEffect(() => { loadDataSources() }, []) const loadDataSources = async () => { setLoading(true) try { const res = await fetch('/api/data-sources') const data = await res.json() if (data.success) { setDataSources(data.sources) setSummary(data.summary) } } catch (error) { console.error('加载数据源失败:', error) } finally { setLoading(false) } } const getStatusBadge = (status: string) => { switch (status) { case "connected": return 已连接 case "warning": return 待配置 case "disconnected": return 未连接 default: return 未知 } } const getTypeIcon = (type: string) => { switch (type) { case "mongodb": return case "mysql": return case "api": return case "webhook": return default: return } } const formatNumber = (num: number): string => { if (num >= 1000000000) return `${(num / 1000000000).toFixed(2)}B` if (num >= 1000000) return `${(num / 1000000).toFixed(1)}M` if (num >= 1000) return `${(num / 1000).toFixed(0)}K` return num.toLocaleString() } // 筛选数据源 const filteredSources = dataSources.filter((source) => { if (activeTab !== "all" && source.type !== activeTab) return false if (searchQuery) { const query = searchQuery.toLowerCase() return ( source.name.toLowerCase().includes(query) || source.nameCn?.toLowerCase().includes(query) || source.description?.toLowerCase().includes(query) || source.dataCategory?.toLowerCase().includes(query) ) } return true }) // 按分类分组 const groupedSources = filteredSources.reduce((acc, source) => { const category = source.dataCategory || '其他' if (!acc[category]) acc[category] = [] acc[category].push(source) return acc }, {} as Record) // 测试连接 const handleTestConnection = async () => { // TODO: 实现连接测试 alert('连接测试功能开发中') } // 添加数据源 const handleAddSource = async () => { // TODO: 实现添加数据源 setShowAddDialog(false) loadDataSources() } return (
{/* 顶部标题 */}

数据源管理

管理所有数据接入源,支持MongoDB、MySQL、API、Webhook等多种数据源

{/* 统计卡片 */}

数据源总数

{summary.total}

已连接

{summary.connected}

总数据量

{formatNumber(summary.totalRecords)}

平均延迟

{summary.latency}ms

{/* 搜索和筛选 */}
setSearchQuery(e.target.value)} className="pl-9 bg-white" />
全部 MongoDB MySQL API Webhook
{/* 加载状态 */} {loading ? (
加载数据源...
) : ( /* 按分类分组显示 */
{Object.entries(groupedSources).map(([category, sources]) => (
{category} {sources.length} 个数据源
{sources.map((source) => ( router.push(`/data-ingestion/sources/${source.id}`)} >
{getTypeIcon(source.type)}

{source.nameCn || source.name}

{getStatusBadge(source.status)}

{source.name}

{/* 描述 */}

{source.description}

{/* 统计信息 */}
{source.collections !== undefined && ( {source.collections} 集合 )} {source.tables !== undefined && source.tables > 0 && ( {source.tables} 表 )}
{formatNumber(source.recordCount)}
))}
))}
)} {/* 添加数据源弹窗 */} 添加数据源 配置新的数据源,数据将通过清洗规则处理后同步到指定的数据中台集合
{/* 数据源类型 */}
{[ { type: "mongodb", label: "MongoDB", icon: Database, desc: "文档数据库" }, { type: "mysql", label: "MySQL", icon: Server, desc: "关系型数据库" }, { type: "api", label: "REST API", icon: Globe, desc: "HTTP接口" }, { type: "webhook", label: "Webhook", icon: Webhook, desc: "实时推送" }, ].map((item) => ( ))}
{/* 基本信息 */}
setNewSource({ ...newSource, name: e.target.value })} />
setNewSource({ ...newSource, nameCn: e.target.value })} />