2026-01-16 22:04:12 +08:00
|
|
|
|
<template>
|
|
|
|
|
|
<div class="register-container" :class="{'dark-mode': isDarkMode}">
|
|
|
|
|
|
<div class="register-form-container">
|
|
|
|
|
|
<div class="register-type-tabs">
|
|
|
|
|
|
<div
|
|
|
|
|
|
v-for="(item, index) in registerTypes"
|
|
|
|
|
|
:key="item.type"
|
|
|
|
|
|
class="register-type-tab"
|
|
|
|
|
|
:class="{
|
|
|
|
|
|
'register-type-tab--active': currentType === item.type,
|
|
|
|
|
|
'register-type-tab--margin': index + 1 !== registerTypes.length
|
|
|
|
|
|
}"
|
|
|
|
|
|
@click="currentType = item.type"
|
|
|
|
|
|
>
|
|
|
|
|
|
{{ item.title }}
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
<el-form
|
|
|
|
|
|
:model="formData"
|
|
|
|
|
|
ref="formRef"
|
|
|
|
|
|
:rules="formRules"
|
|
|
|
|
|
:validate-on-rule-change="false"
|
|
|
|
|
|
class="register-form"
|
|
|
|
|
|
>
|
|
|
|
|
|
<!-- 用户名注册 -->
|
|
|
|
|
|
<div v-show="currentType === 'username'" class="register-form-section">
|
|
|
|
|
|
<el-form-item prop="username" class="form-item">
|
|
|
|
|
|
<div class="input-container">
|
|
|
|
|
|
<el-input
|
|
|
|
|
|
v-model="formData.username"
|
2026-04-07 11:01:11 +08:00
|
|
|
|
:placeholder="t('auth.register.username_placeholder')"
|
2026-01-16 22:04:12 +08:00
|
|
|
|
clearable
|
|
|
|
|
|
:inline-message="true"
|
|
|
|
|
|
:readonly="realNameInput"
|
|
|
|
|
|
@click="realNameInput = false"
|
|
|
|
|
|
@blur="realNameInput = true"
|
|
|
|
|
|
class="register-input"
|
|
|
|
|
|
>
|
|
|
|
|
|
<template #prefix>
|
|
|
|
|
|
<Icon
|
2026-04-07 11:01:11 +08:00
|
|
|
|
icon="ant-design:user-outlined"
|
|
|
|
|
|
:color="isDarkMode ? '#ffffff' : '#000000'"
|
|
|
|
|
|
class="input-prefix-icon"
|
|
|
|
|
|
/>
|
|
|
|
|
|
</template>
|
|
|
|
|
|
</el-input>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</el-form-item>
|
|
|
|
|
|
|
|
|
|
|
|
<el-form-item prop="email" class="form-item">
|
|
|
|
|
|
<div class="input-container">
|
|
|
|
|
|
<el-input
|
|
|
|
|
|
v-model="formData.email"
|
|
|
|
|
|
:placeholder="t('auth.register.email_placeholder')"
|
|
|
|
|
|
clearable
|
|
|
|
|
|
class="register-input"
|
|
|
|
|
|
>
|
|
|
|
|
|
<template #prefix>
|
|
|
|
|
|
<Icon
|
|
|
|
|
|
icon="ant-design:mail-outlined"
|
2026-01-16 22:04:12 +08:00
|
|
|
|
:color="isDarkMode ? '#ffffff' : '#000000'"
|
|
|
|
|
|
class="input-prefix-icon"
|
|
|
|
|
|
/>
|
|
|
|
|
|
</template>
|
|
|
|
|
|
</el-input>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</el-form-item>
|
|
|
|
|
|
|
|
|
|
|
|
<el-form-item prop="password" class="form-item">
|
|
|
|
|
|
<div class="input-container">
|
|
|
|
|
|
<el-input
|
|
|
|
|
|
v-model="formData.password"
|
2026-04-07 11:01:11 +08:00
|
|
|
|
:placeholder="t('auth.register.password_placeholder')"
|
2026-01-16 22:04:12 +08:00
|
|
|
|
type="password"
|
|
|
|
|
|
clearable
|
|
|
|
|
|
show-password
|
|
|
|
|
|
class="register-input"
|
|
|
|
|
|
>
|
|
|
|
|
|
<template #prefix>
|
|
|
|
|
|
<Icon
|
2026-04-07 11:01:11 +08:00
|
|
|
|
icon="ant-design:lock-outlined"
|
2026-01-16 22:04:12 +08:00
|
|
|
|
:color="isDarkMode ? '#ffffff' : '#000000'"
|
|
|
|
|
|
class="input-prefix-icon"
|
|
|
|
|
|
/>
|
|
|
|
|
|
</template>
|
|
|
|
|
|
</el-input>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</el-form-item>
|
|
|
|
|
|
|
|
|
|
|
|
<el-form-item prop="confirmPassword" class="form-item">
|
|
|
|
|
|
<div class="input-container">
|
|
|
|
|
|
<el-input
|
2026-04-07 11:01:11 +08:00
|
|
|
|
v-model="formData.confirm_password"
|
|
|
|
|
|
:placeholder="t('auth.register.confirm_password_placeholder')"
|
2026-01-16 22:04:12 +08:00
|
|
|
|
type="password"
|
|
|
|
|
|
clearable
|
|
|
|
|
|
show-password
|
|
|
|
|
|
class="register-input"
|
|
|
|
|
|
>
|
|
|
|
|
|
<template #prefix>
|
|
|
|
|
|
<Icon
|
2026-04-07 11:01:11 +08:00
|
|
|
|
icon="ant-design:lock-outlined"
|
2026-01-16 22:04:12 +08:00
|
|
|
|
:color="isDarkMode ? '#ffffff' : '#000000'"
|
|
|
|
|
|
class="input-prefix-icon"
|
|
|
|
|
|
/>
|
|
|
|
|
|
</template>
|
|
|
|
|
|
</el-input>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</el-form-item>
|
|
|
|
|
|
|
|
|
|
|
|
<el-form-item prop="captchaCode" class="form-item">
|
|
|
|
|
|
<div class="input-container">
|
|
|
|
|
|
<el-input
|
2026-04-07 11:01:11 +08:00
|
|
|
|
v-model="formData.captcha_code"
|
|
|
|
|
|
:placeholder="t('auth.register.captcha_placeholder')"
|
2026-01-16 22:04:12 +08:00
|
|
|
|
class="register-input"
|
|
|
|
|
|
>
|
|
|
|
|
|
<template #prefix>
|
|
|
|
|
|
<Icon
|
2026-04-07 11:01:11 +08:00
|
|
|
|
icon="ant-design:safety-outlined"
|
2026-01-16 22:04:12 +08:00
|
|
|
|
:color="isDarkMode ? '#ffffff' : '#000000'"
|
|
|
|
|
|
class="input-prefix-icon"
|
|
|
|
|
|
/>
|
|
|
|
|
|
</template>
|
|
|
|
|
|
<template #suffix>
|
|
|
|
|
|
<div class="captcha-image-container">
|
|
|
|
|
|
<el-image
|
|
|
|
|
|
:src="captcha.image.value"
|
|
|
|
|
|
class="captcha-image"
|
|
|
|
|
|
@click="captcha.refresh()"
|
|
|
|
|
|
></el-image>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</template>
|
|
|
|
|
|
</el-input>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</el-form-item>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
<!-- 手机号注册 -->
|
|
|
|
|
|
<div v-show="currentType === 'mobile' || configStore.login.is_bind_mobile" class="register-form-section">
|
|
|
|
|
|
<el-form-item prop="mobile" class="form-item">
|
|
|
|
|
|
<div class="input-container">
|
|
|
|
|
|
<el-input
|
|
|
|
|
|
v-model="formData.mobile"
|
2026-04-07 11:01:11 +08:00
|
|
|
|
:placeholder="t('auth.register.mobile_placeholder')"
|
2026-01-16 22:04:12 +08:00
|
|
|
|
clearable
|
|
|
|
|
|
class="register-input"
|
|
|
|
|
|
>
|
|
|
|
|
|
<template #prefix>
|
|
|
|
|
|
<Icon
|
2026-04-07 11:01:11 +08:00
|
|
|
|
icon="ant-design:phone-outlined"
|
2026-01-16 22:04:12 +08:00
|
|
|
|
:color="isDarkMode ? '#ffffff' : '#000000'"
|
|
|
|
|
|
class="input-prefix-icon"
|
|
|
|
|
|
/>
|
|
|
|
|
|
</template>
|
|
|
|
|
|
</el-input>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</el-form-item>
|
|
|
|
|
|
|
|
|
|
|
|
<el-form-item prop="mobileCode" class="form-item">
|
|
|
|
|
|
<div class="input-container">
|
|
|
|
|
|
<el-input
|
2026-04-07 11:01:11 +08:00
|
|
|
|
v-model="formData.mobile_code"
|
|
|
|
|
|
:placeholder="t('auth.register.code_placeholder')"
|
2026-01-16 22:04:12 +08:00
|
|
|
|
class="register-input"
|
|
|
|
|
|
>
|
|
|
|
|
|
<template #prefix>
|
|
|
|
|
|
<Icon
|
2026-04-07 11:01:11 +08:00
|
|
|
|
icon="ant-design:key-outlined"
|
2026-01-16 22:04:12 +08:00
|
|
|
|
:color="isDarkMode ? '#ffffff' : '#000000'"
|
|
|
|
|
|
class="input-prefix-icon"
|
|
|
|
|
|
/>
|
|
|
|
|
|
</template>
|
|
|
|
|
|
<template #suffix>
|
|
|
|
|
|
<sms-code
|
|
|
|
|
|
:mobile="formData.mobile"
|
|
|
|
|
|
type="login"
|
2026-04-07 11:01:11 +08:00
|
|
|
|
v-model="formData.mobile_key"
|
2026-01-16 22:04:12 +08:00
|
|
|
|
@click="sendSmsCode"
|
|
|
|
|
|
ref="smsCodeRef"
|
|
|
|
|
|
></sms-code>
|
|
|
|
|
|
</template>
|
|
|
|
|
|
</el-input>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</el-form-item>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
<!-- 登录链接 -->
|
|
|
|
|
|
<div class="form-actions">
|
|
|
|
|
|
<el-button type="primary" link @click="switchToLogin" class="login-link">
|
2026-04-07 11:01:11 +08:00
|
|
|
|
{{ t('auth.register.have_account') }},{{ t('auth.register.to_login') }}
|
2026-01-16 22:04:12 +08:00
|
|
|
|
</el-button>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
<!-- 注册按钮 -->
|
|
|
|
|
|
<div class="register-button-container">
|
|
|
|
|
|
<el-button
|
|
|
|
|
|
type="primary"
|
|
|
|
|
|
class="register-button"
|
|
|
|
|
|
size="large"
|
|
|
|
|
|
@click="handleRegister"
|
|
|
|
|
|
:loading="isLoading"
|
|
|
|
|
|
>
|
2026-04-07 11:01:11 +08:00
|
|
|
|
{{ isLoading ? t('auth.register.registering') : t('auth.register.register') }}
|
2026-01-16 22:04:12 +08:00
|
|
|
|
</el-button>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
<!-- 协议同意 -->
|
|
|
|
|
|
<div
|
|
|
|
|
|
v-if="configStore.login.agreement_show"
|
|
|
|
|
|
class="agreement-container"
|
|
|
|
|
|
>
|
|
|
|
|
|
<span
|
|
|
|
|
|
class="iconfont agreement-checkbox"
|
|
|
|
|
|
:class="isAgreeChecked ? 'icon-xuanze1' : 'icon-checkbox_nol'"
|
|
|
|
|
|
@click="toggleAgreement"
|
|
|
|
|
|
></span>
|
2026-04-07 11:01:11 +08:00
|
|
|
|
{{ t('auth.register.register_agree_tips') }}
|
2026-01-16 22:04:12 +08:00
|
|
|
|
<NuxtLink :to="protocolUrls.service" target="_blank">
|
2026-04-07 11:01:11 +08:00
|
|
|
|
<span class="agreement-link">{{ t('auth.register.user_agreement') }}</span>
|
2026-01-16 22:04:12 +08:00
|
|
|
|
</NuxtLink>
|
2026-04-07 11:01:11 +08:00
|
|
|
|
{{ t('auth.register.and') }}
|
2026-01-16 22:04:12 +08:00
|
|
|
|
<NuxtLink :to="protocolUrls.privacy" target="_blank">
|
2026-04-07 11:01:11 +08:00
|
|
|
|
<span class="agreement-link">{{ t('auth.register.privacy_agreement') }}</span>
|
2026-01-16 22:04:12 +08:00
|
|
|
|
</NuxtLink>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</el-form>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</template>
|
|
|
|
|
|
|
|
|
|
|
|
<script lang="ts" setup>
|
|
|
|
|
|
import { ref, reactive, computed } from 'vue'
|
|
|
|
|
|
import { registerUser, registerWithMobile } from '@/api/auth'
|
2026-04-07 11:01:11 +08:00
|
|
|
|
import { useConfigStore } from '@/stores/config'
|
|
|
|
|
|
import { useMemberStore } from '@/stores/member'
|
|
|
|
|
|
import { useCaptcha } from '~/composables/captcha'
|
2026-01-16 22:04:12 +08:00
|
|
|
|
import type { FormInstance } from 'element-plus'
|
2026-04-07 11:01:11 +08:00
|
|
|
|
import { Icon } from '~/components/icon'
|
|
|
|
|
|
import {t} from '~/composables/lang'
|
|
|
|
|
|
import validate from '~/utils/validate'
|
|
|
|
|
|
import { ElMessage } from 'element-plus'
|
|
|
|
|
|
|
2026-01-16 22:04:12 +08:00
|
|
|
|
|
|
|
|
|
|
// 状态管理
|
|
|
|
|
|
const memberStore = useMemberStore()
|
|
|
|
|
|
const configStore = useConfigStore()
|
|
|
|
|
|
|
|
|
|
|
|
// 暗黑模式状态
|
|
|
|
|
|
const isDarkMode = computed(() => {
|
|
|
|
|
|
return configStore.isDarkMode
|
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
|
|
// 初始化配置
|
2026-04-07 11:01:11 +08:00
|
|
|
|
// 配置已在 app.vue 中统一加载,这里不再重复请求
|
2026-01-16 22:04:12 +08:00
|
|
|
|
|
|
|
|
|
|
// 协议链接配置
|
|
|
|
|
|
const protocolUrls = computed(() => {
|
|
|
|
|
|
const basePath = location.pathname.includes('web') ? '/web' : ''
|
|
|
|
|
|
return {
|
|
|
|
|
|
service: `${basePath}/auth/agreement?key=service`,
|
|
|
|
|
|
privacy: `${basePath}/auth/agreement?key=privacy`
|
|
|
|
|
|
}
|
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
|
|
// 注册类型配置
|
|
|
|
|
|
const currentType = ref('')
|
|
|
|
|
|
const registerTypes = computed(() => {
|
|
|
|
|
|
const types = []
|
|
|
|
|
|
if (configStore.login.is_username) {
|
2026-04-07 11:01:11 +08:00
|
|
|
|
types.push({ type: 'username', title: t('auth.register.username_register') })
|
2026-01-16 22:04:12 +08:00
|
|
|
|
}
|
|
|
|
|
|
if (configStore.login.is_mobile && !configStore.login.is_bind_mobile) {
|
2026-04-07 11:01:11 +08:00
|
|
|
|
types.push({ type: 'mobile', title: t('auth.register.mobile_register') })
|
2026-01-16 22:04:12 +08:00
|
|
|
|
}
|
|
|
|
|
|
currentType.value = types[0]?.type || ''
|
|
|
|
|
|
return types
|
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
|
|
// 表单数据
|
|
|
|
|
|
const isLoading = ref(false)
|
|
|
|
|
|
const formData = reactive({
|
|
|
|
|
|
username: '',
|
2026-04-07 11:01:11 +08:00
|
|
|
|
email: '',
|
2026-01-16 22:04:12 +08:00
|
|
|
|
password: '',
|
2026-04-07 11:01:11 +08:00
|
|
|
|
confirm_password: '',
|
2026-01-16 22:04:12 +08:00
|
|
|
|
mobile: '',
|
2026-04-07 11:01:11 +08:00
|
|
|
|
mobile_code: '',
|
|
|
|
|
|
mobile_key: '',
|
|
|
|
|
|
captcha_key: '',
|
|
|
|
|
|
captcha_code: ''
|
2026-01-16 22:04:12 +08:00
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
|
|
// 表单验证规则
|
|
|
|
|
|
const formRef = ref<FormInstance>()
|
|
|
|
|
|
const formRules = computed(() => ({
|
|
|
|
|
|
username: {
|
|
|
|
|
|
type: 'string',
|
|
|
|
|
|
required: currentType.value === 'username',
|
2026-04-07 11:01:11 +08:00
|
|
|
|
message: t('auth.register.username_placeholder'),
|
2026-01-16 22:04:12 +08:00
|
|
|
|
trigger: ['blur', 'change'],
|
|
|
|
|
|
},
|
2026-04-07 11:01:11 +08:00
|
|
|
|
email: [
|
2026-01-16 22:04:12 +08:00
|
|
|
|
{
|
|
|
|
|
|
type: 'string',
|
|
|
|
|
|
required: currentType.value === 'username',
|
2026-04-07 11:01:11 +08:00
|
|
|
|
message: t('auth.register.email_required'),
|
|
|
|
|
|
trigger: ['blur', 'change'],
|
|
|
|
|
|
},
|
|
|
|
|
|
{
|
|
|
|
|
|
validator(rule: any, value: string, callback: any) {
|
|
|
|
|
|
if (currentType.value !== 'username') return true
|
|
|
|
|
|
return validate.email(value)
|
|
|
|
|
|
},
|
|
|
|
|
|
message: t('auth.register.email_error'),
|
|
|
|
|
|
trigger: ['change', 'blur'],
|
|
|
|
|
|
}
|
|
|
|
|
|
],
|
|
|
|
|
|
password: {
|
|
|
|
|
|
type: 'string',
|
|
|
|
|
|
required: currentType.value === 'username',
|
|
|
|
|
|
message: t('auth.register.password_placeholder'),
|
|
|
|
|
|
trigger: ['blur', 'change']
|
|
|
|
|
|
},
|
|
|
|
|
|
confirm_password: [
|
|
|
|
|
|
{
|
|
|
|
|
|
type: 'string',
|
|
|
|
|
|
required: currentType.value === 'username',
|
|
|
|
|
|
message: t('auth.register.confirm_password_placeholder'),
|
2026-01-16 22:04:12 +08:00
|
|
|
|
trigger: ['blur', 'change']
|
|
|
|
|
|
},
|
|
|
|
|
|
{
|
|
|
|
|
|
validator(rule: any, value: string, callback: any) {
|
|
|
|
|
|
return value === formData.password
|
|
|
|
|
|
},
|
2026-04-07 11:01:11 +08:00
|
|
|
|
message: t('auth.register.confirm_password_error'),
|
2026-01-16 22:04:12 +08:00
|
|
|
|
trigger: ['change', 'blur'],
|
|
|
|
|
|
}
|
|
|
|
|
|
],
|
|
|
|
|
|
mobile: [
|
|
|
|
|
|
{
|
|
|
|
|
|
type: 'string',
|
|
|
|
|
|
required: currentType.value === 'mobile' || configStore.login.is_bind_mobile,
|
2026-04-07 11:01:11 +08:00
|
|
|
|
message: t('auth.register.mobile_placeholder'),
|
2026-01-16 22:04:12 +08:00
|
|
|
|
trigger: ['blur', 'change'],
|
|
|
|
|
|
},
|
|
|
|
|
|
{
|
|
|
|
|
|
validator(rule: any, value: string, callback: any) {
|
|
|
|
|
|
if (currentType.value !== 'mobile' && !configStore.login.is_bind_mobile) return true
|
|
|
|
|
|
return validate.mobile(value)
|
|
|
|
|
|
},
|
2026-04-07 11:01:11 +08:00
|
|
|
|
message: t('auth.register.mobile_error'),
|
2026-01-16 22:04:12 +08:00
|
|
|
|
trigger: ['change', 'blur'],
|
|
|
|
|
|
}
|
|
|
|
|
|
],
|
2026-04-07 11:01:11 +08:00
|
|
|
|
mobile_code: {
|
2026-01-16 22:04:12 +08:00
|
|
|
|
type: 'string',
|
|
|
|
|
|
required: currentType.value === 'mobile' || configStore.login.is_bind_mobile,
|
2026-04-07 11:01:11 +08:00
|
|
|
|
message: t('auth.register.code_placeholder'),
|
2026-01-16 22:04:12 +08:00
|
|
|
|
trigger: ['blur', 'change']
|
|
|
|
|
|
},
|
2026-04-07 11:01:11 +08:00
|
|
|
|
captcha_code: {
|
2026-01-16 22:04:12 +08:00
|
|
|
|
type: 'string',
|
|
|
|
|
|
required: currentType.value === 'username',
|
2026-04-07 11:01:11 +08:00
|
|
|
|
message: t('auth.register.captcha_placeholder'),
|
2026-01-16 22:04:12 +08:00
|
|
|
|
trigger: ['blur', 'change'],
|
|
|
|
|
|
}
|
|
|
|
|
|
}))
|
|
|
|
|
|
|
|
|
|
|
|
// 协议同意状态
|
|
|
|
|
|
const isAgreeChecked = ref(false)
|
|
|
|
|
|
const toggleAgreement = () => {
|
|
|
|
|
|
isAgreeChecked.value = !isAgreeChecked.value
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 注册处理
|
|
|
|
|
|
const handleRegister = async () => {
|
|
|
|
|
|
await formRef.value?.validate(async (valid) => {
|
|
|
|
|
|
if (!valid) return
|
|
|
|
|
|
|
|
|
|
|
|
// 检查协议同意
|
|
|
|
|
|
if (configStore.login.agreement_show && !isAgreeChecked.value) {
|
2026-04-07 11:01:11 +08:00
|
|
|
|
ElMessage.error(t('auth.register.is_agree_tips'))
|
2026-01-16 22:04:12 +08:00
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (isLoading.value) return
|
|
|
|
|
|
isLoading.value = true
|
|
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
|
const registerApi = currentType.value === 'username' ? registerUser : registerWithMobile
|
2026-04-07 11:01:11 +08:00
|
|
|
|
const data = await registerApi(formData) as any
|
|
|
|
|
|
memberStore.setToken(data.access_token, data.refresh_token || '')
|
2026-01-16 22:04:12 +08:00
|
|
|
|
memberStore.logClose()
|
|
|
|
|
|
} catch (error) {
|
|
|
|
|
|
isLoading.value = false
|
|
|
|
|
|
captcha.refresh()
|
|
|
|
|
|
}
|
|
|
|
|
|
})
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 验证码功能
|
|
|
|
|
|
const captcha = useCaptcha(formData)
|
|
|
|
|
|
captcha.refresh()
|
|
|
|
|
|
|
|
|
|
|
|
// 短信验证码
|
|
|
|
|
|
const smsCodeRef = ref<AnyObject | null>(null)
|
|
|
|
|
|
const sendSmsCode = async () => {
|
|
|
|
|
|
await formRef.value?.validateField('mobile', (valid) => {
|
|
|
|
|
|
if (valid) {
|
|
|
|
|
|
smsCodeRef.value?.send()
|
|
|
|
|
|
}
|
|
|
|
|
|
})
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 切换到登录
|
|
|
|
|
|
const emit = defineEmits(['typeChange'])
|
|
|
|
|
|
const switchToLogin = () => {
|
|
|
|
|
|
emit('typeChange', 'login')
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 输入框状态
|
|
|
|
|
|
const realNameInput = ref(true)
|
|
|
|
|
|
</script>
|
|
|
|
|
|
|
|
|
|
|
|
<style lang="scss" scoped>
|
|
|
|
|
|
.register-container {
|
|
|
|
|
|
width: 100%;
|
|
|
|
|
|
|
|
|
|
|
|
&.dark-mode {
|
|
|
|
|
|
background: var(--el-bg-color);
|
|
|
|
|
|
color: var(--el-text-color-primary);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.register-form-container {
|
|
|
|
|
|
background: var(--el-bg-color);
|
|
|
|
|
|
width: 100%;
|
|
|
|
|
|
padding: 60px 30px;
|
|
|
|
|
|
border-radius: var(--rounded-big);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.register-type-tabs {
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
align-items: flex-end;
|
|
|
|
|
|
justify-content: center;
|
|
|
|
|
|
margin-bottom: 30px;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.register-type-tab {
|
|
|
|
|
|
font-size: 18px;
|
|
|
|
|
|
cursor: pointer;
|
|
|
|
|
|
color: var(--el-text-color-secondary);
|
|
|
|
|
|
line-height: 24px;
|
|
|
|
|
|
font-family: 'OppoSans-R';
|
|
|
|
|
|
|
|
|
|
|
|
&--active {
|
|
|
|
|
|
color: var(--el-text-color-primary);
|
|
|
|
|
|
font-weight: 600;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
&--margin {
|
|
|
|
|
|
margin-right: 70px;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.register-form {
|
|
|
|
|
|
width: 100%;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.form-item {
|
|
|
|
|
|
margin-bottom: 20px;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.input-container {
|
|
|
|
|
|
flex: 1;
|
|
|
|
|
|
height: 50px;
|
|
|
|
|
|
border: 1px solid var(--el-border-color);
|
|
|
|
|
|
border-radius: 8px;
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
background: var(--el-bg-color);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.register-input {
|
|
|
|
|
|
width: 100%;
|
|
|
|
|
|
border: none;
|
|
|
|
|
|
|
|
|
|
|
|
:deep(.el-input__wrapper) {
|
|
|
|
|
|
box-shadow: none;
|
|
|
|
|
|
background: transparent;
|
|
|
|
|
|
|
|
|
|
|
|
.el-input__inner {
|
|
|
|
|
|
color: var(--el-text-color-primary);
|
|
|
|
|
|
|
|
|
|
|
|
&::placeholder {
|
|
|
|
|
|
color: var(--el-text-color-placeholder);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.input-prefix-icon {
|
|
|
|
|
|
margin-right: 14px;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.captcha-image-container {
|
|
|
|
|
|
padding: 0;
|
|
|
|
|
|
line-height: 1;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.captcha-image {
|
|
|
|
|
|
height: 30px;
|
|
|
|
|
|
cursor: pointer;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.form-actions {
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
justify-content: flex-end;
|
|
|
|
|
|
margin-bottom: 20px;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.login-link {
|
|
|
|
|
|
font-size: 12px;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.register-button-container {
|
|
|
|
|
|
margin-top: 20px;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.register-button {
|
|
|
|
|
|
width: 100%;
|
|
|
|
|
|
height: 50px;
|
|
|
|
|
|
border-radius: 8px;
|
|
|
|
|
|
font-family: 'OppoSans-M';
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.agreement-container {
|
|
|
|
|
|
font-size: 12px;
|
|
|
|
|
|
line-height: 24px;
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
width: 100%;
|
|
|
|
|
|
margin-top: 20px;
|
|
|
|
|
|
color: var(--el-text-color-secondary);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.agreement-checkbox {
|
|
|
|
|
|
color: var(--el-color-primary);
|
|
|
|
|
|
margin-right: 5px;
|
|
|
|
|
|
cursor: pointer;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.agreement-link {
|
|
|
|
|
|
color: var(--el-color-primary);
|
|
|
|
|
|
margin: 0 4px;
|
|
|
|
|
|
|
|
|
|
|
|
&:hover {
|
|
|
|
|
|
color: var(--el-color-primary-light-3);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
</style>
|