perf(verification-code): 优化验证码输入组件逻辑和样式

This commit is contained in:
rd
2025-09-16 11:23:43 +08:00
parent a9434e7270
commit 3fba8ac8fc
4 changed files with 55 additions and 57 deletions

View File

@ -20,7 +20,7 @@
</template>
</Input.Password>
</FormItem>
<FormItem name="confirm_password" class="password-form-item">
<FormItem class="password-form-item !mb-36px" name="confirm_password">
<Input.Password
v-model:value="formData.confirm_password"
placeholder="密码确认"
@ -37,7 +37,7 @@
type="primary"
:disabled="disabledSubmitBtn"
:loading="submitLoading"
class="w-full !h-48px mt-36px !rounded-8px"
class="w-full !h-48px mt-36px !rounded-8px h-22px"
@click="handleSubmit"
>完成更改
</Button>
@ -54,7 +54,9 @@
<p class="cts !text-12px !lh-20px !color-#939499" v-if="countdown > 0">
{{ countdown }} 秒后您可以再次发送验证码
</p>
<Button type="text" class="pl-0" v-if="hasGetCode && countdown === 0" @click="getCaptcha">重新发送</Button>
<Button v-if="hasGetCode && countdown === 0" class="pl-0 h-22px" type="text" @click="getCaptcha"
>重新发送
</Button>
</template>
</div>
@ -79,6 +81,11 @@ import icon2 from '@/assets/img/login/icon-open.png';
const emit = defineEmits(['success', 'cancel']);
const INITIAL_FORM_DATA = {
captcha: '',
password: '',
confirm_password: '',
};
const visible = ref(false);
const phone = ref('');
const isCheckPass = ref(false);
@ -90,11 +97,7 @@ const timer = ref<number | null>(null);
const hasGetCode = ref(false);
const submitLoading = ref(false);
const verificationCodeRef = ref(null);
const formData = ref({
captcha: '',
password: '',
confirm_password: '',
});
const formData = ref(cloneDeep(INITIAL_FORM_DATA));
const formRules = {
password: [
@ -113,7 +116,7 @@ const formRules = {
{
required: true,
validator: (_rule, value) => {
if (value !== formData.value.password) {
if (value && value !== formData.value.password) {
return Promise.reject('确认密码与设置的密码不同');
}
return Promise.resolve();
@ -216,6 +219,8 @@ const onClose = () => {
countdown.value = 0;
hasGetCode.value = false;
submitLoading.value = false;
isCheckPass.value = false;
formData.valu = cloneDeep(INITIAL_FORM_DATA);
formRef.value?.resetFields();
formRef.value?.clearValidate();
clearTimer();

View File

@ -39,14 +39,10 @@
.ant-form {
.ant-form-item {
&:not(:last-child) {
margin-bottom: 24px !important;
}
margin-bottom: 24px !important;
.ant-input-affix-wrapper {
border-radius: 8px !important;
}
}
}

View File

@ -7,10 +7,8 @@
ref="inputRef"
:key="index"
:value="item.value"
:class="{ error: item.error, fill: item.value }"
:class="{ error: isError, fill: item.value }"
@input="handleInput(index, $event.target.value)"
@focus="handleFocus(index)"
@blur="handleBlur(index)"
:maxlength="1"
placeholder=""
/>
@ -34,11 +32,17 @@ const emits = defineEmits(['success']);
const codeArray = ref([]);
const activeIndex = ref(0); // 当前激活的输入框索引
const errorMessage = ref('');
const isError = ref(false);
const inputRef = ref(null);
// 处理输入事件
const handleInput = (index, value) => {
if (!value || !/^\d$/.test(value)) return;
if (!value) {
return;
}
isError.value = false;
errorMessage.value = '';
codeArray.value[index].value = value;
@ -48,45 +52,36 @@ const handleInput = (index, value) => {
}
};
const handleFocus = (index) => {
activeIndex.value = index;
};
const handleBlur = (index) => {
const _value = codeArray.value[index].value;
if (!_value || !/^\d$/.test(_value)) {
codeArray.value[index].error = true;
} else {
if (errorMessage.value) {
codeArray.value.forEach((item) => {
item.error = false;
});
}
errorMessage.value = '';
codeArray.value[index].error = false;
}
};
// 验证验证码逻辑
const validateCode = async () => {
const captcha = codeArray.value.map((item) => item.value).join('');
const { code } = await postCheckUpdatePasswordCaptcha({ captcha });
if (code === 200) {
emits('success', captcha);
} else {
activeIndex.value = 0;
inputRef.value?.[activeIndex.value]?.focus?.();
errorMessage.value = '验证码错误,请检查后重试';
codeArray.value.forEach((item) => {
item.error = true;
item.value = '';
});
}
return new Promise((resolve, reject) => {
isError.value = codeArray.value.some((_value) => !_value);
isError.value ? reject() : resolve();
});
};
const onSubmit = () => {
validateCode().then(async () => {
const captcha = codeArray.value.map((item) => item.value).join('');
const { code } = await postCheckUpdatePasswordCaptcha({ captcha });
if (code === 200) {
emits('success', captcha);
} else {
activeIndex.value = 0;
inputRef.value?.[activeIndex.value]?.focus?.();
errorMessage.value = '验证码错误,请检查后重试';
isError.value = true;
codeArray.value.forEach((item) => {
item.value = '';
});
}
});
};
const init = () => {
activeIndex.value = 0;
errorMessage.value = '';
isError.value = false;
inputRef.value = null;
codeArray.value = new Array(6).fill().map(() => ({ value: '', error: false }));
@ -100,7 +95,7 @@ watch(
() => codeArray.value,
(newVal) => {
if (newVal.every((item) => item.value)) {
validateCode();
onSubmit();
}
},
{ deep: true },
@ -129,17 +124,20 @@ defineExpose({ init });
caret-color: #6d4cfe;
&.error {
border-color: #f64b31 !important;
}
&:hover {
border-color: #6d4cfe !important;
}
&:focus-within {
border-color: #6d4cfe !important;
}
&.fill {
color: #6d4cfe;
}
&.error {
border-color: #f64b31 !important;
}
}
}
}

View File

@ -182,14 +182,13 @@ const formRules = {
{
required: true,
validator: (_rule, value) => {
console.log('validator');
// if (!value) {
// return Promise.reject('请输入密码确认');
// }
// if (value.length < 6) {
// return Promise.reject('密码长度不能小于6位');
// }
if (value !== formData.value.password) {
if (value && value !== formData.value.password) {
return Promise.reject('确认密码与设置的密码不同');
}
return Promise.resolve();