index.vue 2.81 KB
<!-- PasswordStrengthPopover.vue -->
<template>
  <div class="password-wrapper">
    <el-popover
      v-model:visible="popoverVisible"
      placement="top"
      :width="300"
      trigger="manual"
      popper-class="password-strength-popper"
      :disabled="!internalValue"
    >
      <template #reference>
        <el-input
          ref="inputRef"
          :model-value="internalValue"
          :placeholder="placeholder"
          type="password"
          clearable
          showPassword
          :disabled="disabled"
          autocomplete="new-password"
          @update:model-value="onInput"
          @focus="onFocus"
          @blur="onBlur"
          @click="focusInput"
        />
      </template>

      <PasswordStrengthContent
        ref="strengthRef"
        :storeKey="storeKey"
        :password="internalValue"
      />
    </el-popover>
  </div>
</template>

<script setup lang="ts">
import { ref, watch, nextTick } from 'vue';
import { ElInput } from 'element-plus';
import PasswordStrengthContent from './PasswordStrengthContent.vue';

const props = defineProps({
  modelValue: {
    type: String,
    default: ''
  },
  placeholder: {
    type: String,
    default: '请输入密码'
  },
  // 新增:用于表单校验的字段名(可选)
  name: {
    type: String,
    default: 'password'
  },
  storeKey: {
    type: String,
    default: 'firstUnmetRequirement'
  },
  disabled: {
    type: Boolean,
    default: false
  }
});

const emit = defineEmits(['update:modelValue','change']);

const internalValue = ref(props.modelValue);

watch(
  () => props.modelValue,
  (newVal) => {
    internalValue.value = newVal;
  }
);

const onInput = (val) => {
  internalValue.value = val;
  emit('update:modelValue', val);
  emit('change', val);
};

const popoverVisible = ref(false);
const inputRef = ref(null);
const strengthRef = ref(null);

const onFocus = () => {
  if (internalValue.value) {
    popoverVisible.value = true;
  }
};

const onBlur = () => {
  setTimeout(() => {
    const activeEl = document.activeElement;
    const popoverEl = document.querySelector('.password-strength-popper');
    if (!popoverEl?.contains(activeEl)) {
      popoverVisible.value = false;
    }
  }, 200);
};

const focusInput = () => {
  nextTick(() => {
    inputRef.value?.focus();
  });
};

watch(internalValue, (newVal) => {
  if (newVal) {
    if (document.activeElement === inputRef.value?.$el?.querySelector('input')) {
      popoverVisible.value = true;
    }
  } else {
    popoverVisible.value = false;
  }
});



// 暴露给 el-form 使用
defineExpose({
});
</script>

<style scoped lang="scss">
.password-wrapper {
  width: 100%;
  // max-width: 400px;
}
.password-strength-popper {
  padding: 14px !important;
  background: #fff;
  border: 1px solid #ddd;
  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
  border-radius: 8px;
}
</style>