Files
PicHost/app/components/PasswordStrength.vue
O96u 22cb2ca8bc release: v1.0.2
Add i18n (zh-CN/en), mobile navigation menus, stats and upload UI
improvements, and Docker reset-password CLI for emergency recovery.
2026-08-23 17:43:42 +08:00

84 lines
2.1 KiB
Vue

<script setup lang="ts">
import { analyzePassword, PASSWORD_MIN_LENGTH } from '~/utils/password-strength'
const props = defineProps<{
password: string
}>()
const { t } = useI18n()
const analysis = computed(() => analyzePassword(props.password))
const show = computed(() => props.password.length > 0)
const labelColor = computed(() => {
switch (analysis.value.label) {
case 'strong':
return 'text-success'
case 'medium':
return 'text-warning'
default:
return 'text-error'
}
})
const barColor = computed(() => {
switch (analysis.value.label) {
case 'strong':
return 'bg-success'
case 'medium':
return 'bg-warning'
default:
return 'bg-error/80'
}
})
const rules = computed(() => [
{ key: 'minLength', label: t('password.ruleMinLength', { n: PASSWORD_MIN_LENGTH }) },
{ key: 'mixedCase', label: t('password.ruleMixedCase') },
{ key: 'hasNumber', label: t('password.ruleNumber') },
{ key: 'hasSpecial', label: t('password.ruleSpecial') }
] as const)
</script>
<template>
<div
v-if="show"
class="space-y-3 rounded-lg border border-default bg-muted/20 px-3 py-3"
>
<div class="flex items-center justify-between text-sm">
<span class="text-muted">{{ t('password.strength') }}</span>
<span
class="font-medium"
:class="labelColor"
>
{{ t(`password.${analysis.label}`) }}
</span>
</div>
<div class="flex gap-1.5">
<span
v-for="index in 4"
:key="index"
class="h-1.5 flex-1 rounded-full transition-colors"
:class="index <= analysis.score ? barColor : 'bg-default'"
/>
</div>
<ul class="flex flex-wrap gap-x-4 gap-y-1.5 text-xs">
<li
v-for="rule in rules"
:key="rule.key"
class="flex items-center gap-1.5 transition-colors"
:class="analysis.criteria[rule.key] ? 'text-success' : 'text-muted'"
>
<UIcon
:name="analysis.criteria[rule.key] ? 'i-lucide-check' : 'i-lucide-circle'"
class="size-3.5 shrink-0"
/>
<span>{{ rule.label }}</span>
</li>
</ul>
</div>
</template>