323 lines
7.2 KiB
Vue
323 lines
7.2 KiB
Vue
<template>
|
||
<view class="register-container">
|
||
<!-- 手机号 -->
|
||
<view class="input-row">
|
||
<text class="label">手机号:</text>
|
||
<input
|
||
type="number"
|
||
v-model="phone"
|
||
class="input-field"
|
||
placeholder="请输入手机号"
|
||
/>
|
||
</view>
|
||
|
||
<!-- 密码 -->
|
||
<view class="input-row">
|
||
<text class="label">密码:</text>
|
||
<input
|
||
type="password"
|
||
v-model="pwd"
|
||
class="input-field"
|
||
placeholder="请输入密码"
|
||
/>
|
||
</view>
|
||
|
||
<!-- 确认密码 -->
|
||
<view class="input-row">
|
||
<text class="label">确认密码:</text>
|
||
<input
|
||
type="password"
|
||
v-model="confirmPwd"
|
||
class="input-field"
|
||
placeholder="请再次输入密码"
|
||
/>
|
||
</view>
|
||
|
||
<!-- 手机验证码 -->
|
||
<view class="input-row">
|
||
<text class="label">验证码:</text>
|
||
<view class="captcha-container">
|
||
<input
|
||
type="text"
|
||
v-model="userCaptcha"
|
||
class="input-field captcha-input"
|
||
placeholder="请输入验证码"
|
||
/>
|
||
<button
|
||
class="captcha-btn"
|
||
:class="{ disabled: countdown > 0 || !isPhoneValidForCaptcha }"
|
||
@click="sendSmsCode"
|
||
:disabled="countdown > 0 || !isPhoneValidForCaptcha"
|
||
>
|
||
{{ countdown > 0 ? `${countdown}秒后重发` : '获取验证码' }}
|
||
</button>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 注册按钮 -->
|
||
<button
|
||
class="btn register-btn"
|
||
:class="{ disabled: !canRegister }"
|
||
@click="handleRegister"
|
||
:disabled="!canRegister"
|
||
>
|
||
注册
|
||
</button>
|
||
|
||
<!-- 返回登录 -->
|
||
<view class="back-link">
|
||
<text class="back-text" @click="goToLogin">已有账号?点击登录</text>
|
||
</view>
|
||
</view>
|
||
</template>
|
||
|
||
<script setup>
|
||
import { ref, computed, onUnmounted } from 'vue'
|
||
|
||
// 响应式数据
|
||
const account = ref('')
|
||
const pwd = ref('')
|
||
const confirmPwd = ref('')
|
||
const phone = ref('')
|
||
const userCaptcha = ref('') // 用户输入的验证码
|
||
const smsCode = ref('') // 服务器返回的验证码(模拟)
|
||
const countdown = ref(0) // 倒计时
|
||
let timer = null // 倒计时定时器
|
||
|
||
// 计算属性:判断手机号是否有效(用于获取验证码按钮)
|
||
const isPhoneValidForCaptcha = computed(() => {
|
||
const phoneRegex = /^1[3-9]\d{9}$/
|
||
return phone.value.trim() && phoneRegex.test(phone.value)
|
||
})
|
||
|
||
// 计算属性:判断是否可以注册
|
||
const canRegister = computed(() => {
|
||
// 检查账号、密码、确认密码、手机号和验证码是否都已填写
|
||
const isAccountValid = account.value.trim().length >= 6
|
||
const isPasswordValid = pwd.value.trim().length > 0
|
||
const isConfirmPasswordValid = confirmPwd.value === pwd.value
|
||
const isPhoneValid = isPhoneValidForCaptcha.value
|
||
const isCaptchaValid = userCaptcha.value.trim().length > 0
|
||
|
||
return isAccountValid &&
|
||
isPasswordValid &&
|
||
isConfirmPasswordValid &&
|
||
isPhoneValid &&
|
||
isCaptchaValid
|
||
})
|
||
|
||
// 发送短信验证码
|
||
const sendSmsCode = async () => {
|
||
// 验证手机号格式
|
||
const phoneRegex = /^1[3-9]\d{9}$/
|
||
if (!phone.value.trim()) {
|
||
uni.showToast({ title: '请输入手机号', icon: 'none' })
|
||
return
|
||
}
|
||
if (!phoneRegex.test(phone.value)) {
|
||
uni.showToast({ title: '手机号格式不正确', icon: 'none' })
|
||
return
|
||
}
|
||
|
||
// 模拟发送短信验证码(实际应用中应调用后端接口)
|
||
try {
|
||
uni.showLoading({ title: '发送中...' })
|
||
|
||
// 模拟API请求延迟
|
||
await new Promise(resolve => setTimeout(resolve, 1000))
|
||
|
||
// 生成6位数字验证码
|
||
const code = Math.floor(100000 + Math.random() * 900000).toString()
|
||
smsCode.value = code
|
||
|
||
console.log('发送的验证码:', code) // 仅用于调试
|
||
|
||
uni.hideLoading()
|
||
uni.showToast({ title: '验证码已发送', icon: 'success' })
|
||
|
||
// 开始倒计时
|
||
startCountdown()
|
||
} catch (error) {
|
||
uni.hideLoading()
|
||
uni.showToast({ title: '验证码发送失败', icon: 'none' })
|
||
}
|
||
}
|
||
|
||
// 开始倒计时
|
||
const startCountdown = () => {
|
||
countdown.value = 60
|
||
if (timer) clearInterval(timer)
|
||
|
||
timer = setInterval(() => {
|
||
countdown.value--
|
||
if (countdown.value <= 0) {
|
||
clearInterval(timer)
|
||
timer = null
|
||
}
|
||
}, 1000)
|
||
}
|
||
|
||
// 注册逻辑
|
||
const handleRegister = async () => {
|
||
if (!account.value.trim()) {
|
||
uni.showToast({ title: '请输入账号', icon: 'none' })
|
||
return
|
||
}
|
||
|
||
if (account.value.length < 6) {
|
||
uni.showToast({ title: '账号长度不能少于6位', icon: 'none' })
|
||
return
|
||
}
|
||
|
||
if (!pwd.value.trim()) {
|
||
uni.showToast({ title: '请输入密码', icon: 'none' })
|
||
return
|
||
}
|
||
|
||
if (pwd.value !== confirmPwd.value) {
|
||
uni.showToast({ title: '两次输入的密码不一致', icon: 'none' })
|
||
return
|
||
}
|
||
|
||
// 验证手机号
|
||
const phoneRegex = /^1[3-9]\d{9}$/
|
||
if (!phone.value.trim()) {
|
||
uni.showToast({ title: '请输入手机号', icon: 'none' })
|
||
return
|
||
}
|
||
if (!phoneRegex.test(phone.value)) {
|
||
uni.showToast({ title: '手机号格式不正确', icon: 'none' })
|
||
return
|
||
}
|
||
|
||
if (!userCaptcha.value.trim()) {
|
||
uni.showToast({ title: '请输入验证码', icon: 'none' })
|
||
return
|
||
}
|
||
|
||
// 验证验证码
|
||
if (userCaptcha.value !== smsCode.value) {
|
||
uni.showToast({ title: '验证码错误', icon: 'none' })
|
||
return
|
||
}
|
||
|
||
// 这里写你的实际注册请求逻辑(示例)
|
||
uni.showLoading({ title: '注册中...' })
|
||
try {
|
||
// 模拟API请求
|
||
await new Promise(resolve => setTimeout(resolve, 1000))
|
||
|
||
uni.hideLoading()
|
||
uni.showToast({ title: '注册成功', icon: 'success' })
|
||
|
||
// 注册成功后跳转到登录页
|
||
setTimeout(() => {
|
||
uni.navigateTo({
|
||
url: '/pages/login/index'
|
||
})
|
||
}, 500)
|
||
} catch (error) {
|
||
uni.hideLoading()
|
||
uni.showToast({ title: '注册失败', icon: 'none' })
|
||
}
|
||
}
|
||
|
||
// 返回登录页
|
||
const goToLogin = () => {
|
||
uni.navigateTo({ url: '/pages/login/index' })
|
||
}
|
||
|
||
// 在页面卸载时清除定时器
|
||
onUnmounted(() => {
|
||
if (timer) {
|
||
clearInterval(timer)
|
||
}
|
||
})
|
||
</script>
|
||
|
||
<style scoped>
|
||
.register-container {
|
||
padding: 40rpx;
|
||
background-color: #fff;
|
||
min-height: 100vh;
|
||
}
|
||
|
||
.input-row {
|
||
display: flex;
|
||
align-items: center;
|
||
margin-bottom: 40rpx;
|
||
}
|
||
|
||
.label {
|
||
width: 160rpx;
|
||
font-size: 32rpx;
|
||
color: #333;
|
||
}
|
||
|
||
.input-field {
|
||
flex: 1;
|
||
height: 80rpx;
|
||
border: 2px solid #ccc;
|
||
border-radius: 8rpx;
|
||
padding: 0 20rpx;
|
||
font-size: 28rpx;
|
||
-webkit-appearance: none;
|
||
outline: none;
|
||
}
|
||
|
||
.captcha-container {
|
||
display: flex;
|
||
align-items: center;
|
||
flex: 1;
|
||
}
|
||
|
||
.captcha-input {
|
||
flex: 1;
|
||
margin-right: 10rpx;
|
||
}
|
||
|
||
.captcha-btn {
|
||
width: 180rpx;
|
||
height: 80rpx;
|
||
line-height: 80rpx;
|
||
text-align: center;
|
||
background-color: #007aff;
|
||
color: #fff;
|
||
border: none;
|
||
border-radius: 8rpx;
|
||
font-size: 24rpx;
|
||
}
|
||
|
||
.captcha-btn.disabled {
|
||
background-color: #ccc;
|
||
color: #999;
|
||
}
|
||
|
||
.btn {
|
||
width: 100%;
|
||
height: 80rpx;
|
||
border-radius: 8rpx;
|
||
font-size: 32rpx;
|
||
color: #fff;
|
||
background-color: #007aff;
|
||
margin-bottom: 30rpx;
|
||
}
|
||
|
||
.btn.disabled {
|
||
background-color: #ccc;
|
||
}
|
||
|
||
.back-link {
|
||
text-align: center;
|
||
}
|
||
|
||
.back-text {
|
||
font-size: 26rpx;
|
||
color: #999;
|
||
text-decoration: underline;
|
||
}
|
||
|
||
.back-text:active {
|
||
color: #007aff;
|
||
}
|
||
</style> |