提供基本前后端骨架

This commit is contained in:
hisatri
2026-01-06 23:49:23 +08:00
parent 84d4ccc226
commit 06f8176e23
89 changed files with 19293 additions and 2 deletions

View 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>
);
}