提供基本前后端骨架
This commit is contained in:
181
frontend/src/app/dashboard/settings/page.tsx
Normal file
181
frontend/src/app/dashboard/settings/page.tsx
Normal file
@@ -0,0 +1,181 @@
|
||||
'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>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user