2
0
Files
fe.wfc/src/views/_builtin/login/modules/register.vue
2024-11-27 11:39:45 +08:00

335 lines
9.4 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.
<script setup lang="ts">
import { computed, reactive, ref } from 'vue';
import { useI18n } from 'vue-i18n'; // 添加这行
import { useRouterPush } from '@/hooks/common/router';
import { useAntdForm, useFormRules } from '@/hooks/common/form';
import { useCaptcha } from '@/hooks/business/captcha';
import { useWindowSize } from '@vueuse/core';
import { registerTerms } from '@/views/_builtin/login/modules/terms';
defineOptions({
name: 'Register'
});
const { t } = useI18n();
const { toggleLoginModule } = useRouterPush();
const { formRef, validate } = useAntdForm();
const { label, isCounting, loading, getCaptcha } = useCaptcha();
const { width } = useWindowSize();
const isMobile = computed(() => width.value <= 640);
// 当前步骤
const currentStep = ref(0);
// 是否同意协议
const agreeTerms = ref(false);
// 第一步表单数据
interface BasicFormModel {
username: string;
fullName: string;
age: number | undefined;
gender: string;
phone: string;
email: string;
address: string;
}
const basicModel = reactive<BasicFormModel>({
username: '',
fullName: '',
age: undefined,
gender: '',
phone: '',
email: '',
address: ''
});
// 第三表单数据
interface SecurityFormModel {
email: string;
code: string;
password: string;
confirmPassword: string;
}
const securityModel = reactive<SecurityFormModel>({
email: '',
code: '',
password: '',
confirmPassword: ''
});
// 第一步表单验证规则
const basicRules = computed(() => {
const { formRules } = useFormRules();
return {
username: formRules.username,
phone: formRules.phone,
email: formRules.email
};
});
// 第三步表单验证规则
const securityRules = computed(() => {
const { formRules, createConfirmPwdRule } = useFormRules();
return {
phone: formRules.phone,
code: formRules.code,
password: formRules.pwd,
confirmPassword: createConfirmPwdRule(securityModel.password)
};
});
// 协议内容
const terms = computed(() => {
const locale = useI18n().locale.value;
return registerTerms[locale === 'zh-CN' ? 'zh-CN' : 'en-US'];
});
// 步骤控制函数
async function nextStep() {
if (currentStep.value === 0) {
await validate();
// 复制邮箱到第三步
securityModel.email = basicModel.email;
}
if (currentStep.value === 1) {
if (!agreeTerms.value) {
window.$message?.error(t('page.login.register.agreeTermsFirst'));
return;
}
}
currentStep.value += 1;
}
function prevStep() {
currentStep.value -= 1;
}
async function handleSubmit() {
await validate();
// request to register
$message?.success(t('page.login.common.validateSuccess'));
}
// 在script setup部分添加一个新的计算属性
const showSteps = computed(() => !isMobile.value);
</script>
<template>
<ASteps
v-if="showSteps"
:current="currentStep"
size="small"
class="max-w-full mb-16px"
:direction="'horizontal'"
:responsive="false"
>
<AStep :title="t('page.login.register.basicInfo')" />
<AStep :title="t('page.login.register.terms')" />
<AStep :title="t('page.login.register.security')" />
</ASteps>
<div v-else class="mobile-step-indicator mb-16px text-center">
{{ currentStep + 1 }}/3: {{
currentStep === 0
? t('page.login.register.basicInfo')
: currentStep === 1
? t('page.login.register.terms')
: t('page.login.register.security')
}}
</div>
<div class="step-content">
<!-- 第一步基本信息 -->
<div v-if="currentStep === 0">
<AForm
ref="formRef"
:model="basicModel"
:rules="basicRules"
:label-wrap="true"
:label-col="{ span: 8 }"
:wrapper-col="{ span: 16 }"
>
<ARow :gutter="[8,2]">
<ACol :span="24" :lg="24">
<AFormItem name="username" :label="t('page.login.register.username')">
<AInput v-model:value="basicModel.username" />
</AFormItem>
</ACol>
<ACol :span="24" :lg="24">
<AFormItem name="fullName" :label="t('page.login.register.fullName')">
<AInput v-model:value="basicModel.fullName" />
</AFormItem>
</ACol>
<ACol :xs="12" :sm="12" :lg="24">
<AFormItem name="age" :label="t('page.login.register.age')">
<AInputNumber v-model:value="basicModel.age" class="!w-full" />
</AFormItem>
</ACol>
<ACol :xs="12" :sm="12" :lg="24">
<AFormItem name="gender" :label="t('page.login.register.gender')">
<ASelect v-model:value="basicModel.gender">
<ASelectOption value="male">{{ t('page.login.register.male') }}</ASelectOption>
<ASelectOption value="female">{{ t('page.login.register.female') }}</ASelectOption>
</ASelect>
</AFormItem>
</ACol>
<ACol :lg="24" :span="24">
<AFormItem name="phone" :label="t('page.login.register.phone')">
<AInput v-model:value="basicModel.phone" />
</AFormItem>
</ACol>
<ACol :lg="24" :span="24">
<AFormItem name="email" :label="t('page.login.register.email')">
<AInput v-model:value="basicModel.email" />
</AFormItem>
</ACol>
<ACol :lg="24" :span="24">
<AFormItem name="address" :label="t('page.login.register.address')">
<ATextarea v-model:value="basicModel.address" :rows="2" />
</AFormItem>
</ACol>
<ACol :lg="24" :span="24">
<ASpace direction="vertical" size="small" class="w-full">
<AButton type="primary" block size="small" @click="nextStep">
{{ t('page.login.register.next') }}
</AButton>
<AButton block size="small" @click="toggleLoginModule('pwd-login')">
{{ t('page.login.common.back') }}
</AButton>
</ASpace>
</ACol>
</ARow>
</AForm>
</div>
<!-- 第二步协议 -->
<div v-if="currentStep === 1">
<ATextarea
:value="terms"
:rows="12"
readonly
class="mb-16px"
size="small"
:style="{ fontSize: '14px', lineHeight: '1.6' }"
/>
<div class="mb-16px">
<ACheckbox
v-model:checked="agreeTerms"
class="terms-checkbox"
>
{{ t('page.login.register.agreeTerms') }}
</ACheckbox>
</div>
<ASpace direction="vertical" size="small" class="w-full">
<AButton type="primary" block size="small" :disabled="!agreeTerms" @click="nextStep">
{{ t('page.login.register.next') }}
</AButton>
<AButton block size="small" @click="prevStep">
{{ t('page.login.register.prev') }}
</AButton>
</ASpace>
</div>
<!-- 第三步安全信息 -->
<div v-if="currentStep === 2">
<AForm
ref="formRef"
:model="securityModel"
:rules="securityRules"
:label-wrap="true"
:label-col="{ span: 8 }"
:wrapper-col="{ span: 16 }"
class="compact-form"
>
<AFormItem name="email" :label="t('page.login.register.email')">
<AInput
v-model:value="securityModel.email"
:placeholder="t('page.login.common.emailPlaceholder')"
/>
</AFormItem>
<AFormItem name="code" :label="t('page.login.register.code')">
<div class="w-full flex-y-center gap-8px">
<AInput
v-model:value="securityModel.code"
:placeholder="t('page.login.common.codePlaceholder')"
/>
<AButton
size="small"
:disabled="isCounting"
:loading="loading"
@click="getCaptcha(securityModel.email)"
>
{{ label }}
</AButton>
</div>
</AFormItem>
<AFormItem name="password" :label="t('page.login.register.password')">
<AInputPassword
v-model:value="securityModel.password"
:placeholder="t('page.login.common.passwordPlaceholder')"
/>
</AFormItem>
<AFormItem name="confirmPassword" :label="t('page.login.register.confirmPassword')">
<AInputPassword
v-model:value="securityModel.confirmPassword"
:placeholder="t('page.login.common.confirmPasswordPlaceholder')"
/>
</AFormItem>
<AFormItem :wrapper-col="{ xs: { span: 24 }, sm: { span: 24 } }">
<ASpace direction="vertical" size="small" class="w-full">
<AButton type="primary" block size="small" @click="handleSubmit">
{{ t('common.confirm') }}
</AButton>
<AButton block size="small" @click="prevStep">
{{ t('page.login.register.prev') }}
</AButton>
</ASpace>
</AFormItem>
</AForm>
</div>
</div>
</template>
<style scoped>
@media (max-width: 640px) {
.mobile-step-indicator {
font-size: 13px;
padding: 4px 8px;
margin-bottom: 8px;
text-align: center;
width: 100%;
border-radius: 4px;
}
:deep(.ant-space) {
gap: 4px !important;
}
}
:deep(.terms-checkbox) {
.ant-checkbox + span {
font-size: 14px;
color: rgba(0, 0, 0, 0.65);
}
}
:deep(.ant-input[readonly]) {
background-color: #f5f5f5;
cursor: default;
}
:deep(.ant-form-item-label) {
white-space: normal;
text-align: left;
> label {
height: auto !important;
padding-bottom: 4px;
}
}
</style>