Files
continew-admin-ui/src/views/login/components/phone-login.vue
Charles7c b56f029e68 refactor: 引入 unplugin-auto-import,减少重复性 Vue 函数引入
避免在每个 Vue 组件中都重复性的去声明 ref 等函数
2024-01-07 23:54:43 +08:00

250 lines
6.5 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="handleOpenBehaviorCaptcha"
>
{{ 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>
<Verify
ref="verifyRef"
:mode="captchaMode"
:captcha-type="captchaType"
:img-size="{ width: '330px', height: '155px' }"
@success="handleSendCaptcha"
></Verify>
</template>
<script lang="ts" setup>
import { useI18n } from 'vue-i18n';
import { ValidatedError } from '@arco-design/web-vue';
import { useUserStore } from '@/store';
import { PhoneLoginReq } from '@/api/auth';
import { BehaviorCaptchaReq, 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 captchaType = ref('blockPuzzle');
const captchaMode = ref('pop');
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 handleOpenBehaviorCaptcha = () => {
if (captchaLoading.value) return;
proxy.$refs.formRef.validateField('phone', (valid: any) => {
if (!valid) {
proxy.$refs.verifyRef.show();
}
});
};
/**
* 重置验证码
*/
const resetCaptcha = () => {
window.clearInterval(captchaTimer.value);
captchaTime.value = 60;
captchaBtnNameKey.value = 'login.captcha.get';
captchaDisable.value = false;
};
/**
* 发送验证码
*/
const handleSendCaptcha = (captchaParam: BehaviorCaptchaReq) => {
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, captchaParam)
.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%;
}
.arco-btn-primary.arco-btn-disabled,
.arco-btn-primary[type='submit'].arco-btn-disabled {
background-color: var(--color-neutral-4);
box-shadow:
0 0 0 1px var(--color-neutral-4),
0 2px 1px rgba(0, 0, 0, 0.15);
}
}
</style>