188 lines
7.1 KiB
TypeScript
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>
|
|
);
|
|
} |