Files
smart-cropx-ui/src/app/(app)/central-config/personal-center/account-security/components/SecurityOverview.tsx
2025-11-10 09:19:56 +08:00

188 lines
7.1 KiB
TypeScript

'use client';
import { Shield, Key, Smartphone, AlertTriangle, CheckCircle, Clock } from 'lucide-react';
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
import { Progress } from '@/components/ui/progress';
import { Button } from '@/components/ui/button';
import type { SecuritySettings } from '../types';
interface SecurityOverviewProps {
securitySettings: SecuritySettings | null;
onTabChange: (tab: string) => void;
}
export function SecurityOverview({ securitySettings, onTabChange }: SecurityOverviewProps) {
const securityScore = calculateSecurityScore(securitySettings);
function calculateSecurityScore(settings: SecuritySettings | null): number {
if (!settings) return 0;
let score = 0;
// 密码强度 (30%)
if (settings.passwordStrength === 'strong') score += 30;
else if (settings.passwordStrength === 'medium') score += 20;
else score += 10;
// 双因素认证 (25%)
if (settings.twoFactorEnabled) score += 25;
// 安全问题设置 (20%)
if (settings.securityQuestions.length > 0) score += 20;
// 登录提醒 (15%)
if (settings.loginAlert) score += 15;
// 信任设备管理 (10%)
if (settings.trustedDevices.length <= 3) score += 10;
return score;
}
const getSecurityLevel = (score: number) => {
if (score >= 80) return { level: '高', color: 'text-green-600', bg: 'bg-green-50' };
if (score >= 60) return { level: '中', color: 'text-yellow-600', bg: 'bg-yellow-50' };
return { level: '低', color: 'text-red-600', bg: 'bg-red-50' };
};
const securityLevel = getSecurityLevel(securityScore);
const securityItems = [
{
title: '密码强度',
icon: Key,
status: securitySettings?.passwordStrength === 'strong' ? 'good' :
securitySettings?.passwordStrength === 'medium' ? 'warning' : 'danger',
description: securitySettings?.passwordStrength === 'strong' ? '强密码' :
securitySettings?.passwordStrength === 'medium' ? '中等强度' : '弱密码',
action: () => onTabChange('password')
},
{
title: '双因素认证',
icon: Smartphone,
status: securitySettings?.twoFactorEnabled ? 'good' : 'warning',
description: securitySettings?.twoFactorEnabled ? '已启用' : '未启用',
action: () => onTabChange('twoFactor')
},
{
title: '安全问题',
icon: Shield,
status: securitySettings?.securityQuestions.length > 0 ? 'good' : 'warning',
description: securitySettings?.securityQuestions.length > 0 ?
`已设置 ${securitySettings.securityQuestions.length} 个问题` : '未设置',
action: () => onTabChange('questions')
},
{
title: '登录提醒',
icon: Clock,
status: securitySettings?.loginAlert ? 'good' : 'warning',
description: securitySettings?.loginAlert ? '已启用' : '未启用',
action: () => onTabChange('notifications')
}
];
return (
<div className="space-y-6">
{/* 安全评分卡片 */}
<Card>
<CardHeader>
<CardTitle className="flex items-center space-x-2">
<Shield className="h-5 w-5" />
<span></span>
</CardTitle>
</CardHeader>
<CardContent>
<div className="text-center">
<div className={`inline-flex items-center justify-center w-24 h-24 rounded-full ${securityLevel.bg} mb-4`}>
<span className={`text-3xl font-bold ${securityLevel.color}`}>
{securityScore}
</span>
</div>
<h3 className={`text-lg font-semibold ${securityLevel.color}`}>
: {securityLevel.level}
</h3>
<Progress value={securityScore} className="mt-4" />
<p className="text-sm text-gray-500 mt-2">
</p>
</div>
</CardContent>
</Card>
{/* 安全项列表 */}
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
{securityItems.map((item, index) => {
const Icon = item.icon;
return (
<Card key={index}>
<CardContent className="p-4">
<div className="flex items-start justify-between">
<div className="flex items-center space-x-3">
<div className={`p-2 rounded-full ${
item.status === 'good' ? 'bg-green-100' :
item.status === 'warning' ? 'bg-yellow-100' : 'bg-red-100'
}`}>
<Icon className={`h-5 w-5 ${
item.status === 'good' ? 'text-green-600' :
item.status === 'warning' ? 'text-yellow-600' : 'text-red-600'
}`} />
</div>
<div>
<h4 className="font-semibold">{item.title}</h4>
<p className="text-sm text-gray-600">{item.description}</p>
</div>
</div>
<div className="flex items-center space-x-1">
{item.status === 'good' && <CheckCircle className="h-4 w-4 text-green-500" />}
{item.status === 'warning' && <AlertTriangle className="h-4 w-4 text-yellow-500" />}
{item.status === 'danger' && <AlertTriangle className="h-4 w-4 text-red-500" />}
{item.status !== 'good' && (
<Button variant="outline" size="sm" onClick={item.action}>
</Button>
)}
</div>
</div>
</CardContent>
</Card>
);
})}
</div>
{/* 最近活动 */}
<Card>
<CardHeader>
<CardTitle></CardTitle>
</CardHeader>
<CardContent>
<div className="space-y-3">
<div className="flex items-center justify-between p-3 bg-green-50 rounded">
<div className="flex items-center space-x-3">
<CheckCircle className="h-5 w-5 text-green-500" />
<div>
<p className="font-medium"></p>
<p className="text-sm text-gray-600">
{securitySettings?.lastLoginTime ?
new Date(securitySettings.lastLoginTime).toLocaleString('zh-CN') :
'未知时间'
}
</p>
</div>
</div>
<div className="text-right">
<p className="text-sm font-medium">{securitySettings?.lastLoginIp}</p>
<p className="text-xs text-gray-500"></p>
</div>
</div>
</div>
<div className="text-center mt-4">
<Button variant="outline" onClick={() => onTabChange('history')}>
</Button>
</div>
</CardContent>
</Card>
</div>
);
}