zayac-admin/continew-admin-ui/src/views/login/components/phone-login.vue
Charles7c 4d70bc84db feat: 支持手机号登录(演示环境不开放)
1.在个人中心-安全设置中绑手机号后,才支持手机号登录
2.SMS4J(短信聚合框架,轻松集成多家短信服务,解决接入多个短信 SDK 的繁琐流程)
2023-10-27 21:32:25 +08:00

221 lines
5.7 KiB
Vue
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.

<template>
<a-form
ref="formRef"
:model="form"
:rules="rules"
layout="vertical"
size="large"
class="login-form"
@submit="handleLogin"
>
<a-form-item field="phone" hide-label>
<a-select :options="['+86']" style="flex: 1 1" default-value="+86" />
<a-input
v-model="form.phone"
:placeholder="$t('login.phone.placeholder.phone')"
:max-length="11"
allow-clear
/>
</a-form-item>
<a-form-item field="captcha" hide-label>
<a-input
v-model="form.captcha"
:placeholder="$t('login.phone.placeholder.captcha')"
:max-length="4"
allow-clear
style="flex: 1 1"
/>
<a-button
class="captcha-btn"
:loading="captchaLoading"
:disabled="captchaDisable"
@click="handleSendCaptcha"
>
{{ captchaBtnName }}
</a-button>
</a-form-item>
<a-button
class="btn"
:loading="loading"
type="primary"
html-type="submit"
:disabled="captchaDisable"
>{{ $t('login.button') }}演示不开放
</a-button>
</a-form>
</template>
<script lang="ts" setup>
import { getCurrentInstance, ref, toRefs, reactive, computed } from 'vue';
import { useI18n } from 'vue-i18n';
import { useRouter } from 'vue-router';
import { ValidatedError } from '@arco-design/web-vue';
import { useUserStore } from '@/store';
import { PhoneLoginReq } from '@/api/auth';
import { getSmsCaptcha } from '@/api/common/captcha';
const { proxy } = getCurrentInstance() as any;
const { t } = useI18n();
const router = useRouter();
const userStore = useUserStore();
const loading = ref(false);
const captchaLoading = ref(false);
const captchaDisable = ref(true);
const captchaTime = ref(60);
const captchaTimer = ref();
const captchaBtnNameKey = ref('login.captcha.get');
const captchaBtnName = computed(() => t(captchaBtnNameKey.value));
const data = reactive({
form: {
phone: '',
captcha: '',
} as PhoneLoginReq,
rules: {
phone: [
{ required: true, message: t('login.phone.error.required.phone') },
{
match: /^1[3-9]\d{9}$/,
message: t('login.phone.error.match.phone'),
},
],
captcha: [
{ required: true, message: t('login.phone.error.required.captcha') },
],
},
});
const { form, rules } = toRefs(data);
/**
* 重置验证码
*/
const resetCaptcha = () => {
window.clearInterval(captchaTimer.value);
captchaTime.value = 60;
captchaBtnNameKey.value = 'login.captcha.get';
captchaDisable.value = false;
};
/**
* 发送验证码
*/
const handleSendCaptcha = () => {
if (captchaLoading.value) return;
proxy.$refs.formRef.validateField('phone', (valid: any) => {
if (!valid) {
captchaLoading.value = true;
captchaBtnNameKey.value = 'login.captcha.ing';
getSmsCaptcha(form.value.phone)
.then((res) => {
captchaLoading.value = false;
captchaDisable.value = true;
captchaBtnNameKey.value = `${t(
'login.captcha.get'
)}(${(captchaTime.value -= 1)}s)`;
captchaTimer.value = window.setInterval(() => {
captchaTime.value -= 1;
captchaBtnNameKey.value = `${t('login.captcha.get')}(${
captchaTime.value
}s)`;
if (captchaTime.value <= 0) {
resetCaptcha();
}
}, 1000);
proxy.$message.success(res.msg);
})
.catch(() => {
resetCaptcha();
captchaLoading.value = false;
});
}
});
};
/**
* 登录
*
* @param errors 表单验证错误
* @param values 表单数据
*/
const handleLogin = ({
errors,
values,
}: {
errors: Record<string, ValidatedError> | undefined;
values: Record<string, any>;
}) => {
if (loading.value) return;
if (!errors) {
loading.value = true;
userStore
.phoneLogin({
phone: values.phone,
captcha: values.captcha,
})
.then(() => {
const { redirect, ...othersQuery } = router.currentRoute.value.query;
router.push({
name: (redirect as string) || 'Workplace',
query: {
...othersQuery,
},
});
proxy.$notification.success(t('login.success'));
})
.catch(() => {
form.value.captcha = '';
})
.finally(() => {
loading.value = false;
});
}
};
</script>
<style lang="less" scoped>
.login-form {
box-sizing: border-box;
padding: 0 5px;
margin-top: 16px;
.arco-input-wrapper,
:deep(.arco-select-view-single) {
background-color: var(--color-bg-white);
border: 1px solid var(--color-border-3);
height: 40px;
border-radius: 4px;
font-size: 13px;
}
.arco-input-wrapper.arco-input-error {
background-color: var(--color-danger-light-1);
border-color: var(--color-danger-light-4);
}
.captcha-btn {
height: 40px;
margin-left: 12px;
min-width: 98px;
border-radius: 4px;
}
.arco-btn-secondary:not(.arco-btn-disabled) {
background-color: #f6f8fa;
border: 1px solid #dde2e9;
color: #41464f;
}
.arco-btn-secondary:not(.arco-btn-disabled):hover {
background-color: transparent;
border: 1px solid rgb(var(--primary-6));
}
.btn {
border-radius: 4px;
box-shadow: 0 0 0 1px #05f, 0 2px 1px rgba(0, 0, 0, 0.15);
font-size: 14px;
font-weight: 500;
height: 40px;
line-height: 22px;
margin: 20px 0 12px;
width: 100%;
}
}
</style>