0.0.1.0
This commit is contained in:
@@ -0,0 +1,176 @@
|
||||
"use client"
|
||||
|
||||
import { useState } from "react"
|
||||
import { useRouter } from "next/navigation"
|
||||
import { Button } from "@/components/ui/button"
|
||||
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card"
|
||||
import { Alert, AlertDescription, AlertTitle } from "@/components/ui/alert"
|
||||
import { Progress } from "@/components/ui/progress"
|
||||
import { SiteHeader } from "@/components/site-header"
|
||||
import { Database, AlertTriangle, CheckCircle2 } from "lucide-react"
|
||||
import { migrateFromIndexedDB, clearIndexedDB } from "@/lib/migrate-data"
|
||||
|
||||
export default function MigratePage() {
|
||||
const router = useRouter()
|
||||
const [status, setStatus] = useState<"idle" | "migrating" | "success" | "error">("idle")
|
||||
const [progress, setProgress] = useState(0)
|
||||
const [result, setResult] = useState<any>(null)
|
||||
const [error, setError] = useState("")
|
||||
|
||||
const handleMigrate = async () => {
|
||||
setStatus("migrating")
|
||||
setProgress(0)
|
||||
setError("")
|
||||
|
||||
try {
|
||||
// 模拟进度
|
||||
setProgress(20)
|
||||
|
||||
// 这里需要先创建一个家族树,然后获取 treeId 和 userId
|
||||
// 为了演示,我们先创建一个默认的家族树
|
||||
const treeResponse = await fetch("/api/trees", {
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify({
|
||||
name: "我的家族树",
|
||||
description: "从 IndexedDB 迁移的数据"
|
||||
})
|
||||
})
|
||||
|
||||
if (!treeResponse.ok) {
|
||||
throw new Error("创建家族树失败")
|
||||
}
|
||||
|
||||
const { tree } = await treeResponse.json()
|
||||
setProgress(40)
|
||||
|
||||
// 执行迁移
|
||||
const migrateResult = await migrateFromIndexedDB(tree.id, tree.ownerId)
|
||||
setProgress(80)
|
||||
|
||||
setResult(migrateResult)
|
||||
setProgress(100)
|
||||
setStatus("success")
|
||||
|
||||
} catch (err: any) {
|
||||
setError(err.message || "迁移失败")
|
||||
setStatus("error")
|
||||
}
|
||||
}
|
||||
|
||||
const handleClearIndexedDB = async () => {
|
||||
if (!confirm("确定要清空 IndexedDB 数据吗?此操作不可撤销!")) {
|
||||
return
|
||||
}
|
||||
|
||||
try {
|
||||
await clearIndexedDB()
|
||||
alert("IndexedDB 数据已清空")
|
||||
} catch (err: any) {
|
||||
alert("清空失败: " + err.message)
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="min-h-screen bg-background flex flex-col">
|
||||
<SiteHeader />
|
||||
<main className="container mx-auto py-8 px-4 md:px-6 flex-1 max-w-3xl">
|
||||
<h1 className="text-3xl font-serif font-bold mb-8">数据迁移</h1>
|
||||
|
||||
<div className="space-y-6">
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<CardTitle className="flex items-center gap-2">
|
||||
<Database className="h-5 w-5" />
|
||||
从 IndexedDB 迁移到 PostgreSQL
|
||||
</CardTitle>
|
||||
<CardDescription>
|
||||
将本地浏览器中的家族数据迁移到云端数据库
|
||||
</CardDescription>
|
||||
</CardHeader>
|
||||
<CardContent className="space-y-4">
|
||||
<Alert>
|
||||
<AlertTriangle className="h-4 w-4" />
|
||||
<AlertTitle>注意</AlertTitle>
|
||||
<AlertDescription>
|
||||
迁移前请确保:
|
||||
<ul className="list-disc list-inside mt-2 space-y-1">
|
||||
<li>已登录账号</li>
|
||||
<li>本地有需要迁移的数据</li>
|
||||
<li>网络连接正常</li>
|
||||
</ul>
|
||||
</AlertDescription>
|
||||
</Alert>
|
||||
|
||||
{status === "migrating" && (
|
||||
<div className="space-y-2">
|
||||
<div className="flex items-center justify-between text-sm">
|
||||
<span>迁移进度</span>
|
||||
<span>{progress}%</span>
|
||||
</div>
|
||||
<Progress value={progress} />
|
||||
</div>
|
||||
)}
|
||||
|
||||
{status === "success" && result && (
|
||||
<Alert className="border-green-500 bg-green-50">
|
||||
<CheckCircle2 className="h-4 w-4 text-green-600" />
|
||||
<AlertTitle className="text-green-900">迁移成功!</AlertTitle>
|
||||
<AlertDescription className="text-green-800">
|
||||
成功迁移 {result.count} / {result.total} 个成员
|
||||
{result.errors > 0 && `,失败 ${result.errors} 个`}
|
||||
</AlertDescription>
|
||||
</Alert>
|
||||
)}
|
||||
|
||||
{status === "error" && (
|
||||
<Alert variant="destructive">
|
||||
<AlertTriangle className="h-4 w-4" />
|
||||
<AlertTitle>迁移失败</AlertTitle>
|
||||
<AlertDescription>{error}</AlertDescription>
|
||||
</Alert>
|
||||
)}
|
||||
|
||||
<div className="flex gap-4">
|
||||
<Button
|
||||
onClick={handleMigrate}
|
||||
disabled={status === "migrating"}
|
||||
className="flex-1"
|
||||
>
|
||||
{status === "migrating" ? "迁移中..." : "开始迁移"}
|
||||
</Button>
|
||||
|
||||
{status === "success" && (
|
||||
<Button
|
||||
variant="outline"
|
||||
onClick={() => router.push("/")}
|
||||
>
|
||||
查看数据
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
<Card className="border-destructive/50">
|
||||
<CardHeader>
|
||||
<CardTitle className="text-destructive">危险操作</CardTitle>
|
||||
<CardDescription>
|
||||
迁移成功后,可以选择清空本地数据
|
||||
</CardDescription>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<Button
|
||||
variant="destructive"
|
||||
onClick={handleClearIndexedDB}
|
||||
disabled={status !== "success"}
|
||||
>
|
||||
清空 IndexedDB 数据
|
||||
</Button>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</div>
|
||||
</main>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user