Files
medical-mall/pages/user/register.uvue

443 lines
9.4 KiB
Plaintext
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-wrapper">
<!-- Header Logo -->
<view class="header">
<image :src="logoUrl" mode="aspectFit" class="logo" />
</view>
<!-- 注册表单区域 -->
<view class="register-box">
<view class="title">注册账号</view>
<!-- 注册表单 -->
<view class="form-content">
<!-- 邮箱 -->
<view class="input-group">
<view class="input-wrapper">
<image src="/static/user/phone_1.png" class="input-icon" />
<input
type="text"
placeholder="输入邮箱"
v-model="email"
class="input-field"
/>
</view>
</view>
<!-- 密码 -->
<view class="input-group">
<view class="input-wrapper">
<image src="/static/user/code_1.png" class="input-icon" />
<input
type="password"
placeholder="填写密码"
v-model="password"
class="input-field"
/>
</view>
</view>
<!-- 确认密码 -->
<view class="input-group">
<view class="input-wrapper">
<image src="/static/user/code_1.png" class="input-icon" />
<input
type="password"
placeholder="确认密码"
v-model="confirmPassword"
class="input-field"
/>
</view>
</view>
</view>
<!-- 注册按钮 -->
<view class="register-btn" @click="handleRegister" :class="isLoading ? 'disabled' : ''">
注册
</view>
<!-- 已有账号 -->
<view class="tips">
<text class="tips-text">已有账号?</text>
<text class="tips-link" @click="navigateToLogin">立即登录</text>
</view>
<!-- 协议勾选 -->
<view class="protocol">
<checkbox-group @change="handleProtocolChange">
<checkbox
class="protocol-checkbox"
:checked="protocol"
:class="inAnimation ? 'trembling' : ''"
/>
<text class="protocol-text">
已阅读并同意
<text class="main-color" @click="navigateToTerms(3)">《用户协议》</text>
<text class="main-color" @click="navigateToTerms(4)">《隐私协议》</text>
</text>
</checkbox-group>
</view>
</view>
<!-- 底部版权信息 -->
<view class="footer">
<text class="footer-text">Copyright ©2024 Mall. All Rights Reserved</text>
</view>
</view>
</template>
<script setup lang="uts">
import { ref } from 'vue'
import supa from '@/components/supadb/aksupainstance.uts'
import { ensureUserProfile } from '@/utils/sapi.uts'
const email = ref<string>('')
const password = ref<string>('')
const confirmPassword = ref<string>('')
const protocol = ref<boolean>(false)
const inAnimation = ref<boolean>(false)
const isLoading = ref<boolean>(false)
const logoUrl = ref<string>('/static/logo.png')
const handleProtocolChange = (e: UniCheckboxGroupChangeEvent): void => {
protocol.value = protocol.value == false
}
const validateEmail = (): boolean => {
if (email.value.trim() == '') {
uni.showToast({
title: '请填写邮箱',
icon: 'none'
})
return false
}
const atIndex = email.value.indexOf('@')
const dotIndex = email.value.lastIndexOf('.')
if (atIndex == -1 || dotIndex == -1 || atIndex > dotIndex) {
uni.showToast({
title: '请输入正确的邮箱',
icon: 'none'
})
return false
}
return true
}
const validatePassword = (): boolean => {
if (password.value.trim() == '') {
uni.showToast({
title: '请填写密码',
icon: 'none'
})
return false
}
if (password.value.length < 6) {
uni.showToast({
title: '密码长度不能少于6位',
icon: 'none'
})
return false
}
return true
}
const validateConfirmPassword = (): boolean => {
if (confirmPassword.value.trim() == '') {
uni.showToast({
title: '请确认密码',
icon: 'none'
})
return false
}
if (confirmPassword.value != password.value) {
uni.showToast({
title: '两次输入的密码不一致',
icon: 'none'
})
return false
}
return true
}
const handleRegister = async (): Promise<void> => {
if (protocol.value == false) {
inAnimation.value = true
uni.showToast({
title: '请先阅读并同意协议',
icon: 'none'
})
return
}
if (validateEmail() == false) {
return
}
if (validatePassword() == false) {
return
}
if (validateConfirmPassword() == false) {
return
}
isLoading.value = true
try {
// 在注册时传递 user_role 元数据,以便数据库触发器识别
const options = new UTSJSONObject()
const metaData = new UTSJSONObject()
metaData.set('user_role', 'consumer')
options.set('data', metaData)
const result = await supa.signUp(email.value.trim(), password.value, options)
console.log('注册返回结果:', result)
const errorCode = result?.getString('error_code') ?? ''
const errorMsg = result?.getString('msg') ?? ''
const code = result?.getNumber('code') ?? 0
console.log('错误代码:', errorCode, '错误信息:', errorMsg, '状态码:', code)
if (code == 500 && (errorCode == 'unexpected_failure' || errorMsg.includes('confirmation email'))) {
console.warn('邮件发送失败,但用户可能已创建')
}
let user: UTSJSONObject | null = null
let hasSession = false
if (result != null) {
const userField = result.getJSON('user')
if (userField != null) {
user = userField
console.log('找到 user 字段:', user.getString('id'), user.getString('email'))
} else {
const id = result.getString('id')
if (id != null && id != '') {
user = result
console.log('result 本身就是 user 对象:', id)
} else {
console.warn('未找到 user 信息')
}
}
const sessionField = result.getJSON('session')
if (sessionField != null) {
hasSession = true
console.log('找到 session已自动登录')
} else {
console.log('未找到 session可能需要邮箱验证')
}
}
if (user == null && code != 0 && code != 200) {
if (code == 500 && errorMsg.includes('confirmation email')) {
throw new Error('注册失败:邮件服务配置错误')
} else {
throw new Error(errorMsg != '' ? errorMsg : '注册失败,请重试')
}
}
if (user != null) {
try {
const profileResult = await ensureUserProfile(user)
if (profileResult != null) {
console.log('用户资料创建成功:', profileResult.id)
} else {
console.warn('用户资料创建失败,但注册已成功')
}
} catch (profileError) {
console.error('创建用户资料异常:', profileError)
}
} else {
console.warn('注册成功但未获取到用户信息')
}
if (hasSession == false && user != null) {
console.log('需要邮箱验证')
}
uni.showToast({
title: '注册成功',
icon: 'success'
})
setTimeout(() => {
uni.redirectTo({
url: '/pages/user/login'
})
}, 1500)
} catch (err) {
console.error('注册错误:', err)
let errorMessage = '注册失败,请重试'
if (err != null) {
const error = err as Error
if (error.message != null && error.message.trim() != '') {
errorMessage = error.message
if (error.message.includes('confirmation email') || error.message.includes('邮件')) {
errorMessage = '注册可能成功,但邮件发送失败,请稍后尝试登录'
}
}
}
uni.showToast({
title: errorMessage,
icon: 'none',
duration: 3000
})
} finally {
isLoading.value = false
}
}
const navigateToLogin = (): void => {
uni.navigateTo({
url: '/pages/user/login'
})
}
const navigateToTerms = (type: number): void => {
uni.navigateTo({
url: `/pages/user/terms?type=${type}`
})
}
</script>
<style>
.register-wrapper {
flex: 1;
display: flex;
flex-direction: column;
background: #F5F5F5;
}
.header {
padding: 40rpx 0 0 60rpx;
background: #F5F5F5;
}
.logo {
width: 200rpx;
height: 80rpx;
}
.register-box {
flex: 1;
background: #FFFFFF;
margin: 60rpx 40rpx 0;
border-radius: 8rpx;
padding: 60rpx 50rpx 40rpx;
box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.08);
}
.title {
font-size: 40rpx;
font-weight: 700;
color: #333333;
text-align: center;
margin-bottom: 50rpx;
}
.form-content {
margin-bottom: 40rpx;
}
.input-group {
margin-bottom: 30rpx;
}
.input-wrapper {
position: relative;
display: flex;
align-items: center;
padding: 0 20rpx;
height: 88rpx;
border: 1rpx solid #E0E0E0;
border-radius: 4rpx;
background: #FFFFFF;
}
.input-icon {
width: 32rpx;
height: 32rpx;
flex-shrink: 0;
margin-right: 20rpx;
}
.input-field {
flex: 1;
font-size: 28rpx;
height: 100%;
color: #333333;
}
.register-btn {
display: flex;
align-items: center;
justify-content: center;
width: 100%;
height: 88rpx;
margin-top: 50rpx;
background: linear-gradient(135deg, #FF4D4F 0%, #FF7A45 100%);
border-radius: 4rpx;
color: #FFFFFF;
font-size: 32rpx;
font-weight: 700;
box-shadow: 0 4rpx 12rpx rgba(255, 77, 79, 0.3);
}
.register-btn.disabled {
background: #D9D9D9;
box-shadow: none;
opacity: 0.6;
}
.tips {
margin-top: 30rpx;
text-align: center;
}
.tips-text {
font-size: 28rpx;
color: #666666;
}
.tips-link {
font-size: 28rpx;
color: #FF4D4F;
margin-left: 8rpx;
}
.protocol {
margin-top: 40rpx;
display: flex;
align-items: center;
justify-content: center;
}
.protocol-checkbox {
margin-right: 10rpx;
}
.protocol-text {
font-size: 24rpx;
color: #999999;
}
.main-color {
color: #FF4D4F;
}
.footer {
padding: 40rpx 0;
text-align: center;
background: #F5F5F5;
}
.footer-text {
font-size: 22rpx;
color: #999999;
}
</style>