Files
users/components/dialogs/cleaning-rule-logs-dialog.tsx
v0 b17b488f8e refactor: restructure navigation and module layout
Reorganize navigation and module structure based on new requirements.

Co-authored-by: null <4804959+fnvtk@users.noreply.github.com>
2026-01-31 04:32:36 +00:00

179 lines
6.4 KiB
TypeScript

"use client"
import { useState } from "react"
import { Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle } from "@/components/ui/dialog"
import { Button } from "@/components/ui/button"
import { Badge } from "@/components/ui/badge"
import { ScrollArea } from "@/components/ui/scroll-area"
import { FileText, CheckCircle, XCircle, Clock, Download, RefreshCw } from "lucide-react"
interface CleaningRuleLogsDialogProps {
open: boolean
onOpenChange: (open: boolean) => void
ruleName?: string
}
const MOCK_LOGS = [
{
id: 1,
timestamp: "2025-12-16 14:35:12",
level: "INFO",
message: "开始执行清洗任务",
details: "任务ID: task_20251216_143512",
},
{
id: 2,
timestamp: "2025-12-16 14:35:13",
level: "INFO",
message: "连接源数据库成功",
details: "MySQL: 10.88.182.62:3305",
},
{
id: 3,
timestamp: "2025-12-16 14:35:15",
level: "INFO",
message: "读取源数据",
details: "共读取 128,956 条记录",
},
{
id: 4,
timestamp: "2025-12-16 14:35:18",
level: "WARN",
message: "发现异常数据",
details: "25 条记录手机号格式不规范",
},
{
id: 5,
timestamp: "2025-12-16 14:35:22",
level: "INFO",
message: "执行清洗规则",
details: "应用规则: 手机号标准化",
},
{
id: 6,
timestamp: "2025-12-16 14:35:45",
level: "INFO",
message: "数据写入完成",
details: "成功写入 128,931 条, 跳过 25 条",
},
{
id: 7,
timestamp: "2025-12-16 14:35:46",
level: "INFO",
message: "清洗任务完成",
details: "总耗时: 34秒, 错误率: 0.02%",
},
]
const EXECUTION_HISTORY = [
{ id: 1, time: "2025-12-16 14:35", status: "success", processed: 128956, errors: 25, duration: "34秒" },
{ id: 2, time: "2025-12-16 13:35", status: "success", processed: 125832, errors: 18, duration: "32秒" },
{ id: 3, time: "2025-12-16 12:35", status: "success", processed: 130215, errors: 22, duration: "36秒" },
{ id: 4, time: "2025-12-16 11:35", status: "failed", processed: 45000, errors: 1250, duration: "15秒" },
{ id: 5, time: "2025-12-16 10:35", status: "success", processed: 128500, errors: 20, duration: "33秒" },
]
export function CleaningRuleLogsDialog({ open, onOpenChange, ruleName }: CleaningRuleLogsDialogProps) {
const [selectedExecution, setSelectedExecution] = useState(EXECUTION_HISTORY[0])
const getLevelColor = (level: string) => {
switch (level) {
case "INFO":
return "text-blue-600 bg-blue-50"
case "WARN":
return "text-amber-600 bg-amber-50"
case "ERROR":
return "text-red-600 bg-red-50"
default:
return "text-slate-600 bg-slate-50"
}
}
return (
<Dialog open={open} onOpenChange={onOpenChange}>
<DialogContent className="max-w-4xl max-h-[80vh]">
<DialogHeader>
<DialogTitle className="flex items-center gap-2">
<FileText className="w-5 h-5" />
- {ruleName || "清洗规则"}
</DialogTitle>
<DialogDescription></DialogDescription>
</DialogHeader>
<div className="grid grid-cols-3 gap-4">
{/* 执行历史列表 */}
<div className="col-span-1 border-r pr-4">
<h4 className="text-sm font-medium text-slate-700 mb-3"></h4>
<ScrollArea className="h-[400px]">
<div className="space-y-2">
{EXECUTION_HISTORY.map((exec) => (
<div
key={exec.id}
className={`p-3 rounded-lg cursor-pointer transition-colors ${
selectedExecution.id === exec.id
? "bg-blue-50 border border-blue-200"
: "bg-slate-50 hover:bg-slate-100"
}`}
onClick={() => setSelectedExecution(exec)}
>
<div className="flex items-center justify-between mb-1">
<span className="text-xs text-slate-500">{exec.time}</span>
{exec.status === "success" ? (
<CheckCircle className="w-4 h-4 text-emerald-500" />
) : (
<XCircle className="w-4 h-4 text-red-500" />
)}
</div>
<div className="text-sm">
<span className="font-medium">{exec.processed.toLocaleString()}</span>
<span className="text-slate-500"> / </span>
<span className={exec.status === "failed" ? "text-red-600" : "text-slate-600"}>
{exec.errors}
</span>
</div>
<div className="flex items-center gap-1 text-xs text-slate-500 mt-1">
<Clock className="w-3 h-3" />
{exec.duration}
</div>
</div>
))}
</div>
</ScrollArea>
</div>
{/* 日志详情 */}
<div className="col-span-2">
<div className="flex items-center justify-between mb-3">
<h4 className="text-sm font-medium text-slate-700"></h4>
<div className="flex gap-2">
<Button variant="outline" size="sm">
<RefreshCw className="w-4 h-4 mr-1" />
</Button>
<Button variant="outline" size="sm">
<Download className="w-4 h-4 mr-1" />
</Button>
</div>
</div>
<ScrollArea className="h-[400px] bg-slate-900 rounded-lg p-4">
<div className="space-y-2 font-mono text-sm">
{MOCK_LOGS.map((log) => (
<div key={log.id} className="flex gap-3">
<span className="text-slate-500 whitespace-nowrap">{log.timestamp}</span>
<Badge className={`${getLevelColor(log.level)} text-xs px-1.5`}>{log.level}</Badge>
<div>
<span className="text-slate-200">{log.message}</span>
<span className="text-slate-500 ml-2">- {log.details}</span>
</div>
</div>
))}
</div>
</ScrollArea>
</div>
</div>
</DialogContent>
</Dialog>
)
}