651 lines
15 KiB
Vue
651 lines
15 KiB
Vue
<template>
|
||
<view class="register-container">
|
||
<!-- 语言选择文字按钮 -->
|
||
<view class="language-text-btn" @click="showLanguageModal = true">
|
||
<text class="lang-text">{{ $t('login.changelanguage') }}</text>
|
||
<i class="iconfont icon-arrow-down"></i>
|
||
</view>
|
||
|
||
<!-- 语言选择弹窗 -->
|
||
<view class="lang-modal" v-if="showLanguageModal">
|
||
<view class="modal-mask" @click="showLanguageModal = false"></view>
|
||
<view class="modal-content">
|
||
<view class="modal-title">{{ $t('register.chooseLanguage') }}</view>
|
||
<view class="lang-list">
|
||
<view
|
||
class="lang-item"
|
||
@click="changeLanguage('en')"
|
||
:class="{ 'active': currentLang === 'en' }"
|
||
>
|
||
<text>English</text>
|
||
<i class="iconfont icon-check" v-if="currentLang === 'en'"></i>
|
||
</view>
|
||
<view
|
||
class="lang-item"
|
||
@click="changeLanguage('zh')"
|
||
:class="{ 'active': currentLang === 'zh' }"
|
||
>
|
||
<text>Chinese</text>
|
||
<i class="iconfont icon-check" v-if="currentLang === 'zh'"></i>
|
||
</view>
|
||
</view>
|
||
<button class="modal-close" @click="showLanguageModal = false">
|
||
{{ $t('register.languagechangeclose') }}
|
||
</button>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 顶部标题 -->
|
||
<view class="register-header">
|
||
<text class="app-title">{{ $t('register.appTitle') }}</text>
|
||
<text class="register-desc">{{ $t('register.registerDesc') }}</text>
|
||
</view>
|
||
|
||
<!-- 注册表单 -->
|
||
<view class="register-form">
|
||
<!-- 身份证后六位输入 -->
|
||
<view class="form-group">
|
||
<text class="input-label">{{ $t('register.idCardLabel') }}</text>
|
||
<view class="input-wrapper">
|
||
<i class="iconfont icon-idcard"></i>
|
||
<input
|
||
type="text"
|
||
v-model="idCardLast6"
|
||
:placeholder="$t('register.idCardPlaceholder')"
|
||
class="input-field"
|
||
maxlength="6"
|
||
@input="handleIdCardInput"
|
||
/>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 密码输入 -->
|
||
<view class="form-group">
|
||
<text class="input-label">{{ $t('register.passwordLabel') }}</text>
|
||
<view class="input-wrapper">
|
||
<i class="iconfont icon-lock"></i>
|
||
<input
|
||
:type="showPassword ? 'text' : 'password'"
|
||
v-model="password"
|
||
:placeholder="$t('register.passwordPlaceholder')"
|
||
class="input-field"
|
||
@input="validatePassword"
|
||
/>
|
||
<view class="toggle-btn-wrapper">
|
||
<view
|
||
class="toggle-btn"
|
||
@click.stop="togglePasswordVisibility"
|
||
>
|
||
<i class="iconfont" :class="showPassword ? 'icon-eye' : 'icon-eye-close'"></i>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
<text class="password-tip" v-if="showPasswordTip">{{ $t('register.passwordTip') }}</text>
|
||
</view>
|
||
|
||
<!-- 确认密码输入 -->
|
||
<view class="form-group">
|
||
<text class="input-label">{{ $t('register.confirmPasswordLabel') }}</text>
|
||
<view class="input-wrapper">
|
||
<i class="iconfont icon-lock"></i>
|
||
<input
|
||
:type="showConfirmPassword ? 'text' : 'password'"
|
||
v-model="confirmPassword"
|
||
:placeholder="$t('register.confirmPasswordPlaceholder')"
|
||
class="input-field"
|
||
@input="validateConfirmPassword"
|
||
/>
|
||
<view class="toggle-btn-wrapper">
|
||
<view
|
||
class="toggle-btn"
|
||
@click.stop="toggleConfirmPasswordVisibility"
|
||
>
|
||
<i class="iconfont" :class="showConfirmPassword ? 'icon-eye' : 'icon-eye-close'"></i>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 邀请码输入 -->
|
||
<view class="form-group">
|
||
<text class="input-label">{{ $t('register.inviteCodeLabel') }}</text>
|
||
<view class="input-wrapper">
|
||
<i class="iconfont icon-invite"></i>
|
||
<input
|
||
type="text"
|
||
v-model="inviteCode"
|
||
:placeholder="$t('register.inviteCodePlaceholder')"
|
||
class="input-field"
|
||
/>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 邮箱输入 -->
|
||
<view class="form-group">
|
||
<text class="input-label">{{ $t('register.emailLabel') }}</text>
|
||
<view class="input-wrapper">
|
||
<i class="iconfont icon-email"></i>
|
||
<input
|
||
type="text"
|
||
v-model="email"
|
||
:placeholder="$t('register.emailPlaceholder')"
|
||
class="input-field"
|
||
@input="validateEmail"
|
||
/>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 错误提示 -->
|
||
<view class="error-message" v-if="showError">
|
||
{{ errorMessage }}
|
||
</view>
|
||
|
||
<!-- 协议同意勾选框 -->
|
||
<view class="agreement-check">
|
||
<checkbox
|
||
v-model="agreeTerms"
|
||
class="agree-checkbox"
|
||
color="#007aff"
|
||
/>
|
||
<text class="agree-text">
|
||
{{ $t('register.agreeTermsPrefix') }}
|
||
<text class="link" @click="openAgreement('terms')">{{ $t('register.userAgreement') }}</text>
|
||
{{ $t('register.and') }}
|
||
<text class="link" @click="openAgreement('privacy')">{{ $t('register.privacyPolicy') }}</text>
|
||
</text>
|
||
</view>
|
||
|
||
<!-- 注册按钮 -->
|
||
<!-- <button
|
||
class="register-btn"
|
||
@click="handleRegister"
|
||
:disabled="isLoading || !agreeTerms || formInvalid"
|
||
>
|
||
<template v-if="isLoading">
|
||
<i class="iconfont icon-loading loading-icon"></i>
|
||
{{ $t('register.registerProcessing') }}
|
||
</template>
|
||
<template v-else>
|
||
{{ $t('register.registerButton') }}
|
||
</template>
|
||
</button> -->
|
||
<button
|
||
class="register-btn"
|
||
@click="handleRegister"
|
||
|
||
>
|
||
<template v-if="isLoading">
|
||
<i class="iconfont icon-loading loading-icon"></i>
|
||
{{ $t('register.registerProcessing') }}
|
||
</template>
|
||
<template v-else>
|
||
{{ $t('register.registerButton') }}
|
||
</template>
|
||
</button>
|
||
|
||
<!-- 登录链接 -->
|
||
<view class="login-link-section">
|
||
<text class="login-text">
|
||
{{ $t('register.hasAccount') }}
|
||
<text class="login-link" @click="navigateToLogin">
|
||
{{ $t('register.loginNow') }}
|
||
</text>
|
||
</text>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
</template>
|
||
|
||
<script>
|
||
export default {
|
||
data() {
|
||
return {
|
||
// 表单数据
|
||
idCardLast6: '',
|
||
password: '',
|
||
confirmPassword: '',
|
||
inviteCode: '',
|
||
email: '',
|
||
|
||
// 状态控制
|
||
showError: false,
|
||
errorMessage: '',
|
||
isLoading: false,
|
||
currentLang: 'en',
|
||
showLanguageModal: false,
|
||
showPassword: false,
|
||
showConfirmPassword: false,
|
||
showPasswordTip: false,
|
||
formInvalid: true,
|
||
agreeTerms: false
|
||
}
|
||
},
|
||
onLoad() {
|
||
this.currentLang = this.$i18n.locale
|
||
},
|
||
methods: {
|
||
// 处理身份证输入(自动转换小写x为大写X)
|
||
handleIdCardInput() {
|
||
if (this.idCardLast6 && this.idCardLast6.length > 0) {
|
||
const lastChar = this.idCardLast6.slice(-1)
|
||
if (lastChar === 'x') {
|
||
this.idCardLast6 = this.idCardLast6.slice(0, -1) + 'X'
|
||
}
|
||
}
|
||
this.validateIdCard()
|
||
},
|
||
|
||
// 验证身份证后六位(最后一位支持大写X)
|
||
validateIdCard() {
|
||
if (!this.idCardLast6 || this.idCardLast6.trim() === '') {
|
||
this.setError(this.$t('register.errors.emptyIdCard'))
|
||
return false
|
||
}
|
||
|
||
const idReg = /^\d{5}[0-9X]$/
|
||
if (!idReg.test(this.idCardLast6)) {
|
||
this.setError(this.$t('register.errors.invalidIdCard'))
|
||
return false
|
||
}
|
||
|
||
this.clearError()
|
||
this.checkFormValidity()
|
||
return true
|
||
},
|
||
|
||
togglePasswordVisibility() {
|
||
this.showPassword = !this.showPassword
|
||
this.showPasswordTip = true
|
||
},
|
||
|
||
toggleConfirmPasswordVisibility() {
|
||
this.showConfirmPassword = !this.showConfirmPassword
|
||
},
|
||
|
||
changeLanguage(lang) {
|
||
this.currentLang = lang
|
||
this.$i18n.locale = lang
|
||
this.showError = false
|
||
this.showLanguageModal = false
|
||
},
|
||
|
||
validatePassword() {
|
||
const pwdReg = /^(?=.*[A-Za-z])(?=.*\d).{8,}$/
|
||
if (this.password && !pwdReg.test(this.password)) {
|
||
this.setError(this.$t('register.errors.invalidPassword'))
|
||
return false
|
||
}
|
||
this.validateConfirmPassword()
|
||
this.checkFormValidity()
|
||
return true
|
||
},
|
||
|
||
validateConfirmPassword() {
|
||
if (this.confirmPassword && this.confirmPassword !== this.password) {
|
||
this.setError(this.$t('register.errors.passwordMismatch'))
|
||
return false
|
||
}
|
||
this.checkFormValidity()
|
||
return true
|
||
},
|
||
|
||
validateEmail() {
|
||
const emailReg = /^[^\s@]+@[^\s@]+\.[^\s@]+$/
|
||
if (this.email && !emailReg.test(this.email)) {
|
||
this.setError(this.$t('register.errors.invalidEmail'))
|
||
return false
|
||
}
|
||
this.checkFormValidity()
|
||
return true
|
||
},
|
||
|
||
checkFormValidity() {
|
||
const idReg = /^\d{5}[0-9X]$/
|
||
const pwdReg = /^(?=.*[A-Za-z])(?=.*\d).{8,}$/
|
||
const emailReg = /^[^\s@]+@[^\s@]+\.[^\s@]+$/
|
||
|
||
const isIdValid = this.idCardLast6 && idReg.test(this.idCardLast6)
|
||
const isPwdValid = this.password && pwdReg.test(this.password)
|
||
const isConfirmValid = this.confirmPassword && this.confirmPassword === this.password
|
||
const isEmailValid = this.email && emailReg.test(this.email)
|
||
|
||
this.formInvalid = !(isIdValid && isPwdValid && isConfirmValid && isEmailValid)
|
||
},
|
||
|
||
setError(message) {
|
||
this.showError = true
|
||
this.errorMessage = message
|
||
},
|
||
|
||
clearError() {
|
||
this.showError = false
|
||
this.errorMessage = ''
|
||
},
|
||
|
||
handleRegister() {
|
||
if (!this.validateIdCard() || !this.validatePassword() || !this.validateEmail()) {
|
||
return
|
||
}
|
||
|
||
// if (!this.agreeTerms) {
|
||
// this.setError(this.$t('register.errors.agreeTermsFirst'))
|
||
// return
|
||
// }
|
||
|
||
this.isLoading = true
|
||
this.showError = false
|
||
|
||
setTimeout(() => {
|
||
uni.showToast({
|
||
title: this.$t('register.registerSuccess'),
|
||
icon: 'success',
|
||
duration: 2000
|
||
})
|
||
|
||
setTimeout(() => {
|
||
uni.navigateTo({ url: '/pages/login/login' })
|
||
}, 2000)
|
||
|
||
this.isLoading = false
|
||
}, 2000)
|
||
},
|
||
|
||
navigateToLogin() {
|
||
uni.navigateTo({ url: '/pages/login/login' })
|
||
},
|
||
|
||
openAgreement(type) {
|
||
const title = type === 'terms'
|
||
? this.$t('register.userAgreement')
|
||
: this.$t('register.privacyPolicy')
|
||
|
||
uni.showModal({
|
||
title,
|
||
content: this.$t(`register.${type}Content`),
|
||
showCancel: false,
|
||
confirmText: this.$t('register.confirm')
|
||
})
|
||
}
|
||
}
|
||
}
|
||
</script>
|
||
|
||
<style scoped>
|
||
.register-container {
|
||
display: flex;
|
||
flex-direction: column;
|
||
min-height: 100vh;
|
||
padding: 0 30rpx;
|
||
background-color: #f5f5f5;
|
||
position: relative;
|
||
}
|
||
|
||
.language-text-btn {
|
||
position: absolute;
|
||
top: 30rpx;
|
||
right: 30rpx;
|
||
z-index: 10;
|
||
display: flex;
|
||
align-items: center;
|
||
padding: 10rpx 20rpx;
|
||
color: #007aff; /* 使用主题色突出显示 */
|
||
font-size: 28rpx;
|
||
cursor: pointer; /* 显示指针光标表明可点击 */
|
||
}
|
||
|
||
.icon-arrow-down {
|
||
font-size: 24rpx;
|
||
margin-left: 8rpx;
|
||
}
|
||
|
||
/* 语言弹窗样式 */
|
||
.lang-modal {
|
||
position: fixed;
|
||
top: 0;
|
||
left: 0;
|
||
right: 0;
|
||
bottom: 0;
|
||
z-index: 999;
|
||
}
|
||
|
||
.modal-mask {
|
||
position: absolute;
|
||
top: 0;
|
||
left: 0;
|
||
right: 0;
|
||
bottom: 0;
|
||
background-color: rgba(0, 0, 0, 0.5);
|
||
}
|
||
|
||
.modal-content {
|
||
position: absolute;
|
||
bottom: 0;
|
||
left: 0;
|
||
right: 0;
|
||
background-color: #fff;
|
||
border-radius: 30rpx 30rpx 0 0;
|
||
padding: 30rpx;
|
||
}
|
||
|
||
.modal-title {
|
||
text-align: center;
|
||
font-size: 34rpx;
|
||
font-weight: bold;
|
||
color: #333;
|
||
padding: 20rpx 0;
|
||
border-bottom: 1px solid #eee;
|
||
margin-bottom: 20rpx;
|
||
}
|
||
|
||
.lang-list {
|
||
margin-bottom: 30rpx;
|
||
}
|
||
|
||
.lang-item {
|
||
display: flex;
|
||
justify-content: space-between;
|
||
align-items: center;
|
||
padding: 25rpx 10rpx;
|
||
font-size: 30rpx;
|
||
border-bottom: 1px solid #f1f1f1;
|
||
}
|
||
|
||
.lang-item.active {
|
||
color: #007aff;
|
||
}
|
||
|
||
.icon-check {
|
||
font-size: 32rpx;
|
||
}
|
||
|
||
.modal-close {
|
||
width: 100%;
|
||
height: 90rpx;
|
||
line-height: 90rpx;
|
||
background-color: #007aff;
|
||
color: #fff;
|
||
font-size: 32rpx;
|
||
border-radius: 45rpx;
|
||
}
|
||
|
||
.register-header {
|
||
padding-top: 120rpx;
|
||
padding-bottom: 60rpx;
|
||
text-align: center;
|
||
}
|
||
|
||
.app-title {
|
||
font-size: 48rpx;
|
||
font-weight: bold;
|
||
color: #333;
|
||
display: block;
|
||
margin-bottom: 20rpx;
|
||
}
|
||
|
||
.register-desc {
|
||
font-size: 28rpx;
|
||
color: #666;
|
||
}
|
||
|
||
.register-form {
|
||
flex: 1;
|
||
}
|
||
|
||
.form-group {
|
||
margin-bottom: 30rpx;
|
||
}
|
||
|
||
.input-label {
|
||
display: block;
|
||
font-size: 26rpx;
|
||
color: #000000;
|
||
margin-bottom: 15rpx;
|
||
padding-left: 30rpx;
|
||
}
|
||
|
||
.input-wrapper {
|
||
display: flex;
|
||
align-items: center;
|
||
background-color: #fff;
|
||
border-radius: 80rpx;
|
||
padding: 0 30rpx;
|
||
height: 90rpx;
|
||
box-shadow: 0 2rpx 10rpx rgba(0, 0, 0, 0.05);
|
||
}
|
||
|
||
.iconfont {
|
||
font-size: 36rpx;
|
||
color: #999;
|
||
margin-right: 20rpx;
|
||
}
|
||
|
||
.input-field {
|
||
flex: 1;
|
||
height: 100%;
|
||
font-size: 30rpx;
|
||
color: #333;
|
||
}
|
||
|
||
.input-field::placeholder {
|
||
color: #ccc;
|
||
}
|
||
|
||
/* 密码提示 */
|
||
.password-tip {
|
||
display: block;
|
||
font-size: 22rpx;
|
||
color: #999;
|
||
padding-left: 30rpx;
|
||
margin-top: 10rpx;
|
||
line-height: 1.4;
|
||
}
|
||
|
||
.toggle-btn {
|
||
width: 50rpx;
|
||
height: 50rpx;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
background: transparent;
|
||
padding: 0;
|
||
margin: 0;
|
||
border: none;
|
||
}
|
||
|
||
.toggle-btn .iconfont {
|
||
margin: 0;
|
||
color: #999;
|
||
font-size: 34rpx;
|
||
}
|
||
|
||
/* 邀请码提示 */
|
||
.invite-tip {
|
||
display: block;
|
||
font-size: 22rpx;
|
||
color: #999;
|
||
padding-left: 30rpx;
|
||
margin-top: 10rpx;
|
||
}
|
||
|
||
.error-message {
|
||
color: #ff4d4f;
|
||
font-size: 26rpx;
|
||
padding: 15rpx 30rpx;
|
||
height: 60rpx;
|
||
}
|
||
|
||
/* 协议勾选区域 */
|
||
.agreement-check {
|
||
display: flex;
|
||
align-items: flex-start;
|
||
padding: 20rpx 30rpx;
|
||
margin-bottom: 10rpx;
|
||
}
|
||
|
||
.agree-checkbox {
|
||
transform: scale(0.8);
|
||
margin-right: 15rpx;
|
||
margin-top: 5rpx;
|
||
}
|
||
|
||
.agree-text {
|
||
font-size: 24rpx;
|
||
color: #666;
|
||
line-height: 1.4;
|
||
}
|
||
|
||
.link {
|
||
color: #007aff;
|
||
text-decoration: underline;
|
||
}
|
||
|
||
/* 注册按钮 */
|
||
.register-btn {
|
||
width: 100%;
|
||
height: 90rpx;
|
||
z-index: 100;
|
||
line-height: 90rpx;
|
||
border-radius: 80rpx;
|
||
background-color: #007aff !important;
|
||
color: #ffffff !important;
|
||
font-size: 32rpx;
|
||
font-weight: 500;
|
||
margin-top: 20rpx;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
}
|
||
|
||
.register-btn:disabled {
|
||
background-color: #b3d4fc;
|
||
}
|
||
|
||
/* 登录链接区域 */
|
||
.login-link-section {
|
||
text-align: center;
|
||
margin-top: 30rpx;
|
||
margin-bottom: 50rpx;
|
||
}
|
||
|
||
.login-text {
|
||
font-size: 28rpx;
|
||
color: #666;
|
||
}
|
||
|
||
.login-link {
|
||
color: #007aff;
|
||
margin-left: 5rpx;
|
||
text-decoration: underline;
|
||
}
|
||
|
||
/* 加载动画 */
|
||
.loading-icon {
|
||
animation: spin 1s linear infinite;
|
||
margin-right: 10rpx;
|
||
}
|
||
|
||
@keyframes spin {
|
||
from { transform: rotate(0deg); }
|
||
to { transform: rotate(360deg); }
|
||
}
|
||
</style> |