Files
Market_Uniapp/src/pages/register/register.vue
2025-08-06 14:15:21 +08:00

651 lines
15 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>
<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>