You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 

1146 lines
25 KiB

<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
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">登录</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-cn";
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";
const deepChartID = ref("");
const type = ref("member");
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("");
// 使用从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/start/index/index",
});
}
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 account = changeAccount();
const res = await LoginApi({
loginType: switchType.value,
account: account,
verifyCode: verifyCode.value,
password: password.value,
useCode: verifyCode.value ? true : false,
idToken: "",
});
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.switchTab({
url: "/pages/start/index/index",
});
} 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;
}
// 添加弹窗引用
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);
const message = res.message;
if (res.code === 200) {
uni.showToast({
title: message,
icon: "none",
});
}
}
if (switchType.value === "Email") {
// 发送验证码
const res = await SendEmailCodeApi({
email: email.value,
});
console.log("邮箱验证码:", res.message);
const message = res.message;
if (res.code === 200) {
uni.showToast({
title: message,
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; */
}
.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>