|
|
<template> <view class="login-registration-container"> <!-- 自定义导航栏 --> <view class="custom-navbar" :style="{ paddingTop: safeAreaInsets?.top + 'px' }" > <!-- <view class="nav-left"> <text class="back-btn" @click="goToIndex"><</text> </view> --> <view class="nav-right"> <image @click="goToService" class="icons" src="../../../static/icons/Headset.png" alt="联系客服" /> <image class="icons" @click="goToIndex" src="../../../static/icons/Frame.png" alt="返回首页" /> </view> </view>
<!-- Logo --> <!-- <image class="logo" src="/static/logo.png" mode="aspectFit"></image> -->
<!-- 欢迎语 --> <text class="welcome-text">登录DeepChart</text>
<!-- 邮箱/手机号切换 --> <view class="switch-container"> <text class="switch-item" :class="{ active: switchType === 'User' }" @click="switchUser" >用户名</text > <text class="switch-item" :class="{ active: switchType === 'Phone' }" @click="switchPhone" >手机号</text ><text class="switch-item" :class="{ active: switchType === 'Email' }" @click="switchEmail" >邮箱</text > </view> <!-- 输入框 --> <view class="input-container"> <view v-if="switchType === 'User'"> <!-- 修改邮箱输入框容器,将图标包含在内 --> <view class="input-with-icon"> <image class="input-icon" src="../../../static/icons/People-safe.png" alt="" /> <input class="input-field" type="text" placeholder="请输入DeepChart ID" v-model="deepChartID" /> </view> <view class="input-with-icon"> <image class="input-icon" src="../../../static/icons/Unlock.png" alt="" /> <input class="input-field" type="text" placeholder="请输入密码" v-model="password" /> </view> </view> <view v-if="switchType === 'Email'"> <!-- 修改邮箱输入框容器,将图标包含在内 --> <view class="input-with-icon"> <image class="input-icon" src="../../../static/icons/Mail.png" alt="" /> <input class="input-field" type="text" placeholder="请输入邮箱" v-model="email" /> <view> <button class="send-code-btn-email" :disabled="isCodeBtnDisabled" :class="{ 'send-code-btn-disabled': isCodeBtnDisabled }" @click="sendCode" > <text class="send-code-text" :class="{ 'send-code-btn-disabled-text': isCodeBtnDisabled }" >{{ codeBtnText }}</text > </button> </view> </view> <view class="input-with-icon"> <image class="input-icon" src="../../../static/icons/Text-recognition.png" alt="" /> <input class="input-field" type="text" placeholder="请输入验证码" v-model="verifyCode" /> </view> </view> <view v-if="switchType === 'Phone'" class="phone-input-container"> <view class="country-code-selector" @click="showCountryPicker"> <image class="country-flag-img" src="../../../static/icons/Iphone.png" mode="aspectFit" ></image> <text class="country-code">{{ selectedCountry.code }}</text> <!-- <text class="arrow-down">▼</text> --> </view> <input class="input-field phone-input" type="number" placeholder="输入手机号" v-model="phone" @input="onPhoneInput" /> <view> <button class="send-code-btn" :disabled="isCodeBtnDisabled" :class="{ 'send-code-btn-disabled': isCodeBtnDisabled }" @click="sendCode" > <text class="send-code-text" :class="{ 'send-code-btn-disabled-text': isCodeBtnDisabled }" >{{ codeBtnText }}</text > </button> </view> </view> <view v-if="switchType === 'Phone'" class="input-with-icon"> <image class="input-icon" src="../../../static/icons/Text-recognition.png" alt="" /> <input class="input-field" type="text" placeholder="请输入验证码" v-model="verifyCode" /> </view> </view>
<!-- 用户协议 --> <view @click="changeCheckbox" class="agreement-container-one"> <image class="checkbox" :src="checkboxUrl"></image> <text class="agreement-text-one" >接受 <text class="link" @click="openAgreement">用户协议</text> 和 <text class="link" @click="openPrivacy">隐私政策</text></text > </view> <view v-if="switchType === 'User'" class="agreement-container"> <text class="agreement-text" ><text @click="recoverPassword">忘记ID/密码</text> </text> </view> <view v-else class="agreement-container"> <text class="agreement-text"> <!-- 添加占位元素,防止页面变形 --> <text style="visibility: hidden">占位符</text> </text> </view>
<!-- 注册按钮 --> <button class="register-btn" @click="Login"> <text v-if="!isLoading">登录</text> <image v-else class="icons-rotation" src="../../../static/icons/loading.png" ></image> </button>
<!-- 或者 --> <text class="or-text" @click="goToRegistration" >如果您还没有账号,点击注册 <image class="to-icon" src="../../../static/icons/To.png"></image> </text>
<!-- 第三方登录 --> <view class="third-party-login"> <view class="third-party-btn" @click="loginWithApple"> <image class="apple-icon" src="../../../static/icons/appleIcons.png" mode="aspectFit" ></image> <text class="third-party-text">通过 Apple 登录 </text> </view>
<view class="third-party-btn" @click="loginWithGoogle"> <image class="google-icon" src="../../../static/icons/GoogleIcons.png" mode="aspectFit" ></image> <text class="third-party-text">通过 Google 登录</text> </view> </view>
<!-- 同意弹窗 --> <uniPopup ref="agreementPopup" type="dialog"> <view class="popup-content"> <text class="popup-message" >请阅读并同意<text @click="openAgreement" class="popup-message-link" >服务协议</text >和<text @click="openPrivacy" class="popup-message-link" >隐私权限</text > </text> <view class="button-group"> <button class="cancel-button" @click="handleCancel"> <text class="cancel-text">取消</text> </button> <button class="agree-button" @click="handleAgree"> <text class="agree-text">同意</text> </button> </view> </view> </uniPopup> <footerBar class="static-footer" :type="type"></footerBar> </view></template>
<script setup>import { ref } from "vue";// 导入完整的国家列表
import countryList from "./list.js";import footerBar from "../../../components/footerBar";import uniPopupDialogVue from "../../../uni_modules/uni-popup/components/uni-popup-dialog/uni-popup-dialog.vue";import uniPopup from "../../../uni_modules/uni-popup/components/uni-popup/uni-popup.vue";import { verificationPhone, verificationEmail } from "../login/verification";import { LoginApi, SendEmailCodeApi, SendPhoneCodeApi,} from "../../../api/start/login";
import { useUserStore } from "../../../stores/modules/userInfo";import { useDeviceStore } from "../../../stores/modules/deviceInfo";
const deepChartID = ref("");const type = ref("");const email = ref("");const password = ref("");const country = ref("+86");const phone = ref("");const agreed = ref(false);const switchType = ref("User"); // 默认是邮箱注册
const { safeAreaInsets } = uni.getSystemInfoSync();const codeBtnText = ref("获取验证码");const isCodeBtnDisabled = ref(false); // 添加验证码按钮禁用状态
const checkboxUrl = ref("../../../static/icons/Check-one-false.png");const verifyCode = ref("");const account = ref("");const isLoading = ref(false);
// 使用从list.js导入的完整国家列表数据
const countries = ref( countryList.list.map((item) => ({ name: item.name, code: `+${item.tel}`, flag: item.flag, short: item.short, en: item.en, })));
// 默认选中的国家(中国)
const selectedCountry = ref( countries.value.find((country) => country.short === "CN") || countries.value[0]);
// 显示国家选择器
function showCountryPicker() { uni.showActionSheet({ itemList: countries.value.map( (country) => `${country.name} ${country.code}` ), success: function (res) { selectedCountry.value = countries.value[res.tapIndex]; country.value = selectedCountry.value.code; }, });}
function goToIndex() { // 返回上一页
uni.navigateTo({ url: "/pages/home/home", });}function goToService() { // 返回上一页
uni.navigateTo({ url: "/pages/home/home", });}
function switchUser() { // 切换到手机注册
switchType.value = "User"; password.value = ""; verifyCode.value = "";}
function switchEmail() { // 切换到邮箱注册
switchType.value = "Email"; password.value = ""; verifyCode.value = "";}
function switchPhone() { // 切换到手机注册
switchType.value = "Phone"; password.value = ""; verifyCode.value = "";}
// 登录
async function Login() { if (!basicVerification()) { return; }
const deviceStore = useDeviceStore();
const account = changeAccount(); const loginType = changeLoginType(); isLoading.value = true; const res = await LoginApi({ loginType: loginType, account: account, verifyCode: verifyCode.value, password: password.value, useCode: verifyCode.value ? true : false, idToken: "", deviceId: deviceStore.deviceInfo.deviceId, }); isLoading.value = false;
const message = res.message; if (res.code === 200) { // 登录成功
uni.showToast({ title: "登录成功", icon: "success", });
const userStore = useUserStore(); userStore.setUserInfo(res.data);
console.log("userInfo为", userStore.userInfo);
// 跳转到首页
uni.redirectTo({ url: "/pages/home/home", }); } else { // 登录失败
uni.showToast({ title: message, icon: "none", }); }}
// 基础验证
function basicVerification() { if (switchType.value === "User") { if (!deepChartID.value) { uni.showToast({ title: "请输入用户名", icon: "none", }); return false; }
if (!password.value) { uni.showToast({ title: "请输入密码", icon: "none", }); return false; } }
if (switchType.value === "Phone") { // 登录逻辑
if (!phone.value) { uni.showToast({ title: "请输入手机号码", icon: "none", }); return false; }
const phoneAll = `${country.value}${phone.value}`; console.log("完整手机号" + phoneAll); if (!validatePhoneNumber(country.value, phone.value)) { return false; }
if (!verifyCode.value) { uni.showToast({ title: "请输入验证码", icon: "none", }); return false; } }
if (switchType.value === "Email") { // 登录逻辑
if (!email.value) { uni.showToast({ title: "请输入邮箱地址", icon: "none", }); return false; }
const bool = verificationEmail(email.value); console.log("验证是否成功", bool); // 检查格式是否正确
if (!bool) { uni.showToast({ title: "邮箱格式不正确", icon: "none", }); return false; }
if (!verifyCode.value) { uni.showToast({ title: "请输入验证码", icon: "none", }); return false; } }
if (!agreed.value) { // 显示同意弹窗
agreementPopup.value.open(); return; }
return true;}
// 注册码码验证
function VerCodeVerfifcation() { if (switchType.value === "Phone") { if (!phone.value) { uni.showToast({ title: "请输入手机号", icon: "none", }); return false; }
const bool = verificationPhone(country.value, phone.value); console.log("验证是否成功", bool);
// 检查格式是否正确
if (!bool) { uni.showToast({ title: "手机号格式不正确", icon: "none", }); return false; } }
if (switchType.value === "Email") { if (!email.value) { uni.showToast({ title: "请输入邮箱地址", icon: "none", }); return false; } const bool = verificationEmail(email.value); console.log("验证是否成功", bool);
// 检查格式是否正确
if (!bool) { uni.showToast({ title: "邮箱格式不正确", icon: "none", }); return false; } }
return true;}
// 请求账户
function changeAccount() { if (switchType.value === "User") { account.value = deepChartID.value; }
if (switchType.value === "Phone") { account.value = `${country.value}${phone.value}`; } if (switchType.value === "Email") { account.value = email.value; }
return account.value;}
// 改变请求时的type
function changeLoginType() { if (switchType.value === "User") { return "DCCODE"; }
if (switchType.value === "Phone") { return "PHONE"; } if (switchType.value === "Email") { return "EMAIL"; }}
// 添加弹窗引用
const agreementPopup = ref(null);
// 处理同意按钮点击
function handleAgree() { // 关闭弹窗
agreementPopup.value.close(); // 设置为已同意
agreed.value = true; checkboxUrl.value = "../../../static/icons/Check-one-true.png"; // 继续登录流程
}// 处理同意按钮点击
function handleCancel() { // 关闭弹窗
agreementPopup.value.close();}
// 苹果登录
function loginWithApple() { // Apple登录逻辑
console.log("通过Apple登录"); uni.login({ provider: "apple", success: function (loginRes) { // 登录成功
uni.getUserInfo({ provider: "apple", success: function (info) { console.log(info); }, }); }, fail: function (err) { // 登录授权失败
// err.code错误码参考`授权失败错误码(code)说明`
console.log(err); }, });}
// 谷歌登录
function loginWithGoogle() { // Google登录逻辑
console.log("通过Google登录"); uni.login({ provider: "google", success: function (loginRes) { // 登录成功
uni.getUserInfo({ provider: "google", success: function (info) { console.log(info); }, }); }, fail: function (err) { // 登录授权失败
// err.code是错误码
console.log(err); }, });}
function goToRegistration() { // 跳转到登录页
uni.navigateTo({ url: "/pages/start/Registration/Registration", });}
function onPhoneInput(e) { // 确保只允许输入数字
const value = e.detail.value; // 使用 isNaN 检查是否为有效数字
if (isNaN(value)) { phone.value = ""; } else { phone.value = value; }}
// 发送验证码
async function sendCode() { if (!VerCodeVerfifcation()) { return; } // 如果按钮已禁用,则不执行后续逻辑
if (isCodeBtnDisabled.value) return;
if (switchType.value === "Phone") { // 发送验证码
const phoneAll = `${country.value}${phone.value}`; const res = await SendPhoneCodeApi({ phone: phoneAll, }); console.log("手机验证码:", res.message);
if (!res) { uni.showToast({ title: "请求失败", icon: "none", }); } } if (switchType.value === "Email") { // 发送验证码
const res = await SendEmailCodeApi({ email: email.value, }); console.log("邮箱验证码:", res.message);
if (!res) { uni.showToast({ title: "请求失败", icon: "none", }); } }
// 设置按钮为禁用状态
isCodeBtnDisabled.value = true; codeBtnText.value = "重新发送"; let time = 6; const timer = setInterval(() => { time--; codeBtnText.value = "重新发送 " + time + "s"; if (time <= 0) { clearInterval(timer); codeBtnText.value = "重新发送"; // 倒计时结束后启用按钮
isCodeBtnDisabled.value = false; } }, 1000);
return;}
function openAgreement() { // 打开用户协议
console.log("打开用户协议"); uni.navigateTo({ url: "/pages/start/agreement/agreement", });}
function openPrivacy() { // 打开隐私政策
console.log("打开隐私政策"); uni.navigateTo({ url: "/pages/start/privacy/privacy", });}
function recoverPassword() { // 忘记密码
// console.log("忘记密码");
uni.navigateTo({ url: "/pages/start/recoverPassword/recoverPassword", });}
function changeCheckbox() { agreed.value = !agreed.value; checkboxUrl.value = agreed.value ? "../../../static/icons/Check-one-true.png" : "../../../static/icons/Check-one-false.png";}
// 验证手机号是否正确
function validatePhoneNumber(countryCode, phoneNumber) { // 检查是否为空
if (!phoneNumber || phoneNumber.trim() === "") { uni.showToast({ title: "手机号不能为空", icon: "none", }); return false; }
const bool = verificationPhone(countryCode, phoneNumber); console.log("验证是否成功", bool);
// 检查格式是否正确
if (!bool) { uni.showToast({ title: "手机号格式不正确", icon: "none", }); return false; }
// 去掉+号后检查长度(手机号通常在7到15位之间)
const cleanNumber = phoneNumber.replace(/^\+/, ""); if (cleanNumber.length < 7 || cleanNumber.length > 15) { uni.showToast({ title: "手机号长度不正确", icon: "none", }); return false; }
return true;}</script>
<style scoped>.login-registration-container { display: flex; flex-direction: column; align-items: center; justify-content: center; padding: 0 70rpx; height: 100vh; background-color: #ffffff;}
/* 自定义导航栏样式 */.custom-navbar { position: absolute; top: 0; left: 0; /* z-index: 999; */ width: 90%; height: 80rpx; display: flex; justify-content: space-between; align-items: center; padding: 10rpx 40rpx; margin-bottom: 20rpx;}
.nav-left,.nav-right { flex: 1;}
.nav-right { display: flex; justify-content: flex-end;}
.icons { margin: 20rpx; width: 40rpx; height: 40rpx; /* margin-right: 10rpx; */}
.icons-rotation { margin: 20rpx; width: 40rpx; height: 40rpx; /* margin-right: 10rpx; */ animation: rotation 2s linear infinite;}
@keyframes rotation { from { transform: rotate(0deg); } to { transform: rotate(360deg); }}
.back-btn,.headphone-btn { font-size: 36rpx; font-weight: bold; color: #333333; padding: 10rpx;}
.logo { width: 120rpx; height: 120rpx; margin-bottom: 60rpx; border-radius: 20%;}
.welcome-text { font-size: 48rpx; font-weight: bold; color: #333333; margin-bottom: 60rpx; /* text-align: left; */ /* align-self: flex-start; */}
.switch-container { display: flex; margin-bottom: 40rpx; align-self: flex-start;}
.switch-item { font-size: 28rpx; color: #999999; padding: 10rpx 20rpx; position: relative;}
.switch-item::after { content: ""; position: absolute; bottom: 0; left: 50%; transform: translateX(-50%); width: 60%; /* 控制边框宽度 */ height: 2rpx; background-color: transparent;}
.switch-item.active { color: #333333; font-weight: 700;}
.switch-item.active::after { content: ""; position: absolute; top: 60rpx; bottom: 0; left: 50%; transform: translateX(-50%); width: 30%; /* 控制边框宽度 */ height: 7rpx; background-color: #333333;}
.input-container { width: 100%;}
/* 添加图标输入框样式 */.input-with-icon { display: flex; align-items: center; width: 100%; height: 80rpx; border-bottom: 2rpx solid #e5e5e5; margin-bottom: 20rpx;}
.input-icon { width: 40rpx; height: 40rpx; margin: 0 20rpx;}
.input-field { flex: 1; height: 80rpx; padding: 15rpx 0; font-size: 28rpx; color: #333333; border: none; background-color: transparent;}
.phone-input-container { display: flex; align-items: center; width: 95.8%; height: 80rpx; /* border-radius: 20rpx; */ /* border: 2rpx solid #e5e5e5; */ /* background-color: #f5f5f5; */ padding: 0 10rpx; border-bottom: 2rpx solid #e5e5e5; margin-bottom: 20rpx;}
.country-code-selector { display: flex; align-items: center; padding: 0 10rpx; padding-bottom: 1rpx; height: 100%; /* border-right: 2rpx solid #e5e5e5; */ /* background-color: #f5f5f5; */ border-radius: 20rpx 0 0 20rpx;}
.country-code { font-size: 28rpx; color: #333333; margin-right: 10rpx;}
.country-flag-img { width: 40rpx; height: 40rpx; margin-right: 10rpx;}
.arrow-down { font-size: 20rpx; color: #999999;}
.phone-input { flex: 1; width: auto; height: 100%; border: none; background-color: transparent; padding: 0 0rpx;}
.send-code-btn { width: 200rpx; height: 60rpx; display: inline-flex; padding: 0rpx 10rpx; justify-content: center; align-items: center; gap: 10px; border-radius: 4px; background: #000;}.send-code-btn-email { width: 200rpx; height: 60rpx; display: inline-flex; padding: 0rpx 10rpx; justify-content: center; align-items: center; gap: 10px; border-radius: 4px; background: #000; margin-right: 15rpx;}
.send-code-btn-disabled { background: #e6e6e6; /* 禁用状态下的灰色背景 */}
.send-code-btn-disabled-text { color: #999999 !important;}
.send-code-text { color: #fff; font-size: 28rpx;}
.agreement-container-one { display: flex; align-items: center; align-self: flex-start; margin-bottom: 80rpx;}
.agreement-container { display: flex; align-items: center; margin-bottom: 30rpx; margin-top: -60rpx; align-self: flex-start;}
.checkbox { width: 30rpx; height: 30rpx; margin-left: 20rpx; /* flex: content; */}
.agreement-text-one { font-size: 22rpx; color: #666666; text-align: center; margin-left: 10rpx;}
.agreement-text { margin-left: 20rpx; font-size: 24rpx; color: #666666; white-space: nowrap;}
.link { color: #333333; font-weight: bold; text-decoration: underline;}
.register-btn { width: 100%; height: 80rpx; background-color: #000000; color: white; font-size: 32rpx; font-weight: bold; border-radius: 40rpx; margin-bottom: 40rpx;}
.or-text { font-size: 24rpx; color: #999999; margin-bottom: 40rpx;}
.to-icon { width: 10rpx; height: 16rpx;}
.third-party-login { width: 100%; margin-bottom: 60rpx;}
.third-party-text { color: #ffffff; font-weight: bold; white-space: pre;}
.third-party-btn { width: 100%; height: 80rpx; background-color: rgb(0, 0, 0); border: 2rpx solid #e5e5e5; border-radius: 40rpx; display: flex; align-items: center; justify-content: center; margin-bottom: 20rpx; font-size: 28rpx; color: #333333;}
.google-icon,.apple-icon { width: 60rpx; height: 60rpx; margin-right: 20rpx;}
.existing-account { display: flex; align-items: center;}
.account-text { font-size: 24rpx; color: #666666;}
.login-link { font-size: 24rpx; font-weight: bold; color: #333333; margin-left: 10rpx; text-decoration: underline;}
.static-footer { position: fixed; bottom: 0;}
/* 弹窗样式 */.popup-content { background-color: #ffffff; padding: 40rpx; text-align: center; border-radius: 10rpx; width: 550rpx;}
.popup-message { font-size: 28rpx; color: #000000; margin-bottom: 60rpx; margin-top: 20rpx; text-align: center; /* 水平居中 */ display: flex; /* 使用flex布局 */ justify-content: center; /* 水平居中 */ align-items: center; /* 垂直居中 */ font-weight: 300;}.popup-message-link { font-weight: 700;}.button-group { display: flex; justify-content: space-around;}.agree-button { width: 160rpx; height: 56rpx; background-color: #000000; border-radius: 40rpx; display: flex; /* 添加flex布局 */ align-items: center; /* 垂直居中 */ justify-content: center; /* 水平居中 */}.agree-text { color: #ffffff; font-size: 34rpx; /* 添加垂直居中相关样式 */ display: flex; align-items: center; justify-content: center; line-height: 1; /* 确保文字垂直居中 */}
.cancel-button { width: 160rpx; height: 56rpx; background-color: #e5e5e5; border-radius: 40rpx; display: flex; /* 添加flex布局 */ align-items: center; /* 垂直居中 */ justify-content: center; /* 水平居中 */}.cancel-text { color: #333333; font-size: 34rpx; /* 添加垂直居中相关样式 */ display: flex; align-items: center; justify-content: center; line-height: 1; /* 确保文字垂直居中 */}</style>
|