This commit is contained in:
2025-12-12 17:29:38 +08:00
parent 7d9ac4c8ca
commit 7807f4b3e4
14 changed files with 2476 additions and 51 deletions

817
pages/register/register.vue Normal file
View File

@@ -0,0 +1,817 @@
<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>