Files
user-system/frontend/admin/src/components/common/PasswordStrengthIndicator.tsx
2026-05-12 20:34:30 +08:00

85 lines
2.4 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* PasswordStrengthIndicator - 密码强度指示器
*/
import { Progress } from 'antd'
import { useMemo } from 'react'
interface PasswordStrengthIndicatorProps {
password: string
}
function calculateStrength(password: string): { score: number; level: 'weak' | 'fair' | 'good' | 'strong' } {
if (!password) {
return { score: 0, level: 'weak' }
}
let score = 0
// 长度检查
if (password.length >= 8) score += 25
if (password.length >= 12) score += 10
if (password.length >= 16) score += 5
// 字符类型检查
if (/[a-z]/.test(password)) score += 15
if (/[A-Z]/.test(password)) score += 20
if (/[0-9]/.test(password)) score += 20
if (/[^a-zA-Z0-9]/.test(password)) score += 20
// 正则匹配检查
if (/(?=.*[a-z])(?=.*[A-Z])(?=.*[0-9])/.test(password)) score += 5
if (/(?=.*[a-z])(?=.*[A-Z])(?=.*[^a-zA-Z0-9])/.test(password)) score += 5
// 扣分项
if (/^[a-zA-Z0-9]+$/.test(password)) score -= 10 // 纯字母数字
if (/^[a-z]+$|^[A-Z]+$|^[0-9]+$/.test(password)) score -= 15 // 单一种类
// 限制范围
score = Math.max(0, Math.min(100, score))
let level: 'weak' | 'fair' | 'good' | 'strong'
if (score < 30) level = 'weak'
else if (score < 60) level = 'fair'
else if (score < 80) level = 'good'
else level = 'strong'
return { score, level }
}
const strengthConfig = {
weak: { color: '#ff4d4f', text: '弱' },
fair: { color: '#faad14', text: '中等' },
good: { color: '#52c41a', text: '良好' },
strong: { color: '#52c41a', text: '强' },
}
export function PasswordStrengthIndicator({ password }: PasswordStrengthIndicatorProps) {
const { score, level } = useMemo(() => calculateStrength(password), [password])
if (!password) {
return null
}
const config = strengthConfig[level]
return (
<div style={{ marginTop: 4 }}>
<div style={{ display: 'flex', justifyContent: 'space-between', marginBottom: 4 }}>
<span style={{ fontSize: 12, color: 'var(--color-text-muted)' }}></span>
<span style={{ fontSize: 12, color: config.color }}>{config.text}</span>
</div>
<Progress
percent={score}
showInfo={false}
strokeColor={config.color}
trailColor="var(--color-fill-secondary)"
size="small"
/>
<div style={{ fontSize: 11, color: 'var(--color-text-muted)', marginTop: 2 }}>
8
</div>
</div>
)
}