85 lines
2.4 KiB
TypeScript
85 lines
2.4 KiB
TypeScript
/**
|
||
* 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>
|
||
)
|
||
}
|