Files
martial-mini/pages/register/register.vue
2025-12-12 17:29:38 +08:00

818 lines
17 KiB
Vue
Raw Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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-page">
<!-- 背景装饰 -->
<view class="bg-decoration">
<view class="circle circle-1"></view>
<view class="circle circle-2"></view>
<view class="circle circle-3"></view>
</view>
<!-- 顶部Logo区域 -->
<view class="register-header">
<view class="logo-container">
<image class="logo" src="/static/logo.png" mode="aspectFit"></image>
</view>
<text class="title">用户注册</text>
<text class="subtitle">CREATE YOUR ACCOUNT</text>
</view>
<!-- 注册表单 -->
<view class="register-form">
<view class="form-title">创建账号</view>
<view class="form-item">
<view class="input-wrapper">
<view class="input-icon">👤</view>
<input
class="form-input"
v-model="form.account"
placeholder="请输入账号4-20位字母或数字"
placeholder-class="placeholder"
/>
</view>
</view>
<view class="form-item">
<view class="input-wrapper">
<view class="input-icon">📱</view>
<input
class="form-input"
v-model="form.phone"
type="number"
maxlength="11"
placeholder="请输入手机号"
placeholder-class="placeholder"
/>
</view>
</view>
<view class="form-item">
<view class="code-input-wrapper">
<view class="input-wrapper code-input">
<view class="input-icon">🔢</view>
<input
class="form-input"
v-model="form.code"
type="number"
maxlength="6"
placeholder="请输入验证码"
placeholder-class="placeholder"
/>
</view>
<view
class="get-code-btn"
:class="{ disabled: countdown > 0 }"
@click="handleGetCode"
>
{{ countdown > 0 ? `${countdown}s` : '获取验证码' }}
</view>
</view>
<view class="test-tip">💡 测试提示可使用万能验证码 888888</view>
</view>
<view class="form-item">
<view class="input-wrapper">
<view class="input-icon"></view>
<input
class="form-input"
v-model="form.realName"
placeholder="请输入真实姓名"
placeholder-class="placeholder"
/>
</view>
</view>
<view class="form-item">
<view class="gender-label">性别</view>
<view class="gender-selector">
<view
class="gender-option"
:class="{ active: form.sex === 1 }"
@click="form.sex = 1"
>
<text class="gender-icon">👨</text>
<text></text>
</view>
<view
class="gender-option"
:class="{ active: form.sex === 2 }"
@click="form.sex = 2"
>
<text class="gender-icon">👩</text>
<text></text>
</view>
</view>
</view>
<view class="form-item">
<view class="input-wrapper">
<view class="input-icon">🔒</view>
<input
class="form-input"
v-model="form.password"
:password="!showPassword"
placeholder="请输入密码6-20位"
placeholder-class="placeholder"
/>
<view class="eye-icon" @click="showPassword = !showPassword">
<text>{{ showPassword ? '👁️' : '👁️‍🗨️' }}</text>
</view>
</view>
</view>
<view class="form-item">
<view class="input-wrapper">
<view class="input-icon">🔐</view>
<input
class="form-input"
v-model="form.confirmPassword"
:password="!showConfirmPassword"
placeholder="请再次输入密码"
placeholder-class="placeholder"
/>
<view class="eye-icon" @click="showConfirmPassword = !showConfirmPassword">
<text>{{ showConfirmPassword ? '👁️' : '👁️‍🗨️' }}</text>
</view>
</view>
</view>
<view class="agreement-wrapper">
<label class="checkbox-label" @click="agreeTerms = !agreeTerms">
<view :class="['checkbox', agreeTerms ? 'checked' : '']">
<text v-if="agreeTerms" class="check-icon"></text>
</view>
<text class="agreement-text">
我已阅读并同意
<text class="link-text" @click.stop="showAgreement">用户协议</text>
<text class="link-text" @click.stop="showPrivacy">隐私政策</text>
</text>
</label>
</view>
<button class="register-btn" @click="handleRegister" :loading="loading" :disabled="loading">
<text class="btn-text">{{ loading ? '注册中...' : '立即注册' }}</text>
</button>
<view class="login-link">
已有账号<text class="link-text" @click="goToLogin">立即登录</text>
</view>
</view>
<!-- 底部装饰 -->
<view class="footer-decoration">
<view class="wave"></view>
</view>
</view>
</template>
<script>
import userAPI from '@/api/user.js'
export default {
data() {
return {
form: {
account: '',
phone: '',
code: '',
realName: '',
sex: 1,
password: '',
confirmPassword: ''
},
showPassword: false,
showConfirmPassword: false,
agreeTerms: false,
loading: false,
countdown: 0,
timer: null
}
},
onUnload() {
if (this.timer) {
clearInterval(this.timer)
}
},
methods: {
/**
* 获取验证码
*/
async handleGetCode() {
if (this.countdown > 0) return
// 验证手机号
if (!this.form.phone) {
uni.showToast({
title: '请输入手机号',
icon: 'none'
})
return
}
if (!/^1[3-9]\d{9}$/.test(this.form.phone)) {
uni.showToast({
title: '请输入正确的手机号',
icon: 'none'
})
return
}
try {
await userAPI.getCaptcha(this.form.phone)
uni.showToast({
title: '验证码已发送',
icon: 'success'
})
// 开始倒计时
this.countdown = 60
this.timer = setInterval(() => {
this.countdown--
if (this.countdown <= 0) {
clearInterval(this.timer)
this.timer = null
}
}, 1000)
} catch (error) {
console.error('获取验证码失败:', error)
uni.showToast({
title: error.message || '获取验证码失败',
icon: 'none'
})
}
},
/**
* 表单验证
*/
validateForm() {
if (!this.form.account) {
uni.showToast({
title: '请输入账号',
icon: 'none'
})
return false
}
if (!/^[a-zA-Z0-9]{4,20}$/.test(this.form.account)) {
uni.showToast({
title: '账号为4-20位字母或数字',
icon: 'none'
})
return false
}
if (!this.form.phone) {
uni.showToast({
title: '请输入手机号',
icon: 'none'
})
return false
}
if (!/^1[3-9]\d{9}$/.test(this.form.phone)) {
uni.showToast({
title: '请输入正确的手机号',
icon: 'none'
})
return false
}
if (!this.form.code) {
uni.showToast({
title: '请输入验证码',
icon: 'none'
})
return false
}
// 测试用万能验证码888888
// 如果不是万能验证码则需要验证码长度为6位
if (this.form.code !== '888888' && this.form.code.length !== 6) {
uni.showToast({
title: '请输入6位验证码',
icon: 'none'
})
return false
}
if (!this.form.realName) {
uni.showToast({
title: '请输入真实姓名',
icon: 'none'
})
return false
}
if (!/^[\u4e00-\u9fa5]{2,10}$/.test(this.form.realName)) {
uni.showToast({
title: '请输入正确的姓名2-10个汉字',
icon: 'none'
})
return false
}
if (!this.form.password) {
uni.showToast({
title: '请输入密码',
icon: 'none'
})
return false
}
if (this.form.password.length < 6 || this.form.password.length > 20) {
uni.showToast({
title: '密码长度为6-20位',
icon: 'none'
})
return false
}
if (!this.form.confirmPassword) {
uni.showToast({
title: '请确认密码',
icon: 'none'
})
return false
}
if (this.form.password !== this.form.confirmPassword) {
uni.showToast({
title: '两次密码输入不一致',
icon: 'none'
})
return false
}
if (!this.agreeTerms) {
uni.showToast({
title: '请阅读并同意用户协议',
icon: 'none'
})
return false
}
return true
},
/**
* 注册
*/
async handleRegister() {
if (!this.validateForm()) return
this.loading = true
try {
await userAPI.register({
account: this.form.account,
phone: this.form.phone,
code: this.form.code,
realName: this.form.realName,
sex: this.form.sex,
password: this.form.password
})
uni.showToast({
title: '注册成功',
icon: 'success'
})
// 延迟跳转到登录页
setTimeout(() => {
uni.navigateBack()
}, 1500)
} catch (error) {
console.error('注册失败:', error)
uni.showToast({
title: error.message || '注册失败',
icon: 'none'
})
} finally {
this.loading = false
}
},
/**
* 跳转到登录页
*/
goToLogin() {
uni.navigateBack()
},
/**
* 显示用户协议
*/
showAgreement() {
uni.showToast({
title: '用户协议',
icon: 'none'
})
// TODO: 跳转到用户协议页面
},
/**
* 显示隐私政策
*/
showPrivacy() {
uni.showToast({
title: '隐私政策',
icon: 'none'
})
// TODO: 跳转到隐私政策页面
}
}
}
</script>
<style lang="scss" scoped>
.register-page {
min-height: 100vh;
background: linear-gradient(135deg, #C93639 0%, #A82E31 50%, #8B1F22 100%);
padding: 0;
position: relative;
overflow: hidden;
}
/* 背景装饰圆圈 */
.bg-decoration {
position: absolute;
width: 100%;
height: 100%;
top: 0;
left: 0;
overflow: hidden;
z-index: 0;
}
.circle {
position: absolute;
border-radius: 50%;
background: rgba(255, 255, 255, 0.08);
animation: float 6s ease-in-out infinite;
}
.circle-1 {
width: 300rpx;
height: 300rpx;
top: -100rpx;
right: -50rpx;
animation-delay: 0s;
}
.circle-2 {
width: 200rpx;
height: 200rpx;
bottom: 100rpx;
left: -50rpx;
animation-delay: 2s;
}
.circle-3 {
width: 150rpx;
height: 150rpx;
top: 30%;
right: 50rpx;
animation-delay: 4s;
}
@keyframes float {
0%, 100% {
transform: translateY(0) scale(1);
}
50% {
transform: translateY(-30rpx) scale(1.05);
}
}
/* 顶部Logo区域 */
.register-header {
text-align: center;
padding: 80rpx 60rpx 60rpx;
position: relative;
z-index: 1;
}
.logo-container {
width: 160rpx;
height: 160rpx;
margin: 0 auto 30rpx;
background: rgba(255, 255, 255, 0.15);
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
backdrop-filter: blur(10rpx);
box-shadow: 0 8rpx 32rpx rgba(0, 0, 0, 0.1);
}
.logo {
width: 120rpx;
height: 120rpx;
}
.title {
font-size: 44rpx;
font-weight: bold;
color: #fff;
margin-bottom: 12rpx;
letter-spacing: 4rpx;
text-shadow: 0 4rpx 12rpx rgba(0, 0, 0, 0.15);
}
.subtitle {
font-size: 22rpx;
color: rgba(255, 255, 255, 0.85);
letter-spacing: 2rpx;
font-weight: 300;
}
/* 注册表单 */
.register-form {
background: #fff;
border-radius: 40rpx 40rpx 0 0;
padding: 50rpx 40rpx 80rpx;
position: relative;
z-index: 1;
box-shadow: 0 -8rpx 32rpx rgba(0, 0, 0, 0.08);
max-height: calc(100vh - 300rpx);
overflow-y: auto;
}
.form-title {
font-size: 32rpx;
font-weight: bold;
color: #333;
margin-bottom: 40rpx;
text-align: center;
}
.form-item {
margin-bottom: 28rpx;
}
.input-wrapper {
position: relative;
display: flex;
align-items: center;
background: #F7F8FA;
border-radius: 16rpx;
border: 2rpx solid transparent;
transition: all 0.3s ease;
overflow: hidden;
}
.input-wrapper:focus-within {
background: #fff;
border-color: #C93639;
box-shadow: 0 4rpx 16rpx rgba(201, 54, 57, 0.1);
}
.input-icon {
width: 70rpx;
display: flex;
align-items: center;
justify-content: center;
font-size: 32rpx;
flex-shrink: 0;
}
.form-input {
flex: 1;
height: 88rpx;
font-size: 26rpx;
color: #333;
background: transparent;
border: none;
padding-right: 30rpx;
}
.placeholder {
color: #999;
font-size: 26rpx;
}
.eye-icon {
width: 70rpx;
height: 88rpx;
display: flex;
align-items: center;
justify-content: center;
font-size: 32rpx;
flex-shrink: 0;
cursor: pointer;
transition: opacity 0.3s;
}
.eye-icon:active {
opacity: 0.6;
}
/* 验证码输入 */
.code-input-wrapper {
display: flex;
align-items: center;
gap: 12rpx;
}
.code-input {
flex: 1;
}
.get-code-btn {
height: 88rpx;
padding: 0 24rpx;
background: linear-gradient(135deg, #C93639 0%, #A82E31 100%);
color: #fff;
border-radius: 16rpx;
display: flex;
align-items: center;
justify-content: center;
font-size: 24rpx;
white-space: nowrap;
transition: all 0.3s;
box-shadow: 0 4rpx 12rpx rgba(201, 54, 57, 0.2);
font-weight: bold;
}
.get-code-btn:active {
transform: scale(0.95);
}
.get-code-btn.disabled {
opacity: 0.6;
background: #999;
box-shadow: none;
}
.test-tip {
margin-top: 10rpx;
font-size: 22rpx;
color: #ff9800;
padding-left: 10rpx;
}
/* 性别选择 */
.gender-label {
font-size: 26rpx;
color: #666;
margin-bottom: 12rpx;
padding-left: 10rpx;
}
.gender-selector {
display: flex;
gap: 16rpx;
}
.gender-option {
flex: 1;
height: 88rpx;
background: #F7F8FA;
border-radius: 16rpx;
border: 2rpx solid transparent;
display: flex;
align-items: center;
justify-content: center;
gap: 10rpx;
font-size: 26rpx;
color: #666;
transition: all 0.3s;
}
.gender-option.active {
background: linear-gradient(135deg, #C93639 0%, #A82E31 100%);
color: #fff;
border-color: #C93639;
box-shadow: 0 4rpx 16rpx rgba(201, 54, 57, 0.2);
}
.gender-icon {
font-size: 32rpx;
}
/* 用户协议 */
.agreement-wrapper {
margin: 30rpx 0 40rpx;
}
.checkbox-label {
display: flex;
align-items: flex-start;
cursor: pointer;
}
.checkbox {
width: 32rpx;
height: 32rpx;
border: 2rpx solid #ddd;
border-radius: 8rpx;
display: flex;
align-items: center;
justify-content: center;
margin-right: 10rpx;
margin-top: 2rpx;
flex-shrink: 0;
transition: all 0.3s ease;
}
.checkbox.checked {
background: #C93639;
border-color: #C93639;
}
.check-icon {
color: #fff;
font-size: 20rpx;
font-weight: bold;
}
.agreement-text {
font-size: 24rpx;
color: #666;
flex: 1;
line-height: 1.6;
}
.link-text {
color: #C93639;
font-weight: bold;
cursor: pointer;
transition: opacity 0.3s;
}
.link-text:active {
opacity: 0.7;
}
/* 注册按钮 */
.register-btn {
width: 100%;
height: 92rpx;
background: linear-gradient(135deg, #C93639 0%, #A82E31 100%);
border-radius: 16rpx;
border: none;
box-shadow: 0 8rpx 24rpx rgba(201, 54, 57, 0.3);
transition: all 0.3s ease;
position: relative;
overflow: hidden;
}
.register-btn::before {
content: '';
position: absolute;
top: 0;
left: -100%;
width: 100%;
height: 100%;
background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.2), transparent);
transition: left 0.5s;
}
.register-btn:active {
transform: scale(0.98);
box-shadow: 0 4rpx 16rpx rgba(201, 54, 57, 0.2);
}
.register-btn:active::before {
left: 100%;
}
.btn-text {
font-size: 30rpx;
font-weight: bold;
color: #fff;
letter-spacing: 2rpx;
}
/* 登录链接 */
.login-link {
text-align: center;
margin-top: 40rpx;
font-size: 26rpx;
color: #666;
}
/* 底部装饰 */
.footer-decoration {
position: fixed;
bottom: 0;
left: 0;
width: 100%;
height: 100rpx;
z-index: 0;
pointer-events: none;
}
.wave {
position: absolute;
bottom: 0;
left: 0;
width: 100%;
height: 100%;
background: linear-gradient(180deg, transparent 0%, rgba(255, 255, 255, 0.05) 100%);
}
</style>