Fix CSS issue and create missing documentation files Add new data platform and mobile optimization features Co-authored-by: null <4804959+fnvtk@users.noreply.github.com>
491 lines
20 KiB
TypeScript
491 lines
20 KiB
TypeScript
"use client"
|
|
|
|
import { useState } from "react"
|
|
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card"
|
|
import { Button } from "@/components/ui/button"
|
|
import { Badge } from "@/components/ui/badge"
|
|
import { Progress } from "@/components/ui/progress"
|
|
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select"
|
|
import {
|
|
Database,
|
|
Upload,
|
|
Download,
|
|
RefreshCw,
|
|
CheckCircle,
|
|
AlertCircle,
|
|
Clock,
|
|
FileText,
|
|
Settings,
|
|
ChevronRight,
|
|
ChevronDown,
|
|
} from "lucide-react"
|
|
import { Collapsible, CollapsibleContent, CollapsibleTrigger } from "@/components/ui/collapsible"
|
|
|
|
export default function DataPlatformPage() {
|
|
const [timeRange, setTimeRange] = useState("today")
|
|
const [expandedSections, setExpandedSections] = useState({
|
|
overview: true,
|
|
sources: true,
|
|
integration: false,
|
|
quality: false,
|
|
})
|
|
|
|
// 数据中台数据
|
|
const platformData = {
|
|
overview: {
|
|
totalRecords: 1245678,
|
|
dataSources: 8,
|
|
syncStatus: "正常",
|
|
lastUpdate: "2分钟前",
|
|
dataQuality: 94.6,
|
|
storageUsed: 2.3,
|
|
storageTotal: 10.0,
|
|
},
|
|
dataSources: [
|
|
{
|
|
name: "用户行为数据库",
|
|
type: "MySQL",
|
|
status: "connected",
|
|
records: 456789,
|
|
lastSync: "1分钟前",
|
|
quality: 96.8,
|
|
description: "用户行为轨迹数据",
|
|
},
|
|
{
|
|
name: "CRM系统",
|
|
type: "API",
|
|
status: "connected",
|
|
records: 125678,
|
|
lastSync: "3分钟前",
|
|
quality: 94.2,
|
|
description: "客户关系管理数据",
|
|
},
|
|
{
|
|
name: "内容平台数据",
|
|
type: "API",
|
|
status: "syncing",
|
|
records: 234567,
|
|
lastSync: "正在同步",
|
|
quality: 92.5,
|
|
description: "抖音、小红书等平台数据",
|
|
},
|
|
{
|
|
name: "触客宝后台",
|
|
type: "API",
|
|
status: "connected",
|
|
records: 89234,
|
|
lastSync: "5分钟前",
|
|
quality: 95.1,
|
|
description: "呼入电话信息数据",
|
|
},
|
|
{
|
|
name: "飞书妙记",
|
|
type: "API",
|
|
status: "connected",
|
|
records: 12345,
|
|
lastSync: "10分钟前",
|
|
quality: 91.3,
|
|
description: "会议记录和内容摘要",
|
|
},
|
|
{
|
|
name: "表单提交数据",
|
|
type: "Webhook",
|
|
status: "connected",
|
|
records: 67890,
|
|
lastSync: "2分钟前",
|
|
quality: 97.2,
|
|
description: "在线表单提交数据",
|
|
},
|
|
],
|
|
integrationStats: {
|
|
totalIntegrations: 8,
|
|
activeIntegrations: 7,
|
|
failedIntegrations: 0,
|
|
pendingIntegrations: 1,
|
|
avgSyncTime: "2.3分钟",
|
|
successRate: 98.7,
|
|
},
|
|
dataQuality: {
|
|
completeness: 94.6,
|
|
accuracy: 96.2,
|
|
consistency: 92.8,
|
|
timeliness: 95.4,
|
|
validity: 93.7,
|
|
uniqueness: 97.1,
|
|
},
|
|
}
|
|
|
|
const toggleSection = (section: string) => {
|
|
setExpandedSections((prev) => ({
|
|
...prev,
|
|
[section]: !prev[section],
|
|
}))
|
|
}
|
|
|
|
const getStatusColor = (status: string) => {
|
|
switch (status) {
|
|
case "connected":
|
|
return "bg-green-100 text-green-800"
|
|
case "syncing":
|
|
return "bg-yellow-100 text-yellow-800"
|
|
case "error":
|
|
return "bg-red-100 text-red-800"
|
|
case "disconnected":
|
|
return "bg-gray-100 text-gray-800"
|
|
default:
|
|
return "bg-gray-100 text-gray-800"
|
|
}
|
|
}
|
|
|
|
const getStatusIcon = (status: string) => {
|
|
switch (status) {
|
|
case "connected":
|
|
return <CheckCircle className="h-4 w-4 text-green-600" />
|
|
case "syncing":
|
|
return <Clock className="h-4 w-4 text-yellow-600 animate-spin" />
|
|
case "error":
|
|
return <AlertCircle className="h-4 w-4 text-red-600" />
|
|
default:
|
|
return <AlertCircle className="h-4 w-4 text-gray-600" />
|
|
}
|
|
}
|
|
|
|
return (
|
|
<div className="container mx-auto py-4 md:py-6 space-y-4 md:space-y-6">
|
|
{/* 页面标题和工具栏 */}
|
|
<div className="flex flex-col md:flex-row md:justify-between md:items-center gap-4">
|
|
<div>
|
|
<h1 className="text-2xl md:text-3xl font-bold">数据中台</h1>
|
|
<p className="text-sm md:text-base text-muted-foreground mt-1">多源数据整合与管理中心</p>
|
|
</div>
|
|
<div className="flex flex-wrap gap-2">
|
|
<Select value={timeRange} onValueChange={setTimeRange}>
|
|
<SelectTrigger className="w-[120px] text-sm">
|
|
<SelectValue placeholder="时间范围" />
|
|
</SelectTrigger>
|
|
<SelectContent>
|
|
<SelectItem value="today">今日</SelectItem>
|
|
<SelectItem value="week">本周</SelectItem>
|
|
<SelectItem value="month">本月</SelectItem>
|
|
</SelectContent>
|
|
</Select>
|
|
<Button variant="outline" size="icon" className="h-9 w-9 bg-transparent">
|
|
<Upload className="h-4 w-4" />
|
|
</Button>
|
|
<Button variant="outline" size="icon" className="h-9 w-9 bg-transparent">
|
|
<RefreshCw className="h-4 w-4" />
|
|
</Button>
|
|
<Button variant="outline" size="sm" className="hidden md:flex bg-transparent">
|
|
<Download className="mr-2 h-4 w-4" />
|
|
导出
|
|
</Button>
|
|
</div>
|
|
</div>
|
|
|
|
{/* 数据中台概览 */}
|
|
<Collapsible
|
|
open={expandedSections.overview}
|
|
onOpenChange={(open) => setExpandedSections((prev) => ({ ...prev, overview: open }))}
|
|
>
|
|
<Card className="border-none shadow-md">
|
|
<CollapsibleTrigger className="w-full">
|
|
<CardHeader className="bg-gradient-to-r from-blue-50 to-indigo-50 border-b hover:from-blue-100 hover:to-indigo-100 transition-colors cursor-pointer py-3 md:py-4">
|
|
<div className="flex justify-between items-center">
|
|
<div className="flex items-center gap-3">
|
|
<Database className="h-4 w-4 md:h-5 md:w-5 text-blue-600" />
|
|
<div className="text-left">
|
|
<CardTitle className="text-base md:text-lg">平台概览</CardTitle>
|
|
<CardDescription className="text-xs md:text-sm">数据中台整体运行状况</CardDescription>
|
|
</div>
|
|
</div>
|
|
{expandedSections.overview ? (
|
|
<ChevronDown className="h-4 w-4 md:h-5 md:w-5" />
|
|
) : (
|
|
<ChevronRight className="h-4 w-4 md:h-5 md:w-5" />
|
|
)}
|
|
</div>
|
|
</CardHeader>
|
|
</CollapsibleTrigger>
|
|
<CollapsibleContent>
|
|
<CardContent className="p-4">
|
|
<div className="grid grid-cols-2 md:grid-cols-4 gap-3 md:gap-4">
|
|
<div className="bg-blue-50 p-3 rounded-lg text-center">
|
|
<div className="text-lg md:text-2xl font-bold text-blue-600">
|
|
{platformData.overview.totalRecords.toLocaleString()}
|
|
</div>
|
|
<div className="text-xs text-blue-700">总数据量</div>
|
|
</div>
|
|
<div className="bg-green-50 p-3 rounded-lg text-center">
|
|
<div className="text-lg md:text-2xl font-bold text-green-600">
|
|
{platformData.overview.dataSources}
|
|
</div>
|
|
<div className="text-xs text-green-700">数据源</div>
|
|
</div>
|
|
<div className="bg-purple-50 p-3 rounded-lg text-center">
|
|
<div className="text-lg md:text-2xl font-bold text-purple-600">
|
|
{platformData.overview.dataQuality}%
|
|
</div>
|
|
<div className="text-xs text-purple-700">数据质量</div>
|
|
</div>
|
|
<div className="bg-orange-50 p-3 rounded-lg text-center">
|
|
<div className="text-lg md:text-2xl font-bold text-orange-600">
|
|
{platformData.overview.storageUsed}GB
|
|
</div>
|
|
<div className="text-xs text-orange-700">存储使用</div>
|
|
</div>
|
|
</div>
|
|
<div className="mt-4 space-y-3">
|
|
<div>
|
|
<div className="flex justify-between text-sm mb-1">
|
|
<span>存储使用率</span>
|
|
<span>
|
|
{platformData.overview.storageUsed}GB / {platformData.overview.storageTotal}GB
|
|
</span>
|
|
</div>
|
|
<Progress value={(platformData.overview.storageUsed / platformData.overview.storageTotal) * 100} />
|
|
</div>
|
|
<div>
|
|
<div className="flex justify-between text-sm mb-1">
|
|
<span>数据质量</span>
|
|
<span>{platformData.overview.dataQuality}%</span>
|
|
</div>
|
|
<Progress value={platformData.overview.dataQuality} />
|
|
</div>
|
|
</div>
|
|
</CardContent>
|
|
</CollapsibleContent>
|
|
</Card>
|
|
</Collapsible>
|
|
|
|
{/* 数据源管理 */}
|
|
<Collapsible
|
|
open={expandedSections.sources}
|
|
onOpenChange={(open) => setExpandedSections((prev) => ({ ...prev, sources: open }))}
|
|
>
|
|
<Card className="border-none shadow-md">
|
|
<CollapsibleTrigger className="w-full">
|
|
<CardHeader className="bg-gradient-to-r from-green-50 to-emerald-50 border-b hover:from-green-100 hover:to-emerald-100 transition-colors cursor-pointer py-3 md:py-4">
|
|
<div className="flex justify-between items-center">
|
|
<div className="flex items-center gap-3">
|
|
<FileText className="h-4 w-4 md:h-5 md:w-5 text-green-600" />
|
|
<div className="text-left">
|
|
<CardTitle className="text-base md:text-lg">数据源管理</CardTitle>
|
|
<CardDescription className="text-xs md:text-sm">
|
|
数据源连接状态 ({platformData.dataSources.length})
|
|
</CardDescription>
|
|
</div>
|
|
</div>
|
|
{expandedSections.sources ? (
|
|
<ChevronDown className="h-4 w-4 md:h-5 md:w-5" />
|
|
) : (
|
|
<ChevronRight className="h-4 w-4 md:h-5 md:w-5" />
|
|
)}
|
|
</div>
|
|
</CardHeader>
|
|
</CollapsibleTrigger>
|
|
<CollapsibleContent>
|
|
<CardContent className="p-4">
|
|
<div className="space-y-3">
|
|
{platformData.dataSources.map((source, index) => (
|
|
<div key={index} className="bg-white border rounded-lg p-3 md:p-4">
|
|
<div className="flex flex-col md:flex-row md:items-center justify-between gap-3">
|
|
<div className="flex-1">
|
|
<div className="flex items-center gap-2 mb-2">
|
|
{getStatusIcon(source.status)}
|
|
<h3 className="font-medium text-sm md:text-base">{source.name}</h3>
|
|
<Badge className={getStatusColor(source.status)}>{source.status}</Badge>
|
|
<Badge variant="outline" className="text-xs">
|
|
{source.type}
|
|
</Badge>
|
|
</div>
|
|
<p className="text-xs md:text-sm text-gray-600 mb-2">{source.description}</p>
|
|
<div className="flex flex-wrap gap-4 text-xs">
|
|
<span>数据量: {source.records.toLocaleString()}</span>
|
|
<span>最后同步: {source.lastSync}</span>
|
|
<span>质量评分: {source.quality}%</span>
|
|
</div>
|
|
</div>
|
|
<div className="flex flex-col md:items-end gap-2">
|
|
<div className="text-right">
|
|
<div className="text-sm font-bold text-green-600">{source.quality}%</div>
|
|
<div className="text-xs text-gray-500">数据质量</div>
|
|
</div>
|
|
<Button variant="outline" size="sm" className="bg-transparent">
|
|
<Settings className="h-3 w-3 mr-1" />
|
|
配置
|
|
</Button>
|
|
</div>
|
|
</div>
|
|
<div className="mt-3">
|
|
<Progress value={source.quality} className="h-1.5" />
|
|
</div>
|
|
</div>
|
|
))}
|
|
</div>
|
|
</CardContent>
|
|
</CollapsibleContent>
|
|
</Card>
|
|
</Collapsible>
|
|
|
|
{/* 数据集成统计 */}
|
|
<Collapsible
|
|
open={expandedSections.integration}
|
|
onOpenChange={(open) => setExpandedSections((prev) => ({ ...prev, integration: open }))}
|
|
>
|
|
<Card className="border-none shadow-md">
|
|
<CollapsibleTrigger className="w-full">
|
|
<CardHeader className="bg-gradient-to-r from-purple-50 to-pink-50 border-b hover:from-purple-100 hover:to-pink-100 transition-colors cursor-pointer py-3 md:py-4">
|
|
<div className="flex justify-between items-center">
|
|
<div className="flex items-center gap-3">
|
|
<RefreshCw className="h-4 w-4 md:h-5 md:w-5 text-purple-600" />
|
|
<div className="text-left">
|
|
<CardTitle className="text-base md:text-lg">集成统计</CardTitle>
|
|
<CardDescription className="text-xs md:text-sm">数据集成性能指标</CardDescription>
|
|
</div>
|
|
</div>
|
|
{expandedSections.integration ? (
|
|
<ChevronDown className="h-4 w-4 md:h-5 md:w-5" />
|
|
) : (
|
|
<ChevronRight className="h-4 w-4 md:h-5 md:w-5" />
|
|
)}
|
|
</div>
|
|
</CardHeader>
|
|
</CollapsibleTrigger>
|
|
<CollapsibleContent>
|
|
<CardContent className="p-4">
|
|
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
|
|
<div className="bg-blue-50 p-4 rounded-lg">
|
|
<div className="flex items-center gap-2 mb-2">
|
|
<Database className="h-4 w-4 text-blue-600" />
|
|
<span className="text-sm font-medium">集成概况</span>
|
|
</div>
|
|
<div className="space-y-2">
|
|
<div className="flex justify-between">
|
|
<span className="text-xs text-gray-600">总集成数</span>
|
|
<span className="text-sm font-bold">{platformData.integrationStats.totalIntegrations}</span>
|
|
</div>
|
|
<div className="flex justify-between">
|
|
<span className="text-xs text-gray-600">活跃集成</span>
|
|
<span className="text-sm font-bold">{platformData.integrationStats.activeIntegrations}</span>
|
|
</div>
|
|
<div className="flex justify-between">
|
|
<span className="text-xs text-gray-600">待处理</span>
|
|
<span className="text-sm font-bold">{platformData.integrationStats.pendingIntegrations}</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div className="bg-green-50 p-4 rounded-lg">
|
|
<div className="flex items-center gap-2 mb-2">
|
|
<CheckCircle className="h-4 w-4 text-green-600" />
|
|
<span className="text-sm font-medium">性能指标</span>
|
|
</div>
|
|
<div className="space-y-2">
|
|
<div className="flex justify-between">
|
|
<span className="text-xs text-gray-600">成功率</span>
|
|
<span className="text-sm font-bold">{platformData.integrationStats.successRate}%</span>
|
|
</div>
|
|
<div className="flex justify-between">
|
|
<span className="text-xs text-gray-600">平均同步时间</span>
|
|
<span className="text-sm font-bold">{platformData.integrationStats.avgSyncTime}</span>
|
|
</div>
|
|
<div className="flex justify-between">
|
|
<span className="text-xs text-gray-600">失败集成</span>
|
|
<span className="text-sm font-bold">{platformData.integrationStats.failedIntegrations}</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div className="bg-purple-50 p-4 rounded-lg">
|
|
<div className="flex items-center gap-2 mb-2">
|
|
<Clock className="h-4 w-4 text-purple-600" />
|
|
<span className="text-sm font-medium">实时状态</span>
|
|
</div>
|
|
<div className="space-y-2">
|
|
<div className="flex justify-between">
|
|
<span className="text-xs text-gray-600">最后更新</span>
|
|
<span className="text-sm font-bold">{platformData.overview.lastUpdate}</span>
|
|
</div>
|
|
<div className="flex justify-between">
|
|
<span className="text-xs text-gray-600">同步状态</span>
|
|
<span className="text-sm font-bold text-green-600">{platformData.overview.syncStatus}</span>
|
|
</div>
|
|
<div className="flex justify-between">
|
|
<span className="text-xs text-gray-600">系统负载</span>
|
|
<span className="text-sm font-bold">正常</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</CardContent>
|
|
</CollapsibleContent>
|
|
</Card>
|
|
</Collapsible>
|
|
|
|
{/* 数据质量监控 */}
|
|
<Collapsible
|
|
open={expandedSections.quality}
|
|
onOpenChange={(open) => setExpandedSections((prev) => ({ ...prev, quality: open }))}
|
|
>
|
|
<Card className="border-none shadow-md">
|
|
<CollapsibleTrigger className="w-full">
|
|
<CardHeader className="bg-gradient-to-r from-orange-50 to-red-50 border-b hover:from-orange-100 hover:to-red-100 transition-colors cursor-pointer py-3 md:py-4">
|
|
<div className="flex justify-between items-center">
|
|
<div className="flex items-center gap-3">
|
|
<CheckCircle className="h-4 w-4 md:h-5 md:w-5 text-orange-600" />
|
|
<div className="text-left">
|
|
<CardTitle className="text-base md:text-lg">数据质量监控</CardTitle>
|
|
<CardDescription className="text-xs md:text-sm">数据质量多维度评估</CardDescription>
|
|
</div>
|
|
</div>
|
|
{expandedSections.quality ? (
|
|
<ChevronDown className="h-4 w-4 md:h-5 md:w-5" />
|
|
) : (
|
|
<ChevronRight className="h-4 w-4 md:h-5 md:w-5" />
|
|
)}
|
|
</div>
|
|
</CardHeader>
|
|
</CollapsibleTrigger>
|
|
<CollapsibleContent>
|
|
<CardContent className="p-4">
|
|
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
|
|
{Object.entries(platformData.dataQuality).map(([key, value], index) => {
|
|
const labels = {
|
|
completeness: "完整性",
|
|
accuracy: "准确性",
|
|
consistency: "一致性",
|
|
timeliness: "及时性",
|
|
validity: "有效性",
|
|
uniqueness: "唯一性",
|
|
}
|
|
|
|
const colors = [
|
|
"bg-blue-50 text-blue-600",
|
|
"bg-green-50 text-green-600",
|
|
"bg-purple-50 text-purple-600",
|
|
"bg-orange-50 text-orange-600",
|
|
"bg-pink-50 text-pink-600",
|
|
"bg-indigo-50 text-indigo-600",
|
|
]
|
|
|
|
return (
|
|
<div key={key} className={`p-4 rounded-lg ${colors[index]}`}>
|
|
<div className="text-center">
|
|
<div className="text-2xl font-bold mb-1">{value}%</div>
|
|
<div className="text-sm">{labels[key as keyof typeof labels]}</div>
|
|
<div className="mt-2">
|
|
<Progress value={value} className="h-1.5" />
|
|
</div>
|
|
</div>
|
|
</div>
|
|
)
|
|
})}
|
|
</div>
|
|
</CardContent>
|
|
</CollapsibleContent>
|
|
</Card>
|
|
</Collapsible>
|
|
</div>
|
|
)
|
|
}
|