| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131 |
- import React, { useEffect, useState } from 'react';
- import { passwordApi, PasswordStrengthLevel } from '../services/passwordApi';
- import { Shield, ShieldAlert, ShieldCheck } from 'lucide-react';
- interface PasswordStrengthIndicatorProps {
- password: string;
- onStrengthChange?: (meetsRequirements: boolean) => void;
- }
- const PasswordStrengthIndicator: React.FC<PasswordStrengthIndicatorProps> = ({
- password,
- onStrengthChange
- }) => {
- const [strength, setStrength] = useState<PasswordStrengthLevel>('weak');
- const [score, setScore] = useState<number>(0);
- const [suggestions, setSuggestions] = useState<string[]>([]);
- const [meetsRequirements, setMeetsRequirements] = useState<boolean>(false);
- const [isChecking, setIsChecking] = useState<boolean>(false);
- useEffect(() => {
- const checkStrength = async () => {
- if (!password) {
- setStrength('weak');
- setScore(0);
- setSuggestions([]);
- setMeetsRequirements(false);
- onStrengthChange?.(false);
- return;
- }
- setIsChecking(true);
- try {
- const result = await passwordApi.checkPasswordStrength(password);
- setStrength(result.strength);
- setScore(result.score);
- setSuggestions(result.suggestions);
- setMeetsRequirements(result.meets_requirements);
- onStrengthChange?.(result.meets_requirements);
- } catch (error) {
- console.error('密码强度检测失败:', error);
- } finally {
- setIsChecking(false);
- }
- };
- // 防抖处理
- const timer = setTimeout(checkStrength, 300);
- return () => clearTimeout(timer);
- }, [password, onStrengthChange]);
- if (!password) {
- return null;
- }
- // 强度配置
- const strengthConfig = {
- weak: {
- label: '弱',
- color: 'text-red-600',
- bgColor: 'bg-red-100',
- borderColor: 'border-red-300',
- barColor: 'bg-red-500',
- icon: ShieldAlert,
- width: '33%'
- },
- medium: {
- label: '中',
- color: 'text-yellow-600',
- bgColor: 'bg-yellow-100',
- borderColor: 'border-yellow-300',
- barColor: 'bg-yellow-500',
- icon: Shield,
- width: '66%'
- },
- strong: {
- label: '强',
- color: 'text-green-600',
- bgColor: 'bg-green-100',
- borderColor: 'border-green-300',
- barColor: 'bg-green-500',
- icon: ShieldCheck,
- width: '100%'
- }
- };
- const config = strengthConfig[strength];
- const Icon = config.icon;
- return (
- <div className="space-y-2">
- {/* 强度指示器 */}
- <div className="flex items-center gap-2">
- <div className="flex-1">
- <div className="h-2 bg-gray-200 rounded-full overflow-hidden">
- <div
- className={`h-full ${config.barColor} transition-all duration-300`}
- style={{ width: config.width }}
- />
- </div>
- </div>
- <div className={`flex items-center gap-1 ${config.color}`}>
- <Icon className="w-4 h-4" />
- <span className="text-sm font-bold">{config.label}</span>
- </div>
- </div>
- {/* 建议提示 */}
- {suggestions.length > 0 && (
- <div className={`text-xs ${config.color} space-y-1`}>
- {suggestions.map((suggestion, index) => (
- <div key={index} className="flex items-start gap-1">
- <span>•</span>
- <span>{suggestion}</span>
- </div>
- ))}
- </div>
- )}
- {/* 强度说明 */}
- {!isChecking && (
- <div className="text-xs text-gray-500">
- {strength === 'weak' && '密码强度较弱,建议添加大写字母、数字和特殊符号'}
- {strength === 'medium' && '密码强度中等,添加更多字符类型可提高安全性'}
- {strength === 'strong' && '密码强度很好,符合安全要求'}
- </div>
- )}
- </div>
- );
- };
- export default PasswordStrengthIndicator;
|