Files
sweekar-app/pages/login/register.vue
T
2026-01-21 11:07:37 +08:00

393 lines
11 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.
<script setup>
import { ref } from 'vue';
import { register, sendCodeToEmail, loginByEmail } from './api';
import { useUserStore } from '@/store/user.js';
const userStore = useUserStore();
// 注册相关变量
const registerEmail = ref(''); // 注册邮箱
const registerPassword = ref(''); // 注册密码
const confirmPassword = ref(''); // 确认密码
const verificationCode = ref(''); // 验证码
const codeBtnText = ref('获取验证码');
const codeBtnDisabled = ref(false);
const checked = ref(true); // 同意条款,默认勾选
let countdownTimer = null;
// 邮箱格式验证
const validateEmail = (email) => {
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
return emailRegex.test(email);
};
// 同意条款切换
const onChange = (e) => {
checked.value = e.detail.value; // 更新checked值
};
// 显示用户协议
const showAgreement = () => {
uni.showModal({
title: '用户协议',
content: '欢迎使用我们的服务!\n\n本协议规定了您使用我们服务的条款和条件,请仔细阅读。\n\n1. 服务使用条款\n2. 隐私政策\n3. 账户安全\n4. 免责声明\n\n继续使用即表示您同意遵守这些条款。',
showCancel: true,
confirmText: '同意',
cancelText: '取消',
success: (res) => {
if (res.confirm) {
checked.value = true;
}
}
});
};
// 获取验证码
const getVerificationCode = async () => {
if (!registerEmail.value) {
uni.showToast({
title: '请输入邮箱',
icon: 'none',
});
return;
}
if (!validateEmail(registerEmail.value)) {
uni.showToast({
title: '请输入正确的邮箱',
icon: 'none',
});
return;
}
// 发送验证码
try {
uni.showLoading({
title: '发送中...',
mask: true,
});
const params = {
email: registerEmail.value,
register: true
};
const res = await sendCodeToEmail(params);
if (res && res.code === 0) {
uni.hideLoading();
uni.showToast({
title: '验证码已发送',
icon: 'success',
});
// 开始倒计时
let timeLeft = 60;
codeBtnDisabled.value = true;
codeBtnText.value = `${timeLeft}秒`;
countdownTimer = setInterval(() => {
timeLeft--;
codeBtnText.value = `${timeLeft}秒`;
if (timeLeft <= 0) {
clearInterval(countdownTimer);
codeBtnText.value = '重新获取';
codeBtnDisabled.value = false;
}
}, 1000);
} else {
uni.hideLoading();
uni.showToast({
title: res.msg || '发送失败',
icon: 'error',
});
}
} catch (error) {
uni.hideLoading();
uni.showToast({
title: error.message || '发送失败,请重试',
icon: 'error',
});
}
};
// 注册提交
const onRegister = async () => {
if (!registerEmail.value) {
uni.showToast({
title: '请输入邮箱',
icon: 'none',
});
return;
}
if (!validateEmail(registerEmail.value)) {
uni.showToast({
title: '请输入正确的邮箱',
icon: 'none',
});
return;
}
if (!verificationCode.value) {
uni.showToast({
title: '请输入验证码',
icon: 'none',
});
return;
}
if (!registerPassword.value) {
uni.showToast({
title: '请输入密码',
icon: 'none',
});
return;
}
if (registerPassword.value !== confirmPassword.value) {
uni.showToast({
title: '两次输入密码不一致',
icon: 'none',
});
return;
}
if (registerPassword.value.length < 6) {
uni.showToast({
title: '密码至少6位',
icon: 'none',
});
return;
}
if (!checked.value) {
uni.showToast({
title: '请先同意用户协议',
icon: 'none',
duration: 2000,
});
return;
}
try {
uni.showLoading({
title: '注册中...',
mask: true,
});
// 调用注册API
const registerParams = {
email: registerEmail.value,
verificationCode: verificationCode.value,
password: registerPassword.value,
confirmPassword: confirmPassword.value
};
const res = await register(registerParams);
if (res && res.code === 0) {
uni.hideLoading();
uni.showToast({
title: '注册成功',
icon: 'success',
});
// 注册成功后自动登录
setTimeout(async () => {
try {
const loginRes = await loginByEmail({
email: registerEmail.value,
password: registerPassword.value
});
if (loginRes && loginRes.code === 0) {
// 存储用户信息到全局状态
userStore.setUserInfo(loginRes.data);
userStore.setToken(loginRes.data.accessToken);
userStore.setLoginStatus(true);
uni.showToast({
title: '登录成功',
icon: 'success',
});
// 跳转到首页
uni.switchTab({
url: '/pages/mine/index'
});
} else {
uni.showToast({
title: loginRes.msg || '自动登录失败',
icon: 'none',
});
// 即使自动登录失败也跳转到首页,让用户重新登录
uni.switchTab({
url: '/pages/mine/index'
});
}
} catch (error) {
console.error('自动登录失败:', error);
uni.showToast({
title: '自动登录失败,请手动登录',
icon: 'none',
});
// 跳转到首页
uni.switchTab({
url: '/pages/mine/index'
});
}
}, 1000);
} else {
uni.hideLoading();
uni.showToast({
title: res.msg || '注册失败',
icon: 'error',
});
}
} catch (error) {
uni.hideLoading();
uni.showToast({
title: error.message || '注册失败,请稍后重试',
icon: 'error',
});
}
};
// 清空输入框
const clearInput = (type) => {
if (type === 'registerEmail') {
registerEmail.value = '';
} else if (type === 'registerPassword') {
registerPassword.value = '';
} else if (type === 'confirmPassword') {
confirmPassword.value = '';
} else if (type === 'verificationCode') {
verificationCode.value = '';
}
};
</script>
<template>
<view class="h-[120vh] p-4 min-h-screen bg-white flex flex-col justify-center">
<!-- 标题 -->
<view class="mb-12 mt-20 text-center">
<image src="/static/tabbar/find.png" class="w-20 h-20 mb-8" mode="aspectFit"></image>
<text class="block text-3xl font-bold text-gray-800 mb-4">CREATE ACCOUNT</text>
<text class="text-sm text-gray-500">Join Sweekar to get started</text>
</view>
<!-- 注册邮箱输入 -->
<view class="mb-6">
<view class=" flex items-center h-12 bg-gray-100 rounded-lg px-6 gap-1 border border-solid border-round-theme">
<u-icon name="email" class="h-10" color="black" bold="true"></u-icon>
<input
class="w-full text-base text-gray-800 bg-transparent"
type="email"
placeholder="Email Address"
v-model="registerEmail"
/>
<view
v-if="registerEmail"
class="absolute right-6 w-8 h-8 flex items-center justify-center bg-gray-400 rounded-full text-black font-bold"
@tap="clearInput('registerEmail')"
>
×
</view>
</view>
</view>
<!-- 验证码输入 -->
<view class="mb-6 relative">
<view class=" flex items-center h-12 bg-gray-100 rounded-lg px-6 gap-1 border border-solid border-round-theme">
<u-icon name="chat" class="h-10" color="black" bold="true"></u-icon>
<input
class="w-full text-base text-gray-800 bg-transparent pr-32"
type="text"
placeholder="Verification Code"
v-model="verificationCode"
/>
</view>
<button
class="absolute right-2 top-1/2 transform -translate-y-1/2 h-10 px-4 bg-theme text-white text-xs rounded-full disabled:bg-gray-400 disabled:text-gray-300 flex items-center justify-center"
:disabled="codeBtnDisabled"
@tap="getVerificationCode"
>
{{ codeBtnText }}
</button>
</view>
<!-- 注册密码输入 -->
<view class="mb-6">
<view class=" flex items-center h-12 bg-gray-100 rounded-lg px-6 gap-1 border border-solid border-round-theme">
<u-icon name="lock" class="h-10" color="black" bold="true"></u-icon>
<input
class="w-full text-base text-gray-800 bg-transparent"
type="password"
placeholder="Password"
v-model="registerPassword"
/>
<view
v-if="registerPassword"
class="absolute right-6 w-8 h-8 flex items-center justify-center bg-gray-400 rounded-full text-white font-bold"
@tap="clearInput('registerPassword')"
>
×
</view>
</view>
</view>
<!-- 确认密码输入 -->
<view class="mb-8">
<view class=" flex items-center h-12 bg-gray-100 rounded-lg px-6 gap-1 border border-solid border-round-theme">
<u-icon name="lock" class="h-10" color="black" bold="true"></u-icon>
<input
class="w-full text-base text-gray-800 bg-transparent"
type="password"
placeholder="Confirm Password"
v-model="confirmPassword"
/>
<view
v-if="confirmPassword"
class="absolute right-6 w-8 h-8 flex items-center justify-center bg-gray-400 rounded-full text-white font-bold"
@tap="clearInput('confirmPassword')"
>
×
</view>
</view>
</view>
<!-- 注册按钮 -->
<view
class="w-full h-12 bg-theme rounded-lg text-white text-xl flex items-center justify-center"
@click="onRegister"
>
<text>SIGN UP</text>
</view>
<!-- 登录链接 -->
<view class="flex justify-center items-center my-8 text-gray-500 text-sm">
<text>Already have an account?</text>
<text class="text-green-500 ml-2" @click="$Router.push('/pages/login/index')">Sign in</text>
</view>
<!-- 同意条款 -->
<view class="mt-auto text-center">
<label class="flex items-center justify-center text-xs text-gray-500 leading-loose">
<checkbox
class="scale-75 mr-2"
:checked="checked"
@change="onChange"
/>
我已阅读并同意
<text class="text-green-500 underline" @tap="showAgreement">《用户协议》</text>
和
<text class="text-green-500 underline" @tap="showAgreement">《隐私政策》</text>
</label>
</view>
</view>
</template>