- 创建 Toast 通知组件,替换所有 alert() 调用 - 修复 useReview hook 内存泄漏(setInterval 清理) - 移除所有 console.error 和 console.log 语句 - 为复制操作失败添加用户友好的 toast 提示 Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
243 lines
9.5 KiB
TypeScript
243 lines
9.5 KiB
TypeScript
'use client'
|
||
|
||
import { useState } from 'react'
|
||
import { useRouter } from 'next/navigation'
|
||
import { useToast } from '@/components/ui/Toast'
|
||
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/Card'
|
||
import { Button } from '@/components/ui/Button'
|
||
import { Input } from '@/components/ui/Input'
|
||
import {
|
||
ArrowLeft,
|
||
Lock,
|
||
Shield,
|
||
Smartphone,
|
||
Mail,
|
||
Key,
|
||
Eye,
|
||
EyeOff,
|
||
CheckCircle,
|
||
AlertTriangle
|
||
} from 'lucide-react'
|
||
|
||
export default function AgencyAccountSettingsPage() {
|
||
const router = useRouter()
|
||
const toast = useToast()
|
||
const [showOldPassword, setShowOldPassword] = useState(false)
|
||
const [showNewPassword, setShowNewPassword] = useState(false)
|
||
const [showConfirmPassword, setShowConfirmPassword] = useState(false)
|
||
const [passwordForm, setPasswordForm] = useState({
|
||
oldPassword: '',
|
||
newPassword: '',
|
||
confirmPassword: '',
|
||
})
|
||
const [isSaving, setIsSaving] = useState(false)
|
||
|
||
// 模拟账号安全状态
|
||
const securityStatus = {
|
||
phone: { bound: true, value: '138****8888' },
|
||
email: { bound: true, value: 'zhang@starmedia.com' },
|
||
twoFactor: { enabled: false },
|
||
}
|
||
|
||
const handleChangePassword = async () => {
|
||
if (!passwordForm.oldPassword || !passwordForm.newPassword || !passwordForm.confirmPassword) {
|
||
toast.error('请填写完整密码信息')
|
||
return
|
||
}
|
||
if (passwordForm.newPassword !== passwordForm.confirmPassword) {
|
||
toast.error('两次输入的新密码不一致')
|
||
return
|
||
}
|
||
if (passwordForm.newPassword.length < 8) {
|
||
toast.error('新密码长度不能少于8位')
|
||
return
|
||
}
|
||
setIsSaving(true)
|
||
await new Promise(resolve => setTimeout(resolve, 1000))
|
||
setIsSaving(false)
|
||
toast.success('密码修改成功')
|
||
setPasswordForm({ oldPassword: '', newPassword: '', confirmPassword: '' })
|
||
}
|
||
|
||
return (
|
||
<div className="space-y-6">
|
||
{/* 顶部导航 */}
|
||
<div className="flex items-center gap-4">
|
||
<button
|
||
type="button"
|
||
onClick={() => router.back()}
|
||
className="p-2 rounded-lg hover:bg-bg-elevated transition-colors"
|
||
>
|
||
<ArrowLeft size={20} className="text-text-secondary" />
|
||
</button>
|
||
<div>
|
||
<h1 className="text-2xl font-bold text-text-primary">账户设置</h1>
|
||
<p className="text-sm text-text-secondary mt-0.5">管理密码和账号安全</p>
|
||
</div>
|
||
</div>
|
||
|
||
<div className="grid grid-cols-1 lg:grid-cols-2 gap-6">
|
||
{/* 修改密码 */}
|
||
<Card>
|
||
<CardHeader>
|
||
<CardTitle className="flex items-center gap-2">
|
||
<Lock size={18} className="text-accent-indigo" />
|
||
修改密码
|
||
</CardTitle>
|
||
</CardHeader>
|
||
<CardContent className="space-y-4">
|
||
<div>
|
||
<label className="text-sm text-text-secondary mb-1.5 block">当前密码</label>
|
||
<div className="relative">
|
||
<Input
|
||
type={showOldPassword ? 'text' : 'password'}
|
||
value={passwordForm.oldPassword}
|
||
onChange={(e) => setPasswordForm({ ...passwordForm, oldPassword: e.target.value })}
|
||
placeholder="请输入当前密码"
|
||
/>
|
||
<button
|
||
type="button"
|
||
onClick={() => setShowOldPassword(!showOldPassword)}
|
||
className="absolute right-3 top-1/2 -translate-y-1/2 text-text-tertiary hover:text-text-secondary"
|
||
>
|
||
{showOldPassword ? <EyeOff size={18} /> : <Eye size={18} />}
|
||
</button>
|
||
</div>
|
||
</div>
|
||
<div>
|
||
<label className="text-sm text-text-secondary mb-1.5 block">新密码</label>
|
||
<div className="relative">
|
||
<Input
|
||
type={showNewPassword ? 'text' : 'password'}
|
||
value={passwordForm.newPassword}
|
||
onChange={(e) => setPasswordForm({ ...passwordForm, newPassword: e.target.value })}
|
||
placeholder="请输入新密码(至少8位)"
|
||
/>
|
||
<button
|
||
type="button"
|
||
onClick={() => setShowNewPassword(!showNewPassword)}
|
||
className="absolute right-3 top-1/2 -translate-y-1/2 text-text-tertiary hover:text-text-secondary"
|
||
>
|
||
{showNewPassword ? <EyeOff size={18} /> : <Eye size={18} />}
|
||
</button>
|
||
</div>
|
||
</div>
|
||
<div>
|
||
<label className="text-sm text-text-secondary mb-1.5 block">确认新密码</label>
|
||
<div className="relative">
|
||
<Input
|
||
type={showConfirmPassword ? 'text' : 'password'}
|
||
value={passwordForm.confirmPassword}
|
||
onChange={(e) => setPasswordForm({ ...passwordForm, confirmPassword: e.target.value })}
|
||
placeholder="请再次输入新密码"
|
||
/>
|
||
<button
|
||
type="button"
|
||
onClick={() => setShowConfirmPassword(!showConfirmPassword)}
|
||
className="absolute right-3 top-1/2 -translate-y-1/2 text-text-tertiary hover:text-text-secondary"
|
||
>
|
||
{showConfirmPassword ? <EyeOff size={18} /> : <Eye size={18} />}
|
||
</button>
|
||
</div>
|
||
</div>
|
||
<Button
|
||
variant="primary"
|
||
className="w-full"
|
||
onClick={handleChangePassword}
|
||
disabled={isSaving}
|
||
>
|
||
{isSaving ? '保存中...' : '确认修改'}
|
||
</Button>
|
||
</CardContent>
|
||
</Card>
|
||
|
||
{/* 账号安全 */}
|
||
<Card>
|
||
<CardHeader>
|
||
<CardTitle className="flex items-center gap-2">
|
||
<Shield size={18} className="text-accent-green" />
|
||
账号安全
|
||
</CardTitle>
|
||
</CardHeader>
|
||
<CardContent className="space-y-4">
|
||
{/* 手机绑定 */}
|
||
<div className="flex items-center justify-between p-4 rounded-xl bg-bg-elevated">
|
||
<div className="flex items-center gap-3">
|
||
<div className="w-10 h-10 rounded-lg bg-accent-indigo/15 flex items-center justify-center">
|
||
<Smartphone size={20} className="text-accent-indigo" />
|
||
</div>
|
||
<div>
|
||
<p className="font-medium text-text-primary">手机绑定</p>
|
||
<p className="text-sm text-text-secondary">
|
||
{securityStatus.phone.bound ? securityStatus.phone.value : '未绑定'}
|
||
</p>
|
||
</div>
|
||
</div>
|
||
<div className="flex items-center gap-2">
|
||
{securityStatus.phone.bound ? (
|
||
<CheckCircle size={18} className="text-accent-green" />
|
||
) : (
|
||
<AlertTriangle size={18} className="text-accent-amber" />
|
||
)}
|
||
<Button variant="secondary" size="sm">
|
||
{securityStatus.phone.bound ? '更换' : '绑定'}
|
||
</Button>
|
||
</div>
|
||
</div>
|
||
|
||
{/* 邮箱绑定 */}
|
||
<div className="flex items-center justify-between p-4 rounded-xl bg-bg-elevated">
|
||
<div className="flex items-center gap-3">
|
||
<div className="w-10 h-10 rounded-lg bg-accent-blue/15 flex items-center justify-center">
|
||
<Mail size={20} className="text-accent-blue" />
|
||
</div>
|
||
<div>
|
||
<p className="font-medium text-text-primary">邮箱绑定</p>
|
||
<p className="text-sm text-text-secondary">
|
||
{securityStatus.email.bound ? securityStatus.email.value : '未绑定'}
|
||
</p>
|
||
</div>
|
||
</div>
|
||
<div className="flex items-center gap-2">
|
||
{securityStatus.email.bound ? (
|
||
<CheckCircle size={18} className="text-accent-green" />
|
||
) : (
|
||
<AlertTriangle size={18} className="text-accent-amber" />
|
||
)}
|
||
<Button variant="secondary" size="sm">
|
||
{securityStatus.email.bound ? '更换' : '绑定'}
|
||
</Button>
|
||
</div>
|
||
</div>
|
||
|
||
{/* 两步验证 */}
|
||
<div className="flex items-center justify-between p-4 rounded-xl bg-bg-elevated">
|
||
<div className="flex items-center gap-3">
|
||
<div className="w-10 h-10 rounded-lg bg-accent-amber/15 flex items-center justify-center">
|
||
<Key size={20} className="text-accent-amber" />
|
||
</div>
|
||
<div>
|
||
<p className="font-medium text-text-primary">两步验证</p>
|
||
<p className="text-sm text-text-secondary">
|
||
{securityStatus.twoFactor.enabled ? '已开启' : '未开启,建议开启以增强安全'}
|
||
</p>
|
||
</div>
|
||
</div>
|
||
<div className="flex items-center gap-2">
|
||
{securityStatus.twoFactor.enabled ? (
|
||
<CheckCircle size={18} className="text-accent-green" />
|
||
) : (
|
||
<AlertTriangle size={18} className="text-accent-amber" />
|
||
)}
|
||
<Button variant="secondary" size="sm">
|
||
{securityStatus.twoFactor.enabled ? '关闭' : '开启'}
|
||
</Button>
|
||
</div>
|
||
</div>
|
||
</CardContent>
|
||
</Card>
|
||
</div>
|
||
</div>
|
||
)
|
||
}
|