182 lines
5.8 KiB
TypeScript
182 lines
5.8 KiB
TypeScript
'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>
|
||
);
|
||
}
|
||
|