Reorganize navigation and module structure based on new requirements. Co-authored-by: null <4804959+fnvtk@users.noreply.github.com>
171 lines
6.5 KiB
TypeScript
171 lines
6.5 KiB
TypeScript
"use client"
|
||
|
||
import { useState } from "react"
|
||
import {
|
||
Dialog,
|
||
DialogContent,
|
||
DialogDescription,
|
||
DialogFooter,
|
||
DialogHeader,
|
||
DialogTitle,
|
||
} from "@/components/ui/dialog"
|
||
import { Button } from "@/components/ui/button"
|
||
import { Label } from "@/components/ui/label"
|
||
import { Checkbox } from "@/components/ui/checkbox"
|
||
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select"
|
||
import { Progress } from "@/components/ui/progress"
|
||
import { RefreshCw, Database, Clock, AlertTriangle } from "lucide-react"
|
||
|
||
interface BatchRecalculateDialogProps {
|
||
open: boolean
|
||
onOpenChange: (open: boolean) => void
|
||
}
|
||
|
||
const DATA_SOURCES = [
|
||
{ id: "1", name: "存客宝-用户主库", records: 128956342 },
|
||
{ id: "2", name: "触客宝-行为数据", records: 89234567 },
|
||
{ id: "3", name: "数智员工-账号API", records: 45678901 },
|
||
]
|
||
|
||
const AI_RULES = [
|
||
{ id: 1, name: "用户数据智能清洗", selected: true },
|
||
{ id: 2, name: "交易流水异常检测", selected: true },
|
||
{ id: 3, name: "聊天记录语义清洗", selected: false },
|
||
]
|
||
|
||
export function BatchRecalculateDialog({ open, onOpenChange }: BatchRecalculateDialogProps) {
|
||
const [isProcessing, setIsProcessing] = useState(false)
|
||
const [progress, setProgress] = useState(0)
|
||
const [selectedRules, setSelectedRules] = useState(AI_RULES.filter((r) => r.selected).map((r) => r.id))
|
||
const [scope, setScope] = useState("incremental")
|
||
|
||
const handleStart = () => {
|
||
setIsProcessing(true)
|
||
setProgress(0)
|
||
const interval = setInterval(() => {
|
||
setProgress((prev) => {
|
||
if (prev >= 100) {
|
||
clearInterval(interval)
|
||
setIsProcessing(false)
|
||
return 100
|
||
}
|
||
return prev + 10
|
||
})
|
||
}, 500)
|
||
}
|
||
|
||
return (
|
||
<Dialog open={open} onOpenChange={onOpenChange}>
|
||
<DialogContent className="max-w-lg">
|
||
<DialogHeader>
|
||
<DialogTitle className="flex items-center gap-2">
|
||
<RefreshCw className="w-5 h-5" />
|
||
批量重算
|
||
</DialogTitle>
|
||
<DialogDescription>重新执行选中的AI清洗规则,处理全量或增量数据</DialogDescription>
|
||
</DialogHeader>
|
||
|
||
{isProcessing ? (
|
||
<div className="space-y-4 py-4">
|
||
<div className="flex items-center justify-center">
|
||
<RefreshCw className="w-12 h-12 text-violet-500 animate-spin" />
|
||
</div>
|
||
<div className="text-center">
|
||
<p className="font-medium text-slate-800">正在重算...</p>
|
||
<p className="text-sm text-slate-500 mt-1">已处理 {progress}%</p>
|
||
</div>
|
||
<Progress value={progress} className="h-2" />
|
||
<div className="text-center text-sm text-slate-500">
|
||
预计剩余时间: {Math.ceil(((100 - progress) / 10) * 0.5)} 分钟
|
||
</div>
|
||
</div>
|
||
) : (
|
||
<div className="space-y-4">
|
||
<div className="space-y-2">
|
||
<Label>选择AI清洗规则</Label>
|
||
<div className="space-y-2">
|
||
{AI_RULES.map((rule) => (
|
||
<div key={rule.id} className="flex items-center gap-3 p-3 bg-slate-50 rounded-lg">
|
||
<Checkbox
|
||
checked={selectedRules.includes(rule.id)}
|
||
onCheckedChange={(checked) => {
|
||
if (checked) {
|
||
setSelectedRules([...selectedRules, rule.id])
|
||
} else {
|
||
setSelectedRules(selectedRules.filter((id) => id !== rule.id))
|
||
}
|
||
}}
|
||
/>
|
||
<span className="text-sm font-medium">{rule.name}</span>
|
||
</div>
|
||
))}
|
||
</div>
|
||
</div>
|
||
|
||
<div className="space-y-2">
|
||
<Label>重算范围</Label>
|
||
<Select value={scope} onValueChange={setScope}>
|
||
<SelectTrigger>
|
||
<SelectValue />
|
||
</SelectTrigger>
|
||
<SelectContent>
|
||
<SelectItem value="incremental">增量数据(仅新增/变更)</SelectItem>
|
||
<SelectItem value="full">全量数据</SelectItem>
|
||
<SelectItem value="failed">仅失败记录</SelectItem>
|
||
</SelectContent>
|
||
</Select>
|
||
</div>
|
||
|
||
<div className="p-4 bg-amber-50 rounded-lg border border-amber-200">
|
||
<div className="flex items-start gap-2">
|
||
<AlertTriangle className="w-5 h-5 text-amber-600 mt-0.5" />
|
||
<div>
|
||
<p className="font-medium text-amber-800">注意</p>
|
||
<p className="text-sm text-amber-700 mt-1">
|
||
{scope === "full"
|
||
? "全量重算将处理所有历史数据,预计耗时较长,请确认后执行。"
|
||
: "增量重算仅处理新增或变更的数据,速度较快。"}
|
||
</p>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div className="grid grid-cols-2 gap-4 text-sm">
|
||
<div className="p-3 bg-slate-50 rounded-lg">
|
||
<div className="flex items-center gap-2 text-slate-500 mb-1">
|
||
<Database className="w-4 h-4" />
|
||
预计处理量
|
||
</div>
|
||
<p className="font-bold text-slate-800">
|
||
{scope === "full" ? "128.9M" : scope === "failed" ? "3.4K" : "15.6K"} 条
|
||
</p>
|
||
</div>
|
||
<div className="p-3 bg-slate-50 rounded-lg">
|
||
<div className="flex items-center gap-2 text-slate-500 mb-1">
|
||
<Clock className="w-4 h-4" />
|
||
预计耗时
|
||
</div>
|
||
<p className="font-bold text-slate-800">
|
||
{scope === "full" ? "约 2 小时" : scope === "failed" ? "约 5 分钟" : "约 15 分钟"}
|
||
</p>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
)}
|
||
|
||
<DialogFooter>
|
||
<Button variant="outline" onClick={() => onOpenChange(false)} disabled={isProcessing}>
|
||
{isProcessing ? "后台运行" : "取消"}
|
||
</Button>
|
||
{!isProcessing && (
|
||
<Button onClick={handleStart} disabled={selectedRules.length === 0}>
|
||
<RefreshCw className="w-4 h-4 mr-2" />
|
||
开始重算
|
||
</Button>
|
||
)}
|
||
</DialogFooter>
|
||
</DialogContent>
|
||
</Dialog>
|
||
)
|
||
}
|