Files
SatoNano/frontend/src/app/dashboard/settings/page.tsx
2026-01-06 23:49:23 +08:00

182 lines
5.8 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

'use client';
import { useState } from 'react';
import { useForm } from 'react-hook-form';
import { zodResolver } from '@hookform/resolvers/zod';
import { motion } from 'framer-motion';
import { Lock, Shield, CheckCircle, AlertTriangle } from 'lucide-react';
import {
Card,
CardHeader,
CardTitle,
CardDescription,
CardContent,
Button,
Input,
Alert,
} from '@/components/ui';
import { useAuthStore } from '@/lib/store';
import { changePasswordSchema, ChangePasswordFormData } from '@/lib/validations';
export default function SettingsPage() {
const { changePassword, error, clearError, isLoading } = useAuthStore();
const [success, setSuccess] = useState(false);
const {
register,
handleSubmit,
reset,
formState: { errors },
} = useForm<ChangePasswordFormData>({
resolver: zodResolver(changePasswordSchema),
});
const onSubmit = async (data: ChangePasswordFormData) => {
setSuccess(false);
try {
await changePassword(data.currentPassword, data.newPassword);
setSuccess(true);
reset();
setTimeout(() => setSuccess(false), 5000);
} catch {
// 错误已在 store 中处理
}
};
return (
<div className="p-6 lg:p-10 max-w-4xl">
{/* 页面标题 */}
<motion.div
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
className="mb-8"
>
<h1 className="text-2xl lg:text-3xl font-bold text-foreground">
</h1>
<p className="text-foreground-muted mt-2">
</p>
</motion.div>
{/* 修改密码 */}
<motion.div
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
transition={{ delay: 0.1 }}
className="mb-6"
>
<Card>
<CardHeader>
<div className="flex items-center gap-3">
<div className="p-2 rounded-lg bg-primary/10 text-primary">
<Shield className="w-5 h-5" />
</div>
<div>
<CardTitle></CardTitle>
<CardDescription>
</CardDescription>
</div>
</div>
</CardHeader>
<CardContent>
{/* 提示信息 */}
{error && (
<Alert variant="error" className="mb-6" onClose={clearError}>
{error}
</Alert>
)}
{success && (
<Alert variant="success" className="mb-6">
<CheckCircle className="w-4 h-4 inline mr-2" />
使
</Alert>
)}
<form onSubmit={handleSubmit(onSubmit)} className="space-y-6">
<Input
{...register('currentPassword')}
type="password"
label="当前密码"
placeholder="请输入当前密码"
leftIcon={<Lock className="w-5 h-5" />}
error={errors.currentPassword?.message}
autoComplete="current-password"
/>
<Input
{...register('newPassword')}
type="password"
label="新密码"
placeholder="请输入新密码"
leftIcon={<Lock className="w-5 h-5" />}
error={errors.newPassword?.message}
hint="8-128 位,需包含大小写字母和数字"
autoComplete="new-password"
/>
<Input
{...register('confirmPassword')}
type="password"
label="确认新密码"
placeholder="请再次输入新密码"
leftIcon={<Lock className="w-5 h-5" />}
error={errors.confirmPassword?.message}
autoComplete="new-password"
/>
<div className="flex justify-end pt-4 border-t border-border">
<Button
type="submit"
isLoading={isLoading}
leftIcon={<Lock className="w-5 h-5" />}
>
</Button>
</div>
</form>
</CardContent>
</Card>
</motion.div>
{/* 安全提示 */}
<motion.div
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
transition={{ delay: 0.2 }}
>
<Card>
<CardContent>
<div className="flex items-start gap-4">
<div className="p-2 rounded-lg bg-accent-yellow/10 text-accent-yellow">
<AlertTriangle className="w-5 h-5" />
</div>
<div>
<h3 className="font-medium text-foreground mb-2"></h3>
<ul className="text-sm text-foreground-muted space-y-2">
<li className="flex items-center gap-2">
<div className="w-1.5 h-1.5 rounded-full bg-foreground-subtle" />
</li>
<li className="flex items-center gap-2">
<div className="w-1.5 h-1.5 rounded-full bg-foreground-subtle" />
使
</li>
<li className="flex items-center gap-2">
<div className="w-1.5 h-1.5 rounded-full bg-foreground-subtle" />
</li>
</ul>
</div>
</div>
</CardContent>
</Card>
</motion.div>
</div>
);
}