fix:用户注册界面
This commit is contained in:
@@ -1,86 +1,334 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { computed, reactive } from 'vue';
|
import { computed, reactive, ref } from 'vue';
|
||||||
import { $t } from '@/locales';
|
import { useI18n } from 'vue-i18n'; // 添加这行
|
||||||
import { useRouterPush } from '@/hooks/common/router';
|
import { useRouterPush } from '@/hooks/common/router';
|
||||||
import { useAntdForm, useFormRules } from '@/hooks/common/form';
|
import { useAntdForm, useFormRules } from '@/hooks/common/form';
|
||||||
import { useCaptcha } from '@/hooks/business/captcha';
|
import { useCaptcha } from '@/hooks/business/captcha';
|
||||||
|
import { useWindowSize } from '@vueuse/core';
|
||||||
|
import { registerTerms } from '@/views/_builtin/login/modules/terms';
|
||||||
|
|
||||||
defineOptions({
|
defineOptions({
|
||||||
name: 'CodeLogin'
|
name: 'Register'
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const { t } = useI18n();
|
||||||
const { toggleLoginModule } = useRouterPush();
|
const { toggleLoginModule } = useRouterPush();
|
||||||
const { formRef, validate } = useAntdForm();
|
const { formRef, validate } = useAntdForm();
|
||||||
const { label, isCounting, loading, getCaptcha } = useCaptcha();
|
const { label, isCounting, loading, getCaptcha } = useCaptcha();
|
||||||
|
|
||||||
interface FormModel {
|
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;
|
phone: string;
|
||||||
|
email: string;
|
||||||
|
address: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
const basicModel = reactive<BasicFormModel>({
|
||||||
|
username: '',
|
||||||
|
fullName: '',
|
||||||
|
age: undefined,
|
||||||
|
gender: '',
|
||||||
|
phone: '',
|
||||||
|
email: '',
|
||||||
|
address: ''
|
||||||
|
});
|
||||||
|
|
||||||
|
// 第三表单数据
|
||||||
|
interface SecurityFormModel {
|
||||||
|
email: string;
|
||||||
code: string;
|
code: string;
|
||||||
password: string;
|
password: string;
|
||||||
confirmPassword: string;
|
confirmPassword: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
const model: FormModel = reactive({
|
const securityModel = reactive<SecurityFormModel>({
|
||||||
phone: '',
|
email: '',
|
||||||
code: '',
|
code: '',
|
||||||
password: '',
|
password: '',
|
||||||
confirmPassword: ''
|
confirmPassword: ''
|
||||||
});
|
});
|
||||||
|
|
||||||
const rules = computed<Record<keyof FormModel, App.Global.FormRule[]>>(() => {
|
// 第一步表单验证规则
|
||||||
const { formRules, createConfirmPwdRule } = useFormRules();
|
const basicRules = computed(() => {
|
||||||
|
const { formRules } = useFormRules();
|
||||||
|
return {
|
||||||
|
username: formRules.username,
|
||||||
|
phone: formRules.phone,
|
||||||
|
email: formRules.email
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
// 第三步表单验证规则
|
||||||
|
const securityRules = computed(() => {
|
||||||
|
const { formRules, createConfirmPwdRule } = useFormRules();
|
||||||
return {
|
return {
|
||||||
phone: formRules.phone,
|
phone: formRules.phone,
|
||||||
code: formRules.code,
|
code: formRules.code,
|
||||||
password: formRules.pwd,
|
password: formRules.pwd,
|
||||||
confirmPassword: createConfirmPwdRule(model.password)
|
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() {
|
async function handleSubmit() {
|
||||||
await validate();
|
await validate();
|
||||||
// request to register
|
// request to register
|
||||||
$message?.success($t('page.login.common.validateSuccess'));
|
$message?.success(t('page.login.common.validateSuccess'));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 在script setup部分添加一个新的计算属性
|
||||||
|
const showSteps = computed(() => !isMobile.value);
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<AForm ref="formRef" :model="model" :rules="rules">
|
<ASteps
|
||||||
<AFormItem name="phone">
|
v-if="showSteps"
|
||||||
<AInput v-model:value="model.phone" size="large" :placeholder="$t('page.login.common.phonePlaceholder')" />
|
:current="currentStep"
|
||||||
</AFormItem>
|
size="small"
|
||||||
<AFormItem name="code">
|
class="max-w-full mb-16px"
|
||||||
<div class="w-full flex-y-center gap-16px">
|
:direction="'horizontal'"
|
||||||
<AInput v-model:value="model.code" size="large" :placeholder="$t('page.login.common.codePlaceholder')" />
|
:responsive="false"
|
||||||
<AButton size="large" :disabled="isCounting" :loading="loading" @click="getCaptcha(model.phone)">
|
>
|
||||||
{{ label }}
|
<AStep :title="t('page.login.register.basicInfo')" />
|
||||||
</AButton>
|
<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>
|
</div>
|
||||||
</AFormItem>
|
<ASpace direction="vertical" size="small" class="w-full">
|
||||||
<AFormItem name="password">
|
<AButton type="primary" block size="small" :disabled="!agreeTerms" @click="nextStep">
|
||||||
<AInputPassword
|
{{ t('page.login.register.next') }}
|
||||||
v-model:value="model.password"
|
</AButton>
|
||||||
size="large"
|
<AButton block size="small" @click="prevStep">
|
||||||
:placeholder="$t('page.login.common.passwordPlaceholder')"
|
{{ t('page.login.register.prev') }}
|
||||||
/>
|
</AButton>
|
||||||
</AFormItem>
|
</ASpace>
|
||||||
<AFormItem name="confirmPassword">
|
</div>
|
||||||
<AInputPassword
|
|
||||||
v-model:value="model.confirmPassword"
|
<!-- 第三步:安全信息 -->
|
||||||
size="large"
|
<div v-if="currentStep === 2">
|
||||||
:placeholder="$t('page.login.common.confirmPasswordPlaceholder')"
|
<AForm
|
||||||
/>
|
ref="formRef"
|
||||||
</AFormItem>
|
:model="securityModel"
|
||||||
<ASpace direction="vertical" size="large" class="w-full">
|
:rules="securityRules"
|
||||||
<AButton type="primary" block size="large" shape="round" @click="handleSubmit">
|
:label-wrap="true"
|
||||||
{{ $t('common.confirm') }}
|
:label-col="{ span: 8 }"
|
||||||
</AButton>
|
:wrapper-col="{ span: 16 }"
|
||||||
<AButton block size="large" shape="round" @click="toggleLoginModule('pwd-login')">
|
class="compact-form"
|
||||||
{{ $t('page.login.common.back') }}
|
>
|
||||||
</AButton>
|
<AFormItem name="email" :label="t('page.login.register.email')">
|
||||||
</ASpace>
|
<AInput
|
||||||
</AForm>
|
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>
|
</template>
|
||||||
|
|
||||||
<style scoped></style>
|
<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>
|
||||||
|
|||||||
Reference in New Issue
Block a user