127 lines
5.1 KiB
TypeScript
127 lines
5.1 KiB
TypeScript
"use client"
|
||
|
||
import { SiteHeader } from "@/components/site-header"
|
||
import { useFamily } from "@/context/family-context"
|
||
import { Button } from "@/components/ui/button"
|
||
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card"
|
||
import { Download, Upload, AlertTriangle, RefreshCw } from "lucide-react"
|
||
import { Alert, AlertDescription, AlertTitle } from "@/components/ui/alert"
|
||
import { useRef, useState } from "react"
|
||
|
||
export default function SettingsPage() {
|
||
const { treeData, loadData, resetData } = useFamily()
|
||
const [importStatus, setImportStatus] = useState<string>("")
|
||
const fileInputRef = useRef<HTMLInputElement>(null)
|
||
|
||
return (
|
||
<div className="min-h-screen bg-background flex flex-col font-sans">
|
||
<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">系统设置 (Settings)</h1>
|
||
|
||
<div className="space-y-6">
|
||
<Card>
|
||
<CardHeader>
|
||
<CardTitle className="flex items-center gap-2">
|
||
<Download className="h-5 w-5" /> 数据备份 (Backup)
|
||
</CardTitle>
|
||
<CardDescription>将家族数据导出为 JSON 文件进行本地保存。</CardDescription>
|
||
</CardHeader>
|
||
<CardContent>
|
||
<Button
|
||
onClick={() => {
|
||
const dataStr = JSON.stringify(treeData, null, 2)
|
||
const blob = new Blob([dataStr], { type: "application/json" })
|
||
const url = URL.createObjectURL(blob)
|
||
const link = document.createElement("a")
|
||
link.href = url
|
||
link.download = `family_tree_backup_${new Date().toISOString().split("T")[0]}.json`
|
||
document.body.appendChild(link)
|
||
link.click()
|
||
document.body.removeChild(link)
|
||
}}
|
||
>
|
||
导出数据 (Export JSON)
|
||
</Button>
|
||
</CardContent>
|
||
</Card>
|
||
|
||
<Card>
|
||
<CardHeader>
|
||
<CardTitle className="flex items-center gap-2">
|
||
<Upload className="h-5 w-5" /> 数据恢复 (Restore)
|
||
</CardTitle>
|
||
<CardDescription>从 JSON 备份文件恢复数据。注意:这将覆盖当前的所有数据。</CardDescription>
|
||
</CardHeader>
|
||
<CardContent className="space-y-4">
|
||
<Alert variant="destructive">
|
||
<AlertTriangle className="h-4 w-4" />
|
||
<AlertTitle>警告</AlertTitle>
|
||
<AlertDescription>导入操作不可撤销。建议在导入前先导出当前数据备份。</AlertDescription>
|
||
</Alert>
|
||
|
||
<div className="flex items-center gap-4">
|
||
<input
|
||
type="file"
|
||
ref={fileInputRef}
|
||
className="hidden"
|
||
accept=".json"
|
||
onChange={(e) => {
|
||
const file = e.target.files?.[0]
|
||
if (!file) return
|
||
|
||
const reader = new FileReader()
|
||
reader.onload = async (event) => {
|
||
try {
|
||
const json = JSON.parse(event.target?.result as string)
|
||
if (json.members && json.rootId) {
|
||
await loadData(json)
|
||
setImportStatus("导入成功!")
|
||
// Clear input so same file can be selected again if needed
|
||
if (fileInputRef.current) fileInputRef.current.value = ""
|
||
} else {
|
||
setImportStatus("错误:无效的数据格式")
|
||
}
|
||
} catch (err) {
|
||
console.error(err)
|
||
setImportStatus("错误:无法解析文件")
|
||
}
|
||
}
|
||
reader.readAsText(file)
|
||
}}
|
||
/>
|
||
<Button variant="outline" onClick={() => fileInputRef.current?.click()}>
|
||
选择文件...
|
||
</Button>
|
||
<span className="text-sm text-muted-foreground">{importStatus}</span>
|
||
</div>
|
||
</CardContent>
|
||
</Card>
|
||
|
||
<Card>
|
||
<CardHeader>
|
||
<CardTitle className="flex items-center gap-2">
|
||
<RefreshCw className="h-5 w-5" /> 重置系统
|
||
</CardTitle>
|
||
<CardDescription>清除所有数据并恢复到初始演示状态。</CardDescription>
|
||
</CardHeader>
|
||
<CardContent>
|
||
<Button
|
||
variant="destructive"
|
||
onClick={async () => {
|
||
if (confirm("确定要重置吗?所有更改将丢失。")) {
|
||
await resetData()
|
||
alert("数据已重置为演示状态。")
|
||
}
|
||
}}
|
||
>
|
||
重置所有数据
|
||
</Button>
|
||
</CardContent>
|
||
</Card>
|
||
</div>
|
||
</main>
|
||
</div>
|
||
)
|
||
}
|